30 Commits

Author SHA1 Message Date
fbe1ee70c9 changed some styles and fixed search layout overflow 2023-08-22 04:00:11 -07:00
c7b1fec73e Merge pull request #13 from PiyushSuthar/main
Shifting to Material You
2023-08-22 03:18:24 -07:00
9cc3c3dde5 implemented resume reading for pdf and epub 2023-08-21 22:52:19 -07:00
72abde09e0 added: autogenerated platform files 2023-08-21 21:56:00 +05:30
3d3097152d fixed: trending page card ui and colors 2023-08-21 20:31:36 +05:30
c5b1fc54d9 fixed & replaced with material you components 2023-08-21 20:31:01 +05:30
d253e52971 fixed search page 2023-08-21 20:30:10 +05:30
0b502629fb fixed: result page and redesigned card 2023-08-21 20:29:43 +05:30
8ec03ba5d7 fixed: alert dialog and button layouts 2023-08-21 20:28:07 +05:30
068a2c68e0 fixed: Loading Indicator 2023-08-21 20:27:04 +05:30
59d467c692 fixed: fontsize and background color 2023-08-21 20:25:23 +05:30
9fffb3829f removed media query 2023-08-21 20:22:40 +05:30
e25a12b684 shifted to material you style widgets 2023-08-21 01:24:24 +05:30
86684c5436 added material dynamic color 2023-08-21 01:23:29 +05:30
e7b6a9d40f added resume reading for pdf viewer 2023-08-18 07:03:23 -07:00
60df4afac2 improved code readability 2023-08-14 23:13:10 -07:00
bcfb35ab14 Fixed a bug with Sqflite_ffi 2023-08-13 22:56:01 -07:00
4ac80f08d8 Merge pull request #6 from bipinkrish/main
View for non mobile platforms
2023-08-13 22:31:14 -07:00
a9fde1c561 view for non mobile platforms 2023-08-13 20:49:19 +05:30
110d089b8d Merge branch 'main' of https://github.com/dstark5/Openlib 2023-08-13 05:04:50 -07:00
4445fc4d84 added wake lock to permissions and commented unused lines 2023-08-13 05:04:41 -07:00
11d19b3001 Update README.md 2023-08-13 05:01:13 -07:00
052c5993b1 Update README.md 2023-08-13 04:34:07 -07:00
bc36a350ab Update README.md 2023-08-13 04:28:54 -07:00
9102b0be7b Add files via upload 2023-08-13 04:24:43 -07:00
365d29c19e Update README.md 2023-08-13 04:23:42 -07:00
52c742e8d0 Update README.md 2023-08-13 04:21:54 -07:00
d2c992bf80 Merge pull request #1 from IzzySoft/fastlane
initial fastlane structure
2023-08-12 06:27:20 -07:00
39a91b9992 initial fastlane structure 2023-08-12 14:32:43 +02:00
19df3b4f7b Update README.md 2023-08-11 04:59:59 -07:00
40 changed files with 791 additions and 625 deletions

View File

@ -3,7 +3,13 @@
#### An Open source app to download and read books from shadow library ([Annas Archive](https://annas-archive.org/)). #### An Open source app to download and read books from shadow library ([Annas Archive](https://annas-archive.org/)).
[![made-with-flutter](https://img.shields.io/badge/Made%20with-Flutter-4361ee.svg)](https://flutter.dev/) [![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-e63946.svg)](https://opensource.org/licenses/) [![made-with-flutter](https://img.shields.io/badge/Made%20with-Flutter-4361ee.svg)](https://flutter.dev/) [![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-e63946.svg)](https://opensource.org/licenses/) ![version](https://img.shields.io/badge/version-1.0_beta-06d6a0)
[<img src="github_releases.png"
alt="Download from GitHub"
height="80">](https://github.com/dstark5/Openlib/releases) [<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png"
alt="Get it on IzzyDroid"
height="80">](https://android.izzysoft.de/repo/apk/com.app.openlib)
## Note ## Note
@ -43,6 +49,7 @@
- Adding Support For Multiple Downloads - Adding Support For Multiple Downloads
## Installation and updates ## Installation and updates
- Download the APK from [ IzzyOnDroid ](https://android.izzysoft.de/repo/apk/com.app.openlib) and install it.
- Download the APK from [GitHub Releases](https://github.com/dstark5/Openlib/releases) and install it. - Download the APK from [GitHub Releases](https://github.com/dstark5/Openlib/releases) and install it.
## Building from Source ## Building from Source
@ -69,7 +76,7 @@ flutter build
- The Build Will Be In './build/app/outputs/flutter-apk/app-release.apk' - The Build Will Be In './build/app/outputs/flutter-apk/app-release.apk'
## Contribution ## Contribution
Whether you have ideas, design changes or even major code changes, help is always welcome. The app gets better and better with each contribution, no matter how big or small! If you'd like to get involved just fork the app make a pull request. Whether you have ideas, design changes or even major code changes, help is always welcome. The app gets better and better with each contribution, no matter how big or small! If you'd like to get involved ,Please read our [CONTRIBUTING.md](https://github.com/dstark5/Openlib/blob/main/CONTRIBUTING.md)
## Issues ## Issues

View File

@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<application <application
android:label="Openlib" android:label="Openlib"
android:name="${applicationName}" android:name="${applicationName}"

View File

@ -0,0 +1 @@
<p><i>Openlib</i> is an open source app to download and read books from shadow library (<a href='https://annas-archive.org/' target='_blank' rel='nofollow noopener'>Annas Archive</a>). The App Has Built In Reader to Read Books.</p><p>As <i>Annas Archive</i> doesn't have an API, the app works by sending requests to <i>Annas Archive</i> and parses the response to objects. The app extracts the mirrors from the responses, downloads the book and stores it in the application's document directory.</p><p>Features include a.o.:</p><ul><li>Trending Books</li><li>Download And Read Books With In-Built Viewer</li><li>Supports Epub And Pdf Formats</li><li>Filter Books</li><li>Sort Books</li></ul>

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1 @@
download and read books from shadow library (Annas Archive)

BIN
github_releases.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,7 +1,10 @@
import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:io' show Platform;
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_nav_bar/google_nav_bar.dart'; import 'package:google_nav_bar/google_nav_bar.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:openlib/ui/extensions.dart'; import 'package:openlib/ui/extensions.dart';
@ -14,55 +17,49 @@ import 'package:openlib/state/state.dart'
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
sqfliteFfiInit();
databaseFactory = databaseFactoryFfi;
}
Database db = await Sqlite.initDb(); Database db = await Sqlite.initDb();
runApp(ProviderScope( runApp(
ProviderScope(
overrides: [dbProvider.overrideWithValue(MyLibraryDb(dbInstance: db))], overrides: [dbProvider.overrideWithValue(MyLibraryDb(dbInstance: db))],
child: const MyApp())); child: const MyApp(),
),
);
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return DynamicColorBuilder(
builder: (BuildContext context, Widget? child) { builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
return MediaQuery( return MaterialApp(
data: MediaQuery.of(context).copyWith( // builder: (BuildContext context, Widget? child) {
textScaleFactor: 1.0, // return MediaQuery(
), // data: MediaQuery.of(context).copyWith(
child: child!, // textScaleFactor: 1.0,
); // ),
}, // child: child!,
debugShowCheckedModeBanner: false, // );
title: 'Openlib', // },
theme: ThemeData( debugShowCheckedModeBanner: false,
primaryColor: Colors.white, title: 'Openlib',
colorScheme: ColorScheme.light( theme: ThemeData(
primary: Colors.white,
secondary: '#FB0101'.toColor(),
tertiary: Colors.black,
tertiaryContainer: '#F2F2F2'.toColor(),
),
textTheme: const TextTheme(
displayLarge: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 21,
),
displayMedium: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Colors.black,
overflow: TextOverflow.ellipsis,
),
),
fontFamily: GoogleFonts.nunito().fontFamily,
useMaterial3: true, useMaterial3: true,
textSelectionTheme: TextSelectionThemeData( colorScheme: lightDynamic,
selectionColor: '#FB0101'.toColor(), ),
selectionHandleColor: '#FB0101'.toColor())), darkTheme: ThemeData(
home: const HomePage(), useMaterial3: true,
); colorScheme: darkDynamic,
),
home: const HomePage(),
);
});
} }
} }
@ -84,67 +81,37 @@ class _HomePageState extends ConsumerState<HomePage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final selectedIndex = ref.watch(selectedIndexProvider); final selectedIndex = ref.watch(selectedIndexProvider);
return Scaffold( return AnnotatedRegion<SystemUiOverlayStyle>(
appBar: AppBar( value: SystemUiOverlayStyle(
backgroundColor: Theme.of(context).colorScheme.primary, systemNavigationBarColor: ElevationOverlay.applySurfaceTint(
title: const Text("Openlib"), Theme.of(context).colorScheme.surface,
titleTextStyle: Theme.of(context).textTheme.displayLarge, Theme.of(context).colorScheme.surfaceTint,
), 3)),
body: _widgetOptions.elementAt(selectedIndex), child: Scaffold(
bottomNavigationBar: SafeArea( backgroundColor: Theme.of(context).colorScheme.background,
child: GNav( appBar: AppBar(
rippleColor: Colors.redAccent, leading:
backgroundColor: Colors.black, Icon(Icons.book, color: Theme.of(context).colorScheme.primary),
haptic: true, title: const Text(
tabBorderRadius: 50, "Openlib",
tabActiveBorder: Border.all( style: TextStyle(fontWeight: FontWeight.w600),
color: Theme.of(context).colorScheme.secondary,
), ),
tabMargin: const EdgeInsets.fromLTRB(13, 6, 13, 2.5), titleSpacing: 1,
curve: Curves.easeInOut, // tab animation curves ),
duration: const Duration(milliseconds: 150), body: _widgetOptions.elementAt(selectedIndex),
gap: 5, bottomNavigationBar: NavigationBar(
color: const Color.fromARGB(255, 255, 255, 255), destinations: const [
activeColor: const Color.fromARGB(255, 255, 255, 255), NavigationDestination(
iconSize: 21, // tab button icon size icon: Icon(Icons.trending_up), label: "Trending"),
tabBackgroundColor: Theme.of(context).colorScheme.secondary, NavigationDestination(icon: Icon(Icons.search), label: "Search"),
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 6.5), NavigationDestination(
tabs: const [ icon: Icon(Icons.collections_bookmark), label: "My Library"),
GButton(
icon: Icons.trending_up,
text: 'Trending',
iconColor: Colors.white,
textStyle: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.white,
fontSize: 13,
),
),
GButton(
icon: Icons.search,
text: 'Search',
iconColor: Colors.white,
textStyle: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.white,
fontSize: 13,
),
),
GButton(
icon: Icons.collections_bookmark,
text: 'My Library',
iconColor: Colors.white,
textStyle: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.white,
fontSize: 13,
),
),
], ],
selectedIndex: selectedIndex, selectedIndex: selectedIndex,
onTabChange: (index) async { onDestinationSelected: (index) async {
ref.read(selectedIndexProvider.notifier).state = index; ref.read(selectedIndexProvider.notifier).state = index;
}, },
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
), ),
), ),
); );

