Skip to content

Commit 9398acb

Browse files
Merge pull request #19 from Hypertext-Assassin-RSS/dev
Dev
2 parents f6bdb42 + 9a31331 commit 9398acb

File tree

3 files changed

+183
-29
lines changed

3 files changed

+183
-29
lines changed

lib/src/screens/explore.dart

Lines changed: 166 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,198 @@
1+
import 'package:dio/dio.dart';
2+
import 'package:flutter/foundation.dart';
13
import 'package:flutter/material.dart';
2-
import 'package:http/http.dart' as http;
3-
import 'dart:convert';
4-
import 'details.dart'; // Import the DetailsScreen
4+
import 'package:flutter_dotenv/flutter_dotenv.dart';
5+
6+
import 'category.dart';
7+
import 'itemcards.dart';
8+
import 'trends.dart';
9+
import 'details.dart';
510

611
class ExploreScreen extends StatefulWidget {
712
const ExploreScreen({super.key});
813

914
@override
10-
_ExploreScreenState createState() => _ExploreScreenState();
15+
State<ExploreScreen> createState() => _ExploreScreenState();
1116
}
1217

1318
class _ExploreScreenState extends State<ExploreScreen> {
1419
List books = [];
20+
List filteredBooks = [];
21+
bool isLoading = true;
22+
bool _isSearchVisible = false;
23+
final TextEditingController _searchController = TextEditingController();
24+
final FocusNode _searchFocusNode = FocusNode();
1525

1626
@override
1727
void initState() {
1828
super.initState();
29+
fetchBooks();
30+
_searchController.addListener(_filterBooks);
1931
}
2032

33+
Future<void> fetchBooks() async {
34+
await dotenv.load(fileName: "assets/config/.env");
35+
try {
36+
final response = await Dio().get('${dotenv.env['API_BASE_URL']}/books');
37+
if (response.statusCode == 200) {
38+
if (mounted) {
39+
setState(() {
40+
books = response.data;
41+
filteredBooks = books;
42+
isLoading = false;
43+
});
44+
}
45+
} else {
46+
throw Exception('Failed to load books');
47+
}
48+
} catch (e) {
49+
if (mounted) {
50+
setState(() {
51+
isLoading = false;
52+
});
53+
}
54+
if (kDebugMode) {
55+
print('Error fetching books: $e');
56+
}
57+
}
58+
}
2159

60+
void _filterBooks() {
61+
setState(() {
62+
filteredBooks = books
63+
.where((book) =>
64+
book['title'].toLowerCase().contains(_searchController.text.toLowerCase()))
65+
.toList();
66+
if (kDebugMode) {
67+
print('Filtered Books: ${filteredBooks.length}');
68+
}
69+
});
70+
}
71+
72+
void _toggleSearchBar() {
73+
setState(() {
74+
_isSearchVisible = !_isSearchVisible;
75+
if (_isSearchVisible) {
76+
_searchFocusNode.requestFocus();
77+
} else {
78+
_searchFocusNode.unfocus();
79+
_searchController.clear();
80+
filteredBooks = books;
81+
}
82+
});
83+
}
84+
85+
@override
86+
void dispose() {
87+
_searchController.removeListener(_filterBooks);
88+
_searchController.dispose();
89+
_searchFocusNode.dispose();
90+
super.dispose();
91+
}
2292

2393
@override
2494
Widget build(BuildContext context) {
2595
// Scaffold is a layout for
2696
// the major Material Components.
2797
return Scaffold(
2898
appBar: AppBar(
29-
title: const Text('Example title'),
30-
actions: const [
99+
title: _isSearchVisible
100+
? TextField(
101+
controller: _searchController,
102+
focusNode: _searchFocusNode,
103+
decoration: const InputDecoration(
104+
hintText: 'Search...',
105+
border: InputBorder.none,
106+
),
107+
style: const TextStyle(color: Colors.black),
108+
)
109+
: const Text('Search',
110+
style: TextStyle(
111+
fontSize: 22,
112+
fontWeight: FontWeight.w800,
113+
color: Colors.black,
114+
),
115+
),
116+
actions: [
31117
IconButton(
32-
icon: Icon(Icons.search),
118+
icon: Icon(_isSearchVisible ? Icons.close : Icons.search),
33119
tooltip: 'Search',
34-
onPressed: null,
120+
onPressed: _toggleSearchBar,
35121
),
36122
],
37123
),
38-
// body is the majority of the screen.
39-
body: const Center(
40-
child: Text('Hello, world!'),
41-
),
124+
body: isLoading
125+
? const Center(child: CircularProgressIndicator())
126+
: SingleChildScrollView(
127+
scrollDirection: Axis.vertical,
128+
child: Column(
129+
children: [
130+
const SizedBox(height: 5),
131+
const CategoryCard(),
132+
const SizedBox(height: 5),
133+
SingleChildScrollView(
134+
scrollDirection: Axis.horizontal,
135+
child: Row(
136+
children: filteredBooks.map((book) {
137+
return InkWell(
138+
onTap: () {
139+
Navigator.push(
140+
context,
141+
MaterialPageRoute(
142+
builder: (context) => DetailsScreen(
143+
imageUrl: book['cover_url'] ?? 'assets/images/imgae_not.jpg',
144+
title: book['title'] ?? 'Unknown Title',
145+
author: book['author'] ?? 'Unknown Author',
146+
description: book['description'] ?? 'No description available.',
147+
bookUrl: book['pdf_url'],
148+
),
149+
),
150+
);
151+
},
152+
child: ItemCards(
153+
imagepic: book['cover_url'] ?? 'assets/images/imgae_not.jpg',
154+
text1: book['title'] ?? 'Unknown Title',
155+
text2: book['author'] ?? 'Unknown Author',
156+
),
157+
);
158+
}).toList(),
159+
),
160+
),
161+
const SizedBox(height: 10),
162+
const Trends(),
163+
const SizedBox(height: 15),
164+
SingleChildScrollView(
165+
scrollDirection: Axis.horizontal,
166+
child: Row(
167+
children: books.map((book) {
168+
return InkWell(
169+
onTap: () {
170+
Navigator.push(
171+
context,
172+
MaterialPageRoute(
173+
builder: (context) => DetailsScreen(
174+
imageUrl: book['cover_url'] ?? 'assets/images/imgae_not.jpg',
175+
title: book['title'] ?? 'Unknown Title',
176+
author: book['author'] ?? 'Unknown Author',
177+
description: book['description'] ?? 'No description available.',
178+
bookUrl: book['pdf_url'],
179+
),
180+
),
181+
);
182+
},
183+
child: ItemCards(
184+
imagepic: book['cover_url'] ?? 'assets/images/imgae_not.jpg',
185+
text1: book['title'] ?? 'Unknown Title',
186+
text2: book['author'] ?? 'Unknown Author',
187+
),
188+
);
189+
}).toList(),
190+
),
191+
),
192+
const SizedBox(height: 20),
193+
],
194+
),
195+
),
42196
);
43197
}
44198
}

lib/src/screens/homepage.dart

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,29 @@ class _HomePageState extends State<HomePage> {
2727
fetchBooks();
2828
}
2929

30-
Future<void> fetchBooks() async {
31-
await dotenv.load(fileName: "assets/config/.env");
32-
try {
33-
final response = await Dio().get('${dotenv.env['API_BASE_URL']}/books');
34-
if (response.statusCode == 200) {
30+
Future<void> fetchBooks() async {
31+
await dotenv.load(fileName: "assets/config/.env");
32+
try {
33+
final response = await Dio().get('${dotenv.env['API_BASE_URL']}/books');
34+
if (response.statusCode == 200) {
35+
if (mounted) {
36+
setState(() {
37+
books = response.data;
38+
isLoading = false;
39+
});
40+
}
41+
} else {
42+
throw Exception('Failed to load books');
43+
}
44+
} catch (e) {
3545
if (mounted) {
3646
setState(() {
37-
books = response.data;
3847
isLoading = false;
3948
});
4049
}
41-
} else {
42-
throw Exception('Failed to load books');
43-
}
44-
} catch (e) {
45-
if (mounted) {
46-
setState(() {
47-
isLoading = false;
48-
});
50+
print('Error fetching books: $e');
4951
}
50-
print('Error fetching books: $e');
5152
}
52-
}
5353

5454

5555

lib/src/widgets/bottomnav.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,4 @@ class _BottomNavBarState extends State<BottomNavBar> {
5656
]),
5757
);
5858
}
59-
}
59+
}

0 commit comments

Comments
 (0)