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

基于Flutter Provider的API错误处理与弹窗展示方案问询

问题分析

你当前的实现存在两个核心问题:

  1. 在build方法中重复添加ViewModel监听器,每次UI重建都会新增一个监听器,导致错误发生时弹出多个重复弹窗。
  2. 错误弹窗的触发逻辑与UI构建强耦合,没有做重复触发的拦截,容易引发异常和不良用户体验。
正确处理思路
  1. ViewModel层:负责管理API请求的加载、错误、数据状态,同时新增错误弹窗的触发标记,避免重复弹窗。
  2. UI层:通过一次性的状态监听触发弹窗,将弹窗展示逻辑与UI渲染逻辑分离,确保仅在错误首次出现时弹出提示。
实现方案

1. ViewModel优化(错误状态管理)

class HomeViewModel extends ChangeNotifier {
  final RequestManager requestManager;
  bool loading = false;
  Exception? error;
  List<Item> items = [];
  bool _hasShownErrorDialog = false; // 标记错误弹窗是否已展示

  HomeViewModel(this.requestManager) {
    fetchData(); // 初始化调用接口
  }

  Future<void> fetchData() async {
    loading = true;
    error = null;
    _hasShownErrorDialog = false; // 重试时重置弹窗标记
    notifyListeners();

    try {
      final response = await requestManager.getItems();
      items = response.data;
    } catch (e) {
      error = e as Exception;
    } finally {
      loading = false;
      notifyListeners();
    }
  }

  // 重置错误状态,用于重试操作
  void resetError() {
    error = null;
    _hasShownErrorDialog = false;
    notifyListeners();
  }

  // 判断是否需要展示错误弹窗
  bool shouldShowErrorDialog() {
    return error != null && !_hasShownErrorDialog;
  }

  // 标记弹窗已展示,避免重复触发
  void markErrorDialogShown() {
    _hasShownErrorDialog = true;
  }
}

2. UI层优化(弹窗与界面展示)

@override
Widget build(BuildContext context) {
  final viewModel = context.watch<HomeViewModel>();

  // 利用PostFrameCallback确保UI构建完成后再判断弹窗,避免异常
  WidgetsBinding.instance.addPostFrameCallback((_) {
    if (viewModel.shouldShowErrorDialog()) {
      _showErrorDialog(context, viewModel);
      viewModel.markErrorDialogShown();
    }
  });

  return Scaffold(
    body: _ui(viewModel, context),
  );
}

// 封装错误弹窗展示逻辑
void _showErrorDialog(BuildContext context, HomeViewModel viewModel) {
  showDialog(
    context: context,
    builder: (context) => BaseDialog(
      title: "请求失败",
      content: viewModel.error?.toString() ?? "未知错误",
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text("取消"),
        ),
        TextButton(
          onPressed: () {
            Navigator.pop(context);
            viewModel.resetError();
            viewModel.fetchData();
          },
          child: const Text("重试"),
        ),
      ],
    ),
  );
}

Widget _ui(HomeViewModel viewModel, BuildContext context) {
  if (viewModel.loading) {
    return const Center(child: CircularProgressIndicator());
  } else if (viewModel.error != null) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            viewModel.error.toString(),
            style: Theme.of(context).textTheme.titleLarge?.copyWith(color: Colors.red),
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              viewModel.resetError();
              viewModel.fetchData();
            },
            child: const Text("重试"),
          ),
        ],
      ),
    );
  } else {
    return SafeArea(
      child: ListView.builder(
        itemCount: viewModel.items.length,
        itemBuilder: (context, index) {
          final item = viewModel.items[index];
          return Card(
            margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text("ID: ${item.id}", style: Theme.of(context).textTheme.titleMedium),
                  const SizedBox(height: 8),
                  Text(item.title),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}
关键优化点
  • 通过_hasShownErrorDialog标记,确保同一错误仅弹出一次弹窗,重试失败时会重新触发。
  • 使用addPostFrameCallback避免在UI构建过程中直接调用弹窗方法,防止上下文异常。
  • 错误页面和弹窗都提供重试按钮,提升用户操作连续性。
  • 状态管理与UI展示完全分离,ViewModel专注业务逻辑,UI层只负责状态渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 10:02:38