没有 Key

如果没有使用 key 情况下,当 widget 的顺序变化,状态是不会跟着变化

变化前

变化后

import 'package:flutter/material.dart';

/// create by:  zhengzeqin
/// create time:  2021-12-19 14:29
/// des:  https://www.bilibili.com/video/BV15k4y1B74z?spm_id_from=333.999.0.0

class TWFlutterKey extends StatelessWidget {
  
  const TWFlutterKey({Key? key}) : super(key: key);
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'key Demo',
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text("key demo"),
        ),
        body: Center(
          child: _buildRowBox(),
        ),
      ),
    );
  }
  
  Widget _buildColumnBox() {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: const [
        _Box(
          // key: ValueKey("grey"),
          color: Colors.grey,
        ),
        _Box(
          // key: ValueKey("red"),
          color: Colors.red,
        ),
        _Box(
          // key: ValueKey("blue"),
          color: Colors.blue,
        ),
      ],
    );
  }
  
  Widget _buildRowBox() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: const [
        _Box(
          // key: ValueKey("grey"),
          color: Colors.grey,
        ),
        _Box(
          // key: ValueKey("red"),
          color: Colors.red,
        ),
        _Box(
          // key: ValueKey("blue"),
          color: Colors.blue,
        ),
      ],
    );
  }
}

class _Box extends StatefulWidget {
  final Color color;
  final width = 100.0;
  final height = 100.0;
  
  const _Box({Key? key, this.color = Colors.yellow}) : super(key: key);
  
  
  _CountState createState() => _CountState();
}

class _CountState extends State<_Box> {
  int _counter = 0;
  
  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        _incrementCounter();
      },
      child: Container(
        alignment: Alignment.center,
        width: widget.width,
        height: widget.height,
        color: widget.color,
        child: Text("count:$_counter"),
      ),
    );
  }
  
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
}

key 的作用

Widget 的 state 是根据 key 来记录的,当没有使用 key 下变化位置,颜色是调整了。但数字没有改变。



Widget 的状态是根据以下做对比的

  • 类型,比如 Column 的 Element tree 是 ColumnElement 。如果 Colunmn 换成 Row 那就会引起状态变化。所以判断两个对象是否相等, 会先去比较它们的类型
  • key,其次判断 key 是否一致, 来定位对应的 widget

三种 Key 的

  • LocalKey 局部键,在同一级中要唯一, 可以理解为同级唯一性
    • 同一级指的是同个 tree 下,比如下面 Center 和 Column 不在同一级下所以同样的 ValueKey(1)就没有影响

  • 三种 LocalKey
    • ValueKey
    • ObjectKey
    • UniqueKey
  • GlobalKey 全局键 , 在整个App中必须是唯一的.

ValueKey

class ValueKey<T> extends LocalKey {
  /// Creates a key that delegates its [operator==] to the given value.
  const ValueKey(this.value);

  /// The value to which this key delegates its [operator==]
  final T value;

  
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType)
      return false;
    return other is ValueKey<T>
        && other.value == value;
  }

  
  int get hashCode => hashValues(runtimeType, value);

  
  String toString() {
    final String valueString = T == String ? "<'$value'>" : '<$value>';
    // The crazy on the next line is a workaround for
    // https://github.com/dart-lang/sdk/issues/33297
    if (runtimeType == _TypeLiteral<ValueKey<T>>().type)
      return '[$valueString]';
    return '[$T $valueString]';
  }
}

ObjectKey

class ObjectKey extends LocalKey {
  /// Creates a key that uses [identical] on [value] for its [operator==].
  const ObjectKey(this.value);
  
  /// The object whose identity is used by this key's [operator==].
  final Object? value;
  
  
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType)
      return false;
    return other is ObjectKey
      && identical(other.value, value);
  }
  
  
  int get hashCode => hashValues(runtimeType, identityHashCode(value));
  
  
  String toString() {
    if (runtimeType == ObjectKey)
      return '[${describeIdentity(value)}]';
    return '[${objectRuntimeType(this, 'ObjectKey')} ${describeIdentity(value)}]';
  }
}

UniqueKey

class UniqueKey extends LocalKey {
  /// Creates a key that is equal only to itself.
  ///
  /// The key cannot be created with a const constructor because that implies
  /// that all instantiated keys would be the same instance and therefore not
  /// be unique.
  // ignore: prefer_const_constructors_in_immutables , never use const for this class
  UniqueKey();

  
  String toString() => '[#${shortHash(this)}]';
}

参考