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

Riverpod StateNotifier报错时展示SnackBar并保留历史加载列表

问题核心原因

你当前的状态设计将SalesOrderListSuccess和SalesOrderListError作为完全独立的平级状态,当请求失败触发状态切换为SalesOrderListError时,状态内不再持有之前加载成功的列表数据,同时你在组件内强制将状态转换为SalesOrderListSuccess类型会触发类型转换异常,自然无法保留原有UI。

解决方案

推荐优先调整状态类结构,让错误状态可以携带最后一次成功的列表数据,无需大幅修改原有逻辑:

步骤1:修改状态类定义

给错误状态新增字段存储最后一次加载成功的列表:

class SalesOrderListError extends SalesOrderListState {
  final dynamic error;
  // 新增字段存储最后一次成功获取的列表
  final List<SalesOrderListItem> lastSuccessItems;

  const SalesOrderListError({
    required this.error,
    required this.lastSuccessItems,
  });

  @override
  List<Object?> get props => [error, lastSuccessItems];
}

步骤2:调整StateNotifier的错误状态赋值逻辑

触发请求失败时,将已缓存的列表数据传入错误状态:

// 原有请求失败逻辑
// state = SalesOrderListError(error: response.data);
// 修改为
state = SalesOrderListError(
  error: response.data,
  // _items是你之前存储加载成功列表的变量
  lastSuccessItems: _items,
);

步骤3:修改Widget代码,兼容多状态渲染

@override
Widget build(BuildContext context, WidgetRef ref) {    
  final todos = ref.watch(todoListProvider);
  final selectedtrack = ref.read(selectedProductIdProvider2.notifier);

  // 提取要展示的列表,兼容两种状态
  List<SalesOrderListItem> showItems = [];
  if (todos is SalesOrderListSuccess) {
    showItems = todos.salesOrderListItems;
  } else if (todos is SalesOrderListError) {
    showItems = todos.lastSuccessItems;
  }

  ref.listen(todoListProvider, (previous, current) {
    if (current is SalesOrderListError) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('请求出错,请稍后重试')),
      );
    }
  });

  // 仅从未加载到成功数据时才展示空页,已有数据时直接渲染列表
  if (showItems.isEmpty) {
    return const Center(child: Text('暂无销售订单数据'));
  }

  return ListView.builder(
    shrinkWrap: true,
    physics: const ClampingScrollPhysics(),
    itemCount: showItems.length,
    itemBuilder: (context, index) {
      final current = showItems[index];
      return ListTile(
        title: Text('${current.title}'),
        onTap: () {
          selectedtrack.state = index;
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => const DetailScreen(),
            ),
          );
        }
      );
    },
  );
}

可选优化方案

如果不想拆分多状态类,也可以直接将状态类改为统一结构,始终持有列表、错误、加载状态三个属性,更新时仅修改对应属性即可,状态切换时不会丢失已加载的列表数据:

class SalesOrderListState extends Equatable {
  final List<SalesOrderListItem> items;
  final dynamic error;
  final bool isLoading;

  const SalesOrderListState({
    this.items = const [],
    this.error,
    this.isLoading = false,
  });

  SalesOrderListState copyWith({
    List<SalesOrderListItem>? items,
    dynamic? error,
    bool? isLoading,
  }) => SalesOrderListState(
    items: items ?? this.items,
    // 错误字段主动清空,避免上次错误重复提示
    error: error,
    isLoading: isLoading ?? this.isLoading,
  );

  @override
  List<Object?> get props => [items, error, isLoading];
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 18:27:04