View File

@ -127,7 +127,7 @@ class AnnasArchieve {
String? link = pTag?.querySelector('a')?.attributes['href']; String? link = pTag?.querySelector('a')?.attributes['href'];
return link; return link;
} catch (e) { } catch (e) {
print(e); // print(e);
return null; return null;
} }
} }

View File

@ -1,15 +1,32 @@
import 'dart:io';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
class Sqlite { class Sqlite {
static Future<Database> initDb() async { static Future<Database> initDb() async {
var databasesPath = await getDatabasesPath(); var databasesPath = await getDatabasesPath();
String path = '$databasesPath/mylibrary.db'; String path = '$databasesPath/mylibrary.db';
bool isMobile = Platform.isAndroid || Platform.isIOS;
Database dbInstance = await openDatabase(path, version: 1, Database dbInstance = await openDatabase(
onCreate: (Database db, int version) async { path,
await db.execute( version: 2,
'CREATE TABLE mybooks (id TEXT PRIMARY KEY, title TEXT,author TEXT,thumbnail TEXT,link TEXT,publisher TEXT,info TEXT,format TEXT,description TEXT)'); onCreate: (Database db, int version) async {
}); await db.execute(
'CREATE TABLE mybooks (id TEXT PRIMARY KEY, title TEXT,author TEXT,thumbnail TEXT,link TEXT,publisher TEXT,info TEXT,format TEXT,description TEXT)');
if (isMobile) {
await db.execute(
'CREATE TABLE bookposition (fileName TEXT PRIMARY KEY,position TEXT)');
}
},
onUpgrade: (db, oldVersion, newVersion) async {
List<dynamic> isTableExist = await db.query('sqlite_master',
where: 'name = ?', whereArgs: ['bookposition']);
if (isMobile && isTableExist.isEmpty) {
await db.execute(
'CREATE TABLE bookposition (fileName TEXT PRIMARY KEY,position TEXT)');
}
},
);
return dbInstance; return dbInstance;
} }
} }
@ -118,4 +135,33 @@ class MyLibraryDb {
}); });
return myBookList.reversed.toList(); return myBookList.reversed.toList();
} }
Future<void> saveBookState(String fileName, String position) async {
await dbInstance.insert(
'bookposition',
{'fileName': fileName, 'position': position},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<void> deleteBookState(String fileName) async {
await dbInstance.delete(
'bookposition',
where: 'fileName = ?',
whereArgs: [fileName],
);
}
Future<String?> getBookState(String fileName) async {
List<Map<String, dynamic>> data = await dbInstance
.query('bookposition', where: 'fileName = ?', whereArgs: [fileName]);
List<dynamic> dataList = List.generate(data.length, (i) {
return {'fileName': data[i]['fileName'], 'position': data[i]['position']};
});
if (dataList.isNotEmpty) {
return dataList[0]['position'];
} else {
return null;
}
}
} }

View File

@ -35,9 +35,11 @@ Future<void> deleteFileWithDbData(
String appDirPath = await getAppDirectoryPath; String appDirPath = await getAppDirectoryPath;
await deleteFile('$appDirPath/$fileName'); await deleteFile('$appDirPath/$fileName');
await ref.read(dbProvider).delete(md5); await ref.read(dbProvider).delete(md5);
await ref.read(dbProvider).deleteBookState(fileName);
// ignore: unused_result // ignore: unused_result
ref.refresh(myLibraryProvider); ref.refresh(myLibraryProvider);
} catch (e) { } catch (e) {
print(e); // print(e);
rethrow;
} }
} }

