??????2019 Google I/O 大會(huì)上重磅消息出了支持 flutter_web 之外,另一個(gè)便是棄用之前的狀態(tài)管理 Provide,轉(zhuǎn)而推薦相似的庫 Provider具帮;雖然只有一個(gè)字母之差使用方式差別卻很大;小菜初步學(xué)習(xí)一下新的狀態(tài)管理庫 Provider;
??????Flutter 針對(duì)不同類型對(duì)象提供了多種不同的 Provider滋饲;Provider 也是借助了 InheritWidget,將共享狀態(tài)放到頂層 MaterialApp 之上;
Provider 方式
??????最基本的狀態(tài)管理方式喊巍,以一個(gè)參數(shù)方式綁定和展示屠缭;
1. 綁定數(shù)據(jù)
??????Provider 可在需要的 Widget 處進(jìn)行數(shù)據(jù)綁定:
const Provider.value({
Key key,
@required T value,
this.updateShouldNotify,
this.child,
}) : dispose = null,
super.value(key: key, value: value);
??????分析源碼 Provider.value 并沒有限制 value 類型,我們可以根據(jù)需求隨意綁定需要的數(shù)據(jù)類型崭参;當(dāng)我們確定綁定的數(shù)據(jù)類型時(shí)呵曹,建議綁定時(shí)添加數(shù)據(jù)類型,如:Provider<String>.value( value: '', child:)何暮;
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: Provider<String>.value( value: 'FirstPage Provider', child: MyHomePage(title: 'Peovider Demo')));
}
}
2. 獲取數(shù)據(jù)
??????Provider 需要在數(shù)據(jù)綁定的子 Widget 中進(jìn)行獲妊傥埂;使用靜態(tài)方法 Provider.of<T>(BuildContext context)海洼,此方法從 BuildContext 關(guān)聯(lián)的 Widget Tree 中查找最近的相同類型的數(shù)據(jù)進(jìn)行展示跨新;沒有則報(bào)異常;
Text('${Provider.of<String>(context)}'),
Text('FirstPage Provider: ${Provider.of<String>(context)} | ${Provider.of<int>(context)} | ${Provider.of<bool>(context)}}'),
3. 綁定多條數(shù)據(jù)
??????在我們實(shí)際開發(fā)中不會(huì)只綁定一條數(shù)據(jù)坏逢,當(dāng)綁定多條數(shù)據(jù)時(shí)可以采用如下兩種方式:嵌套綁定和聚合綁定域帐;兩種方式效果完全相同,小菜更傾向于 MultiProvider 綁定是整,層級(jí)更清晰簡(jiǎn)潔肖揣;
// 嵌套綁定
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: Provider<User>.value(
value: new User('Flutter', 300),
child: Provider<int>.value(
value: 200,
child: Provider<bool>.value(
value: false, child: MyHomePage(title: 'Peovider Demo')))));
}
}
// 聚合方式
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: MultiProvider(providers: [
Provider<User>.value(value: new User('Flutter', 300)),
Provider<int>.value(value: 200),
Provider<bool>.value(value: false)
], child: MyHomePage(title: 'Peovider Demo')));
}
}
4. 綁定數(shù)據(jù)類型
??????Provider 綁定數(shù)據(jù)類型比較靈活,并非只是基本數(shù)據(jù)類型浮入,小菜定義了一個(gè) User 類龙优,可正常狀態(tài)管理;小菜在獲取 User 后重新設(shè)置 name 之后獲取的 User 為最新的數(shù)據(jù)舵盈;
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: MultiProvider(providers: [
Provider<User>.value(value: new User('Flutter', 300)),
Provider<int>.value(value: 200),
Provider<bool>.value(value: false)
], child: MyHomePage(title: 'Peovider Demo')));
}
}
Text(
'FirstPage Provider: ${Provider.of<String>(context)} | '
'${Provider.of<int>(context)} | ${Provider.of<bool>(context)} | ${Provider.of<User>(context).name = 'Hello World!'}',
style: TextStyle(color: Colors.redAccent)),
Text('${Provider.of<User>(context).name}'),
5. 作用域
??????小菜在剛開始學(xué)習(xí)時(shí)被作用域卡到陋率,實(shí)際文檔說的很明白,獲取綁定數(shù)據(jù)的范圍是在綁定數(shù)據(jù)的子 Widget 中秽晚;小菜繪制了一下個(gè)人理解的基本作用域圖瓦糟,如有錯(cuò)誤請(qǐng)多多指導(dǎo);
??????void main() => runApp() 為范圍最廣的 application 作用域赴蝇,其作用范圍包括各個(gè) Page 之間菩浙;FirstPage 中定義的 Provider A 作用在藍(lán)色框范圍內(nèi),Provider B 作用在粉色框范圍內(nèi),SecondPage 中定義的 Provider C 作用在綠色范圍內(nèi)劲蜻;超出范圍則不能進(jìn)行狀態(tài)管理陆淀;
6. 作用域內(nèi)容
??????如上圖作用域劃分,在 FirstPage 多個(gè)作用域的粉色框中先嬉,若獲取 String 類型的狀態(tài)管理 Provider<String>.value( value: '', child:)轧苫,首先在粉色作用域中查找,若存在則展示粉色框中綁定數(shù)據(jù)疫蔓;若沒有則查找上一層藍(lán)色作用域含懊,存在則為藍(lán)色框綁定數(shù)據(jù);若依然沒有查找 application 作用域衅胀,存在則展示 application 作用域綁定數(shù)據(jù)岔乔;若均沒有則報(bào)異常;
??????這也驗(yàn)證了文檔中提及的子 Widget 作用域滚躯,一層一層往外層查找雏门,直到可以正常獲取掸掏;
ChangeNotifierProvider 方式
??????通過調(diào)用 ChangeNotifier.notifyListeners 對(duì) ChangeNotifier 進(jìn)行監(jiān)聽茁影,將其公開給它的子 Widget 并重建依賴項(xiàng);
1. 綁定數(shù)據(jù)
ChangeNotifierProvider 綁定數(shù)據(jù)有兩種方式:
- ChangeNotifierProvider({Key key, @required ValueBuilder<T> builder, Widget child })
??????通過構(gòu)造器創(chuàng)建一個(gè) ChangeNotifier阅束,在 ChangeNotifierProvider 移除時(shí)自動(dòng)處理呼胚;
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<User>(
builder: (_) => User('Flutter', 0),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: MyHomePage(title: 'Peovider Demo')));
}
}
- ChangeNotifierProvider.value({Key key, @required T notifier, Widget child })
??????通過監(jiān)聽通知給子 Widget 并重建依賴項(xiàng);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<User>.value(
notifier: User('Flutter', 0),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: MyHomePage(title: 'Peovider Demo')));
}
}
2. 獲取數(shù)據(jù)
??????獲取數(shù)據(jù)的方式與直接使用 Provider 相似息裸;
Text('${Provider.of<User>(context).getName}'),
??????相對(duì)于 Provider蝇更,ChangeNotifierProvider 方式更加靈活,可以通過重寫 get/set 方法來對(duì)狀態(tài)管理進(jìn)行修改和使用呼盆;
// User 實(shí)體 Bean
class User with ChangeNotifier {
var name;
var age;
User(this.name, this.age);
void setName(String name) {
this.name = name;
notifyListeners();
}
String get getName => this.name;
}
// 綁定 Provider
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<User>(
builder: (_) => User('Flutter', 0),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: MyHomePage(title: 'Peovider Demo')));
}
}
// 獲取 Provider 數(shù)據(jù)
Expanded(
child: TextField(
onChanged: (changed) =>
Provider.of<User>(context).setName(changed),
controller: _phonecontroller,
decoration: InputDecoration(
hintText: '請(qǐng)輸入用戶名',
suffixIcon: IconButton(
icon: Icon(Icons.clear,
color: Colors.black45),
onPressed: () {
_phonecontroller.clear();
})))),
Text('${Provider.of<User>(context).getName}'),
問題小結(jié)
??????小菜在開始嘗試時(shí)總是遇到如下問題年扩,Could not find the correct Provider... 測(cè)試后了解是在子 Widget 中層級(jí)查找未找到對(duì)應(yīng)的綁定數(shù)據(jù);極有可能是綁定數(shù)據(jù)的 Widget 位置未綁定或綁定位置錯(cuò)誤访圃;
??????小菜對(duì) Provider 的理解還很淺顯厨幻,對(duì)于其他 Provider 的使用還未嘗試;如有錯(cuò)誤請(qǐng)多多指導(dǎo)腿时!
來源:阿策小和尚