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

Flutter-Riverpod Future Provider:报错时如何保留旧数据?

Riverpod刷新出错时保留现有数据的解决方案

默认FutureProvider在刷新数据失败时,会将状态切换为error,覆盖之前的data,导致现有数据丢失、UI直接切换到错误组件。要实现刷新失败保留旧数据的需求,最优雅的方式是用StateNotifierProvider手动管控状态逻辑,具体实现如下:

1. 定义状态类

创建一个包含数据、加载状态、错误信息的状态模型,方便精细化更新:

class DataState<T> {
  final T? data;
  final bool isLoading;
  final Object? error;

  DataState({
    this.data,
    this.isLoading = false,
    this.error,
  });

  // 拷贝方法,仅更新指定字段,保留其他原有状态
  DataState<T> copyWith({
    T? data,
    bool? isLoading,
    Object? error,
  }) {
    return DataState<T>(
      data: data ?? this.data,
      isLoading: isLoading ?? this.isLoading,
      error: error ?? this.error,
    );
  }
}

2. 实现StateNotifier

编写状态控制器,在刷新失败时仅更新错误和加载状态,不清除现有数据:

class DataNotifier<T> extends StateNotifier<DataState<T>> {
  final Future<T> Function() fetchData;

  DataNotifier(this.fetchData) : super(DataState<T>()) {
    // 初始化时自动加载数据
    _loadInitialData();
  }

  Future<void> _loadInitialData() async {
    state = state.copyWith(isLoading: true);
    try {
      final newData = await fetchData();
      state = state.copyWith(
        data: newData,
        isLoading: false,
        error: null,
      );
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        error: e,
      );
    }
  }

  // 手动刷新方法,失败保留旧数据
  Future<void> refresh() async {
    state = state.copyWith(isLoading: true);
    try {
      final newData = await fetchData();
      state = state.copyWith(
        data: newData,
        isLoading: false,
        error: null,
      );
    } catch (e) {
      // 仅更新错误和加载状态,data字段沿用旧值
      state = state.copyWith(
        isLoading: false,
        error: e,
      );
    }
  }
}

3. 创建Provider

注册对应的StateNotifierProvider:

final dataProvider = StateNotifierProvider<DataNotifier<MyModel>, DataState<MyModel>>((ref) {
  return DataNotifier(() => fetchData()); // 传入你的数据获取方法
});

4. UI层使用

在组件中监听状态,即使刷新出错,现有数据依然保留,错误提示可叠加显示而非替换数据组件:

final dataState = ref.watch(dataProvider);

return Stack(
  children: [
    // 始终显示现有数据(如果存在)
    if (dataState.data != null) DataWidget(dataState.data!),
    // 加载状态覆盖显示
    if (dataState.isLoading) LoadingWidget(),
    // 错误状态可放在角落或顶部,不替换数据
    if (dataState.error != null && !dataState.isLoading)
      Positioned(
        top: 16,
        left: 0,
        right: 0,
        child: ErrorWidget(dataState.error!),
      ),
  ],
);

5. 触发刷新

用户触发刷新时调用控制器的refresh方法即可:

ref.read(dataProvider.notifier).refresh();

这种方案的核心是通过StateNotifier完全掌控状态更新逻辑,避免了默认FutureProvider状态切换时丢失旧数据的问题,同时能灵活控制加载、错误、数据的显示逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 16:00:53