最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Flutter 狀態(tài)管理scoped model源碼解讀

 更新時(shí)間:2022年11月11日 11:23:21   作者:feelingHy  
這篇文章主要為大家介紹了Flutter 狀態(tài)管理scoped model源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一、什么是 scoped_model

本文主要從 scoped_model 的簡單使用說起,然后再深入源碼進(jìn)行剖析(InheritedWidget、Listenable、AnimatedBuilder),不會探討 Flutter 狀態(tài)管理的優(yōu)劣,單純?yōu)榱藢W(xué)習(xí)作者的設(shè)計(jì)思想。

scoped_model 是一個(gè)第三方 Dart 庫,可以讓您輕松的將數(shù)據(jù)模型從父 Widget 傳遞到子 Widget。此外,它還會在模型更新時(shí)重新構(gòu)建所有使用該模型的子 Widget。

它直接來自于 Google 正在開發(fā)的新系統(tǒng) Fuchsia 核心 Widgets 中對 Model 類的簡單提取,作為獨(dú)立使用的獨(dú)立 Flutter 插件發(fā)布。

二、用法

class CounterModel extends Model {
  int _counter = 0;
  int get counter => _counter;
  static CounterModel of(BuildContext context) =>
      ScopedModel.of<CounterModel>(context, rebuildOnChange: true);
  void increment() {
    _counter++;
    notifyListeners();
  }
}
class ScopedModelDemoPage extends StatelessWidget {
  const ScopedModelDemoPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('scopedModel'),
        centerTitle: true,
      ),
      body: ScopedModel(
        model: CounterModel(),
        child: ScopedModelDescendant<CounterModel>(
          builder: (context, child, model) => Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('${model.counter}'),
                OutlinedButton(
                  onPressed: ScopedModel.of<CounterModel>(context).increment,
                  // onPressed: model.increment,
                  child: Text('add'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

從上面的代碼可以看出 scoped_model 的使用非常簡單,只需要以下三步:

  • 定義 Model 的實(shí)現(xiàn),如 CounterModel,這里需要注意的是 CounterModel 一定要繼承自 Model(為什么一定要繼承 Model 我們后面細(xì)說)并且在狀態(tài)改變的時(shí)候執(zhí)行 notifyListeners。
  • 使用 ScopedModel Widget 包裹需要用到 Model 的 Widget。
  • 使用 ScopedModelDescendant 或者 ScopedModel.of<CounterModel>(context) 來進(jìn)行獲取數(shù)據(jù)。

三、實(shí)現(xiàn)原理

在 scoped_model 中的整個(gè)實(shí)現(xiàn)中,它很巧妙的借助了 AnimatedBuilder、Listenable、InheritedWidget 等 Flutter 的基礎(chǔ)特性。

scoped_model 使用了觀察者模式,將數(shù)據(jù)放在父 Widget,子 Widget 通過找到父 Widget 的 model 進(jìn)行數(shù)據(jù)渲染,最后改變數(shù)據(jù)的時(shí)候再將數(shù)據(jù)傳回,父 Widget 再通知所有用到了該 model 的子 Widget 去更新狀態(tài)。

我們首先從 ScopedModel 入手,通過源碼我們不難發(fā)現(xiàn),ScopedModel 是一個(gè) StatelessWidget 最后返回一個(gè) AnimatedBuilder,在 AnimatedBuilder 中在通過 builder 返回 _InheritedModel。

我們再從 Model 入手,可以看出 Model 是一個(gè) 繼承自 Listenable 的抽象類,主要有一個(gè) _listeners 變量用 Set 來進(jìn)行存儲,復(fù)寫了 addListener、removeListenernotifyListeners 方法。在這里不知道大家有沒有想過 Model 為什么要繼承 Listenable? 在這里先賣個(gè)關(guān)子,在后面會詳細(xì)講解。

如果只是單單看 ScopedModelModel 好像也看不出來什么巧妙之處,但是如果把 ScopedModel 中返回的 AnimatedBuilderModel 所繼承的 Listenable 結(jié)合起來進(jìn)行思考就會發(fā)現(xiàn),AnimatedBuilder 繼承自 AnimatedWidget,在 AnimatedWidget 的生命周期中會對 Listenable 添加監(jiān)聽,而 Model 正好就實(shí)現(xiàn)了 Listenable 接口。

Model 實(shí)現(xiàn)了 Listenable 接口,內(nèi)部剛好有一個(gè) Set<VoidCallback> _listeners 用來保存接收者。當(dāng) Model 賦值給 AnimatedBuilder 中的 animation 時(shí),Listenable 的 addListener 就會被調(diào)用,然后添加一個(gè) _handleChange 方法,_handleChange 內(nèi)部只有一行代碼 setState((){}),當(dāng)調(diào)用 notifyListeners 時(shí),會從創(chuàng)建一個(gè) Microtask,去執(zhí)行一遍 _listeners 中的 _handleChange ,當(dāng) _handleChange 被調(diào)用時(shí)就會進(jìn)行更新 UI 界面。其實(shí)這里也就解釋了 Model 為什么要繼承 Listenable。

不知道大家發(fā)現(xiàn)沒有,講了這么多,還沒有講到 ScopedModelDescendant 到底是干什么的?那我就不得不先說起 InheritedWidget 了。

InheritedWidget 是 Flutter 中非常重要的一個(gè)功能型組件,它提供了一種在 Widget 樹中從上到下共享數(shù)據(jù)的方式,比如我們在應(yīng)用的根 Widget 中通過 InheritedWidget 共享了一個(gè)數(shù)據(jù),那么我們便可以在任意子 Widget 中來獲取該共享的數(shù)據(jù)。它主要有以下兩個(gè)作用:

  • 子 Widget 可以通過 Inherited Widgets 提供的靜態(tài) of 方法拿到離他最近的父Inherited widgets 實(shí)例。
  • 當(dāng) Inherited Widgets 改變 state 之后,會自動(dòng)觸發(fā) state 消費(fèi)者的 rebuild 行為。

scoped_model 中我們可以通過 ScopedModel.of<CountModel>(context) 來獲取 我們的 model,最主要的就是在 ScopedModel 中返回了 AnimatedBuilder,而 AnimatedBuilder 中 builder 又返回了 _InheritedModel_InheritedModel 又繼承了 InheritedWidget。

言歸正傳,我們一起回到 ScopedModelDescendant 的主題,不知道大家有沒有嘗試過,不用 ScopedModelDescendant 來獲取 model 會發(fā)生什么樣的情況?通過窺探源碼我們發(fā)現(xiàn)有 ScopedModelError 這樣一個(gè)異常類,說的已經(jīng)很明確了,必須要提供 ScopedModelDescendant。what ?其實(shí)它主要做了以下 2 件事情:

  • 隱式調(diào)用 ScopedModel.of<T>(context) 來獲取 model。
  • 明確語義化,不然我們每次都需要用 Builder 來進(jìn)行構(gòu)建,不然將獲取不到 model,還會拋出異常。

// 不使用 ScopedModelDescendant 使用 Builder 的用法
class ScopedModelDemoPage extends StatelessWidget {
  const ScopedModelDemoPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('scopedModel'),
        centerTitle: true,
      ),
      body: ScopedModel(
        model: CounterModel(),
        child: Builder(
          builder: (context) {
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('${CounterModel.of(context).counter}'),
                OutlinedButton(
                  onPressed: CounterModel.of(context).increment,
                  child: Text('add1'),
                ),
              ],
            );
          },
        ),
        // child: ScopedModelDescendant<CounterModel>(
        //   builder: (context, child, model) => Center(
        //     child: Column(
        //       mainAxisAlignment: MainAxisAlignment.center,
        //       children: [
        //         Text('${model.counter}'),
        //         OutlinedButton(
        //           onPressed: ScopedModel.of<CounterModel>(context).increment,
        //           // onPressed: model.increment,
        //           child: Text('add'),
        //         ),
        //       ],
        //     ),
        //   ),
        // ),
      ),
    );
  }
}

