···
-
import 'package:cached_network_image/cached_network_image.dart';
-
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../models/post.dart';
import '../../providers/auth_provider.dart';
import '../../providers/feed_provider.dart';
class FeedScreen extends StatefulWidget {
const FeedScreen({super.key});
···
final isLoadingMore = context.select<FeedProvider, bool>(
-
backgroundColor: const Color(0xFF0B0F14),
-
backgroundColor: const Color(0xFF0B0F14),
-
foregroundColor: Colors.white,
title: Text(isAuthenticated ? 'Feed' : 'Explore'),
automaticallyImplyLeading: false,
···
isLoadingMore: isLoadingMore,
isAuthenticated: isAuthenticated,
···
required List<FeedViewPost> posts,
required bool isLoadingMore,
required bool isAuthenticated,
// Loading state (only show full-screen loader for initial load,
if (isLoading && posts.isEmpty) {
-
child: CircularProgressIndicator(color: Color(0xFFFF6B35)),
···
-
color: Color(0xFFFF6B35),
const SizedBox(height: 16),
fontWeight: FontWeight.bold,
const SizedBox(height: 8),
_getUserFriendlyError(error),
-
style: const TextStyle(fontSize: 14, color: Color(0xFFB6C2D2)),
textAlign: TextAlign.center,
const SizedBox(height: 24),
-
Provider.of<FeedProvider>(
style: ElevatedButton.styleFrom(
-
backgroundColor: const Color(0xFFFF6B35),
child: const Text('Retry'),
···
mainAxisAlignment: MainAxisAlignment.center,
-
const Icon(Icons.forum, size: 64, color: Color(0xFFFF6B35)),
const SizedBox(height: 24),
isAuthenticated ? 'No posts yet' : 'No posts to discover',
fontWeight: FontWeight.bold,
···
? 'Subscribe to communities to see posts in your feed'
: 'Check back later for new posts',
-
style: const TextStyle(fontSize: 14, color: Color(0xFFB6C2D2)),
textAlign: TextAlign.center,
···
-
color: const Color(0xFFFF6B35),
controller: _scrollController,
// Add extra item for loading indicator or pagination error
-
posts.length + (isLoadingMore || error != null ? 1 : 0),
itemBuilder: (context, index) {
// Footer: loading indicator or error message
if (index == posts.length) {
···
padding: EdgeInsets.all(16),
-
child: CircularProgressIndicator(color: Color(0xFFFF6B35)),
···
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
-
color: const Color(0xFF1A1F26),
borderRadius: BorderRadius.circular(8),
-
border: Border.all(color: const Color(0xFFFF6B35)),
-
color: Color(0xFFFF6B35),
const SizedBox(height: 8),
_getUserFriendlyError(error),
-
color: Color(0xFFB6C2D2),
textAlign: TextAlign.center,
···
const SizedBox(height: 12),
-
Provider.of<FeedProvider>(
style: TextButton.styleFrom(
-
foregroundColor: const Color(0xFFFF6B35),
child: const Text('Retry'),
···
final post = posts[index];
-
label: 'Feed post in ${post.post.community.name} by '
'${post.post.author.displayName ?? post.post.author.handle}. '
'${post.post.title ?? ""}',
-
child: _PostCard(post: post),
···
return 'Something went wrong. Please try again';
-
class _PostCard extends StatelessWidget {
-
const _PostCard({required this.post});
-
final FeedViewPost post;
-
Widget build(BuildContext context) {
-
margin: const EdgeInsets.only(bottom: 8),
-
decoration: const BoxDecoration(
-
color: Color(0xFF1A1F26),
-
border: Border(bottom: BorderSide(color: Color(0xFF2A2F36))),
-
padding: const EdgeInsets.all(16),
-
crossAxisAlignment: CrossAxisAlignment.start,
-
// Community and author info
-
// Community avatar placeholder
-
decoration: BoxDecoration(
-
color: const Color(0xFFFF6B35),
-
borderRadius: BorderRadius.circular(4),
-
post.post.community.name[0].toUpperCase(),
-
style: const TextStyle(
-
fontWeight: FontWeight.bold,
-
const SizedBox(width: 8),
-
crossAxisAlignment: CrossAxisAlignment.start,
-
'c/${post.post.community.name}',
-
style: const TextStyle(
-
fontWeight: FontWeight.bold,
-
'Posted by ${post.post.author.displayName ?? ''
-
'${post.post.author.handle}'}',
-
style: const TextStyle(
-
color: Color(0xFFB6C2D2),
-
const SizedBox(height: 12),
-
if (post.post.title != null) ...[
-
style: const TextStyle(
-
fontWeight: FontWeight.bold,
-
const SizedBox(height: 12),
-
// Embed (link preview)
-
if (post.post.embed?.external != null) ...[
-
_EmbedCard(embed: post.post.embed!.external!),
-
const SizedBox(height: 12),
-
color: Colors.white.withValues(alpha: 0.6),
-
const SizedBox(width: 4),
-
'${post.post.stats.score}',
-
color: Colors.white.withValues(alpha: 0.6),
-
const SizedBox(width: 16),
-
Icons.comment_outlined,
-
color: Colors.white.withValues(alpha: 0.6),
-
const SizedBox(width: 4),
-
'${post.post.stats.commentCount}',
-
color: Colors.white.withValues(alpha: 0.6),
-
class _EmbedCard extends StatelessWidget {
-
const _EmbedCard({required this.embed});
-
final ExternalEmbed embed;
-
Widget build(BuildContext context) {
-
// Only show image if thumbnail exists
-
if (embed.thumb == null) {
-
return const SizedBox.shrink();
-
decoration: BoxDecoration(
-
borderRadius: BorderRadius.circular(8),
-
border: Border.all(color: const Color(0xFF2A2F36)),
-
clipBehavior: Clip.antiAlias,
-
child: CachedNetworkImage(
-
imageUrl: embed.thumb!,
-
width: double.infinity,
-
placeholder: (context, url) => Container(
-
width: double.infinity,
-
color: const Color(0xFF1A1F26),
-
child: CircularProgressIndicator(color: Color(0xFF484F58)),
-
errorWidget: (context, url, error) {
-
debugPrint('❌ Image load error: $error');
-
debugPrint('URL: $url');
-
width: double.infinity,
-
color: const Color(0xFF1A1F26),
-
color: Color(0xFF484F58),
···
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
+
import '../../constants/app_colors.dart';
import '../../models/post.dart';
import '../../providers/auth_provider.dart';
import '../../providers/feed_provider.dart';
+
import '../../widgets/post_card.dart';
class FeedScreen extends StatefulWidget {
const FeedScreen({super.key});
···
final isLoadingMore = context.select<FeedProvider, bool>(
+
final currentTime = context.select<FeedProvider, DateTime?>(
+
backgroundColor: AppColors.background,
+
backgroundColor: AppColors.background,
+
foregroundColor: AppColors.textPrimary,
title: Text(isAuthenticated ? 'Feed' : 'Explore'),
automaticallyImplyLeading: false,
···
isLoadingMore: isLoadingMore,
isAuthenticated: isAuthenticated,
+
currentTime: currentTime,
···
required List<FeedViewPost> posts,
required bool isLoadingMore,
required bool isAuthenticated,
+
required DateTime? currentTime,
// Loading state (only show full-screen loader for initial load,
if (isLoading && posts.isEmpty) {
+
child: CircularProgressIndicator(color: AppColors.primary),
···
+
color: AppColors.primary,
const SizedBox(height: 16),
+
color: AppColors.textPrimary,
fontWeight: FontWeight.bold,
const SizedBox(height: 8),
_getUserFriendlyError(error),
+
style: const TextStyle(
+
color: AppColors.textSecondary,
textAlign: TextAlign.center,
const SizedBox(height: 24),
+
Provider.of<FeedProvider>(context, listen: false).retry();
style: ElevatedButton.styleFrom(
+
backgroundColor: AppColors.primary,
child: const Text('Retry'),
···
mainAxisAlignment: MainAxisAlignment.center,
+
const Icon(Icons.forum, size: 64, color: AppColors.primary),
const SizedBox(height: 24),
isAuthenticated ? 'No posts yet' : 'No posts to discover',
+
color: AppColors.textPrimary,
fontWeight: FontWeight.bold,
···
? 'Subscribe to communities to see posts in your feed'
: 'Check back later for new posts',
+
style: const TextStyle(
+
color: AppColors.textSecondary,
textAlign: TextAlign.center,
···
+
color: AppColors.primary,
controller: _scrollController,
// Add extra item for loading indicator or pagination error
+
itemCount: posts.length + (isLoadingMore || error != null ? 1 : 0),
itemBuilder: (context, index) {
// Footer: loading indicator or error message
if (index == posts.length) {
···
padding: EdgeInsets.all(16),
+
child: CircularProgressIndicator(color: AppColors.primary),
···
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
+
color: AppColors.background,
borderRadius: BorderRadius.circular(8),
+
border: Border.all(color: AppColors.primary),
+
color: AppColors.primary,
const SizedBox(height: 8),
_getUserFriendlyError(error),
+
color: AppColors.textSecondary,
textAlign: TextAlign.center,
···
const SizedBox(height: 12),
+
Provider.of<FeedProvider>(context, listen: false)
style: TextButton.styleFrom(
+
foregroundColor: AppColors.primary,
child: const Text('Retry'),
···
final post = posts[index];
+
'Feed post in ${post.post.community.name} by '
'${post.post.author.displayName ?? post.post.author.handle}. '
'${post.post.title ?? ""}',
+
child: PostCard(post: post, currentTime: currentTime),
···
return 'Something went wrong. Please try again';