JHHK

欢迎来到我的个人网站
行者常至 为者常成

10 key

目录

LocalKey

一、说明

图中的 widgeta 和 widgetb的代码如下:

class StfulItem extends StatefulWidget {
  // ignore: prefer_const_constructors_in_immutables
  StfulItem(this.title, {Key? key}) : super(key: key);

  // 注意 title 是widget的属性
  final String title;

  final _colorInWidget = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);

  @override
  _StfulItemState createState() => _StfulItemState();
}

class _StfulItemState extends State<StfulItem> {

  // 注意:color是state的属性
  final _colorInState = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      // color: widget._colorInWidget,
      color: _colorInState,
      child: Text(widget.title),
    );
  }
}

注意:
1、title是widget的属性。
2、color是state的属性

widget不稳定,每次build都会创建新的
element稳定,并且element持有widget和state。
flutter系统会根据需要来更新element持有的widget或者销毁当前element,创建新的element
是更新widget还是重新创建取决与一个方法:

//这个是widget的静态方法
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }

当更新element时,调用:Widget.canUpdate()方法
当widget创建没有指定key时,oldWidget.key == newWidget.key 会被认为是true.

如图中:
当我们删除掉widgeta时,更新element时会拿element树的第一个节点和widget树的第一个节点比较
调用Widget.canUpdate(elementa.widget,widgetb),因为没有指定key,所以返回true
这时会将widgetb.title更新到elementa.widget,但color不是widgit的属性,color是state的属性而state属于element
所以颜色是红色的b

二、ChatGPT关于该方法的说明:

canUpdate 方法返回 true 时,Flutter 框架会执行以下操作来处理:

  1. 复用现有的 Element
    Flutter 框架会尝试复用现有的 Element 实例,而不是重新创建新的 Element
    Element 是与 Widget 相关联的 UI 元素,负责构建和管理实际的渲染树。
    由于 canUpdate 方法返回了 true,表示新的 Widget 实例的数据可以更新到现有的 Widget 实例,因此不需要创建新的 Element

  2. 更新 Widget 实例:
    Flutter 框架会将新的 Widget 实例中的属性更新到现有的 Widget 实例中。这样,现有的 Widget 实例就能反映出新的配置和属性。
    这一步骤是在 Element.update() 方法中完成的。在这个方法中,Flutter 框架会检查新的 Widget 实例与现有 Widget 实例之间的差异,并根据差异更新实际的渲染树。

  3. 更新 UI 树:
    Element.update() 方法完成后,Flutter 框架会更新 UI 树,以反映出 Widget 的更新。
    这将触发所需部分的重绘,并将更改反映在应用程序的界面上。

通过复用现有的 Element 并更新 Widget 实例,Flutter 避免了不必要的 UI 重建,从而提高了应用程序的性能和效率。
这是 Flutter 框架中的一个重要优化机制,它能够在 UI 更新过程中避免不必要的渲染,从而更加高效地管理应用程序的界面。

需要注意的是,当 canUpdate 返回 true 时,通常意味着新旧 Widget 实例的配置和属性相同,这样才能保证更新是安全和正确的。
如果新的 Widget 实例与旧的实例在配置或属性上有显著差异,那么 canUpdate 应该返回 false,以确保正确的 UI 更新行为。

canUpdate 方法返回 false 时,Flutter 框架会执行以下操作来处理:

  1. 删除旧的 Element
    Flutter 框架会将旧的 Widget 实例关联的 Element 从渲染树中删除。
    这个过程是通过调用 Element.unmount() 方法来实现的。
    Element.unmount() 方法会从父级 Element 中移除当前 Element,并递归地将其从渲染树中移除。

  2. 创建新的 Element
    由于 canUpdate 方法返回了 false,表示新的 Widget 实例与旧的实例在配置或属性上有显著差异,因此不能复用现有的 Element
    因此,Flutter 框架会创建一个新的 Element 来代表新的 Widget 实例。
    创建新的 Element 是通过调用 Widget.createElement() 方法实现的。 这个方法由 Widget 类定义,通常在 Element 的子类中被重写。

  3. 构建新的 UI 树:
    当新的 Element 创建完毕后,Flutter 框架会执行 Element.build() 方法来构建新的 UI 树。 这个方法由 Element 的子类实现,用于构建 Widget 对应的渲染树。
    Element.build() 方法中,Flutter 框架会递归地创建新的子元素,并构建整个 UI 树。

  4. 更新 UI 树:
    当新的 UI 树构建完成后,Flutter 框架会将其插入到正确的位置,以反映出新的 Widget 实例的配置和属性。
    这将触发所需部分的重绘,并将更改反映在应用程序的界面上。

通过删除旧的 Element 并创建新的 Element,Flutter 可以确保 UI 树与新的 Widget 实例的配置和属性保持同步。
这样,无论是新的 Widget 实例还是旧的实例,都能正确地显示在应用程序的界面上。
canUpdate 返回 false 时,通常意味着新的 Widget 实例与旧的实例在配置或属性上有显著差异,需要进行全新的 UI 构建。
因此,canUpdate 方法的返回值在决定 Flutter 是否重新构建 UI 时起到了重要的作用。

GlobalKey

一、说明
GlobalKey在创建的时候可以指定一个State

final GlobalKey<_ChildPageState> _globalKey = GlobalKey();

创建widget时将key传入

ChildPage(key: _globalKey)

在任何地方我们都可以拿到state的数据了

_ChildPageState? state = _globalKey.currentState;
if (state != null) {
      state.setState(() {
            state.data = 'old:' + state.count.toString();
            state.count++;
      });
}

二、使用场景
在创建Widget树时,我们尽量将需要更新的树(statefullWidget)位于树的叶子节点处,这样可以使用局部更新来提高性能
但这样存在一个问题,statefullWidget的setState方法有可能获取不到就没办法进行局部更新,这个时候就可以使用GlobalKey

class GlobalKeyDemo extends StatelessWidget {
  GlobalKeyDemo({Key? key}) : super(key: key);

  final GlobalKey<_ChildPageState> _globalKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GlobalKeyDemo'),
      ),
      body: ChildPage(key: _globalKey),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          _ChildPageState? state = _globalKey.currentState;
          if (state != null) {
            state.setState(() {
              state.data = 'old:' + state.count.toString();
              state.count++;
            });
          }
        },
      ),
    );
  }
}

class ChildPage extends StatefulWidget {
  const ChildPage({Key? key}) : super(key: key);
  @override
  _ChildPageState createState() => _ChildPageState();
}

class _ChildPageState extends State<ChildPage> {
  int count = 0;
  String data = 'hello';
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: <Widget>[
          Text(count.toString()),
          Text(data),
        ],
      ),
    );
  }
}

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.