Flutter获取Firebase Storage文件夹名列表重复项问题如何解决
问题根因
出现方法重复调用、ListView条目重复的核心原因有三点:
initState中已经调用过一次getFolders(),同时FutureBuilder的future属性直接传入getFolders()方法调用,每次组件触发重建(状态更新、父组件刷新等场景)都会重新执行一次拉取逻辑getFolders()每次执行都会直接向courses列表追加元素,没有提前清空旧数据,多次执行后同一条目会被重复添加- 对
Future<List>类型存在认知偏差:Future<List>是异步操作的包装对象,本身不是列表实例,因此无法直接调用.add()方法,不需要将存储结果的courses列表声明为Future类型
修复实现
1. 调整状态声明与初始化逻辑
单独定义一个Future类型变量存储异步请求实例,保证请求只会在初始化时执行一次;同时在每次拉取数据前清空列表旧数据,避免残留。
class _SemesterState extends State<Semester> { // 存储文件夹名称列表 List<String> courses = []; // 存储异步请求实例,避免build过程重复触发 late Future<List<String>> folderFuture; Future<List<String>> getFolders() async { // 拉取前清空旧数据,防止重复追加 courses.clear(); final storageRef = FirebaseStorage.instance.ref().child(widget.value); final listResult = await storageRef.listAll(); for (var prefix in listResult.prefixes) { courses.add(prefix.name); } return courses; } @override void initState() { super.initState(); // 初始化时仅执行一次拉取,将结果赋值给预定义的Future变量 folderFuture = getFolders(); }
2. 修改FutureBuilder绑定逻辑
FutureBuilder的future属性不要直接调用getFolders(),绑定初始化好的folderFuture即可;列表渲染优先使用snapshot返回的数据,避免全局状态的时序问题。
@override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( leading: kIconButton(), elevation: 0, centerTitle: true, title: const Text("Semester", style: kTitleStyle), backgroundColor: Colors.white, ), body: FutureBuilder( future: folderFuture, builder: (context, snapshot) { // 增加异常态处理 if (snapshot.hasError) { return Center(child: Text('加载失败:${snapshot.error}')); } switch (snapshot.connectionState) { case ConnectionState.waiting: return const Center( child: spinkitLines, ); default: return Column( children: [ const SizedBox(height: 20.0), const Center( child: Text( 'Tap to choose course', style: kPlaceholderStyle, ), ), const SizedBox(height: 30.0), Expanded( child: ListView.builder( itemCount: snapshot.data?.length ?? 0, itemBuilder: (context, index) { final course = snapshot.data![index]; return Padding( padding: const EdgeInsets.all(8.0), child: GestureDetector( onTap: () { String passedValue = widget.value; String value = "$passedValue/$course"; Get.to(() => CourseContent(value: value, courseChosen: course)); }, child: Container( height: 80, decoration: const BoxDecoration( color: Colors.black, borderRadius: BorderRadius.all(Radius.circular(20)), boxShadow: [ BoxShadow( color: Color.fromARGB(75, 0, 0, 0), blurRadius: 4, spreadRadius: 0, offset: Offset(0, 4)) ], ), child: Center( child: Padding( padding: const EdgeInsets.all(5.0), child: Text( course, style: kCardTitleStyle, ), ), ), ), ), ); }), ), ], ); } }, ), ); }
扩展说明
如果后续需要实现手动刷新(如下拉刷新),只需要在刷新逻辑中重新调用getFolders(),将返回值赋值给folderFuture后调用setState即可,不需要修改其他渲染逻辑。
内容的提问来源于stack exchange,提问作者Kevinhector
相关产品推荐
相关产品推荐

