找回密码
 会员注册
查看: 17|回复: 0

Flutter的Widget之间的通信方式及状态管理

[复制链接]

1

主题

0

回帖

4

积分

新手上路

积分
4
发表于 2024-10-9 15:13:08 | 显示全部楼层 |阅读模式
Flutter的Widget之间的通信方式及状态管理 Flutter的Widget之间的通信方式及状态管理 刘洋@贝壳找房 贝壳产品技术 贝壳产品技术 “贝壳产品技术公众号”作为贝壳官方产品技术号,致力打造贝壳产品、技术干货分享平台,面向互联网/O2O开发/产品从业者,每周推送优质产品技术文章、技术沙龙活动及招聘信息等。欢迎大家关注我们。 242篇内容 2021年01月31日 11:41 Flutter的发展如火如荼,各大互联网公司都在使用。但是对于Widget之间的通信方式及状态管理,很多开发者其实并没有系统和全面的认识。本文将从父Widget和子Widget之间的通信、跨多个Widget/页面之间的通信、状态管理这个三个方面讲解相关的知识及原理。1 父Widget和子Widget之间的通信父Widget和子Widget之间通信有4种方式:直接传值、传递函数、修改Key值、使用GlobalKey。1.1 直接传值父Widget通过子Widget的构造函数向子Widget传值。1.2 传递函数父Widget通过子Widget的构造函数传递一个函数对象给子Widget,子Widget通过调用父Widget的这个函数对象,调用父Widget中的函数。1.3 修改Key的值父Widget修改子Widget的参数Key的值,导致子Widget中的State重建。从父Widget传递一个已经改变的值给子Widget。如果子Widget是StatelessWidget,使用1.1就可以。如果子Widget是StatefulWidget,使用setState并不能让StatefulWidget的State中的那个值改变。这时我们就可以修改Key值让StatefulWidget的State重建。流程图如上,从源码中可以看到Key的继承结构如下:每当我们调用setState的时候,framework会调用canUpdate对新旧Widget的类型以及key做判断,如果不相等就会重建子Widget的Element,而State是由StatefulElement的构造函数创建,所以StatefulElement的重建导致了State的重建。子类ValueKey中,重写了操作符==,所以canUpdate比较的是ValueKey的参数value。1.4 使用GlobalKey创建一个GlobalKey对象,将它的引用作为参数赋值给子Widget。父Widget通过子Widget的GlobalKey,调用子Widget中的函数。GlobalKey的流程图如上,蓝色为注册过程,绿色为使用过程。源码分析如下:GlobalKey里面有个公共的静态Map叫_registry。在每个Element的mount()里面会先判断widget的参数key,如果是GlobalKey,就会将Widget的GlobalKey和对应的element做为key、value放入GlobalKey里面的公共的静态Map 中。在Element的unmount()里面又会将这个GlobalKey移除。GlobalKey中有个currentState的函数,会返回element的state,也就是StatefulWidget的State。我们写代码时调用globalKey.currentState.doSomething(),里面其实就是调用了StatefulWidget的State中的doSomething()。2 跨多个Widget/页面之间的通信一种解决方案就是通过上面的父Widget和子Widget之间的通信方式,在多个Widget/页面之间从上往下逐层传递。但这样做就比较繁琐了,而且耦合太重。另一种解决方案就是使用一些状态管理的工具。3 状态管理什么是状态管理?状态管理是数据状态的集中管理和分发。通俗说,就是一个地方的数据改变了,其他使用这些数据的地方也要跟着改变。其实就是利用了观察者模式。状态管理有8种方式:InheritedWidget、Stream、EventBus、Bloc、Scoped_model、Provider、Redux。3.1 InheritedWidgetInheritedWidget是Flutter的Element树中的一种观察者模式。InheritedWidget的流程图如上,蓝色为注册过程,绿色为刷新过程。Flutter中每个Element都持有相应的Widget的对象。InheritedWidget源码分析如下:Widget的build中参数就是context,context就是Element,我们调用context.inheritFromWidgetOfExactType(MyInheritedWidget),其实是从Map _inheritedWidgets中取出InheritedElement,并返回自定义的MyInheritedWidget的对象。我们就可以从MyInheritedWidget对象得到共享的数据。_inheritedWidgets这个Map是从哪里来的呢?是Element在mount里从父Element取得的,所以_inheritedWidgets这个Map是从上往下逐层传递的。_inheritedWidgets这个Map最初是由谁创建的呢?是InheritedElement创建的。以上是被观察者InheritedElement的注册与传递。那么观察者是怎么注册和刷新的呢?context.inheritFromWidgetOfExactType(MyInheritedWidget)里面会同时将当前使用这个函数的Element加入到MyInheritedWidget所对应的InheritedElement的Map _dependents里面。自定义的MyInheritedWidget需要重写updateShouldNotify(),如果返回true,就会从InheritedElement的Map _dependents里面取出所有调了inheritFromWidgetOfExactType()的Element,然后调用他们的didChangeDependencies()。Element的didChangeDependencies()里面调用了markNeedsBuild(),标记刷新,等待系统的Vsync信号来刷新。StatefulElement里面还调用了StatefulWidget的State的didChangeDependencies()。3.2 StreamStream是Dart自带的一种观察者模式。这里重点讲下状态管理相关的广播流。广播流的流程图如上,蓝色为注册过程,绿色为刷新过程。源码分析如下:首先,创建一个广播型的StreamController。同步异步这两种BroadcastStreamController都继承了_BroadcastStreamController。BroadcastStreamController的stream是_BroadcastStream,BroadcastStream的父类是_ControllerStream,_ControllerStream的父类是_StreamImpl。我们通过Stream调用listen()的时候,会调_StreamImpl的listen()。然后在_StreamImpl的子类_ControllerStream中调用_createSubscription()。在_ControllerStream的子类_BroadcastStreamController的_subscribe()中,创建_BroadcastSubscription。_BroadcastSubscription是一个双向链表结构。_BroadcastStreamController持有了这个双向链表的头尾。新创建的_BroadcastSubscription会加入到链表中。注册的回调函数会被_BroadcastSubscription的父类_BufferingStreamSubscription持有。这是广播流的注册过程。下面我们看看怎么发送事件。发送事件时我们会调用_BroadcastStreamController的add(),add()又调用了_sendData()。_sendData()里会遍历_BroadcastSubscription链表,执行它的add(),最终会调到_BroadcastSubscription的父类_BufferingStreamSubscription的_sendData(),执行注册时在_BufferingStreamSubscription中持有的回调函数。异步和同步:同步如上。异步sendData后会调用scheduleMicrotask执行。scheduleMicrotask是Dart中执行异步操作的一种方式。3.3 EventBusEventBus对广播Stream做了简单封装,让广播Stream的使用更方便。使用方式和Android中的EventBus和广播是一样的,都需要注册和注销监听,发送和接收事件。3.4 BlocBloc内部封装了Stream,当然也可以使用InheritedWidget实现。Bloc采用MVVM的架构。既可以在全局作为状态管理的方式使用,也可以在局部作为MVVM使用。3.5 Scoped_modelScoped_model内部封装了InheritedWidget,采用MVVM的架构。既可以在全局作为状态管理的方式使用,也可以在局部作为MVVM使用。3.6 ProviderProvider内部封装了InheritedWidget,采用MVVM的架构。既可以在全局作为状态管理的方式使用,也可以在局部作为MVVM使用。Provider使用更方便。谷歌官方团队出品和维护。3.7 ReduxRedux内部封装了InheritedWidget。Redux使用比较繁琐,一般情况下不建议使用。4 总结如果是父Widget和子Widget之间的通信,采用上述第一章中的四种方式即可。如果是跨多个Widget/页面之间的通信,可以使用一些状态管理工具。这些状态管理工具中,InheritedWidget和Stream是基础,理解它们的原理有利于更好的理解其它状态管理工具。因为其它状态管理工具都是封装了InheritedWidget或Stream。 预览时标签不可点 Flutter15移动端37大前端69Flutter · 目录#Flutter上一篇贝壳Flutter动态化原理与实践下一篇Flutter for Web在贝壳找房容灾降级中的应用关闭更多小程序广告搜索「undefined」网络结果
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-3 01:10 , Processed in 0.443949 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表