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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 20:40:02