在观看 Flutter 中的 Keys 教程后,我尝试改编一些示例:https://www.youtube.com/watch?v=kn0EOS-ZiIc
尽管我使用的是 uniqueKey(),但在使用 ListView.Builder 布局图块时,每次都会为两个交换的小部件元素创建状态对象。如果我使用一列来显示小部件,它确实会保留状态。除了不同的 UI 元素之外,代码是相同的。这是 MRE:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatefulWidget {
const MainApp({super.key});
@override
State<MainApp> createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
late List<Widget> tiles;
@override
void initState() {
super.initState();
tiles = [
ColorTile(key: UniqueKey()),
ColorTile(key: UniqueKey()),
ColorTile(key: UniqueKey()),
];
}
@override
Widget build(BuildContext context) {
print('build outside');
return MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton(onPressed: swapTiles),
/* body: Column(
children: tiles,
), */
body: ListView.builder(
itemCount: tiles.length,
itemBuilder: (context, index) {
return tiles[index];
},
),
),
);
}
void swapTiles() {
setState(() {
tiles.insert(2, tiles.removeAt(1));
});
}
}
class ColorTile extends StatefulWidget {
const ColorTile({super.key});
@override
State<ColorTile> createState() => _ColorTileState();
}
class _ColorTileState extends State<ColorTile> {
final Color color = Colors.primaries[Random().nextInt(Colors.primaries.length)];
@override
Widget build(BuildContext context) {
print("build ColorTile..., key: ${widget.key}");
return Container(
width: 100,
height: 100,
color: color,
);
}
}
根据我能找到的所有信息,如果 widgettype 和 key 相同,那么 Flutter 不会处理元素树上的状态对象。然而,对于 ListView.builder 来说,每次按下交换按钮时似乎都会创建一个新的状态对象。
这是为什么?
Dart SDK版本:3.0.3(稳定), Flutter 3.10.3 • 通道稳定
所有图块的状态保持一致,但每次调用
setState
时,都会对其应用新的随机颜色。要解决此问题,您可以从 Tile
小部件类中提取 Colors并将它们传递给构造函数。这样,随机颜色只会在执行
parent widget
时生成一次。如果您在交换图块时调用 setState
,则不会影响颜色状态。
示例如下:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatefulWidget {
const MainApp({super.key});
@override
State<MainApp> createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
late List<Widget> tiles;
@override
void initState() {
super.initState();
tiles = [
ColorTile( color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
key: UniqueKey()),
ColorTile(color: Colors.primaries[Random().nextInt(Colors.primaries.length)], key: UniqueKey()),
ColorTile(color: Colors.primaries[Random().nextInt(Colors.primaries.length)], key: UniqueKey()),
];
}
@override
Widget build(BuildContext context) {
print('build outside');
return MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton(onPressed: swapTiles),
/* body: Column(
children: tiles,
), */
body: ListView.builder(
itemCount: tiles.length,
itemBuilder: (context, index) {
return tiles[index];
},
),
),
);
}
void swapTiles() {
setState(() {
tiles.insert(2, tiles.removeAt(1));
});
}
}
class ColorTile extends StatefulWidget {
const ColorTile({required this.color,super.key});
final Color color;
@override
State<ColorTile> createState() => _ColorTileState();
}
class _ColorTileState extends State<ColorTile> {
@override
Widget build(BuildContext context) {
print("build ColorTile..., key: ${widget.key}");
return Container(
width: 100,
height: 100,
color:widget. color,
);
}
}