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

Riverpod:API错误时如何在输入框下方正确显示错误信息

解决方案

你的问题核心是混合了本地状态(isError)和Riverpod控制器状态,导致状态不同步,且AsyncNotifier的错误状态不会自动重置。下面是符合声明式编程的优雅解决方案:

1. 优化控制器:自定义异常+添加状态重置方法

首先给控制器添加自定义异常(避免字符串匹配异常的脆弱性),并新增清除错误状态的方法:

// submit_controller.dart
class InvalidOtpException implements Exception {}

class SubmitController extends AsyncNotifier<void> {
  @override
  FutureOr<void> build() {}

  Future<void> submit(String otp) async {
    // 每次提交先进入loading状态,重置之前的错误
    state = const AsyncValue.loading();
    try {
      // 替换为实际的API调用
      await Future.delayed(const Duration(seconds: 3));
      // 模拟OTP验证失败
      throw InvalidOtpException();
    } on InvalidOtpException catch (error, stackTrace) {
      // 仅捕获OTP无效的异常
      state = AsyncError(error, stackTrace);
    } catch (error, stackTrace) {
      // 处理其他未知异常
      state = AsyncError(error, stackTrace);
    }
  }

  // 清除错误状态,用于用户输入时重置
  void clearError() {
    if (state.hasError) {
      state = const AsyncData(null);
    }
  }
}

2. 重构页面:移除本地状态,依赖控制器单一数据源

去掉页面中的isError本地状态,直接从控制器状态读取错误信息,同时在用户输入时触发状态重置:

// main.dart
class HomePage extends ConsumerStatefulWidget {
  const HomePage({super.key});

  @override
  ConsumerState<HomePage> createState() => _HomePageState();
}

class _HomePageState extends ConsumerState<HomePage> {
  final TextEditingController controller = TextEditingController();

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final submitState = ref.watch(submitControllerProvider);

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              enabled: !submitState.isLoading,
              controller: controller,
              onChanged: (_) {
                // 用户输入时立即清除错误状态
                ref.read(submitControllerProvider.notifier).clearError();
              },
            ),
            // 根据控制器状态直接渲染错误信息
            if (submitState.error is InvalidOtpException)
              const Text(
                'Invalid OTP',
                style: TextStyle(color: Colors.red),
              )
            else
              const SizedBox.shrink(),
            TextButton(
              // loading时禁用按钮,防止重复提交
              onPressed: submitState.isLoading
                  ? null
                  : () {
                      ref.read(submitControllerProvider.notifier).submit(controller.text);
                    },
              child: const Text('Submit'),
            ),
          ],
        ),
      ),
    );
  }
}

关键改进点

  • 单一数据源:所有状态由Riverpod控制器管理,避免本地状态和控制器状态不一致的问题。
  • 声明式渲染:错误信息的显示完全依赖控制器的状态,无需手动维护isError变量。
  • 状态自动同步:用户输入时主动重置控制器的错误状态,确保输入时错误信息立即消失。
  • 健壮的异常处理:用自定义异常替代字符串匹配,避免后续异常信息修改导致的逻辑失效。

内容的提问来源于stack exchange,提问作者Syed Ahmed Jamil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 03:12:43