概述
Provider 和 Scoped Model 状态管理框架都是基于 InheritedWidget 的,主要是实现共享数据 以及 实现局部更新(这个一般是ChangeNotifier的功能),比如系统中的 Theme.of(context) 以及 MediaQuery.of(context)都是利用 InheritedWidget 实现的。
原理以及用途
在 Flutter 中子widget无法单独感知父widget的变化,但是InheritedWidget可以让子组件获取父组件的数据。
InheritedWidget 和 ValueNotifier(ChangeNotifier 也可以) 组合实现 局部更新
代码里面有个Logger-github 或者 Logger-pub 库,是实现在Flutter中有级别打印以及不同的颜色
创建一个可共享的 User 类
class User {
String? name;
int? age;
User({this.name, this.age});
}
创建一个 DemoInheritedWidget
继承于 InheritedWidget,并且里面的一个变量 ValueNotifier ,泛型是User,同时提供了 更改User 以及 只更改name的方法。注意of(context)方法内,是 getElementForInheritedWidgetOfExactType 不是 dependOnInheritedWidgetOfExactType
class DemoInheritedWidget extends InheritedWidget {
late ValueNotifier<User> userNotifier;
DemoInheritedWidget(User user, {Key? key, required Widget child})
: super(key: key, child: child) {
userNotifier = ValueNotifier<User>(user);
}
/// 主要是用来获取 DemoInheritedWidget
static DemoInheritedWidget of(BuildContext context) {
// 不要用 dependOnInheritedWidgetOfExactType
InheritedElement element =
context.getElementForInheritedWidgetOfExactType<DemoInheritedWidget>()!;
return element.widget as DemoInheritedWidget;
}
void updateUser(User user) {
userNotifier.value = user;
}
/// 实际上 ValueNotifier 就是对 ChangeNotifier 的一个简单封装
void updateUserName(String name) {
userNotifier.value.name = name;
userNotifier.notifyListeners();
}
@override
bool updateShouldNotify(covariant DemoInheritedWidget oldWidget) {
return false;
}
}
写一个页面 使用 上面的DemoInheritedWidget,
我们根据 DemoInheritedWidget.of(context).updateUser(User(name: "改变后张三1", age: 19)); 去更新User ,使用 DemoInheritedWidget.of(context).updateUserName("李四") 更新名字
class InheritedWidgetDemoPage extends StatefulWidget {
InheritedWidgetDemoPage({Key? key}) : super(key: key) {
Logger.e("Page 初始化");
}
@override
_InheritedWidgetDemoPageState createState() =>
_InheritedWidgetDemoPageState();
}
class _InheritedWidgetDemoPageState extends State<InheritedWidgetDemoPage> {
@override
Widget build(BuildContext context) {
Logger.e("_PageState: build");
return DemoInheritedWidget(User(name: "张三", age: 18), child: Builder(
builder: (context) {
return Scaffold(
appBar: AppBar(
title: const Text("InheritedWidgetDemo"),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: WidgetA(
child: WidgetB(),
),
),
MaterialButton(
color: Colors.blue,
onPressed: () {
DemoInheritedWidget.of(context)
.updateUser(User(name: "改变后张三1", age: 19));
},
child: const Text(
"改变User",
style: TextStyle(color: Colors.white),
),
),
MaterialButton(
color: Colors.blue,
onPressed: () {
DemoInheritedWidget.of(context).updateUserName("李四");
},
child: const Text(
"只改变name",
style: TextStyle(color: Colors.white),
),
),
],
),
);
},
));
}
@override
void dispose() {
Logger.e("_PageState: dispose");
super.dispose();
}
}
widgetA 和 WidgetB
因为 WidgetB 需要用到User对象的数据,所以 widgetB 用 ValueListenableBuilder作为child,当 User发生变化的时候 只会 widgetB 会 rebuild,其他的不会走
class WidgetA extends StatefulWidget {
final Widget child;
const WidgetA({Key? key, required this.child}) : super(key: key);
@override
_WidgetAState createState() => _WidgetAState();
}
class _WidgetAState extends State<WidgetA> {
@override
void initState() {
super.initState();
Logger.e('WidgetA initState');
}
@override
Widget build(BuildContext context) {
Logger.e('WidgetA build');
return Center(
child: widget.child,
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
Logger.e('WidgetA didChangeDependencies');
}
@override
void dispose() {
super.dispose();
Logger.e('WidgetA dispose');
}
}
class User {
String? name;
int? age;
User({this.name, this.age});
}
class DemoInheritedWidget extends InheritedWidget {
late ValueNotifier<User> userNotifier;
DemoInheritedWidget(User user, {Key? key, required Widget child})
: super(key: key, child: child) {
userNotifier = ValueNotifier<User>(user);
}
/// 主要是用来获取 DemoInheritedWidget
static DemoInheritedWidget of(BuildContext context) {
// 不要用 dependOnInheritedWidgetOfExactType
InheritedElement element =
context.getElementForInheritedWidgetOfExactType<DemoInheritedWidget>()!;
return element.widget as DemoInheritedWidget;
}
void updateUser(User user) {
userNotifier.value = user;
}
void updateUserName(String name) {
userNotifier.value.name = name;
userNotifier.notifyListeners();
}
@override
bool updateShouldNotify(covariant DemoInheritedWidget oldWidget) {
return false;
}
}
class WidgetB extends StatefulWidget {
@override
_WidgetBState createState() => _WidgetBState();
}
class _WidgetBState extends State<WidgetB> {
@override
void initState() {
super.initState();
Logger.e('WidgetB initState');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
Logger.e('WidgetB didChangeDependencies');
}
@override
Widget build(BuildContext context) {
Logger.e('WidgetB build');
return ValueListenableBuilder<User>(
builder: (BuildContext context, user, Widget? child) {
Logger.e("ValueListenableBuilder");
return RichText(
text: TextSpan(children: [
const TextSpan(text: "名字是:", style: TextStyle(color: Colors.red)),
TextSpan(text: user.name, style: const TextStyle(color: Colors.black)),
const TextSpan(text: " 年龄是:", style: TextStyle(color: Colors.red)),
TextSpan(text: "${user.age}", style: const TextStyle(color: Colors.black)),
]),
);
},
valueListenable: DemoInheritedWidget.of(context).userNotifier,
);
}
@override
void dispose() {
super.dispose();
Logger.e('F dispose');
}
}
结果
当我们点击更新的时候 只有 ValueListenableBuilder 去 rebuild 了,其他都没有 rebuild