Flutter 使用Scoped Model狀態(tài)管理

移動端中經(jīng)常有這樣的場景比如電商界面中商品詳情頁點(diǎn)擊收藏后同步至列表頁對于這種需求一般實(shí)現(xiàn)起來比較繁瑣,而前端開發(fā)中經(jīng)常聽到redux等進(jìn)行一些狀態(tài)管理這方便了很多功能的實(shí)現(xiàn)而在flutter中也有狀態(tài)管理Scoped_Model就是其中之一
本文簡單的記錄一下Scoped_Model的用法

先介紹一下Scoped_Model

A set of utilities that allow you to easily pass a data Model from a parent Widget down to it's descendants. In addition, it also rebuilds all of the children that use the model when the model is updated. This library was originally extracted from the Fuchsia codebase.
一組實(shí)用程序姆打,允許您輕松地將數(shù)據(jù)模型從父窗口小部件傳遞給它的后代。此外粱甫,它還重建了模型更新時(shí)使用模型的所有子代鞠鲜。這個庫最初是從 Fuchsia 基代碼中提取的疚宇。

其中有一個概念就是InheritedWidget

InheritedWidget是Flutter中非常重要的一個功能型Widget籽腕,它可以高效的將數(shù)據(jù)在Widget樹中向下傳遞乾蓬、共享,這在一些需要在Widget樹中共享數(shù)據(jù)的場景中非常方便壁榕,如Flutter中矛紫,正是通過InheritedWidget來共享應(yīng)用主題(Theme)和Locale(當(dāng)前語言環(huán)境)信息的。

我們先看一下如何使用Scoped_Model

先聲明一個CounterModel官方文檔中說明我們聲明的CounterModel必須繼承與Model


// Start by creating a class that has a counter and a method to increment it.
//
// Note: It must extend from Model.
class CounterModel extends Model {
  int _counter = 0;
 
  int get counter => _counter;
 
  void increment() {
    // First, increment the counter
    _counter++;
 
    // Then notify all the listeners.
    notifyListeners();
  }
}

點(diǎn)擊進(jìn)去觀看Model源碼

abstract class Model extends Listenable {
  final Set<VoidCallback> _listeners = Set<VoidCallback>();
  int _version = 0;
  int _microtaskVersion = 0;
 
  /// [listener] 將在Model更改時(shí)調(diào)用护桦。
  @override
  void addListener(VoidCallback listener) {
    _listeners.add(listener);
  }
 
  /// [listener] 移除時(shí)調(diào)用含衔。
  @override
  void removeListener(VoidCallback listener) {
    _listeners.remove(listener);
  }
 
  /// Returns the number of listeners listening to this model.
  int get listenerCount => _listeners.length;
 
  /// 僅當(dāng)Model已更改時(shí)由[model]調(diào)用。
  @protected
  void notifyListeners() {
    // 我們安排一個微任務(wù)來消除可能同時(shí)發(fā)生的多個更改二庵。
    if (_microtaskVersion == _version) {
      _microtaskVersion++;
      scheduleMicrotask(() {
        _version++;
        _microtaskVersion = _version;
 
        // Convert the Set to a List before executing each listener. This
        // prevents errors that can arise if a listener removes itself during
        // invocation!
        _listeners.toList().forEach((VoidCallback listener) => listener());
      });
    }
  }
}

我們不難發(fā)現(xiàn)Model 繼承了 Listenable,所以我們可以調(diào)用notifyListeners();

然后我們在需要使用這個Model的地方用ScopedModelDescendant來獲取

