注册

跟我学flutter:细细品Widget(三)ProxyWidget,InheritedWidget

前言

跟我学flutter系列:
跟我学flutter:我们来举个例子通俗易懂讲解dart 中的 mixin
跟我学flutter:我们来举个例子通俗易懂讲解异步(一)ioslate
跟我学flutter:我们来举个例子通俗易懂讲解异步(二)ioslate循环机制

跟我学flutter:在国内如何发布自己的Plugin 或者 Package

跟我学flutter:Flutter雷达图表(一)如何使用kg_charts

跟我学flutter:细细品Widget(一)Widget&Element初识

跟我学flutter:细细品Widget(二)StatelessWidget&StatefulWidget

跟我学flutter:细细品Widget(三)ProxyWidget,InheritedWidget

跟我学flutter:细细品Widget(四)Widget 渲染过程 与 RenderObjectWidget

跟我学flutter:细细品Widget(五)Element

企业级篇目:
跟我学企业级flutter项目:用bloc手把手教你搭建用户认证系统
跟我学企业级flutter项目:dio网络框架增加公共请求参数&header
跟我学企业级flutter项目:如何用dio封装一套企业级可扩展高效的网络层
跟我学企业级flutter项目:如何封装一套易用,可扩展的Hybrid混合开发webview
跟我学企业级flutter项目:手把手教你制作一款低耦合空页面widget


ProxyWidget作为抽象基类本身没有任何功能,但他有两个实现类ParentDataWidget & InheritedElement


源码


abstract class ProxyWidget extends Widget {

const ProxyWidget({ Key? key, required this.child }) : super(key: key);

final Widget child;
}

InheritedWidget


InheritedWidget 用于在树上向下传递数据。


通过BuildContext.dependOnInheritedWidgetOfExactType可以获取最近的「Inherited Widget」,需要注意的是通过这种方式获取「Inherited Widget」时,当「Inherited Widget」状态有变化时,会导致该引用方 rebuild。


通常,为了使用方便会「Inherited Widget」会提供静态方法of,在该方法中调用BuildContext.dependOnInheritedWidgetOfExactType。of方法可以直接返回「Inherited Widget」,也可以是具体的数据。


有时,「Inherited Widget」是作为另一个类的实现细节而存在的,其本身是私有的(外部不可见),此时of方法就会放到对外公开的类上。最典型的例子就是Theme,其本身是StatelessWidget类型,但其内部创建了一个「Inherited Widget」:_InheritedTheme,of方法就定义在上Theme上:


  static ThemeData of(BuildContext context) {
final _InheritedTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
final MaterialLocalizations? localizations = Localizations.of(context, MaterialLocalizations);
final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike;
final ThemeData theme = inheritedTheme?.theme.data ?? _kFallbackTheme;
return ThemeData.localize(theme, theme.typography.geometryThemeFor(category));
}

该of方法返回的是ThemeData类型的具体数据,并在其内部首先调用了BuildContext.dependOnInheritedWidgetOfExactType。


我们经常使用的「Inherited Widget」莫过于MediaQuery,同样提供了of方法:


  static MediaQueryData of(BuildContext context) {
assert(context != null);
assert(debugCheckHasMediaQuery(context));
return context.dependOnInheritedWidgetOfExactType()!.data;
}

在这里插入图片描述


源码


abstract class InheritedWidget extends ProxyWidget {

const InheritedWidget({ Key? key, required Widget child })
:
super(key: key, child: child)
;

@override
InheritedElement createElement() => InheritedElement(this);

@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

createElement


「Inherited Widget」对应的 Element 为InheritedElement,一般情况下InheritedElement子类不用重写该方法;


updateShouldNotify


「Inherited Widget」rebuilt 时判断是否需要 rebuilt 那些依赖它的 Widget;


如下是MediaQuery.updateShouldNotify的实现,在新老Widget.data 不相等时才 rebuilt 那依赖的 Widget。


  @override
bool updateShouldNotify(MediaQuery oldWidget)
=> data != oldWidget.data;


依赖了 InheritedWidget 在数据变动的情况下 didChangeDependencies 会被调用,
依赖的意思是 使用 return context.dependOnInheritedWidgetOfExactType()
如果使用context.getElementForInheritedWidgetOfExactType().widget的话,只会用其中的数据,而不会重新rebuild



@override
InheritedElement getElementForInheritedWidgetOfExactType() {
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
@override
InheritedWidget dependOnInheritedWidgetOfExactType({ Object aspect }) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
//多出的部分
if (ancestor != null) {
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}

我们可以看到,dependOnInheritedWidgetOfExactType() 比 getElementForInheritedWidgetOfExactType()多调了dependOnInheritedElement方法,dependOnInheritedElement源码如下:


  @override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}

可以看到dependOnInheritedElement方法中主要是注册了依赖关系!看到这里也就清晰了,调用dependOnInheritedWidgetOfExactType() 和 getElementForInheritedWidgetOfExactType()的区别就是前者会注册依赖关系,而后者不会,所以在调用dependOnInheritedWidgetOfExactType()时,InheritedWidget和依赖它的子孙组件关系便完成了注册,之后当InheritedWidget发生变化时,就会更新依赖它的子孙组件,也就是会调这些子孙组件的didChangeDependencies()方法和build()方法。而当调用的是 getElementForInheritedWidgetOfExactType()时,由于没有注册依赖关系,所以之后当InheritedWidget发生变化时,就不会更新相应的子孙Widget。


文章内容参考:http://www.jb51.net/article/221…



0 个评论

要回复文章请先登录注册