除了上面這種使用 Builder 的方式,當(dāng)然我們還可以使用下面的方法,把它單獨(dú)提取出一個(gè) Widget,代碼如下:

// 單獨(dú)提取 Widget 的方式
class ScopedModelDemoPage extends StatelessWidget {
  const ScopedModelDemoPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('scopedModel'),
        centerTitle: true,
      ),
      body: ScopedModel<CounterModel>(
        model: CounterModel(),
        // 不使用 ScopedModelDescendant 的用法
        // child: Builder(
        //   builder: (context) {
        //     return Column(
        //       mainAxisAlignment: MainAxisAlignment.center,
        //       children: [
        //         Text('${CounterModel.of(context).counter}'),
        //         OutlinedButton(
        //           onPressed: CounterModel.of(context).increment,
        //           child: Text('add1'),
        //         ),
        //       ],
        //     );
        //   },
        // ),
        child: NewWidget(),
      ),
    );
  }
}
class NewWidget extends StatelessWidget {
  const NewWidget({
    Key? key,
  }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text('${CounterModel.of(context).counter}'),
          OutlinedButton(
            onPressed: CounterModel.of(context).increment,
            // onPressed: model.increment,
            child: Text('add'),
          ),
        ],
      ),
    );
  }
}
class CounterModel extends Model {
  int _counter = 0;
  int get counter => _counter;
  static CounterModel of(BuildContext context) =>
      ScopedModel.of<CounterModel>(context, rebuildOnChange: true);
  void increment() {
    _counter++;
    notifyListeners();
  }
}

