Flutter Riverpod FutureProvider点击按钮不显示加载指示器问题
问题
使用Riverpod实现搜索功能时,创建了带autoDispose的family类型FutureProvider,初始化Widget时通过watch监听带默认参数的Provider;点击按钮传入新参数执行搜索时逻辑正常,但CircularProgressIndicator加载指示器不显示,请问原因是什么?
服务代码
import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:i_condominio/models/Condominio.dart'; final condominioPRovider = Provider((ref) => CondominioService()); final condFuture = FutureProvider.autoDispose.family<List<Condominio>, String>((ref, arg) { final serviceProvider = ref.watch(condominioPRovider); return serviceProvider.findByCepFuture(arg); }); class CondominioService { Future<List<Condominio>> findByCepFuture(String cepProcura) async { List<Condominio> listCondominio = [ Condominio( nome: 'Porto panorama', cep: Cep( cep: "11075-350", logradouro: "Rua Monsenhor Paula Rodrigues", complemento: "", bairro: "Vila Belmiro", localidade: "Santos", uf: "SP", ibge: "3548500"), numero: "129"), Condominio( nome: 'Central Park', cep: Cep( cep: "11075-350", logradouro: "Rua Monsenhor Paula Rodrigues", complemento: "", bairro: "Vila Belmiro", localidade: "Santos", uf: "SP", ibge: "3548500"), numero: "126"), Condominio( nome: 'Sao Vicente park', cep: Cep( cep: "11380-120", logradouro: "Rua Monsenhor Paula Rodrigues", complemento: "", bairro: "Vila Belmiro", localidade: "Santos", uf: "SP", ibge: "3548500"), numero: "130") ]; print('buscando $cepProcura'); await Future.delayed(const Duration(seconds: 1)); final retorno = listCondominio .where((element) => element.cep.cep == cepProcura) .toList(); print('acabou ${retorno.length}'); return Future.value(retorno); } }
Widget代码
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:i_condominio/models/Condominio.dart'; import 'package:i_condominio/services/CondominioService.dart'; import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; class SelecionarCondominioPage extends ConsumerWidget { const SelecionarCondominioPage({super.key}); selecionarCondoninio(Condominio e) { print('condominio Selecionado ${e.nome}'); } @override Widget build(BuildContext context, WidgetRef ref) { final cf = ref.watch(condFuture('11380-120')); var maskFormatter = MaskTextInputFormatter( mask: '#####-###', filter: {"#": RegExp(r'[0-9]')}, type: MaskAutoCompletionType.lazy); return Scaffold( appBar: AppBar( title: const Text('Novo usuario'), ), body: Column( children: [ Padding( padding: const EdgeInsets.all(8.0), child: TextField( inputFormatters: [maskFormatter], decoration: const InputDecoration( labelText: 'CEP', ), keyboardType: TextInputType.number, ), ), const Padding( padding: EdgeInsets.all(8.0), child: Text( "Condominios encontrados", style: TextStyle(fontSize: 22), ), ), ElevatedButton( onPressed: () => ref.watch(condFuture('11075-350')), child: const Text('Buscar 11075-350'), ), cf.when( data: (data) => Expanded( child: ListView( children: [ ...data.map((e) => ListTile( title: Text(e.nome), subtitle: Text(e.enderecoCompleto), onTap: () => showDialog( context: context, builder: ((ctx) => AlertDialog( title: const Text( 'Condominio selecionado'), content: Text( 'Confirma o condominio ${e.nome}'), actions: [ const TextButton( onPressed: null, child: Text("OK")), TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text("CANCELAR")), ], )), ))) ], ), ), error: (error, n) => Text(error.toString()), loading: () => const Center(child: CircularProgressIndicator())), ], ), ); } }
原因分析
核心问题在于按钮点击时未更新Widget监听的Provider参数:
- 你在
build方法中固定监听condFuture('11380-120'),这个Provider实例的状态已经处于data阶段 - 按钮点击时调用
ref.watch(condFuture('11075-350'))只是获取了新的Provider实例,但Widget并未切换到监听这个新实例,因此不会触发loading状态的重建
另外,ref.watch仅用于在build方法中监听状态变化,不能直接在点击事件中调用,点击事件中应该用ref.read或状态管理来切换参数。
解决方案
需要添加一个状态Provider来保存当前搜索的CEP,让Widget根据动态参数监听对应的FutureProvider实例:
1. 添加状态Provider管理当前CEP
// 在服务代码中添加 final currentCepProvider = StateProvider<String>((ref) => '11380-120'); // 默认搜索CEP
2. 修改Widget监听逻辑
在build方法中根据当前CEP动态监听condFuture:
@override Widget build(BuildContext context, WidgetRef ref) { final currentCep = ref.watch(currentCepProvider); final cf = ref.watch(condFuture(currentCep)); var maskFormatter = MaskTextInputFormatter( mask: '#####-###', filter: {"#": RegExp(r'[0-9]')}, type: MaskAutoCompletionType.lazy); // ... 其余代码不变 }
3. 修改按钮点击事件
通过更新状态Provider来切换搜索参数:
ElevatedButton( onPressed: () => ref.read(currentCepProvider.notifier).state = '11075-350', child: const Text('Buscar 11075-350'), ),
可选优化:支持TextField输入触发搜索
让用户输入的CEP自动更新搜索参数:
TextField( inputFormatters: [maskFormatter], decoration: const InputDecoration( labelText: 'CEP', ), keyboardType: TextInputType.number, onChanged: (value) { // 输入完整8位数字时自动触发搜索 if (maskFormatter.getUnmaskedText().length == 8) { ref.read(currentCepProvider.notifier).state = value; } }, ),
修改后,当切换CEP参数时,Widget会重建并监听新的condFuture实例,此时新Provider会进入loading状态,CircularProgressIndicator就能正常显示了。
内容的提问来源于stack exchange,提问作者Fabio Ebner
相关产品推荐
相关产品推荐

