一統天下 flutter - widget 基礎: Key - 鍵

源碼 https://github.com/webabcd/flutter_demo
作者 webabcd

一統天下 flutter - widget 基礎: Key - 鍵

示例如下:

lib\widget\basic\key.dart

/*
 * Key - 鍵
 *
 * LocalKey 和 GlobalKey 用於讓 element 找到 widget
 * GlobalKey 也可以用於獲取對應的 Widget/State/RenderObject
 *
 * 1、LocalKey - 這是一個抽象類,用於標識 Widget,要求在 widget 樹中同一級必須唯一
 *   a) ValueKey - 值類型的 LocalKey,判斷 key 是否相同時按照值是否相等判斷
 *   b) ObjectKey - 引用類型的 LocalKey,判斷 key 是否相同時按照指針是否相等判斷
 *   c) UniqueKey - 自動生成的唯一 LocalKey,每次都會生成不同的唯一 key
 * 2、GlobalKey - 用於標識 Widget,要求在整個應用程序中必須唯一
 */

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_demo/helper.dart';


class KeyDemo extends StatefulWidget {
  const KeyDemo({Key? key}) : super(key: key);

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

class _KeyDemoState extends State<KeyDemo> {

  /// 沒有指定 key
  List<Widget> widgets1 = [
    _MyStatefulWidget(color: Colors.red),
    _MyStatefulWidget(color: Colors.green),
  ];

  /// 指定了 ValueKey
  /// ValueKey, ObjectKey 要求在樹的同一級上必須唯一
  /// 如果是 UniqueKey 的話,則每次 Widget 樹變化後,都會生成唯一的 key,所以原 Element 找不到 key 相同的 Widget,所以就會重新創建新的 Element,原 Element 會被銷燬
  List<Widget> widgets2 = [
    _MyStatefulWidget(color: Colors.red, key: ValueKey(1),),
    _MyStatefulWidget(color: Colors.green, key: ValueKey(2),),
  ];

  /// 指定了 GlobalKey
  List<Widget> widgets3 = [
    _MyStatefulWidget(color: Colors.red, key: GlobalKey(),),
    _MyStatefulWidget(color: Colors.green, key: GlobalKey(),),
  ];

  /// 指定了 GlobalKey
  /// 如果定義了 GlobalKey<T> 中的 T 則,則後續通過 GlobalKey 的 currentState 就能直接拿到類型爲 T 的 State
  final GlobalKey _globalKey1 = GlobalKey();
  final GlobalKey<_MyStatefulWidgetState> _globalKey2 = GlobalKey<_MyStatefulWidgetState>();
  List<Widget> widgets4 = [ ];

  @override
  void initState() {
    super.initState();

    widgets4 = [
      _MyStatefulWidget(color: Colors.red, key: _globalKey1,),
      _MyStatefulWidget(color: Colors.green, key: _globalKey2,),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        GestureDetector(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: widgets1,
          ),
          onTap: () {
            /// 顏色會調換,數字不會調換
            /// 因爲顏色保存在 Widget 中,數字保存在 State 中(注:State 是保存在 Element 樹中的)
            /// Widget 樹變化後,Element 會從同級樹的同樣的位置開始找類型相同的 Widget(不會在父級或子級查找)
            widgets1.insert(0, widgets1.removeAt(1));
            setState(() {});
          },
        ),

        GestureDetector(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: widgets2,
          ),
          onTap: () {
            /// 顏色會調換,數字也會調換
            /// Widget 樹變化後,Element 會從同級樹的同樣的位置開始找 key 相同的 Widget(不會在父級或子級查找)
            widgets2.insert(0, widgets2.removeAt(1));
            setState(() {});
          },
        ),

        GestureDetector(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: widgets3,
          ),
          onTap: () {
            /// 顏色會調換,數字也會調換
            /// Widget 樹變化後,Element 會從整個應用程序中找 key 相同的 Widget(即使層級發生變化也能找到)
            widgets3.insert(0, Center(child: widgets3.removeAt(1)));
            setState(() {});
          },
        ),

        GestureDetector(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: widgets4,
          ),
          onTap: () {
            widgets4.insert(0, widgets4.removeAt(1));

            /// 通過 GlobalKey 可以獲取對應的 Widget/State/RenderObject
            var widget = _globalKey1.currentWidget as _MyStatefulWidget;
            var state = _globalKey1.currentState as _MyStatefulWidgetState;
            var box = _globalKey1.currentContext?.findRenderObject() as RenderBox;
            log("color:${widget.color}, state:${state._number}, globalOffset:${box.localToGlobal(Offset.zero)}, size:${box.size}");

            /// 如果定義 GlobalKey 時指定了 GlobalKey<T> 中的 T 則這裏通過 currentState 就能直接拿到類型爲 T 的 State
            var _ = _globalKey2.currentState?._number;

            setState(() {});
          },
        ),
      ],
    );
  }
}

class _MyStatefulWidget extends StatefulWidget {
  const _MyStatefulWidget({Key? key, required this.color}) : super(key: key);

  final Color color;

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

class _MyStatefulWidgetState extends State<_MyStatefulWidget> {

  final Random _random = Random();
  int _number = 0;

  @override
  void initState() {
    super.initState();

    _number = _random.nextInt(100);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      width: 50,
      height: 50,
      color: widget.color,
      child: MyText("$_number"),
    );
  }
}

源碼 https://github.com/webabcd/flutter_demo
作者 webabcd

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章