定义:
在Tree中从上往下高效传递数据的基类widget , 定义为:abstract class InheritedWidget extends ProxyWidget
InheritedWidget是什么?
- InheritedWidget是Flutter中非常重要的一个功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式,比如我们在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据!这个特性在一些需要在widget树中共享数据的场景中非常方便!如Flutter SDK中正是通过InheritedWidget来共享应用主题(Theme)和Locale (当前语言环境)信息的。
- InheritedWidget和React中的context功能类似,和逐级传递数据相比,它们能实现组件跨级传递数据。InheritedWidget的在widget树中数据传递方向是从上到下的,这和通知Notification的传递方向正好相反。
原理:
Flutter的响应式开发与React类似,数据都是自顶向下的。
假设有祖先组点A,中间经过结点B, C,然后到结点D,D需要从A中获取数据f,那按照自顶向下数据流转,f需要依次传递给B及C,最后才到C。这样开发极为不灵活,成本也比较高。所有Flutter需要有跨结点(只能是祖先后代节点,不能跨兄弟节点)高效传递数据的方案。
源码定义:
/// Base class for widgets that efficiently propagate
///information down the tree.
/// To obtain the nearest instance of a particular type of inherited
///widget from a build context, use
///[BuildContext.inheritFromWidgetOfExactType].
/// Inherited widgets, when referenced in this way, will cause the
///consumer to rebuild when the inherited widget itself changes state.
大体意思如下:
InheritedWidget 是在树中高效向下传递信息的基类部件;
调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例;
在 InheritedWidget 类型的控件被引用,也就是调用过 inheritFromWidgetOfExactType 方法后,当 InheritedWidget 自身状态改变时,会导致引用了 InheritedWidget 类型的子控件重构(rebuild)。
InheritedWidget 用法案例
定义数据模型
这里随便定义一个 Person 类。
//数据模型
class Person {
String name;
int age;
Person(this.name, this.age);
@override
String toString() {
// TODO: implement toString
return "我叫${this.name},今年${this.age}了";
}
}
自定义 InheritedWidget 控件类
创建一个类继承 InheritedWidget,并实现 updateShouldNotify 方法。
class InheriedDataWidget extends InheritedWidget {
final Person person; //需要在树中共享的数据
InheriedDataWidget({@required this.person,Widget child})
: super(child: child);
//定义一个便捷方法,方便子树中的widget获取共享数据
static InheriedDataWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(InheriedDataWidget);
}
@override
bool updateShouldNotify(InheriedDataWidget oldWidget) {
// TODO: implement updateShouldNotify
//如果返回true,则子树中依赖(build函数中有调用)本widget的子 .
//widget的`state.didChangeDependencies`方法会被调用
return this.person.name != oldWidget.person.name ||
this.person.age != oldWidget.person.age;
}
}
之前说到调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例,所以此处定义一个静态的 of 方法,通过传入的 context 获取到最近的 InheriedDataWidget 实例。
InheritedWidget使用:
1.定义数据模型
这里随便定义一个 Person 类。
//数据模型
class Person {
String name;
int age;
Person(this.name, this.age);
@override
String toString() {
// TODO: implement toString
return "我叫${this.name},今年${this.age}了";
}
2.自定义 InheritedWidget 控件类
创建一个类继承 InheritedWidget,并实现 updateShouldNotify 方法。
class InheriedDataWidget extends InheritedWidget {
final Person person; //需要在树中共享的数据
InheriedDataWidget({@required this.person,Widget child})
: super(child: child);
//定义一个便捷方法,方便子树中的widget获取共享数据
static InheriedDataWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(InheriedDataWidget);
}
@override
bool updateShouldNotify(InheriedDataWidget oldWidget) {
// TODO: implement updateShouldNotify
//如果返回true,则子树中依赖(build函数中有调用)本widget的子 .
//widget的`state.didChangeDependencies`方法会被调用
return this.person.name != oldWidget.person.name ||
this.person.age != oldWidget.person.age;
}
}
之前说到调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例,所以此处定义一个静态的 of 方法,通过传入的 context 获取到最近的 InheriedDataWidget 实例。
3.InheriedDataWidget 的使用
InheriedDataWidget 使用起来也很简单,它本身也是一个控件,只要在任意一个页面的子控件调用其构造方法就行,这里我们定义一个形如的 Widget 树。
WidgetA 类
//WidgetA
class WidgetA extends StatefulWidget {
@override
_WidgetAState createState() => _WidgetAState();
}
class _WidgetAState extends State<WidgetA> {
Person person = new Person("小明", 24);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
//调用构造,在此节点共享了一个Person类型的数据
child: InheriedDataWidget(
person: person,
child: WidgetA1(),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
//在setState中刷新Person的值
this.person=new Person(person.name, person.age+1);
});
},
child: Icon(Icons.add),
),
);
}
}
WidgetA 是一个 StatefulWidget 类型的控件,可以调用 setState 刷新,如果是继承 Stateless 类型的控件,那我们也可以通过 Stream 或者其他方式刷新数据,感兴趣请看[什么是 Stream? Dart
WidgetA1 类
//控件WidgetA
class WidgetA1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey,
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
Text(
"A1 Widget",
style: TextStyle(color: Colors.white,fontSize: 20),
),
//调用InheriedDataWidget.of()获取数据,其实也就是调用了 .
//inheritFromWidgetOfExactType方法
Text(
"共享数据的信息: ${InheriedDataWidget.of(context).person.toString()}",
style: TextStyle(color: Colors.white,fontSize: 20),
),
Container(
margin: EdgeInsets.only(top: 40),
color: Colors.deepOrangeAccent,
height: MediaQuery.of(context).size.height*0.5,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
WidgetA1_1(),
WidgetA1_2(),
WidgetA1_3(),
],
),
WidgetA1_1 类
class WidgetA1_1 extends StatefulWidget {
@override
_WidgetA1_1State createState() => _WidgetA1_1State();
}
class _WidgetA1_1State extends State<WidgetA1_1> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: 140,
width: MediaQuery.of(context).size.width/3-20,
//引用共享数据
child: Text("WidgetA1_1ful\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
);
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
//当WidgetA中Person数据发生改变时,此方法会被调用,
//前提是updateShouldNotify返回true
print("WidgetA1_1ful===>didChangeDependencies");
}
}
WidgetA1_2 类
class WidgetA1_2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: 140,
width: MediaQuery.of(context).size.width/3-20,
child: Text("WidgetA1_2less\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
);;
}
}
WidgetA1_3 类
class WidgetA1_3 extends StatefulWidget {
@override
_WidgetA1_3State createState() => _WidgetA1_3State();
}
class _WidgetA1_3State extends State<WidgetA1_3> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: 140,
width: MediaQuery.of(context).size.width/3-20,
child: Text("WidgetA1_3ful\n\n未引用共享数据信息"),
);
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("WidgetA1_3===>didChangeDependencies");
}
}
运行结果
当我们点击 floatingActionButton 的时候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都会更新 Person 的信息,而且每点 floatingActionButton 一次,
当我们点击 floatingActionButton 的时候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都会更新 Person 的信息,而且每点 floatingActionButton 一次,都会输出:
WidgetA1_1ful===>didChangeDependencies
如果我们试图在和 WidgetA 的同一层级的兄弟节点去访问 InheriedDataWidget 的 Person 数据,是不行的,因为父节点中并没有插入 InheriedDataWidget。
- Widget B
把 WidgetB 和 WidgetA 保持同一节点
//WidgetB
class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
//此处将报错,因为WidgetB 与WidgetA 是兄弟节点,
//B中并未找到A中 InheriedDataWidget 的数据
return Text(InheriedDataWidget.of(context).person.toString());
}
}
这也体现了 Inheried(遗传) 这一单词的特性,遗传只存在于父子。兄弟不存在遗传的关系。
这种数据共享的方式在某些场景还是很有用的,就比如说全局主题,字体大小,字体颜色的变更,只要在 App 根层级共享出这些配置数据,然后在触发数据改变之后,所有引用到这些共享数据的地方都会刷新,这换主题,字体是不是就很轻松,事实上 Theme.of(context).primaryColor 之流就是这么干的。
总结:
以上就是有关InheritedWidget的使用。自己也是从事Android开发5年有余了;整理了一些Android开发技术进阶资料;https://docs.qq.com/doc/DUkNRVFFzTG96VHNiAndroid技术进阶手册丶面试题纲丶核心笔记。
网友评论