做flutter项目永远离不开状态管理,状态管理随处可见,如果用不好就会造成频繁刷新严重影响性能,要学好诸如getx、provider、riverpord等状态管理框架,我们就要先打好flutter自带的InheritedWidget状态管理,才能为后面的学习做好铺垫。今天我们来窥探状态管理的底层原理,首先我们看看它是怎么用的,然后再做最详细的解析。
第一步:创建继承自InheritedWidget的CounterInheritedWidget类
第二步:创建包装CounterInheritedWidget的CounterWidget类
第三步:创建使用CounterInheritedWidget内部变量的CounterText类
///首先创建一个inheritedWidget状态共享类
class CounterInheritedWidget extends InheritedWidget {
final int counter;
final Widget child;
CounterInheritedWidget({
Key? key,
required this.counter,
required this.child,
}) : super(key: key, child: child);
@override
bool updateShouldNotify(CounterInheritedWidget oldWidget) {
return counter != oldWidget.counter;
}
static CounterInheritedWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CounterInheritedWidget>();
}
}
//创建一个使用InheritedWidget的类包装的widget
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return CounterInheritedWidget(
counter: _counter,
child: Scaffold(
appBar: AppBar(
title: Text('InheritedWidget Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
CounterText(), // 显示计数器值的子组件
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
}
}
///使用inheritedWidget中的变量
class CounterText extends StatelessWidget {
@override
Widget build(BuildContext context) {
///注意这个方法的使用,非常重要。
final inheritedWidget = CounterInheritedWidget.of(context);
return Text(
'${inheritedWidget?.counter ?? 0}',
style: Theme.of(context).textTheme.headline4,
);
}
}
1.其中创建了三个widget,一个InheritedWidget类型的widget,一个包装InheritedWidget类型的wiget,一个使用InheritedWidget的widget(这里单独创建一个是为了局部更新)
2.关键点,创建,包装,使用,缺一不可。
3.必须通过CounterInheritedWidget.of(context);这种方式拿到InheritedWidget并使用其中的变量
现在我们开始分析其使用原理
首先分析dependOnInheritedWidgetOfExactType这个方法是做什么的。因为第一步会走这个方法
///CounterText的element
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
//这里并不为空,因为顶层的inheritiedwidget挂载的时候已经创建了并将自身保存在了_inheritedElements中
final InheritedElement? ancestor = _inheritedElements == null ? null : _inheritedElements![T];
if (ancestor != null) {
///由于顶层包装了inheritiedwidget,所以最终会走这里
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
///这里先取出自身保存的_dependencies节点,顾名思义,依赖,所以最终会将自身依赖的inheritedwidget节点添加进_dependencies集合中
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
_dependencies ??= HashSet<InheritedElement>();
///这里会将inheritedwidget添加进集合中
_dependencies!.add(ancestor);
///并调用自身所依赖的所有inheritedwidget的updateDependencies方法。就是上面案例中的方法
ancestor.updateDependencies(this, aspect);
return ancestor.widget as InheritedWidget;
}
带着两个疑问向下看
1._inheritedElements为什么不为null?
2._inheritedElements什么时候被初始化?
咱们先总结一下上面的逻辑
1.将当前使用dependOnInheritedWidgetOfExactType这个方法的widget的_inheritedElements拿到,_inheritedElements是map类型,所以通过_inheritedElements则拿到对应的CounterInheritedWidget,即获取到当前widget即上面案例的CounterText的依赖,也是最近的依赖啦。
2.再去获取CounterText的所有依赖集合_dependencies,即所有的inheritedwidget的集合,然后将CounterInheritedWidget加入到_dependencies集合中,并调用所有的依赖即inheritedwidget的updateDependencies的方法,即案例中的CounterInheritedWidget的方法。
首先我们先解决上面两个疑问
@mustCallSuper
void mount(Element? parent, Object? newSlot) {
if (parent != null) {
_owner = parent.owner;
}
assert(owner != null);
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
///开始执行
_updateInheritance();
attachNotificationTree();
}
///InheritedElement
@override
void _updateInheritance() {
assert(_lifecycleState == _ElementLifecycle.active);
final PersistentHashMap<Type, InheritedElement> incomingWidgets =
_parent?._inheritedElements ?? const PersistentHashMap<Type, InheritedElement>.empty();
_inheritedElements = incomingWidgets.put(widget.runtimeType, this);
}
///element
void _updateInheritance() {
assert(_lifecycleState == _ElementLifecycle.active);
_inheritedElements = _parent?._inheritedElements;
}
1.当widget开始挂载的时候,调用mout,同时调用实现了_updateInheritance的widget,即先调用inheritedElement的_updateInheritance并初始化_inheritedElements。
2.直到子widget即上面案例中的CounterText开始挂载的时候拿到父widget的_inheritedElements。
前置逻辑
解决了这两个问题, 我们继续跟着代码往下看inheritedElement中的updateDependencies方法
class InheritedElement extends ProxyElement {
final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
@protected
void updateDependencies(Element dependent, Object? aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object? value) {
///记录调用InheritedElement的子widget
_dependents[dependent] = value;
}
}
1.InheritedElement同时也对子widget即调用了dependOnInheritedWidgetOfExactType的widget做了记录保存
2.这样操作是为了后面方便更新。
重点:inheritedElement也对子widget进行了保存的操作
疑问:InheritedElement怎么做到局部更新的呢?
abstract class ProxyElement extends ComponentElement {
/// Initializes fields for subclasses.
ProxyElement(ProxyWidget super.widget);
@override
Widget build() => (widget as ProxyWidget).child;
///当调用了setstate等标记脏数据,即开始进入build流程
@override
void update(ProxyWidget newWidget) {
///先记录旧的widget配置,即当前inheritedWidget
final ProxyWidget oldWidget = widget as ProxyWidget;
assert(widget != newWidget);
///更新widget,当然满足条件的话就会更新
super.update(newWidget);
assert(widget == newWidget);
///主要看这个方法做了什么
updated(oldWidget);
rebuild(force: true);
}
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
}
class InheritedElement extends ProxyElement {
@override
void notifyClients(InheritedWidget oldWidget) {
for (final Element dependent in _dependents.keys) {
///遍历所有的key,即前面InheritedElement保存的所有依赖其自身的子widget!
notifyDependent(oldWidget, dependent);
}
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
///将所有依赖InheritedElement的子widget调用didChangeDependencies的生命周期方法。
dependent.didChangeDependencies();
}
@override
void updated(InheritedWidget oldWidget) {
if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) {
super.updated(oldWidget);
}
}
}
1.当调用setstate的时候,首先会调用ProxyElement(即InheritedElement的父类)的update方法,先保存记录当前的widget。
2.调用InheritedElement的updated方法,其中调用了实现Inheritedwidget的CounterInheritedWidget的updateShouldNotify方法进行对比,如果返回true,即内部使用的变量发生变化,则调用父类的updated方法。
3.在父类的updated方法中再调用InheritedElement的notifyDependent方法,遍历调用所有依赖inheritedwidget的子widget的didChangeDependencies方法。
关键就在didChangeDependencies方法!!!我们重点看看
///element
@mustCallSuper
void didChangeDependencies() {
assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
///标记脏数据,请求更新一帧
markNeedsBuild();
}
从setstate开始执行
- 更新逻辑第一次走进了InheritedElement的父类ProxyElement的update方法
- 然后进了InheritedElement的updated中执行updateShouldNotify(CounterInheritedWidget实现的)的方法做变量判断是否有变化
- 如果有变化执行父类ProxyElement的updated方法调用notifyClients通知依赖发生变化
- 此时所有依赖inheritedelement的即使用了inheritedwidget的变量的widget都开始执行didChangeDependencies方法,此方法内部调用了markNeedsBuild即请求刷新数据