原文:《Flutter路由栈和生命周期解析》 作者:Harry_Chen
什么是路由
路由(Routes)是什么?路由是屏幕或应用程序页面的抽象。
Flutter 使我们能够优雅地管理路由主要依赖的是 Navigator(导航器)类。这是一个用于管理一组具有某种进出规则的页面的 Widget,也就是说用它我们能够实现各个页面间有规律的切换。而这里的规则便是在其内部维护的一个“ 路由栈”。
命名路由
在一般应用中,我们用的最多的还是命名路由,它是将应用中需要访问的每个页面命名为不重复的字符串,我们便可以通过这个字符串来将该页面实例推进路由。
例如:
new MaterialApp( home: new Screen1(), routes: <String, WidgetBuilder> { '/screen1': (BuildContext context) => new Screen1(), '/screen2' : (BuildContext context) => new Screen2(), '/screen3' : (BuildContext context) => new Screen3(), '/screen4' : (BuildContext context) => new Screen4() }, )
路由栈
Navigator维护了一个堆栈,来保存路由跳转的状态。
由screen1
跳转到screen2
Navigator.pushNamed(context, '/screen2')
退出路由pop
Navigator.pop()
切勿用pushNamed
替代pop
,不然就会出现下面的情况了。
生命周期
简单地聊完路由栈,接下来介绍 StatefulWidget
一系列的生命周期。
上图就是 State 的生命周期图。
StatefulWidget.createState()
Framework 通过调用 StatefulWidget.createState() 来创建一个 State。
initState()
新创建的 State 会和一个 BuildContext 产生关联,此时认为 State 已经被安装好了,initState() 函数将会被调用。通常,我们可以重写这个函数,进行初始化操作。
didChangeDependencies()
在 initState() 调用结束后,这个函数会被调用。事实上,当 State 对象的依赖关系发生变化时,这个函数总会被 Framework 调用。
build()
经过以上步骤,系统认为一个 State 已经准备好了,就会调用 build() 来构建视图。我们需要在这个函数中,返回一个 Widget。
deactivate()
当 State 被暂时从视图树中移除时,会调用这个函数。页面切换时,也会调用它,因为此时 State 在视图树中的位置发生了变化,需要先暂时移除后添加。⚠️注意,重写的时候必须要调用 super.deactivate()。
dispose()
当 State 被永久的从视图树中移除,Framework 会调用该函数。在销毁前触发,我们可以在这里进行最终的资源释放。在调用这个函数之前,总会先调用 deactivate()。⚠️注意,重写的时候必须要调用 super.dispose()
didUpdateWidget(covariant T oldWidget)
当 widget 的配置发生变化时,会调用这个函数。比如,Hot-reload 的时候就会调用这个函数。这个函数调用后,会调用 build()。
setState()
当我需要更新 State 的视图时,需要手动调用这个函数,它会触发 build() 。
一般情况下我们用的最多的是initState
,build
,deactivate
,dispose
,setState
。接下来也主要讲initState
以及dispose
在路由跳转过程中的触发条件。
路由跳转时的initState
,deactivate
,dispose
一开始我们是这么写的
@override void initState() { super.initState(); print('------initstate--------'); } @override void deactivate() { super.deactivate(); print('-----deactivate----'); } @override void dispose() { super.dispose(); print('----dispose--------'); }
当我们通过pushNamed
跳转到其他路由,发现deactivate
触发了,但是dispose
并没有触发,并且再次通过pushNamed
回到本页的时候,initstate
也并没有再次执行。
写多了Vue
单页面应用的同学,应该知道这其实很让人困扰。我们很多时候需要像Vue
一样,在created
的时候初始化数据,在beforeDestroy
的时候移除页面一些监听事件或者销毁定时器,防止内存泄露。
让我们回到生命周期说明那里:
deactivate()
当 State 被暂时从视图树中移除时,会调用这个函数。
dispose()
当 State 被永久的从视图树中移除,Framework 会调用该函数。
此时是不是恍然大悟!pushNamed
只是暂时把视图移除了,并没有彻底移除,所以导致了一些奇怪的问题(前提是你跟我一样,业务上需要控制页面的初始化和销毁)
那怎么办。此时popAndPushNamed
闪亮登场!用它替代pushNamed
,你就会发现该触发的事件都触发了~
popAndPushNamed
将当前路由弹出并跳转到新的路由,彻底移除旧路由。
还有类似的方法pushReplacementNamed
,pushNamedAndRemoveUntil
等,就不赘述了。
取消导航Header的返回键
AppBar(automaticallyImplyLeading: false)
WillPopScope
取消Android物理返回键
这个方法也可以取消iOS默认左右滑动切换路由。
Widget build(BuildContext context) { return WillPopScope( onWillPop: () async { return false; }, child: Scaffold( // Your Code ) ); }