View File

@ -125,6 +125,22 @@ final deleteFileFromMyLib =
final pdfCurrentPage = StateProvider.autoDispose<int>((ref) => 0); final pdfCurrentPage = StateProvider.autoDispose<int>((ref) => 0);
final totalPdfPage = StateProvider.autoDispose<int>((ref) => 0); final totalPdfPage = StateProvider.autoDispose<int>((ref) => 0);
Future<void> savePdfState(String fileName, WidgetRef ref) async {
String position = ref.watch(pdfCurrentPage).toString();
await ref.watch(dbProvider).saveBookState(fileName, position);
}
Future<void> saveEpubState(
String fileName, String? position, WidgetRef ref) async {
String pos = position ?? '';
await ref.watch(dbProvider).saveBookState(fileName, pos);
}
final getBookPosition =
FutureProvider.family.autoDispose<String?, String>((ref, fileName) async {
return await ref.read(dbProvider).getBookState(fileName);
});
final filePathProvider = final filePathProvider =
FutureProvider.family<String, String>((ref, fileName) async { FutureProvider.family<String, String>((ref, fileName) async {
String path = await getFilePath(fileName); String path = await getFilePath(fileName);

View File

@ -23,18 +23,21 @@ import 'package:openlib/ui/components/file_buttons_widget.dart';
import 'package:openlib/ui/components/snack_bar_widget.dart'; import 'package:openlib/ui/components/snack_bar_widget.dart';
class BookInfoPage extends ConsumerWidget { class BookInfoPage extends ConsumerWidget {
const BookInfoPage({Key? key, required this.url}) : super(key: key); const BookInfoPage({Key? key, required this.url, required this.title})
: super(key: key);
final String url; final String url;
final String title;
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final bookInfo = ref.watch(bookInfoProvider(url)); final bookInfo = ref.watch(bookInfoProvider(url));
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary, // backgroundColor: Theme.of(context).colorScheme.primary,
title: const Text("Openlib"), title: Text(title),
titleTextStyle: Theme.of(context).textTheme.displayLarge, titleSpacing: 0,
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
), ),
body: bookInfo.when( body: bookInfo.when(
data: (data) { data: (data) {
@ -95,21 +98,23 @@ class _ActionButtonWidgetState extends ConsumerState<ActionButtonWidget> {
} else { } else {
return Padding( return Padding(
padding: const EdgeInsets.only(top: 21, bottom: 21), padding: const EdgeInsets.only(top: 21, bottom: 21),
child: TextButton( child: ElevatedButton(
style: TextButton.styleFrom( // style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.secondary, // // backgroundColor: Theme.of(context).colorScheme.secondary,
textStyle: const TextStyle( // textStyle: const TextStyle(
fontSize: 13, // // fontSize: 13,
fontWeight: FontWeight.w900, // fontWeight: FontWeight.w500,
color: Colors.white, // // color: Colors.white,
)), // )),
onPressed: () async { onPressed: () async {
await downloadFileWidget(ref, context, widget.data); await downloadFileWidget(ref, context, widget.data);
}, },
child: const Padding( child: const Text('Add To My Library',
padding: EdgeInsets.all(8.0), style: TextStyle(
child: Text('Add To My Library'), fontSize: 14,
), fontWeight: FontWeight.w400,
// color: Colors.white,
)),
), ),
); );
} }
@ -190,111 +195,33 @@ class _ShowDialog extends ConsumerWidget {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
return Stack( return AlertDialog(
alignment: Alignment.center, title: const Text("Downloading Book"),
children: [ content: Column(
Padding( mainAxisSize: MainAxisSize.min,
padding: const EdgeInsets.all(15.0), children: [
child: Container( Text(title),
width: double.infinity, const SizedBox(
height: 255, height: 20,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.white,
),
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.all(8),
child: Text(
"Downloading Book",
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 54, 54, 54),
decoration: TextDecoration.none),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(
title,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Colors.black54,
decoration: TextDecoration.none),
overflow: TextOverflow.ellipsis,
maxLines: 2,
textAlign: TextAlign.start,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(8),
child: Text(
'$downloadedFileSize/$fileSize',
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.secondary,
decoration: TextDecoration.none,
letterSpacing: 1),
overflow: TextOverflow.ellipsis,
maxLines: 1,
textAlign: TextAlign.start,
),
),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(50)),
child: LinearProgressIndicator(
color: Theme.of(context).colorScheme.secondary,
backgroundColor: Colors.black26,
value: downloadProgress,
minHeight: 4,
),
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
style: TextButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.secondary,
textStyle: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w900,
color: Colors.white,
)),
onPressed: () {
ref.read(cancelCurrentDownload).cancel();
Navigator.of(context).pop();
},
child: const Padding(
padding: EdgeInsets.all(3.0),
child: Text('Cancel'),
),
)
],
),
)
],
),
),
), ),
LinearProgressIndicator(
value: downloadProgress,
),
const SizedBox(
height: 20,
),
Text(
'$downloadedFileSize/$fileSize',
)
],
),
actions: <Widget>[
TextButton(
onPressed: () {
ref.read(cancelCurrentDownload).cancel();
Navigator.pop(context, 'Cancel');
},
child: const Text('Cancel'),
), ),
], ],
); );

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:openlib/ui/extensions.dart'; import 'package:openlib/ui/extensions.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
// TODO: Redesign this widget
class BookInfoCard extends StatelessWidget { class BookInfoCard extends StatelessWidget {
const BookInfoCard( const BookInfoCard(
{Key? key, {Key? key,
@ -22,26 +23,29 @@ class BookInfoCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return InkWell( return Card(
onTap: onClick, shadowColor: Colors.black.withOpacity(0),
child: Container( margin: const EdgeInsets.only(bottom: 20),
width: double.infinity, shape: RoundedRectangleBorder(
height: 120, borderRadius: BorderRadius.circular(5),
decoration: BoxDecoration( side: BorderSide(
borderRadius: BorderRadius.circular(5), color: Theme.of(context).colorScheme.surfaceVariant,
color: Theme.of(context).colorScheme.tertiaryContainer, width: 1,
), ),
margin: const EdgeInsets.only(bottom: 10), ),
child: InkWell(
onTap: onClick,
borderRadius: BorderRadius.circular(5),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
CachedNetworkImage( CachedNetworkImage(
height: 120, height: 140,
width: 90, width: 105,
imageUrl: thumbnail ?? "", imageUrl: thumbnail ?? "",
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(5)), borderRadius: BorderRadius.circular(5),
image: DecorationImage( image: DecorationImage(
image: imageProvider, image: imageProvider,
fit: BoxFit.fill, fit: BoxFit.fill,
@ -51,7 +55,7 @@ class BookInfoCard extends StatelessWidget {
placeholder: (context, url) => Container( placeholder: (context, url) => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
color: "#F8C0C8".toColor(), color: Theme.of(context).colorScheme.surfaceVariant,
), ),
height: 120, height: 120,
width: 90, width: 90,
@ -60,7 +64,7 @@ class BookInfoCard extends StatelessWidget {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
color: "#F8C0C8".toColor(), color: Theme.of(context).colorScheme.surfaceVariant,
), ),
height: 120, height: 120,
width: 90, width: 90,
@ -72,7 +76,7 @@ class BookInfoCard extends StatelessWidget {
), ),
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.all(5), padding: const EdgeInsets.all(18),
child: SizedBox( child: SizedBox(
width: double.infinity, width: double.infinity,
child: Column( child: Column(
@ -82,29 +86,33 @@ class BookInfoCard extends StatelessWidget {
Text( Text(
title, title,
style: const TextStyle( style: const TextStyle(
fontSize: 15, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.black, // color: Colors.black,
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 2, maxLines: 2,
), ),
Text( Padding(
publisher, padding: const EdgeInsets.symmetric(vertical: 4),
style: TextStyle( child: Text(
fontSize: 12, publisher,
fontWeight: FontWeight.bold, style: const TextStyle(
color: "#4D4D4D".toColor(), fontSize: 12,
fontWeight: FontWeight.w500,
// color: "#4D4D4D".toColor(),
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
), ),
overflow: TextOverflow.ellipsis,
maxLines: 1,
), ),
Text( Text(
author, author,
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 14,
fontWeight: FontWeight.bold, fontWeight: FontWeight.w500,
color: "#7B7B7B".toColor(), // color: "#7B7B7B".toColor(),
color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 1, maxLines: 1,

View File

@ -12,10 +12,10 @@ class BookInfoWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SingleChildScrollView( return SingleChildScrollView(
physics: const BouncingScrollPhysics(), // physics: const BouncingScrollPhysics(),
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 15, right: 15, top: 10), padding: const EdgeInsets.only(left: 20, right: 20, top: 10),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -26,8 +26,8 @@ class BookInfoWidget extends StatelessWidget {
), ),
Center( Center(
child: CachedNetworkImage( child: CachedNetworkImage(
height: 230, height: 240,
width: 170, width: 180,
imageUrl: data.thumbnail, imageUrl: data.thumbnail,
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -43,8 +43,8 @@ class BookInfoWidget extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
color: Colors.grey, color: Colors.grey,
), ),
height: 230, height: 240,
width: 170, width: 180,
), ),
errorWidget: (context, url, error) { errorWidget: (context, url, error) {
return Container( return Container(
@ -63,21 +63,22 @@ class BookInfoWidget extends StatelessWidget {
), ),
_TopPaddedText( _TopPaddedText(
text: data.title, text: data.title,
fontSize: 19, fontSize: 22,
topPadding: 15, topPadding: 15,
color: Colors.black, fontWeight: FontWeight.w600,
// color: Colors.black,
maxLines: 7, maxLines: 7,
), ),
_TopPaddedText( _TopPaddedText(
text: data.publisher ?? "unknown", text: data.publisher ?? "unknown",
fontSize: 15, fontSize: 17,
topPadding: 7, topPadding: 7,
color: "#595E60".toColor(), color: Theme.of(context).colorScheme.onSecondaryContainer,
maxLines: 4, maxLines: 4,
), ),
_TopPaddedText( _TopPaddedText(
text: data.author ?? "unknown", text: data.author ?? "unknown",
fontSize: 13, fontSize: 20,
topPadding: 7, topPadding: 7,
color: "#7F7F7F".toColor(), color: "#7F7F7F".toColor(),
maxLines: 3, maxLines: 3,
@ -91,33 +92,41 @@ class BookInfoWidget extends StatelessWidget {
), ),
// child slot of page // child slot of page
child, child,
Column( Card(
mainAxisAlignment: MainAxisAlignment.start, shape: RoundedRectangleBorder(
crossAxisAlignment: CrossAxisAlignment.start, borderRadius: BorderRadius.circular(15),
children: [ ),
Text( child: SizedBox(
"Description", width: double.infinity,
style: TextStyle( child: Padding(
fontSize: 16, padding: const EdgeInsets.all(20.0),
fontWeight: FontWeight.w900, child: Column(
color: Theme.of(context).colorScheme.tertiary, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Description",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.onSurface,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
Padding(
padding: const EdgeInsets.only(top: 15, bottom: 10),
child: Text(
data.description ?? "",
style: const TextStyle(
fontSize: 16,
),
),
)
],
), ),
overflow: TextOverflow.ellipsis,
maxLines: 1,
), ),
Padding( ),
padding: const EdgeInsets.only(top: 7, bottom: 10),
child: Text(
data.description ?? "",
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: "#6B6B6B".toColor(),
letterSpacing: 1.5,
),
),
)
],
) )
], ],
), ),
@ -130,14 +139,16 @@ class _TopPaddedText extends StatelessWidget {
final String text; final String text;
final double fontSize; final double fontSize;
final double topPadding; final double topPadding;
final Color color; final Color? color;
final int maxLines; final int maxLines;
final FontWeight fontWeight;
const _TopPaddedText( const _TopPaddedText(
{required this.text, {required this.text,
required this.fontSize, required this.fontSize,
required this.topPadding, required this.topPadding,
required this.color, this.color,
this.fontWeight = FontWeight.w400,
required this.maxLines, required this.maxLines,
Key? key}) Key? key})
: super(key: key); : super(key: key);
@ -150,9 +161,9 @@ class _TopPaddedText extends StatelessWidget {
text, text,
style: TextStyle( style: TextStyle(
fontSize: fontSize, fontSize: fontSize,
fontWeight: FontWeight.w900, fontWeight: fontWeight,
color: color, color: color,
letterSpacing: 0.5, // letterSpacing: 0.5,
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: maxLines, maxLines: maxLines,

View File

@ -16,117 +16,28 @@ class ShowDeleteDialog extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return Stack( // if (false) {
alignment: Alignment.center, // return
children: [ // }
Padding(
padding: const EdgeInsets.all(15.0),
child: Container(
width: double.infinity,
height: 219,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.white,
),
padding: const EdgeInsets.fromLTRB(20, 50, 20, 20),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.all(8),
child: Text(
"Delete Book",
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 54, 54, 54),
decoration: TextDecoration.none),
),
),
const Padding(
padding: EdgeInsets.all(8),
child: Text(
"This is permanent and cannot be undone",
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Colors.black54,
decoration: TextDecoration.none),
overflow: TextOverflow.ellipsis,
maxLines: 2,
textAlign: TextAlign.start,
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
style: ButtonStyle(
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0),
side: BorderSide(
width: 3,
color: Theme.of(context)
.colorScheme
.secondary),
),
),
),
onPressed: () {
ref.read(deleteFileFromMyLib(
FileName(md5: id, format: format)));
Navigator.of(context).pop();
showSnackBar( return AlertDialog(
context: context, title: const Text("Delete Book?"),
message: 'Book has been Deleted!'); content: const Text("This is permanent and cannot be undone"),
actions: <Widget>[
TextButton(
onPressed: () {
ref.read(deleteFileFromMyLib(FileName(md5: id, format: format)));
Navigator.of(context).pop();
onDelete(); showSnackBar(context: context, message: 'Book has been Deleted!');
},
child: const Padding( onDelete();
padding: EdgeInsets.all(5.0), },
child: Text( child: const Text('Delete'),
'Delete', ),
style: TextStyle( TextButton(
fontSize: 11, onPressed: () => Navigator.pop(context, 'Cancel'),
fontWeight: FontWeight.bold, child: const Text('Cancel'),
color: Colors.black,
),
),
),
),
const SizedBox(
width: 10,
),
TextButton(
style: TextButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.secondary,
textStyle: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w900,
color: Colors.white,
)),
onPressed: () {
Navigator.of(context).pop();
},
child: const Padding(
padding: EdgeInsets.all(5.0),
child: Text('Cancel'),
),
)
],
),
)
],
),
),
),
), ),
], ],
); );

View File

@ -24,7 +24,7 @@ class FileOpenAndDeleteButtons extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TextButton( FilledButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.secondary, backgroundColor: Theme.of(context).colorScheme.secondary,
textStyle: const TextStyle( textStyle: const TextStyle(
@ -60,7 +60,7 @@ class FileOpenAndDeleteButtons extends StatelessWidget {
const SizedBox( const SizedBox(
width: 10, width: 10,
), ),
TextButton( OutlinedButton(
style: ButtonStyle( style: ButtonStyle(
shape: MaterialStateProperty.all( shape: MaterialStateProperty.all(
RoundedRectangleBorder( RoundedRectangleBorder(
@ -82,14 +82,14 @@ class FileOpenAndDeleteButtons extends StatelessWidget {
); );
}); });
}, },
child: const Padding( child: Padding(
padding: EdgeInsets.all(5.3), padding: const EdgeInsets.all(5.3),
child: Text( child: Text(
'Delete', 'Delete',
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.black, color: Theme.of(context).colorScheme.onSurface,
), ),
), ),
), ),

View File

@ -7,13 +7,13 @@ class TitleText extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Padding(
padding: const EdgeInsets.only(left: 10, bottom: 7), padding: const EdgeInsets.only(left: 10, right: 10, top: 20, bottom: 10),
child: Text( child: Text(
text, text,
style: TextStyle( style: const TextStyle(
fontSize: 19, fontSize: 25,
fontWeight: FontWeight.bold, fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.secondary, // color: Theme.of(context).colorScheme.onInverseSurface,
), ),
), ),
); );

View File

@ -5,14 +5,14 @@ void showSnackBar({required BuildContext context, required String message}) {
content: Text( content: Text(
message, message,
style: const TextStyle( style: const TextStyle(
fontSize: 11, fontSize: 12,
fontWeight: FontWeight.bold, // fontWeight: FontWeight.bold,
color: Colors.white, // color: Colors.white,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
backgroundColor: Theme.of(context).colorScheme.secondary, // backgroundColor: Theme.of(context).colorScheme.secondary,
width: 300, width: 300,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(50))), borderRadius: BorderRadius.all(Radius.circular(50))),

View File

@ -3,7 +3,8 @@ import 'package:flutter/material.dart';
import 'package:epub_view/epub_view.dart'; import 'package:epub_view/epub_view.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:openlib/state/state.dart' show filePathProvider; import 'package:openlib/state/state.dart'
show filePathProvider, saveEpubState, getBookPosition;
class EpubViewerWidget extends ConsumerStatefulWidget { class EpubViewerWidget extends ConsumerStatefulWidget {
const EpubViewerWidget({super.key, required this.fileName}); const EpubViewerWidget({super.key, required this.fileName});
@ -19,48 +20,52 @@ class _EpubViewState extends ConsumerState<EpubViewerWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final filePath = ref.watch(filePathProvider(widget.fileName)); final filePath = ref.watch(filePathProvider(widget.fileName));
return filePath.when(data: (data) { return filePath.when(data: (data) {
return EpubViewer(filePath: data); return EpubViewer(filePath: data, fileName: widget.fileName);
}, error: (error, stack) { }, error: (error, stack) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary, // backgroundColor: Theme.of(context).colorScheme.primary,
title: const Text("Openlib"), title: const Text("Openlib"),
titleTextStyle: Theme.of(context).textTheme.displayLarge, // titleTextStyle: Theme.of(context).textTheme.displayLarge,
), ),
body: Center(child: Text(error.toString())), body: Center(child: Text(error.toString())),
); );
}, loading: () { }, loading: () {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary, // backgroundColor: Theme.of(context).colorScheme.primary,
title: const Text("Openlib"), title: const Text("Openlib"),
titleTextStyle: Theme.of(context).textTheme.displayLarge, // titleTextStyle: Theme.of(context).textTheme.displayLarge,
), ),
body: Center( body: Center(
child: SizedBox( child: SizedBox(
width: 25, width: 25,
height: 25, height: 25,
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.secondary, color: Theme.of(context).colorScheme.secondary,
),
), ),
)), ),
); );
}); });
} }
} }
class EpubViewer extends StatefulWidget { class EpubViewer extends ConsumerStatefulWidget {
const EpubViewer({Key? key, required this.filePath}) : super(key: key); const EpubViewer({Key? key, required this.filePath, required this.fileName})
: super(key: key);
final String filePath; final String filePath;
final String fileName;
@override @override
// ignore: library_private_types_in_public_api // ignore: library_private_types_in_public_api
_EpubViewerState createState() => _EpubViewerState(); _EpubViewerState createState() => _EpubViewerState();
} }
class _EpubViewerState extends State<EpubViewer> { class _EpubViewerState extends ConsumerState<EpubViewer> {
late EpubController _epubReaderController; late EpubController _epubReaderController;
String? epubConf;
@override @override
void initState() { void initState() {
@ -70,6 +75,14 @@ class _EpubViewerState extends State<EpubViewer> {
super.initState(); super.initState();
} }
@override
void deactivate() {
if (Platform.isAndroid || Platform.isIOS) {
saveEpubState(widget.fileName, epubConf, ref);
}
super.deactivate();
}
@override @override
void dispose() { void dispose() {
_epubReaderController.dispose(); _epubReaderController.dispose();
@ -78,23 +91,59 @@ class _EpubViewerState extends State<EpubViewer> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final position = ref.watch(getBookPosition(widget.fileName));
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary, // backgroundColor: Theme.of(context).colorScheme.primary,
title: const Text("Openlib"), title: const Text("Openlib"),
titleTextStyle: Theme.of(context).textTheme.displayLarge, titleSpacing: 0,
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
), ),
endDrawer: Drawer( endDrawer: Drawer(
child: EpubViewTableOfContents(controller: _epubReaderController), child: EpubViewTableOfContents(controller: _epubReaderController),
), ),
body: EpubView( body: position.when(
onDocumentLoaded: (doc) {}, data: (data) {
onChapterChanged: (value) {}, return EpubView(
builders: EpubViewBuilders<DefaultBuilderOptions>( onDocumentLoaded: (doc) {
options: const DefaultBuilderOptions(), Future.delayed(const Duration(milliseconds: 20), () {
chapterDividerBuilder: (_) => const Divider(), String pos = data ?? "";
), _epubReaderController.gotoEpubCfi(pos);
controller: _epubReaderController, });
},
onChapterChanged: (value) {
epubConf = _epubReaderController.generateEpubCfi();
},
builders: EpubViewBuilders<DefaultBuilderOptions>(
options: const DefaultBuilderOptions(),
chapterDividerBuilder: (_) => const Divider(),
),
controller: _epubReaderController,
);
},
error: (err, _) {
return EpubView(
onChapterChanged: (value) {
epubConf = _epubReaderController.generateEpubCfi();
},
builders: EpubViewBuilders<DefaultBuilderOptions>(
options: const DefaultBuilderOptions(),
chapterDividerBuilder: (_) => const Divider(),
),
controller: _epubReaderController,
);
},
loading: () {
return Center(
child: SizedBox(
width: 25,
height: 25,
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.secondary,
),
),
);
},
), ),
); );
} }

