久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

07 | 狀態(tài)管理:Flutter 狀態(tài)管理及對比選型

 zybingliu 2021-09-30

上一課時我詳細(xì)介紹了有/無狀態(tài)組件的應(yīng)用設(shè)計,但是在設(shè)計過程中,還缺乏一個對狀態(tài)管理的考慮,。本課時介紹狀態(tài)管理設(shè)計的必要性,,以及一些常見的狀態(tài)管理技術(shù)對比,最后再著重通過 Provider 來優(yōu)化前一課時中的例子,。

狀態(tài)管理場景

上一課時的例子中,,只涉及一個有狀態(tài)的組件 article_like_bar ,接下來我們需要實現(xiàn)另外一個詳情頁面,,并且在詳情頁面中也需要一個點(diǎn)贊功能,,具體的界面效果可以參考動圖 1 (為了界面更好,我在上一課時的基礎(chǔ)上增加了一些樣式),。

20200620_110314.gif
圖 1 增加二級點(diǎn)贊詳情頁面效果

在上面的動圖例子中,,你是否發(fā)現(xiàn)了一個問題?第一個頁面的點(diǎn)贊數(shù)與第二個頁面的點(diǎn)贊數(shù)并不同步,。在實際項目開發(fā)過中,,需求方希望二級詳情頁面的點(diǎn)贊數(shù)能與第一個頁面的點(diǎn)贊數(shù)同步。

如果不引入新的技術(shù)方案,,能想到的辦法就是將該狀態(tài)進(jìn)行提升,,放到其共同的父節(jié)點(diǎn)上,然后將父節(jié)點(diǎn)設(shè)計為有狀態(tài)組件,,并提供修改狀態(tài)的方法給到子組件,。可以用圖 2 來表示,。

Drawing 1.png

圖 2 狀態(tài)提升共享方式

上面的方式是可以做到這點(diǎn),,但是你有沒有發(fā)現(xiàn),只因為一個點(diǎn)贊行為,,就需要將兩個頁面的所有組件(靜態(tài)組件和動圖組件)進(jìn)行重新 build ,,成本實在太高,這也違背了我們上一課時的組件設(shè)計原則(盡可能減少動態(tài)組件下的靜態(tài)組件),。為了更好地解決這個問題,,我們就需要引入一些狀態(tài)管理的方法,下面就介紹一些常見的技術(shù)方案,,同時做一個對比,。

狀態(tài)選型對比

狀態(tài)管理技術(shù)不少于 10 種,但是為了高效,,我只介紹其中比較核心的三個,,第一個是原生所使用的 InheritedWidget ;第二個是相對前端同學(xué)比較熟悉的 Redux 技術(shù),;最后一個則是我們推薦使用的技術(shù) Provider ,。

InheritedWidget

InheritedWidget 核心原理和狀態(tài)提升原理一致,,將 likeNum 提升到根節(jié)點(diǎn),但不需要一層層地將變量傳遞下去,,只需要在根節(jié)點(diǎn)聲明即可,。

現(xiàn)在我們有一個頁面,頁面下有兩個組件,,兩個組件都需要用同一個名字,,并且第二個組件的名字可以點(diǎn)擊切換隨機(jī)名字,而切換以后需要及時更新第一個組件中的名字,。頁面效果如圖 3 所示,。

Drawing 3.png
圖 3 多組件狀態(tài)共享效果

按照上面介紹的例子以及上一課時的知識點(diǎn),畫一個簡單的組件樹,,并且附帶上需要的狀態(tài)屬性,,如圖 4 所示。

Drawing 4.png
圖 4 InheritedWidget 組件設(shè)計

  • 首先創(chuàng)建一個根結(jié)點(diǎn)為一個有狀態(tài)組件 name_game,;

  • name_game 為一個有狀態(tài)類,狀態(tài)屬性為 name,,并帶有 changName 的狀態(tài)修改方法,;

  • 創(chuàng)建一個狀態(tài)管理類組件 NameInheritedWidget ;

  • 創(chuàng)建 NameInheritedWidget 的三個子組件,,分別為 welcome(顯示歡迎 name ),、random_name(顯示 name ,并且有點(diǎn)擊切換隨機(jī) name 操作)和 other_widgets ,。

