内容简介:我们先来看一下
我们先来看一下 InheritedWidget
是如何实现共享数据的.
以下是官网上的一个小例子
class ShareDataWidget extends InheritedWidget { ShareDataWidget({ @required this.data, Widget child }) :super(child: child); final int data; //需要在子树中共享的数据,保存点击次数 //定义一个便捷方法,方便子树中的widget获取共享数据 static ShareDataWidget of(BuildContext context) { return context.inheritFromWidgetOfExactType(ShareDataWidget); } //该回调决定当data发生变化时,是否通知子树中依赖data的Widget @override bool updateShouldNotify(ShareDataWidget old) { //如果返回true,则子树中依赖(build函数中有调用)本widget //的子widget的`state.didChangeDependencies`会被调用 return old.data != data; } } class _TestWidget extends StatefulWidget { @override __TestWidgetState createState() => new __TestWidgetState(); } class __TestWidgetState extends State<_TestWidget> { @override Widget build(BuildContext context) { //使用InheritedWidget中的共享数据 return Text(ShareDataWidget .of(context) .data .toString()); } @override void didChangeDependencies() { super.didChangeDependencies(); //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。 //如果build中没有依赖InheritedWidget,则此回调不会被调用。 print("Dependencies change"); } } class InheritedWidgetTestRoute extends StatefulWidget { @override _InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState(); } class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> { int count = 0; @override Widget build(BuildContext context) { return Center( child: ShareDataWidget( //使用ShareDataWidget data: count, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Padding( padding: const EdgeInsets.only(bottom: 20.0), child: _TestWidget(),//子widget中依赖ShareDataWidget ), RaisedButton( child: Text("Increment"), //每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新 onPressed: () => setState(() => ++count), ) ], ), ), ); } } 复制代码
这里需要注意:
-
context.inheritFromWidgetOfExactType(ShareDataWidget)
来获取到指定InheritedWidget
中的数据, 实际通知的时候是父Widget往下传递还是子Widget往上遍历呢?
接下lai 探索原因
InheritedWidget
的源码很简单, 继承了 ProxyWidget
, 也没有实现太多逻辑, 对 createElement
进行了实现, 还定义了 updateShouldNotify
, 该方法的意思就是更新的时候是否应该通知在 build
阶段通过 inheritFromWidgetOfExactType
查找该 Widget
的子 Widget
.
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); } 复制代码
顺藤摸瓜, 我们去就去看 InheritedElement(this)
做了什么, 进去之后我们可以发现 _updateInheritance
方法.
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; } 复制代码
同时我们对比一下普通的 Element
的该方法实现, 只是简单的将父 Element
的 _inheritedWidgets
属性保存到自身(这样就保证了父级的向子集传递特性).
void _updateInheritance() { assert(_active); _inheritedWidgets = _parent?._inheritedWidgets; } 复制代码
但是这个 _inheritedWidgets
属性又是在哪里出现呢? 它定义在 Element
中, 每一个实例都有这个属性. 它的作用是存储上级节点 Widget
和 Element
之间的映射.
abstract class Element extends DiagnosticableTree implements BuildContext { /// Creates an element that uses the given widget as its configuration. /// /// Typically called by an override of [Widget.createElement]. Element(Widget widget) : assert(widget != null), _widget = widget; Element _parent; ... Map<Type, InheritedElement> _inheritedWidgets; 复制代码
现在我们回到 InheritedElement
的实现.
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; } 复制代码
InheritedElement
会将自身的信息添加到 _inheritedWidgets
属性中, 然后子孙都可以通过他们自身的该属性访问当前的 InheritedElement
了.
现在我们知道如何访问_inheritedWidgets属性以及包含的内容了, 那么通知机制是如何实现呢?
一开始例子中就是使用 inheritFromWidgetOfExactType(Type)
方法去获取到指定的 InherientWidget
的, 那么这个方法是怎么实现呢?
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 InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) { assert(ancestor != null); _dependencies ??= HashSet<InheritedElement>(); _dependencies.add(ancestor); ancestor.updateDependencies(this, aspect); return ancestor.widget; } 复制代码
首先会获取 Element(Context)
的 _inheritedWidgets
指定类型的 Element
, 如果获取到了, 则会添加到自身的依赖列表中, 祖先节点也有记录这个依赖, 这样在更新时候就可以直接通过 _dependencies
属性来进行通知了.
每一次 InherientElement
更新的时候, 都会调用 notifyClients
方法来通知子节点, 调用子节点的 didChangeDependencies
方法
void notifyClients(InheritedWidget oldWidget) { if (!widget.updateShouldNotify(oldWidget)) return; assert(_debugCheckOwnerBuildTargetExists('notifyClients')); for (Element dependent in _dependents) { 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)); dependent.didChangeDependencies(); } } 复制代码
以上就是 InherientWidget
的原理, 接下来就是看看 scoped_model
做了怎样的封装.
scoped_model
该库主要的类有:
abstract class Model extends Listenable
abstract class Model extends Listenable { final Set<VoidCallback> _listeners = Set<VoidCallback>(); int _version = 0; int _microtaskVersion = 0; void addListener(VoidCallback listener) { debugPrint("添加监听器"); listener(); _listeners.add(listener); } @override void removeListener(VoidCallback listener) { print("监听器被去除"); _listeners.remove(listener); } int get listenerCount => _listeners.length; @protected void notifyListeners() { if (_microtaskVersion == _version) { _microtaskVersion++; scheduleMicrotask(() { _version++; _microtaskVersion = _version; _listeners.toList().forEach((VoidCallback listener) => listener()); }); } } } 复制代码
Model
继承了 Listenable
, 值在改变的时候调用 notifyListeners
既可以通知到所有的监听器.
class ScopedModel<T extends Model> extends StatelessWidget
class ScopedModel<T extends Model> extends StatelessWidget { final T model; final Widget child; ScopedModel({@required this.model, @required this.child}) : assert(model != null), assert(child != null); @override Widget build(BuildContext context) { return AnimatedBuilder( animation: model, builder: (context, _) => _InheritedModel<T>(model: model, child: child), ); } // 找到 ScopeModel, 并且让子节点和祖先节点建立依赖 static T of<T extends Model>( BuildContext context, { bool rebuildOnChange = false, }) { final Type type = _type<_InheritedModel<T>>(); Widget widget = rebuildOnChange ? context.inheritFromWidgetOfExactType(type) : context.ancestorInheritedElementForWidgetOfExactType(type)?.widget; if (widget == null) { throw ScopedModelError(); } else { return (widget as _InheritedModel<T>).model; } } static Type _type<T>() => T; } 复制代码
通知方法
作者在构造 ScopedModel
的时候, 使用了 AnimationBuilder
, 这里会注册一个监听器到 Model
中, 然后每一次值的改变都会调用 AnimationBuilder.builder
方法, 然后就会触发 InherientWidget
的改变, 根据 updateShouldNotify
来决定是否通知子孙控件更新, 但是在这里我们并没有看到 InherientWidget
的影子, 让我们接着往下看.
class _InheritedModel<T extends Model> extends InheritedWidget
class _InheritedModel<T extends Model> extends InheritedWidget { final T model; final int version; _InheritedModel({Key key, Widget child, T model}) : this.model = model, this.version = model._version, super(key: key, child: child); @override bool updateShouldNotify(_InheritedModel<T> oldWidget) => (oldWidget.version != version); } 复制代码
看到这个类, 我们就可以发现作者的实现方式了, _InheritedModel
继承自 InheritedWidget
, 上方 ScopedModel.build()
方法, 里面就返回了该 inherientWidget
对象, 但是与上方的例子还缺少子类对父类进行依赖的一步.
class ScopedModelDescendant<T extends Model> extends StatelessWidget
typedef Widget ScopedModelDescendantBuilder<T extends Model>( BuildContext context, Widget child, T model, ); class ScopedModelDescendant<T extends Model> extends StatelessWidget { final ScopedModelDescendantBuilder<T> builder; final Widget child; final bool rebuildOnChange; ScopedModelDescendant({ @required this.builder, this.child, this.rebuildOnChange = true, }); @override Widget build(BuildContext context) { return builder( context, child, ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange), ); } } 复制代码
ScopedModelDescendant
就是 ScopedModel
的子孙, 那么可以看到它的 Build
方法, 里面有调用 ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange)
, 我们把这个方法在拿出来看一下:
static T of<T extends Model>( BuildContext context, { bool rebuildOnChange = false, }) { final Type type = _type<_InheritedModel<T>>(); Widget widget = rebuildOnChange ? context.inheritFromWidgetOfExactType(type) : context.ancestorInheritedElementForWidgetOfExactType(type)?.widget; if (widget == null) { throw ScopedModelError(); } else { return (widget as _InheritedModel<T>).model; } } static Type _type<T>() => T; 复制代码
到这里, 就可以发现它和 inherient
做法相同了, 首先获取到 InheritedWidget
的类型, _type<_InheritedModel<T>>()
得到 _InheritedModel<T>
, 然后判断是否需要在改变的时候重绘, 默认是 True
, 如果需要重绘就会调用 inheritFromWidgetOfExactType
去建立依赖, 如果为 false
, 则会调用 ancestorInheritedElementForWidgetOfExactType
, 这个方法不会建立依赖, 所以在改变的时候不会收到通知并重绘, 官方的注释有这么一句: This method does not establish a relationship with the target in the way that [inheritFromWidgetOfExactType] does.
最后贴一下作者提供的小例子
import 'package:flutter/material.dart'; import 'package:scoped_model/scoped_model.dart'; void main() { CounterModel model = CounterModel(); runApp(MyApp( model: model, )); } class MyApp extends StatelessWidget { final CounterModel model; const MyApp({Key key, @required this.model}) : super(key: key); @override Widget build(BuildContext context) { // At the top level of our app, we'll, create a ScopedModel Widget. This // will provide the CounterModel to all children in the app that request it // using a ScopedModelDescendant. return ScopedModel<CounterModel>( model: model, child: MaterialApp( title: 'Scoped Model Demo', home: CounterHome('Scoped Model Demo'), ), ); } } // Start by creating a class that has a counter and a method to increment it. // // Note: It must extend from Model. class CounterModel extends Model { int _counter = 0; int get counter => _counter; void increment() { // First, increment the counter _counter++; // Then notify all the listeners. notifyListeners(); } } class CounterHome extends StatelessWidget { final String title; CounterHome(this.title); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('You have pushed the button this many times:'), // Create a ScopedModelDescendant. This widget will get the // CounterModel from the nearest parent ScopedModel<CounterModel>. // It will hand that CounterModel to our builder method, and // rebuild any time the CounterModel changes (i.e. after we // `notifyListeners` in the Model). ScopedModelDescendant<CounterModel>( builder: (context, child, model) { return Text( model.counter.toString(), style: Theme.of(context).textTheme.display1, ); }, ), ], ), ), // Use the ScopedModelDescendant again in order to use the increment // method from the CounterModel floatingActionButton: ScopedModelDescendant<CounterModel>( builder: (context, child, model) { return FloatingActionButton( onPressed: model.increment, tooltip: 'Increment', child: Icon(Icons.add), ); }, ), ); } } 复制代码
这里也能验证我们上方所分析的, 首先需要构建 ScopedModel
, 然后共享状态的子孙节点通过 ScopedModelDescendant
来添加.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【源码阅读】AndPermission源码阅读
- 【源码阅读】Gson源码阅读
- 如何阅读Java源码 ,阅读java的真实体会
- 我的源码阅读之路:redux源码剖析
- JDK源码阅读(六):HashMap源码分析
- 如何阅读源码?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深度学习框架PyTorch:入门与实践
陈云 / 电子工业出版社 / 2018-1 / 65
《深度学习框架PyTorch:入门与实践》从多维数组Tensor开始,循序渐进地带领读者了解PyTorch各方面的基础知识。结合基础知识和前沿研究,带领读者从零开始完成几个经典有趣的深度学习小项目,包括GAN生成动漫头像、AI滤镜、AI写诗等。《深度学习框架PyTorch:入门与实践》没有简单机械地介绍各个函数接口的使用,而是尝试分门别类、循序渐进地向读者介绍PyTorch的知识,希望读者对PyT......一起来看看 《深度学习框架PyTorch:入门与实践》 这本书的介绍吧!