版权声明:本文为作者原创书籍。转载请注明作者和出处,未经授权,严禁私自转载,侵权必究!!!
情感语录: 生活可以漂泊,可以孤独,但灵魂必须有所归依。所有的不甘,都是因为还心存梦想,在你放弃之前,好好拼一把,只怕心老,不怕路长。
欢迎来到本章节,上一章节介绍了Flutter 中的 WebSocket
的使用,知识点回顾 戳这里 Flutter第十九章
本章节来介绍 Flutter 中的数据共享,实际开发中很多时候共享数据都是通过传值的方式达到数据的统一,比如: A界面请求到的数据,在B界面也需要做展示。通常你会想到几种方式:1:全局静态变量、2:单例模式、3:跳转传值(构造函数 或 路由)、4:数据持久化(ShardPrefences,File,Sql)等。但是这些方式存在一个弊端就是只能单一的传值,往往都是界面跳转后才能取值,因此缺乏了何时去取值的契机。
业务开发中经常会碰到这样的情况,在收到某条数据后,多个界面都需要同时做出响应的改变,比如应用换肤等 即多个组件在收到消息后都需要进行改变颜色。 此时上面说到的几种方式都不能满足业务需求了。熟悉设计模式的童鞋会想到用观察者模式去处理,即 当点击了换肤按钮后,则向所有订阅者发送一条消息说你们都改变下自己的颜色吧,然后订阅者开始纷纷修改自己的颜色。
在原生开发中一般的实现都是观察者模式,需要开发者自行实现并维护观察者的列表。而在Flutter中,提供了用于Widget间共享数据的InheritedWidget
组件,当InheritedWidget发生变化时,它的子树中所有依赖了它的数据的Widget都会收到通知并重新构建刷新。
一、案例演示
为了能更好的理解,先结合上面的情景写一个简单的案例,在主界面修改数据后,通知其他页面刷新数据。
1、主界面
//主界面
class DataShardPage extends StatefulWidget {
@override
_DataShardPageState createState() => _DataShardPageState();
}
class _DataShardPageState extends State<DataShardPage> {
//用于存储数据的 Mode,初始化数据 为 0
InheritedTestModel inheritedTestModel = InheritedTestModel(0);
@override
Widget build(BuildContext context) {
//在该组件下的 子组件可以收到消息通知
return MyInheritedWidget(
inheritedTestModel: inheritedTestModel,
child: Scaffold(
floatingActionButton: FloatingActionButton(
elevation: 0,
child: Text('增加'),
onPressed: () {
setState(() {
//修改共享的数据,并刷新子组件
inheritedTestModel.count = inheritedTestModel.count + 1;
});
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
appBar: AppBar(
title: Text("InheritedWidget"),
),
body: Center(
child: ListView(
children: <Widget>[
//创建 三个 页面
WidgetTestPageOne(),
WidgetTestPageTwo(),
WidgetTestPageThree(),
],
))));
}
}
2、MyInheritedWidget 组件
// 组件共享数据的容器
class MyInheritedWidget extends InheritedWidget {
//数据的Mode
final InheritedTestModel inheritedTestModel;
MyInheritedWidget({
Key key,
@required this.inheritedTestModel,
@required Widget child,
}) : super(key: key, child: child);
//定义一个方法,方便子树中的widget获取共享数据
static MyInheritedWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(MyInheritedWidget);
}
//是否重建widget就取决于数据是否相同
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
//判断数据是否反生改变,如果为真 所有子组件的 build 方法会被调用;
// 有状态组件 didChangeDependencies()方法会被调用
return inheritedTestModel != oldWidget.inheritedTestModel;
}
}
3、WidgetTestPageOne 界面
// 第一个页面
class WidgetTestPageOne extends StatefulWidget {
@override
_WidgetTestPageOneState createState() => _WidgetTestPageOneState();
}
class _WidgetTestPageOneState extends State<WidgetTestPageOne> {
@override
Widget build(BuildContext context) {
final myInheritedWidget = MyInheritedWidget.of(context);
final inheritedTestModel = myInheritedWidget.inheritedTestModel;
print("WidgetTestPageOne____${inheritedTestModel.count}");
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Text("One页面获取到最新值:${inheritedTestModel.count}"));
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("WidgetTestPageOne___didChangeDependencies");
}
}
其它界面这里就先不贴了,基本类似。首先在 点击主界面的 增加
按钮后会触发调用 setState()
方法,在该方法中将共享的数据进行了累加。在前面 StatefulWidget 的章节中就有介绍到 setState() 还会导致 _DataShardPageState 重新 build。此时创建的三个页面就重新创建了。你可能会说 既然都重新创建了,何尝不通过构造器将数据代入呢? 没错,是可以这样做,但如果有很多界面都需要该数据, 岂不是每个界面都要修改,这样做维护的代价就太大了。更加优雅的方式就是,我管你需不要该数据,反正我将数据暴露提供出来,至于你内部要不要该数据,那就交由内部自行维护了。
第一个界面 WidgetTestPageOne 中通过 继承了 InheritedWidget 的 MyInheritedWidget将共享的数据进行了取值,并刷新到界面上。 那 MyInheritedWidget 又是怎么取到值的呢? 这里先买个关子.......
下面来看下效果简版图:
控制台输出结果:
I/flutter ( 2882): WidgetTestPageOne___didChangeDependencies
I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
I/flutter ( 2882): WidgetTestPageOne____1
I/flutter ( 2882): WidgetTestPageTwo____1
I/flutter ( 2882): WidgetTestPageThree____1
I/flutter ( 2882): WidgetTestPageOne___didChangeDependencies
I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
I/flutter ( 2882): WidgetTestPageOne____2
I/flutter ( 2882): WidgetTestPageTwo____2
I/flutter ( 2882): WidgetTestPageThree____2
I/flutter ( 2882): WidgetTestPageOne___didChangeDependencies
I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
I/flutter ( 2882): WidgetTestPageOne____3
I/flutter ( 2882): WidgetTestPageTwo____3
I/flutter ( 2882): WidgetTestPageThree____3
.....
从控制台可以看出在每次点击事件后,都触发了每个界面的 build()
和 didChangeDependencies()
方法。那么一个很严重的问题就来了。假如我的是 A,B,C 三个界面。 A 界面并不想修改自己,不关心共享数据的变化,即不参与订阅。B 界面可根据业务情况可参与订阅。C 界面则为订阅者。 但在每次点击后调用了 setState()
方法后导致了 节点下的页面全部重新构建了,而 A 界面根本不需要重新构建,因为它不关心数据的变化。因此这种方式将导致严重的性能问题........
二、源码分析
InheritedWidget 怎么就能从上往下共享数据呢? 下面进入到内部源码进行分析下。
/// Base class for widgets that efficiently propagate information down the tree.
abstract class InheritedWidget extends ProxyWidget {
const InheritedWidget({ Key key, Widget child })
: super(key: key, child: child);
@override
InheritedElement createElement() => InheritedElement(this);
@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
内部源码对 InheritedWidget 有这么一个概述,它是用于高效的沿树传播信息的小部件的基类。它是一个抽象类,要使用它因此我们必须要去实现该类。InheritedWidget 继承至 ProxyWidget ,那先进入 ProxyWidget 中看看干了啥。
abstract class ProxyWidget extends Widget {
const ProxyWidget({ Key key, @required this.child }) : super(key: key);
final Widget child;
}
可以看到 ProxyWidget 也是一个抽象类,也没干啥事,那毫无疑问它实现关键一定就是 InheritedElement 了,下面跟进 InheritedElement中,把英文注释去掉大致如下:
class InheritedElement extends ProxyElement {
InheritedElement(InheritedWidget widget) : super(widget);
@override
InheritedWidget get widget => super.widget;
final Map<Element, Object> _dependents = HashMap<Element, Object>();
@override
void _updateInheritance() {
assert(_active);
final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
_inheritedWidgets[widget.runtimeType] = this;
}
@override
void debugDeactivated() {
assert(() {
assert(_dependents.isEmpty);
return true;
}());
super.debugDeactivated();
}
@protected
Object getDependencies(Element dependent) {
return _dependents[dependent];
}
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
@protected
void updateDependencies(Element dependent, Object aspect) {
setDependencies(dependent, null);
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (Element dependent in _dependents.keys) {
assert(() {
// check that it really is our descendant
Element ancestor = dependent._parent;
while (ancestor != this && ancestor != null)
ancestor = ancestor._parent;
return ancestor == this;
}());
// check that it really depends on us
assert(dependent._dependencies.contains(this));
notifyDependent(oldWidget, dependent);
}
}
}
代码有点多,下面来逐步分析下。首先 InheritedElement 你可以看做是一个 InheritedWidget 的配置文件,主要记录组件树中的依赖关系。
主要方法介绍:
// 记录所有依赖关系的子元素 集合
final Map<Element, Object> _dependents = HashMap<Element, Object>();
@override
void _updateInheritance() {
assert(_active);
final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
_inheritedWidgets[widget.runtimeType] = this;
}
_updateInheritance()
该方法会在Element 中 mount和activate方法中被调用,_inheritedWidgets为基类Element中的成员,用于提高Widget查找父节点中的 InheritedWidget 的效率。首先判断基类中的 incomingWidgets 是否为空,如果不为则先将基类中的 InheritedWidget 添加到一个新集合中,如果为空,则先创建一个集合。 最后将自身添加到 _inheritedWidgets 中。
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
notifyDependent()
方法会触发 didChangeDependencies()
方法的调用,在第四章节中 StatefulWidget 有说到 State对象有一个didChangeDependencies回调,它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中InheritedWidget的数据。
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
updated()
该方法 首先会通过 基类中的 widget 进行判断是否有发生改变,如果有则触发父类中的 updated()
方法,最终会在父类 ProxyElement 中回调走到如下方法中:
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (Element dependent in _dependents.keys) {
assert(() {
// check that it really is our descendant
Element ancestor = dependent._parent;
while (ancestor != this && ancestor != null)
ancestor = ancestor._parent;
return ancestor == this;
}());
// check that it really depends on us
assert(dependent._dependencies.contains(this));
notifyDependent(oldWidget, dependent);
}
}
notifyClients()
则比较简单,遍历出 _dependents 集合中所有依赖关系的子元素widget 进行更新,即会触发 didChangeDependencies()
方法的调用。
看到这里,你可能会说: “司机,刹一脚,刹一脚!”。notifyClients 方法中在取出有依赖关系的 子元素,但咋没看到什么地方在注册依赖关系呢?
哈哈,其实这个问题就是上面 一小节中买关子的地方。回答这个问题,还是先回到源码中去。但在 InheritedElement 类中确实没发现有注册依赖关系的方法呀。那没办法了,只能向父类中探索:InheritedElement--->ProxyElement--->ComponentElement-->Element
最终发现 Element 实现了 BuildContext 接口,在 BuildContext 中定义了如下三个方法。
/// Registers this build context with [ancestor] such that when
/// [ancestor]'s widget changes this build context is rebuilt.
///
/// Returns `ancestor.widget`.
///
/// This method is rarely called directly. Most applications should use
/// [inheritFromWidgetOfExactType], which calls this method after finding
/// the appropriate [InheritedElement] ancestor.
///
/// All of the qualifications about when [inheritFromWidgetOfExactType] can
/// be called apply to this method as well.
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect });
/// Obtains the nearest widget of the given type, which must be the type of a
/// concrete [InheritedWidget] subclass, and registers this build context with
/// that widget such that when that widget changes (or a new widget of that
/// type is introduced, or the widget goes away), this build context is
/// rebuilt so that it can obtain new values from that widget.
///
/// This is typically called implicitly from `of()` static methods, e.g.
/// [Theme.of].
///
/// This method should not be called from widget constructors or from
/// [State.initState] methods, because those methods would not get called
/// again if the inherited value were to change. To ensure that the widget
/// correctly updates itself when the inherited value changes, only call this
/// (directly or indirectly) from build methods, layout and paint callbacks, or
/// from [State.didChangeDependencies].
///
/// This method should not be called from [State.dispose] because the element
/// tree is no longer stable at that time. To refer to an ancestor from that
/// method, save a reference to the ancestor in [State.didChangeDependencies].
/// It is safe to use this method from [State.deactivate], which is called
/// whenever the widget is removed from the tree.
///
/// It is also possible to call this method from interaction event handlers
/// (e.g. gesture callbacks) or timers, to obtain a value once, if that value
/// is not going to be cached and reused later.
///
/// Calling this method is O(1) with a small constant factor, but will lead to
/// the widget being rebuilt more often.
///
/// Once a widget registers a dependency on a particular type by calling this
/// method, it will be rebuilt, and [State.didChangeDependencies] will be
/// called, whenever changes occur relating to that widget until the next time
/// the widget or one of its ancestors is moved (for example, because an
/// ancestor is added or removed).
///
/// The [aspect] parameter is only used when [targetType] is an
/// [InheritedWidget] subclasses that supports partial updates, like
/// [InheritedModel]. It specifies what "aspect" of the inherited
/// widget this context depends on.
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect });
/// Obtains the element corresponding to the nearest widget of the given type,
/// which must be the type of a concrete [InheritedWidget] subclass.
///
/// Calling this method is O(1) with a small constant factor.
///
/// This method does not establish a relationship with the target in the way
/// that [inheritFromWidgetOfExactType] does.
///
/// This method should not be called from [State.dispose] because the element
/// tree is no longer stable at that time. To refer to an ancestor from that
/// method, save a reference to the ancestor by calling
/// [inheritFromWidgetOfExactType] in [State.didChangeDependencies]. It is
/// safe to use this method from [State.deactivate], which is called whenever
/// the widget is removed from the tree.
InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType);
看到 inheritFromElement()
对该方法的描述就知道了,子类肯定是实现了该方法后对子元素进行了依赖添加。 这里直接回到 Element 中看它是如何实现的:
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
return ancestor;
}
可以看到 inheritFromElement 方法中对 _dependencies 集合中添加了依赖关系,并触发子类 InheritedElement 中的 _dependents集合进行依赖更新。
那 inheritFromWidgetOfExactType()
和 ancestorInheritedElementForWidgetOfExactType()
两个方法有啥作用呢? 看上去都差不多。区别就是前者会注册依赖关系,而后者不会,所以在调用inheritFromWidgetOfExactType()时,InheritedWidget和依赖它的子孙组件关系便完成了注册,之后当InheritedWidget发生变化时,就会更新依赖它的子孙组件,也就是会调这些子孙组件的didChangeDependencies()方法和build()方法。而当调用的是 ancestorInheritedElementForWidgetOfExactType()时,由于没有注册依赖关系,所以之后当InheritedWidget发生变化时,就不会触发子孙组件的重建了。
看到这里是不是对 一小节中提出面临的性能问题是不是就有解决的思路了呢?
下面来将上面的代码进行修改,首先要让参不差与重构的界面变得可配置。
//定义一个方法,方便子树中的widget获取共享数据
static MyInheritedWidget of(BuildContext context, {bool rebuild = true}) {
return rebuild
? (context.inheritFromWidgetOfExactType(MyInheritedWidget) as MyInheritedWidget)
: ((context.ancestorInheritedElementForWidgetOfExactType(MyInheritedWidget).widget) as MyInheritedWidget);
}
其次,为了方便数据的获取和通知子组件的刷新,应该再建立一个 管理者封装到 StatefulWidget 中去。
class InheritedWidgetManger extends StatefulWidget {
InheritedWidgetManger({Key key, this.child, this.data}) : super(key: key);
final Widget child;
InheritedTestModel data;
@override
InheritedWidgetMangerState createState() => InheritedWidgetMangerState();
//定义一个方法,方便子树中的widget获取共享数据
static MyInheritedWidget of(BuildContext context, {bool rebuild = true}) {
return rebuild
? (context.inheritFromWidgetOfExactType(MyInheritedWidget) as MyInheritedWidget)
: ((context.ancestorInheritedElementForWidgetOfExactType(MyInheritedWidget).widget) as MyInheritedWidget);
}
}
class InheritedWidgetMangerState extends State<InheritedWidgetManger> {
@override
Widget build(BuildContext context) {
//将最新的数据往下传递
return MyInheritedWidget(
data: widget.data,
child: widget.child,
mangerState: this,
);
}
//提供一个方法修改数据,并通知子组件 刷新
void updateData(InheritedTestModel model) {
setState(() {
widget.data = model;
});
}
}
然后 MyInheritedWidget 组件也可以相应的简化了。
// 组件共享数据的容器
class MyInheritedWidget extends InheritedWidget {
//数据的Mode
InheritedTestModel data;
InheritedWidgetMangerState mangerState;
MyInheritedWidget({
Key key,
@required this.data,
@required Widget child,
this.mangerState
}) : super(key: key, child: child);
//是否重建widget就取决于数据是否相同
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
//判断数据是否反生改变,如果为真 所有子组件的 build 方法会被调用;
// 有状态组件 didChangeDependencies()方法会被调用
return data != oldWidget.data;
}
}
在需要共享的组件树根部因此不再使用 MyInheritedWidget ,而是用最新创建的 InheritedWidgetManger。代码如下:
//主界面
class DataShardPage extends StatefulWidget {
@override
_DataShardPageState createState() => _DataShardPageState();
}
class _DataShardPageState extends State<DataShardPage> {
//用于存储数据的 Mode,初始化数据 为 0
InheritedTestModel inheritedTestModel = InheritedTestModel(0);
@override
Widget build(BuildContext context) {
//在该组件下的 子组件可以收到消息通知
return InheritedWidgetManger(
data: inheritedTestModel,
child: Scaffold(
floatingActionButton: FloatingActionButton(
elevation: 0,
child: WidgetTestPageButton(),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
appBar: AppBar(
title: Text("InheritedWidget"),
),
body: Center(
child: ListView(
children: <Widget>[
//创建 三个 页面
WidgetTestPageOne(),
WidgetTestPageTwo(),
WidgetTestPageThree(),
],
))));
}
}
第一个界面做如下修改:
// 第一个页面
class WidgetTestPageOne extends StatefulWidget {
@override
_WidgetTestPageOneState createState() => _WidgetTestPageOneState();
}
class _WidgetTestPageOneState extends State<WidgetTestPageOne> {
@override
Widget build(BuildContext context) {
print("WidgetTestPageOne___build");
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Text("One页面不参与订阅:"));
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("WidgetTestPageOne___didChangeDependencies");
}
}
第二个界面做如下修改:
//第二个页面
class WidgetTestPageTwo extends StatefulWidget {
@override
_WidgetTestPageTwoState createState() => _WidgetTestPageTwoState();
}
class _WidgetTestPageTwoState extends State<WidgetTestPageTwo> {
@override
Widget build(BuildContext context) {
//可控制的
final inheritedWidgetManger = InheritedWidgetManger.of(context,rebuild: true);
final inheritedTestModel = inheritedWidgetManger.data;
print("WidgetTestPageTwo____${inheritedTestModel.count}");
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Text("Two页面获取到最新值:${inheritedTestModel.count}"));
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("WidgetTestPageTwo___didChangeDependencies");
}
}
第三个界面代码如下:
//第三个页面
class WidgetTestPageThree extends StatefulWidget {
@override
_WidgetTestPageThreeState createState() => _WidgetTestPageThreeState();
}
class _WidgetTestPageThreeState extends State<WidgetTestPageThree> {
@override
Widget build(BuildContext context) {
final myInheritedWidget = InheritedWidgetManger.of(context);
final inheritedTestModel = myInheritedWidget.data;
print("WidgetTestPageThree____${inheritedTestModel.count}");
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Text("Three页面获取到最新值:${inheritedTestModel.count}"));
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("WidgetTestPageThree___didChangeDependencies");
}
}
最终效果如下:
控制台输出如下:
I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
I/flutter ( 2882): WidgetTestPageTwo____1
I/flutter ( 2882): WidgetTestPageThree____1
I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
I/flutter ( 2882): WidgetTestPageTwo____2
I/flutter ( 2882): WidgetTestPageThree____2
I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
I/flutter ( 2882): WidgetTestPageTwo____3
I/flutter ( 2882): WidgetTestPageThree____3
可以看出第一个界面 在没引用 InheritedWidget 的情况,界面不在发生重建,第二个界面中 InheritedWidgetManger 中提供的方法 可配置 rebuild 参数是否可以重建该界面。
1、当 rebuild 为 true 时会将共享的组件树 中注册依赖关系,即将Widget添加到订阅者列表中。
2、当 rebuild 为 false 时仍然可以访问数据,但不使用 InheritedWidget 的内部实现。
本次修改完美规避了上面提到的性能问题,但是还有很多值得优化的地方,比如案例中使用的 InheritedTestModel 在实际开发中为了更加灵活可用 建议使用 泛型代替。
写在最后
当组件树中的依赖发生变化时 不仅会触发 didChangeDependencies() 方法的调用,在依赖改变后 Framework 还会调用 build()方法,因此在实际开发中 不建议将耗时类的操作放到 build() 方法中去,如需耗时类工作 建议复写 didChangeDependencies() 方法在这里面完成。这样可以避免每次build()都执行这些耗时类工作带来的性能问题。
[注意:]
笔者在写文章时是用的早期flutter 2.0以前版本,2.0后请将 文章中的 inheritFromWidgetOfExactType
替换成 dependOnInheritedWidgetOfExactType
, ancestorInheritedElementForWidgetOfExactType
替换成 findAncestorWidgetOfExactType
新的api使用。
好了本章节到此结束,又到了说再见的时候了,如果你喜欢请留下你的小红星;你们的支持才是创作的动力,如有错误,请热心的你留言指正, 谢谢大家观看,下章再会 O(∩_∩)O
实例源码地址:https://github.com/zhengzaihong/flutter_learn/blob/master/lib/page/Inherited/DataShardPage.dart