Flutter狀態管理學習手冊(一)——ScopedModel

1、ScopedModel簡介

ScopedModel屬於入門級別的狀態管理框架,它的思想比較簡單,參考官方文檔即可以很容易理解其中構架。git

FlutterLifting state up(狀態提高)是十分必要的,狀態提高能夠理解爲把組件之間相互共享的狀態提取出來放在一個較高層級中管理的一種思想。ScopedModel提供了對於這種狀態管理的便利。github

2、ScopedModel中的三個概念

ScopedModel主要有三個重要的概念,也是其中的三個類:ModelScopedModelScopedModelDescendantScopedModel基本上經過這三個類實現其功能。bash

Model是封裝狀態和狀態操做的地方。咱們能夠將想要的數據存放在Model當中而且將對數據操做,如添加刪除的相關方法放在這裏。Model還提供了一個notifyListeners()方法,它的做用是當數據發生改變時,能夠經過調用notifyListeners()方法通知界面進行更新。網絡

ScopedModel是一個用於保存ModelWidget。一般ScopedModel會一個應用的入口處做爲父佈局使用,並以Model做爲參數傳入,使得ScopedModel持有Modelapp

ScopedModel的子佈局中,能夠經過ScopedModel.of<Model>(context)方法來獲取Model框架

ScopedModelDescendant,顧名思義,是ScopedModel的派生物。一樣的,它也是一個WidgetScopedModelDescendant會做爲ScopedModel下的子佈局存在,它的主要做用是響應狀態更新。async

ScopedModelDescendant中存在builder函數,這個函數會在ModelnotifyListeners()發生時被調用,從而根據Model中的數據生成相應的界面。ide

3、ScopedModel的實踐

這裏以常見的獲取列表選擇列表爲例子。一個頁面用於展現選中項和跳轉到列表,一個頁面用於顯示列表。函數

1. 引入scoped_model第三方庫

在根目錄的pubspec.yaml文件的dependencies中加入依賴佈局

dependencies:
  ...
  scoped_model: ^1.0.0
複製代碼

2. 定義Model

建立一個ListModel類,這個類須要繼承scoped_model包裏的Model類。

ListModel類中包含三個狀態:列表初始化標誌、列表數據、選中的列表項。

bool _init = false; // 列表初始化標誌
  List<String> _list = []; // 列表數據
  String _selected = '未選中'; // 選中的列表項
複製代碼

Model中不只只有數據,還包括對數據操做的方法,這裏定義兩個操做方法,分別是選中列表項目和加載列表的方法,而且,這兩個方法在更新數據後,須要調用notifyListeners()通知UI更新。

/**
   * 選中列表項
   */
  void select(String selected) {
    _selected = selected;

    // 通知數據變動
    notifyListeners();
  }

  /**
   * 加載列表
   */
  void loadList() async {
    // 模擬網絡請求
    await Future.delayed(Duration(milliseconds: 3000));
    _list = [
      '1. Scoped Model',
      '2. Scoped Model',
      '3. Scoped Model',
      '4. Scoped Model',
      '5. Scoped Model',
      '6. Scoped Model',
      '7. Scoped Model',
      '8. Scoped Model',
      '9. Scoped Model',
      '10. Scoped Model'
    ];

    _init = true;
    // 通知數據變動
    notifyListeners();
  }
複製代碼

3. UI佈局

在UI上,使用ScopedModel做爲根佈局,提供Model,使用ScopedModelDescendant做爲子佈局,響應Model

首先,在main()方法中,建立ListModel實例,用ScopedModel包裹MyApp佈局

void main() {

  // 建立Model實例
  ListModel listModel = ListModel();
  // 使用ScopedModel做爲根佈局
  runApp(ScopedModel(model: listModel, child: MyApp()));
}
複製代碼

爲了體現狀態提高這一律念,例子中使用兩個頁面,一個是ShowPage,另外一個是ListPageShowPage用於顯示選中的列表項目和提供跳轉到ListPage的入口,ListPage用於加載顯示列表。

ShowPage中,顯示ListModel中的選中項。

ScopedModelDescendant<ListModel>(
  builder: (context, child, model) {
    String selected = model.selected;
    return Text(selected);
  }
),
複製代碼

ScopedModelDescendant的泛型指明ListModel,它便會自動獲取ScopedModel中的ListModel,在builder: (context, child, model)中便可經過其中的model參數獲取狀態,構建UI。

一樣的,在ListPage中,經過ScopedModelDescendant來顯示加載狀態和列表。

body: ScopedModelDescendant<ListModel>(builder: (context, child, model) {
        // 根據狀態顯示界面
        if (!model.isInit) {
          // 顯示loading界面
          return buildLoad();
        } else {
          // 顯示列表界面
          var list = model.list;
          return buildList(list);
        }
      }),
複製代碼

4. 狀態改變

ListPage是一個StatefulWidget,因此能夠在initState()方法中進行列表的加載工做。

@override
void initState() {
  super.initState();
  ListModel model = ScopedModel.of<ListModel>(context); // 獲取ListModel
  if (!model.isInit) {
    model.loadList(); // 加載列表
  }
}
複製代碼

點擊列表中的某一項時,會選中該項,這時調用ListModel中的select(String)方法,並返回上一個界面。

onTap: () {
  ListModel model = ScopedModel.of<ListModel>(context);
  model.select(list[index]);
  // 返回到上一級頁面
  Navigator.pop(context);
},
複製代碼

完整代碼能夠參考github.com/windinwork/…

4、ScopedModel的注意事項

  1. ScopedModelDescendant的層級須要儘可能低,能夠避免大範圍的UI重建。這裏引用官方的例子。
// 錯誤示例
return ScopedModelDescendant<CartModel>(
  builder: (context, child, cart) {
    return HumongousWidget(
      // ...
      child: AnotherMonstrousWidget(
        // ...
        child: Text('Total price: ${cart.totalPrice}'),
      ),
    );
  },
);
複製代碼
// 正確示例
return HumongousWidget(
  // ...
  child: AnotherMonstrousWidget(
    // ...
    child: ScopedModelDescendant<CartModel>(
      builder: (context, child, cart) {
        return Text('Total price: ${cart.totalPrice}');
      },
    ),
  ),
);
複製代碼

5、總結

ScopedModel功能比較簡單,使用Model保存狀態和通知狀態改變,使用ScopedModel提供Model,使用ScopedModelDescendant佈局來響應狀態變化,是一個十分適合入門者理解的狀態管理模型。

參考目錄

Simple app state management

相關文章
相關標籤/搜索