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

使用Riverpod处理异步数据:点击按钮自动填充TextField的最佳方案

解决方案:连接FutureProvider到UI并处理异步状态

一、当前场景下优先选择 FutureProvider + when()

你的需求是点击按钮触发API请求并填充输入框,同时支持手动编辑,这种简单的异步请求场景用 FutureProvider.family + AsyncValue.when() 最直接,代码量少且逻辑清晰。

修改后的完整UI实现代码

@override
Widget build(BuildContext context, WidgetRef ref) {
  final cep = ref.watch(cepStateProvider);
  final cepResult = ref.watch(getCepProvider(cep));

  // 监听请求结果,成功时填充地址输入框
  ref.listen<AsyncValue<ResultCep>>(getCepProvider(cep), (previous, next) {
    next.whenData((data) {
      // 仅当请求成功时更新地址,避免覆盖用户手动输入
      _addressController.text = data.logradouro;
      // 可扩展填充其他字段,例如bairro、cidade等
    });
  });

  return Scaffold(
    body: Padding(
      padding: const EdgeInsets.all(12.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              buildCustomTextField(
                controller: _cepController,
                labelText: 'CEP',
                maxWidth: 250,
                validator: (value) {},
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: ElevatedButton(
                  onPressed: () {
                    // 更新Cep状态,触发FutureProvider请求
                    ref.read(cepStateProvider.notifier).state = _cepController.text;
                  },
                  style: ElevatedButton.styleFrom(
                    minimumSize: const Size(100, 50),
                    backgroundColor: colorPrimary,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(20),
                    ),
                  ),
                  child: const Text("Buscar"),
                ),
              ),
            ],
          ),
          // 处理异步状态:加载、错误、成功
          cepResult.when(
            loading: () => const CircularProgressIndicator(),
            error: (error, stack) => Text('Erro: ${error.toString()}'),
            data: (_) => const SizedBox.shrink(), // 数据已通过listen填充,这里无需额外显示
          ),
          buildCustomTextField(
            controller: _addressController,
            labelText: 'Endereço',
            maxWidth: 400,
            validator: (value) {},
          ),
        ],
      ),
    ),
  );
}

关键说明:

  • AsyncValue.when():专门用于处理Provider异步状态的方法,完美覆盖加载中、出错、成功三种场景。
  • ref.listen():监听Provider状态变化,仅在请求成功时更新地址输入框,不会干扰用户手动编辑操作。
  • 补充了Column布局包裹Row和地址输入框,修复原代码的布局结构问题。

二、什么时候适合用 NotifierProvider?

如果你的需求后续扩展为:

  • 需要保存历史请求记录
  • 允许用户取消正在进行的请求
  • 对获取到的地址数据进行本地修改并同步状态
  • 多组件共享复杂的异步状态逻辑

此时推荐使用 AsyncNotifierProvider(适用于异步场景的Notifier),它提供更灵活的状态控制能力。

重构为AsyncNotifierProvider的示例代码

1. 定义Notifier类
class CepNotifier extends AsyncNotifier<ResultCep?> {
  @override
  ResultCep? build() => null; // 初始状态为null

  Future<void> fetchCep(String cep) async {
    state = const AsyncLoading(); // 设置加载状态
    try {
      final service = CepService(cep);
      final result = await service.fetchCep();
      state = AsyncData(result); // 请求成功,保存数据
    } catch (e) {
      state = AsyncError(e, StackTrace.current); // 请求失败,保存错误信息
    }
  }
}

// 注册Provider
final cepNotifierProvider = AsyncNotifierProvider<CepNotifier, ResultCep?>(CepNotifier.new);
2. 对应的UI代码
@override
Widget build(BuildContext context, WidgetRef ref) {
  final cepAsyncValue = ref.watch(cepNotifierProvider);

  return Scaffold(
    body: Padding(
      padding: const EdgeInsets.all(12.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              buildCustomTextField(
                controller: _cepController,
                labelText: 'CEP',
                maxWidth: 250,
                validator: (value) {},
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: ElevatedButton(
                  onPressed: () {
                    // 直接调用Notifier的fetch方法触发请求
                    ref.read(cepNotifierProvider.notifier).fetchCep(_cepController.text);
                  },
                  style: ElevatedButton.styleFrom(
                    minimumSize: const Size(100, 50),
                    backgroundColor: colorPrimary,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(20),
                    ),
                  ),
                  child: const Text("Buscar"),
                ),
              ),
            ],
          ),
          // 处理异步状态
          cepAsyncValue.when(
            loading: () => const CircularProgressIndicator(),
            error: (error, stack) => Text('Erro: ${error.toString()}'),
            data: (result) {
              // 数据成功返回时填充地址输入框
              if (result != null) {
                _addressController.text = result.logradouro;
              }
              return const SizedBox.shrink();
            },
          ),
          buildCustomTextField(
            controller: _addressController,
            labelText: 'Endereço',
            maxWidth: 400,
            validator: (value) {},
          ),
        ],
      ),
    ),
  );
}

关键说明:

  • AsyncNotifier 将异步请求逻辑封装在内部,UI层只需调用方法即可,逻辑更内聚。
  • 状态由Notifier主动管理,支持更复杂的状态转换逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 20:39:54