From 881d40f891eeabb1350fe0dd4a7d3649c4be87e4 Mon Sep 17 00:00:00 2001 From: Simon Ding Date: Fri, 19 Jul 2024 14:49:50 +0800 Subject: [PATCH] infinite scroll --- ui/lib/providers/welcome_data.dart | 57 +++++++++++++++++++++++------- ui/lib/search.dart | 28 +++++++-------- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/ui/lib/providers/welcome_data.dart b/ui/lib/providers/welcome_data.dart index 9cd82d3..73c047a 100644 --- a/ui/lib/providers/welcome_data.dart +++ b/ui/lib/providers/welcome_data.dart @@ -29,9 +29,8 @@ final movieWatchlistDataProvider = FutureProvider.autoDispose((ref) async { return favList; }); -var searchPageDataProvider = - AsyncNotifierProvider.autoDispose.family, String>( - SearchPageData.new); +var searchPageDataProvider = AsyncNotifierProvider.autoDispose + .family, String>(SearchPageData.new); var movieTorrentsDataProvider = AsyncNotifierProvider.autoDispose .family, String>( @@ -40,28 +39,41 @@ var movieTorrentsDataProvider = AsyncNotifierProvider.autoDispose class SearchPageData extends AutoDisposeFamilyAsyncNotifier, String> { List list = List.empty(growable: true); - + String? q; + int page = 1; @override FutureOr> build(String arg) async { + q = arg; if (isBlank(arg)) { return List.empty(); } - list = List.empty(growable: true); + return query(arg, 1); + } + + FutureOr> query(String q, int page) async { final dio = await APIs.getDio(); - var resp = await dio.get(APIs.searchUrl, queryParameters: {"query": arg}); + var resp = await dio + .get(APIs.searchUrl, queryParameters: {"query": q, "page": page}); var rsp = ServerResponse.fromJson(resp.data as Map); if (rsp.code != 0) { throw rsp.message; } - var data = rsp.data as Map; - var results = data["results"] as List; - for (final r in results) { - var res = SearchResult.fromJson(r); - list.add(res); - } - return list; + var sp = SearchResponse.fromJson(rsp.data); + return sp.results ?? List.empty(); + } + + FutureOr queryNextPage() async { + //state = const AsyncLoading(); + final newState = await AsyncValue.guard( + () async { + page++; + final awaiteddata = await query(q!, page); + return [...?state.value, ...awaiteddata]; + }, + ); + state = newState; } Future submit2Watchlist( @@ -92,6 +104,25 @@ class SearchPageData } } +class SearchResponse { + int? page; + int? totalResults; + int? totalPage; + List? results; + + SearchResponse({this.page, this.totalResults, this.totalPage, this.results}); + + factory SearchResponse.fromJson(Map json) { + return SearchResponse( + page: json["page"], + totalPage: json["total_page"], + totalResults: json["total_results"], + results: (json["results"] as List) + .map((v) => SearchResult.fromJson(v)) + .toList()); + } +} + class MediaDetail { int? id; int? tmdbId; diff --git a/ui/lib/search.dart b/ui/lib/search.dart index 1365166..fb30e91 100644 --- a/ui/lib/search.dart +++ b/ui/lib/search.dart @@ -21,10 +21,9 @@ class SearchPage extends ConsumerStatefulWidget { class _SearchPageState extends ConsumerState { List list = List.empty(); - Future? _pendingFuture; @override Widget build(BuildContext context) { - final q = widget.query??""; + final q = widget.query ?? ""; var searchList = ref.watch(searchPageDataProvider(q)); List res = searchList.when( @@ -96,18 +95,18 @@ class _SearchPageState extends ConsumerState { error: (err, trace) => [Text("$err")], loading: () => [const MyProgressIndicator()]); - var f = FutureBuilder( - // We listen to the pending operation, to update the UI accordingly. - future: _pendingFuture, - builder: (context, snapshot) { - if (snapshot.connectionState != ConnectionState.done && - snapshot.connectionState != ConnectionState.none) { - return const MyProgressIndicator(); + var f = NotificationListener( + onNotification: (ScrollNotification scrollInfo) { + if (scrollInfo is ScrollEndNotification && + scrollInfo.metrics.axisDirection == AxisDirection.down && + scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent) { + ref.read(searchPageDataProvider(q).notifier).queryNextPage(); } - return ListView( - children: res, - ); - }); + return true; + }, + child: ListView( + children: res, + )); return Column( children: [ TextField( @@ -194,7 +193,8 @@ class _SearchPageState extends ConsumerState { child: const Text('确定'), onPressed: () { ref - .read(searchPageDataProvider(widget.query??"").notifier) + .read(searchPageDataProvider(widget.query ?? "") + .notifier) .submit2Watchlist(item.id!, storageSelected, resSelected, item.mediaType!); Navigator.of(context).pop();