异步Redux等待失败成功处理Skill asyncredux-wait-fail-succeed

该技能用于在Flutter应用中使用AsyncRedux框架处理异步操作的状态,包括等待(加载中)、失败(错误)和成功状态,以实现UI的加载指示器、错误消息显示和状态更新。关键词:AsyncRedux, Flutter, 异步操作, 状态管理, 加载状态, 错误处理, UI更新

移动开发 0 次安装 0 次浏览 更新于 3/19/2026

名称: asyncredux-wait-fail-succeed 描述: 在组件中显示加载状态和处理操作失败。覆盖 isWaiting(ActionType) 用于旋转器,isFailed(ActionType) 用于错误状态,exceptionFor(ActionType) 用于错误消息,以及 clearExceptionFor() 用于重置失败状态。

异步Redux等待、失败、成功

AsyncRedux 提供了上下文扩展方法来跟踪异步操作状态:等待(进行中)、失败(错误)和成功(完成)。这些对于在UI中显示旋转器、错误消息和成功状态至关重要。

四个核心方法

方法 返回 目的
isWaiting(ActionType) bool 如果操作当前正在运行,则为真
isFailed(ActionType) bool 如果操作最近失败,则为真
exceptionFor(ActionType) UserException? 来自失败操作的异常
clearExceptionFor(ActionType) void 手动清除存储的异常

显示加载旋转器

使用 isWaiting() 在操作运行时显示旋转器:

Widget build(BuildContext context) {
  if (context.isWaiting(FetchDataAction)) {
    return CircularProgressIndicator();
  }
  return Text('数据: ${context.state.data}');
}

组件在操作开始和完成时自动重建。

显示错误状态

使用 isFailed()exceptionFor() 显示错误消息:

Widget build(BuildContext context) {
  if (context.isFailed(FetchDataAction)) {
    var exception = context.exceptionFor(FetchDataAction);
    return Text('错误: ${exception?.message}');
  }
  return Text('数据: ${context.state.data}');
}

组合模式:加载、错误和成功

典型模式处理所有三种状态:

Widget build(BuildContext context) {
  // 加载状态
  if (context.isWaiting(GetItemsAction)) {
    return Center(child: CircularProgressIndicator());
  }

  // 错误状态带重试
  if (context.isFailed(GetItemsAction)) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('加载项目失败'),
        Text(context.exceptionFor(GetItemsAction)?.message ?? ''),
        ElevatedButton(
          onPressed: () => context.dispatch(GetItemsAction()),
          child: Text('重试'),
        ),
      ],
    );
  }

  // 成功状态
  return ListView.builder(
    itemCount: context.state.items.length,
    itemBuilder: (context, index) => ListTile(
      title: Text(context.state.items[index].name),
    ),
  );
}

自动错误清除

当操作再次被调度时,该操作类型的任何先前错误都会自动清除。这意味着:

  • 用户看到错误
  • 用户点击“重试”再次调度操作
  • isFailed() 立即变为假
  • isWaiting() 变为真
  • 如果操作成功,组件显示成功状态
  • 如果操作再次失败,isFailed() 变为真,并带有新异常

手动错误清除

使用 clearExceptionFor() 当需要在不重试的情况下关闭错误时:

Widget build(BuildContext context) {
  if (context.isFailed(SubmitFormAction)) {
    return AlertDialog(
      title: Text('错误'),
      content: Text(context.exceptionFor(SubmitFormAction)?.message ?? ''),
      actions: [
        TextButton(
          onPressed: () {
            context.clearExceptionFor(SubmitFormAction);
          },
          child: Text('关闭'),
        ),
        TextButton(
          onPressed: () => context.dispatch(SubmitFormAction()),
          child: Text('重试'),
        ),
      ],
    );
  }
  // ...
}

操作如何失败

当操作在 before()reduce() 中抛出错误时会失败。使用 UserException 处理面向用户的错误:

class FetchDataAction extends ReduxAction<AppState> {
  @override
  Future<AppState?> reduce() async {
    final response = await api.fetchData();

    if (response.statusCode == 404) {
      throw UserException('数据未找到。');
    }

    if (response.statusCode != 200) {
      throw UserException('加载数据失败。请重试。');
    }

    return state.copy(data: response.data);
  }
}

检查多个操作

可以检查多个操作类型的等待或失败:

Widget build(BuildContext context) {
  // 检查是否有任何操作正在运行
  bool isLoading = context.isWaiting(FetchUserAction) ||
                   context.isWaiting(FetchSettingsAction);

  if (isLoading) {
    return CircularProgressIndicator();
  }

  // 检查是否有任何失败
  if (context.isFailed(FetchUserAction)) {
    return Text('加载用户失败');
  }
  if (context.isFailed(FetchSettingsAction)) {
    return Text('加载设置失败');
  }

  return MyContent();
}

下拉刷新集成

dispatchAndWait() 结合用于刷新指示器:

class MyListWidget extends StatelessWidget {
  Future<void> _onRefresh(BuildContext context) {
    return context.dispatchAndWait(RefreshItemsAction());
  }

  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
      onRefresh: () => _onRefresh(context),
      child: ListView.builder(
        itemCount: context.state.items.length,
        itemBuilder: (context, index) => ListTile(
          title: Text(context.state.items[index].name),
        ),
      ),
    );
  }
}

完整示例

class LoadProductsAction extends ReduxAction<AppState> {
  @override
  Future<AppState?> reduce() async {
    final products = await api.fetchProducts();
    if (products.isEmpty) {
      throw UserException('没有可用的产品。');
    }
    return state.copy(products: products);
  }
}

class ProductsScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('产品')),
      body: _buildBody(context),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.dispatch(LoadProductsAction()),
        child: Icon(Icons.refresh),
      ),
    );
  }

  Widget _buildBody(BuildContext context) {
    if (context.isWaiting(LoadProductsAction)) {
      return Center(child: CircularProgressIndicator());
    }

    if (context.isFailed(LoadProductsAction)) {
      final error = context.exceptionFor(LoadProductsAction);
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.error, size: 64, color: Colors.red),
            SizedBox(height: 16),
            Text(error?.message ?? '发生错误'),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () => context.dispatch(LoadProductsAction()),
              child: Text('再试一次'),
            ),
          ],
        ),
      );
    }

    final products = context.state.products;
    if (products.isEmpty) {
      return Center(child: Text('还没有产品。点击刷新加载。'));
    }

    return ListView.builder(
      itemCount: products.length,
      itemBuilder: (context, index) => ListTile(
        title: Text(products[index].name),
        subtitle: Text('\$${products[index].price}'),
      ),
    );
  }
}

参考

文档URL: