mirror of
https://github.com/dstark5/Openlib.git
synced 2025-05-19 15:36:46 +08:00
250 lines
7.8 KiB
Dart
250 lines
7.8 KiB
Dart
// Dart imports:
|
|
import 'dart:io' show Platform;
|
|
|
|
// Flutter imports:
|
|
import 'package:flutter/material.dart';
|
|
|
|
// Package imports:
|
|
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:open_file/open_file.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
// Project imports:
|
|
import 'package:openlib/services/files.dart' show getFilePath;
|
|
|
|
import 'package:openlib/state/state.dart'
|
|
show
|
|
filePathProvider,
|
|
pdfCurrentPage,
|
|
totalPdfPage,
|
|
savePdfState,
|
|
openPdfWithExternalAppProvider,
|
|
getBookPosition;
|
|
|
|
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, linuxByProcess: true);
|
|
} else {
|
|
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
|
|
return PdfView(
|
|
fileName: fileName,
|
|
);
|
|
}));
|
|
}
|
|
}
|
|
|
|
class PdfView extends ConsumerStatefulWidget {
|
|
const PdfView({super.key, required this.fileName});
|
|
|
|
final String fileName;
|
|
|
|
@override
|
|
ConsumerState<ConsumerStatefulWidget> createState() => _PdfViewState();
|
|
}
|
|
|
|
class _PdfViewState extends ConsumerState<PdfView> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final filePath = ref.watch(filePathProvider(widget.fileName));
|
|
return filePath.when(data: (data) {
|
|
return PdfViewer(filePath: data, fileName: widget.fileName);
|
|
}, error: (error, stack) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
title: const Text("Openlib"),
|
|
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
|
),
|
|
body: Center(child: Text(error.toString())),
|
|
);
|
|
}, loading: () {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
title: const Text("Openlib"),
|
|
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
|
),
|
|
body: Center(
|
|
child: SizedBox(
|
|
width: 25,
|
|
height: 25,
|
|
child: CircularProgressIndicator(
|
|
color: Theme.of(context).colorScheme.secondary,
|
|
),
|
|
)),
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
class PdfViewer extends ConsumerStatefulWidget {
|
|
const PdfViewer({super.key, required this.filePath, required this.fileName});
|
|
|
|
final String filePath;
|
|
final String fileName;
|
|
|
|
@override
|
|
ConsumerState<ConsumerStatefulWidget> createState() => _PdfViewerState();
|
|
}
|
|
|
|
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(
|
|
appBar: AppBar(
|
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
title: const Text("Openlib"),
|
|
titleTextStyle: Theme.of(context).textTheme.displayLarge,
|
|
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",
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|