InheritedWidget 組件是功能型組件,提供了沿樹向下,共享數(shù)據(jù)的功能,即子組件可以獲取父組件(InheritedWidget 的子類)的數(shù)據(jù),通過BuildContext.dependOnInheritedWidgetOfExactType 獲取。
class MyInheritedWidget extends InheritedWidget {
final UserInfo userInfo;
MyInheritedWidget({this.userInfo, Widget child}):super(child: child);
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}
@override
bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
return oldWidget.userInfo != userInfo;
}
}
updateShouldNotify 方法必須重寫,此方法是判斷新的共享數(shù)據(jù)和原數(shù)據(jù)是否一致,是否將通知傳遞給所有子組件(已注冊)。重建此組件時,有時需要重建繼承 InheritedWidget 組件的組件,但有時不需要。 例如,如果此組件所保存的數(shù)據(jù)與“ oldWidget”所保存的數(shù)據(jù)相同,則我們無需重建繼承了“ oldWidget”所保存的數(shù)據(jù)的組件。
class _InheritedWidgetDemoState extends State<InheritedWidgetDemo> {
UserInfo _userInfo = UserInfo(name: 'lisa', age: 18);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('InheritedWidget Demo'),
),
body: Center(
child: MyInheritedWidget(
userInfo: _userInfo,
child: A(
child: F(),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_userInfo = UserInfo(name: '小明', age: 18);
});
},
),
);
}
}
下面是A和F
class F extends StatefulWidget {
@override
_FState createState() => _FState();
}
class _FState extends State<F> {
@override
void initState() {
super.initState();
print('F initState');
}
@override
Widget build(BuildContext context) {
print('F build');
return Text('name:${MyInheritedWidget.of(context).userInfo.name}');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('F didChangeDependencies');
}
}
class A extends StatefulWidget {
final Widget child;
const A({Key key, this.child}) : super(key: key);
@override
_AState createState() => _AState();
}
class _AState extends State<A> {
@override
void initState() {
super.initState();
print('A initState');
}
@override
Widget build(BuildContext context) {
print('A build');
return Center(
child: widget.child,
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('A didChangeDependencies');
}
}
修改_userInfo時依賴 MyInheritedWidget 組件的 F 組件調用 didChangeDependencies 方法,而 A 組件沒有調用 didChangeDependencies 方法,因為 A 沒有依賴 MyInheritedWidget 組件。
F 組件使用 InheritedWidget 的共享數(shù)據(jù)并展示,如果 updateShouldNotify 返回 false,那么 F 組件 rebuild 時只會執(zhí)行 build 函數(shù),而 updateShouldNotify 返回 true時, F 組件 rebuild 時會執(zhí)行 didChangeDependencies 和 build 函數(shù),因此可以將類似訪問服務器接口這種耗時工作放在 didChangeDependencies 函數(shù)中,這也是 didChangeDependencies 生命周期存在的意義。
使用方法
Step1:添加依賴
dependencies:
flutter:
sdk: flutter
provider: ^4.0.4
Step:創(chuàng)建一個ChangeNotifier
我們先新建一個 NumberManager,繼承 ChangeNotifier,使之成為我們的數(shù)據(jù)提供者之一。
class NumberManager with ChangeNotifier, DiagnosticableTreeMixin {
int _firstNum = 0;
int _secondNum = 0;
int get firstNum => _firstNum;
int get secondNum => _secondNum;
void changeData1() {
_firstNum++;
notifyListeners();
}
void changeData2() {
_secondNum++;
notifyListeners();
}
}
Step5:創(chuàng)建ChangeNotifierProvider
List<SingleChildWidget> providerList = [
ChangeNotifierProvider(create: (context) => NumberManager()),
];
void main() {
// ignore: prefer_const_constructors
runApp(MultiProvider(
providers: providerList,
child: MyApp(),
));
// runApp(const MyApp());
}
獲取數(shù)據(jù)
1. 使用Provider.of<T>(context)方法來引用數(shù)據(jù)
- read讀取數(shù)據(jù),如果數(shù)據(jù),不會主動調用build,所以數(shù)據(jù)變化后,顯示的數(shù)據(jù)再沒有重新build前,顯示的還是之前的數(shù)據(jù):
context.read()
T read<T>() {
return Provider.of<T>(this, listen: false);
}
- Provider.of<T>(context) 可以簡寫為context.watch(),
context.watch()
T watch<T>() {
return Provider.of<T>(this);
}
}
通過此方法應用數(shù)據(jù)時,當前widget所在的build會在收到通知時進行重繪,所以在使用過程中如果不希望當前widget所在的類進行rebuild,可以為當前widget單獨創(chuàng)建一個StatelessWidget:
class FirstNumWidget extends StatelessWidget {
const FirstNumWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
print("----------FirstNumWidget被重執(zhí)行了----------");
// 使用Provider.of<T>(context)方法來引用數(shù)據(jù),每次數(shù)據(jù)變化都會重新build
return ElevatedButton(
child: Text(
"consumer firstNum的值:${Provider.of<NumberManager>(context).firstNum}"),
onPressed: () {
Provider.of<NumberManager>(context, listen: false).changeData1();
},
);
}
}
Consumer
具體用法如下,builder 中的參數(shù)分別是 Context context, T value, Widget child,value 即Model1,value 的類型和 Model1 類型一致,builder 方法返回的是 Widget,也就是被 Consumer 包裹的 widget,當監(jiān)聽的 model 值發(fā)生改變,此 widget 會被 Rebuild。
Consumer<Model>(
builder: (context, model, child) {
return Text('Model count:${model.count}');
},
)
Selector
Selectord定義
class Selector<A, S> extends Selector0<S> {
Key key,
// Widget Function(BuildContext context, T value, Widget child)
// 用于構建 Widget
@required ValueWidgetBuilder<S> builder,
// S Function(BuildContext, A)
// 用于指定使用哪個值作為重重建判斷依據(jù)
@required S Function(BuildContext, A) selector,
// bool Function(T previous, T next)
// 是否重建,一般情況下不用我們實現(xiàn)
ShouldRebuild<S> shouldRebuild,
Widget child,
})
Selector 和 Consumer 很相似,唯一不同的是,Selector 可以自定義返回類型 ,所以Selector可以自定義是否在收到通知時進行頁面重繪
Selector<Model1, int>(
builder: (context, count, child) => Text(
"Selector示例演示: $count",
style: Theme.of(context).textTheme.title,
),
selector: (context, model) => model.count,
),
Selector 有 Selector0 ~ Selector6,具體使用大同小異,可以傳 0 ~ 6 個 ChangeNotifer,但是 S(Value)泛型只有一個,如果需要多個個值同時判斷返回 S 怎么辦呢?這個時候可以使用元祖 Tuple
總結
Consumer 可以避免 widget 多余的 rebuild,當 Consumer 中監(jiān)聽的 value 不發(fā)生變化,其包裹的 widget 不會 Rebuild。 Selector 在 Consumer 基礎上提供了更加精確的監(jiān)聽,還支持自定義 rebuild,可以更加靈活地控制 widget rebuild 問題。