【发布时间】:2022-10-25 19:36:21
【问题描述】:
我是 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))),
],
),
)
],
),
),
);
}
}
谢谢 !
【问题讨论】:
-
单击“搜索”按钮时是否遇到此问题?
-
是的,在初始化时我没有列表,也没有加载状态,除了搜索栏外,屏幕上什么也没有。从我启动搜索的那一刻起,加载状态就不会停止
-
我回答了这个问题。
-
嘿 @Colembole - 你为什么要使用 AsyncValue<T>?你不需要它,你知道吗?
-
嘿@DishankJindal,不,我不知道,正如我解释的那样,我是 Flutter 初学者,我很迷茫。如果您有替代解决方案,请与我分享,这将非常有帮助。
标签: flutter dart riverpod flutter-riverpod