使用RiverPod StateNotifierProvider引发build期间状态更新错误求助
问题场景
我在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

