Reputation: 376
I'm using Riverpod as a beginner and I want to implements pagination in my page.
When I scroll down, I'm doing a new API call to fetch next items, but at the same moment, I'm back at the top of my content. The items are well loaded, so I need to scroll again to see new items.
UPDATED Here is my page code :
import 'package:auto_route/annotations.dart';
import 'package:des_cartes/src/core/logger/log_service.dart';
import 'package:des_cartes/src/features/boardgames/presentation/manager/boardgames.provider.dart';
import 'package:des_cartes/src/features/boardgames/presentation/widgets/boardgame_tile.widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@RoutePage(name: 'BoardgamesPage')
class BoardgamesPage extends ConsumerStatefulWidget {
const BoardgamesPage({super.key});
@override
ConsumerState createState() => _BoardgamesPageState();
}
class _BoardgamesPageState extends ConsumerState<BoardgamesPage> {
late ScrollController scrollController;
late GlobalKey scrollKey;
@override
void initState() {
super.initState();
scrollController = ScrollController();
scrollKey = GlobalKey();
scrollController.addListener(() {
if (scrollController.position.atEdge) {
if (scrollController.position.pixels != 0) {
ref.read(pageProvider.notifier).update((state) => state = state + 1);
}
}
});
}
@override
Widget build(BuildContext context) {
final boardgames = ref.watch(boardgamesListNotifierProvider);
return Scaffold(
appBar: AppBar(
title: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(
"assets/logo_title_cropped.png",
height: 45,
),
)),
backgroundColor: const Color.fromRGBO(171, 237, 216, 1.0),
),
body: SafeArea(
child: Column(
children: [
TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Search',
),
onChanged: (value) {
ref
.read(searchGameProvider.notifier)
.update((state) => state = value);
},
),
Expanded(
child: SingleChildScrollView(
key: scrollKey,
controller: scrollController,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: boardgames.when(
data: (boardgames) => Center(
child: Wrap(
spacing: 10,
runSpacing: 10,
children: boardgames
.map((boardgame) =>
BoardgameTile(boardgame: boardgame))
.toList()),
),
error: (err, _) => Text("error: $err"),
loading: () => const CircularProgressIndicator()),
),
)
],
),
),
),
],
)),
);
}
}
And here is my providers :
import 'package:des_cartes/src/core/logger/log_service.dart';
import 'package:des_cartes/src/features/boardgames/data/data_sources/boardgames.datasource.dart';
import 'package:des_cartes/src/features/boardgames/data/repositories/boardgames.repository.dart';
import 'package:des_cartes/src/features/boardgames/domain/entities/boardgame.dart';
import 'package:des_cartes/src/features/boardgames/domain/entities/boardgame.sort_filter_config.dart';
import 'package:des_cartes/src/features/boardgames/domain/repositories/boardgames.repository.dart';
import 'package:des_cartes/src/features/boardgames/domain/use_cases/get_boardgames.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'boardgames.provider.g.dart';
@riverpod
BoardgamesDatasource boardgameDatasource(BoardgameDatasourceRef ref) =>
BoardgamesDatasource();
@riverpod
BoardgamesRepository boardgamesRepository(BoardgamesRepositoryRef ref) =>
BoardgamesRepositoryImpl(
datasource: ref.watch(boardgameDatasourceProvider));
@riverpod
class BoardgamesListNotifier extends _$BoardgamesListNotifier {
@override
Future<List<Boardgame>> build() async {
return fetchBoardgames();
}
Future<List<Boardgame>> fetchBoardgames() async {
final searchGame = ref.watch(searchGameProvider);
final page = ref.watch(pageProvider);
final config = BoardgameSortFilterConfig(
filter: searchGame,
limit: 10,
page: page,
type: BoardgameType.boardgameexpansion);
final boardgamesRepository = ref.read(boardgamesRepositoryProvider);
final boardgames = await GetBoardgames(boardgamesRepository)(config);
return boardgames.fold((failure) => [], (boardgames) {
return [...?state.value, ...boardgames];
});
}
}
final searchGameProvider = StateProvider<String>((ref) => '');
final pageProvider = StateProvider<int>((ref) => 1);
I want to stay at the bottom of my page, to see new items loaded ! I think I'm not using the correct things, please let me know !
It's better ! I have a strange behavior, here is a video link : https://drive.google.com/file/d/1am7t4Cu9Tz8J23sggx7fKL3sBFRO7hvF/view?usp=sharing
Upvotes: 1
Views: 139
Reputation: 4844
Try creating a new list instead of expanding an existing one:
return boardgames.fold((failure) => [], (boardgames) {
return [...?state.value, ...boardgames];
});
Also, you should not create controllers, global keys, or attach listeners in the build
method. build
is called many times and each time a listener will be attached, and old controllers will not be properly disposed of. Instead, use ConsumerStatefulWidget
and in initState
initialize your data correctly (and attach listeners).
Upvotes: 1