class CounterHome extends StatelessWidget {
  final String title;
  CounterHome(this.title);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            // Create a ScopedModelDescendant. This widget will get the
            // CounterModel from the nearest parent ScopedModel<CounterModel>.
            // It will hand that CounterModel to our builder method, and
            // rebuild any time the CounterModel changes (i.e. after we
            // `notifyListeners` in the Model).
            ScopedModelDescendant<CounterModel>(
              builder: (context, child, model) {
                return Text(
                  model.counter.toString(),
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
        Container(
          child: RaisedButton(
            onPressed: () {
             Navigator.push(context,MaterialPageRoute(builder:(context) => ScopedTestWidget()));
            },
            child: Text(title),
          ),
        ),
          ],
        ),
      ),
      // Use the ScopedModelDescendant again in order to use the increment
      // method from the CounterModel
      floatingActionButton: ScopedModelDescendant<CounterModel>(
        builder: (context, child, model) {
          return FloatingActionButton(
            onPressed: model.increment,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          );
        },
      ),
    );
  }

我們可以點(diǎn)擊ScopedModelDescendant看一下它的內(nèi)部結(jié)構(gòu)

ScopedModelDescendant({
    @required this.builder,
    this.child,
    this.rebuildOnChange = true,
  });

可以看到ScopedModelDescendant<T extends Model>是一個Stateless Widget,它接收三個參數(shù)其中一個是builder我們再點(diǎn)擊builder一看究竟

// Builds a child for a [ScopedModelDescendant].
typedef Widget ScopedModelDescendantBuilder<T extends Model>(
  BuildContext context,
  Widget child,
  T model,
);

發(fā)現(xiàn)builder中有一個model他可以獲取到我們定義的CountModel
看到這里似乎明白了
我們可以通過ScopedModelDescendant在任何一個widget里面拿到我們想要的model
我們新建一個dart文件

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../main.dart';


class ScopedTestWidget extends StatefulWidget {
  @override
  _ScopedTestWidgetState createState() => _ScopedTestWidgetState();
}

class _ScopedTestWidgetState extends State<ScopedTestWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('lisi'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            // Create a ScopedModelDescendant. This widget will get the
            // CounterModel from the nearest parent ScopedModel<CounterModel>.
            // It will hand that CounterModel to our builder method, and
            // rebuild any time the CounterModel changes (i.e. after we
            // `notifyListeners` in the Model).
            ScopedModelDescendant<CounterModel>(
              builder: (context, child, model) {
                return Text(
                  model.counter.toString(),
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
          ],
        ),
      ),
      // Use the ScopedModelDescendant again in order to use the increment
      // method from the CounterModel
    );
  }
  }

我們引入
import 'package:scoped_model/scoped_model.dart';
這樣在我們想獲取count的地方我們可以通過ScopedModelDescendant拿到達(dá)到了狀態(tài)的同步

本篇文章就介紹到這里當(dāng)然Scoped Model還有很多高級的用法因?yàn)椴攀鑼W(xué)淺還在研究中,以后將會進(jìn)行補(bǔ)充

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贪染,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子催享,更是在濱河造成了極大的恐慌杭隙,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件因妙,死亡現(xiàn)場離奇詭異痰憎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)攀涵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門铣耘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人以故,你說我怎么就攤上這事蜗细。” “怎么了怒详?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵炉媒,是天一觀的道長。 經(jīng)常有香客問我昆烁,道長吊骤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任静尼,我火速辦了婚禮白粉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鼠渺。我一直安慰自己蜗元,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布系冗。 她就那樣靜靜地躺著奕扣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掌敬。 梳的紋絲不亂的頭發(fā)上惯豆,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天池磁,我揣著相機(jī)與錄音,去河邊找鬼楷兽。 笑死地熄,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芯杀。 我是一名探鬼主播端考,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼揭厚!你這毒婦竟也來了却特?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤筛圆,失蹤者是張志新(化名)和其女友劉穎裂明,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體太援,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闽晦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了提岔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仙蛉。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖碱蒙,靈堂內(nèi)的尸體忽然破棺而出荠瘪,到底是詐尸還是另有隱情,我是刑警寧澤振亮,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布巧还,位于F島的核電站鞭莽,受9級特大地震影響坊秸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜澎怒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一褒搔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喷面,春花似錦星瘾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盒齿,卻和暖如春念逞,著一層夾襖步出監(jiān)牢的瞬間困食,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工翎承, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留硕盹,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓叨咖,卻偏偏與公主長得像瘩例,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子甸各,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容