View File

@ -7,17 +7,18 @@ import 'package:openlib/ui/components/book_info_widget.dart';
import 'package:openlib/ui/components/file_buttons_widget.dart'; import 'package:openlib/ui/components/file_buttons_widget.dart';
class BookPage extends StatelessWidget { class BookPage extends StatelessWidget {
const BookPage({Key? key, required this.id}) : super(key: key); const BookPage({Key? key, required this.id, required this.title})
: super(key: key);
final String id; final String id;
final String title;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary, title: Text(title),
title: const Text("Openlib"), titleSpacing: 0,
titleTextStyle: Theme.of(context).textTheme.displayLarge,
), ),
body: Consumer( body: Consumer(
builder: (BuildContext context, WidgetRef ref, _) { builder: (BuildContext context, WidgetRef ref, _) {
@ -42,12 +43,8 @@ class BookPage extends StatelessWidget {
); );
} else { } else {
return Center( return Center(
child: SizedBox( child: CircularProgressIndicator(
width: 25, color: Theme.of(context).colorScheme.secondary,
height: 25,
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.secondary,
),
)); ));
} }
}); });

View File

@ -38,7 +38,7 @@ class MyLibraryPage extends ConsumerWidget {
onClick: () { onClick: () {
Navigator.push(context, MaterialPageRoute( Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) { builder: (BuildContext context) {
return BookPage(id: i.id); return BookPage(id: i.id, title: i.title);
})); }));
})) }))
.toList()), .toList()),
@ -81,12 +81,8 @@ class MyLibraryPage extends ConsumerWidget {
}, },
loading: () { loading: () {
return Center( return Center(
child: SizedBox( child: CircularProgressIndicator(
width: 25, color: Theme.of(context).colorScheme.secondary,
height: 25,
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.secondary,
),
)); ));
}, },
); );

