0x00 概念介紹
簡單一點說
- inhertedwidget 是一個widget,跟其他widget不一樣的地方是,他可以在他所持有的child中共享自己的資料。如Theme。
- 應用場景:app的複雜度越來越大,對於資料之間的傳遞,如果都是根據dic或者model作為widget內部的引數傳遞,是不友好的方式。正常的想法,此時應該有個資料中心,或eventbus,用於資料傳遞和取用,而在flutter中是inhertwidget
- 實現: 內部實現資料更新,自動通知的方式,從而自動重新整理介面
- 寫法: 見下面例子
對於趕時間的同學看到這裡就可以回去搬磚了。下面留給還有五分鐘時間瀏覽的同學。
google 在flutter widget of the week
中介紹 inheritedwidget
, 短短的兩個簡短的視訊,讓人看到了flutter的用心,外語一般的我也能把概念看得個大概。但是對於真正使用其上手開發的同學總覺得離實際開發距離有點遠,還是得編寫一下例子才能理解更深一點。
作為一名高效的搬磚工,先看看它說了啥
0x01 前情提要
當應用變得更大時,小部件樹,變得更復雜,傳遞和訪問資料,會變得很麻煩。 如果你有四個或五個小部件一個接一個地巢狀, 並且您需要從頂部獲取一些資料。將它新增到所有這些建構函式中,以及所有這些構建方法。
然而我只是想到達widget來獲取資料。 不想一級一級傳遞資料。怎麼辦?幸運的是,有一個小部件型別允許這樣。 它叫做InheritedWidget。
0x02 使用方式
建立一個InhertWidget
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.color,
@required Widget child,
}) : assert(color != null),
assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor) as FrogColor;
}
@override
bool updateShouldNotify(FrogColor old) => color != old.color;
}
複製程式碼
建立子WidgetA B
class TestWidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
final inheritedContext = FrogColor.of(context);
return new Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Container(
color: inheritedContext.color,
height: 100,
width: 100,
child: Text('第一個widget'),
));
}
}
class TestWidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
final inheritedContext = FrogColor.of(context);
return new Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Container(
color: inheritedContext.color,
height: 50,
width: 50,
child: Text('第二個widget'),
));
}
}
複製程式碼
建立帶狀態的widgetC
class TestWidgetC extends StatefulWidget {
TestWidgetC({Key key}) : super(key: key);
_TestWidgetCState createState() => _TestWidgetCState();
}
class _TestWidgetCState extends State<TestWidgetC> {
@override
Widget build(BuildContext context) {
final inheritedContext = FrogColor.of(context);
// print(" 重建c CCC ");
return Container(
child: Container(
color: inheritedContext.color,
height: 200,
width: 200,
child: prefix0.Column(
children: <Widget>[
Text("第三個widget"),
],
),
));
}
@override
void didChangeDependencies() {
print(" 更改依賴 CCC ");
super.didChangeDependencies();
}
}
複製程式碼
組裝
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
Color color = Color.fromARGB(0, 0xff, 0xdd, 0xdd);
Color color2 = Color.fromARGB(0, 0xff, 0xdd, 0xdd);
void _incrementCounter() {
setState(() {
_counter = (_counter + 20) % 255;
Color color = Color.fromARGB(_counter, 0xff, 0xdd, 0xdd);
this.color = color;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
new FrogColor(
color: this.color,
color2: this.color2,
child: Column(
children: <Widget>[
new TestWidgetA(),
new TestWidgetB(),
new TestWidgetC()
],
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
複製程式碼
本段程式碼實現了點選+號,數字增加,並且兩個widget的顏色加深
效果圖如下
0x02 使用建議
InheritedWidgets通常包含一個名為的靜態方法 of
它為您呼叫InheritWidget的精確型別方法。
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor) as FrogColor;
}
複製程式碼
final 不可變性
例子中FrogColor 裡面的color就是一個final型別,不可以改變
只能替換InheritedWidget的欄位,通過重建
整個widget 樹。 這個很重要!!! 只是意味著它無法重新分配
。 但是並不意味著它不能在內部改變。
didChangeDependencies 改變時機
inhertedwidget改變了就會觸發,didChangeDependencies,對於耗時操作的業務如網路請求來說可以放置這裡。
從上例中可以做個試驗,在widgetC中移除 FrogColor.of(context)
這句話,可以看到,顏色不好隨著按鈕點選變色,另外也不會呼叫didChangeDependencies
這個方法了。但是widgetc還是會走build方法。
可以印證兩點,widget會重建,但是state不會重建,didChangeDespendice方法呼叫的時機是其依賴的上下文內容改變。
0x03 應用場景
-
Theme實際上是一種InheritedWidget。 Scaffold,Focus Scope等等也是如此。
-
附加服務物件到InheritedWidget。 如開發資料庫的包裝器
-
Web API的代理或資產提供者。 服務物件可以有自己的內部狀態。 它可以啟動網路呼叫,任何事情。
0x04 繼承者們
老大,InheritedNotifier
繼承自Inhertedwidget,其值可以是被監聽的,並且只要值傳送通知就會通知依賴者。
使用場景有 ChangeNotifier
或ValueNotifier
abstract class InheritedNotifier<T extends Listenable> extends InheritedWidget {
const InheritedNotifier({
Key key,
this.notifier,
@required Widget child,
}) : assert(child != null),
super(key: key, child: child);
@override
bool updateShouldNotify(InheritedNotifier<T> oldWidget) {
return oldWidget.notifier != notifier;
}
@override
_InheritedNotifierElement<T> createElement() => _InheritedNotifierElement<T>(this);
}
複製程式碼
老二, InheritedModel
繼承自 Inertedwidget的,允許客戶端訂閱值的子部分的更改。
就比InertedWidget多了一個必要方法updateShouldNotifyDependent
,表示可以根據,部分內容的改變傳送依賴變更通知。
class ABModel extends InheritedModel<String> {
ABModel({this.a, this.b, Widget child}) : super(child: child);
final int a;
final int b;
@override
bool updateShouldNotify(ABModel old) {
return a != old.a || b != old.b;
}
@override
bool updateShouldNotifyDependent(ABModel old, Set<String> aspects) {
return (a != old.a && aspects.contains('a')) ||
(b != old.b && aspects.contains('b'));
}
// ...
}
複製程式碼
一圖說明
參考