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

Flutter如何处理widget唯一性?解决ListView不同条目展示相同图片问题

问题根因

  1. 你定义的list_of_images是全局/外层作用域的共享变量,每次调用getimages都往同一个列表累加数据,没有清空逻辑,所有TitleList组件拿到的都是同一个列表的引用,自然显示内容一致。
  2. getimages是异步方法,而ListView.builder的itemBuilder是同步执行的,调用getimages后直接返回了TitleList组件,此时数据库查询还没完成。等第一次查询完成填充了列表,后续所有条目复用的都是这个已经被填充的列表,自然所有条目都显示第一张的图片。
  3. 直接在itemBuilder里调用异步方法本身不合理,列表滚动时itemBuilder会反复触发,会产生大量重复的数据库查询请求,性能极低。

修改方案

第一步:改造图片获取逻辑

不要用共享变量存储查询结果,改为直接异步返回对应id的图片列表:

Future<List<File>> getImages(int id) async {
  // 等待数据库查询完成
  List<Map<String, dynamic>> data = await DBHelper.selectImageforlist(id);
  // 直接转换为File列表返回
  return data.map((item) => File(item['Image'])).toList();
}

第二步:改造TitleList组件

取消外部传入images的逻辑,改为组件内部自己请求对应标题id的图片,用FutureBuilder处理异步加载状态:

class TitleList extends StatelessWidget {
  final dynamic titleList;
  const TitleList({super.key, required this.titleList});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        Navigator.of(context).pushNamed(
          ImageScreen.routeName,
          arguments: {'id': titleList.id, 'name': titleList.title},
        );
      },
      child: Row(
        children: [
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Container(
                padding: const EdgeInsets.only(top: 15, left: 12),
                child: Row(
                  children: [
                    Text(
                      truncate(titleList.title, length: 7),
                      style: const TextStyle(
                          fontSize: 20,
                          fontWeight: FontWeight.bold,
                          color: Colors.white),
                    ),
                    const SizedBox(width: 15),
                    Text(
                      getdate(titleList.date),
                      style: const TextStyle(
                          fontWeight: FontWeight.normal,
                          color: Colors.white),
                    ),
                  ],
                ),
              ),
              Container(
                padding: const EdgeInsets.only(left: 10),
                // 用FutureBuilder处理异步图片加载
                child: FutureBuilder<List<File>>(
                  future: getImages(titleList.id),
                  builder: (context, snapshot) {
                    // 加载中显示占位
                    if (snapshot.connectionState == ConnectionState.waiting) {
                      return const SizedBox.shrink();
                    }
                    // 加载出错或无数据时显示空
                    if (snapshot.hasError || !snapshot.hasData) {
                      return const SizedBox.shrink();
                    }
                    final images = snapshot.data!;
                    return Row(children: [
                      Stack(
                        overflow: Overflow.visible,
                        children: makephoto(images.length, images),
                      ),
                    ]);
                  },
                ),
              )
            ],
          ),
        ],
      ),
    );
  }
}

第三步:改造首页列表逻辑

删除itemBuilder里的异步调用和list_of_images传递:

future: future,
builder: (ctx, snapshot) =>
    snapshot.connectionState == ConnectionState.waiting
        ? const Center(
            child: CircularProgressIndicator(),
          )
        : Consumer<Titles>(
            builder: (ctx, titles, ch) => Expanded(
              child: ListView.builder(
                  controller: ScrollController(),
                  scrollDirection: Axis.vertical,
                  shrinkWrap: true,
                  itemCount: titles.items.length,
                  itemBuilder: (ctx, i) {
                    final item = titles.items[i];
                    if (searchString.isEmpty) {
                      return TitleList(titleList: item);
                    }
                    return item.title!.contains(searchString) 
                        ? TitleList(titleList: item) 
                        : const SizedBox.shrink(); 
                  }),
            ),
          )

可选优化

如果要避免滚动时重复查询数据库,可以在请求层加内存缓存,把已经查询过的id对应的图片列表存起来,下次查询同id时直接返回缓存结果即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 19:45:05