對于上面的結(jié)構(gòu),,肯定有很多同學(xué)比較疑惑,other_widgets 并沒有使用這個 name 狀態(tài),,為什么要在 NameInheritedWidget 下呢,?

帶著這樣的疑惑,我們先來看下 name_game 核心代碼(為了在專欄中更簡潔,,我省去了部分代碼,,完整代碼大家可以參考文章下的 github 代碼地址)。

復(fù)制代碼
  1. /// 隨機(jī)名字游戲組件狀態(tài)管理類
  2. class NameGameState extends State<NameGame> {
  3. /// name 狀態(tài)
  4. String name;
  5. /// 構(gòu)造函數(shù)參數(shù),,避免父組件狀態(tài)變化,,而引起的子組件的重 build 操作
  6. Widget child;
  7. /// 修改當(dāng)前名字
  8. void changeName() {
  9. List<String> nameList = ['flutter one', 'flutter two', 'flutter three'];
  10. int pos = Random().nextInt(3);
  11. setState(() {
  12. name = nameList[pos];
  13. });
  14. }
  15. @override
  16. void initState() {
  17. setState(() {
  18. name = 'test flutter';
  19. });
  20. super.initState();
  21. }
  22. /// 構(gòu)造函數(shù)
  23. NameGameState()
  24. {
  25. child = Column (
  26. children: <Widget>[
  27. Welcome(),
  28. RandomName(),
  29. TestOther(),
  30. ]
  31. );
  32. }
  33. @override
  34. Widget build(BuildContext context) {
  35. return Column(
  36. children: <Widget>[
  37. NameInheritedWidget(
  38. child: child,
  39. onNameChange: changeName,
  40. name: name
  41. ),
  42. ],
  43. );
  44. }
  45. }

上面代碼中,定義狀態(tài)屬性 name ,,并創(chuàng)建了可以修改 state 的 changeName 方法,。接下來在 build 中使用 NameInheritedWidget 這個組件(該組件可以理解為前端所說的高階組件,也就是通過將組件作為參數(shù)傳遞進(jìn)該組件,,并返回一個新的組件的功能組件),,這個組件包裹了兩個需要狀態(tài) name 的組件( Welcome 和 RandomName )以及一個不需要狀態(tài)的 TestOther,。

上面代碼中還有一個比較特殊的地方,就是將 child 作為了 state ,,在構(gòu)造函數(shù)中進(jìn)行了定義,,并將該組件的所有子組件都包含在了 child 中。具體什么原因,,大家可以繼續(xù)往下學(xué)習(xí),。

接下來我們看一下 NameInheritedWidget 的實現(xiàn)邏輯,代碼如下:

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. /// 定義一個name共享父組件
  3. class NameInheritedWidget extends InheritedWidget {
  4. /// 共享狀態(tài)
  5. final String name;
  6. /// 修改共享狀態(tài)方法
  7. final Function onNameChange;
  8. /// 構(gòu)造函數(shù)
  9. NameInheritedWidget({
  10. Key key,
  11. @required Widget child,
  12. @required this.name,
  13. @required this.onNameChange,
  14. }) : super(key: key, child: child);
  15. @override
  16. bool updateShouldNotify(NameInheritedWidget old) =>
  17. name != old.name;
  18. }

主要是接受兩個參數(shù),, name 和 onNameChange 方法,,并且有一個判斷函數(shù) updateShouldNotify 。前面兩個參數(shù)不用介紹,,關(guān)鍵在于 updateShouldNotify ,,這個判斷函數(shù)的作用就是上面大家的疑惑點(diǎn)。
如果將 TestOther 不作為該子組件,,那么根據(jù)我們之前了解到的知識點(diǎn),,由于 setState 會觸發(fā)父組件 NameGame 的更新,而子組件會因為父組件的更新,,則會引發(fā)執(zhí)行 build 操作,。

如果 TestOther 是 NameInheritedWidget 的子組件,那么在執(zhí)行 setState 后,,NameInheritedWidget 會判斷狀態(tài)是否有狀態(tài)變化,,還會判斷子組件是否有依賴該 name 狀態(tài),從而就保證了兩點(diǎn):

  1. 狀態(tài)變化時,,如果未使用該狀態(tài)子組件,,則不會發(fā)生 build;

  2. 使用了該狀態(tài)組件,,如果組件的狀態(tài)沒有發(fā)生變化,,也不會發(fā)生 build。

