From 0b2a1fee6f29c58cb2443aeff13b2b85464890e8 Mon Sep 17 00:00:00 2001 From: Hypertext-Assassin-RSS Date: Thu, 24 Oct 2024 12:11:40 +0530 Subject: [PATCH 1/2] details screen update --- lib/src/widgets/details.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/src/widgets/details.dart b/lib/src/widgets/details.dart index dd42555..3ef362f 100644 --- a/lib/src/widgets/details.dart +++ b/lib/src/widgets/details.dart @@ -49,6 +49,7 @@ class _DetailsScreenState extends State { @override void initState() { + debugPrint(widget.rating); fetchBookmarks(); checkLibrary(); super.initState(); @@ -152,10 +153,10 @@ class _DetailsScreenState extends State { // Function to open the URL in the browser Future _openUrlInBrowser() async { String title = widget.title.replaceAll(' ', '-'); - final Uri url = Uri.parse('https://samanaladanuma.lk/product/'+ title); - if (!await launchUrl(url)) { - throw Exception('Could not launch $url'); - } + final Uri url = Uri.parse('https://samanaladanuma.lk/product/'+ title); + if (!await launchUrl(url)) { + throw Exception('Could not launch $url'); + } } From ae7611d2181f199fcfb9c16ce0c4c3ae52822b90 Mon Sep 17 00:00:00 2001 From: Hypertext-Assassin-RSS Date: Mon, 28 Oct 2024 13:49:34 +0530 Subject: [PATCH 2/2] bookmark screen --- assets/icons/bookmark-solid.svg | 1 + lib/src/screens/bookmark.dart | 193 ++++++++++++++++++++++++++++++++ lib/src/screens/library.dart | 185 ++++++++++++++---------------- lib/src/widgets/bottomnav.dart | 14 ++- lib/src/widgets/details.dart | 1 - 5 files changed, 294 insertions(+), 100 deletions(-) create mode 100644 assets/icons/bookmark-solid.svg create mode 100644 lib/src/screens/bookmark.dart diff --git a/assets/icons/bookmark-solid.svg b/assets/icons/bookmark-solid.svg new file mode 100644 index 0000000..45f078c --- /dev/null +++ b/assets/icons/bookmark-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/src/screens/bookmark.dart b/lib/src/screens/bookmark.dart new file mode 100644 index 0000000..7ba9c3f --- /dev/null +++ b/lib/src/screens/bookmark.dart @@ -0,0 +1,193 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:mindful_reader/src/screens/itemcards.dart'; +import 'package:mindful_reader/src/widgets/category.dart'; +import 'package:mindful_reader/src/widgets/details.dart'; +import 'package:mindful_reader/src/widgets/splashScreen.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class Bookmark extends StatefulWidget { + const Bookmark({super.key}); + + @override + State createState() => _BookmarkState(); +} + +class _BookmarkState extends State { + List books = []; + List filteredBooks = []; + bool isLoading = true; + bool _isSearchVisible = false; + final TextEditingController _searchController = TextEditingController(); + final FocusNode _searchFocusNode = FocusNode(); + + @override + void initState() { + super.initState(); + if (books.isEmpty) { + fetchBooks(); + } + _searchController.addListener(_filterBooks); + } + + + Future fetchBooks() async { + debugPrint('Getting Bookmarks'); + await dotenv.load(fileName: "assets/config/.env"); + final prefs = await SharedPreferences.getInstance(); + var username = prefs.getString('username') ?? ''; + try { + final response = await Dio().get('${dotenv.env['API_BASE_URL']}/books/allbookmarks', + data: { + 'username': username, + }, + ); + + + if (response.statusCode == 200) { + if (mounted) { + setState(() { + books = response.data; + filteredBooks = books; + isLoading = false; + }); + } + } else { + throw Exception('Failed to load books'); + } + } catch (e) { + if (mounted) { + setState(() { + isLoading = false; + }); + } + if (kDebugMode) { + print('Error fetching books: $e'); + } + } + } + + void _filterBooks() { + setState(() { + filteredBooks = books + .where((book) => + book['title'].toLowerCase().contains(_searchController.text.toLowerCase())) + .toList(); + if (kDebugMode) { + print('Filtered Books: ${filteredBooks.length}'); + } + }); + } + + void _toggleSearchBar() { + setState(() { + _isSearchVisible = !_isSearchVisible; + if (_isSearchVisible) { + _searchFocusNode.requestFocus(); + } else { + _searchFocusNode.unfocus(); + _searchController.clear(); + filteredBooks = books; + } + }); + } + + @override + void dispose() { + _searchController.removeListener(_filterBooks); + _searchController.dispose(); + _searchFocusNode.dispose(); + super.dispose(); + } + + + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: _isSearchVisible + ? CupertinoSearchTextField( + controller: _searchController, + focusNode: _searchFocusNode, + placeholder: 'Search', + style: const TextStyle(color: Colors.black), + ) + : const Text('Search', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.normal, + color: Colors.black, + ), + ), + actions: [ + IconButton( + icon: Icon(_isSearchVisible ? Icons.close : Icons.search), + tooltip: 'Search', + onPressed: _toggleSearchBar, + ), + ], + ), + body: isLoading + ? const Center(child: CircularProgressIndicator()) + : Column( + children: [ + const SizedBox(height: 5), + const CategoryCard(), + const SizedBox(height: 5), + Expanded( + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10.0, + mainAxisSpacing: 10.0, + childAspectRatio: 0.75, + ), + itemCount: filteredBooks.length, + itemBuilder: (context, index) { + final book = filteredBooks[index]; + return Center( + child: InkWell( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SplashScreen( + imageUrl: book['cover_url'] ?? 'assets/images/imgae_not.jpg', + nextScreen: DetailsScreen( + imageUrl: book['cover_url'] ?? 'assets/images/imgae_not.jpg', + title: book['title'] ?? 'Unknown Title', + author: book['author'] ?? 'Unknown Author', + description: book['description'] ?? 'No description available.', + bookUrl: book['pdf_url'], + isBookmarked: book['bookmarked'] ?? false, + id: book['_id'], + size: book['size'] ?? '00', + pages: book['pages'] ?? '01', + price: book['price'] ?? '00', + rating: book['rating'] ?? '5.00', + ), + ), + ), + ); + }, + child: ItemCards( + imagepic: book['cover_url'] ?? 'assets/images/imgae_not.jpg', + text1: book['title'] ?? 'Unknown Title', + text2: book['author'] ?? 'Unknown Author', + ), + ), + ); + }, + ), + ), + const SizedBox(height: 10), + const SizedBox(height: 15), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/screens/library.dart b/lib/src/screens/library.dart index b5153e2..9bdfef7 100644 --- a/lib/src/screens/library.dart +++ b/lib/src/screens/library.dart @@ -1,6 +1,8 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:mindful_reader/src/screens/itemcards.dart'; import 'package:mindful_reader/src/widgets/details.dart'; import 'package:mindful_reader/src/widgets/splashScreen.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -15,6 +17,9 @@ class Library extends StatefulWidget { class _LibraryState extends State { late String username; late String userId; + bool _isSearchVisible = false; + final TextEditingController _searchController = TextEditingController(); + final FocusNode _searchFocusNode = FocusNode(); List books = []; bool isLoading = true; @@ -25,6 +30,19 @@ class _LibraryState extends State { fetchLibraryBooks(); } + void _toggleSearchBar() { + setState(() { + _isSearchVisible = !_isSearchVisible; + if (_isSearchVisible) { + _searchFocusNode.requestFocus(); + } else { + _searchFocusNode.unfocus(); + _searchController.clear(); + books = books; + } + }); + } + Future fetchLibraryBooks() async { try { final prefs = await SharedPreferences.getInstance(); @@ -74,113 +92,84 @@ class _LibraryState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Library'), - backgroundColor: Colors.transparent, - elevation: 0, + title: _isSearchVisible + ? CupertinoSearchTextField( + controller: _searchController, + focusNode: _searchFocusNode, + placeholder: 'Search', + style: const TextStyle(color: Colors.black), + ) + : const Text('Search', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.normal, + color: Colors.black, + ), + ), actions: [ IconButton( - icon: Icon(Icons.search, color: Colors.black54), + icon: Icon(_isSearchVisible ? Icons.close : Icons.search), tooltip: 'Search', - onPressed: () { - // search functionality - }, + onPressed: _toggleSearchBar, ), ], ), body: isLoading - ? const Center(child: CircularProgressIndicator()) - : books.isEmpty - ? const Center(child: Text('No books found', style: TextStyle(fontSize: 18, color: Colors.black54))) - : Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 10, - mainAxisSpacing: 10, - childAspectRatio: 0.7, - ), - itemCount: books.length, - itemBuilder: (context, index) { - final book = books[index]; - return GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SplashScreen( - imageUrl: book['cover_url'] ?? 'assets/images/imgae_not.jpg', - nextScreen: DetailsScreen( - imageUrl: book['cover_url'] ?? 'assets/images/imgae_not.jpg', - title: book['title'] ?? 'Unknown Title', - author: book['author'] ?? 'Unknown Author', - description: book['description'] ?? 'No description available.', - bookUrl: book['pdf_url'], - isBookmarked: book['bookmarked'] ?? false, - id: book['_id'], - size: book['size'] ?? '00', - pages: book['pages'] ?? '01', - price: book['price'] ?? '0.00', - rating: book['rating'] ?? 5, - ), - ), - ), - ); - }, - child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 5, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(12), - topRight: Radius.circular(12), - ), - child: Image.network( - book['cover_url'], - width: double.infinity, - height: 160, - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return const Icon(Icons.broken_image, size: 100, color: Colors.grey); - }, - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - book['title'] ?? 'Unknown Title', - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - book['author'] ?? 'Unknown Author', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - color: Colors.black54, - fontSize: 14, - ), - ), + ? const Center(child: CircularProgressIndicator()) + : Column( + children: [ + const SizedBox(height: 5), + const SizedBox(height: 5), + Expanded( + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10.0, + mainAxisSpacing: 10.0, + childAspectRatio: 0.75, + ), + itemCount: books.length, + itemBuilder: (context, index) { + final book = books[index]; + return Center( + child: InkWell( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SplashScreen( + imageUrl: book['cover_url'] ?? 'assets/images/imgae_not.jpg', + nextScreen: DetailsScreen( + imageUrl: book['cover_url'] ?? 'assets/images/imgae_not.jpg', + title: book['title'] ?? 'Unknown Title', + author: book['author'] ?? 'Unknown Author', + description: book['description'] ?? 'No description available.', + bookUrl: book['pdf_url'], + isBookmarked: book['bookmarked'] ?? false, + id: book['_id'], + size: book['size'] ?? '00', + pages: book['pages'] ?? '01', + price: book['price'] ?? '00', + rating: book['rating'] ?? '5.00', ), - ], + ), ), - ), - ); - }, - ), - ), + ); + }, + child: ItemCards( + imagepic: book['cover_url'] ?? 'assets/images/imgae_not.jpg', + text1: book['title'] ?? 'Unknown Title', + text2: book['author'] ?? 'Unknown Author', + ), + ), + ); + }, + ), + ), + const SizedBox(height: 10), + const SizedBox(height: 15), + ], + ), ); } } diff --git a/lib/src/widgets/bottomnav.dart b/lib/src/widgets/bottomnav.dart index 2b7ae87..9cdf5f2 100644 --- a/lib/src/widgets/bottomnav.dart +++ b/lib/src/widgets/bottomnav.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:mindful_reader/src/screens/bookmark.dart'; import '../colors/color.dart'; import '../screens/homepage.dart'; import '../screens/profile.dart'; @@ -20,7 +21,9 @@ class _BottomNavBarState extends State { const HomePage(), const ExploreScreen(), const Library(), + const Bookmark(), const Profile(), + ]; @override @@ -57,6 +60,15 @@ class _BottomNavBarState extends State { ), label: 'Library', ), + BottomNavigationBarItem( + icon: SvgPicture.asset( + 'assets/icons/bookmark-solid.svg', + color: KFourthColor, + width: 16, + height: 19, + ), + label: 'Bookmarks', + ), BottomNavigationBarItem( icon: SvgPicture.asset( 'assets/icons/user-solid.svg', @@ -65,7 +77,7 @@ class _BottomNavBarState extends State { height: 19, ), label: 'Profile', - ), + ) ]), ); } diff --git a/lib/src/widgets/details.dart b/lib/src/widgets/details.dart index 3ef362f..52bc125 100644 --- a/lib/src/widgets/details.dart +++ b/lib/src/widgets/details.dart @@ -49,7 +49,6 @@ class _DetailsScreenState extends State { @override void initState() { - debugPrint(widget.rating); fetchBookmarks(); checkLibrary(); super.initState();