【Flutter】主题、国际化

1,633 阅读3分钟

回顾

Flutter实战之状态管理Redux篇

结合之前介绍的Redux全局状态管理框架一起使用效果最佳。

主题

Flutter官方提供Material和Cupertino视觉组件分别高保真Android和iOS系统UI。这里使用Material Components来介绍主题的使用和切换功能。

MaterialApp支持对theme的配置,当创建MaterialApp作为应用主入口可以配置theme参数设置应用各种主题色。theme则是一个ThemeData类对象,内部定义了各种各样的全局主题色用于在不同组件中使用。

 MaterialApp(
            title: 'Flutter',
            theme: AppTheme.themes[store.state.appThemeState.themeType],
            home: Scaffold(
              //侧滑菜单栏
              drawer: ,
              //顶部栏
              appBar: ,
              //内容
              body: ,
    );

通过官方文档可以看到ThemeData配置参数很多,也因为如此这里也就不一一介绍,详见文档。这里挑几个具有代表性颜色配置来做介绍:

ThemeData(
      indicatorColor: Colors.blue, //指示器 例如BottomNavigationBar选择状态颜色
      primaryColor: Colors.blue, //应用主色调 例如Toobar
      textSelectionColor: Colors.blue, //文本选中状态颜色 例如BottomNavigationBar选择状态文本颜色
      tabBarTheme: TabBarTheme( //TabBar组件主题配置 
        unselectedLabelColor: Colors.blue[200], //未选中文本标签颜色
        labelColor: Colors.blue, //选中文本标签颜色
      ),
    )

主题色的切换功能实现并不难,配置好全局主题色通过选择对应主题实现实时切换。

class AppTheme {
  static var themes = [
    ThemeData(
      indicatorColor: Colors.blue,
      primaryColor: Colors.blue,
      textSelectionColor: Colors.blue,
      tabBarTheme: TabBarTheme(
        unselectedLabelColor: Colors.blue[200],
        labelColor: Colors.blue,
      ),
    ),
    ThemeData(
      indicatorColor: Colors.red,
      primaryColor: Colors.red,
      textSelectionColor: Colors.red,
      tabBarTheme: TabBarTheme(
        unselectedLabelColor: Colors.red[200],
        labelColor: Colors.red,
      ),
    )
  ];
}

但主题配置是在MaterialApp设置的,若在其他Widget中希望切换主题并告知到全局就比较困难了。这个时候Redux的作用就发挥出来了,用全局状态管理告知MaterialApp去更新主题。结合Redux篇的代码通过dispath操作修改主题,然后通过store中主题的state获取到选中了哪个主题。

StoreProvider.of<AppGlobalState>(context).dispatch(setAppThemeAction(
            AppThemeState(
                1, ThemeData(primaryColor: themeColor = Colors.red))));
                ...
theme: AppTheme.themes[store.state.appThemeState.themeType]

国际化

其实官方文档就有介绍国际化要不就不介绍了😀。

咦,官方文档有也不见得会用吧。😂实话说官方给的教程开始看的是云里雾里真不清楚具体如何实现语言的动态切换。最后参考别人开发者文章介绍算是明白如何实现国际化功能。

实现国际化功能要做哪些:

  • 多语言资源
  • AppLocalizationsDelegate和AppLocalizations

多语言可以用抽象类实现,定义多语言字段然后实现各国语言内容。

abstract class StringBase{
  String test;
}
class StringEn extends StringBase{
  @override
  String get test => "test";
}
class StringZh extends StringBase{
  @override
  String get test => "测试";
}

AppLocalizations是自定义类用来对外提供多语言字段的入口。通过定义的locale从静态_localizedValues中提取对应语言的字段资源而locale则是通过AppLocalizationsDelegate获取,然后通过locale.languageCode判断当前语言。

class AppLocalizations {

  AppLocalizations(this.locale);

  final Locale locale;

  static AppLocalizations of(BuildContext context) {
    return Localizations.of(context, AppLocalizations);
  }

  static Map<String, StringBase> _localizedValues = {
    'en': StringEn(),
    'zh': StringZh(),
  };

  StringBase get currentLocalization {
    if(_localizedValues.containsKey(locale.languageCode.toLowerCase())){
      return _localizedValues[locale.languageCode.toLowerCase()];
    }else{
      return _localizedValues["zh"];
    }
  }

}

AppLocalizationsDelegate是需要继承LocalizationsDelegate实现加载locale的类,在获取locale之后实例化AppLocalizations从中获取到当前应用语言。

class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
  const AppLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);

  @override
  Future<AppLocalizations> load(Locale locale) {
    return SynchronousFuture<AppLocalizations>(AppLocalizations(locale));
  }

  @override
  bool shouldReload(AppLocalizationsDelegate old) => false;

  static const AppLocalizationsDelegate delegate = AppLocalizationsDelegate();
}

接着在MaterialApp中初始化localizationsDelegates和supportedLocales。

MaterialApp(
    //国际化配置 
    localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        AppLocalizationsDelegate.delegate
    ],
    supportedLocales: [const Locale("en"), const Locale("zh")],
    
    //AppLocalizations中获取多语言字段
    AppLocalizations.of(context).currentLocalization.test
)

最后多语言实时切换还是用Redux进行实现更加快捷高效,具体实现代码在Redux篇有过介绍,同样通过dispatch操作修改locale更新语言。

 StoreProvider.of<AppGlobalState>(context)
            .dispatch(setAppLocalAction(AppLocalState(Locale("en"))));

参考