View File

@ -2,7 +2,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart'; import 'package:flutter_pdfview/flutter_pdfview.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:openlib/state/state.dart' import 'package:openlib/state/state.dart'
show filePathProvider, pdfCurrentPage, totalPdfPage; show
filePathProvider,
pdfCurrentPage,
totalPdfPage,
savePdfState,
getBookPosition;
import 'package:url_launcher/url_launcher.dart';
import 'dart:io' show Platform;
class PdfView extends ConsumerStatefulWidget { class PdfView extends ConsumerStatefulWidget {
const PdfView({super.key, required this.fileName}); const PdfView({super.key, required this.fileName});
@ -18,7 +25,7 @@ class _PdfViewState extends ConsumerState<PdfView> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final filePath = ref.watch(filePathProvider(widget.fileName)); final filePath = ref.watch(filePathProvider(widget.fileName));
return filePath.when(data: (data) { return filePath.when(data: (data) {
return PdfViewer(filePath: data); return PdfViewer(filePath: data, fileName: widget.fileName);
}, error: (error, stack) { }, error: (error, stack) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@ -31,9 +38,9 @@ class _PdfViewState extends ConsumerState<PdfView> {
}, loading: () { }, loading: () {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary, // backgroundColor: Theme.of(context).colorScheme.primary,
title: const Text("Openlib"), title: const Text("Openlib"),
titleTextStyle: Theme.of(context).textTheme.displayLarge, // titleTextStyle: Theme.of(context).textTheme.displayLarge,
), ),
body: Center( body: Center(
child: SizedBox( child: SizedBox(
@ -49,9 +56,11 @@ class _PdfViewState extends ConsumerState<PdfView> {
} }
class PdfViewer extends ConsumerStatefulWidget { class PdfViewer extends ConsumerStatefulWidget {
const PdfViewer({Key? key, required this.filePath}) : super(key: key); const PdfViewer({Key? key, required this.filePath, required this.fileName})
: super(key: key);
final String filePath; final String filePath;
final String fileName;
@override @override
ConsumerState<ConsumerStatefulWidget> createState() => _PdfViewerState(); ConsumerState<ConsumerStatefulWidget> createState() => _PdfViewerState();
@ -60,60 +69,151 @@ class PdfViewer extends ConsumerStatefulWidget {
class _PdfViewerState extends ConsumerState<PdfViewer> { class _PdfViewerState extends ConsumerState<PdfViewer> {
late PDFViewController controller; late PDFViewController controller;
@override
void initState() {
super.initState();
}
@override
void deactivate() {
if (Platform.isAndroid || Platform.isIOS) {
savePdfState(widget.fileName, ref);
}
super.deactivate();
}
@override
void dispose() {
super.dispose();
}
Future<void> _openPdfWithDefaultViewer(String fileName) async {
debugPrint("Opening : $fileName");
final fileUrl = Uri.parse(fileName);
if (await canLaunchUrl(fileUrl)) {
await launchUrl(fileUrl);
} else {
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Could not open the PDF',
textAlign: TextAlign.center,
)),
);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isMobile = Platform.isAndroid || Platform.isIOS;
final currentPage = ref.watch(pdfCurrentPage); final currentPage = ref.watch(pdfCurrentPage);
final totalPages = ref.watch(totalPdfPage); final totalPages = ref.watch(totalPdfPage);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary, // backgroundColor: Theme.of(context).colorScheme.primary,
title: const Text("Openlib"), title: const Text("Openlib"),
titleTextStyle: Theme.of(context).textTheme.displayLarge, titleSpacing: 0,
actions: [ // titleTextStyle: Theme.of(context).textTheme.displayLarge,
IconButton( actions: isMobile
onPressed: () { ? [
if (currentPage != 0) { IconButton(
ref.read(pdfCurrentPage.notifier).state = currentPage - 1; onPressed: () {
controller.setPage(currentPage - 1); if (currentPage != 0) {
} else { ref.read(pdfCurrentPage.notifier).state =
ref.read(pdfCurrentPage.notifier).state = totalPages; currentPage - 1;
controller.setPage(totalPages - 1); controller.setPage(currentPage - 1);
} } else {
}, ref.read(pdfCurrentPage.notifier).state = totalPages;
icon: const Icon( controller.setPage(totalPages - 1);
Icons.arrow_left, }
size: 25, },
)), icon: const Icon(
Text('${(currentPage + 1).toString()} / ${totalPages.toString()}'), Icons.arrow_left,
IconButton( size: 25,
onPressed: () { )),
if (currentPage + 1 < totalPages) { Text(
ref.read(pdfCurrentPage.notifier).state = currentPage + 1; '${(currentPage + 1).toString()} / ${totalPages.toString()}'),
controller.setPage(currentPage + 1); IconButton(
} else { onPressed: () {
ref.read(pdfCurrentPage.notifier).state = 0; if (currentPage + 1 < totalPages) {
controller.setPage(0); ref.read(pdfCurrentPage.notifier).state =
} currentPage + 1;
}, controller.setPage(currentPage + 1);
icon: const Icon( } else {
Icons.arrow_right, ref.read(pdfCurrentPage.notifier).state = 0;
size: 25, controller.setPage(0);
)), }
], },
), icon: const Icon(
body: PDFView( Icons.arrow_right,
swipeHorizontal: true, size: 25,
fitEachPage: true, )),
fitPolicy: FitPolicy.BOTH, ]
filePath: widget.filePath, : [],
onViewCreated: (controller) {
this.controller = controller;
},
onPageChanged: (page, total) {
ref.read(pdfCurrentPage.notifier).state = page ?? 0;
ref.read(totalPdfPage.notifier).state = total ?? 0;
},
), ),
body: isMobile
? ref.watch(getBookPosition(widget.fileName)).when(
data: (data) {
return PDFView(
swipeHorizontal: true,
fitEachPage: true,
fitPolicy: FitPolicy.BOTH,
filePath: widget.filePath,
onViewCreated: (controller) {
this.controller = controller;
},
defaultPage: int.parse(data ?? '0'),
onPageChanged: (page, total) {
ref.read(pdfCurrentPage.notifier).state = page ?? 0;
ref.read(totalPdfPage.notifier).state = total ?? 0;
},
);
},
error: (error, stackTrace) {
return PDFView(
swipeHorizontal: true,
fitEachPage: true,
fitPolicy: FitPolicy.BOTH,
filePath: widget.filePath,
onViewCreated: (controller) {
this.controller = controller;
},
onPageChanged: (page, total) {
ref.read(pdfCurrentPage.notifier).state = page ?? 0;
ref.read(totalPdfPage.notifier).state = total ?? 0;
},
);
},
loading: () {
return Center(
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.secondary,
));
},
)
: Center(
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.secondary,
textStyle: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w900,
color: Colors.white,
)),
onPressed: () async {
await _openPdfWithDefaultViewer("file://${widget.filePath}");
// ignore: use_build_context_synchronously
Navigator.pop(context);
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
"Open with System's PDF Viewer",
),
),
),
),
); );
} }
} }

