在这里先安利一下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
多语言插件intl的具体使用方法参考另一篇博客:
Flutter 使用intl实现国际化
同样fish-redux使用这里也不做介绍了。
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 是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