這兩點(diǎn)就非常好地保護(hù)了我們剛開始提到的問題,,因為有狀態(tài)父組件的更新,,而導(dǎo)致全部子節(jié)點(diǎn)的 build 操作。這里要非常注意,,需要使用 NameGameState 方法來封裝組件,,如果該子組件直接寫在 build 中的 child 方法中,就無法利用 NameInheritedWidget 優(yōu)點(diǎn),,這點(diǎn)大家要特別注意,。

最后我們再來看下子組件如何利用 name 和 onNameChange 這兩個值,我們可以看下 RandomName 組件,,代碼如下:

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. import 'package:two_you/inherited_widget/name_inherited_widget.dart';
  3. /// 隨機(jī)展示人名
  4. class RandomName extends StatelessWidget {
  5. /// 有狀態(tài)類返回組件信息
  6. @override
  7. Widget build(BuildContext context) {
  8. final String name = (
  9. context.inheritFromWidgetOfExactType(NameInheritedWidget)
  10. as NameInheritedWidget).name;
  11. final Function changeName = (
  12. context.inheritFromWidgetOfExactType(NameInheritedWidget)
  13. as NameInheritedWidget).onNameChange;
  14. return FlatButton(
  15. child: Text(name),
  16. onPressed: () => changeName(),
  17. );
  18. }
  19. }

上面代碼中可以看到,,是通過以下方式來獲得 InheritedWidget 對象中的方法和屬性,。

復(fù)制代碼
  1. context.inheritFromWidgetOfExactType(NameInheritedWidget) as NameInheritedWidget)

總結(jié)下 InheritedWidget 實現(xiàn)狀態(tài)管理的要點(diǎn):

  1. 狀態(tài)提升,將需要共享的狀態(tài)提升到共同且最近的一個父節(jié)點(diǎn),,并使用 InheritedWidget 來管理,;

  2. 該父節(jié)點(diǎn)上,將所有子節(jié)點(diǎn)作為該節(jié)點(diǎn)狀態(tài)管理類的一個構(gòu)造函數(shù)參數(shù),,并且傳遞給 InheritedWidget,;

  3. 子節(jié)點(diǎn)通過 inheritFromWidgetOfExactType 的方法來獲取狀態(tài)管理類 InheritedWidget 中的屬性以及方法。

Redux

由于 Redux 在前端是一個比較常用的狀態(tài)管理技術(shù)解決方案,,因此這里簡單介紹一下,,不過在 Flutter 中 ,Redux 并非第一選擇,。Redux 核心思想是單向數(shù)據(jù)流架構(gòu),,將所有的狀態(tài)存儲在 store 中,所有數(shù)據(jù)改變都是通過 Action ,,然后 Action 觸發(fā) store 存儲,,store 變化觸發(fā)所有應(yīng)用該狀態(tài)的組件的 build 操作。為了實現(xiàn)效果,,我們也同樣使用上面的例子,,步驟如下:

  1. 因為是第三方庫,因此需要在 pubspec.yaml 增加依賴,;

  2. 實現(xiàn) state 管理中心;

  3. 創(chuàng)建相應(yīng)的 Action ,,觸發(fā)狀態(tài)變化,;

  4. 創(chuàng)建相應(yīng)的 reduce;

  5. 將狀態(tài)添加到 store 中,,并放在 APP 最頂層,。

接下來我們一步步實現(xiàn)代碼邏輯。

這里單獨(dú)創(chuàng)建一個目錄 states ,,用于狀態(tài)管理,,其次在 states 目錄中創(chuàng)建 name_state.dart ,并實現(xiàn)其中的代碼如下,,創(chuàng)建相應(yīng)的 state 以及 Action,。