是不是很意外?主要起作用的是下面這一段代碼:

單獨(dú)提取出來 Widget,可以獲取到正確的 context,從而可以獲取到離他最近的父 Inherited widgets 實(shí)例。

四、結(jié)束

以上就是我對 scoped_model 的使用以及部分源碼的解讀,如果有不足之處,還請指教。 最后,大家也可以思考一下,我們是如何通過 context 就能獲取到共享的 Model 呢?

以上就是Flutter 狀態(tài)管理scoped model源碼解讀的詳細(xì)內(nèi)容,更多關(guān)于Flutter狀態(tài)管理 scoped model的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android自定義控件仿ios下拉回彈效果

    Android自定義控件仿ios下拉回彈效果

    這篇文章主要為大家詳細(xì)介紹了Android自定義控件仿ios下拉回彈效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • Android 保存文件路徑方法

    Android 保存文件路徑方法

    今天小編就為大家分享一篇Android 保存文件路徑方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Android 匿名內(nèi)存深入分析

    Android 匿名內(nèi)存深入分析

    這篇文章主要為大家介紹了Android 匿名內(nèi)存深入分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Android實(shí)戰(zhàn)教程第四十篇之Chronometer實(shí)現(xiàn)倒計(jì)時(shí)

    Android實(shí)戰(zhàn)教程第四十篇之Chronometer實(shí)現(xiàn)倒計(jì)時(shí)

    這篇文章主要介紹了Android實(shí)戰(zhàn)教程第四十篇之Chronometer實(shí)現(xiàn)倒計(jì)時(shí),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Android解決View的滑動(dòng)沖突的方法

    Android解決View的滑動(dòng)沖突的方法

    這篇文章主要介紹了Android解決View的滑動(dòng)沖突的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • Android工具類整合教程

    Android工具類整合教程

    這篇文章主要介紹了Android工具類整合教程,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2020-09-09
  • Android之自定義實(shí)現(xiàn)BaseAdapter(通用適配器三)

    Android之自定義實(shí)現(xiàn)BaseAdapter(通用適配器三)

    這篇文章主要為大家詳細(xì)介紹了Android之自定義實(shí)現(xiàn)BaseAdapter通用適配器第三篇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • Android 詳解沉浸式狀態(tài)欄的實(shí)現(xiàn)流程

    Android 詳解沉浸式狀態(tài)欄的實(shí)現(xiàn)流程

    沉浸式就是要給用戶提供完全沉浸的體驗(yàn),使用戶有一種置身于虛擬世界之中的感覺。沉浸式模式就是整個(gè)屏幕中顯示都是應(yīng)用的內(nèi)容,沒有狀態(tài)欄也沒有導(dǎo)航欄,用戶不會被一些系統(tǒng)的界面元素所打擾,讓我們來實(shí)現(xiàn)下網(wǎng)上傳的沸沸揚(yáng)揚(yáng)的安卓沉浸式狀態(tài)欄
    2021-11-11
  • Android開發(fā)登陸案例

    Android開發(fā)登陸案例

    這篇文章主要介紹了Android開發(fā)登陸案例的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-07-07
  • Android SRT字幕文件基礎(chǔ)操作講解

    Android SRT字幕文件基礎(chǔ)操作講解

    這篇文章主要介紹了Android SRT字幕文件基礎(chǔ)操作,SRT的數(shù)據(jù)格式是通過以上單個(gè)數(shù)據(jù)節(jié)點(diǎn)可以提供一個(gè)大致的思路是:先定位一個(gè)數(shù)據(jù)節(jié)點(diǎn)的固定格式,然后將一行一行的讀取到數(shù)據(jù)
    2023-01-01

最新評論

宁海县| 兖州市| 万荣县| 乌什县| 东至县| 揭西县| 工布江达县| 开鲁县| 赞皇县| 仪征市| 长治县| 报价| 健康| 麦盖提县| 宝应县| 山阴县| 龙江县| 通化市| 无极县| 乌兰县| 荣昌县| 南雄市| 根河市| 会理县| 杭锦旗| 祥云县| 岫岩| 宜州市| 平陆县| 驻马店市| 四子王旗| 忻城县| 朔州市| 景德镇市| 宁蒗| 乐昌市| 丹巴县| 西林县| 应用必备| 苏尼特左旗| 正宁县|