Skip to content

Commit 5861fab

Browse files
committed
Updated MyNewsScreen with improved UI and error handling
1 parent 19f18c4 commit 5861fab

File tree

6 files changed

+140
-66
lines changed

6 files changed

+140
-66
lines changed

android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2+
<uses-permission android:name="android.permission.INTERNET"/>
23
<application
34
android:label="technew"
45
android:name="${applicationName}"

lib/screens/ai_news_screen.dart

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,16 @@ class _MyNewsScreenState extends State<MyNewsScreen> {
7474
} else if (snapshot.hasError) {
7575
return Center(child: Text("Error loading AI news"));
7676
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
77-
return Center(child: Text("No AI news available"));
77+
return RefreshIndicator(
78+
onRefresh: () async {
79+
fetchMyNews();
80+
},
81+
child: ListView(
82+
children: [
83+
Center(child: Text("No AI news available")),
84+
],
85+
),
86+
);
7887
}
7988

8089
return ListView.builder(
@@ -107,8 +116,7 @@ class _MyNewsScreenState extends State<MyNewsScreen> {
107116
style: TextStyle(color: Colors.grey),
108117
),
109118
SizedBox(height: 5),
110-
_buildClickableUrl(
111-
news['url']), // **Now properly clickable**
119+
_buildClickableUrl(news['url']),
112120
],
113121
),
114122
),

lib/screens/news_detail_screen.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,22 @@ class _NewsDetailScreenState extends State<NewsDetailScreen>
5858

5959
void toggleBookmark() async {
6060
SharedPreferences prefs = await SharedPreferences.getInstance();
61-
List<String> savedNewsList = prefs.getStringList('bookmarkedNews') ?? [];
61+
String? savedBookmarks = prefs.getString('bookmarked_news');
62+
List<dynamic> savedNewsList =
63+
savedBookmarks != null ? jsonDecode(savedBookmarks) : [];
6264

6365
String newsJson = jsonEncode(widget.news);
6466

6567
setState(() {
6668
if (isBookmarked) {
67-
savedNewsList.remove(newsJson);
69+
savedNewsList.removeWhere((item) => jsonEncode(item) == newsJson);
6870
} else {
69-
savedNewsList.add(newsJson);
71+
savedNewsList.add(widget.news);
7072
}
7173
isBookmarked = !isBookmarked;
7274
});
7375

74-
await prefs.setStringList('bookmarkedNews', savedNewsList);
76+
await prefs.setString('bookmarked_news', jsonEncode(savedNewsList));
7577
print("Updated bookmarks: $savedNewsList"); // Debugging print
7678
}
7779

lib/screens/news_screen.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,19 @@ class _NewsTabState extends State<NewsTab> {
286286
if (snapshot.connectionState == ConnectionState.waiting) {
287287
return Center(child: CircularProgressIndicator());
288288
} else if (snapshot.hasError) {
289+
print("Error: ${snapshot.error}");
289290
return Center(child: Text("Error loading news"));
290291
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
291-
return Center(child: Text("No news available"));
292+
return RefreshIndicator(
293+
onRefresh: () async {
294+
fetchNews();
295+
},
296+
child: ListView(
297+
children: [
298+
Center(child: Text("No news available")),
299+
],
300+
),
301+
);
292302
}
293303

294304
return ListView.builder(

lib/screens/saved_news_screen.dart

Lines changed: 109 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
22
import 'package:shared_preferences/shared_preferences.dart';
33
import 'dart:convert';
44
import 'news_detail_screen.dart';
5+
import 'package:timeago/timeago.dart' as timeago;
56

67
class 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
}

lib/services/news_service.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import 'dart:convert';
22
import 'package:http/http.dart' as http;
33

44
class NewsService {
5-
static const String apiUrl = "Your api route here";
6-
static const String aiNewsApiUrl = "Your api route here";
5+
static const String apiUrl = "YOUR-API-KEY-HERE";
6+
static const String aiNewsApiUrl = "YOUR-API-KEY-HERE";
77

88
// Fetch Tech News
99
Future<List<dynamic>> fetchNews() async {

0 commit comments

Comments
 (0)