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

Flutter/Dart使用dartz的Either时如何弹出对话框而非返回Widget

问题原因
  • 你当前的报错核心来自两个不符合Flutter开发规范的写法:
    1. Either.fold要求左右分支返回完全相同的类型,你在错误分支仅调用showDialog没有返回Widget,触发编译类型校验错误。
    2. 弹窗属于副作用操作,不能放在Widget的build方法中执行——build方法会在状态变化时多次重复执行,直接写在build里会导致弹窗重复弹出,也不符合纯渲染逻辑的设计原则。
最优解决方案:使用Riverpod状态监听器处理副作用

这是最符合Riverpod设计规范的实现方式,通过ref.listen监听状态变化,仅在请求完成的回调中判断是否需要弹出错误弹窗,渲染逻辑只负责返回对应状态的Widget即可。

代码修改点

1. 调整渲染逻辑,添加状态监听器

修改HomePage中第一个Consumer的代码:

Consumer(
  builder: (ctx, ref, child) {
    // 注册状态监听器,仅在请求状态变为loaded时触发一次
    ref.listen<NotifierState>(
      notifier.select((notifier) => notifier.state),
      (previous, next) {
        if (next == NotifierState.loaded) {
          ref.read(notifier).post.fold(
            (failure) {
              // 弹窗逻辑移到这里执行,不涉及渲染返回值
              showDialog(
                context: ctx,
                barrierDismissible: true,
                builder: (BuildContext context) => AlertDialog(
                  title: Text(failure.toString()),
                ),
              );
            },
            // 成功分支不需要操作直接留空即可
            (_) {},
          );
        }
      },
    );

    // 纯渲染逻辑,所有分支都返回Widget
    if (ref.watch(notifier).state == NotifierState.initial) {
      return const Text('Press the button');
    } else if (ref.watch(notifier).state == NotifierState.loading) {
      return const CircularProgressIndicator();
    } else {
      return ref.read(notifier).post.fold(
        // 错误分支随便返回一个占位Widget即可,不需要展示错误状态就返回SizedBox.shrink()
        (failure) => const SizedBox.shrink(),
        (post) => Text(post.toString()),
      );
    }
  },
),
备选方案:在点击事件中直接处理请求结果

如果你不想用监听器,也可以修改PostChangeNotifier的getOnePost方法,让它直接返回请求结果,在按钮点击的异步回调中判断是否弹出错误:

1. 修改PostChangeNotifier的getOnePost方法

// 修改返回值类型为Future<Either<Failure, Post?>>
Future<Either<Failure, Post?>> getOnePost() async {
  _setState(NotifierState.loading);
  final result = await Task(() => _postService.getOnePost())
      .attempt()
      .mapLeftToFailure()
      .run() as Either<Failure, Post?>;
  _setPost(result);
  _setState(NotifierState.loaded);
  // 返回请求结果
  return result;
}

2. 修改按钮点击逻辑

ElevatedButton(
  onPressed: () async {
    // 等待请求完成,直接处理结果
    final result = await ref.read(notifier).getOnePost();
    result.fold(
      (failure) => showDialog(
        context: ctx,
        barrierDismissible: true,
        builder: (BuildContext context) => AlertDialog(
          title: Text(failure.toString()),
        ),
      ),
      (_) {},
    );
  },
  child: const Text('Get Post'),
)
核心逻辑说明

Either本身是纯数据类型,和UI操作没有绑定,你遇到的问题本质是把副作用写在了错误的位置,只要把弹窗这类非渲染逻辑移到事件回调、状态监听器这类专门处理副作用的位置,就可以和Either的错误处理完美配合。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 19:24:04