flutter inheritedwidget(共享数据)
一、Flutter Sdk正是通过inheritedWidget实现在widget树中共享数据的(Theme和Locale当前语言环境)
1.inheritedWidget是Flutter中一个重要的功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式,比如我们在应用的根widget中通过InheritedWidget
共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据
2.didChangeDependencies
在statefulwidget 的State中有一个didChangeDependencies方法,它指的正是组件是否依赖了父widget的inheritedwidget共享数据,如果inheritedwidget的数据发生变化,那么didChangeDependencies方法就会被调用,进而更新组件
示例:通过继承Inheritedwidget实现一个点击自增数据以清楚其原理
a:InheritedWidget 的updateShouldNotify方法,此方法决定是否调用State的didChangeDependencies方法,如果返回true则调用
class ShareDataWidget extends InheritedWidget { ShareDataWidget({ @required this.data, Widget child }) :super(child: child); final int data; //需要在子树中共享的数据,保存点击次数 //定义一个便捷方法,方便子树中的widget获取共享数据 static ShareDataWidget of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>(); } //该回调决定当data发生变化时,是否通知子树中依赖data的Widget @override bool updateShouldNotify(ShareDataWidget old) { //如果返回true,则子树中依赖(build函数中有调用)本widget //的子widget的`state.didChangeDependencies`会被调用 return old.data != data; } }
class _TestWidget extends StatefulWidget { @override __TestWidgetState createState() => new __TestWidgetState(); } class __TestWidgetState extends State<_TestWidget> { @override Widget build(BuildContext context) { //使用InheritedWidget中的共享数据 return Text(ShareDataWidget .of(context) .data .toString()); } @override void didChangeDependencies() { super.didChangeDependencies(); //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。 //如果build中没有依赖InheritedWidget,则此回调不会被调用。 print("Dependencies change"); } }
class InheritedWidgetTestRoute extends StatefulWidget { @override _InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState(); } class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> { int count = 0; @override Widget build(BuildContext context) { return Center( child: ShareDataWidget( //使用ShareDataWidget data: count, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Padding( padding: const EdgeInsets.only(bottom: 20.0), child: _TestWidget(),//子widget中依赖ShareDataWidget ), RaisedButton( child: Text("Increment"), //每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新 onPressed: () => setState(() => ++count), ) ], ), ), ); } }
以上例子,如果没有依赖ShareDataWidget的数据,那么TestWidget的State 的didChangeDependencies是不会被调用的。
重点结论:InheritedWidget正是Flutter框架在widget树中实现共享数据,子widget依赖发生变化时让子组件实现更新的
3.应该在didChangeDependencies()中做什么?
一般来说,子widget很少会重写此方法,因为在依赖改变后framework也都会调用build()
方法。但是,如果你需要在依赖改变后执行一些昂贵的操作,比如网络请求,这时最好的方式就是在此方法中执行,这样可以避免每次build()
都执行这些昂贵操作。
4.dependOnInheritedWidgetOfExactType和getElementForInheritedWidgetOfExactType的区别
//定义一个便捷方法,方便子树中的widget获取共享数据 static ShareDataWidget of(BuildContext context) { //return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>(); return context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget; }
@override InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() { assert(_debugCheckStateIsActiveForAncestorLookup()); final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T]; return ancestor; } @override InheritedWidget dependOnInheritedWidgetOfExactType({ Object aspect }) { assert(_debugCheckStateIsActiveForAncestorLookup()); 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; }
@override InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) { assert(ancestor != null); _dependencies ??= HashSet<InheritedElement>(); _dependencies.add(ancestor); ancestor.updateDependencies(this, aspect); return ancestor.widget; }
查看代码可以发现dependOn方法比getElementFor多了一个注册到ancertor和dependOnInheritedElement,而dependOnInheritedElement方法中执行了ancestor.updateDependencies方法,正是这个方法让State的didChangeDependen方法被调用
二、Provide正是通过InheritedWidget实现的数据共享
这里可参考大神的跨组件状态共享加深理解:https://book.flutterchina.club/chapter7/provider.html
Flutter的Provide重要的有以下几个点:
1.ChangeNotifierProvider 变更通知提供者,也是订阅者 Comsumer基本都会在订阅者的子widget,因为订阅者需要依赖共享数据
2.Comsumer 消费者,就是消费共享数据的
3.ChangeNotifier 变更通知者,就是当依赖的model数据变化时,通知到各个监听者 这里往往需要共享的数据,某某Model 会extends继承ChangeNotifier
这里给个baseWidget 使用了Provide的示例,代码如下:
import 'package:business/base/base_viewmodel.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class BaseWidget<T extends ChangeNotifier> extends StatefulWidget { final Widget Function(BuildContext context, T model, Widget child) builder; final T model; final Widget child; final Function(T) onModelReady; BaseWidget({ Key key, this.builder, this.model, this.child, this.onModelReady, loginService, }) : super(key: key); _BaseWidgetState<T> createState() => _BaseWidgetState<T>(); } class _BaseWidgetState<T extends ChangeNotifier> extends State<BaseWidget<T>> { T model; @override void initState() { model = widget.model; if (widget.onModelReady != null) { widget.onModelReady(model); } super.initState(); } @override void dispose() { if(model != null){ if(model is BaseViewModel){ BaseViewModel m = model as BaseViewModel; m.isDisposed = true; } } super.dispose(); } @override Widget build(BuildContext context) { return ChangeNotifierProvider<T>( create: (context) => model, child: Consumer<T>( builder: widget.builder, child: widget.child, ), ); } }
注意:这里有一点就是onModelReady的model参数就是BaseWidget的model域。同时builder 的构造,(context,model,child){} 的model也是和onModelReady是同一个model数据。因为下面这个
Widget build(BuildContext context) { return ChangeNotifierProvider<T>( create: (context) => model, child: Consumer<T>( builder: widget.builder, child: widget.child, ), ); }