在BottomSheet中使用TextFormField时为何触发Widget重建?
问题分析与解决方案
原因
点击TextField弹出键盘时,MediaQuery.of(context).viewInsets会发生变化,而你的showModalBottomSheet的builder函数直接依赖了这个值,Flutter会因此多次调用builder更新UI。再加上isScrollControlled: true会让BottomSheet随键盘调整高度,进一步触发重建,导致你看到多次打印"showBottomSheet"。
解决方法
把BottomSheet的内容封装成独立的StatefulWidget,这样即使builder被调用,子组件的状态能保留,且只有必要的部分会重建:
步骤1:创建独立的BottomSheet内容组件
class _BottomSheetContent extends StatefulWidget { const _BottomSheetContent(); @override State<_BottomSheetContent> createState() => _BottomSheetContentState(); } class _BottomSheetContentState extends State<_BottomSheetContent> { @override Widget build(BuildContext context) { // 仅在必要时重建,而非每次builder调用都执行 return Padding( padding: const EdgeInsets.all(8.0), child: Padding( padding: MediaQuery.of(context).viewInsets, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: const [ TextField() ], ), ), ); } }
步骤2:修改showModalBottomSheet的builder
showModalBottomSheet( context: context, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(25.0), ), ), isScrollControlled: true, backgroundColor: Colors.white, builder: (context) { print('showBottomSheet'); // 返回封装好的组件 return const _BottomSheetContent(); } );
额外说明
如果尝试用MediaQuery.of(context, listen: false)获取viewInsets,能避免因MediaQuery变化触发的重建,但会导致键盘弹出时BottomSheet无法自动调整padding。因此更推荐封装独立组件的方式,既能保留键盘适配,又能减少不必要的重建范围。
内容的提问来源于stack exchange,提问作者our world
相关产品推荐
相关产品推荐

