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

使用RiverPod StateNotifierProvider引发build期间状态更新错误求助

解决RiverPod StateNotifier在build期间更新state导致的setState报错问题

问题场景

我在Flutter应用中采用RiverPod v2的StateNotifierProvider实现AppBar的通知指示器,NotificationNotifier代码如下:

class NotificationNotifier extends StateNotifier<List<UserNotification>> {
  NotificationNotifier() : super([]);

  List<UserNotification> get getState => state;

  Future<List<UserNotification>> fetchUserNotifications(uid) async {
    state = [];
    try {
      final res = await http.get(Uri.parse("${Environment().config.baseApiUrl}/users/$uid/notifications"));
      final data = jsonDecode(res.body);
      List<UserNotification> retrievedNotifications = [];
      for (var i = 0; i < data["notifications"].length; i++) {
        UserNotification tmpNotifications = UserNotification.fromJson(data["notifications"][i]);
        retrievedNotifications.add(tmpNotifications);
      }
      state = [...state, ...retrievedNotifications];

      print("fetchUserNotifications:state: $state");

      return state;
    } catch (err) {
      throw Exception("Unable to fetch user notifications: $err");
    }
  }

  Future<int> onClickNotification(UserNotification notification) async {
    print("UserRepositoryNotifier:onClickNotification");

    for (var n = 0; n < state.length; n++) {
      if (state[n].notificationId == notification.notificationId) {
        try {
          final response = await http.patch(
            Uri.parse("${Environment().config.baseApiUrl}/notifications/${state[n].notificationId}"),
          );

          return response.statusCode;
        } catch (err) {
          throw Exception("Unable to fetch user frieds: $err");
        }
      }
    }

    return 1;
  }
}

为了让AppBar图标实时更新,我尝试在initState、didChangeDependencies和build方法中调用fetchUserNotifications,但该方法通过state = [...state, ...retrievedNotifications];更新state时会触发组件重建,导致以下报错:

Unhandled Exception: setState() or markNeedsBuild() called during build.

解决方案

1. 绝对禁止在build方法中调用状态更新方法

build是Flutter组件构建的核心流程,在此阶段调用会触发state更新的异步方法,必然会打破构建生命周期规则,直接删除build方法内的fetchUserNotifications调用。

2. 在initState中延迟执行异步更新

initState是组件初始化的生命周期,但直接在此调用异步状态更新仍可能触发报错。使用WidgetsBinding.instance.addPostFrameCallback,确保当前帧构建完成后再执行异步操作:

@override
void initState() {
  super.initState();
  // 延迟到当前帧构建完成后执行
  WidgetsBinding.instance.addPostFrameCallback((_) {
    ref.read(notificationNotifierProvider.notifier).fetchUserNotifications(uid);
  });
}

3. 优化NotificationNotifier的状态更新逻辑

当前fetchUserNotifications中先将state设为空,再用拼接方式更新,逻辑冗余且可能导致不必要的状态变更。直接将state替换为新获取的列表即可:

// 替换原有的 state = [...state, ...retrievedNotifications];
state = retrievedNotifications;

4. 避免didChangeDependencies重复触发

如果需要在didChangeDependencies中调用,添加标记确保只执行一次,防止依赖变化时重复请求:

bool _hasFetchedNotifications = false;

@override
void didChangeDependencies() {
  super.didChangeDependencies();
  if (!_hasFetchedNotifications) {
    _hasFetchedNotifications = true;
    WidgetsBinding.instance.addPostFrameCallback((_) {
      ref.read(notificationNotifierProvider.notifier).fetchUserNotifications(uid);
    });
  }
}

核心原因

报错本质是在Flutter的构建流程(build阶段)中触发了状态更新,违反了"构建期间不能修改状态"的规则。通过将异步状态更新延迟到构建完成后,同时优化状态更新逻辑,就能彻底解决该问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 05:30:47