View File

@ -23,22 +23,30 @@ class ResultPage extends ConsumerWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary, // backgroundColor: Theme.of(context).colorScheme.primary,
title: const Text("Openlib"), title: const Text(
titleTextStyle: Theme.of(context).textTheme.displayLarge, "Results",
style: TextStyle(fontWeight: FontWeight.w500),
),
titleSpacing: 0,
// titleTextStyle: Theme.of(context).textTheme.displayLarge,
), ),
body: searchBooks.when( body: searchBooks.when(
data: (data) { data: (data) {
if (data.isNotEmpty) { if (data.isNotEmpty) {
return Padding( return Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 10), padding: const EdgeInsets.only(
left: 5,
right: 5,
),
child: CustomScrollView( child: CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[
const SliverToBoxAdapter( // const SliverToBoxAdapter(
child: TitleText("Results"), // child: TitleText("Results"),
), // ),
SliverPadding( SliverPadding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 10), padding: const EdgeInsets.only(left: 5, right: 5, top: 15),
sliver: SliverList( sliver: SliverList(
delegate: SliverChildListDelegate(data delegate: SliverChildListDelegate(data
.map((i) => BookInfoCard( .map((i) => BookInfoCard(
@ -50,7 +58,8 @@ class ResultPage extends ConsumerWidget {
onClick: () { onClick: () {
Navigator.push(context, MaterialPageRoute( Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) { builder: (BuildContext context) {
return BookInfoPage(url: i.link); return BookInfoPage(
url: i.link, title: i.title);
})); }));
}, },
)) ))
@ -103,22 +112,14 @@ class ResultPage extends ConsumerWidget {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Padding(
padding: EdgeInsets.only(left: 5, right: 5, top: 10),
child: TitleText("Results"),
),
Expanded( Expanded(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Center( Center(
child: SizedBox( child: CircularProgressIndicator(
width: 25, color: Theme.of(context).colorScheme.secondary,
height: 25,
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.secondary,
),
)) ))
], ],
), ),

View File

@ -39,19 +39,23 @@ class SearchPage extends ConsumerWidget {
padding: const EdgeInsets.only(left: 7, right: 7, top: 10), padding: const EdgeInsets.only(left: 7, right: 7, top: 10),
child: TextField( child: TextField(
showCursor: true, showCursor: true,
cursorColor: Theme.of(context).colorScheme.secondary, // cursorColor: Theme.of(context).colorScheme.secondary,
decoration: InputDecoration( decoration: InputDecoration(
enabledBorder: const OutlineInputBorder( label: const Text("Search"),
borderSide: BorderSide(color: Colors.black45, width: 2), enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(50)), borderSide: BorderSide(
color: Theme.of(context).colorScheme.outline, width: 2),
borderRadius: const BorderRadius.all(Radius.circular(50)),
), ),
focusedBorder: const OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black54, width: 2), borderSide: BorderSide(
borderRadius: BorderRadius.all(Radius.circular(50)), color: Theme.of(context).colorScheme.outline, width: 2),
borderRadius: const BorderRadius.all(Radius.circular(50)),
), ),
suffixIcon: IconButton( suffixIcon: IconButton(
padding: const EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 10),
color: Theme.of(context).colorScheme.secondary, // color: Theme.of(context).colorScheme.secondary,
icon: const Icon( icon: const Icon(
Icons.search, Icons.search,
size: 23, size: 23,
@ -59,16 +63,16 @@ class SearchPage extends ConsumerWidget {
onPressed: () => onSubmit(context, ref), onPressed: () => onSubmit(context, ref),
), ),
filled: true, filled: true,
hintStyle: const TextStyle( // hintStyle: const TextStyle(
color: Colors.grey, fontWeight: FontWeight.bold), // color: Colors.grey, fontWeight: FontWeight.bold),
hintText: "Search", hintText: "Search books ,author or ISBN",
fillColor: Theme.of(context).colorScheme.primary, // fillColor: Theme.of(context).colorScheme.primary,
), ),
onSubmitted: (String value) => onSubmit(context, ref), onSubmitted: (String value) => onSubmit(context, ref),
style: TextStyle( style: const TextStyle(
color: Theme.of(context).colorScheme.tertiary, // color: Theme.of(context).colorScheme.tertiary,
fontWeight: FontWeight.bold, // fontWeight: FontWeight.bold,
fontSize: 12, fontSize: 14,
), ),
onChanged: (String value) { onChanged: (String value) {
ref.read(searchQueryProvider.notifier).state = value; ref.read(searchQueryProvider.notifier).state = value;
@ -83,17 +87,21 @@ class SearchPage extends ConsumerWidget {
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Type', labelText: 'Type',
labelStyle: TextStyle( labelStyle: TextStyle(
fontSize: 11, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.secondary, color: Theme.of(context).colorScheme.secondary,
), ),
enabledBorder: const OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black54, width: 2), borderSide: BorderSide(
borderRadius: BorderRadius.all(Radius.circular(50)), color: Theme.of(context).colorScheme.outline,
width: 2),
borderRadius: const BorderRadius.all(Radius.circular(50)),
), ),
focusedBorder: const OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black54, width: 2), borderSide: BorderSide(
borderRadius: BorderRadius.all(Radius.circular(50)), color: Theme.of(context).colorScheme.outline,
width: 2),
borderRadius: const BorderRadius.all(Radius.circular(50)),
), ),
), ),
icon: const Icon(Icons.arrow_drop_down), icon: const Icon(Icons.arrow_drop_down),
@ -105,7 +113,7 @@ class SearchPage extends ConsumerWidget {
value: value, value: value,
child: Text( child: Text(
value, value,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 14),
), ),
); );
}).toList(), }).toList(),
@ -123,17 +131,21 @@ class SearchPage extends ConsumerWidget {
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Sort by', labelText: 'Sort by',
labelStyle: TextStyle( labelStyle: TextStyle(
fontSize: 11, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.secondary, color: Theme.of(context).colorScheme.secondary,
), ),
enabledBorder: const OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black45, width: 2), borderSide: BorderSide(
borderRadius: BorderRadius.all(Radius.circular(50)), color: Theme.of(context).colorScheme.outline,
width: 2),
borderRadius: const BorderRadius.all(Radius.circular(50)),
), ),
focusedBorder: const OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black54, width: 2), borderSide: BorderSide(
borderRadius: BorderRadius.all(Radius.circular(50)), color: Theme.of(context).colorScheme.outline,
width: 2),
borderRadius: const BorderRadius.all(Radius.circular(50)),
), ),
), ),
value: dropdownSortValue, value: dropdownSortValue,
@ -144,7 +156,7 @@ class SearchPage extends ConsumerWidget {
value: value, value: value,
child: Text( child: Text(
value, value,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 14),
), ),
); );
}).toList(), }).toList(),
@ -153,7 +165,7 @@ class SearchPage extends ConsumerWidget {
}, },
), ),
), ),
) ),
], ],
), ),
), ),

