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

Flutter中GridView选图及Slider滑动时避免FutureBuilder重建的方法

问题根因

  • FutureBuilder的future参数直接绑定了实时执行的fetchAndSetPlaces()方法,每次页面触发setState(选中图片、拖动Slider都会触发)都会导致该方法重新执行,重复拉取数据库数据,同时触发页面重绘导致选中状态被覆盖。
  • 选中状态的更新逻辑没有和数据加载逻辑解耦,重复加载数据时状态无法持久保留。

解决方案

1. 缓存Future请求,避免重复拉取数据

在当前页面的State类中定义一个Future成员变量,仅在页面初始化时执行一次数据拉取,后续页面重build不会重新触发数据库请求,修改后的完整代码如下:

class _YourPageState extends State<YourPage> {
  late Future _loadImagesFuture;
  int? selectedCard; // 选中下标,定义为State的成员变量
  double _currentSliderValue = 0;

  @override
  void initState() {
    super.initState();
    // 仅在初始化时执行一次数据拉取,缓存Future结果
    _loadImagesFuture = Provider.of<Images>(context, listen: false).fetchAndSetPlaces();
  }

  // 选中逻辑调整,传入index更新选中状态
  void add(int index, File image) {
    // 保留你原有业务逻辑
    setState(() {
      selectedCard = index;
    });
  }

  // 保留你原有的计算GridView列数的逻辑
  int getSize(double value) {
    return value.round() + 2;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Image"),
        elevation: 0,
        actions: <Widget>[
          IconButton(
            icon: const Icon(
              Icons.add,
              color: Colors.white,
            ),
            onPressed: () => scan(context, selectLocation.id),
          )
        ],
        backgroundColor: Colors.transparent,
      ),
      // 替换为缓存的_loadImagesFuture,不会重复执行拉取逻辑
      body: FutureBuilder(
        future: _loadImagesFuture,
        builder: (ctx, snapshot) => snapshot.connectionState == ConnectionState.waiting
            ? const Center(
                child: CircularProgressIndicator(),
              )
            : Column(
                children: [
                  Expanded(
                    flex: 2,
                    child: Container(
                      padding: const EdgeInsets.only(
                          top: 30, bottom: 1, left: 40, right: 40),
                      child: Consumer<Images>(
                        builder: (ctx, titles, ch) => GridView.builder(
                          physics: const ScrollPhysics(),
                          itemCount: titles.items.length,
                          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                            crossAxisCount: getSize(_currentSliderValue),
                            mainAxisSpacing: 50,
                            childAspectRatio: 0.8,
                            crossAxisSpacing: 5,
                          ),
                          itemBuilder: (ctx, index) {
                            return GestureDetector(
                              onTap: () => add(index, titles.items[index].image),
                              child: Container(
                                color: selectedCard == index ? Colors.blue : Colors.green,
                                child: Image.file(titles.items[index].image),
                              ),
                            );
                          },
                        ),
                      ),
                    ),
                  ),
                  Expanded(
                    child: Container(
                      padding: const EdgeInsets.only(left: 30, right: 30),
                      child: Theme(
                        data: Theme.of(context).copyWith(
                          accentTextTheme: TextTheme(
                              bodyText2: const TextStyle(color: Colors.white)),
                        ),
                        child: Slider(
                          inactiveColor: Colors.purple,
                          activeColor: Colors.purple,
                          value: _currentSliderValue,
                          min: 0,
                          max: 2,
                          divisions: 2,
                          label: _currentSliderValue.round().toString(),
                          onChanged: (double value) {
                            setState(() {
                              _currentSliderValue = value;
                            });
                          },
                        ),
                      ),
                    ),
                  ),
                ],
              ),
      ),
    );
  }
}

2. 多选场景优化

如果需要支持多选图片,可将selectedCard改为集合类型存储选中下标,修改逻辑如下:

// 定义选中下标集合
Set<int> selectedIndexes = {};

void add(int index, File image) {
  setState(() {
    if (selectedIndexes.contains(index)) {
      selectedIndexes.remove(index);
    } else {
      selectedIndexes.add(index);
    }
  });
}

// 背景色判断逻辑修改为
color: selectedIndexes.contains(index) ? Colors.blue : Colors.green,

内容的提问来源于stack exchange,提问作者cvsrt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 09:06:02