Flutter添加Firebase记录后报setState调用时机错误求助
问题:添加视频后需重启APP才能显示,且报setState错误
报错信息
setState() or markNeedsBuild() called during build.
问题场景
- 包含视频添加页和视频列表页,两页面无直接关联
- 添加视频前无报错,仅添加后出现问题
- 视频列表页通过
FutureBuilder从Firebase获取视频,传给ShowFilteredVideos组件,该组件通过GetX控制器按点赞数筛选视频 - 添加视频后必须重启APP才能看到新视频,点击导航栏打开列表页时触发错误
相关代码
获取视频的Future函数
Future<List<VideoModel>> fetAllVideos() async{ try{ final videoList=await videoRepo.fetchFollowingVideos(); return videoList; }catch(e){ MyHelperFunction.showSnackbar(e.toString()); return []; } }
视频列表页及组件代码
class AllVideosScreen extends StatelessWidget { const AllVideosScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final controller=Get.put(VideoController()); return Scaffold( body: SingleChildScrollView( child: Column(children: [ FutureBuilder( future: controller.fetchFollowingVideos(), builder: (context,snapshot){ final widget=MyHelperFunction.futureBuilderState(snapshot: snapshot); if(widget!=null) return widget; final videos=snapshot.data!; return ShowFilteredVideos(videos: videos); }, ) ],), ), ); } }
class ShowFilteredVideos extends StatelessWidget { final List<VideoModel> videos; const ShowFilteredVideos({Key? key, required this.videos}) : super(key: key); @override Widget build(BuildContext context) { final profileVideoController = Get.put(ProfileVideoController()); profileVideoController.assignVideos(videos); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Obx(() => MyGridView( itemCount: profileVideoController.videoList.length, itemBuilder: (_, index) { final video = profileVideoController.videoList[index]; return CircularImage( imageUrl: video.videoUrl, isNetworkImage: true, fit: BoxFit.fill, onTap: () { //controller.openImage(video.videoUrl); }, ); }, mainAxisExtent: 200, )) ], ); } }
筛选控制器代码
class ProfileVideoController extends GetxController { RxList<VideoModel> videoList=<VideoModel>[].obs; RxString selectedFilterOption='Popular'.obs; void filterVideos(String option) { selectedFilterOption.value=option; switch(option) { case 'Popular': videoList.sort((a,b)=>b.likes.length.compareTo(a.likes.length)); break; case 'All': videoList.sort((a,b)=>a.likes.length.compareTo(b.likes.length)); break; default: videoList.sort((a,b)=>a.likes.length.compareTo(b.likes.length)); } } void assignVideos(List<VideoModel> videos) { videoList.assignAll(videos); filterVideos('Popular'); } }
解决方案
1. 修复build期间修改状态的错误
ShowFilteredVideos的build方法中直接调用assignVideos,会在组件构建过程中修改RxList状态并触发UI更新,导致报错。将状态更新延迟到当前build完成后执行:
@override Widget build(BuildContext context) { final profileVideoController = Get.put(ProfileVideoController()); WidgetsBinding.instance.addPostFrameCallback((_) { profileVideoController.assignVideos(videos); }); return Column( // 保留原有UI代码 ); }
2. 解决添加视频后不自动刷新的问题
原FutureBuilder每次build都会重新执行Future,且无法感知Firebase数据变化。改用GetX的可观察变量监听数据状态,进入页面时主动刷新:
- 修改
VideoController,将视频列表改为可观察对象:
class VideoController extends GetxController { RxList<VideoModel> videoList = <VideoModel>[].obs; RxBool isLoading = false.obs; Future<void> fetchFollowingVideos() async { isLoading.value = true; try { final list = await videoRepo.fetchFollowingVideos(); videoList.assignAll(list); } catch(e) { MyHelperFunction.showSnackbar(e.toString()); } finally { isLoading.value = false; } } }
- 修改
AllVideosScreen,用Obx监听数据变化:
class AllVideosScreen extends StatelessWidget { const AllVideosScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final videoController = Get.put(VideoController()); // 进入页面时主动刷新数据 WidgetsBinding.instance.addPostFrameCallback((_) { videoController.fetchFollowingVideos(); }); return Scaffold( body: SingleChildScrollView( child: Column(children: [ Obx(() { if(videoController.isLoading.value) { return const CircularProgressIndicator(); } return ShowFilteredVideos(videos: videoController.videoList); }) ],), ), ); } }
3. 优化筛选控制器的赋值逻辑
避免重复数据,赋值前先清空列表:
void assignVideos(List<VideoModel> videos) { videoList.clear(); videoList.addAll(videos); filterVideos('Popular'); }
内容的提问来源于stack exchange,提问作者user22362195
相关产品推荐
相关产品推荐

