欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Flutter以兩種方式實(shí)現(xiàn)App主題切換的代碼

 更新時(shí)間:2020年04月09日 10:30:34   作者:Songlcy  
這篇文章主要介紹了Flutter以兩種方式實(shí)現(xiàn)App主題切換的代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

概述

App主題切換已經(jīng)成為了一種流行的用戶體驗(yàn),豐富了應(yīng)用整體UI視覺效果。例如,白天夜間模式切換。實(shí)現(xiàn)該功能的思想其實(shí)不難,就是將涉及主題的資源文件進(jìn)行全局替換更新。說到這里,我想你肯定能聯(lián)想到一種設(shè)計(jì)模式:觀察者模式。多種觀察對(duì)象(主題資源)來觀察當(dāng)前主題更新的行為(被觀察對(duì)象),進(jìn)行主題的更新。今天和大家分享在 Flutter 平臺(tái)上如何實(shí)現(xiàn)主題更換。

效果

實(shí)現(xiàn)流程

在 Flutter 項(xiàng)目中,MaterialApp組件為開發(fā)者提供了設(shè)置主題的api:

 const MaterialApp({
 ...
 this.theme, // 主題
 ...
 })

通過 theme 屬性,我們可以設(shè)置在MaterialApp下的主題樣式。theme 是 ThemeData 的對(duì)象實(shí)例:

ThemeData({
 
 Brightness brightness,
 MaterialColor primarySwatch,
 Color primaryColor,
 Brightness primaryColorBrightness,
 Color primaryColorLight,
 Color primaryColorDark,
 
 ...
 
 })

ThemeData 中包含了很多主題設(shè)置,我們可以選擇性的改變其中的顏色,字體等等。所以我們可以通過改變 primaryColor 來實(shí)現(xiàn)狀態(tài)欄的顏色改變。并通過Theme來獲取當(dāng)前 primaryColor 顏色值,將其賦值到其他組件上即可。在觸發(fā)主題更新行為時(shí),通知 ThemeData 的 primaryColor改變行對(duì)應(yīng)顏色值。 有了以上思路,接下來我們通過兩種方式來展示如何實(shí)現(xiàn)主題的全局更新。

主題選項(xiàng)

在實(shí)例中我們以一下主題顏色為主:

/**
 * 主題選項(xiàng)
 */
import 'package:flutter/material.dart';
 
final List<Color> 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 方式實(shí)現(xiàn)

Flutter中EventBus提供了事件總線的功能,以監(jiān)聽通知的方式進(jìn)行主體間通信。我們可以在main.dart入口文件下注冊(cè)主題修改的監(jiān)聽,通過EventBus發(fā)送通知來動(dòng)態(tài)修改 theme。核心代碼如下:

 @override
 void initState() {
 super.initState();
 Application.eventBus = new EventBus();
 themeColor = ThemeList[widget.themeIndex];
 this.registerThemeEvent();
 }
 
 /**
 * 注冊(cè)主題切換監(jiān)聽
 */
 void registerThemeEvent() {
 Application.eventBus.on<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(),
 );
 }

然后在更新主題行為的地方來發(fā)送通知刷新即可:

 changeTheme() async {
 Application.eventBus.fire(new ThemeChangeEvent(1));
 }

scoped_model 狀態(tài)管理方式實(shí)現(xiàn)

了解 React、 React Naitve 開發(fā)的朋友對(duì)狀態(tài)管理框架肯定都不陌生,例如 Redux 、Mobx、 Flux 等等。狀態(tài)框架的實(shí)現(xiàn)可以幫助我們非常輕松的控制項(xiàng)目中的狀態(tài)邏輯,使得代碼邏輯清晰易維護(hù)。Flutter 借鑒了 React 的狀態(tài)控制,同樣產(chǎn)生了一些狀態(tài)管理框架,例如 flutter_redux、scoped_model、bloc。接下來我們使用 scoped_model 的方式實(shí)現(xiàn)主題的切換。 關(guān)于 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 中,定義了對(duì)應(yīng)的主題下標(biāo),changeTheme() 方法為更改主題,并調(diào)用 notifyListeners() 進(jìn)行全局通知。

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 類似。以上代碼我們實(shí)現(xiàn)了主題的切換,細(xì)心的朋友可以發(fā)現(xiàn),我們還需要對(duì)主題進(jìn)行保存,當(dāng)下次啟動(dòng) App 時(shí),要顯示上次切換的主題。Flutter中提供了 shared_preferences 來實(shí)現(xiàn)本地持久化存儲(chǔ)。

主題持久化保存

當(dāng)進(jìn)行主題更換時(shí),我們可以對(duì)主題進(jìn)行持久化本地存儲(chǔ)

 void changeTheme(int themeIndex) async {
 _themeIndex = themeIndex;
 SharedPreferences sp = await SharedPreferences.getInstance();
 sp.setInt("themeIndex", themeIndex);
 }

然后在項(xiàng)目啟動(dòng)時(shí),取出本地存儲(chǔ)的主題下標(biāo),設(shè)置在theme上即可

void main() async {
 int themeIndex = await getTheme();
 runApp(App(themeIndex));
}
 
Future<int> 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<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(),
   );
  },
  )
 );
}

以上我們通過兩種方式來實(shí)現(xiàn)了主題的切換,實(shí)現(xiàn)思想都是通過通知的方式來觸發(fā)組件 build 進(jìn)行刷新。那么兩種方式有什么區(qū)別呢?

