概述
flutter 和 react 差不多,react 是 component(class 的),flutter 是 widget;react 是通过 props 和 state 变化驱动 rerender,flutter 是根据父组件构造函数参数值和 state 变化驱动 rerender;flutter 和 react 管理 state 状态的都是 setState;甚至连生命周期也都是差不多
flutter 的生命周期
生命周期方法 | 解释 |
---|---|
createState() | 创建 State 对象的方法,只会被调用一次。在这个方法中通常会初始化一些状态或变量 |
initState() | 在 State 对象被插入到树中时调用,只会被调用一次。在这个方法中通常会执行一些初始化操作,如订阅事件、初始化动画等 |
didChangeDependencies() | 在 initState() 调用之后立即调用。当 State 对象的依赖关系发生变化时会被调用,可以用来处理依赖关系变化时的逻辑 |
build() | 构建 Widget 树的方法,会被调用多次。在这个方法中定义界面的结构和布局 |
didUpdateWidget() | 在 Widget 的配置发生变化时调用。可以在这个方法中执行一些更新操作 |
deactivate() | 在 State 对象从树中移除时调用,可以用来释放资源 |
dispose() | 在 State 对象被永久删除时调用,用来释放资源、取消订阅等 |
对了,这些都是 Stateful Widget 用的,Stateless Widget 没有生命周期方法,因为它们是无状态的,不包含状态或副作用
flutter 的 setState()
setState 里传一个回调,里面是更新 state 的函数。在回调函数中更新状态后,Flutter 会在当前帧结束时重新构建 Widget
立即刷新的解决方案
setState()后不会立刻刷新,而是要到下一帧再统一刷新
如果要立刻刷新使用 scheduleFrameCallback()
setState(() {
_counter++;
});
WidgetsBinding.instance!.scheduleFrameCallback((timeStamp) {
setState(() {});
});
WidgetsBinding 还有一个 addPostFrameCallback,是当前帧结束后立即执行什么回调函数,scheduleFrameCallback 是在下一帧开始前执行的,这俩感觉用起来差不多。那个!是非空断言操作符
这一帧末尾 | 空白的时间开始 | 空白的时间末尾 | 下一帧开始 |
---|---|---|---|
addPostFrameCallback() | |||
scheduleFrameCallback() |
异步
如果 setState 里的函数是异步的,可能会出现问题,因为下一轮 rerender 不会等这个异步执行完的,等这个异步函数执行完,也不会告诉 flutter 说该再刷一次了,只能等哪个其他的 state 更新了,顺带给它更新上。所以最好是在异步操作后来一次空的 setState((){})让它刷一下
性能
- 避免在 setState() 中进行耗时操作,以免影响界面的流畅性
- 尽量将 setState() 用于更新界面的部分,避免不必要的重建
flutter hook
https://pub.dev/packages/flutter_hooks
flutter 性能最佳实践
https://docs.flutter.dev/perf/best-practices
- build 里的 widget 不要太大,多拆点,这样刷新的时候不用以一个整体刷,只要刷变化的部分
- 少用 saveLayer。这个 saveLayer 的意思是保存一个图层,然后之后在这个图层的基础上再画其他的,这样开销肯定大
- 少用 grids 和 lists,用的话上懒加载
- Minimize layout passes caused by intrinsic operations,这个有点复杂,不好细说,大意就是最好父组件知道子组件的 layout,不要子组件一个一个跟他汇报;子组件也不要每个子组件都去问父组件有啥约束
- 在 16ms 内渲染完成每一帧(构建+显示)
- 其他的,动画上少用 opacity,clip,动画和不动的分开,如果子元素就显示几个其他的不显示看看能不能不要用 List 啥的,没必要重载==
一些细节
在 Flutter 中,setState() 是一个用于通知框架重新构建 Widget 的方法。当调用 setState() 后,Flutter 会标记当前的 Widget 为"脏",并在当前帧结束后调用 build() 方法来重新构建这个 Widget,从而更新界面。
所以说,即使是空的 setState((){}),虽然什么 state 都没有改变,但还是会让 flutter 认为这个 widget 已经脏了,然后刷新。这一点和 react 中的 diff 不一样,diff 会切实的去比较有没有真变的。我们可以利用这一特性在我们希望立刻刷新的时候调用 setState((){})