Flutter 結合fish-redux實現多語言以及主題切換

在這裏先安利一下fish-redux這個框架。我一直在用這個框架做路由跟開發框架。
fish-redux是阿里鹹魚開源的一個基於 Redux 數據管理的組裝式 flutter 應用框架。
它的特點是配置式組裝。 一方面我們將一個大的頁面,對視圖和數據層層拆解爲互相獨立的 Component|Adapter,上層負責組裝,下層負責實現; 另一方面將 Component|Adapter 拆分爲 View,Reducer,Effect 等相互獨立的上下文無關函數。感興趣的小夥伴可以去了解一下(PS:這個標題怕是沒用過的不會點進來吧)
言歸正傳,今天主要是記錄一下多語言跟多主題切換的實現問題。
首先實現這個功能需要用到一下插件:

  #國際化
  flutter_localizations:
    sdk: flutter
  intl: ^0.16.0
  intl_translation: ^0.17.2
  fish_redux: ^0.3.1
  #sp 數據存儲
  shared_preferences: ^0.5.2
  #數據同步,多線程
  synchronized: ^2.2.0
1.多語言功能實現

多語言插件intl的具體使用方法參考另一篇博客:
Flutter 使用intl實現國際化
同樣fish-redux使用這裏也不做介紹了。

2.多主題功能實現

1.創建global_theme_styles.dart文件來定義APP中用到的所有的顏色:


class GlobalThemeStyles{
  ///默認語言
  static Locale themeLocale =Locale('zh', 'CN');

  ///主題色
  static const List<Color> themeColors = <Color>[
    _VIOLET,
    ORANGE,
  ];
  //基本文字顏色
  static const List<Color> baseTitleColor=<Color>[
    READ,
    BLUE
  ];
  ///背景顏色
  static const List<Color> backGroundColor=<Color>[
    BAC_GRAY,
    YELLOW
  ];
  static const Color BAC_GRAY = const Color(0xFFEBEBEB);
  static const Color _VIOLET = const Color(0xFF68129A);
  static const Color READ = const Color(0xFFDE0A0A);
  static const Color ORANGE = const Color(0xFFF9820E);
  static const Color YELLOW = const Color(0xFFFFEB8C);
  static const Color BLUE = const Color(0xFF256CEC);
}

由於是多主題,所以要根據主題數量的多少配置對應的主題的顏色,在這裏我用list存放每一個顏色,然後根據list的index使用的主題來切換。
2.保存選擇的主題以及語言:


///SharedPreferences 本地存儲
class LocalStorage {

  static save(String key, value) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(key, value);
  }

  static get(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.get(key);
  }

  static remove(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.remove(key);
  }

  ///檢查主題是否持久化
  static Future checnLocalThemeResources() async {
    WidgetsFlutterBinding.ensureInitialized();
    await SpUtil.getInstance();
    String _localThemeLocale =
    SpUtil.getString(Config.LOCAL_THEME_LOCALE_KEY);
    int _localThemeColor = SpUtil.getInt(Config.LOCAL_THEME_COLOR_KEY);
    if (!BaseTools.isEmpty(_localThemeLocale)) {
      if(_localThemeLocale=="en"){
        GlobalThemeStyles.themeLocale = Locale('en', 'US');
      }else{
        GlobalThemeStyles.themeLocale = Locale('zh', 'CN');
      }
      GlobalStore.store.dispatch(GlobalActionCreator.changeLanguage(_localThemeLocale));
    }
    try {
      if (_localThemeColor != null) {
        GlobalStore.store.dispatch(GlobalActionCreator.changeThemeColor(_localThemeColor));
      }
    } catch (e) {}
  }
  ///持久化主題資源
  static void saveLocalThemeResources(var resources) async {
    await SpUtil.getInstance();
    if (resources != null) {
      if (resources is String) {
        SpUtil.putString(Config.LOCAL_THEME_LOCALE_KEY, resources);
      } else if (resources is int) {
        SpUtil.putInt(Config.LOCAL_THEME_COLOR_KEY, resources);
      }
    }
  }
}

其中的Config代碼:

class Config {
  ///包名
  static const APP_PACKAGE_NAME = "com.fish.local";
  ///國際化本地key
  static const LOCAL_THEME_LOCALE_KEY=APP_PACKAGE_NAME+"local_theme_locale_key";

  ///主題本地key
  static const LOCAL_THEME_COLOR_KEY=APP_PACKAGE_NAME+"local_theme_color_key";

}

3.在fish-redux的global_store中定義切換主題以及語言的全局action以及實現:
在action.dart中:

enum GlobalAction { changeThemeColor, changelanguage ,changeAtNight}

class GlobalActionCreator {
///切換主題
  static Action changeThemeColor(int i) {
    return  Action(GlobalAction.changeThemeColor,payload: i);
  }
///切換語言
  static Action changeLanguage(String language) {
    return Action(GlobalAction.changelanguage, payload: language);
  }
}

在global_store/reducer.dart中:

Reducer<GlobalState> buildReducer() {
  return asReducer(
    <Object, Reducer<GlobalState>>{
      GlobalAction.changeThemeColor: _changeThemeColor,
      GlobalAction.changelanguage: _changeLanguage,
      GlobalAction.changeAtNight: _changeAtNight,
    },
  );
}

GlobalState _changeThemeColor(GlobalState state, prefix0.Action action) {
  int _themeIndex = action.payload;
  GlobalState _globalState = state.clone();
  _globalState.theme=_themeIndex;
  //保存目前的主題
  LocalStorage.saveLocalThemeResources(_themeIndex);
  return _globalState;
}