復(fù)制代碼
  1. import 'dart:math';
  2. /// name 狀態(tài)管理類
  3. class NameStates {
  4. final String _name;
  5. /// getter 方法獲取name
  6. get name => _name;
  7. /// 構(gòu)造函數(shù)
  8. NameStates(this._name);
  9. /// 初始設(shè)置
  10. NameStates.initState() : _name = 'test flutter 1';
  11. }
  12. /// 定義 name state 對應(yīng)的狀態(tài)修改 action
  13. ///
  14. /// [NameActions.changeName] 為修改當(dāng)前 name
  15. enum NameActions {
  16. /// 修改 name 的 state
  17. changeName
  18. }

實現(xiàn)對應(yīng)的 Action 方法。

復(fù)制代碼
  1. /// 修改當(dāng)前name,,隨機(jī)選取一個
  2. NameStates changeName() {
  3. List<String> nameList = ['flutter one', 'flutter two', 'flutter three'];
  4. int pos = Random().nextInt(3);
  5. return NameStates(nameList[pos]);
  6. }

在 reducer 中增加對應(yīng) Action 的判斷,。

復(fù)制代碼
  1. /// reducer 方法,觸發(fā)組件更新
  2. NameStates reducer(NameStates state, action){
  3. if (action == NameActions.changeName) {
  4. return changeName();
  5. }
  6. return state;
  7. }

上面就完成了整個 state 類管理,,這點(diǎn)和前端的 reducer 實現(xiàn)完全一致,。接下來我們看下,,在 APP 底層創(chuàng)建的代碼。

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_redux/flutter_redux.dart';
  3. import 'package:redux/redux.dart';
  4. import 'package:two_you/pages/name_game.dart';
  5. import 'package:two_you/states/name_states.dart';
  6. /// APP 核心入口文件
  7. void main() {
  8. final store =
  9. Store<NameStates>(reducer, initialState: NameStates.initState());
  10. runApp(MyApp(store));
  11. }
  12. /// MyApp 核心入口界面
  13. class MyApp extends StatelessWidget {
  14. /// 初始
  15. final Store<NameStates> store;
  16. /// 構(gòu)造函數(shù)
  17. MyApp(this.store);
  18. // This widget is the root of your application.
  19. @override
  20. Widget build(BuildContext context) {
  21. return StoreProvider<NameStates>(
  22. store: store,
  23. child: MaterialApp(
  24. title: 'Two You', // APP 名字
  25. debugShowCheckedModeBanner: false,
  26. theme: ThemeData(
  27. primarySwatch: Colors.blue, // APP 主題
  28. ),
  29. home: Scaffold(
  30. appBar: AppBar(
  31. title: Text('Two You'), // 頁面名字
  32. ),
  33. body: Center(
  34. //child: HomePage(),
  35. child: NameGame(store: store),
  36. ))),
  37. );
  38. }
  39. }

在 main 函數(shù)中創(chuàng)建 store 對象并執(zhí)行初始化,,然后在具體需要使用 store 的方法中使用如下代碼規(guī)則:

復(fù)制代碼
  1. return StoreProvider<NameStates>(
  2. store: store,
  3. child: (具體的組件,,可以直接使用 store 變量),
  4. )

子組件如果需要使用 store ,也需要在子組件中聲明 store 變量作為組件參數(shù),,我們看下 RandomName 組件內(nèi)的使用和實現(xiàn),。

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_redux/flutter_redux.dart';
  3. import 'package:redux/redux.dart';
  4. import 'package:two_you/states/name_states.dart';
  5. /// 隨機(jī)展示人名
  6. class RandomName extends StatelessWidget {
  7. /// store
  8. final Store store;
  9. /// 構(gòu)造函數(shù)
  10. RandomName({Key key, this.store}) : super(key: key);
  11. /// 有狀態(tài)類返回組件信息
  12. @override
  13. Widget build(BuildContext context) {
  14. print('random name build');
  15. return StoreConnector<NameStates,String>(
  16. converter: (store) => store.state.name.toString(),
  17. builder: (context, name) {
  18. return StoreConnector<NameStates,VoidCallback>(
  19. converter: (store) {
  20. return () => store.dispatch(NameActions.changeName);
  21. },
  22. builder: (context, callback) {
  23. return FlatButton(
  24. child: Text(name),
  25. onPressed: () => callback(),
  26. );
  27. }
  28. );
  29. },
  30. );
  31. }
  32. }

