接續昨天的內容,本篇將來談談Consumer
,並介紹其背後的函式Provider.of
與他們間的差異。
常見的有以下幾個方法:
Consumer
Provider.of
context.watch
(與Provider.of
作用相同)Consumer
是package provider中的一個類別,而這個類別會在每次它「訂閱」的ChangeNotifier有更新(也就是ChangeNotifier去「通知」「聽眾」時),會重新執行一次builder中的Widget建構。至於builder,是宣告Consumer時唯一的必要欄位。
因為每次獲得新通知時,builder內的widget將會重新建構,因此這個類別在實作上為了效能考量有以下兩個需要注意的點:
return Consumer<Foo>(
builder: (context, foo, child) {
return Text('Value of Foo: ${foo.value}');
},
// build expensive widget here
child: const SomeExpensiveWidget();
);
// BAD
return Consumer<CartModel>(
builder: (context, cart, child) {
return HumongousWidget(
// ...
child: AnotherMonstrousWidget(
// ...
child: Text('Total price: ${cart.totalPrice}'),
),
);
},
);
// GOOD
return HumongousWidget(
// ...
child: AnotherMonstrousWidget(
// ...
child: Consumer<CartModel>(
builder: (context, cart, child) {
return Text('Total price: ${cart.totalPrice}');
},
),
),
);
Consumer widget能夠訂閱與獲得ChangeNotifier的通知,實際上背後是利用Provider.of
函式,那麼Provider.of
又是什麼呢?
Provider.of
以下內容參考Medium - How to use Provider: Context.read, watch and select
Provider.of
與我們之前使用的context.watch
十分相似,用法幾乎相同。而剛剛介紹的Consumer
其實只是將Provider.of
包裝成一個widget,並增加一個builder項目。
用之前第一個程式做範例:
class FavoritesPage extends StatelessWidget {
@override
Widget build(context) {
var appState = context.watch<MyAppState>();
if (appState.favorites.isEmpty) {
return Center(child: Text('No favorites Saved'),);
}
return Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
child: ListView(
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Text('You have ${appState.favorites.length} favorites'),
),
for (var wordPair in appState.favorites)
ListTile(...),
],
),
);
}
}
當Provider.of<MyAppState>(context)
或是context.watch<MyAppState>()
(二者可互換),獲得MyAppState的更新通知時,將會重新建立Widget build(context) {}
中的內容。
這時你可能會有個疑問,什麼時候要使用Consumer,什麼時候要選擇Provider.of?
答案就是:隨個人喜好就好了。
flutter在談論App State與Ephemeral state的比較時提到了Redux的開發者,Dan Abramov曾說的話:
基本上,只要覺得怎麼樣寫起來比較順手,就怎麼做吧!
雖然話是這麼說,但兩者間仍有部份使用上的差異,我們所需要做的,便是知道他們的優勢在哪些方面,並利用這些優勢來開發程式。
以下內容擷取自StackOverflow - When to use Provider.of<X> vs. Consumer<X> in Flutter的最佳回答
因此可以簡單列出兩者各自的優勢及差異:
BuildContext
才可以運作BuildContext
的情況下也能運作關於BuildContext
這一部份,官方有給出個範例:
下方程式碼為使用Provider.of
,而在此Provider.of
所得到的BuildContext是屬於該provider的祖先(ancestor),非子代,因此它會丟出ProviderNotFoundException
的錯誤。
// ERROR: ProviderNotFound
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => Foo(),
child: Text(Provider.of<Foo>(context).value),
);
}
若是改使用Consumer
,則能夠正常執行並建構Text這個widget,且在foo更改(Foo通知訂閱者)時更新Text的內容。
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => Foo(),
child: Consumer<Foo>(
builder: (_, foo, __) => Text(foo.value),
},
);
}
本篇的內容就到此為止,因為內容篇多了一些,若再加上實作感覺會有點亂,因此實作的部份就留到明天。
有任何問題或是想說的話都歡迎留言及email,我們明天見!
email: nnyjan02426@gmail.com
github: nnyjan02426