I have a social media app which like any other social media apps shows a list of posts which is vertically scrollable. So the problem is that it the functionality isn’t working as expected and I don’t know what’s wrong. I’m sure there are posts available to show but the problem might be the UI. This is what I have done to fetch posts from Firebase:
final postsProvider = FutureProvider.family<List<Post>, model.User>(
(ref, model.User user) async {
final friends = user.following;
final allPosts = await Future.wait(
friends.map((frId) async {
final asyncValue = ref.read(postPoolProvider(frId));
return asyncValue.maybeWhen(
data: (posts) => posts,
orElse: () => <Post>[],
);
}),
);
return allPosts.expand((posts) => posts).toList();
});
So basically here I’m creating a FutureProvider
which takes in two arguments, ref
and model.User user
and returns a list of posts. The postPoolProvider
is used to get a list of all the post of the user who’s userId corresponds to frId
. Here’s how I used this in building the UI:
Widget _buildPosts(BuildContext context, model.User user) {
final hiddenPosts = ref.watch(hiddenPostsProvider);
final postAsyncValue = ref.watch(postsProvider(user));
return postAsyncValue.when(
data: (data) {
final visiblePosts =
data.where((post) => !hiddenPosts.contains(post.id)).toList();
return CustomScrollView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index != 0 && index % 4 == 0) {
final height = MediaQuery.of(context).size.height * 0.6;
return _ad != null
? SizedBox(height: height, child: AdWidget(ad: _ad!))
: const SizedBox.shrink();
} else {
final postIndex = index - (index ~/ 4);
if (postIndex < visiblePosts.length) {
return PostCard(
id: visiblePosts[postIndex].userId,
user: user,
index: postIndex);
} else {
return const SizedBox.shrink();
}
}
},
childCount: visiblePosts.length + (visiblePosts.length ~/ 3),
),
),
],
);
},
error: (error, stack) => _buildErrorScreen(error),
loading: () => _buildLoadingScreen(),
);
}
I called the above widget as a body of a NestedScrollView
. I’m 100% certain there are posts in my database so the problem is either with home I’m fetching them or building the UI. Any help will be appreciated. Thanks
I think you should use a listener provider, something like this:
import 'package:flutter_riverpod/flutter_riverpod.dart';
part '....';
@riverpod
class Posts extends _$Posts {
@override
Future<List<Post>> build(model.User user) => fetch();
Future<List<Post>> fetch() async {
state = const AsyncValue.loading();
final friends = user.following; //note that user is visible everywhere in the class
final allPosts = List.empty(growable: true);
for (final frId in friends) {
final posts = await ref.read(postPoolProvider(frId).future);
allPosts.add(posts);
}
state = await AsyncValue.guard(() => allPosts);
return allPosts;
}
}
then in your Widget , where you wrote:
final postAsyncValue = ref.watch(postsProvider(user));
you should use these lines of code :
@override
Widget build(BuildContext context, WidgetRef ref) {
final postAsyncValue = ref.watch(postsProvider(user));
return switch (postAsyncValue ) {
AsyncError<List<Post>>(:final error) => _buildErrorScreen(error),
),
AsyncData<List<Post>>(:final value) => _buildWithData(context, ref, value),
_ => _buildLoadingScreen(),
};
}
in the _buildWithData(…) you should put your _buildPosts(…) code, but simpler, look that the logic loading-error-load_data is already written in this build() method.