传入相同Key后Widget仍重建的原因探究
最小可复现代码
import 'dart:math' as math; import 'package:flutter/material.dart'; class MainScreen extends StatefulWidget { @override State<MainScreen> createState() => _MainScreenState(); } class _MainScreenState extends State<MainScreen> { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton(onPressed: () => setState(() {})), body: FooPage(key: ValueKey(0)), ); } } class FooPage extends StatelessWidget { FooPage({super.key}); final int _number = math.Random().nextInt(100); @override Widget build(BuildContext context) { print('number = $_number'); return Container(); } }
问题描述
每次点击FAB触发setState后,控制台都会输出新的number值。明明已经给FooPage传入了固定的ValueKey(0),为什么MainScreen的build方法还是会创建新的FooPage实例,而不是复用已有的那个?
注意
请勿提供以下解决方案:在FooPage中声明const构造函数并使用const FooPage(),或者将FooPage实例赋值为MainScreen的final成员变量。
解答
你混淆了Widget实例创建和Element树复用的核心逻辑:
Widget实例必然会重建:每次调用
setState,MainScreen的build方法都会重新执行,FooPage(key: ValueKey(0))这行代码会直接生成一个新的FooPageWidget实例——这是build方法执行的必然结果,和Key无关。Key管的是Element复用:Key的作用是给Flutter的Element树做唯一标识。当Widget树更新时,Flutter会对比新旧Widget的Key和类型,如果两者都匹配,就会复用对应的Element对象,不会重新创建Element。但Element复用的逻辑是:把新的Widget实例关联到已有Element上,然后调用这个新Widget的
build方法。_number属于Widget实例:你的
_number是FooPageWidget实例的成员变量,每次新的FooPage实例创建时,_number都会被重新赋值为随机数。Element复用后,会调用这个新Widget实例的build方法,所以控制台会打印新的number值。
总结:Key确保了Element不会被重建,但管不了Widget实例的创建——你每次build都生成新的Widget,_number自然跟着新实例变成随机值。
内容的提问来源于stack exchange,提问作者iDecode

