转载请联系: 微信号: michaelzhoujayredux
原文请访问个人博客缓存
本文主要介绍InheritedWidget的设计目的、用法以及推荐的最佳实践微信
Flutter的Widget层级能够作得很是深,在Widget层级间传递数据会让效率变得很低,也会多处不少bolierplate代码。less
例以下面的accountId
,scopeId
,若是MyWidget肯本用不到它们,accountId
, scopeId
仍是要做为MyWidget的final参数,由于它们要做为构造函数的参数接受参数值并向MyOtherWidget
传递,先得很是冗余,由于MyWidget彻底没有必要知道这两个参数。ide
class MyPage extends StatelessWidget {
final int accountId;
final int scopeId;
MyPage(this.accountId, this.scopeId);
Widget build(BuildContext context) {
return new MyWidget(accountId, scopeId);
}
}
class MyWidget extends StatelessWidget {
final int accountId;
final int scopeId;
MyWidget(this.accountId, this.scopeId);
Widget build(BuildContext context) {
// somewhere down the line
new MyOtherWidget(accountId, scopeId);
...
}
}
class MyOtherWidget extends StatelessWidget {
final int accountId;
final int scopeId;
MyOtherWidget(this.accountId, this.scopeId);
Widget build(BuildContext context) {
// rinse and repeat
...
复制代码
经过引进InheritedWidget,咱们就能解决这个困扰。函数
InheritedWidget的思想和redux的思想差很少,所谓accountId
,scopeId
其实是这些widget的状态,用户交互或其余事件会触发Action,Action产生新的State,Widget接受新的State生成新的RenderNode挂载到Widget Tree上,完成一次渲染。ui
那怎么让这些参数在不一样层级之间传递呢?答案是将State往上放,也就是俗称state up lifting
this
例以下面的代码就是经过插入一个InheritedWidget
将accountId
和scopeId
存储在InheritedWidget描述的这一层级中,全部这一层级和下面的层级都能访问这两个参数。spa
class MyInheritedWidget extends InheritedWidget {
final int accountId;
final int scopeId;
MyInheritedWidget(accountId, scopeId, child): super(child);
@override
bool updateShouldNotify(MyInheritedWidget old) =>
accountId != old.accountId || scopeId != old.scopeId;
}
class MyPage extends StatelessWidget {
final int accountId;
final int scopeId;
MyPage(this.accountId, this.scopeId);
Widget build(BuildContext context) {
return new MyInheritedWidget(
accountId,
scopeId,
const MyWidget(),
);
}
}
class MyWidget extends StatelessWidget {
const MyWidget();
Widget build(BuildContext context) {
// somewhere down the line
const MyOtherWidget();
...
}
}
class MyOtherWidget extends StatelessWidget {
const MyOtherWidget();
Widget build(BuildContext context) {
final myInheritedWidget = MyInheritedWidget.of(context);
print(myInheritedWidget.scopeId);
print(myInheritedWidget.accountId);
...
复制代码
注意:设计
InheritedWidget
的child
是const修饰的构造函数,这样作的目的是让child能缓存accountId
或者scopeId
值更新的时候,MyInheritedWidget会被从新建立,可是它的child不必定会被建立,取决因而否用到了accountId
或者scopeId
,上面的这个例子中MyOtherWidget
会被rebuild,可是MyWidget
不会被rebuildInheritedWidget
会被rebuild,可是child一样不必定被rebuild,由于accountId
和scopeId
没变。为了让InheritedWidget更加高效,推荐下面的最佳实践:
尽可能让你的Context语义尽量单一,这样Flutter渲染时才能更细粒度的判断哪些Widget须要rebuild哪些须要复用,不然就失去了InheritedWidget
的意义。
要这样
class TeamContext {
int teamId;
String teamName;
}
class StudentContext {
int studentId;
String studentName;
}
class ClassContext {
int classId;
...
}
复制代码
不要这样
class MyAppContext {
int teamId;
String teamName;
int studentId;
String studentName;
int classId;
...
}
复制代码
若是没有const,InheritedWidget
的整个child都不会缓存,选择性rebuild整个子tree就不会生效。
由于Flutter的整个App是一个Tree,因此InheritedWidget
能够放在任意一个层级,这样显然不利于人脑来理解并管理,因此必须你们规约好,例如规定InheritedWidget
只接受Scaffold
做为child,这样全部的InheritedWidget
最大粒度是Page,便于人脑理解及阅读。
Flutter眼里没有page不page的,全部东西就是widget,若是使用Navigation.push
一个Widget请注意route的下一个widget没有继承上一个widget的InheritedWidget的Context,你须要明确将参数传递,而不是将InheriedWidget往上提。