區(qū)別

從 print log 中,可以發(fā)現(xiàn),當(dāng)使用 eventbus 事件總線進(jìn)行切換主題刷新時(shí),_AppState 下的 build方法 和 home指向的組件界面  整體都會(huì)重新構(gòu)建。而使用scoped_model等狀態(tài)管理工具,_AppState 下的 build方法不會(huì)重新執(zhí)行,只會(huì)刷新使用到了Model的組件,但是home對(duì)應(yīng)的組件依然會(huì)重新執(zhí)行build方法進(jìn)行構(gòu)建。所以我們可以得出以下結(jié)論:

兩者方式都會(huì)導(dǎo)致 home 組件被重復(fù) build。明顯區(qū)別在于使用狀態(tài)管理工具的方式可以避免父組件 build 重構(gòu)。

源碼已上傳到 Github,詳細(xì)代碼可以查看 

EventBus 實(shí)現(xiàn)整體代碼:

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 = 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<StatefulWidget> createState() => AppState();
}
 
class AppState extends State<App> {
 
 Color themeColor;
 
 @override
 void initState() {
 super.initState();
 Application.eventBus = new EventBus();
 themeColor = ThemeList[widget.themeIndex];
 this.registerThemeEvent();
 }
 
 void registerThemeEvent() {
 Application.eventBus.on<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(),
 );
 }
 
 @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 實(shí)現(xiàn)整體代碼:

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 = 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<App> {
 @override
 void initState() {
 super.initState();
 Application.eventBus = new EventBus();
 }
 @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(),
   );
  },
  )
 );
 }
}
 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);
 }

總結(jié)

相關(guān)文章

  • Android編程之下拉菜單Spinner控件用法示例

    Android編程之下拉菜單Spinner控件用法示例

    這篇文章主要介紹了Android編程之下拉菜單Spinner控件用法,結(jié)合簡單實(shí)例形式分析了Android下拉菜單Spinner的布局與功能相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-07-07
  • android開發(fā)教程之時(shí)間對(duì)話框核心代碼

    android開發(fā)教程之時(shí)間對(duì)話框核心代碼

    這篇文章主要介紹了android的時(shí)間對(duì)話框核心代碼,需要的朋友可以參考下
    2014-04-04
  • Flutter集成到已有iOS工程的方法步驟

    Flutter集成到已有iOS工程的方法步驟

    這篇文章主要介紹了Flutter集成到已有iOS工程的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Android的支付密碼輸入框?qū)崿F(xiàn)淺析

    Android的支付密碼輸入框?qū)崿F(xiàn)淺析

    現(xiàn)在有很多的應(yīng)用都有支付密碼功能,而支付密碼的密碼輸入框確實(shí)一個(gè)很好玩的控件。雖然網(wǎng)上有很多例子,但是抽時(shí)間自己設(shè)計(jì)一個(gè)未嘗不可啊,下面來一起看看吧,有需要的朋友們可以參考學(xué)習(xí)。
    2016-09-09
  • android控件之WebView控件縮小

    android控件之WebView控件縮小

    發(fā)現(xiàn)這個(gè)控件挺好用,能自已控制進(jìn)度條,而且這個(gè)控件的功能非常壯大,先上個(gè)簡單的
    2013-05-05
  • Android自定義View實(shí)現(xiàn)QQ音樂中圓形旋轉(zhuǎn)碟子

    Android自定義View實(shí)現(xiàn)QQ音樂中圓形旋轉(zhuǎn)碟子

    這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)QQ音樂中圓形旋轉(zhuǎn)碟子,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Android使用第三方服務(wù)器Bmob實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼

    Android使用第三方服務(wù)器Bmob實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼

    這篇文章主要介紹了Android使用第三方服務(wù)器Bmob實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼的思路詳解,需要的朋友可以參考下
    2016-09-09
  • Android中post和get的提交方式【三種】

    Android中post和get的提交方式【三種】

    本文主要對(duì)Android中三種POST和GET的提交方式進(jìn)行詳細(xì)介紹。通過任何一種方式可以實(shí)現(xiàn)的功能是,從安卓手機(jī)端提交數(shù)據(jù)到服務(wù)器端,服務(wù)器端進(jìn)行判斷,并返回相應(yīng)的結(jié)果。三種方式各有利弊,實(shí)現(xiàn)效果相同,在實(shí)際的使用過程中可以根據(jù)本身的需要進(jìn)行選擇。
    2016-12-12
  • 實(shí)例探究Android開發(fā)中Fragment狀態(tài)的保存與恢復(fù)方法

    實(shí)例探究Android開發(fā)中Fragment狀態(tài)的保存與恢復(fù)方法

    這篇文章主要介紹了實(shí)例探究Android開發(fā)中Fragment狀態(tài)的保存與恢復(fù)方法,或許開發(fā)者們對(duì)Fragment的操作都比較熟悉,但onSaveInstanceState()方法并不能夠很好地保存Fragment狀態(tài),需要的朋友可以參考下
    2016-04-04
  • Android設(shè)置鬧鐘相對(duì)完善的解決方案

    Android設(shè)置鬧鐘相對(duì)完善的解決方案

    這篇文章主要為大家詳細(xì)介紹了Android設(shè)置鬧鐘相對(duì)完善的解決方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06

最新評(píng)論