這種方式就需要層層傳遞這個 store ,從而會顯得代碼非常臃腫,,特別是上面代碼中的 19 行和 22 行,。你會發(fā)現(xiàn),如果需要的 Action 越多,,StoreConnector 的層級就越深,,你就會陷入深深的代碼嵌套中。

當(dāng)然使用 redux ,,并不會因為父組件的更新而導(dǎo)致子組件的 build 問題,,其他部分詳細(xì)的代碼,大家可參考 github 源碼,。

Provider

最后我們來看下官方推薦的技術(shù)方案 Provider ,,開發(fā)過程比較簡單,分為三步:

  1. 創(chuàng)建狀態(tài)管理類 name_model ,,創(chuàng)建對應(yīng)的狀態(tài) name 以及其修改 name 的方法 changeName,;

  2. 在 name_game 中增加 provider 的支持,并將相應(yīng)需要共享的組件使用 provider 進(jìn)行封裝,,監(jiān)聽數(shù)據(jù)變化,;

  3. 在子組件中獲取 provider 的 name 數(shù)據(jù)以及 changeName 方法,在相應(yīng)的點(diǎn)擊部分觸發(fā) changeName 事件,。

在使用 Provider 來實現(xiàn)狀態(tài)管理,,我們需要創(chuàng)建一個 model 文件夾,放入對應(yīng)的狀態(tài)類 name_model ,,代碼實現(xiàn)如下:

復(fù)制代碼
  1. import 'dart:math';
  2. import 'package:flutter/material.dart';
  3. /// name狀態(tài)管理模塊
  4. class NameModel with ChangeNotifier {
  5. /// 聲明私有變量
  6. String _name = 'test flutter';
  7. /// 設(shè)置get方法
  8. String get value => _name;
  9. /// 修改當(dāng)前name,,隨機(jī)選取一個
  10. void changeName() {
  11. List<String> nameList = ['flutter one', 'flutter two', 'flutter three'];
  12. int pos = Random().nextInt(3);
  13. if(_name != nameList[pos]) {
  14. _name = nameList[pos];
  15. notifyListeners();
  16. }
  17. }
  18. }

在第 6 行代碼中,使用了一個 Dart 的 with 關(guān)鍵詞,,這個用法是表示 NameModel 可以直接調(diào)用 ChangeNotifier 的方法,,比如第 15 行的代碼就是調(diào)用了 ChangeNotifier 類中的方法。上面代碼中,,在 changeName 中設(shè)置完狀態(tài)屬性 _name 以后,,通過 ChangeNotifier 通知監(jiān)聽方。為了性能優(yōu)化,,在第 18 到第 21 行進(jìn)行了判斷,,避免屬性未改變而觸發(fā) build 操作,。接下來看一下,在 name_game 中是如何監(jiān)聽數(shù)據(jù)變化,,代碼實現(xiàn)如下:

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. import 'package:two_you/model/name_model.dart';
  4. import 'package:two_you/widgets/name_game/random_name.dart';
  5. import 'package:two_you/widgets/name_game/test_other.dart';
  6. import 'package:two_you/widgets/name_game/welcome.dart';
  7. /// 測試隨機(jī)名字游戲組件
  8. class NameGame extends StatelessWidget {
  9. /// 設(shè)置狀態(tài) name
  10. final name = NameModel();
  11. @override
  12. Widget build(BuildContext context) {
  13. return Column(
  14. children: <Widget>[
  15. Provider<String>.value(
  16. child: ChangeNotifierProvider.value(
  17. value: name,
  18. child: Column(
  19. children: <Widget>[
  20. Welcome(),
  21. RandomName(),
  22. ],
  23. ),
  24. ),
  25. ),
  26. TestOther(),
  27. ],
  28. );
  29. }
  30. }

上述代碼中,,第 13 行獲取狀態(tài)屬性 name ,在 build 邏輯中使用 Provider.value 來封裝需要共享的組件,,String 為 name 相應(yīng)的字段類型,。并且使用 ChangeNotifierProvider 來接受監(jiān)聽數(shù)據(jù)變化,當(dāng)數(shù)據(jù)發(fā)生變化時則觸發(fā)子組件的 build ,。

