Flutter以两种⽅式实现App主题切换的代码
概述
App主题切换已经成为了⼀种流⾏的⽤户体验,丰富了应⽤整体UI视觉效果。例如,⽩天夜间模式切换。实现该功能的思想其实不难,就是将涉及主题的资源⽂件进⾏全局替换更新。说到这⾥,我想你肯定能联想到⼀种设计模式:观察者模式。多种观察对象(主题资源)来观察当前主题更新的⾏为(被观察对象),进⾏主题的更新。今天和⼤家分享在 Flutter 平台上如何实现主题更换。
效果
实现流程
在 Flutter 项⽬中,MaterialApp组件为开发者提供了设置主题的api:
const MaterialApp({
...
this.theme, // 主题
...
})
通过 theme 属性,我们可以设置在MaterialApp下的主题样式。theme 是 ThemeData 的对象实例:
ThemeData({
Brightness brightness,
MaterialColor primarySwatch,
Color primaryColor,
Brightness primaryColorBrightness,
Color primaryColorLight,
Color primaryColorDark,
...
})
ThemeData 中包含了很多主题设置,我们可以选择性的改变其中的颜⾊,字体等等。所以我们可以通过改变 primaryColor 来实现状态栏的颜⾊改变。并通过Theme来获取当前 primaryColor 颜⾊值,将其赋值到其他组件上即可。在触发主题更新⾏为时,通知 ThemeData 的 primaryColor改变⾏对应颜⾊值。有了以上思路,接下来我们通过两种⽅式来展⽰如何实现主题的全局更新。
主题选项
在实例中我们以⼀下主题颜⾊为主:
/
**
* 主题选项
*/
import 'package:flutter/material.dart';
final List<Color> themeList = [
Colors.black,
Colors.pink,
Colors.amber,
<,
Colors.blue,
Colors.lightBlue,
Colors.purple,
Colors.deepPurple,
Colors.indigo,
Colors.brown,
<,
Colors.blueGrey
];
EventBus ⽅式实现
Flutter中EventBus提供了事件总线的功能,以监听通知的⽅式进⾏主体间通信。我们可以在main.dart⼊⼝⽂件下注册主题修改的监听,通过EventBus发送通知来动态修改 theme。核⼼代码如下:
@override
void initState() {
super.initState();
Application.eventBus = new EventBus();
themeColor = ThemeList[widget.themeIndex];
}
/**
* 注册主题切换监听
*/
void registerThemeEvent() {
<ThemeChangeEvent>().listen((ThemeChangeEvent onData)=> this.changeTheme(onData));
}
/**
* 刷新主题样式
*/
void changeTheme(ThemeChangeEvent onData) {
setState(() {
themeColor = themeList[onData.themeIndex];
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: themeColor
),
home: HomePage(),
);
}
然后在更新主题⾏为的地⽅来发送通知刷新即可:
changeTheme() async {
Application.eventBus.fire(new ThemeChangeEvent(1));
}
scoped_model 状态管理⽅式实现
1. ⾸先定义主题 Model
/**
* 主题Model
* Create by Songlcy
*/
import 'package:scoped_model/scoped_model.dart';
abstract class ThemeStateModel extends Model {
int _themeIndex;
get themeIndex => _themeIndex;
void changeTheme(int themeIndex) async {
_themeIndex = themeIndex;
notifyListeners();
}
}
在 ThemeStateModel 中,定义了对应的主题下标,changeTheme() ⽅法为更改主题,并调⽤ notifyListeners() 进⾏全局通知。
2. 注⼊Model
@override
Widget build(BuildContext context) {
return ScopedModel<MainStateModel>(
model: MainStateModel(),
child: ScopedModelDescendant<MainStateModel>(
builder: (context, child, model) {
return MaterialApp(
theme: ThemeData(
primaryColor: themeList[model.themeIndex]
),
home: HomePage(),
);
},
)
);
}
3. 修改主题
changeTheme(int index) async {
int themeIndex = index;
MainStateModel().of(context).changeTheme(themeIndex);
}
可以看到,使⽤scoped_model 的⽅式同样⽐较简单,思路和 EventBus 类似。以上代码我们实现了主题的切换,细⼼的朋友可以发现,我们还需要对主题进⾏保存,当下次启动 App 时,要显⽰上次切换的主题。Flutter中提供了 shared_preferences 来实现本地持久化存储。
主题持久化保存
当进⾏主题更换时,我们可以对主题进⾏持久化本地存储
void changeTheme(int themeIndex) async {
_themeIndex = themeIndex;
SharedPreferences sp = Instance();
sp.setInt("themeIndex", themeIndex);
}
然后在项⽬启动时,取出本地存储的主题下标,设置在theme上即可
void main() async {
int themeIndex = await getTheme();
runApp(App(themeIndex));
}
Future<int> getTheme() async {
SharedPreferences sp = Instance();
int themeIndex = sp.getInt("themeIndex");
if(themeIndex != null) {
return themeIndex;
}
return 0;
}
@override
Widget build(BuildContext context) {
return ScopedModel<MainStateModel>(
model: mainStateModel,
child: ScopedModelDescendant<MainStateModel>(
builder: (context, child, model) {
return MaterialApp(
theme: ThemeData(
primaryColor: themeList[model.themeIndex != null ? model.themeIndex : widget.themeIndex]
),
home: HomePage(),
);
},
)
);
}
以上我们通过两种⽅式来实现了主题的切换,实现思想都是通过通知的⽅式来触发组件 build 进⾏刷新。那么两种⽅式有什么区别呢?
区别
从 print log 中,可以发现,当使⽤ eventbus 事件总线进⾏切换主题刷新时,_AppState 下的 build⽅法和 home指向的组件界⾯整体都会重新构建。⽽使⽤scoped_model等状态管理⼯具,_AppState 下的 build⽅法不会重新执⾏,只会刷新使⽤到了Model的组件,但是home对应的组件依然会重新执⾏build⽅法进⾏构建。所以我们可以得出以下结论:
两者⽅式都会导致 home 组件被重复 build。明显区别在于使⽤状态管理⼯具的⽅式可以避免⽗组件 build 重构。
源码已上传到 Github,详细代码可以查看
EventBus 实现整体代码:
import 'package:flutter/material.dart';
import 'package:event_bus/event_bus.dart';
import './config/application.dart';
import './pages/home_page.dart';
import './events/theme_event.dart';
import './constants/theme.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
int themeIndex = await getDefaultTheme();
runApp(App(themeIndex));
}
Future<int> getDefaultTheme() async {
/
/ 从shared_preferences中获取上次切换的主题
SharedPreferences sp = Instance();
int themeIndex = sp.getInt("themeIndex");
print(themeIndex);
if(themeIndex != null) {
return themeIndex;
}
return 0;
}
class App extends StatefulWidget {
int themeIndex;
App(this.themeIndex);
@override
State<StatefulWidget> createState() => AppState();
}
class AppState extends State<App> {
Color themeColor;
@override
void initState() {
super.initState();
Application.eventBus = new EventBus();
themeColor = ThemeList[widget.themeIndex];
}
void registerThemeEvent() {
<ThemeChangeEvent>().listen((ThemeChangeEvent onData)=> this.changeTheme(onData)); }
void changeTheme(ThemeChangeEvent onData) {
setState(() {
themeColor = ThemeList[onData.themeIndex];
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: themeColor
),
flutter开发app
home: HomePage(),
);
}
@override
void dispose() {
super.dispose();
Application.eventBus.destroy();
}
}
changeTheme() async {
SharedPreferences sp = Instance();
sp.setInt("themeIndex", 1);
Application.eventBus.fire(new ThemeChangeEvent(1));
}
scoped_model 实现整体代码:
import 'package:flutter/material.dart';
import 'package:event_bus/event_bus.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
import './config/application.dart';
import './pages/home_page.dart';
import './constants/theme.dart';
import './models/state_model/main_model.dart';
void main() async {
int themeIndex = await getTheme();
runApp(App(themeIndex));
}
Future<int> getTheme() async {
SharedPreferences sp = Instance();
int themeIndex = sp.getInt("themeIndex");
if(themeIndex != null) {
return themeIndex;
}
return 0;
}
class App extends StatefulWidget {
final int themeIndex;
App(this.themeIndex);
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
@override
void initState() {
super.initState();
Application.eventBus = new EventBus();
}
@override
Widget build(BuildContext context) {
return ScopedModel<MainStateModel>(
model: MainStateModel(),
child: ScopedModelDescendant<MainStateModel>(

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。