概述
從指令式程式設計框架(iOS、Android)到宣告式程式設計(Flutter、vue), 比如 Flutter 有大量的 State 需要來進行管理,資料共享的時候,
分類
- 短時狀態
某些簡單的資料變化,只需要使用 statefulWidget 的 state 類自己管理即可
- 應用狀態AppState
比如使用者資訊,全域性性的資料資訊,需要選擇全域性狀態管理的方式
共享狀態管理
InheritedWidget
定義一個共享資料的 InheritedWidget
,需要繼承自 InheritedWidget
- 需要定義一個
of
靜態方法,通過傳入context
開始去查詢祖先
的資料
class ShareWidget extends InheritedWidget {
//共享的 counter 資料
final int counter;
ShareWidget({this.counter, Widget child}) : super(child: child);
static ShareWidget of(BuildContext context) {
//沿著 context 往上查詢最近的祖先 widget
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(covariant ShareWidget oldWidget) {
//是否需要通知資料變更
return this.counter != oldWidget.counter;
}
}
複製程式碼
其他 widget 使用
@override
Widget build(BuildContext context) {
// 通過呼叫 of 靜態方法拿到 “ShareWidget”
int _counter = ShareWidget.of(context).counter;
return Container(
child: Text("$_counter"),
);
}
複製程式碼
Provider
官方推薦的全域性狀態管理工具
dependencies:
provider:^4.0.4
複製程式碼
三個概念
- ChangeNotifier: 真正資料(狀態)存放的地方
- ChangeNotifierProvider: widget樹中提供資料的地方,會在其中建立對應的 ChangeNotifier
- Consumer: Widget 樹中需要使用資料(狀態)的地方
Provider 的基本用法
1.1 建立 provider
繼承自 ChangeNotifier
class ViewModelProvider extends ChangeNotifier {
// 1.建立自己想要共享的資料
int _counter = 100;
int get counter => _counter;
set counter(int value) {
_counter = value;
notifyListeners();
}
}
複製程式碼
1.2 插入 provider
在應用頂層放入 ChangeNotifierProvider
, 這樣全域性都可以使用 ViewModelProvider
void main() {
runApp(ChangeNotifierProvider(
create: (ctx) => ViewModelProvider(), child: HomePage()));
}
複製程式碼
1.3 使用資料
1.3.1 Provider.of
int _counter = Provider.of<ViewModelProvider>(context).counter;
複製程式碼
1.3.2 Consumer
- 使用 Consumer 包裹要使用共享資料的 widget
- builder 會返回三個引數
-
context
返回的是當前樹的位置
-
provider
ChangeNotifier對應的例項,可以直接使用 provider 中的資料
-
child
目的是防止 child 被重複 build
- 當然使用 Consumer 的話,builder 所返回的 widget(例如demo中的FloatingActionButton) 仍然會被重複 build, 所以引入
Seletor
floatingActionButton: Consumer<ViewModelProvider>(
child: Icon(Icons.add),
//builder 會返回三個引數 context、provider、child
builder: (cxt, provider, child) {
return FloatingActionButton(
child: child,
onPressed: () {
setState(() {
provider.counter++;
});
},
);
},
),
複製程式碼
1.3.3 Selector
關鍵引數
- 1.泛型引數<A,S>
-
- A: 我們這次要使用的 provider
-
- S: 轉換之後的資料型別
- 2.seletor 回撥函式
-
- 轉換的回撥函式,沒有轉換的話,可以直接返回 Provider 的例項
- 3.是否需要 rebuild
Selector({
Key? key,
required ValueWidgetBuilder<S> builder,
required S Function(BuildContext, A) selector,
ShouldRebuild<S>? shouldRebuild,
Widget? child,
})
複製程式碼
floatingActionButton: Selector<ViewModelProvider, ViewModelProvider>(
selector: (cxt, provider) => provider,
shouldRebuild: (pre, next) => false,
builder: (cxt, provider, child) {
print("Selector build");
return FloatingActionButton(
onPressed: () {
provider.counter++;
},
child: child,
);
},
),
複製程式碼
1.3.4 MultiProvider
開發中肯定不止一個 provider,不可能全部巢狀在 runApp中,因此使用 MultiProvider
可以定義一個 dart 類專門存放 providers
List<SingleChildWidget> providers = [
ChangeNotifierProvider(
create: (ctx) => Provider1(),
),
ChangeNotifierProvider(
create: (ctx) => Provider2(),
),
];
複製程式碼
void main() {
runApp(
MultiProvider(
// 使用
providers: providers,
child: HomePage(),
),
);
}
複製程式碼
使用小結
* 1.建立自己想要共享的資料 Provider extends ChangeNotifier
* 2.在應用頂層修改 runApp 新增 ChangeNotifierProvider
* 3.在其他位置使用共享資料
* 1)Provider.of<ViewModelProvider>(context)
* 2) Consumer (相對推薦)
* 3) Selector
* 4.使用 MultiProvider 管理多個共享狀態
複製程式碼