View File

@ -19,7 +19,7 @@ class TrendingPage extends ConsumerWidget {
final trendingBooks = ref.watch(getTrendingBooks); final trendingBooks = ref.watch(getTrendingBooks);
return trendingBooks.when(data: (data) { return trendingBooks.when(data: (data) {
return Padding( return Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 10), padding: const EdgeInsets.only(left: 5, right: 5, top: 0),
child: CustomScrollView( child: CustomScrollView(
slivers: [ slivers: [
const SliverToBoxAdapter( const SliverToBoxAdapter(
@ -37,6 +37,7 @@ class TrendingPage extends ConsumerWidget {
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) { (BuildContext context, int index) {
return InkWell( return InkWell(
borderRadius: BorderRadius.circular(10),
onTap: () { onTap: () {
Navigator.push(context, Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) { MaterialPageRoute(builder: (BuildContext context) {
@ -45,72 +46,66 @@ class TrendingPage extends ConsumerWidget {
); );
})); }));
}, },
child: Container( child: Column(
width: double.infinity, mainAxisAlignment: MainAxisAlignment.start,
height: double.infinity, crossAxisAlignment: CrossAxisAlignment.center,
color: const Color.fromARGB(255, 255, 255, 255), children: [
child: Column( CachedNetworkImage(
mainAxisAlignment: MainAxisAlignment.start, height: imageHeight,
crossAxisAlignment: CrossAxisAlignment.center, width: imageWidth,
children: [ imageUrl: data[index].thumbnail!,
CachedNetworkImage( imageBuilder: (context, imageProvider) =>
height: imageHeight, Container(
width: imageWidth, decoration: BoxDecoration(
imageUrl: data[index].thumbnail!, borderRadius: const BorderRadius.all(
imageBuilder: (context, imageProvider) => Radius.circular(5)),
Container( image: DecorationImage(
decoration: BoxDecoration( image: imageProvider,
boxShadow: const [ fit: BoxFit.fill,
BoxShadow(
color: Colors.grey,
spreadRadius: 0.1,
blurRadius: 1)
],
borderRadius: const BorderRadius.all(
Radius.circular(5)),
image: DecorationImage(
image: imageProvider,
fit: BoxFit.fill,
),
), ),
), ),
placeholder: (context, url) => Container( ),
placeholder: (context, url) => Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
// color: "#E3E8E9".toColor(),
color: Theme.of(context)
.colorScheme
.surfaceVariant,
),
height: imageHeight,
width: imageWidth,
),
errorWidget: (context, url, error) {
return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
color: "#E3E8E9".toColor(), color: Theme.of(context)
.colorScheme
.surfaceVariant,
), ),
height: imageHeight, height: imageHeight,
width: imageWidth, width: imageWidth,
), child: const Center(
errorWidget: (context, url, error) { child: Icon(Icons.image_rounded),
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.grey,
),
height: imageHeight,
width: imageWidth,
child: const Center(
child: Icon(Icons.image_rounded),
),
);
},
),
Padding(
padding: const EdgeInsets.only(top: 4),
child: SizedBox(
width: imageWidth,
child: Text(
data[index].title!,
style: Theme.of(context)
.textTheme
.displayMedium,
maxLines: 2,
), ),
);
},
),
Padding(
padding: const EdgeInsets.only(top: 8),
child: SizedBox(
width: imageWidth,
child: Text(
data[index].title!,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400),
maxLines: 2,
), ),
), ),
]), ),
), ]),
); );
}, },
childCount: data.length, childCount: data.length,
@ -130,13 +125,13 @@ class TrendingPage extends ConsumerWidget {
}, },
); );
}, loading: () { }, loading: () {
return Center( return const Center(
child: SizedBox( child: SizedBox(
width: 25, width: 25,
height: 25, height: 25,
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.secondary, // color: Theme.of(context).colorScheme.secondary,
), ),
)); ));
}); });
} }

