在以前我写过一篇文章,教你如何实现Flutter的BLoC框架(juejin.cn/post/684490… ),这个BLoC的实现,模仿android开发中的MVVM开发方式,利用Flutter的Stream,在数据改变的时候,由Stream推送数据给UI层,然后UI层自动更新UI。这次我们来自己实现一个Provider,这个也是Flutter中最常用的状态管理框架,由我们自己实现Provider,来了解Provider内部核心原理。
首先,在上一篇文章中(InheritWidget原理解析:juejin.cn/post/684490… ),我们查看输出日志发现一个问题,就是点击“数字+1”这个按钮的时候,“数字+1”这个按钮也刷新了(日志输出CustomRaisedButton build),这是因为我们调用的setState方法是BodyWidgetState这个类的方法,所以BodyWidget和它的child widget都重建了,而我们的需求是InheritedWidget的数据改变的时候只刷新依赖此InheritedWidget的widget,要做到这一点,我们就不能调用BodyWidgetState的setState方法,而只调用InheritedWidget上一个节点的setState,也就是说要把InheritedWidget作为一个StatefulWidget的child。然后我们分步骤编写代码:
- 创建可被监听的数据管理类,为什么我们不直接让
InheritedWidget成为可被监听的类型呢?这是因为InheritedWidget只是提供数据,数据的消费者应该持有的是数据而不是InheritedWidget,否则数据消费者如果持有InheritedWidget的话,修改具体数据的方法就要添加到InheritedWidget里,而数据类型是多种多样的,不可能全部写到InheritedWidget里,所以我们要创建可被监听的数据管理类,这样当数据发生变化的时候,调用数据管理类的方法,数据管理类再通知该类的监听者 - 创建
InheritedWidget子类,用来保存数据管理类,该类可以在重建以后通知所有依赖了该类的widget - 创建
StatefulWidget,这里我们起名为ProviderCreator,用来保存实际显示的Widget和可被监听的数据管理类,并根据两者创建InheritedWidget,并监听数据管理类的变化。这样可以在数据管理类中的数据发生变化的时候可以通过ProviderCreator对应的State调用setState来让InheritedWidget重建。InheritedWidget重建时如果ProviderCreator对应的Element没有被销毁的话,那这个ProviderCreator内部的可被监听的数据管理类和实际显示的child就被缓存起来了(注意:这个child是我们传入的实际显示的widget,而不是ProviderCreator对应的State的build方法里返回的widget)
开始我们的代码编写
1. 创建可被监听的数据管理类
创建InheritedWidget子类的时候,里面的数据可以是任意类型,但是我们需要在数据改变的时候通知监听者,所以我们约束一下里面的数据类型必须是可被监听的类型,在flutter里,有一个类叫做ChangeNotifier,非常适合用来作为被监听者,代码如下
class ChangeNotifier implements Listenable {
ObserverList<VoidCallback> _listeners = ObserverList<VoidCallback>();
@protected
bool get hasListeners {
return _listeners.isNotEmpty;
}
@override
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
@override
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
@mustCallSuper
void dispose() {
_listeners = null;
}
@protected
@visibleForTesting
void notifyListeners() {
if (_listeners != null) {
final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners);
for (final VoidCallback listener in localListeners) {
listener();
}
}
}
}
代码我简化了一下,就是上面的样子,我们的可被监听的数据类都需要继承该类
2. 创建InheritedWidget子类
因为该类用来提供可被监听的数据管理类,所以起名叫`Provider`
class Provider<T extends ChangeNotifier> extends InheritedWidget {
final T data;
Provider({Key key, this.data, Widget child}) : super(key: key, child: child) {
print("Provider=$hashCode");
}
//定义一个便捷方法,方便子树中的widget获取共享数据
static Provider<T> of<T extends ChangeNotifier>(BuildContext context, bool dependOn) {
if (dependOn) {
return context.dependOnInheritedWidgetOfExactType<Provider<T>>();
} else {
return context.getElementForInheritedWidgetOfExactType<Provider<T>>().widget as Provider<T>;
}
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
//在此简单返回true,则每次更新都会调用依赖其的子孙节点的'didChangeDependencies'。
return true;
}
}
3. 创建StatefulWidget,用来保存实际显示的Widget和可被监听的数据管理类,并根据两者创建InheritedWidget,并监听数据管理类的变化。
class ProviderCreator<T extends ChangeNotifier> extends StatefulWidget {
final T data;
final Widget child;
ProviderCreator({
Key key,
this.data,
this.child,
}) {
print("ProviderCreator=$hashCode");
}
@override
State<StatefulWidget> createState() {
return ProviderCreatorState<T>();
}
}
class ProviderCreatorState<T extends ChangeNotifier> extends State<ProviderCreator> {
void update() {
setState(() {});
}
@override
void initState() {
widget.data.addListener(update);
super.initState();
}
@override
void dispose() {
widget.data.dispose();
super.dispose();
}
@override
void didUpdateWidget(ProviderCreator<ChangeNotifier> oldWidget) {
//当Provider更新时,如果新旧数据不"==",则解绑旧数据监听,同时添加新数据监听
if (oldWidget.data != widget.data) {
oldWidget.data.dispose();
widget.data.addListener(update);
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
print("""CustomInheritedWidgetCreatorState build
\twidget=${widget.hashCode}
\twidget.data.hashCode=${widget.data.hashCode}
\twidget.child=${widget.child.hashCode}""");
return Provider<T>(
data: widget.data,
child: widget.child,
);
}
}
示例
然后,我们用一个示例来试一下我们刚才自己写的Provider
我们还用上篇文章里的示例,用一个计数器程序来测试。
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter InheritWidget',
home: Scaffold(
appBar: AppBar(),
body: Center(
child: BodyWidget(),
),
),
);
}
}
class BodyWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return BodyWidgetState();
}
}
class BodyWidgetState extends State<BodyWidget> {
Counter counter = Counter();
@override
Widget build(BuildContext context) {
return ProviderCreator<Counter>(
data: counter,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
//依赖
DependOnInheritedWidget<Counter>(),
//不依赖
Builder(builder: (context) {
return Text(Provider.of<Counter>(context, false).data.toString());
}),
Builder(builder: (context) {
return CustomRaisedButton(
onPressed: () {
//不依赖
Provider.of<Counter>(context, false).data.increment();
},
child: Text("数字+1"),
);
})
],
),
);
}
}
class Counter extends ChangeNotifier {
int num = 0;
void increment() {
num++;
notifyListeners();
}
@override
String toString() {
return "$num";
}
}
class DependOnInheritedWidget<T extends ChangeNotifier> extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return DependOnInheritedWidgetState<T>();
}
}
class DependOnInheritedWidgetState<T extends ChangeNotifier> extends State<DependOnInheritedWidget> {
@override
Widget build(BuildContext context) {
print("DependOnInheritedWidgetState build");
return Text(Provider.of<T>(context, true).data.toString());
}
@override
void didChangeDependencies() {
print("DependOnInheritedWidgetState didChangeDependencies");
super.didChangeDependencies();
}
}
class CustomRaisedButton extends RaisedButton {
const CustomRaisedButton({
@required VoidCallback onPressed,
Widget child,
}) : super(onPressed: onPressed, child: child);
@override
Widget build(BuildContext context) {
print("CustomRaisedButton build");
return super.build(context);
}
}
当我们点击“数字+1”这个按钮的时候,会在日志打印里发现如下信息:
I/flutter ( 489): CustomInheritedWidgetCreatorState build
I/flutter ( 489): widget=136741630
I/flutter ( 489): widget.data.hashCode=597399651
I/flutter ( 489): widget.child=443053943
I/flutter ( 489): Provider=611638398
I/flutter ( 489): DependOnInheritedWidgetState didChangeDependencies
I/flutter ( 489): DependOnInheritedWidgetState build
说明CustomRaisedButton不再build了,注意:Provider的of方法中的dependOn参数,为true说明调用了该方法的widget依赖了InheritedWidget,为false就没有依赖InheritedWidget,具体的可以看dependOnInheritedWidgetOfExactType和getElementForInheritedWidgetOfExactType这两个方法的源码,这里不再赘述。
至此,我们做了一个简单的Provider,大功告成!