img_key_screen
代码如下
import 'dart:math'; import 'package:flutter/material.dart'; class StatelessContainer extends StatelessWidget { final Color color = Color.fromRGBO( Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1); @override Widget build(BuildContext context) { return Container( width: 100, height: 100, color: color, ); } } class Screen extends StatefulWidget { @override _ScreenState createState() => _ScreenState(); } class _ScreenState extends State<Screen> { List<Widget> widgets = [ StatelessContainer(), StatelessContainer(), ]; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: widgets, ), ), floatingActionButton: FloatingActionButton( onPressed: switchWidget, child: Icon(Icons.undo), ), ); } switchWidget() { widgets.insert(0, widgets.removeAt(1)); setState(() {}); } }
有状态组件
有状态组件的状态信息(如颜色)通常是存储在state中的,而state是存储在element树中的。
那么Key到底应该用到哪呢? 我们再来一个例子,我们把色块用Padding包装一下。运行之后会发现,色块并没有交换,而是以随机的形式在变换颜色。为什么呢?
import 'dart:math'; import 'package:flutter/material.dart'; class Screen extends StatefulWidget { Screen({Key key}) : super(key: key); @override _ScreenState createState() => _ScreenState(); } class _ScreenState extends State<Screen> { List<Widget> widgets = [ Padding( padding: const EdgeInsets.all(8.0), child: StatefulContainer(key: UniqueKey()), ), Padding( padding: const EdgeInsets.all(8.0), child: StatefulContainer(key: UniqueKey()), ), ]; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: widgets, ), ), floatingActionButton: FloatingActionButton( onPressed: switchWidget, child: Icon(Icons.undo), ), ); } switchWidget() { widgets.insert(0, widgets.removeAt(1)); setState(() {}); print('${widgets[0]}, ${widgets[1]}'); } } class StatefulContainer extends StatefulWidget { StatefulContainer({Key key}) : super(key: key); @override _StatefulContainerState createState() => _StatefulContainerState(); } class _StatefulContainerState extends State<StatefulContainer> { final Color color = Color.fromRGBO( Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1); @override Widget build(BuildContext context) { return Container( color: color, width: 100, height: 100, ); } }
结合我们上面的理论,我们分析一下这次的Widget Tree 和 Element Tree,当我们交换元素后,Flutter element-to-widget matching algorithm,(元素-组件匹配算法),开始进行对比,算法每次只对比一层,即Padding这一层。显然,Padding并没有发生本质的变化。
于是开始进行第二层对比,在对比时Flutter发现元素与组件的Key并不匹配,于是,把它设置成不可用状态,但是这里所使用的Key只是本地Key(Local Key),Flutter并不能找到另一层里面的Key(即另外一个Padding Widget中的Key)所以,Flutter就创建了一个新的Widget,而这个Widget的颜色就成了我们看到的『随机色』。
通过上面的示例,我们能明显的看出,我们的Key要设置到组件树的 顶层,而这一层在改变时,才能复用或者更新状态。
修正版本:
import 'dart:math'; import 'package:flutter/material.dart'; class Screen extends StatefulWidget { Screen({Key key}) : super(key: key); @override _ScreenState createState() => _ScreenState(); } class _ScreenState extends State<Screen> { List<Widget> widgets = [ Padding( key: UniqueKey(), padding: const EdgeInsets.all(8.0), child: StatefulContainer(), ), Padding( key: UniqueKey(), padding: const EdgeInsets.all(8.0), child: StatefulContainer(), ), ]; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: widgets, ), ), floatingActionButton: FloatingActionButton( onPressed: switchWidget, child: Icon(Icons.undo), ), ); } switchWidget() { widgets.insert(0, widgets.removeAt(1)); setState(() {}); print('${widgets[0]}, ${widgets[1]}'); } } class StatelessContainer extends StatelessWidget { final Color color = Color.fromRGBO( Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1); @override Widget build(BuildContext context) { return Container( color: color, width: 100, height: 100, ); } } class StatefulContainer extends StatefulWidget { StatefulContainer({Key key}) : super(key: key); @override _StatefulContainerState createState() => _StatefulContainerState(); } class _StatefulContainerState extends State<StatefulContainer> { final Color color = Color.fromRGBO( Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1); @override Widget build(BuildContext context) { return Container( color: color, width: 100, height: 100, ); } }