+2
-1
lib/providers/multi_feed_provider.dart
+2
-1
lib/providers/multi_feed_provider.dart
···
269
newPosts = [...currentState.posts, ...response.feed];
270
}
271
272
+
final hasMore = response.cursor != null;
273
_feedStates[type] = currentState.copyWith(
274
posts: newPosts,
275
cursor: response.cursor,
276
+
hasMore: hasMore,
277
error: null,
278
isLoading: false,
279
isLoadingMore: false,
+1
lib/screens/home/feed_screen.dart
+1
lib/screens/home/feed_screen.dart
+33
-3
lib/widgets/feed_page.dart
+33
-3
lib/widgets/feed_page.dart
···
25
required this.posts,
26
required this.isLoading,
27
required this.isLoadingMore,
28
required this.error,
29
required this.scrollController,
30
required this.onRefresh,
···
39
final List<FeedViewPost> posts;
40
final bool isLoading;
41
final bool isLoadingMore;
42
final String? error;
43
final ScrollController scrollController;
44
final Future<void> Function() onRefresh;
···
55
with AutomaticKeepAliveClientMixin {
56
@override
57
bool get wantKeepAlive => true;
58
59
@override
60
Widget build(BuildContext context) {
···
185
cacheExtent: 800,
186
// Add top padding so content isn't hidden behind transparent header
187
padding: const EdgeInsets.only(top: 44),
188
-
// Add extra item for loading indicator or pagination error
189
itemCount:
190
widget.posts.length +
191
-
(widget.isLoadingMore || widget.error != null ? 1 : 0),
192
itemBuilder: (context, index) {
193
-
// Footer: loading indicator or error message
194
if (index == widget.posts.length) {
195
// Show loading indicator for pagination
196
if (widget.isLoadingMore) {
···
234
foregroundColor: AppColors.primary,
235
),
236
child: const Text('Retry'),
237
),
238
],
239
),
···
25
required this.posts,
26
required this.isLoading,
27
required this.isLoadingMore,
28
+
required this.hasMore,
29
required this.error,
30
required this.scrollController,
31
required this.onRefresh,
···
40
final List<FeedViewPost> posts;
41
final bool isLoading;
42
final bool isLoadingMore;
43
+
final bool hasMore;
44
final String? error;
45
final ScrollController scrollController;
46
final Future<void> Function() onRefresh;
···
57
with AutomaticKeepAliveClientMixin {
58
@override
59
bool get wantKeepAlive => true;
60
+
61
+
/// Whether to show a footer item (loading, error, or end of feed)
62
+
bool get _shouldShowFooter =>
63
+
widget.isLoadingMore || widget.error != null || !widget.hasMore;
64
65
@override
66
Widget build(BuildContext context) {
···
191
cacheExtent: 800,
192
// Add top padding so content isn't hidden behind transparent header
193
padding: const EdgeInsets.only(top: 44),
194
+
// Add extra item for loading indicator, pagination error, or end of feed
195
itemCount:
196
widget.posts.length +
197
+
(_shouldShowFooter ? 1 : 0),
198
itemBuilder: (context, index) {
199
+
// Footer: loading indicator, error message, or end of feed
200
if (index == widget.posts.length) {
201
// Show loading indicator for pagination
202
if (widget.isLoadingMore) {
···
240
foregroundColor: AppColors.primary,
241
),
242
child: const Text('Retry'),
243
+
),
244
+
],
245
+
),
246
+
);
247
+
}
248
+
// Show end of feed message when no more posts available
249
+
if (!widget.hasMore) {
250
+
return const Padding(
251
+
padding: EdgeInsets.symmetric(vertical: 32, horizontal: 16),
252
+
child: Column(
253
+
children: [
254
+
Icon(
255
+
Icons.check_circle_outline,
256
+
color: AppColors.textSecondary,
257
+
size: 32,
258
+
),
259
+
SizedBox(height: 8),
260
+
Text(
261
+
"You're all caught up!",
262
+
style: TextStyle(
263
+
color: AppColors.textSecondary,
264
+
fontSize: 14,
265
+
fontWeight: FontWeight.w500,
266
+
),
267
),
268
],
269
),