用户更改了应用程序的主题后,字体的主题无法正确重建(GetX状态管理)

时间:2020-09-26 10:16:52

标签: flutter

我创建了一个“几乎完成”的应用程序,但是在切换主题方面存在一些问题。我正在使用getxget_storage进行状态管理。

我还为主题使用了许多static类型,因为我认为它永远不会被重建。它适用于其他组件,但不适用于文本。像这样,有点奇怪。

problem visualization

我不知道是什么原因造成的,应该避免为主题使用static类吗? 请查看我的脚本,并随时告诉我是否做错了事。

main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await GetStorage.init();
  ...
  ...
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final box = GetStorage();

  @override
  Widget build(BuildContext context) {
    return SimpleBuilder(
      builder: (_) {
        bool isDark = box.read('darkMode');
        return GetMaterialApp(
          ...
          theme: CustomTheme.light,
          darkTheme: CustomTheme.dark,
          themeMode: isDark == null
              ? ThemeMode.system
              : isDark ? ThemeMode.dark : ThemeMode.light,
          ...
        );
      },
    );
  }
}

custom_theme.dart

class CustomTheme {
  static ThemeData get light {
    return ThemeData.light().copyWith(
      ...
      cardTheme: CustomCardTheme.shared,
      textTheme: CustomTextTheme.light,
      ...
    );
  }

  static ThemeData get dark {
    return ThemeData.dark().copyWith(
      ...
      cardTheme: CustomCardTheme.shared,
      textTheme: CustomTextTheme.dark,
      ...
    );
  }
}

card_theme.dart

class CustomCardTheme {
  static CardTheme get shared {
    return CardTheme(
      elevation: 8.0,
      margin: const EdgeInsets.only(bottom: 8.0),
      shadowColor: Colors.black26,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(10.0),
      ),
    );
  }
}

text_theme.dart

const String _fontFamily = 'Nunito Sans';

const TextStyle _lightStyle = TextStyle(
  color: Colors.black,
  fontFamily: _fontFamily,
);

const TextStyle _darkStyle = TextStyle(
  color: Colors.white,
  fontFamily: _fontFamily,
);

class CustomTextTheme {
  static TextTheme get light {
    return ThemeData.light().textTheme.copyWith(
          headline1: _lightStyle,
          headline2: _lightStyle,
          headline3: _lightStyle,
          headline4: _lightStyle,
          headline5: _lightStyle,
          headline6: _lightStyle,
          subtitle1: _lightStyle,
          subtitle2: _lightStyle,
          bodyText1: _lightStyle,
          bodyText2: _lightStyle,
          // caption: _secondaryStyle,
        );
  }

  static TextTheme get dark {
    return ThemeData.dark().textTheme.copyWith(
          headline1: _darkStyle,
          headline2: _darkStyle,
          headline3: _darkStyle,
          headline4: _darkStyle,
          headline5: _darkStyle,
          headline6: _darkStyle,
          subtitle1: _darkStyle,
          subtitle2: _darkStyle,
          bodyText1: _darkStyle,
          bodyText2: _darkStyle,
          // caption: _secondaryStyle,
        );
  }
}

profile_menu_card.dart

class ProfileMenuCard extends GetView<ProfileController> {
  const ProfileMenuCard({
    Key key,
    this.iconBackgroundColor,
    this.iconHeight = 18.0,
    this.iconWidth = 18.0,
    this.trailing,
    @required this.label,
    @required this.icon,
    @required this.onTap,
  }) : super(key: key);

  final Color iconBackgroundColor;
  final String label;
  final String icon;
  final double iconHeight;
  final double iconWidth;
  final VoidCallback onTap;
  final Widget trailing;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16.0),
        child: ListTile(
          onTap: onTap,
          contentPadding: EdgeInsets.zero,
          leading: Container(
            height: 32,
            width: 32,
            decoration: BoxDecoration(
              color: iconBackgroundColor ?? Get.theme.primaryColor,
              borderRadius: BorderRadius.circular(10.0),
            ),
            child: Center(
              child: SvgPicture.asset(
                icon,
                height: iconHeight,
                width: iconWidth,
              ),
            ),
          ),
          title: Text(
            StringUtils.capitalize(label, allWords: true),
            style: Get.textTheme.bodyText1.copyWith(
              fontWeight: FontWeight.w600,
            ),
          ),
          trailing: trailing ?? Icon(Icons.chevron_right),
        ),
      ),
    );
  }
}

dark_mode_switch.dart

class DarkModeSwitch extends StatelessWidget {
  final box = GetStorage();

