Flutter實戰之主題、國際化篇

JulyYu發表於2019-08-08

回顧

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: ,
    );
複製程式碼

Flutter實戰之主題、國際化篇
通過官方文件可以看到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"))));
複製程式碼

參考

相關文章