局部状态管理
- Flutter局部状态管理是通过注册InheritedElement dependencies实现,为此构造了InheritedWidget和InheritedModel来实现数据状态管理.
- InheritedWidget中内部包括了一个泛型的data类型,用于接受数据,它本身是一个StatefulWidget,用于包装child并为child提供数据,这样以
InheritedWidget
下所有的子节点就能访问它的data
.
InheritedWidget
- InheritedWidget > ProxyWidget > Widget
- 在widget树创建的时候,会通过BuilderOwner创建
InheritedElement
,由InheritedElement
来管理它的数据
同时提供了一个方法用于确定是否需要更新子视图,具体逻辑由InheritedElement
实现,InheritedElement
继承于Element
InheritedElement createElement() => InheritedElement(this);
bool updateShouldNotify(covariant InheritedWidget oldWidget);
- 获取数据并注册监听,
InheritedElement
实现,InheritedElement
继承于Element
//子视图向`InheritedWidget`中注册监听事件
class Element{
//用于缓存当前Element节点以及向上查找所有InheritedWidget,保存他们的的Element元素
Map<Type, InheritedElement> _inheritedWidgets;
Set<InheritedElement> _dependencies;
....
@override
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
//获取对应的InheritedElement,通过element又能获取对应的widget,这样就能拿到新的数据了
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
//承接上面的方法,在每次获取数据的时候都会向当前的指定IheritedWidget(ExactType<XXXInheritedWidget>)注册依赖,这样每当这个InheritedWidget有数据更新时就会接收到通知。
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
//this为当前的订阅者,将它传递给Inherited ancestor.
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
class InheritedElement extends ProxyElement {
...
//每个InheritedElement都会更新`_inheritedWidgets`,确保从它开始到rootWidget所有的_inheritedWidgets引用都能获取到
@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;
}
//保存对应的aspect,这个在InheritedElement中暂时用不到,默认都为null,是为了个InheritedModel使用
final Map<Element, Object> _dependents = HashMap<Element, Object>();
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
- 触发数据更新,并传递给它的监听者
首先需要重建InheritedWidget
,重新修改它的data
class Element {
...
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
child.update(newWidget);
}
//更新代理Element
class ProxyElement extends ComponentElement {
...
void updated(covariant ProxyWidget oldWidget) {
//通知代理Widget,是否需要更新它的订阅者
notifyClients(oldWidget);
}
}
class InheritedElement extends ProxyElement {
void notifyClients(InheritedWidget oldWidget) {
...
notifyDependent(oldWidget, dependent);
}
//通知子视图重建
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
//此处的dependent及为我们前面所注册的订阅者,标记为dirty执行构建
dependent.didChangeDependencies();
}
//重写`ProxyElement`,决定是否需要更新
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
}
关键点: _inheritedWidgets
包含了所有的InheritedElement
的被订阅者,_dependents
包含了当前所有的订阅者
InheritedModel介绍
- InheritedModel继承成于InheritedWidget,实现原理完全相同
- 相比较
InheritedWidget
它增加了一个标志位的属性,用于确定在当前的InheritedModel
中可以选择具体某个属性更新
abstract class InheritedModel<T> extends InheritedWidget
@protected
bool updateShouldNotifyDependent(covariant InheritedModel<T> oldWidget, Set<T> dependencies);
class InheritedModelElement<T> extends InheritedElement {
/// Creates an element that uses the given widget as its configuration.
InheritedModelElement(InheritedModel<T> widget) : super(widget);
@override
InheritedModel<T> get widget => super.widget as InheritedModel<T>;
@override
void updateDependencies(Element dependent, Object aspect) {
...
setDependencies(dependent, (dependencies ?? HashSet<T>())..add(aspect as T));
}
@override
void notifyDependent(InheritedModel<T> oldWidget, Element dependent) {
//获取当前订阅者指定的标志位 `aspect`
final Set<T> dependencies = getDependencies(dependent) as Set<T>;
if (dependencies == null)
return;
if (dependencies.isEmpty || widget.updateShouldNotifyDependent(oldWidget, dependencies))
dependent.didChangeDependencies();
}
}
代码实现
import 'package:flutter/material.dart';
class InheritedWidgetDemo extends StatefulWidget {
@override
_InheritedWidgetDemoState createState() => _InheritedWidgetDemoState();
}
class _InheritedWidgetDemoState extends State<InheritedWidgetDemo> {
int middleCount = 0;
int bottomCount = 0;
@override
void initState() {
super.initState();
print('_InheritedWidgetDemoState init');
}
@override
Widget build(BuildContext context) {
context.findAncestorStateOfType<_InheritedWidgetDemoState>();
return Container(
child: Column(
children: <Widget>[
StateLessWidgetA(),
GestureDetector(
child: SharedDataInheritedWidget(
data: middleCount,
child: SharedDataInheritedWidgetContainer(),
),
onTap: (){
middleCount++;
setState(() {
});
},
),
GestureDetector(
child: SharedDataInheritedModel(
first: bottomCount,
second: 2,
third: 3,
child: SharedDataInheritedModelContainer(),
),
onTap: (){
bottomCount++;
setState(() {
});
},
),
],
),
);
}
void onTap() {
print('onTap ......');
}
}
class StateLessWidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
print('StateLessWidgetA build');
return Padding(
padding: EdgeInsets.all(20.0),
child: Text('StateLessWidgetA'),
);
}
}
//// ======= InheritedWidget ======== ////
class SharedDataInheritedWidget extends InheritedWidget {
final int data;
SharedDataInheritedWidget({this.data, Widget child}) : super(child: child);
@override
bool updateShouldNotify(SharedDataInheritedWidget oldWidget) {
return this.data != oldWidget.data;
}
static SharedDataInheritedWidget of(BuildContext context,
{bool listener = true}) {
return listener
? context.dependOnInheritedWidgetOfExactType<
SharedDataInheritedWidget>() ??
null
: context.getElementForInheritedWidgetOfExactType<
SharedDataInheritedWidget>();
}
}
class SharedDataInheritedWidgetContainer extends StatefulWidget {
@override
_SharedDataInheritedWidgetContainer createState() =>
_SharedDataInheritedWidgetContainer();
}
class _SharedDataInheritedWidgetContainer
extends State<SharedDataInheritedWidgetContainer> {
@override
Widget build(BuildContext context) {
final data = SharedDataInheritedWidget.of(context).data;
print('StateLessWidgetB build');
return Padding(
padding: EdgeInsets.all(20.0),
child: Text('StateLessWidgetB :$data'),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('_SharedDataInheritedWidgetContainer didChangeDependencies');
}
}
//// ======= InheritedModel ========== ////
enum ShareDataDependent {
one,
two,
three,
}
class SharedDataInheritedModel extends InheritedModel {
final int first;
final int second;
final int third;
final Widget child;
SharedDataInheritedModel({this.first, this.second, this.third, this.child})
: super(child: child);
@override
bool updateShouldNotify(SharedDataInheritedModel oldWidget) {
return first != oldWidget.first ||
second != oldWidget.second ||
third != oldWidget.third;
}
@override
bool updateShouldNotifyDependent(
SharedDataInheritedModel oldWidget, Set dependencies) {
return first != oldWidget.first &&
dependencies.contains(ShareDataDependent.one) ||
second != oldWidget.second &&
dependencies.contains(ShareDataDependent.two) ||
third != oldWidget.third &&
dependencies.contains(ShareDataDependent.three);
}
static SharedDataInheritedModel of(BuildContext context,
{bool listener = true, ShareDataDependent aspect}) {
return listener
? context.dependOnInheritedWidgetOfExactType<
SharedDataInheritedModel>(aspect: aspect) ??
null
: context.getElementForInheritedWidgetOfExactType<
SharedDataInheritedModel>();
}
}
class SharedDataInheritedModelContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
final sharedDataInheritedModel = SharedDataInheritedModel.of(context, aspect: ShareDataDependent.one);
return Container(
color: Colors.amber[100],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text('1: ${sharedDataInheritedModel.first}'),
Text('2: ${sharedDataInheritedModel.second}'),
Text('3: ${sharedDataInheritedModel.third}'),
],
),
);
}
}
网友评论