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

Tam*_*mma 5 flutter

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

我还使用了很多static主题类型,因为我认为它不应该一直被重建。它适用于其他组件,但不适用于文本。它的行为就像这样,有点奇怪..

问题可视化

我不知道是什么原因造成的,我应该避免使用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,
          ...
        );
      },
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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,
      ...
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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,
        );
  }
}
Run Code Online (Sandbox Code Playgroud)

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),
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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: () {},
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

roi*_*ker 8

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

您可以使用字体、颜色、图像等的静态值...我会使用它们,因为对我来说,手动构建样式比尝试理解ThemeData. 虽然,当您这样做时,您会失去主题默认情况下插入值的转换功能。无论如何,如果您使用GetX,只需在您想要在主题更改时重建的每个小部件的开头添加即可。

build(context){
   context.theme; /// this
   return ...
}
Run Code Online (Sandbox Code Playgroud)

这是我不久前制作的要点演示。