@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
22import 'package:shared_preferences/shared_preferences.dart' ;
33import 'dart:convert' ;
44import 'news_detail_screen.dart' ;
5+ import 'package:timeago/timeago.dart' as timeago;
56
67class SavedNewsScreen extends StatefulWidget {
78 @override
@@ -14,89 +15,141 @@ class _SavedNewsScreenState extends State<SavedNewsScreen> {
1415 @override
1516 void initState () {
1617 super .initState ();
17- loadSavedNews ();
18+ fetchSavedNews ();
1819 }
1920
20- @override
21- void didChangeDependencies () {
22- super .didChangeDependencies ();
23- loadSavedNews (); // Reload news when the screen is revisited
24- }
25-
26- @override
27- void reassemble () {
28- super .reassemble ();
29- loadSavedNews (); // Refresh when coming back from background
30- }
31-
32- Future <void > loadSavedNews () async {
21+ Future <void > fetchSavedNews () async {
3322 SharedPreferences prefs = await SharedPreferences .getInstance ();
3423 String ? savedBookmarks = prefs.getString ('bookmarked_news' );
35-
3624 if (savedBookmarks != null ) {
3725 setState (() {
38- savedNews = List . from ( jsonDecode (savedBookmarks) );
26+ savedNews = jsonDecode (savedBookmarks);
3927 });
40- print ("Loaded saved news: \$ savedNews" ); // Debugging print
41- } else {
42- print ("No saved news found in storage." );
4328 }
4429 }
4530
46- void removeBookmark (int index) async {
47- SharedPreferences prefs = await SharedPreferences .getInstance ();
48- savedNews.removeAt (index);
49- await prefs.setString ('bookmarked_news' , jsonEncode (savedNews));
50-
51- setState (() {});
52- }
53-
5431 @override
5532 Widget build (BuildContext context) {
5633 return Scaffold (
57- appBar: AppBar (title: Text ("Saved News" )),
5834 body: RefreshIndicator (
59- onRefresh: loadSavedNews ,
35+ onRefresh: fetchSavedNews ,
6036 child: savedNews.isEmpty
61- ? Center (child: Text ("No saved news found" ))
37+ ? ListView (
38+ children: [
39+ Center (child: Text ("No saved news available" )),
40+ ],
41+ )
6242 : ListView .builder (
6343 itemCount: savedNews.length,
6444 itemBuilder: (context, index) {
6545 final news = savedNews[index];
6646
67- return ListTile (
68- onTap: () {
69- Navigator .push (
70- context,
71- MaterialPageRoute (
72- builder: (context) => NewsDetailScreen (news: news),
47+ return Padding (
48+ padding: EdgeInsets .symmetric (horizontal: 16 , vertical: 8 ),
49+ child: Card (
50+ shape: RoundedRectangleBorder (
51+ borderRadius: BorderRadius .circular (16 ),
52+ ),
53+ elevation: 4 ,
54+ child: InkWell (
55+ borderRadius: BorderRadius .circular (16 ),
56+ onTap: () {
57+ Navigator .push (
58+ context,
59+ MaterialPageRoute (
60+ builder: (context) =>
61+ NewsDetailScreen (news: news),
62+ ),
63+ );
64+ },
65+ child: Column (
66+ crossAxisAlignment: CrossAxisAlignment .start,
67+ children: [
68+ ClipRRect (
69+ borderRadius: BorderRadius .vertical (
70+ top: Radius .circular (16 )),
71+ child: Image .network (
72+ news['imageUrl' ] ?? '' ,
73+ height: 180 ,
74+ width: double .infinity,
75+ fit: BoxFit .cover,
76+ errorBuilder: (context, error, stackTrace) =>
77+ Container (
78+ height: 180 ,
79+ color: Colors .grey[200 ],
80+ child: Icon (Icons .image_not_supported,
81+ size: 50 , color: Colors .grey),
82+ ),
83+ ),
84+ ),
85+ Padding (
86+ padding: EdgeInsets .all (16 ),
87+ child: Column (
88+ crossAxisAlignment: CrossAxisAlignment .start,
89+ children: [
90+ Text (
91+ news['title' ],
92+ style: TextStyle (
93+ fontSize: 18 ,
94+ fontWeight: FontWeight .bold,
95+ ),
96+ maxLines: 2 ,
97+ overflow: TextOverflow .ellipsis,
98+ ),
99+ SizedBox (height: 8 ),
100+ Text (
101+ news['description' ] ??
102+ 'No description available' ,
103+ maxLines: 2 ,
104+ overflow: TextOverflow .ellipsis,
105+ style: TextStyle (
106+ color: Colors .grey[600 ],
107+ fontSize: 14 ,
108+ ),
109+ ),
110+ SizedBox (height: 12 ),
111+ Row (
112+ children: [
113+ Text (
114+ news['source' ] ?? 'Unknown' ,
115+ style: TextStyle (
116+ color: Colors .blue,
117+ fontWeight: FontWeight .w500,
118+ fontSize: 12 ,
119+ ),
120+ ),
121+ Spacer (),
122+ Icon (Icons .access_time,
123+ size: 16 , color: Colors .grey),
124+ SizedBox (width: 4 ),
125+ Text (
126+ getTimeAgo (news['createdAt' ]),
127+ style: TextStyle (
128+ color: Colors .grey, fontSize: 12 ),
129+ ),
130+ ],
131+ ),
132+ ],
133+ ),
134+ ),
135+ ],
73136 ),
74- ).then ((_) => loadSavedNews ()); // Refresh after returning
75- },
76- leading: ClipRRect (
77- borderRadius: BorderRadius .circular (8.0 ),
78- child: Image .network (
79- news['imageUrl' ] ?? '' ,
80- width: 70 ,
81- height: 70 ,
82- fit: BoxFit .cover,
83- errorBuilder: (context, error, stackTrace) =>
84- Icon (Icons .image_not_supported, size: 70 ),
85137 ),
86138 ),
87- title: Text (news['title' ],
88- style: TextStyle (
89- fontWeight: FontWeight .bold, fontSize: 16 )),
90- subtitle: Text (news['category' ] ?? "General" ,
91- style: TextStyle (color: Colors .grey)),
92- trailing: IconButton (
93- icon: Icon (Icons .delete, color: Colors .red),
94- onPressed: () => removeBookmark (index),
95- ),
96139 );
97140 },
98141 ),
99142 ),
100143 );
101144 }
145+
146+ String getTimeAgo (String ? createdAt) {
147+ if (createdAt == null || createdAt.isEmpty) return "Unknown time" ;
148+ try {
149+ DateTime parsedDate = DateTime .parse (createdAt).toLocal ();
150+ return timeago.format (parsedDate, locale: 'en' );
151+ } catch (e) {
152+ return "Unknown time" ;
153+ }
154+ }
102155}
0 commit comments