最后我們再來看其中的一個子組件 RandomName ,,在 RandomName 中展示 name 字段,并且有一個按鈕觸發(fā) changeName 操作,,代碼實現(xiàn)如下,。

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. import 'package:two_you/model/name_model.dart';
  4. /// 隨機(jī)展示人名
  5. class RandomName extends StatelessWidget {
  6. /// 有狀態(tài)類返回組件信息
  7. @override
  8. Widget build(BuildContext context) {
  9. final _name = Provider.of<NameModel>(context);
  10. print('random name build');
  11. return FlatButton(
  12. child: Text(_name.value),
  13. onPressed: () => _name.changeName(),
  14. );
  15. }
  16. }

第 11 行通過 Provider.of(context) 方式,獲得根節(jié)點(diǎn) NameModel 的句柄,,然后通過 NameModel 的 value 獲得狀態(tài) name 的值,,其次使用 _name.changeName 執(zhí)行 NameModel 的方法,觸發(fā) name 狀態(tài)值的修改,,從而再通過 ChangeNotifier 通知到兩個組件 welcome 和 random_name ,。

以上就完成了整個 Provider 的實現(xiàn)邏輯,相對其他兩種技術(shù)方案,,則更簡潔一些,。

三者的對比

上面三種技術(shù)方案,在同頁面組件共享都沒有任何問題,,在性能方面也都解決了組件更新避免全局子組件的更新問題,。但是 InheritedWidget 在多頁面間數(shù)據(jù)共享比較麻煩(因為需要一個共同的父節(jié)點(diǎn),對于多個頁面來說沒有共同的父節(jié)點(diǎn)這個概念),,這點(diǎn)對于 Redux 和 Provider 則較為簡單。其次由于 Redux 容易陷入無限的深度嵌套,,因此也不建議使用,。所以本專欄推薦使用 Provider 技術(shù)方案,使用方式較為簡單,,其次也不會帶來其他負(fù)面的影響,。

本課時一開始就介紹了關(guān)于多頁面內(nèi)容共享引起的問題,從而思考狀態(tài)管理的技術(shù)方案,,那么通過技術(shù)對比,我們選擇了 Provider ,,接下來我使用 Provider 來完善上一課時中的例子,。

創(chuàng)建 like_num_model

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. /// name狀態(tài)管理模塊
  3. class LikeNumModel with ChangeNotifier {
  4. /// 聲明私有變量
  5. int _likeNum = 0;
  6. /// 設(shè)置get方法
  7. int get value => _likeNum;
  8. /// 修改當(dāng)前name,,隨機(jī)選取一個
  9. void like() {
  10. _likeNum++;
  11. notifyListeners();
  12. }
  13. }

由于每次都會自增,因此在 like 函數(shù)中無須判斷是否 likeNum 狀態(tài)有變化,,只要自增了 likeNum 狀態(tài)后通知監(jiān)聽方即可。

main 函數(shù)創(chuàng)建監(jiān)聽組件

由于涉及兩個頁面,并不是兩個組件,,因此這里需要將狀態(tài)提升到 main 函數(shù)中,,mian 組件的實現(xiàn)如下:

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. import 'package:two_you/model/like_num_model.dart';
  4. import 'package:two_you/pages/home_page.dart';
  5. /// APP 核心入口文件
  6. void main() {
  7. runApp(MyApp());
  8. }
  9. /// MyApp 核心入口界面
  10. class MyApp extends StatelessWidget {
  11. /// 創(chuàng)建 like model
  12. final likeNumModel = LikeNumModel();
  13. // This widget is the root of your application.
  14. @override
  15. Widget build(BuildContext context) {
  16. return Provider<int>.value(
  17. child: ChangeNotifierProvider.value(
  18. value: likeNumModel,
  19. child: MaterialApp(
  20. title: 'Two You', // APP 名字
  21. debugShowCheckedModeBanner: false,
  22. theme: ThemeData(
  23. primarySwatch: Colors.blue, // APP 主題
  24. ),
  25. home: Scaffold(
  26. appBar: AppBar(
  27. title: Text('Two You'), // 頁面名字
  28. ),
  29. body: Center(
  30. child: HomePage(),
  31. ))),
  32. ),
  33. );
  34. }
  35. }

