android跳转flutter 主题,Flutter以两种方式实现App主题切换的代码

概述

App主题切换已经成为了一种流行的用户体验,丰富了应用整体UI视觉效果。例如,白天夜间模式切换。实现该功能的思想其实不难,就是将涉及主题的资源文件进行全局替换更新。说到这里,我想你肯定能联想到一种设计模式:观察者模式。多种观察对象(主题资源)来观察当前主题更新的行为(被观察对象),进行主题的更新。今天和大家分享在 Flutter 平台上如何实现主题更换。

效果

51e383f8a513e8040a5c7bcd905afa97.gif

实现流程

在 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 themeList = [

Colors.black,

Colors.red,

Colors.teal,

Colors.pink,

Colors.amber,

Colors.orange,

Colors.green,

Colors.blue,

Colors.lightBlue,

Colors.purple,

Colors.deepPurple,

Colors.indigo,

Colors.cyan,

Colors.brown,

Colors.grey,

Colors.blueGrey

];

EventBus 方式实现

Flutter中EventBus提供了事件总线的功能,以监听通知的方式进行主体间通信。我们可以在main.dart入口文件下注册主题修改的监听,通过EventBus发送通知来动态修改 theme。核心代码如下:

@override

void initState() {

super.initState();

Application.eventBus = new EventBus();

themeColor = ThemeList[widget.themeIndex];

this.registerThemeEvent();

}

/**

* 注册主题切换监听

*/

void registerThemeEvent() {

Application.eventBus.on().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 状态管理方式实现

了解 React、 React Naitve 开发的朋友对状态管理框架肯定都不陌生,例如 Redux 、Mobx、 Flux 等等。状态框架的实现可以帮助我们非常轻松的控制项目中的状态逻辑,使得代码逻辑清晰易维护。Flutter 借鉴了 React 的状态控制,同样产生了一些状态管理框架,例如 flutter_redux、scoped_model、bloc。接下来我们使用 scoped_model 的方式实现主题的切换。 关于 scoped_model 的使用方式可以参考pub仓库提供的文档:https://pub.dartlang.org/packages/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(

model: MainStateModel(),

child: ScopedModelDescendant(

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 = await SharedPreferences.getInstance();

sp.setInt("themeIndex", themeIndex);

}

然后在项目启动时,取出本地存储的主题下标,设置在theme上即可

void main() async {

int themeIndex = await getTheme();

runApp(App(themeIndex));

}

Future getTheme() async {

SharedPreferences sp = await SharedPreferences.getInstance();

int themeIndex = sp.getInt("themeIndex");

if(themeIndex != null) {

return themeIndex;

}

return 0;

}

@override

Widget build(BuildContext context) {

return ScopedModel(

model: mainStateModel,

child: ScopedModelDescendant(

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 getDefaultTheme() async {

// 从shared_preferences中获取上次切换的主题

SharedPreferences sp = await SharedPreferences.getInstance();

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 createState() => AppState();

}

class AppState extends State {

Color themeColor;

@override

void initState() {

super.initState();

Application.eventBus = new EventBus();

themeColor = ThemeList[widget.themeIndex];

this.registerThemeEvent();

}

void registerThemeEvent() {

Application.eventBus.on().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(),

);

}

@override

void dispose() {

super.dispose();

Application.eventBus.destroy();

}

}

changeTheme() async {

SharedPreferences sp = await SharedPreferences.getInstance();

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 getTheme() async {

SharedPreferences sp = await SharedPreferences.getInstance();

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 {

@override

void initState() {

super.initState();

Application.eventBus = new EventBus();

}

@override

Widget build(BuildContext context) {

return ScopedModel(

model: MainStateModel(),

child: ScopedModelDescendant(

builder: (context, child, model) {

return MaterialApp(

theme: ThemeData(

primaryColor: ThemeList[model.themeIndex != null ? model.themeIndex : widget.themeIndex]

),

home: HomePage(),

);

},

)

);

}

}

changeTheme() async {

int themeIndex = MainStateModel().of(context).themeIndex == 0 ? 1 : 0;

SharedPreferences sp = await SharedPreferences.getInstance();

sp.setInt("themeIndex", themeIndex);

MainStateModel().of(context).changeTheme(themeIndex);

}

总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值