Flutter如何处理widget唯一性?解决ListView不同条目展示相同图片问题
问题根因
- 你定义的
list_of_images是全局/外层作用域的共享变量,每次调用getimages都往同一个列表累加数据,没有清空逻辑,所有TitleList组件拿到的都是同一个列表的引用,自然显示内容一致。 getimages是异步方法,而ListView.builder的itemBuilder是同步执行的,调用getimages后直接返回了TitleList组件,此时数据库查询还没完成。等第一次查询完成填充了列表,后续所有条目复用的都是这个已经被填充的列表,自然所有条目都显示第一张的图片。- 直接在
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
相关产品推荐
相关产品推荐