GlobalState _changeLanguage(GlobalState state, prefix0.Action action) {
  GlobalState _globalState = state.clone();
  String _lanauage = action.payload;
  //如果還有其他語言,做相應的判斷
  if(_lanauage=="en"){
    _globalState.languageLocale = Locale('en', 'US');
  }else{
    _globalState.languageLocale  = Locale('zh', 'CN');
  }
  AppLocalizationsDelegate.delegate.load(_globalState.languageLocale );
  //保存目前的語言版本
  LocalStorage.saveLocalThemeResources(_lanauage);
  return _globalState;
}

在global_store/state.dart中:

abstract class GlobalBaseState<T extends Cloneable<T>> implements Cloneable<T> {
  int get theme;
  set theme(int theme);
  Locale get languageLocale;
  set languageLocale(Locale languageLocale);
}

class GlobalState implements GlobalBaseState<GlobalState> {
  @override
  int theme=0;
  @override
  Locale languageLocale;


  @override
  GlobalState clone() {
    // TODO: implement clone
    return GlobalState()
      ..theme = theme
      ..languageLocale = languageLocale;
  }
}

在自定義的全局路由中添加如下:

///定義一個全局的route
class AppRoute {
  static AbstractRoutes _global;
  static AbstractRoutes get global {
    if (_global == null) {
      _global = PageRoutes(
        pages: <String, Page<Object, dynamic>>{
        },
        visitor: (String path, Page<Object, dynamic> page) {
          /// 只有特定的範圍的Page才需要建立和AppStore的連接關係
          /// 滿足Page<T> T 是GlobalBaseState的之類
          if (page.isTypeof<GlobalBaseState>()) {
            /// 建立AppStore驅動PageStore的單項數據連接
            /// 1. 參數1 AppStore
            /// 2. 參數2 當 AppStore.state 變化時, PageStore.state 該如何變化
            page.connectExtraStore<GlobalState>(
              GlobalStore.store,
              (Object pagestate, GlobalState appState) {
                final GlobalBaseState p = pagestate;
                if (p.theme != appState.theme) {
                  if (pagestate is Cloneable) {
                    final Object copy = pagestate.clone();
                    final GlobalBaseState newState = copy;
                    newState.theme = appState.theme;
                    return newState;
                  }
                }
                if (p.languageLocale != appState.languageLocale) {
                  if (pagestate is Cloneable) {
                    final Object copy = pagestate.clone();
                    final GlobalBaseState newState = copy;
                    newState.languageLocale = appState.languageLocale;
                    return newState;
                  }
                }
                return pagestate;
              },
            );
          }
        },
      );
    }
    return _global;
  }
}

核心代碼是visitor中,這裏的作用是通知所有頁面刷新主題跟語言。

在main.dart中:


void main() =>LocalStorage.checnLocalThemeResources().then((e) =>runApp(MyApp()));

class MyApp extends StatefulWidget {
  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> with WidgetsBindingObserver{
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print("--" + state.toString());
    switch (state) {
      case AppLifecycleState.inactive: // 處於這種狀態的應用程序應該假設它們可能在任何時候暫停。
        break;
      case AppLifecycleState.resumed:// 應用程序可見,前臺

        break;
      case AppLifecycleState.paused: // 應用程序不可見,後臺
        break;
      case AppLifecycleState.detached:
        // TODO: Handle this case.
        break;
    }
  }



  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'FLEXlend',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        platform: TargetPlatform.iOS,
        scaffoldBackgroundColor: Colors.white,
        appBarTheme: AppBarTheme(
            iconTheme: IconThemeData(color: GlobalThemeStyles.WHITE),
            textTheme: TextTheme(
                title: TextStyle(
                    fontSize:18,
                    color: GlobalThemeStyles.WHITE))),
      ),
      localizationsDelegates: [
        AppLocalizationsDelegate(), // 我們定義的代理
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      locale: GlobalThemeStyles.themeLocale,
      supportedLocales: <Locale>[
        const Locale('en', 'US'), // 美國英語
        const Locale('zh', 'CN'), // 中文簡體
      ],
      home: AppRoute.global.buildPage(RoutePath.TEST_LIST, null),
      onGenerateRoute: (RouteSettings settings) {
        return MaterialPageRoute<Object>(builder: (BuildContext context) {
          return AppRoute.global.buildPage(settings.name, settings.arguments);
        });
      },
    );
  }
}

在main中需要注意的是,啓動函數的改變:

void main() =>LocalStorage.checnLocalThemeResources().then((e) =>runApp(MyApp()));

其中LocalStorage.checnLocalThemeResources() 是爲了在啓動時確定目前APP的語言以及主題。

最後,所有需要使用多主題以及多語言的頁面是state.dart都需要實現GlobalBaseState:


class ThemeDemoState implements GlobalBaseState<ThemeDemoState> {
  @override
  bool isAtNight;
  @override
  Locale languageLocale;
  @override
  int theme;
  @override
  ThemeDemoState clone() {
    return ThemeDemoState()..languageLocale=languageLocale..theme=theme;
  }
}
ThemeDemoState initState(Map<String, dynamic> args) {
  return ThemeDemoState();
}

在修改主題時調用:

 GlobalStore.store.dispatch(GlobalActionCreator.changeThemeColor(0));

修改語言時調用:

GlobalStore.store.dispatch(GlobalActionCreator.changeLanguage("zh"));

相關代碼地址:
https://github.com/qq1057119720/flutter_fish_local

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章