赞
踩
在Flutter中,有三种类型的Widget:Stateless(无状态), Stateful(有状态) 和Inherited(继承型),所有的Widget都是不可变的,但是有些Widget使用它们的element附加了状态(state)。接下来,我们将分别了解这三种类型的Widget。
无状态的Widget在它的build()方法被调用后,就无法修改它的状态或者属性了,当属性不需要随时间而更改时,无状态Widget是一个非常好的选择。
Stateless widgets的生命周期从它的构造器开始,你可以从这里传递参数,然后到build()方法,这是一个需要重写的方法,build()方法决定了Widget的外观。
以下事件会导致 Stateless widgets更新:
Stateful widgets 引用有一个State,非常适合在UI中需要动态更新的部分使用。
Stateful widget将其可变状态存储在一个单独的State类中,这就是为什么每个Stateful widget都必须重写 createState()方法。
每一个Widget的build()方法都有一个BuildContext作为参数,BuildContext可以告诉你你在widget tree的位置,你还可以通过BuildContext访问任何widget的element,稍后,您将看到为什么BuildContext很重要,特别是对于从parent widget 访问状态信息。
我们仔细看看上图中的生命周期:
1. 当将BuildContext绑定到widget时,一个内部标志mounted将设置为true,这使框架知道这个widget被加入到widget tree上了。
2. initState() 是widget被创建后第一个被调用的方法,类似Android 中的onCreate() 或IOS中的viewDidLoad() 。
3. 当framework开始构建widget时,它会在initState()之后调用didChangeDependencies()
方法,如果widget的state对象所依赖的 inherited widget发生了变化,didChangeDependencies()方法可能被再次调用。后面我们会学习 inherited widget。
4. 最终framework会回调build()方法,这个方法对开发者来说是最重要的,因为每次widget需要渲染的时候都会调用该方法。widget tree中的每一个widget都会被遍历回调该方法,因此这个方法里的操作必须是轻量的,不能进行耗时操作,就像你在Andorid的main thread中不能进行耗时操作一样。
5. 当父widget发生变化或者需要重绘时, framework会调用 didUpdateWidget(_) 方法,此时你会得到一个旧的widget对象作为参数,你可以把这个对象与当前widget进行对比并进行处理。
6. 当你想要修改widget中的状态时,调用setState(),然后framework会调用widget的build()方法
注意:在编写异步代码的时候,调用setState()之前需要确保mounted值为true,因为此时widget可能从widget tree中被移除了
7. 当你将widget从widget tree移除时,framework会调用deactivate()方法,在某些情况下,framework会重新将state对象添加进widget tree的其他部分。
8. 当您从widget tree中永久删除widget及其状态时,框架会dispose()方法,这个方法非常重要,因为您需要在其中处理内存清理,例如取消订阅流和处理动画或控制器等。完成dispose()方法的经验法则是检查你在当前状态中定义的任何属性,并确保您已经正确地处理了它们。
回到我们的Card2中,它是一个Stateless Widget,我们点击头像右边的收藏按钮时,现在只能弹出一个文本提示,而不是像通常情况那样会切换成已收藏的图标,我们现在就来修改一下,实现想要的那种效果。
我们可以把Card2变成Stateful Widget,然后再把其中的AuthorCard变成Stateful Widget,但是记住Stateful Widget可能会经常进行重绘,重绘可能是自己触发的也有可能是父Widget或兄弟节点重绘时触发的,这些不必要的重绘会拖累我们的效率,我们尽量让重绘发生在最小的范围内。
我们回到AuthorCard中,Android Studio 提供了一些快捷方式帮助我们提高效率。右键点击AuthorCard,然后选择 Show Context Actions
在弹出的菜单中,选择 Convert to StatefulWidget,它会自动修改成Stateful Widget。
上面进行了两个事情:
现在我们在AuthorCardState中添加一个变量来记录它是否被收藏了:
bool _isFavorited = false;
现在把AuthorCard中原来的IconButton替换为以下代码:
IconButton(
//根据是否收藏来显示不同的图标
icon: Icon(_isFavorited ? Icons.favorite : Icons.favorite_border),
iconSize: 30,
//变成红色更符合常规效果
color: Colors.red[400],
onPressed: () {
//点击时,更新状态
//setState()方法调用后引起重绘
setState(() {
_isFavorited = !_isFavorited;o
});
},
)
实际效果如下,当然不是我自己的截图,我的截图太大了。。
现在我们来看看element tree是如何来管理状态变化的。
framework在构建widget tree的时候也会为每个widget对象创建一个element对象,在本例中,创建了一个StatefulElement对象来管理state 对象
当用户点击心形按钮时, setState()方法被调用,将_isFavorited变量设为true,同时state对象将element标记为dirty,这将会触发渲染,调用build()方法。
element对象将旧的widget移除并用一个新的对象来替换它,而新的对象有一个完整的心形。
framework总是尽可能的只更新需要改变的widget,尽可能的复用来提高其效率。
Inherited widgets可以让你在树结构中,从父element中访问到state 信息。
假设你在widget tree中有一个要访问的数据片段,一种解决方案是将数据作为参数传递到每个嵌套的widget上——但这很烦人,也很可能导致错误。如果有一种中心化的方式来访问这些数据,会不会更好呢?
这就是Inherited widgets的作用所在!通过在树中添加Inherited widgets,你可以引用来自其任何后代的数据,这被称为状态提升。例如以下场景:
Inherited widgets是一个高级点的课题,我们会在后面的章节里继续深入学习它。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。