上述代碼第 16 行,,創(chuàng)建了狀態(tài)管理類的對象,并通過 Provider.value 和 ChangeNotifierProvider.value 來封裝組件 HomePage ,由于 ArticlePage 也是在頁面組件中的 MaterialApp 組件下,,因此都可以通過 context 獲取 likeNumModel 句柄,。

使用 likeNumModel

使用 Provider 的好處就在于,不使用的部分完全不需要修改,只需要在使用該狀態(tài)的地方修改即可,。由于 likeNumModel 只在 article_detail_like 和 article_like_bar 中使用,因此修改這兩個組件即可。

article_like_bar 代碼如下:

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. import 'package:two_you/model/like_num_model.dart';
  4. import 'package:two_you/styles/text_syles.dart';
  5. /// 帖子文章的贊組件
  6. ///
  7. /// 包括點(diǎn)贊組件 icon ,,以及組件點(diǎn)擊效果
  8. /// 需要外部參數(shù)[likeNum],點(diǎn)贊數(shù)量
  9. class ArticleLikeBar extends StatelessWidget {
  10. /// 有狀態(tài)類返回組件信息
  11. @override
  12. Widget build(BuildContext context) {
  13. final likeNumModel = Provider.of<LikeNumModel>(context);
  14. return Row(
  15. mainAxisAlignment: MainAxisAlignment.center,
  16. children: <Widget>[
  17. FlatButton(
  18. child: Row(
  19. children: <Widget>[
  20. Icon(Icons.thumb_up, color: Colors.grey, size: 18),
  21. Padding(padding: EdgeInsets.only(left: 10)),
  22. Text(
  23. '${likeModel.value}',
  24. style: TextStyles.commonStyle(),
  25. ),
  26. ],
  27. ),
  28. onPressed: () => likeNumModel.like(),
  29. ),
  30. ],
  31. );
  32. }
  33. }

在第 15 行獲取操作句柄,,然后在第 26 行獲取屬性 likeNum ,, 在第 31 行執(zhí)行 likeNumModel 執(zhí)行 like 操作,。

article_detail_like 代碼如下:

復(fù)制代碼
  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. import 'package:two_you/model/like_num_model.dart';
  4. import 'package:two_you/styles/text_syles.dart';
  5. /// 帖子詳情頁的贊組件
  6. ///
  7. /// 包括點(diǎn)贊組件 icon ,以及組件點(diǎn)擊效果
  8. /// 需要外部參數(shù)[likeNum],點(diǎn)贊數(shù)量
  9. class ArticleDetailLike extends StatelessWidget {
  10. /// 有狀態(tài)類返回組件信息
  11. @override
  12. Widget build(BuildContext context) {
  13. final likeNumModel = Provider.of<LikeNumModel>(context);
  14. return Column(
  15. crossAxisAlignment: CrossAxisAlignment.center,
  16. children: <Widget>[
  17. FlatButton(
  18. child: Icon(Icons.thumb_up, color: Colors.grey, size: 40),
  19. onPressed: () => likeNumModel.like(),
  20. ),
  21. Text(
  22. '${likeNumModel.value}',
  23. style: TextStyles.commonStyle(),
  24. ),
  25. ],
  26. );
  27. }
  28. }

同樣上面的第 15 行獲取 likeNumModel 操作句柄,,然后在第 22 行執(zhí)行 like 操作,在第 25 行顯示點(diǎn)贊數(shù)量,。

接下來我們運(yùn)行下項目,,可以看到效果如圖 5 所示。

20200620_213558.gif
圖 5 多頁面狀態(tài)點(diǎn)贊同步效果

總結(jié)

以上就是本課時的所有內(nèi)容,,學(xué)完本課時你需要掌握使用狀態(tài)管理的場景,,常見的狀態(tài)管理有哪些,。本課時的核心是需要你掌握 Provider 的狀態(tài)管理技術(shù)方案,。

至此,,我已經(jīng)將組件的設(shè)計基本介紹完畢,,接下來我將介紹組件的單元測試,,以及完善組件功能,。如果你有疑問,,可以在下方留言,。

點(diǎn)擊此鏈接查看本課時源碼

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多