Flutter中如何防止showModalBottomSheet重构建?
问题描述
在showModalBottomSheet中使用FutureBuilder获取用户地址时,功能正常,但滚动时会触发FutureBuilder不必要的刷新与重复读取。演示示例中,滚动底部弹窗时容器颜色随机变化,说明每次滚动都会触发builder重建:
Future<dynamic> showAddressModalBottomSheet(BuildContext context) async { return showModalBottomSheet( context: context, builder: (_) { return Container( color: Color.fromRGBO( Random().nextInt(255), Random().nextInt(255), Random().nextInt(255), 1, ), ); }, ); }
解决方法
1. 提前初始化Future,避免在builder内创建
不要在builder回调里直接创建Future(或类似会触发状态变化的操作),因为每次builder重建都会重新执行这段代码。把Future的初始化移到弹窗外部:
// 模拟获取用户地址的接口请求 Future<List<String>> fetchUserAddresses() async { await Future.delayed(const Duration(seconds: 1)); return ["北京市朝阳区XX路", "上海市黄浦区XX街"]; } Future<dynamic> showAddressModalBottomSheet(BuildContext context) async { // 提前初始化Future,确保只执行一次 final addressFuture = fetchUserAddresses(); return showModalBottomSheet( context: context, builder: (_) { return FutureBuilder<List<String>>( future: addressFuture, // 使用提前初始化的Future builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return const Center(child: Text('加载失败')); } final addresses = snapshot.data!; return ListView.builder( itemCount: addresses.length, itemBuilder: (context, index) { return ListTile(title: Text(addresses[index])); }, ); }, ); }, ); }
2. 将弹窗内容封装为独立的StatefulWidget
把弹窗内容抽成单独的StatefulWidget,让State管理Future实例,彻底避免外部重建影响内部:
class AddressBottomSheet extends StatefulWidget { const AddressBottomSheet({super.key}); @override State<AddressBottomSheet> createState() => _AddressBottomSheetState(); } class _AddressBottomSheetState extends State<AddressBottomSheet> { late final Future<List<String>> _addressFuture; @override void initState() { super.initState(); // 在initState中初始化Future,仅执行一次 _addressFuture = fetchUserAddresses(); } Future<List<String>> fetchUserAddresses() async { await Future.delayed(const Duration(seconds: 1)); return ["北京市朝阳区XX路", "上海市黄浦区XX街"]; } @override Widget build(BuildContext context) { return FutureBuilder<List<String>>( future: _addressFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return const Center(child: Text('加载失败')); } return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { return ListTile(title: Text(snapshot.data![index])); }, ); }, ); } } // 调用弹窗 Future<dynamic> showAddressModalBottomSheet(BuildContext context) async { return showModalBottomSheet( context: context, builder: (_) => const AddressBottomSheet(), ); }
核心原理
showModalBottomSheet的builder在滚动、父组件重建等场景下会被重新调用,因此不能在builder内部执行会生成新实例的操作(比如创建新Future、生成随机颜色)。通过提前初始化Future或用State管理实例,确保关键操作仅执行一次,避免不必要的刷新与重复读取。
内容的提问来源于stack exchange,提问作者user22362195
相关产品推荐
相关产品推荐

