一統天下 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"),
);
}
}