我觉得ChangeNotifier的管理方法是最容易理解的了,因为他的流程是你在敲代码的时候就可以直观的看到的,哪里需要就在哪里监听,变化的时候来个setState搞定
1. ChangeNotifier
使用的时候需要继承自这个类比如叫A,我全局全局初始化了一个A实例a
class A extends ChangeNotifier {
int count = 0;
void add() {
count++;
notifyListeners();
}
}
final a = A();
然后在需要的地方添加a的监听回调
@override
void initState() {
// TODO: implement initState
super.initState();
a.addListener(() {
setState(() {
});
});
}
addListener
这个方法会将回调添加到数组中,当调用notifyListeners
时,遍历数组调用回调函数,这个时候就会执行setState函数,刷新页面.
这里可以优化, setState本质是调用markNeedsBuild
@override
void initState() {
// TODO: implement initState
super.initState();
a.addListener(() {
Element element = context as Element;
element.markNeedsBuild();
});
}
是不是很好理解,这种逻辑自己写10分钟应该用不了吧.
2. InheritedWidget + ChangeNotifier
这种方式可以有两种思路:
- 通过ChangeNotifier.listen去更新
- 通过InheritedWidget去更新
可能大家比较晕,下面具体阐述一下,第一种是以ChangeNotifier为主, InheritedWidget用来将ChangeNotifier在Widget数中传递,上一篇已经说明了,而第二种是以InheritedWidget为主,使用他的状态管理方法,而ChangeNotifier只是用来更新InheritedWidget用的,因为只有InheritedWidget重新创建,才能调用状态变更的方法,如图调用栈,然后接上篇文章

2.1 ChangeNotifier为主
InheritedWidget使用同样需要继承,我定义为B,实例为b
class B extends InheritedWidget {
final A? a;
const B({required Widget child,Key? key,this.a}) : super(child: child,key: key);
static A of(BuildContext context) {
final inheritedElement = context.getElementForInheritedWidgetOfExactType<B>();
B value = inheritedElement?.widget as B;
return value.a!;
}
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
return false;
}
}
有了InheritedWidget,可以把a作为b的参数,看,这里我们updateShouldNotify
返回的是false,因为我们压根没有用到InheritedWidget的状态管理,只是用它来传参罢了,真正起到关键作用的还是ChangeNotifier
void main() {
// Isolate.spawn(doSth, "Hi");
runApp(
B(
a: A(),
child: const MyApp(),
)
);
}
调用
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
B.of(context).addListener(() {
Element element = context as Element;
element.markNeedsBuild();
});
});
}
通过context找到b,然后就能给a添加回调了,然后就能对页面进行刷新了
但是这里出现了问题,如果我要跟踪a,initState
代码是入侵的并且局限性很大每次需要监听a的时候就要写这么一坨,有没有好的方法呢?这里我们可以优化一下of函数
static A of(BuildContext context,{bool listen = true}) {
final inheritedElement = context.getElementForInheritedWidgetOfExactType<B>();
B value = inheritedElement?.widget as B;
A a = value.a!;
if (listen) {
Element element = context as Element;
a.addListener(() {
element.markNeedsBuild();
});
}
return a;
}
如果是需要监听的,我们调用addListener,这个时候initState就可以丢掉啦,但是随之出现的问题是build会重新创建Text,导致重复addListener,目前这种还无法找到突破口
2.2 InheritedWidget为主
首先是我们主要成员
class MyInheritedWidget extends InheritedWidget {
StateChangeNotifier notifier;
MyInheritedWidget({required this.notifier,Key? key, required Widget child })
: super(key: key, child: child);
@override
bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return true;
}
}
class StateChangeNotifier extends ChangeNotifier {
int count = 0;
add() {
count++;
notifyListeners();
}
}
然后这里巧妙的套一个StatelessWidget
class Provider extends StatelessWidget {
Widget child;
StateChangeNotifier notifier;
Provider({Key? key,required this.child,required this.notifier}) : super(key: key);
Element? _element;
static StateChangeNotifier? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>()?.notifier;
}
@override
Widget build(BuildContext context) {
if (_element == null) {
_element = context as Element;
notifier.addListener(() {
_element?.markNeedsBuild();
});
}
return MyInheritedWidget(notifier: notifier, child: child);
}
}
这个StatelessWidget主要是用来更新InheritedWidget的,这里可以在build中添加监听,这样一来ChangeNotifier调用notifyListeners(), Provider 重新build, MyInheritedWidget被重新构建,所有使用过Provider.of(context)的组件就被重新build了
然后我们对Provider和InheritedWidget做一下泛型处理,这样我们就可以自定义ChangeNotifier了
class Provider<T extends ChangeNotifier> extends StatelessWidget {
Widget child;
T notifier;
Provider({Key? key,required this.child,required this.notifier}) : super(key: key);
Element? _element;
static T? of<T>(BuildContext context) {
MyInheritedWidget? inheritedWidget = context.dependOnInheritedWidgetOfExactType<MyInheritedWidget<T>>();
return inheritedWidget?.notifier;
}
@override
Widget build(BuildContext context) {
if (_element == null) {
_element = context as Element;
notifier.addListener(() {
_element?.markNeedsBuild();
});
}
return MyInheritedWidget<T>(notifier: notifier, child: child);
}
}
class MyInheritedWidget<T> extends InheritedWidget {
T notifier;
MyInheritedWidget({required this.notifier,Key? key, required Widget child })
: super(key: key, child: child);
@override
bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
return true;
}
}
最后一点就是我们在调用of的时候有的是不需要监听的,比如我的按钮,只是为了调用add函数,并没有监听什么数据,这个时候我们在优化一下of函数
static T? of<T>(BuildContext context,{bool listen = true}) {
MyInheritedWidget? inheritedWidget;
inheritedWidget = ((context.getElementForInheritedWidgetOfExactType<MyInheritedWidget<T>>())?.widget as MyInheritedWidget?);
if (listen) {
context.dependOnInheritedWidgetOfExactType<MyInheritedWidget<T>>();
}
return inheritedWidget?.notifier;
}
这样就完美的实现了对状态的管理
provider三方库也是这样实现的状态管理,但是它的监听是放到了of函数,还有就是它修改了InheritedWidget调用流程,它屏蔽了系统的调用方式
class _InheritedProviderScope<T> extends InheritedWidget {
const _InheritedProviderScope({
required this.owner,
required this.debugType,
required Widget child,
}) : assert(null is T),
super(child: child);
final InheritedProvider<T> owner;
final String debugType;
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return false;
}
@override
_InheritedProviderScopeElement<T> createElement() {
return _InheritedProviderScopeElement<T>(this);
}
}
然后自定义了InheritedElement,修改了build方法,直接在build中调用了notifyClients
,感觉是很牛逼...
@override
Widget build() {
if (widget.owner._lazy == false) {
value; // this will force the value to be computed.
}
_delegateState.build(
isBuildFromExternalSources: _isBuildFromExternalSources,
);
_isBuildFromExternalSources = false;
if (_shouldNotifyDependents) {
_shouldNotifyDependents = false;
notifyClients(widget);
}
return super.build();
}
当然我的demo只是一些思路上的东西,具体代码的规范以及严谨性肯定是有漏洞的
网友评论