You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 02:40:16