Flutter | 狀態(tài)管理探索篇——Scoped Model(一)

前言

Flutter的很多靈感來自于React,它的設計思想是數(shù)據(jù)與視圖分離,由數(shù)據(jù)映射渲染視圖。所以在Flutter中,它的Widget是immutable的,而它的動態(tài)部分全部放到了狀態(tài)(State)中。

假如你曾進行過react開發(fā),也許你一下會想到Redux。flutter有類似redux的狀態(tài)管理的庫嗎?答案是肯定的,但是有關在flutter中使用redux的應用實踐我們會在之后的文章中進行介紹。

今天要和大家分享的是使用Scoped_model進行狀態(tài)管理。

什么是Scoped_model

Scoped_model是一個dart第三方庫,提供了讓您能夠輕松地將數(shù)據(jù)模型從父Widget傳遞到它的后代的功能。此外,它還會在模型更新時重新渲染使用該模型的所有子項。

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

實現(xiàn)原理

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

而我們則需要將它們放在頂層入口MaterialApp之上,這樣就能進行全局的狀態(tài)管理了。

image

這里page3,page4代表使用到該狀態(tài)(model)的子頁面。

Lets do it!

這里我們以一個最簡單的CountApp舉例,詳細介紹Scoped_model的用法。該項目完整代碼已放在github倉庫。

這是一個在不同頁面使用Scoped共享狀態(tài)信息的app。這兩個頁面都依賴于一個數(shù)字,這個數(shù)字會隨著我們按下按鈕的次數(shù)而增加。


image

第一步:添加依賴

在pubspec中添加scoped_model的依賴。


image

第二步:創(chuàng)建Model

在Scoped中,Model是一個只包含與狀態(tài)相關信息的單位。我們應該把狀態(tài)數(shù)據(jù)與操作數(shù)據(jù)的方法抽象出來封裝到Model中。

import 'package:scoped_model/scoped_model.dart';

class CountModel extends Model{
  int _count = 0;
  get count => _count;
  
  void increment(){
    _count++;
    notifyListeners();
  }
}
  • 我們需要讓我們自定義的CountModel繼承至Model。
  • 在狀態(tài)發(fā)生變化時(increment)通知所有用到了該model的子項更新狀態(tài)。(notifyListeners)

第三步:將Model放入頂層

//創(chuàng)建頂層狀態(tài)
  CountModel countModel = CountModel();

  @override
  Widget build(BuildContext context) {
    return ScopedModel<CountModel>(
      model: countModel,
      child: new MaterialApp(
        home: TopScreen(),
      ),
    );
  }
  • 我們在頂層創(chuàng)建了一個CountModel的實例。
  • ScopedModel<T extends Model>是一個StatelessWidget,它接收一個model,并提供給需要它的所有部件。
  • 將ScopedModel<T extends Model>的model屬性綁定我們的CountModel對象。

第四步:在子頁面中獲取Model

Scoped_model提供了兩種方式在子頁面中獲取model。我們先來介紹第一種,使用ScopedModelDescendant獲取model。

@override
  Widget build(BuildContext context) {
    return ScopedModelDescendant<CountModel>(
      builder: (context,child,model){
        return Scaffold(
          body: Center(
            child: Text(
              model.count.toString(),
              style: TextStyle(fontSize: 48.0),
            ),
          ),
        );
      },
    );
  }
  • ScopedModelDescendant<T extends Model>是一個Stateless Widget,它接收三個參數(shù)。


    image
  • builder是一個ScopedModelDescendantBuilder<T>,它接收三個參數(shù)。


    image

    ,在builder中能夠通過model來獲取CountModel實例。

  • rebuildOnChange屬性能夠控制當該狀態(tài)發(fā)生變化時,是否rebuild,作用等同于setState。也就是說我們調用改變狀態(tài)的一些方法時,不必再setState。
floatingActionButton: new FloatingActionButton(
          onPressed: () => model.increment(),
          tooltip: 'Increment',
          child: new Icon(Icons.add),
        )

第二種獲取model的方式——使用ScopedModel.of

final countModel = ScopedModel.of<CountModel>(context);
countModel.increment();

或者在Model中重寫of方法

class CountModel extends Model{
  int _count = 0;
  get count => _count;

  void increment(){
    _count++;
    notifyListeners();
  }
//重寫of方法
  CountModel of(context) =>
      ScopedModel.of<CountModel>(context);
}

然后直接通過CountModel獲取model實例

final countModel2 = CountModel().of(context);

這種方式似乎讓我們的代碼有更好的可閱讀性。

Q&A

這里看上去似乎只添加了一個model,我應該如何添加多個model

要解決這個問題很簡單,使用Mixin!

class MainModel extends Model with AModel,BModel,CModel{}

然后將MainModel放在頂層即可。
這里有一個比較完整的使用ScopedModel管理狀態(tài)的應用,詳細用法可參考該項目。

Scoped是如何做到同步不同頁面中的狀態(tài)的

image

Model實現(xiàn)了Listenable接口,并重寫了void addListener(VoidCallback listener),removeListener(VoidCallback listener)方法,實現(xiàn)了觀察者模式。
每當我們調用notifyListeners()方法時,將會通知觀察者更新狀態(tài)。

Scoped如何做到數(shù)據(jù)能夠互相共享的

在不同頁面間的數(shù)據(jù)傳遞使用了InheritedWidget。

image

是否具有侵入性

正在研究當中...

寫在最后

在flutter中,Scoped_model是一種非常簡單易上手,并能保持代碼高可閱讀性的一種新的狀態(tài)管理方式,值得各位去嘗試一下!

本次所用到的代碼已經(jīng)上傳Github: https://github.com/Vadaski/Vadaski-flutter_note_book/tree/master/mecury_project/example/scoped_demo

如果您對scoped還有任何疑問或者文章的建議,歡迎在下方評論區(qū)以及我的郵箱1652219550a@gmail.com與我聯(lián)系,我會及時回復!

下一章我們將探索Redux在Flutter中的實踐,敬請關注。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 作為系列文章的第二篇,本篇將為你著重展示:如何搭建一個通用的Flutter App 常用功能腳手架,快速開發(fā)一個完...
    戀貓月亮閱讀 34,307評論 13 99
  • 年前最后一天上班,今天很放縱,消費了不少。一杯大杯本周,一部場面宏大刺激的imax電影星球大戰(zhàn)外傳,一頓定食,一杯...
    Hirune閱讀 173評論 0 0
  • 遺然世外的古廟,只因曲徑通幽里,穿越歲月而來的美好祝福,多了偌多的朝拜者,無論來時初心如何,到了,必然是各種形式的...
    陌上花開l閱讀 624評論 0 2
  • 自定義錯誤頁面 開發(fā)環(huán)境,當 APP_DEBUG = true 時,使用默認錯誤頁面; 生產環(huán)境,當 APP_DE...
    分析閱讀 1,726評論 0 1
  • 濛濛細雨隨夜來 千枝萬樹綠紅白 五顏六色箏飛天 鶯歌燕舞童顏開
    山東聊城李超閱讀 242評論 0 1

友情鏈接更多精彩內容