+2
-1
lib/providers/multi_feed_provider.dart
+2
-1
lib/providers/multi_feed_provider.dart
···
269
269
newPosts = [...currentState.posts, ...response.feed];
270
270
}
271
271
272
+
final hasMore = response.cursor != null;
272
273
_feedStates[type] = currentState.copyWith(
273
274
posts: newPosts,
274
275
cursor: response.cursor,
275
-
hasMore: response.cursor != null,
276
+
hasMore: hasMore,
276
277
error: null,
277
278
isLoading: false,
278
279
isLoadingMore: false,
+1
lib/screens/home/feed_screen.dart
+1
lib/screens/home/feed_screen.dart
···
376
376
posts: state.posts,
377
377
isLoading: state.isLoading,
378
378
isLoadingMore: state.isLoadingMore,
379
+
hasMore: state.hasMore,
379
380
error: hasError ? error : null,
380
381
scrollController: _getOrCreateScrollController(feedType),
381
382
onRefresh: () => provider.loadFeed(feedType, refresh: true),
+33
-3
lib/widgets/feed_page.dart
+33
-3
lib/widgets/feed_page.dart
···
25
25
required this.posts,
26
26
required this.isLoading,
27
27
required this.isLoadingMore,
28
+
required this.hasMore,
28
29
required this.error,
29
30
required this.scrollController,
30
31
required this.onRefresh,
···
39
40
final List<FeedViewPost> posts;
40
41
final bool isLoading;
41
42
final bool isLoadingMore;
43
+
final bool hasMore;
42
44
final String? error;
43
45
final ScrollController scrollController;
44
46
final Future<void> Function() onRefresh;
···
55
57
with AutomaticKeepAliveClientMixin {
56
58
@override
57
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;
58
64
59
65
@override
60
66
Widget build(BuildContext context) {
···
185
191
cacheExtent: 800,
186
192
// Add top padding so content isn't hidden behind transparent header
187
193
padding: const EdgeInsets.only(top: 44),
188
-
// Add extra item for loading indicator or pagination error
194
+
// Add extra item for loading indicator, pagination error, or end of feed
189
195
itemCount:
190
196
widget.posts.length +
191
-
(widget.isLoadingMore || widget.error != null ? 1 : 0),
197
+
(_shouldShowFooter ? 1 : 0),
192
198
itemBuilder: (context, index) {
193
-
// Footer: loading indicator or error message
199
+
// Footer: loading indicator, error message, or end of feed
194
200
if (index == widget.posts.length) {
195
201
// Show loading indicator for pagination
196
202
if (widget.isLoadingMore) {
···
234
240
foregroundColor: AppColors.primary,
235
241
),
236
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
+
),
237
267
),
238
268
],
239
269
),