I am trying to implement pagination in my Flutter app but this is the first time I have done it. My idea was to create a Stream
of data that updates each time the user reaches the bottom of a list. I have failed to get this working. My current code has the logic for getting the new data and adding it to the existing data, but right now it's not even returning the first range of data so the snapshots are empty. As for the pagination functionality, I tried to use a ChangeNotifier
to notify the Stream
to update, but I don't know if that is working. Please take a look at the code below and let me know what should be changed.
The DataProvider
class:
class DataProvider extends ChangeNotifier{
DataProvider() : super();
static var changeController = ChangeNotifier();
static void reload() {
changeController.notifyListeners();
}
static int lastRange = 0;
static List data = [];
static Stream<List> paginatedUsersAndPosts() async* {
List<UserSearchResult> usersList = data.first;
List<Post> postsList = data.last;
print('Notifier');
changeController.addListener(() async {
print('Change notified, getting data');
List<int> range() {
if (lastRange == 0) {
lastRange = 10;
return [0, 10];
} else {
// Example 0, 10 => 11, 20
int newMin = lastRange 1;
int newMax = lastRange 10;
lastRange = newMax;
return [newMin, newMax];
}
}
List<Map<String, dynamic>> postsDocs = await Supabase.db
.from('posts')
.select()
.order('date', ascending: false)
.range(range().first, range().last);
List<Post> newPostsList =
postsDocs.map((postDoc) => Post.fromJson(postDoc)).toList();
newPostsList.forEach((post) async {
postsList.add(post);
if (usersList.where((u) => u.uid == post.uid).isNotEmpty) {
Map<String, dynamic> userDoc =
await Supabase.db.from('profiles').select().single();
ProfileInfoObject profileInfo = ProfileInfoObject.fromJson(userDoc);
print('New profile: $profileInfo');
Profile profile = Profile(profileInfo, []);
profile.posts.add(post);
List blockedUsers = userDoc['blockedUsers'] as List;
UserSearchResult user = (UserSearchResult(
profile, userDoc['uid'].toString(), blockedUsers));
usersList.add(user);
}
});
});
yield [usersList, postsList];
}
}
The main widget that uses the stream:
class FloatingTabBarView extends StatefulWidget {
const FloatingTabBarView({Key? key}) : super(key: key);
@override
State<FloatingTabBarView> createState() => _FloatingTabBarViewState();
}
class _FloatingTabBarViewState extends State<FloatingTabBarView> {
@override
void initState() {
PermissionsService.checkPermissions(context);
DataProvider.reload();
super.initState();
}
Stream<List> stream = DataProvider.paginatedUsersAndPosts();
return StreamBuilder<List>(
stream: stream,
builder: (context, AsyncSnapshot<List<dynamic>> snapshot) {
...
});
}
@override
Widget build(BuildContext context) {
return floatingTabBarPageView();
}
}
CodePudding user response:
Please take a look at pull_to_refresh package. This allows to implement pull to refresh and incrementally load data. Use the RefreshController
to update when the data is refreshed or loaded.
- Instantiate
RefreshController
- Wrap the
ListView
withSmartRefresher
- Implement the
onLoading
callback to fetch data incrementally - Update the
RefreshController
on completion or error.
The package has a good usage example, do check it out.
CodePudding user response:
The issue was the way I was creating the stream. Since I made the Stream
a function, there was no way for me to call that function to reload. Instead, I moved the code to a static void
that I can call from any page, created a StreamController
, and used controller.add(data)
at the end of the void
which allows me to update the stream with the new data.