Flutter Riverpod StateNotifierProvider始终处于加载状态求助
解决Riverpod StateNotifier一直处于加载状态的问题
核心问题分析
search方法中错误使用ref.read(...)结合when处理异步结果:when是同步方法,无法等待Future完成,且未将获取到的数据赋值给state- 页面初始化时未自动触发热门桌游列表加载
- API调用逻辑有缺陷:当查询参数为空时,URL仍携带
name=参数,不符合API获取热门列表的规则
修正步骤及代码
1. 修复RemoteApi的API调用逻辑
当查询参数为空时,不添加name参数,确保API返回热门桌游列表:
final boardGamesListProvider = FutureProvider.family.autoDispose<List<BoardGame>, String>((ref, request) => RemoteApi().getBoardGames(request)); class RemoteApi { Future<List<BoardGame>> getBoardGames(String request) async { try { // 根据查询参数是否为空构造请求URL String url = 'https://api.boardgameatlas.com/api/search?client_id=JLBr5npPhV'; if (request.isNotEmpty) { url += '&name=$request'; } final response = await Dio().get(url); if (response.statusCode == 200) { final data = Map<String, dynamic>.from(response.data); final results = List<Map<String, dynamic>>.from(data['games'] ?? []); return results.map((e) => BoardGame.fromMap(e)).toList(); } return []; } on DioError catch (err) { print(err); throw ErrorHandler(message: err.response?.statusMessage ?? '请求出错!'); } on SocketException catch (err) { print(err); throw const ErrorHandler(message: '请检查网络连接。'); } } }
2. 修复StateNotifier的逻辑
改用await等待Future结果返回,并在初始化时自动加载热门列表:
final boardGamesListControllerProvider = StateNotifierProvider<BoardGameList, AsyncValue<List<BoardGame>>>((ref) { final controller = BoardGameList(const AsyncValue.data([]), ref); // 初始化时自动加载热门桌游 controller.search(''); return controller; }); class BoardGameList extends StateNotifier<AsyncValue<List<BoardGame>>> { BoardGameList(AsyncValue<List<BoardGame>> items, this.ref) : super(items); final Ref ref; Future<void> search(String request) async { state = const AsyncValue.loading(); try { // 等待FutureProvider返回结果 final data = await ref.read(boardGamesListProvider(request).future); state = AsyncValue.data(data); } catch (err, stackTrace) { state = AsyncValue.error(err, stackTrace: stackTrace); } } }
3. 优化搜索页面细节
使用Hook管理文本控制器,避免重复创建:
class SearchGameScreen extends HookConsumerWidget { const SearchGameScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { // 用Hook创建文本控制器,避免每次build重建 final searchController = useTextEditingController(); final boardGameListAsync = ref.watch(boardGamesListControllerProvider); return Scaffold( body: Column( children: [ Row( children: [ Expanded( child: Container( padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), margin: const EdgeInsets.only(bottom: 2), child: TextFormField( controller: searchController, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: '搜索桌游', ), ), ), ), Container( height: 50, padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), margin: const EdgeInsets.only(bottom: 2), child: ElevatedButton( child: const Text('搜索', style: TextStyle(color: Colors.white)), onPressed: () { ref.read(boardGamesListControllerProvider.notifier) .search(searchController.text.trim()); }, ), ), ], ), Expanded( child: boardGameListAsync.when( data: (boardGamesList) { return BoardGamesList(boardGames: boardGamesList); }, loading: () => const Center(child: CircularProgressIndicator()), error: (error, _) => ErrorScreen(message: error.toString()), ), ) ], ), ); } } // BoardGamesList和BoardGameItemWidget代码保持不变
内容的提问来源于stack exchange,提问作者Collembole
相关产品推荐
相关产品推荐

