I'm new to Flutter. I use the Riverpod state management library. I am calling a remote API to get a list of board games. If I don't pass a parameter to my query, it returns me a list of popular games, and if I pass a games name parameter, it returns me a list of games containing the parameter in its name. So I want to display a list of popular games when the user lands on the page, and update the list if the user searches for a game by writing its name in the search bar. Problem: My StateNotifier is not working and stays in loading state. Any help will be appreciated, I'm really lost.
Here is my 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.');
}
}
}
My 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();
});
}
}
My 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))),
],
),
)
],
),
),
);
}
}
Thanks !