Flutter Riverpod StateNotificationProvider异步值仍在加载



我是Flutter的新手。我使用Riverpod州管理图书馆。我正在调用远程API来获取棋盘游戏列表。如果我不向查询传递参数,它会返回一个热门游戏列表,如果我传递游戏名称参数,它就会返回一个名称中包含该参数的游戏列表。所以我想在用户登录页面时显示一个热门游戏的列表,如果用户在搜索栏中写下游戏名称来搜索游戏,则更新列表。问题:我的StateNotifier不工作,处于加载状态。任何帮助都将不胜感激,我真的很失落。

这是我的remote_api.dart:

final boardGamesListProvider = FutureProvider.family.autoDispose<List<BoardGame>, String>((ref, request) => RemoteApi().getBoardGames(request));
class RemoteApi {
Future<List<BoardGame>> getBoardGames(String request) async {
// Appel WS
try {
final response = await Dio().get('https://api.boardgameatlas.com/api/search?name=$request&client_id=JLBr5npPhV');
if (response.statusCode == 200) {
final data = Map<String, dynamic>.from(response.data);
final results = List<Map<String, dynamic>>.from(data['games']);
if (results.isNotEmpty) {
return results.map((e) => BoardGame.fromMap(e)).toList();
}
}
return [];
} on DioError catch (err) {
print(err);
throw ErrorHandler(message: err.response?.statusMessage ?? 'Something went wrong!');
} on SocketException catch (err) {
print(err);
throw const ErrorHandler(message: 'Please check your connection.');
}
}
}

我的search_game_controller.dart:

final boardGamesListControllerProvider =
StateNotifierProvider<BoardGameList, AsyncValue<List<BoardGame>>>((ref) {
return BoardGameList(const AsyncValue.data([]), ref);
});
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();
ref.read(boardGamesListProvider(request)).when(data: (data) {
AsyncValue.data(data);
}, error: (err, stackTrace) {
state = AsyncValue.error(err, stackTrace: stackTrace);
}, loading: () {
state = const AsyncValue.loading();
});
}
}

我的search_game_screen.dart:

class SearchGameScreen extends HookConsumerWidget {
const SearchGameScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final searchController = TextEditingController();
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: 'Search a game',
),
),
),
),
Container(
height: 50,
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
margin: const EdgeInsets.only(bottom: 2),
child: ElevatedButton(
child: const Text('Search',
style: TextStyle(color: Colors.white)),
onPressed: () {
ref
.read(boardGamesListControllerProvider.notifier).search(searchController.text);
print(boardGameListAsync);
},
),
),
],
),
Expanded(
child: boardGameListAsync
.when(
data: (boardGamesList) {
return BoardGamesList(boardGames: boardGamesList);
},
loading: () =>
const Center(child: CircularProgressIndicator()),
error: (error, _) => ErrorScreen(message: error.toString()),
),
)
],
),
);
}
}
class BoardGamesList extends HookConsumerWidget {
const BoardGamesList({Key? key, required this.boardGames}) : super(key: key);
final List<BoardGame> boardGames;
@override
Widget build(BuildContext context, WidgetRef ref) {
return ListView.builder(
itemCount: boardGames.length,
itemBuilder: (context, index) {
final boardGame = boardGames[index];
return BoardGameItemWidget(boardGame: boardGame);
},
);
}
}
class BoardGameItemWidget extends ConsumerWidget {
const BoardGameItemWidget({Key? key, required this.boardGame})
: super(key: key);
final BoardGame boardGame;
@override
Widget build(BuildContext context, WidgetRef ref) {
return GestureDetector(
onTap: () {
context.go('/game/details/${boardGame.idFromApi}');
},
child: Card(
margin: const EdgeInsets.all(8),
elevation: 8,
child: Row(
children: [
Hero(
tag: boardGame.title,
child: CachedNetworkImage(
imageUrl: boardGame.image,
placeholder: (context, url) =>
const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => const Icon(Icons.error),
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(bottom: 8),
child: Text(boardGame.title,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 20))),
],
),
)
],
),
),
);
}
}

谢谢!

我找到了解决方案:

我的屏幕:

class SearchGameScreen extends HookConsumerWidget {
const SearchGameScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final searchController = TextEditingController();
AsyncValue<List<BoardGame>> search = 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: 'Search a game',
),
),
),
),
Container(
height: 50,
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
margin: const EdgeInsets.only(bottom: 2),
child: ElevatedButton(
child: const Text('Search',
style: TextStyle(color: Colors.white)),
onPressed: () {
ref.read(boardGamesListControllerProvider.notifier).search(searchController.text);
},
),
),
],
),
Expanded(
child: search.when(
data: (data) => BoardGamesList(boardGames: data),
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (error, _) => const Center(
child: Text('Uh oh... Something went wrong...',
style: TextStyle(color: Colors.white)),
),
)
)
],
),
);
}
}

我的视图模型:

final boardGamesListControllerProvider =
StateNotifierProvider<BoardGameList, AsyncValue<List<BoardGame>>>((ref) {
return BoardGameList(const AsyncValue.data([]), ref);
});
class BoardGameList extends StateNotifier<AsyncValue<List<BoardGame>>> {
BoardGameList(AsyncValue<List<BoardGame>> items, this.ref) : super(items){
init();
}
final Ref ref;
Future<void> init() async {
state = const AsyncValue.loading();
try {
final search = await ref.watch(boardGamesListProvider('').future);
state = AsyncValue.data(search);
} catch (e) {
state = AsyncValue.error(e);
}
}
Future<void> search(String request) async {
state = const AsyncValue.loading();
try {
final search = await ref.watch(boardGamesListProvider(request).future);
state = AsyncValue.data(search);
} catch (e) {
state = AsyncValue.error(e);
}
}
}

尝试以下代码:

final boardGamesListControllerProvider =
StateNotifierProvider<BoardGameList, AsyncValue<List<BoardGame>>>((ref) {
return BoardGameList(const AsyncValue.data([]), ref);
});
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();
ref.read(boardGamesListProvider(request)).when(data: (data) {
state = AsyncValue.data(data);
}, error: (err, stackTrace) {
state = AsyncValue.error(err, stackTrace: stackTrace);
}, loading: () {
state = const AsyncValue.loading();
});
}
}

最新更新