状态管理分为局部状态管理和全局(大的局部)管理,其实所有的一切归根到底都是类似调用setState的逻辑,这里简单的梳理了一下全局状态管的学习思路
1. InheritedWidget
关于InheritedWidget其实就是一个特殊的Widget,被单独放到了一个数组里边(本质是这对应的element,放到了一个单独的数组里),其实这里这样做的好处是查找时候很方便,因为elementTree有时候是很庞大的,从最后一个往上查找找到对应的element是比较浪费资源的,这里定义一个widget,专门用来处理这种树结构传输数据,查找的时候只需要从这一堆中查找就可以了, InheritedWidget由此诞生
网上很多讲解这个类的文章,我也看了很多,但是真正让我感受到他的厉害之处的是我在研究适配暗黑模式的时候,我发现切换light到dark的时候,使用系统自带的Theme中的颜色属性,会马上变换颜色,而自已定义的颜色即使用brightness做了判断也是没用的,比如
class TestColor {
Color titleColor(BuildContext context) {
return Theme.of(context).brightness == Brightness.light?Colors.black:Colors.white;
}
}
我调用titleColor
但是切换模式,颜色并没有跟着变化,然后我点击到Theme.of(context)
函数里边发现系统的Theme是通过InheritedWidget传递的
函数里边调用了dependOnInheritedWidgetOfExactType
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({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;
}
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
其中这个函数ancestor.updateDependencies(this, aspect);
将当前的element加入到InheritedWidget 的_dependents中
@protected
void updateDependencies(Element dependent, Object? aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object? value) {
_dependents[dependent] = value;
}
然后在刷新的时候调用_dependents
中元素(Element)的didChangeDependencies
,而在didChangeDependencies
中,调用markNeedsBuild
,这样就实现了刷新
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (final Element dependent in _dependents.keys) {
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));
//关键函数
notifyDependent(oldWidget, dependent);
}
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
@mustCallSuper
void didChangeDependencies() {
assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
然后就研究为什么会调用notifyClients
这个函数,因为这个函数是我通过_dependents
检索出来的,只是结果去使用它,然后就是沿着notifyClients
往上找
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
@override
void update(ProxyWidget newWidget) {
final ProxyWidget oldWidget = widget;
assert(widget != null);
assert(widget != newWidget);
super.update(newWidget);
assert(widget == newWidget);
updated(oldWidget);
_dirty = true;
rebuild();
}
///更改用于配置此元素的小部件。
///
///当父元素希望使用另一个小部件来配置这个元素时,框架调用这个函数。新小部件保证具有与旧小部件相同的[runtimeType]。
///
///这个函数只在“active”生命周期中被调用。
@mustCallSuper
void update(covariant Widget newWidget) {
// This code is hot when hot reloading, so we try to
// only call _AssertionError._evaluateAssertion once.
assert(
_lifecycleState == _ElementLifecycle.active
&& widget != null
&& newWidget != null
&& newWidget != widget
&& depth != null
&& Widget.canUpdate(widget, newWidget),
);
// This Element was told to update and we can now release all the global key
// reservations of forgotten children. We cannot do this earlier because the
// forgotten children still represent global key duplications if the element
// never updates (the forgotten children are not removed from the tree
// until the call to update happens)
assert(() {
_debugForgottenChildrenWithGlobalKey.forEach(_debugRemoveGlobalKeyReservation);
_debugForgottenChildrenWithGlobalKey.clear();
return true;
}());
_widget = newWidget;
}
我们在更新element的widget时候会调用update
,而InheritedWidget本身就是widget,我们再更新的时候会走update
方法,然后根据widget.updateShouldNotify
判断是否需要调用notifyClients
是不是瞬间通透了呢?我是通透了,至少流程上是这样
所以上面说的暗黑模式切换颜色问题大家是不是有了解决的方法呢,自己定义一个InheritedWidget来处理自定义颜色,或者直接扩展系统的ThemeData,我选择了后者,图省事吧...
class CustomColorTheme {
static ThemeData themeData(BuildContext context) {
return Theme.of(context);
}
}
extension ThemeDataExtension on ThemeData {
Color get orgListBackColor => brightness == Brightness.light ? Colors.blue : Colors.grey[400]!;
}
这里我对扩展又套了一层,这个主要是为了敲代码有提示,extension如果和本类不在一个dart文件中,是没有提示的...蛋疼
调用
CustomColorTheme.themeData(context).orgListBackColor
是不是很酷
网友评论