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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:54:51