  @override
  Widget build(BuildContext context) {
    bool isDark = box.read('darkMode');

    return Transform.scale(
      scale: 0.8,
      child: CupertinoSwitch(
        value: isDark,
        onChanged: (bool val) => box.write('darkMode', val),
      ),
    );
  }
}

profile_page.dart

const EdgeInsets _titlePadding = EdgeInsets.only(bottom: 10.0, left: 8.0);

class ProfilePage extends GetView<ProfileController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SingleChildScrollView(
        padding: const EdgeInsets.symmetric(horizontal: 20.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            ListViewGroup(
              title: Padding(
                padding: _titlePadding,
                child: Text('account.info'.tr, style: Get.textTheme.caption),
              ),
              items: [
                ProfileMenuCard(
                  icon: 'assets/icons/icon-user-circle.svg',
                  label: 'Personal',
                  onTap: () => Get.toNamed(Routes.ACCOUNT_INFO_PERSONAL),
                ),
                ProfileMenuCard(
                  iconBackgroundColor: Color(0xFFF3548D),
                  icon: 'assets/icons/icon-id-badge.svg',
                  label: 'Employment',
                  onTap: () {
                    // Get.toNamed(Routes.EMPLOYMENT_INFO);
                  },
                ),
                ProfileMenuCard(
                  iconBackgroundColor: Color(0xFF9A5BFF),
                  icon: 'assets/icons/icon-contacts.svg',
                  label: 'Contacts',
                  onTap: () => Get.toNamed(Routes.CONTACT_INFO),
                ),
                ProfileMenuCard(
                  iconBackgroundColor: Color(0xFF00D166),
                  icon: 'assets/icons/icon-book-reader.svg',
                  label: 'Educations',
                  onTap: () => Get.toNamed(Routes.EDUCATION_INFO),
                ),
                ProfileMenuCard(
                  iconBackgroundColor: Color(0xFF57109F),
                  icon: 'assets/icons/icon-briefcase.svg',
                  iconHeight: 15.75,
                  label: 'Working Experiences',
                  onTap: () => Get.toNamed(Routes.EXPERIENCE_INFO),
                ),
                ProfileMenuCard(
                  iconBackgroundColor: Color(0xFF28CEE8),
                  icon: 'assets/icons/icon-credit-card.svg',
                  iconWidth: 18,
                  iconHeight: 14,
                  label: 'Payroll',
                  onTap: () {},
                ),
              ],
            ),
            ListViewGroup(
              title: Padding(
                padding: _titlePadding,
                child: Text('settings'.tr, style: Get.textTheme.caption),
              ),
              items: [
                ProfileMenuCard(
                  iconBackgroundColor: Color(0xFF2DB9B0),
                  icon: 'assets/icons/icon-bell.svg',
                  label: 'Notifications',
                  onTap: () {},
                ),
                ProfileMenuCard(
                  iconBackgroundColor: Color(0xFF555D5C),
                  icon: 'assets/icons/icon-moon.svg',
                  label: 'Dark Mode',
                  trailing: DarkModeSwitch(),
                  onTap: null,
                ),
                ProfileMenuCard(
                  iconBackgroundColor: Color(0xFFFF894B),
                  icon: 'assets/icons/icon-paint.svg',
                  label: 'Color Scheme',
                  onTap: () {},
                ),
              ],
            ),
            ListViewGroup(
              title: Padding(
                padding: _titlePadding,
                child: Text('settings.others'.tr, style: Get.textTheme.caption),
              ),
              items: [
                ProfileMenuCard(
                  icon: 'assets/icons/icon-book.svg',
                  label: 'Official Documentation',
                  onTap: () {},
                ),
                ProfileMenuCard(
                  iconBackgroundColor: Color(0xFF9D99B9),
                  icon: 'assets/icons/icon-info.svg',
                  label: 'About this app',
                  onTap: () {},
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

1 个答案:

答案 0 :(得分:2)

Flutter中的主题由InheritedWidget(Theme.of(context))管理,以重建消耗其值的Widget,就像MediaQueryLocalization一样。同样的情况适用,您需要“使当前上下文无效。

您可以对字体,颜色,图像等使用静态值……我会使用它们,因为对于我来说,更容易手动构建样式以尝试理解{{1}中的所有属性}。虽然,当您以这种方式进行操作时,您会丢失主题默认情况下可插值的过渡功能。 无论如何,如果您使用的是GetX,只需在要更改主题更改的每个Widget的开头添加。

ThemeData

这是我前一阵子的gistdemo