从零开始的Flutter之旅: StatelessWidgetgit
从零开始的Flutter之旅: StatefulWidgetgithub
在以前的文章中,介绍了StatelessWidget与StatefulWidget的特性与它们的呈现原理。segmentfault
这期要聊的是它们的另外一个兄弟InheritedWidget。缓存
InheritedWidget是Flutter中的一个很是重要的功能组件,它可以提供数据在widget树中从上到下进行传递。保证数据在不一样子widget中进行共享。这对于一些须要使用共享数据的场景很是有效,例如,在Flutter SDK中就是经过InheritedWidget来共享应用的主题与语言信息。微信
可能你还有点模糊,别急,下面咱们经过一个简单的示例来了解InheritedWidget。网络
相信开始学Flutter时都看过官方的计数器示例。咱们将官方提供的计数器示例使用InheritedWidget进行改造。架构
首先咱们须要一个CountInheritedWidget,它继承于InheritedWidget。app
class CountInheritedWidget extends InheritedWidget { CountInheritedWidget({@required this.count, Widget child}) : super(child: child); // 共享数据,计数的数量 final int count; // 统一的获取CountInheritedWidget实例, 方便树中子widget的获取共享数据 // 必须在State中调用才会有效 static CountInheritedWidget of(BuildContext context) { // 调用共享数据的子widget将不会回调didChangeDependencies方法,即子widget将不会更新 // return context.getElementForInheritedWidgetOfExactType<CountInheritedWidget>().widget; return context.dependOnInheritedWidgetOfExactType<CountInheritedWidget>(); } // true -> 通知树中依赖改共享数据的子widget @override bool updateShouldNotify(CountInheritedWidget oldWidget) { return oldWidget.count != count; } }
如今已经有了共享数据count的提供,接下来是在具体的子widget中进行使用。框架
咱们抽离出一个CountText子widgetless
class CountText extends StatefulWidget { @override _CountTextState createState() { return _CountTextState(); } } class _CountTextState extends State<CountText> { @override Widget build(BuildContext context) { return Text("count: ${CountInheritedWidget.of(context).count.toString()}"); } @override void didChangeDependencies() { super.didChangeDependencies(); print("didChangeDependencies"); } }
最后,咱们再将CountInheritedWidget与CountText结合起来,经过简单的点击自增事件来看下效果
class CountWidget extends StatefulWidget { @override _CountState createState() { return _CountState(); } } class _CountState extends State<CountWidget> { int count = 0; @override Widget build(BuildContext context) { return MaterialApp( title: 'Count App', theme: new ThemeData(primarySwatch: Colors.blue), home: Scaffold( appBar: AppBar( title: Text("Count"), ), body: Center( child: CountInheritedWidget( count: count, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ CountText(), RaisedButton( onPressed: () => setState(() => count++), child: Text("Increment"), ) ], ), ), ), ), ); } }
上面的层级关系是CountText恰好是CountInheritedWidget的子widget。
如今咱们经过点击事件直接改变外部的count值,若是InheritedWidget从上到下数据传到的效果可以生效,那么在CountText中引用的count将会与外部count同步,程序呈现的效果将会是自增的,同时因为依赖的count发生改变CountText中的didChangeDependencies也会回调。
咱们直接运行一下
点击后的输出日志
I/flutter: didChangeDependencies
说明InheritedWidget的效果已经生效,经过InheritedWidget的使用,咱们能够很方便的在嵌套下层的子widget中拿到上层的数据,或者说整个widget的共享数据。
在依赖方式改变时子widget的didChangeDependencies会回调,但因为你可能会在该方法中作一些特殊的操做,例如网络请求。只是须要一次就能够。若是是套用咱们上面的示例,将会在count子增时反复调用。
为了防止didChangeDependencies的调用,咱们再来看CountInheritedWidget的of方法中注释的那部分
static CountInheritedWidget of(BuildContext context) { // 调用共享数据的子widget将不会回调didChangeDependencies方法,即子widget将不会更新 // return context.getElementForInheritedWidgetOfExactType<CountInheritedWidget>().widget; return context.dependOnInheritedWidgetOfExactType<CountInheritedWidget>(); }
咱们使用的是dependOnInheritedWidgetOfExactType方法,依赖的共享数据发生改变时会回调子widget中的didChangeDependencies方法,若是咱们不想要子widget调用该方法,可使用注释的代码,经过getElementForInheritedWidgetOfExactType方法来获取共享数据。
若是此时咱们再运行一下项目,点击count自增,控制台将不会再输出日志。这样就能够解决didChangeDependencies的反复调用。
而这两个方法的主要区别是在dependOnInheritedWidgetOfExactType调用的过程当中会进行注册依赖关系
@override InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) { assert(ancestor != null); _dependencies ??= HashSet<InheritedElement>(); _dependencies.add(ancestor); ancestor.updateDependencies(this, aspect); return ancestor.widget; }
因此dependOnInheritedWidgetOfExactType更新依赖的子widget中的didChangeDependencies方法。
思考下一个问题,虽然如今didChangeDependencies方法不会调用,可是CountText的build方法仍是会执行。缘由是在CountWidget中经过setState来改变count值,会从新build所用的子widget。但咱们真正想要的只是更新子widget中依赖的CountInheritedWidget的组件值。
那么如何解决呢?这里提供一个解决方案是为子widget提供缓存。能够经过封装一个简单的StatefulWidget,将子widget缓存起来。若是对这块感兴趣的,能够期待个人后续文章。
下面介绍一个完整的Flutter项目,对于新手来讲是个不错的入门。
flutter_github,这是一个基于Flutter的Github客户端同时支持Android与IOS,支持帐户密码与认证登录。使用dart语言进行开发,项目架构是基于Model/State/ViewModel的MSVM;使用Navigator进行页面的跳转;网络框架使用了dio。项目正在持续更新中,感兴趣的能够关注一下。
固然若是你想了解Android原生,相信flutter_github的纯Android版本AwesomeGithub是一个不错的选择。
若是你喜欢个人文章模式,或者对我接下来的文章感兴趣,建议您关注个人微信公众号:【Android补给站】
或者扫描下方二维码,与我创建有效的沟通,同时更快更准的收到个人更新推送。