这个生命周期方法很多文章没讲明白,大都一语带过
A调用reload后,A 不会 调用didUpdateWidget,A的子节点B 会 调用didUpdateWidget
附Demo
InheritedWidget 是 Flutter 中的一个功能型 Widget,适用于在 Widget 树中共享数据的场景。
class CounterPage extends StatefulWidget {
CounterPage({Key key}) :super(key: key);
@override
_CounterPageState createState() =>_CounterPageState();
}
//
class _CounterPageState extends State {
int count =0;
//3我们通过 InheritedCountContainerof 方法找到它,获取计数状态 count 并展示:
void _incrementCounter() => setState(() {count++;});// 修改计数器
@override
Widget build(BuildContext context) {
//2我们使用 CountContainer 作为根节点,并用 0 初始化 count。
return CountContainer(
model:this,
increment: _incrementCounter,// 提供修改数据的方法
child:Counter()
);
}
}
/1
首先,为了使用 InheritedWidget,我们定义了一个继承自它的新类 CountContainer。
然后,我们将计数器状态 count 属性放到 CountContainer 中,
并提供了一个 of 方法方便其子 Widget 在 Widget 树中找到它。
最后,我们重写了 updateShouldNotify 方法,这个方法会在 Flutter
判断 InheritedWidget 是否需要重建,从而通知下层观察者组件更新数据时被调用到。
在这里,我们直接判断 count 是否相等即可。
/
class CountContainer extends InheritedWidget {
static CountContainerof(BuildContext context) {
return contextdependOnInheritedWidgetOfExactType();
}
final _CounterPageState model;// 直接使用 MyHomePage 中的 State 获取数据
final Function()increment;
CountContainer({
Key key,
required thismodel,
required thisincrement,
required Widget child,
}):super(key: key, child: child);
@override
bool updateShouldNotify(CountContainer oldWidget) =>model != oldWidgetmodel;
}
class Counter extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 获取 InheritedWidget 节点
CountContainerstate =CountContainerof(context);
return Scaffold(
appBar:AppBar(
title:Text("InheritedWidget demo"),
),
body:Text(
'You have pushed the button this many times: ${statemodelcount}',// 关联数据读方法
),
floatingActionButton:FloatingActionButton(onPressed:stateincrement),// 关联数据修改方法
);
}
}
Notification 是 Flutter 中进行跨层数据共享的另一个重要的机制。如果说 InheritedWidget 的数据流动方式是从父 Widget 到子 Widget 逐层传递,那 Notificaiton 则恰恰相反,数据流动方式是从子 Widget 向上传递至父 Widget。这样的数据传递机制适用于子 Widget 状态变更,发送通知上报的场景。
class CustomNotification extends Notification {
CustomNotification(thismsg);
final String msg;
}
class CustomChild extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
//按钮点击时分发通知
onPressed: () =>CustomNotification("Hi")dispatch(context),
child:Text("Fire Notification"),
);
}
}
class NotificationWidget extends StatefulWidget {
@override
StatecreateState()=>_NotificationState();
}
class _NotificationState extends State {
String _msg ="通知:";
@override
Widget build(BuildContext context) {
//监听通知
return NotificationListener(
onNotification: ( notification) {
setState(() {_msg += notificationmsg+" ";});
return true;
},
child:Column(
mainAxisAlignment:MainAxisAlignmentcenter,
children: [Text(_msg),CustomChild()],
)
);
}
}
无论是 InheritedWidget 还是 Notificaiton,它们的使用场景都需要依靠 Widget 树,也就意味着只能在有父子关系的 Widget 之间进行数据共享。但是,组件间数据传递还有一种常见场景:这些组件间不存在父子关系。这时,事件总线 EventBus 就登场了。
事件总线是在 Flutter 中实现跨组件通信的机制。它遵循发布 / 订阅模式,允许订阅者订阅事件,当发布者触发事件时,订阅者和发布者之间可以通过事件进行交互。发布者和订阅者之间无需有父子关系,甚至非 Widget 对象也可以发布 / 订阅。
// 所以在这里,我们传输数据的载体就选择了一个有字符串属性的自定义事件类 CustomEvent:
class CustomEvent {
String msg;
CustomEvent(thismsg);
}
// 建立公共的 event bus
EventBus eventBus =new EventBus();
/
我们定义了一个全局的 eventBus 对象,并在第一个页面监听了 CustomEvent 事件,
一旦收到事件,就会刷新 UI。
需要注意的是,千万别忘了在 State 被销毁时清理掉事件注册,
否则你会发现 State 永远被 EventBus 持有着,无法释放,从而造成内存泄漏:
/
class FirstPage extends StatefulWidget {
@override
StatecreateState()=>_FirstPageState();
}
class _FirstPageState extends State {
String msg ="通知:";
late StreamSubscription subscription;
@override
void initState() {
//监听CustomEvent事件,刷新UI
subscription =eventBuson()listen((event) {
print(eventmsg);
setState(() {
msg += eventmsg;
});
});
superinitState();
}
dispose() {
subscriptioncancel();//State销毁时,清理注册
superdispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(title:Text("First Page"),),
body:Text(msg),
floatingActionButton:FloatingActionButton(onPressed: ()=>Navigatorpush(context,MaterialPageRoute(builder: (context) =>SecondPage()))),
);
}
}
//我们在第二个页面以按钮点击回调的方式,触发了 CustomEvent 事件:
class SecondPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(title:Text("Second Page"),),
body:RaisedButton(
child:Text('Fire Event'),
// 触发CustomEvent事件
onPressed: ()=>eventBusfire(CustomEvent("hello"))
),
);
}
}
Stateful(有状态) 和 stateless(无状态) widgets
stateless widget 没有内部状态 Icon、 IconButton, 和Text 都是无状态widget, 他们都是 StatelessWidget的子类。
stateful widget 是动态的 用户可以和其交互 (例如输入一个表单、 或者移动一个slider滑块),或者可以随时间改变 (也许是数据改变导致的UI更新) Checkbox, Radio, Slider, InkWell, Form, and TextField 都是 stateful widgets, 他们都是 StatefulWidget的子类。
StatefulWidget类
具有可变状态的小部件。
状态是(1)在构建窗口小部件时可以同步读取的信息,以及(2)在窗口小部件的生命周期内可能会更改的信息。这是小工具实施者的责任,以确保国家的及时通知当这种状态的改变,使用StatesetState。
有状态窗口小部件是一个窗口小部件,它通过构建一个更具体地描述用户界面的其他窗口小部件来描述用户界面的一部分。构建过程以递归方式继续,直到用户界面的描述完全具体(例如,完全由RenderObjectWidget组成,其描述具体的RenderObject)。
当您描述的用户界面部分可以动态更改时(例如由于具有内部时钟驱动状态或依赖于某些系统状态),状态窗口小部件非常有用。对于仅依赖于对象本身中的配置信息以及窗口小部件膨胀的 BuildContext的组合,请考虑使用 StatelessWidget。
StatefulWidget实例本身是不可变的,并且将它们的可变状态存储在由createState方法创建的单独State对象中 ,或者存储在State订阅的对象中,例如Stream或ChangeNotifier对象,其引用存储在StatefulWidget的最终字段中本身。
框架在膨胀StatefulWidget时 调用createState,这意味着如果该窗口小部件已插入到多个位置的树中,则多个State对象可能与同一StatefulWidget关联。同样,如果StatefulWidget从树中移除,后来在树再次插入时,框架将调用createState再创建一个新的国家目标,简化的生命周期状态的对象。
如果StatefulWidget的创建者使用GlobalKey作为其 键,则StatefulWidget在从树中的一个位置移动到另一个位置时保持相同的State对象。由于具有GlobalKey的窗口小部件可以在树中的至多一个位置使用,因此使用GlobalKey的窗口小部件最多只有一个关联元素。当通过将与该窗口小部件关联的(唯一)子树从旧位置移植到新位置(而不是在该位置重新创建子树)时,框架利用此属性将全局键从树中的一个位置移动到另一个位置时利用此属性。新的位置)。与StatefulWidget关联的State对象与子树的其余部分一起被移植,这意味着State对象在新位置被重用(而不是被重新创建)。但是,为了有资格进行嫁接,必须将窗口小部件插入到从旧位置移除它的同一动画帧中的新位置。
StatefulWidget有两个主要类别。
首先是其中一个分配资源StateinitState并在他们的处置Statedispose,但不依赖于InheritedWidget S或致电StatesetState。这些小部件通常在应用程序或页面的根目录中使用,并通过ChangeNotifier, Stream或其他此类对象与子小部件进行通信。遵循这种模式的有状态小部件相对便宜(就CPU和GPU周期而言),因为它们构建一次然后永不更新。因此,它们可能有一些复杂和深刻的构建方法。
第二类是使用StatesetState或依赖于 InheritedWidget的小部件。这些通常会在应用程序的生命周期内重建多次,因此最小化重建此类窗口小部件的影响非常重要。(他们也可以使用StateinitState或 StatedidChangeDependencies并分配资源,但重要的是他们重建。)
可以使用几种技术来最小化重建有状态窗口小部件的影响:
StatelessWidget类
一个不需要可变状态的小部件。
无状态窗口小部件是一个窗口小部件,它通过构建一个更具体地描述用户界面的其他窗口小部件来描述用户界面的一部分。构建过程以递归方式继续,直到用户界面的描述完全具体(例如,完全由RenderObjectWidget组成,其描述具体的RenderObject)。
当您描述的用户界面部分不依赖于对象本身的配置信息以及窗口小部件膨胀的BuildContext时,无状态窗口小部件非常有用。对于可以动态更改的组合,例如由于具有内部时钟驱动状态或依赖于某些系统状态,请考虑使用StatefulWidget。
无状态窗口小部件的构建方法通常仅在以下三种情况下调用:第一次将窗口小部件插入树中,窗口小部件的父窗口更改其配置时,以及何时依赖于更改的InheritedWidget。
如果窗口小部件的父级将定期更改窗口小部件的配置,或者它依赖于经常更改的继承窗口小部件,则优化构建方法的性能以保持流畅的呈现性能非常重要。
可以使用几种技术来最小化重建无状态窗口小部件的影响:
局部key,包含三种类型的key: ValueKey 、 ObjectKey 、 UniqueKey
1、ValueKey通过ValueKey的值来对比。比如1,2,3。用在学生的id等唯一信息上。
2、ObjectKey 以Object对象作为Key,通过指针地址来对比。new一个对象,对象的指针地址进行对比。
3、UniqueKey唯一的,可以保证Key的唯一性。使用之后就不存在Element的复用了,因为每次都是不同的。如果实在没有唯一标识了,可以使用UniqueKey来标识。
总结:ValueKey和ObjectKey设置唯一的key之后,都会保持Element的复用,Element树会根据key来保持原先的界面内容。而UniqueKey在热重载之后,就会重新生成,都不同,所以界面内容会改变。
全局key,GlobalKey可以获取到对应的Widget的State对象!
当我们页面内容很多时,而需要改变的内容只有很少的一部分且在树的底层的时候,我们通常情况下有两种方式,第一种是通过方法的回调,去实现数据更新,第二种是通过GlobalKey,在StatelessWidget引用StatefulWidget。
例如,横竖屏的布局不同,横屏Row竖屏Colum,界面里面的内容在横竖屏切换的时候不能改变,这个时候就需要是用GloableKey了,初始化几个不同name的GloableKey,分别添加到对应的widget中。
常用解决方法
上面使用mounted判断在多数情况下足以应付,但在某些状态管理下,还是会失效
生命周期是一个从创建到销毁的过程,Flutter生命周期分为两部分:
1Widget的生命周期
2APP的生命周期
1StatelessWidget
对于StatelessWidget来说,生命周期只有build过程。build是用来创建Widget的,在每次页面刷新时会调用build。
2StatefulWidget
StatefulWidget的生命周期依次为:
createState是StatefulWidget来创建State的方法,只调用一次,
initState是StatefulWidget创建后调用的第一个方法,而且只执行一次。在执行initState时,View没有渲染,但是StatefulWidget 已经被加载到渲染树里了,这事的StatefulWidget的 mount 的值会变为true,知道dispose才会变为false一般我们把初始化的一些 *** 作都放在initState中。
didChangeDependencies会在initState后立即调用,之后只有当StatefulWidget依赖的InheritedWidget发生变化之后,didChangeDependencies才会调用,所以didChangeDependencies可以调用多次。
build方法会在didChangeDeoendencies之后立即调用,在之后setState()刷新时,会重新调用build绘制页面,所以build方法可以调用多次。但一般不再build中创建除创建Widget的方法,否则会影响渲染效率。
addPostFrameCallback是StatefulWidget渲染结束之后的回调,只会调用一次,一般是在initState里添加回调:,
一般在dispose中做一些取消监听、动画的 *** 作,和initState相对使用。
AppLifecycleState就是App的生命周期,包含四个:
与iOS的ViewController、Android的Activity一样,Flutter中的Widget也存在生命周期,并且通过State来提现。而App则是一个特殊的Widget,除了需要处理视图显示的各个阶段,还需要应对应用从启动到退出所经历的各个状态。
State的生命周期,指的是在用户参与的情况查下,其关联的Widget所经历的从创建到显示再到更新最后到停止,直至销毁的各个过程阶段。
这些不同的阶段涉及到的特定的任务处理,正确理解State的生命周期至关重要,State的生命周期流程图图,如下所示:
从图中可以看到,State的生命周期可以分为3个阶段:创建、更新、销毁。下面将介绍每一个阶段的具体流程
State初始化时会依次执行:构造方法 -> initState -> didChangeDependencies -> build,随后完成页面渲染
Widget的状态更新,主要由3个方法触发:setState、didChangeDependencies与didUpdateWidget。
一旦这三个方法被调用,Flutter就回销毁旧的Widget,并调用build方法重建Widget
组件销毁相对比较简单,组件被移除,或者页面销毁的时候,系统会调用deactivate和dispose这两个方法来移除或销毁组件
下面这张表格也可以帮助我们理解记忆这些调用实际
视图的生命周期,定义了视图的加载到构建的全过程,其回调机制能够确保我们可以更具视图的状态选择合适的时间做恰当的事情,而App的生命周期,则定义了App从启动到退出的全过程
在原生Android、iOS开发中,有时我们需要再对应的App生命周期事件中做相应的处理,比如App从后台进入前台,从前台退出后台,或者在UI绘制完成后做一些处理。
这样的需求,在原生开发中,可以通过重写Activity、ViewController生命周期回调方法,或者是注册应用程序的相关通知来兼容App的生命周期并做相应的处理。而在Flutter中,我们可以利用WidgetsBindingObserver类,来实现同样的需求。
下面我们看看WidgetsBindingObserver中具体有哪些回调函数:
didChangeAppLifecycleState回调函数中,有一个参数类型为AppLifecycleState的枚举类型,这个枚举类型是Flutter对App生命周期状态的封装,它的常用状态包括:
可以将App切前后台,控制台输出的App状态,可以发现:
我们可以通过下面的这张图直观的了解状态切换过程
除了需要监听App的生命周期回调做相应处理外,根据不同的需求,我们需要再组件宣讲之后做一些与显示安全相关的 *** 作,在iOS中,可以通过GCD的方法,让 *** 作在下一个RunLoop执行,在Android中,可以通过Viewpost()插入消息队列,来保证在组件渲染后进行相关 *** 作。在Flutter中实现同样的需求会更简单:使用WidgetsBinding来实现即可
WidgetsBinding提供了单词Frame绘制回调和实时Frame绘制回调两种机制来满足不同的需求场景:
以上就是关于Flutter state生命周期方法之didChangeDependencies 、didUpdateWidget全部的内容,包括:Flutter state生命周期方法之didChangeDependencies 、didUpdateWidget、Flutter 之 数据传递、Flutter_定义控件StatefulWidgets和StatelessWidget等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)