View File

@ -6,6 +6,14 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <dynamic_color/dynamic_color_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
} }

View File

@ -3,6 +3,8 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
url_launcher_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -5,10 +5,14 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import dynamic_color
import path_provider_foundation import path_provider_foundation
import sqflite import sqflite
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
} }

View File

@ -145,6 +145,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.3.0" version: "5.3.0"
dynamic_color:
dependency: "direct main"
description:
name: dynamic_color
sha256: de4798a7069121aee12d5895315680258415de9b00e717723a1bd73d58f0126d
url: "https://pub.dev"
source: hosted
version: "1.6.6"
epub_view: epub_view:
dependency: "direct main" dependency: "direct main"
description: description:
@ -267,6 +275,11 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
gestures: gestures:
dependency: transitive dependency: transitive
description: description:
@ -544,6 +557,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.0" version: "2.5.0"
sqflite_common_ffi:
dependency: "direct main"
description:
name: sqflite_common_ffi
sha256: "0d5cc1be2eb18400ac6701c31211d44164393aa75886093002ecdd947be04f93"
url: "https://pub.dev"
source: hosted
version: "2.3.0+2"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb
url: "https://pub.dev"
source: hosted
version: "2.1.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -616,6 +645,70 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e"
url: "https://pub.dev"
source: hosted
version: "6.1.12"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "3dd2388cc0c42912eee04434531a26a82512b9cb1827e0214430c9bcbddfe025"
url: "https://pub.dev"
source: hosted
version: "6.0.38"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea
url: "https://pub.dev"
source: hosted
version: "2.1.3"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4
url: "https://pub.dev"
source: hosted
version: "2.0.18"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422"
url: "https://pub.dev"
source: hosted
version: "3.0.7"
uuid: uuid:
dependency: transitive dependency: transitive
description: description:
@ -690,4 +783,4 @@ packages:
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.0.5 <4.0.0" dart: ">=3.0.5 <4.0.0"
flutter: ">=3.7.0-0" flutter: ">=3.10.0"

View File

@ -49,10 +49,14 @@ dependencies:
google_fonts: ^5.1.0 google_fonts: ^5.1.0
cached_network_image: ^3.2.3 cached_network_image: ^3.2.3
sqflite_common_ffi: ^2.3.0+2
url_launcher: ^6.1.12
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
dev: ^1.0.0 dev: ^1.0.0
dynamic_color: ^1.6.6
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -6,6 +6,12 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <dynamic_color/dynamic_color_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
DynamicColorPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
} }

View File

@ -3,6 +3,8 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
url_launcher_windows
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST