InheritedWidget 组件是功能型组件,提供了沿树向下,共享数据的功能,即子组件可以获取父组件(InheritedWidget 的子类)的数据,通过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 方法必须重写,此方法是判断新的共享数据和原数据是否一致,是否将通知传递给所有子组件(已注册)。重建此组件时,有时需要重建继承 InheritedWidget 组件的组件,但有时不需要。 例如,如果此组件所保存的数据与“ oldWidget”所保存的数据相同,则我们无需重建继承了“ oldWidget”所保存的数据的组件。
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 的共享数据并展示,如果 updateShouldNotify 返回 false,那么 F 组件 rebuild 时只会执行 build 函数,而 updateShouldNotify 返回 true时, F 组件 rebuild 时会执行 didChangeDependencies 和 build 函数,因此可以将类似访问服务器接口这种耗时工作放在 didChangeDependencies 函数中,这也是 didChangeDependencies 生命周期存在的意义。
使用方法
Step1:添加依赖
dependencies:
flutter:
sdk: flutter
provider: ^4.0.4
Step:创建一个ChangeNotifier
我们先新建一个 NumberManager,继承 ChangeNotifier,使之成为我们的数据提供者之一。
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:创建ChangeNotifierProvider
List<SingleChildWidget> providerList = [
ChangeNotifierProvider(create: (context) => NumberManager()),
];
void main() {
// ignore: prefer_const_constructors
runApp(MultiProvider(
providers: providerList,
child: MyApp(),
));
// runApp(const MyApp());
}
获取数据
1. 使用Provider.of<T>(context)方法来引用数据
- read读取数据,如果数据,不会主动调用build,所以数据变化后,显示的数据再没有重新build前,显示的还是之前的数据:
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);
}
}
通过此方法应用数据时,当前widget所在的build会在收到通知时进行重绘,所以在使用过程中如果不希望当前widget所在的类进行rebuild,可以为当前widget单独创建一个StatelessWidget:
class FirstNumWidget extends StatelessWidget {
const FirstNumWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
print("----------FirstNumWidget被重执行了----------");
// 使用Provider.of<T>(context)方法来引用数据,每次数据变化都会重新build
return ElevatedButton(
child: Text(
"consumer firstNum的值:${Provider.of<NumberManager>(context).firstNum}"),
onPressed: () {
Provider.of<NumberManager>(context, listen: false).changeData1();
},
);
}
}
Consumer
具体用法如下,builder 中的参数分别是 Context context, T value, Widget child,value 即Model1,value 的类型和 Model1 类型一致,builder 方法返回的是 Widget,也就是被 Consumer 包裹的 widget,当监听的 model 值发生改变,此 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)
// 用于指定使用哪个值作为重重建判断依据
@required S Function(BuildContext, A) selector,
// bool Function(T previous, T next)
// 是否重建,一般情况下不用我们实现
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 中监听的 value 不发生变化,其包裹的 widget 不会 Rebuild。 Selector 在 Consumer 基础上提供了更加精确的监听,还支持自定义 rebuild,可以更加灵活地控制 widget rebuild 问题。