Compare commits
36 Commits
v1.0-beta.
...
v1.0.2-bet
Author | SHA1 | Date | |
---|---|---|---|
ccd260d451 | |||
546805be20 | |||
250f0f8b7b | |||
f0d0178a2d | |||
f4f86a1353 | |||
93013fe6bb | |||
1de126104d | |||
e4c8ddaa88 | |||
82798bb749 | |||
16fc051219 | |||
da399604bf | |||
1b87e89470 | |||
0db7794455 | |||
6b31a63e86 | |||
27061a1922 | |||
162ea41196 | |||
010dc1dfdb | |||
b2a1be7a7e | |||
1507656944 | |||
9cc3c3dde5 | |||
e7b6a9d40f | |||
60df4afac2 | |||
bcfb35ab14 | |||
4ac80f08d8 | |||
a9fde1c561 | |||
110d089b8d | |||
4445fc4d84 | |||
11d19b3001 | |||
052c5993b1 | |||
bc36a350ab | |||
9102b0be7b | |||
365d29c19e | |||
52c742e8d0 | |||
d2c992bf80 | |||
39a91b9992 | |||
19df3b4f7b |
11
README.md
@ -3,7 +3,13 @@
|
||||
|
||||
#### An Open source app to download and read books from shadow library ([Anna’s Archive](https://annas-archive.org/)).
|
||||
|
||||
[](https://flutter.dev/) [](https://opensource.org/licenses/)
|
||||
[](https://flutter.dev/) [](https://opensource.org/licenses/) 
|
||||
|
||||
[<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
|
||||
@ -43,6 +49,7 @@
|
||||
- Adding Support For Multiple Downloads
|
||||
|
||||
## 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.
|
||||
|
||||
## Building from Source
|
||||
@ -69,7 +76,7 @@ flutter build
|
||||
- The Build Will Be In './build/app/outputs/flutter-apk/app-release.apk'
|
||||
|
||||
## 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
|
||||
|
||||
|
@ -79,6 +79,8 @@ android {
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
// replace with "debug" when running on debug version
|
||||
signingConfig signingConfigs.release
|
||||
minifyEnabled false
|
||||
shrinkResources false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,22 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEOS" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"/>
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
</queries>
|
||||
<application
|
||||
android:label="Openlib"
|
||||
android:name="${applicationName}"
|
||||
|
15
fastlane/metadata/android/en-US/full_description.txt
Normal file
@ -0,0 +1,15 @@
|
||||
<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'>Anna’s Archive</a>). The App Has Built In Reader to Read Books.
|
||||
</p>
|
||||
<p>
|
||||
As <i>Anna’s Archive</i> doesn't have an API, the app works by sending requests to <i>Anna’s 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>Main Features:</p>
|
||||
<ul>
|
||||
<li>Trending Books</li>
|
||||
<li>Download And Read Books With In-Built Viewer</li>
|
||||
<li>Supports Epub And Pdf Formats</li>
|
||||
<li>Open Books With Your Favourite Ebooks Reader</li>
|
||||
<li>Filter Books</li>
|
||||
<li>Sort Books</li>
|
||||
</ul>
|
BIN
fastlane/metadata/android/en-US/images/icon.png
Normal file
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 17 KiB |
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
download and read books from shadow library (Anna’s Archive)
|
BIN
github_releases.png
Normal file
After Width: | Height: | Size: 15 KiB |
112
lib/main.dart
@ -1,29 +1,65 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_nav_bar/google_nav_bar.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
import 'package:openlib/ui/extensions.dart';
|
||||
import 'package:openlib/ui/themes.dart';
|
||||
import 'package:openlib/ui/trending_page.dart';
|
||||
import 'package:openlib/ui/search_page.dart';
|
||||
import 'package:openlib/ui/mylibrary_page.dart';
|
||||
import 'package:openlib/ui/settings_page.dart';
|
||||
import 'package:openlib/services/database.dart' show Sqlite, MyLibraryDb;
|
||||
import 'package:openlib/services/files.dart'
|
||||
show moveFilesToAndroidInternalStorage;
|
||||
import 'package:openlib/state/state.dart'
|
||||
show selectedIndexProvider, dbProvider;
|
||||
show
|
||||
selectedIndexProvider,
|
||||
themeModeProvider,
|
||||
openPdfWithExternalAppProvider,
|
||||
openEpubWithExternalAppProvider,
|
||||
dbProvider;
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
Database db = await Sqlite.initDb();
|
||||
runApp(ProviderScope(
|
||||
overrides: [dbProvider.overrideWithValue(MyLibraryDb(dbInstance: db))],
|
||||
child: const MyApp()));
|
||||
|
||||
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
|
||||
sqfliteFfiInit();
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
}
|
||||
|
||||
Database initDb = await Sqlite.initDb();
|
||||
MyLibraryDb dataBase = MyLibraryDb(dbInstance: initDb);
|
||||
bool isDarkMode = await dataBase.getPreference('darkMode');
|
||||
bool openPdfwithExternalapp =
|
||||
await dataBase.getPreference('openPdfwithExternalApp');
|
||||
bool openEpubwithExternalapp =
|
||||
await dataBase.getPreference('openEpubwithExternalApp');
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
await moveFilesToAndroidInternalStorage();
|
||||
}
|
||||
|
||||
runApp(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
dbProvider.overrideWithValue(dataBase),
|
||||
themeModeProvider.overrideWith(
|
||||
(ref) => isDarkMode ? ThemeMode.dark : ThemeMode.light),
|
||||
openPdfWithExternalAppProvider
|
||||
.overrideWith((ref) => openPdfwithExternalapp),
|
||||
openEpubWithExternalAppProvider
|
||||
.overrideWith((ref) => openEpubwithExternalapp)
|
||||
],
|
||||
child: const MyApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
class MyApp extends ConsumerWidget {
|
||||
const MyApp({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return MaterialApp(
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return MediaQuery(
|
||||
@ -35,32 +71,9 @@ class MyApp extends StatelessWidget {
|
||||
},
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Openlib',
|
||||
theme: ThemeData(
|
||||
primaryColor: Colors.white,
|
||||
colorScheme: ColorScheme.light(
|
||||
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,
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
selectionColor: '#FB0101'.toColor(),
|
||||
selectionHandleColor: '#FB0101'.toColor())),
|
||||
theme: lightTheme,
|
||||
darkTheme: darkTheme,
|
||||
themeMode: ref.watch(themeModeProvider),
|
||||
home: const HomePage(),
|
||||
);
|
||||
}
|
||||
@ -77,7 +90,8 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
static const List<Widget> _widgetOptions = <Widget>[
|
||||
TrendingPage(),
|
||||
SearchPage(),
|
||||
MyLibraryPage()
|
||||
MyLibraryPage(),
|
||||
SettingsPage()
|
||||
];
|
||||
|
||||
@override
|
||||
@ -101,14 +115,14 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
tabMargin: const EdgeInsets.fromLTRB(13, 6, 13, 2.5),
|
||||
curve: Curves.easeInOut, // tab animation curves
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.easeIn,
|
||||
duration: const Duration(milliseconds: 125),
|
||||
gap: 5,
|
||||
color: const Color.fromARGB(255, 255, 255, 255),
|
||||
activeColor: const Color.fromARGB(255, 255, 255, 255),
|
||||
iconSize: 21, // tab button icon size
|
||||
iconSize: 19, // tab button icon size
|
||||
tabBackgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 6.5),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 6.5),
|
||||
tabs: const [
|
||||
GButton(
|
||||
icon: Icons.trending_up,
|
||||
@ -117,7 +131,7 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
textStyle: TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 13,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
GButton(
|
||||
@ -127,7 +141,7 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
textStyle: TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 13,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
GButton(
|
||||
@ -137,7 +151,17 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
textStyle: TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 13,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
GButton(
|
||||
icon: Icons.build,
|
||||
text: 'Settings',
|
||||
iconColor: Colors.white,
|
||||
textStyle: TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -127,7 +127,7 @@ class AnnasArchieve {
|
||||
String? link = pTag?.querySelector('a')?.attributes['href'];
|
||||
return link;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
// print(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -135,14 +135,19 @@ class AnnasArchieve {
|
||||
Future<BookInfoData?> _bookInfoParser(resData, url) async {
|
||||
var document = parse(resData.toString());
|
||||
var main = document.querySelector('main[class="main"]');
|
||||
var ul = main?.querySelector('ul[class="mb-4"]');
|
||||
var ul = main?.querySelectorAll('ul[class="mb-4"]');
|
||||
|
||||
List<String> mirrors = [];
|
||||
|
||||
if (ul != null) {
|
||||
var a = ul.querySelectorAll('a');
|
||||
var anchorTags = [];
|
||||
if (ul.length == 2) {
|
||||
anchorTags = ul[1].querySelectorAll('a');
|
||||
} else {
|
||||
anchorTags = ul[0].querySelectorAll('a');
|
||||
}
|
||||
|
||||
for (var element in a) {
|
||||
for (var element in anchorTags) {
|
||||
if (element.attributes['href']!.startsWith('https://')) {
|
||||
if (element.attributes['href'] != null) {
|
||||
mirrors.add(element.attributes['href']!);
|
||||
|
@ -1,15 +1,40 @@
|
||||
import 'dart:io';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
class Sqlite {
|
||||
static Future<Database> initDb() async {
|
||||
var databasesPath = await getDatabasesPath();
|
||||
String path = '$databasesPath/mylibrary.db';
|
||||
bool isMobile = Platform.isAndroid || Platform.isIOS;
|
||||
|
||||
Database dbInstance = await openDatabase(path, version: 1,
|
||||
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)');
|
||||
});
|
||||
Database dbInstance = await openDatabase(
|
||||
path,
|
||||
version: 3,
|
||||
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)');
|
||||
await db.execute(
|
||||
'CREATE TABLE preferences (name TEXT PRIMARY KEY,value BOOLEAN)');
|
||||
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']);
|
||||
List<dynamic> isPreferenceTableExist = await db.query('sqlite_master',
|
||||
where: 'name = ?', whereArgs: ['preferences']);
|
||||
if (isPreferenceTableExist.isEmpty) {
|
||||
await db.execute(
|
||||
'CREATE TABLE preferences (name TEXT PRIMARY KEY,value BOOLEAN)');
|
||||
}
|
||||
if (isMobile && isTableExist.isEmpty) {
|
||||
await db.execute(
|
||||
'CREATE TABLE bookposition (fileName TEXT PRIMARY KEY,position TEXT)');
|
||||
}
|
||||
},
|
||||
);
|
||||
return dbInstance;
|
||||
}
|
||||
}
|
||||
@ -118,4 +143,55 @@ class MyLibraryDb {
|
||||
});
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> savePreference(String name, bool value) async {
|
||||
int boolInt = value ? 1 : 0;
|
||||
await dbInstance.insert(
|
||||
'preferences',
|
||||
{'name': name, 'value': boolInt},
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> getPreference(String name) async {
|
||||
List<Map<String, dynamic>> data = await dbInstance
|
||||
.query('preferences', where: 'name = ?', whereArgs: [name]);
|
||||
List<dynamic> dataList = List.generate(data.length, (i) {
|
||||
return {'name': data[i]['name'], 'value': data[i]['value']};
|
||||
});
|
||||
if (dataList.isNotEmpty) {
|
||||
return dataList[0]['value'] == 0 ? false : true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'files.dart';
|
||||
|
||||
Future<String> _getFilePath(String fileName) async {
|
||||
final path = await getApplicationDocumentsDirectory();
|
||||
return '${path.path}/$fileName';
|
||||
final path = await getAppDirectoryPath;
|
||||
return '$path/$fileName';
|
||||
}
|
||||
|
||||
List<String> _reorderMirrors(List<String> mirrors) {
|
||||
@ -14,7 +14,8 @@ List<String> _reorderMirrors(List<String> mirrors) {
|
||||
if (element.contains('ipfs') == true) {
|
||||
ipfsMirrors.add(element);
|
||||
} else {
|
||||
if (element.startsWith('https://annas-archive.gs') != true) {
|
||||
if (element.startsWith('https://annas-archive.gs') != true &&
|
||||
element.startsWith('https://1lib.sk') != true) {
|
||||
httpsMirrors.add(element);
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,32 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:openlib/state/state.dart' show dbProvider, myLibraryProvider;
|
||||
|
||||
Future<String> get getAppDirectoryPath async {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
return directory.path;
|
||||
if (Platform.isAndroid) {
|
||||
final directory = await getExternalStorageDirectory();
|
||||
return directory!.path;
|
||||
} else {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
return directory.path;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> moveFilesToAndroidInternalStorage() async {
|
||||
try {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final directoryExternal = await getExternalStorageDirectory();
|
||||
List<FileSystemEntity> files = Directory(directory.path).listSync();
|
||||
for (var element in files) {
|
||||
if ((element.path.contains('pdf')) || element.path.contains('epub')) {
|
||||
String fileName = element.path.split('/').last;
|
||||
File file = File(element.path);
|
||||
file.copySync('${directoryExternal!.path}/$fileName');
|
||||
file.deleteSync();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> isFileExists(String filePath) async {
|
||||
@ -35,9 +59,11 @@ Future<void> deleteFileWithDbData(
|
||||
String appDirPath = await getAppDirectoryPath;
|
||||
await deleteFile('$appDirPath/$fileName');
|
||||
await ref.read(dbProvider).delete(md5);
|
||||
await ref.read(dbProvider).deleteBookState(fileName);
|
||||
// ignore: unused_result
|
||||
ref.refresh(myLibraryProvider);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
// print(e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:openlib/services/database.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
@ -31,6 +32,8 @@ Map<String, String> sortValues = {
|
||||
|
||||
final selectedIndexProvider = StateProvider<int>((ref) => 0);
|
||||
|
||||
final themeModeProvider = StateProvider<ThemeMode>((ref) => ThemeMode.light);
|
||||
|
||||
final selectedTypeState = StateProvider<String>((ref) => "All");
|
||||
|
||||
final getTypeValue = Provider.autoDispose<String>((ref) {
|
||||
@ -125,6 +128,25 @@ final deleteFileFromMyLib =
|
||||
final pdfCurrentPage = 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 openPdfWithExternalAppProvider = StateProvider<bool>((ref) => false);
|
||||
final openEpubWithExternalAppProvider = StateProvider<bool>((ref) => false);
|
||||
|
||||
final filePathProvider =
|
||||
FutureProvider.family<String, String>((ref, fileName) async {
|
||||
String path = await getFilePath(fileName);
|
||||
|
131
lib/ui/about_page.dart
Normal file
@ -0,0 +1,131 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'package:openlib/ui/components/snack_bar_widget.dart';
|
||||
import 'package:openlib/ui/components/page_title_widget.dart';
|
||||
|
||||
class AboutPage extends StatelessWidget {
|
||||
const AboutPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
body: const SingleChildScrollView(
|
||||
physics: BouncingScrollPhysics(),
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TitleText("About"),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 7, right: 7, top: 13, bottom: 10),
|
||||
child: Text(
|
||||
"An Open source app to download and read books from shadow library (Anna`s Archive)",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 7, right: 7, top: 10),
|
||||
child: Text(
|
||||
"Version",
|
||||
style: TextStyle(fontSize: 19, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 7, right: 7, top: 5),
|
||||
child: Text(
|
||||
"1.0.2",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 7, right: 7, top: 15),
|
||||
child: Text(
|
||||
"Github",
|
||||
style: TextStyle(fontSize: 19, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
_UrlText(
|
||||
text: 'Open Github Page',
|
||||
url: 'https://github.com/dstark5/Openlib',
|
||||
),
|
||||
_UrlText(
|
||||
text: 'Contribute To Openlib',
|
||||
url:
|
||||
'https://github.com/dstark5/Openlib/blob/main/CONTRIBUTING.md'),
|
||||
_UrlText(
|
||||
text: 'Report An Issue',
|
||||
url: 'https://github.com/dstark5/Openlib/issues'),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 7, right: 7, top: 15),
|
||||
child: Text(
|
||||
"Licence",
|
||||
style: TextStyle(fontSize: 19, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
_UrlText(
|
||||
text: "GPL v3.0 license",
|
||||
url: 'https://www.gnu.org/licenses/gpl-3.0.en.html'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _UrlText extends StatelessWidget {
|
||||
const _UrlText({Key? key, required this.text, required this.url})
|
||||
: super(key: key);
|
||||
|
||||
final String url;
|
||||
final String text;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 7, right: 7, top: 5),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
final Uri uri = Uri.parse(url);
|
||||
if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) {
|
||||
// ignore: use_build_context_synchronously
|
||||
showSnackBar(context: context, message: 'Could not launch $uri');
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blueAccent,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
const Icon(
|
||||
Icons.launch,
|
||||
size: 17,
|
||||
color: Colors.blueAccent,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -200,7 +200,7 @@ class _ShowDialog extends ConsumerWidget {
|
||||
height: 255,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).colorScheme.tertiaryContainer,
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
|
||||
child: SingleChildScrollView(
|
||||
@ -208,14 +208,14 @@ class _ShowDialog extends ConsumerWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
"Downloading Book",
|
||||
style: TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(255, 54, 54, 54),
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
decoration: TextDecoration.none),
|
||||
),
|
||||
),
|
||||
@ -223,10 +223,13 @@ class _ShowDialog extends ConsumerWidget {
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black54,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiary
|
||||
.withAlpha(170),
|
||||
decoration: TextDecoration.none),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
@ -259,7 +262,10 @@ class _ShowDialog extends ConsumerWidget {
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
child: LinearProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
backgroundColor: Colors.black26,
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiary
|
||||
.withAlpha(50),
|
||||
value: downloadProgress,
|
||||
minHeight: 4,
|
||||
),
|
||||
|
@ -81,10 +81,10 @@ class BookInfoCard extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
@ -94,7 +94,8 @@ class BookInfoCard extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: "#4D4D4D".toColor(),
|
||||
color:
|
||||
Theme.of(context).textTheme.headlineMedium?.color,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
@ -104,7 +105,7 @@ class BookInfoCard extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: "#7B7B7B".toColor(),
|
||||
color: Theme.of(context).textTheme.headlineSmall?.color,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:openlib/ui/extensions.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
|
||||
class BookInfoWidget extends StatelessWidget {
|
||||
@ -11,6 +10,9 @@ class BookInfoWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String description = data.description.toString().length < 3
|
||||
? "No Description available"
|
||||
: data.description.toString();
|
||||
return SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
scrollDirection: Axis.vertical,
|
||||
@ -65,28 +67,32 @@ class BookInfoWidget extends StatelessWidget {
|
||||
text: data.title,
|
||||
fontSize: 19,
|
||||
topPadding: 15,
|
||||
color: Colors.black,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
maxLines: 7,
|
||||
),
|
||||
_TopPaddedText(
|
||||
text: data.publisher ?? "unknown",
|
||||
fontSize: 15,
|
||||
topPadding: 7,
|
||||
color: "#595E60".toColor(),
|
||||
color: Theme.of(context).textTheme.headlineMedium!.color!,
|
||||
maxLines: 4,
|
||||
),
|
||||
_TopPaddedText(
|
||||
text: data.author ?? "unknown",
|
||||
fontSize: 13,
|
||||
topPadding: 7,
|
||||
color: "#7F7F7F".toColor(),
|
||||
color: Theme.of(context).textTheme.headlineSmall!.color!,
|
||||
maxLines: 3,
|
||||
),
|
||||
_TopPaddedText(
|
||||
text: data.info ?? "",
|
||||
fontSize: 11,
|
||||
topPadding: 9,
|
||||
color: "#A9A8A2".toColor(),
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineSmall!
|
||||
.color!
|
||||
.withAlpha(155),
|
||||
maxLines: 4,
|
||||
),
|
||||
// child slot of page
|
||||
@ -108,11 +114,12 @@ class BookInfoWidget extends StatelessWidget {
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 7, bottom: 10),
|
||||
child: Text(
|
||||
data.description ?? "",
|
||||
description,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: "#6B6B6B".toColor(),
|
||||
color:
|
||||
Theme.of(context).colorScheme.tertiary.withAlpha(150),
|
||||
letterSpacing: 1.5,
|
||||
),
|
||||
),
|
||||
|
@ -26,7 +26,7 @@ class ShowDeleteDialog extends ConsumerWidget {
|
||||
height: 219,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).colorScheme.tertiaryContainer,
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(20, 50, 20, 20),
|
||||
child: SingleChildScrollView(
|
||||
@ -34,25 +34,28 @@ class ShowDeleteDialog extends ConsumerWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
"Delete Book",
|
||||
style: TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(255, 54, 54, 54),
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
decoration: TextDecoration.none),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
"This is permanent and cannot be undone",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black54,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiary
|
||||
.withAlpha(170),
|
||||
decoration: TextDecoration.none),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
@ -88,14 +91,14 @@ class ShowDeleteDialog extends ConsumerWidget {
|
||||
|
||||
onDelete();
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(5.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Text(
|
||||
'Delete',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1,10 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:openlib/ui/components/delete_dialog_widget.dart';
|
||||
import 'package:openlib/ui/epub_viewer.dart';
|
||||
import 'package:openlib/ui/pdf_viewer.dart';
|
||||
import 'package:openlib/ui/epub_viewer.dart' show launchEpubViewer;
|
||||
import 'package:openlib/ui/pdf_viewer.dart' show launchPdfViewer;
|
||||
|
||||
class FileOpenAndDeleteButtons extends StatelessWidget {
|
||||
class FileOpenAndDeleteButtons extends ConsumerWidget {
|
||||
final String id;
|
||||
final String format;
|
||||
final Function onDelete;
|
||||
@ -17,7 +18,7 @@ class FileOpenAndDeleteButtons extends StatelessWidget {
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 21, bottom: 21),
|
||||
child: Row(
|
||||
@ -27,30 +28,19 @@ class FileOpenAndDeleteButtons extends StatelessWidget {
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
textStyle: const TextStyle(
|
||||
textStyle: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
)),
|
||||
onPressed: () => {
|
||||
if (format == 'pdf')
|
||||
{
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return PdfView(
|
||||
fileName: '$id.$format',
|
||||
);
|
||||
}))
|
||||
}
|
||||
else
|
||||
{
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return EpubViewerWidget(
|
||||
fileName: '$id.$format',
|
||||
);
|
||||
}))
|
||||
}
|
||||
onPressed: () async {
|
||||
if (format == 'pdf') {
|
||||
await launchPdfViewer(
|
||||
fileName: '$id.$format', context: context, ref: ref);
|
||||
} else {
|
||||
await launchEpubViewer(
|
||||
fileName: '$id.$format', context: context, ref: ref);
|
||||
}
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.fromLTRB(17, 8, 17, 8),
|
||||
@ -82,14 +72,14 @@ class FileOpenAndDeleteButtons extends StatelessWidget {
|
||||
);
|
||||
});
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(5.3),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.3),
|
||||
child: Text(
|
||||
'Delete',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1,9 +1,67 @@
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:epub_view/epub_view.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:vocsy_epub_viewer/epub_viewer.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
|
||||
import 'package:openlib/state/state.dart' show filePathProvider;
|
||||
import 'package:openlib/services/files.dart' show getFilePath;
|
||||
import 'package:openlib/ui/components/snack_bar_widget.dart';
|
||||
import 'package:openlib/state/state.dart'
|
||||
show
|
||||
filePathProvider,
|
||||
saveEpubState,
|
||||
dbProvider,
|
||||
getBookPosition,
|
||||
openEpubWithExternalAppProvider;
|
||||
|
||||
Future<void> launchEpubViewer(
|
||||
{required String fileName,
|
||||
required BuildContext context,
|
||||
required WidgetRef ref}) async {
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
String path = await getFilePath(fileName);
|
||||
String? epubConfig = await ref.read(dbProvider).getBookState(fileName);
|
||||
bool openWithExternalApp = ref.watch(openEpubWithExternalAppProvider);
|
||||
|
||||
if (openWithExternalApp) {
|
||||
await OpenFile.open(path);
|
||||
} else {
|
||||
try {
|
||||
VocsyEpub.setConfig(
|
||||
// ignore: use_build_context_synchronously
|
||||
themeColor: const Color.fromARGB(255, 210, 15, 1),
|
||||
identifier: "iosBook",
|
||||
scrollDirection: EpubScrollDirection.HORIZONTAL,
|
||||
);
|
||||
|
||||
if ((epubConfig?.isNotEmpty ?? true) &&
|
||||
(epubConfig != null) &&
|
||||
(!(epubConfig.startsWith('epubcfi')))) {
|
||||
VocsyEpub.open(path,
|
||||
lastLocation: EpubLocator.fromJson(json.decode(epubConfig)));
|
||||
} else {
|
||||
VocsyEpub.open(path);
|
||||
}
|
||||
|
||||
VocsyEpub.locatorStream.listen((locator) async {
|
||||
await saveEpubState(fileName, locator, ref);
|
||||
// convert locator from string to json and save to your database to be retrieved later
|
||||
});
|
||||
} catch (e) {
|
||||
// ignore: use_build_context_synchronously
|
||||
showSnackBar(context: context, message: 'Unable to open pdf!');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
|
||||
return EpubViewerWidget(
|
||||
fileName: fileName,
|
||||
);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
class EpubViewerWidget extends ConsumerStatefulWidget {
|
||||
const EpubViewerWidget({super.key, required this.fileName});
|
||||
@ -19,7 +77,7 @@ class _EpubViewState extends ConsumerState<EpubViewerWidget> {
|
||||
Widget build(BuildContext context) {
|
||||
final filePath = ref.watch(filePathProvider(widget.fileName));
|
||||
return filePath.when(data: (data) {
|
||||
return EpubViewer(filePath: data);
|
||||
return EpubViewer(filePath: data, fileName: widget.fileName);
|
||||
}, error: (error, stack) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
@ -37,30 +95,34 @@ class _EpubViewState extends ConsumerState<EpubViewerWidget> {
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
),
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class EpubViewer extends StatefulWidget {
|
||||
const EpubViewer({Key? key, required this.filePath}) : super(key: key);
|
||||
class EpubViewer extends ConsumerStatefulWidget {
|
||||
const EpubViewer({Key? key, required this.filePath, required this.fileName})
|
||||
: super(key: key);
|
||||
|
||||
final String filePath;
|
||||
final String fileName;
|
||||
|
||||
@override
|
||||
// ignore: library_private_types_in_public_api
|
||||
_EpubViewerState createState() => _EpubViewerState();
|
||||
}
|
||||
|
||||
class _EpubViewerState extends State<EpubViewer> {
|
||||
class _EpubViewerState extends ConsumerState<EpubViewer> {
|
||||
late EpubController _epubReaderController;
|
||||
String? epubConf;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -70,6 +132,14 @@ class _EpubViewerState extends State<EpubViewer> {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void deactivate() {
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
saveEpubState(widget.fileName, epubConf, ref);
|
||||
}
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_epubReaderController.dispose();
|
||||
@ -78,6 +148,7 @@ class _EpubViewerState extends State<EpubViewer> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final position = ref.watch(getBookPosition(widget.fileName));
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
@ -87,14 +158,48 @@ class _EpubViewerState extends State<EpubViewer> {
|
||||
endDrawer: Drawer(
|
||||
child: EpubViewTableOfContents(controller: _epubReaderController),
|
||||
),
|
||||
body: EpubView(
|
||||
onDocumentLoaded: (doc) {},
|
||||
onChapterChanged: (value) {},
|
||||
builders: EpubViewBuilders<DefaultBuilderOptions>(
|
||||
options: const DefaultBuilderOptions(),
|
||||
chapterDividerBuilder: (_) => const Divider(),
|
||||
),
|
||||
controller: _epubReaderController,
|
||||
body: position.when(
|
||||
data: (data) {
|
||||
return EpubView(
|
||||
onDocumentLoaded: (doc) {
|
||||
Future.delayed(const Duration(milliseconds: 20), () {
|
||||
String pos = data ?? "";
|
||||
_epubReaderController.gotoEpubCfi(pos);
|
||||
});
|
||||
},
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ class MyLibraryPage extends ConsumerWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: <Widget>[
|
||||
const SliverToBoxAdapter(
|
||||
child: TitleText("My Library"),
|
||||
|
@ -2,7 +2,35 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:openlib/state/state.dart'
|
||||
show filePathProvider, pdfCurrentPage, totalPdfPage;
|
||||
show
|
||||
filePathProvider,
|
||||
pdfCurrentPage,
|
||||
totalPdfPage,
|
||||
savePdfState,
|
||||
openPdfWithExternalAppProvider,
|
||||
getBookPosition;
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:openlib/services/files.dart' show getFilePath;
|
||||
|
||||
Future<void> launchPdfViewer(
|
||||
{required String fileName,
|
||||
required BuildContext context,
|
||||
required WidgetRef ref}) async {
|
||||
bool openWithExternalApp = ref.watch(openPdfWithExternalAppProvider);
|
||||
if (openWithExternalApp) {
|
||||
String path = await getFilePath(fileName);
|
||||
await OpenFile.open(path);
|
||||
} else {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
|
||||
return PdfView(
|
||||
fileName: fileName,
|
||||
);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
class PdfView extends ConsumerStatefulWidget {
|
||||
const PdfView({super.key, required this.fileName});
|
||||
@ -18,7 +46,7 @@ class _PdfViewState extends ConsumerState<PdfView> {
|
||||
Widget build(BuildContext context) {
|
||||
final filePath = ref.watch(filePathProvider(widget.fileName));
|
||||
return filePath.when(data: (data) {
|
||||
return PdfViewer(filePath: data);
|
||||
return PdfViewer(filePath: data, fileName: widget.fileName);
|
||||
}, error: (error, stack) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
@ -49,9 +77,11 @@ class _PdfViewState extends ConsumerState<PdfView> {
|
||||
}
|
||||
|
||||
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 fileName;
|
||||
|
||||
@override
|
||||
ConsumerState<ConsumerStatefulWidget> createState() => _PdfViewerState();
|
||||
@ -60,8 +90,44 @@ class PdfViewer extends ConsumerStatefulWidget {
|
||||
class _PdfViewerState extends ConsumerState<PdfViewer> {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
bool isMobile = Platform.isAndroid || Platform.isIOS;
|
||||
final currentPage = ref.watch(pdfCurrentPage);
|
||||
final totalPages = ref.watch(totalPdfPage);
|
||||
return Scaffold(
|
||||
@ -69,51 +135,109 @@ class _PdfViewerState extends ConsumerState<PdfViewer> {
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: const Text("Openlib"),
|
||||
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
if (currentPage != 0) {
|
||||
ref.read(pdfCurrentPage.notifier).state = currentPage - 1;
|
||||
controller.setPage(currentPage - 1);
|
||||
} else {
|
||||
ref.read(pdfCurrentPage.notifier).state = totalPages;
|
||||
controller.setPage(totalPages - 1);
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.arrow_left,
|
||||
size: 25,
|
||||
)),
|
||||
Text('${(currentPage + 1).toString()} / ${totalPages.toString()}'),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
if (currentPage + 1 < totalPages) {
|
||||
ref.read(pdfCurrentPage.notifier).state = currentPage + 1;
|
||||
controller.setPage(currentPage + 1);
|
||||
} else {
|
||||
ref.read(pdfCurrentPage.notifier).state = 0;
|
||||
controller.setPage(0);
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.arrow_right,
|
||||
size: 25,
|
||||
)),
|
||||
],
|
||||
),
|
||||
body: 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;
|
||||
},
|
||||
actions: isMobile
|
||||
? [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
if (currentPage != 0) {
|
||||
ref.read(pdfCurrentPage.notifier).state =
|
||||
currentPage - 1;
|
||||
controller.setPage(currentPage - 1);
|
||||
} else {
|
||||
ref.read(pdfCurrentPage.notifier).state = totalPages;
|
||||
controller.setPage(totalPages - 1);
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.arrow_left,
|
||||
size: 25,
|
||||
)),
|
||||
Text(
|
||||
'${(currentPage + 1).toString()} / ${totalPages.toString()}'),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
if (currentPage + 1 < totalPages) {
|
||||
ref.read(pdfCurrentPage.notifier).state =
|
||||
currentPage + 1;
|
||||
controller.setPage(currentPage + 1);
|
||||
} else {
|
||||
ref.read(pdfCurrentPage.notifier).state = 0;
|
||||
controller.setPage(0);
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.arrow_right,
|
||||
size: 25,
|
||||
)),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
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: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
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",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -42,12 +42,14 @@ class SearchPage extends ConsumerWidget {
|
||||
cursorColor: Theme.of(context).colorScheme.secondary,
|
||||
decoration: InputDecoration(
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black45, width: 2),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black54, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
@ -88,12 +90,14 @@ class SearchPage extends ConsumerWidget {
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black54, width: 2),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black54, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
),
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
@ -105,7 +109,8 @@ class SearchPage extends ConsumerWidget {
|
||||
value: value,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
style: const TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
@ -128,12 +133,14 @@ class SearchPage extends ConsumerWidget {
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black45, width: 2),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black54, width: 2),
|
||||
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
width: 2),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50)),
|
||||
),
|
||||
),
|
||||
value: dropdownSortValue,
|
||||
@ -144,7 +151,8 @@ class SearchPage extends ConsumerWidget {
|
||||
value: value,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
style: const TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
|
155
lib/ui/settings_page.dart
Normal file
@ -0,0 +1,155 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:openlib/ui/components/page_title_widget.dart';
|
||||
import 'package:openlib/ui/about_page.dart';
|
||||
import 'package:openlib/state/state.dart'
|
||||
show
|
||||
themeModeProvider,
|
||||
openPdfWithExternalAppProvider,
|
||||
openEpubWithExternalAppProvider,
|
||||
dbProvider;
|
||||
|
||||
class SettingsPage extends ConsumerWidget {
|
||||
const SettingsPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const TitleText("Settings"),
|
||||
_PaddedContainer(
|
||||
children: [
|
||||
Text(
|
||||
"Dark Mode",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
// This bool value toggles the switch.
|
||||
value: ref.watch(themeModeProvider) == ThemeMode.dark,
|
||||
activeColor: Colors.red,
|
||||
onChanged: (bool value) {
|
||||
ref.read(themeModeProvider.notifier).state =
|
||||
value == true ? ThemeMode.dark : ThemeMode.light;
|
||||
ref.read(dbProvider).savePreference('darkMode', value);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
_PaddedContainer(
|
||||
children: [
|
||||
Text(
|
||||
"Open PDF with External Reader",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
// This bool value toggles the switch.
|
||||
value: ref.watch(openPdfWithExternalAppProvider),
|
||||
activeColor: Colors.red,
|
||||
onChanged: (bool value) {
|
||||
ref.read(openPdfWithExternalAppProvider.notifier).state =
|
||||
value;
|
||||
ref
|
||||
.read(dbProvider)
|
||||
.savePreference('openPdfwithExternalApp', value);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
_PaddedContainer(
|
||||
children: [
|
||||
Text(
|
||||
"Open Epub with External Reader",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
// This bool value toggles the switch.
|
||||
value: ref.watch(
|
||||
openEpubWithExternalAppProvider,
|
||||
),
|
||||
activeColor: Colors.red,
|
||||
onChanged: (bool value) {
|
||||
ref.read(openEpubWithExternalAppProvider.notifier).state =
|
||||
value;
|
||||
ref
|
||||
.read(dbProvider)
|
||||
.savePreference('openEpubwithExternalApp', value);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
_PaddedContainer(
|
||||
onClick: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return const AboutPage();
|
||||
}));
|
||||
},
|
||||
children: [
|
||||
Text(
|
||||
"About",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PaddedContainer extends StatelessWidget {
|
||||
const _PaddedContainer({Key? key, this.onClick, required this.children})
|
||||
: super(key: key);
|
||||
|
||||
final VoidCallback? onClick;
|
||||
final List<Widget> children;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: InkWell(
|
||||
onTap: onClick,
|
||||
child: Container(
|
||||
height: 61,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Theme.of(context).colorScheme.tertiaryContainer,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
72
lib/ui/themes.dart
Normal file
@ -0,0 +1,72 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:openlib/ui/extensions.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
ThemeData lightTheme = ThemeData(
|
||||
primaryColor: Colors.white,
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: Colors.white,
|
||||
secondary: '#FB0101'.toColor(),
|
||||
tertiary: Colors.black,
|
||||
tertiaryContainer: '#F2F2F2'.toColor(),
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
displayLarge: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 21,
|
||||
),
|
||||
displayMedium: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
headlineMedium: TextStyle(
|
||||
color: "#595E60".toColor(),
|
||||
),
|
||||
headlineSmall: TextStyle(
|
||||
color: "#7F7F7F".toColor(),
|
||||
)),
|
||||
fontFamily: GoogleFonts.nunito().fontFamily,
|
||||
useMaterial3: true,
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
selectionColor: '#FB0101'.toColor(),
|
||||
selectionHandleColor: '#FB0101'.toColor(),
|
||||
),
|
||||
);
|
||||
|
||||
ThemeData darkTheme = ThemeData(
|
||||
primaryColor: Colors.black,
|
||||
colorScheme: ColorScheme.dark(
|
||||
primary: Colors.black,
|
||||
secondary: '#FB0101'.toColor(),
|
||||
tertiary: Colors.white,
|
||||
tertiaryContainer: '#2B2B2B'.toColor(),
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
displayLarge: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 21,
|
||||
),
|
||||
displayMedium: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
headlineMedium: TextStyle(
|
||||
color: "#F5F5F5".toColor(),
|
||||
),
|
||||
headlineSmall: TextStyle(
|
||||
color: "#E8E2E2".toColor(),
|
||||
),
|
||||
),
|
||||
fontFamily: GoogleFonts.nunito().fontFamily,
|
||||
useMaterial3: true,
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
selectionColor: '#FB0101'.toColor(),
|
||||
selectionHandleColor: '#FB0101'.toColor(),
|
||||
),
|
||||
);
|
@ -21,6 +21,7 @@ class TrendingPage extends ConsumerWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
const SliverToBoxAdapter(
|
||||
child: TitleText("Trending"),
|
||||
@ -45,10 +46,9 @@ class TrendingPage extends ConsumerWidget {
|
||||
);
|
||||
}));
|
||||
},
|
||||
child: Container(
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
color: const Color.fromARGB(255, 255, 255, 255),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
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);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
@ -7,8 +7,10 @@ import Foundation
|
||||
|
||||
import path_provider_foundation
|
||||
import sqflite
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
}
|
||||
|
257
pubspec.lock
@ -5,10 +5,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
|
||||
sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.7"
|
||||
version: "3.3.8"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -93,10 +93,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
version: "1.17.2"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -125,10 +125,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
||||
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
version: "1.0.6"
|
||||
dev:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -141,10 +141,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: "3866d67f93523161b643187af65f5ac08bc991a5bcdaf41a2d587fe4ccb49993"
|
||||
sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.0"
|
||||
version: "5.3.2"
|
||||
epub_view:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -181,10 +181,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.1.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -234,10 +234,10 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
|
||||
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.0.3"
|
||||
flutter_pdfview:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -250,10 +250,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_riverpod
|
||||
sha256: b83ac5827baadefd331ea1d85110f34645827ea234ccabf53a655f41901a9bf4
|
||||
sha256: b04d4e9435a563673746ccb328d22018c6c9496bb547e11dd56c1b0cc9829fe5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.6"
|
||||
version: "2.3.10"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -267,6 +267,11 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
gestures:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -359,18 +364,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
version: "0.12.16"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.5.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -387,6 +392,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
open_file:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: open_file
|
||||
sha256: a5a32d44acb7c899987d0999e1e3cbb0a0f1adebbf41ac813ec6d2d8faa0af20
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.2"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -407,50 +420,90 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
|
||||
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.15"
|
||||
version: "2.1.1"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
|
||||
sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.27"
|
||||
version: "2.2.0"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297"
|
||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4"
|
||||
version: "2.3.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.11"
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
|
||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
version: "2.1.1"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
|
||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.7"
|
||||
version: "2.2.1"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.4.3"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: d74e77a5ecd38649905db0a7d05ef16bed42ff263b9efb73ed794317c5764ec3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.3.4"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.1.4"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.11.3"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -463,18 +516,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
||||
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.1.2"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
||||
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.6"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -495,10 +548,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: riverpod
|
||||
sha256: "80e48bebc83010d5e67a11c9514af6b44bbac1ec77b4333c8ea65dbc79e2d8ef"
|
||||
sha256: "6c0a2c30c04206ac05494bcccd8148b76866e1a9248a5a8c84ca7b16fbcb3f6a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.6"
|
||||
version: "2.3.10"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -524,10 +577,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sprintf
|
||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
sqflite:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -544,6 +605,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -556,10 +633,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: state_notifier
|
||||
sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289"
|
||||
sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2+1"
|
||||
version: "1.0.0"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -596,10 +673,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.6.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -616,14 +693,78 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.14"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.20"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.8"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
|
||||
sha256: e03928880bdbcbf496fb415573f5ab7b1ea99b9b04f669c01104d085893c3134
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
version: "4.0.0"
|
||||
vector_graphics:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -656,22 +797,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vocsy_epub_viewer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: vocsy_epub_viewer
|
||||
sha256: "30aab4a5be22e8c98f22fed3345f6989caaca861178c38cab2a8b8cd66ad53de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.4-beta"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee
|
||||
sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.5"
|
||||
version: "5.0.7"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff
|
||||
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "1.0.3"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -689,5 +846,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.0.5 <4.0.0"
|
||||
flutter: ">=3.7.0-0"
|
||||
dart: ">=3.1.0 <4.0.0"
|
||||
flutter: ">=3.13.0"
|
||||
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.0+1
|
||||
version: 1.0.2+1
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.5 <4.0.0'
|
||||
@ -36,6 +36,7 @@ dependencies:
|
||||
|
||||
epub_view: ^3.2.0
|
||||
flutter_pdfview: ^1.2.7
|
||||
vocsy_epub_viewer: ^2.0.0
|
||||
# syncfusion_flutter_pdfviewer: ^22.2.5
|
||||
# pdfx: ^2.4.0
|
||||
|
||||
@ -44,11 +45,16 @@ dependencies:
|
||||
|
||||
sqflite: ^2.3.0
|
||||
path_provider: ^2.0.15
|
||||
permission_handler: ^10.4.3
|
||||
open_file: ^3.3.2
|
||||
|
||||
flutter_svg: ^2.0.7
|
||||
google_fonts: ^5.1.0
|
||||
|
||||
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.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
|
@ -6,6 +6,12 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
permission_handler_windows
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|