Updated PDF library

This commit is contained in:
Hillel Coren 2020-08-16 16:29:55 +03:00
parent 28ed4f8092
commit 6830619f75
4 changed files with 128 additions and 231 deletions

View File

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:typed_data';
import 'dart:ui';
import 'package:built_collection/built_collection.dart';
import 'package:flutter/foundation.dart';
@ -50,8 +51,7 @@ class _DesignEditState extends State<DesignEdit>
FocusScopeNode _focusNode;
TabController _tabController;
PDFPageImage _pdfPageImage;
String _pdfString;
Uint8List _pdfBytes;
bool _isLoading = false;
List<TextEditingController> _controllers;
@ -80,11 +80,11 @@ class _DesignEditState extends State<DesignEdit>
final design = widget.viewModel.design;
_nameController.text = design.name;
_headerController.text = design.getSection(kDesignHeader); //design.design;
_footerController.text = design.getSection(kDesignFooter); //design.design;
_bodyController.text = design.getSection(kDesignBody); //design.design;
_headerController.text = design.getSection(kDesignHeader);
_footerController.text = design.getSection(kDesignFooter);
_bodyController.text = design.getSection(kDesignBody);
_productsController.text =
design.getSection(kDesignProducts); //design.design;
design.getSection(kDesignProducts);
_tasksController.text = design.getSection(kDesignTasks);
_includesController.text = design.getSection(kDesignIncludes);
@ -156,28 +156,12 @@ class _DesignEditState extends State<DesignEdit>
return;
}
if (response == null) {
setState(() {
_isLoading = false;
});
} else if (kIsWeb) {
setState(() {
_isLoading = false;
_pdfString = 'data:application/pdf;base64,' +
base64Encode(response.bodyBytes);
});
} else {
final document = await PDFDocument.openData(response.bodyBytes);
final page = await document.getPage(1);
final pageImage =
await page.render(width: page.width, height: page.height);
page.close();
setState(() {
_isLoading = false;
_pdfPageImage = pageImage;
});
}
setState(() {
_isLoading = false;
if (response != null) {
_pdfBytes = response.bodyBytes;
}
});
});
}
@ -234,8 +218,7 @@ class _DesignEditState extends State<DesignEdit>
onLoadDesign: _loadDesign,
),
DesignPreview(
pdfPageImage: _pdfPageImage,
pdfString: _pdfString,
pdfBytes: _pdfBytes,
isLoading: _isLoading,
),
DesignSection(textController: _headerController),
@ -292,8 +275,7 @@ class _DesignEditState extends State<DesignEdit>
),
Expanded(
child: DesignPreview(
pdfPageImage: _pdfPageImage,
pdfString: _pdfString,
pdfBytes: _pdfBytes,
isLoading: _isLoading,
),
),
@ -373,13 +355,12 @@ class DesignSettings extends StatelessWidget {
class DesignPreview extends StatefulWidget {
const DesignPreview({
@required this.pdfString,
@required this.pdfPageImage,
@required this.pdfBytes,
@required this.isLoading,
});
final String pdfString;
final PDFPageImage pdfPageImage;
final Uint8List pdfBytes;
final bool isLoading;
@override
@ -387,41 +368,30 @@ class DesignPreview extends StatefulWidget {
}
class _DesignPreviewState extends State<DesignPreview> {
double _scrollPosition = 0;
final _scrollController = ScrollController(
//initialScrollOffset: 0,
keepScrollOffset: true,
);
PdfController _pdfController;
@override
void initState() {
super.initState();
_scrollController.addListener(onScrolled);
}
void onScrolled() {
_scrollPosition = _scrollController.offset;
}
String get _pdfString =>
'data:application/pdf;base64,' + base64Encode(widget.pdfBytes);
@override
void didUpdateWidget(oldWidget) {
super.didUpdateWidget(oldWidget);
if (kIsWeb) {
WebUtils.registerWebView(widget.pdfString);
}
if (_scrollController.hasClients && _scrollPosition > 0) {
WidgetsBinding.instance.addPostFrameCallback((duration) {
_scrollController.jumpTo(_scrollPosition);
});
WebUtils.registerWebView(_pdfString);
} else {
final document = PdfDocument.openData(widget.pdfBytes);
if (_pdfController == null) {
_pdfController = PdfController(document: document);
} else {
_pdfController.loadDocument(document);
}
}
}
@override
void dispose() {
_scrollController.removeListener(onScrolled);
_scrollController.dispose();
_pdfController.dispose();
super.dispose();
}
@ -434,26 +404,19 @@ class _DesignPreviewState extends State<DesignPreview> {
child: Stack(
alignment: Alignment.center,
children: <Widget>[
if (widget.pdfPageImage != null)
SingleChildScrollView(
controller: _scrollController,
padding: const EdgeInsets.all(6),
child: Card(
elevation: 6,
child: ExtendedImage.memory(
widget.pdfPageImage.bytes,
fit: BoxFit.fitHeight,
alignment: Alignment.topCenter,
),
if (kIsWeb)
HtmlElementView(viewType: _pdfString)
else if (_pdfController != null)
Padding(
padding: const EdgeInsets.all(8),
child: PdfView(
controller: _pdfController,
scrollDirection:
isMobile(context) ? Axis.vertical : Axis.horizontal,
),
)
else if (widget.pdfString != null)
HtmlElementView(viewType: widget.pdfString)
else
SizedBox(
width: double.infinity,
height: double.infinity,
),
SizedBox(),
if (widget.isLoading)
Column(
mainAxisSize: MainAxisSize.max,

View File

@ -13,29 +13,15 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart';
import 'package:invoiceninja_flutter/utils/dialogs.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:native_pdf_view/native_pdf_view.dart';
import 'package:path_provider/path_provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:native_pdf_renderer/native_pdf_renderer.dart';
import 'package:invoiceninja_flutter/utils/web_stub.dart'
if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart';
Future<Null> viewPdf(InvoiceEntity invoice, BuildContext context,
{String activityId}) async {
/*
final localization = AppLocalization.of(context);
if (Platform.isIOS) {
if (await canLaunch(invoice.invitationBorderlessLink)) {
await launch(invoice.invitationBorderlessLink,
forceSafariVC: true, forceWebView: true);
} else {
throw localization.anErrorOccurred;
}
return;
}
*/
showDialog<Scaffold>(
context: context,
builder: (BuildContext context) {
@ -58,8 +44,8 @@ class PDFScaffold extends StatefulWidget {
class _PDFScaffoldState extends State<PDFScaffold> {
String _pdfString;
List<PDFPageImage> _pdfImages;
http.Response _response;
PdfController _pdfController;
@override
void didChangeDependencies() {
@ -69,27 +55,29 @@ class _PDFScaffoldState extends State<PDFScaffold> {
setState(() {
_response = response;
});
if (kIsWeb) {
renderWebPDF(
context: context,
invoice: widget.invoice,
response: response,
).then((value) => setState(() {
_pdfString = value;
WebUtils.registerWebView(_pdfString);
}));
_pdfString =
'data:application/pdf;base64,' + base64Encode(response.bodyBytes);
WebUtils.registerWebView(_pdfString);
} else {
renderMobilePDF(
context: context,
invoice: widget.invoice,
response: response,
).then((value) => setState(() {
_pdfImages = value;
}));
final document = PdfDocument.openData(_response.bodyBytes);
if (_pdfController == null) {
_pdfController = PdfController(document: document);
} else {
_pdfController.loadDocument(document);
}
}
});
}
@override
void dispose() {
_pdfController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final store = StoreProvider.of<AppState>(context);
@ -97,85 +85,48 @@ class _PDFScaffoldState extends State<PDFScaffold> {
final invoice = widget.invoice;
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
centerTitle: false,
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
title: Text(localization.invoice + ' ' + (invoice.number ?? '')),
actions: <Widget>[
FlatButton(
child: Text(
localization.download,
style: TextStyle(color: store.state.headerTextColor),
),
onPressed: _response == null
? null
: () async {
if (kIsWeb) {
launch(invoice.invitationDownloadLink,
forceSafariVC: false, forceWebView: false);
} else {
final directory = await getExternalStorageDirectory();
final filePath =
'${directory.path}/${invoice.invoiceId}.pdf';
final pdfData = file.File(filePath);
pdfData.writeAsBytes(_response.bodyBytes);
await FlutterShare.shareFile(
title: 'test.pdf',
filePath: filePath);
}
},
backgroundColor: Colors.grey,
appBar: AppBar(
centerTitle: false,
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
],
),
body: kIsWeb
? _pdfString == null
? LoadingIndicator()
: _pdfString == ''
? SizedBox()
: HtmlElementView(viewType: _pdfString)
: _pdfImages == null
? LoadingIndicator()
: _pdfImages.isEmpty
? SizedBox()
: Container(
color: Colors.grey,
padding: const EdgeInsets.symmetric(vertical: 16),
child: _pdfImages.length == 1
? Center(
child: Container(
color: Colors.white,
child: Image(
image: MemoryImage(_pdfImages.first.bytes),
height: double.infinity),
),
)
: ListView(
scrollDirection: Axis.horizontal,
children: _pdfImages
.map((page) => Row(
children: <Widget>[
Container(
width: 20,
height: double.infinity,
color: Colors.grey,
),
Container(
color: Colors.white,
child: ExtendedImage.memory(
page.bytes,
fit: BoxFit.fitHeight,
),
),
],
))
.toList(),
),
),
);
title: Text(localization.invoice + ' ' + (invoice.number ?? '')),
actions: <Widget>[
FlatButton(
child: Text(
localization.download,
style: TextStyle(color: store.state.headerTextColor),
),
onPressed: _response == null
? null
: () async {
if (kIsWeb) {
launch(invoice.invitationDownloadLink,
forceSafariVC: false, forceWebView: false);
} else {
final directory = await getExternalStorageDirectory();
final filePath =
'${directory.path}/${invoice.invoiceId}.pdf';
final pdfData = file.File(filePath);
pdfData.writeAsBytes(_response.bodyBytes);
await FlutterShare.shareFile(
title: 'test.pdf', filePath: filePath);
}
},
),
],
),
body: _pdfString == null && _pdfController == null
? LoadingIndicator()
: kIsWeb
? HtmlElementView(viewType: _pdfString)
: PdfView(
controller: _pdfController,
scrollDirection:
isMobile(context) ? Axis.vertical : Axis.horizontal,
));
}
}
@ -205,37 +156,3 @@ Future<Response> _loadPDF(
return response;
}
Future<String> renderWebPDF({
@required BuildContext context,
@required http.Response response,
@required InvoiceEntity invoice,
}) async {
if (response == null) {
return '';
}
return 'data:application/pdf;base64,' + base64Encode(response.bodyBytes);
}
Future<List<PDFPageImage>> renderMobilePDF({
@required BuildContext context,
@required http.Response response,
@required InvoiceEntity invoice,
}) async {
final List<PDFPageImage> pages = [];
if (response == null) {
return pages;
}
final document = await PDFDocument.openData(response.bodyBytes);
for (var i = 1; i <= document.pagesCount; i++) {
final page = await document.getPage(i);
final pageImage = await page.render(width: page.width, height: page.height);
pages.add(pageImage);
page.close();
}
return pages;
}

View File

@ -219,12 +219,12 @@ packages:
source: hosted
version: "0.4.2+6"
extended_image:
dependency: "direct overridden"
dependency: transitive
description:
name: extended_image
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.3-dev"
version: "1.1.0"
extended_image_library:
dependency: transitive
description:
@ -238,7 +238,7 @@ packages:
name: extension
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.5"
version: "0.1.1"
fake_async:
dependency: transitive
description:
@ -488,6 +488,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.14"
image_picker:
dependency: "direct main"
description:
@ -592,14 +599,14 @@ packages:
name: native_pdf_renderer
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "2.3.2"
native_pdf_view:
dependency: "direct main"
description:
name: native_pdf_view
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
version: "3.8.0"
node_interop:
dependency: transitive
description:
@ -677,6 +684,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
platform:
dependency: transitive
description:
@ -1010,7 +1024,7 @@ packages:
name: url_launcher_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
version: "0.1.2+1"
usage:
dependency: transitive
description:
@ -1095,6 +1109,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "4.3.0"
yaml:
dependency: transitive
description:
@ -1104,4 +1125,4 @@ packages:
version: "2.2.1"
sdks:
dart: ">=2.10.0-0.0.dev <2.10.0"
flutter: ">=1.12.13+hotfix.5 <2.0.0"
flutter: ">=1.17.0 <2.0.0"

View File

@ -38,7 +38,7 @@ dependencies:
flutter_json_widget: ^1.0.2
webview_flutter: ^0.3.19+8
timeago: ^2.0.26
native_pdf_view: ^2.2.0
native_pdf_view: ^3.8.0
#flutter_typeahead: 1.8.0
flutter_typeahead:
git:
@ -51,10 +51,6 @@ dependencies:
version: ^1.0.0
#idb_shim: ^1.11.1+1
dependency_overrides:
# Error: No named parameter with the name 'animation'
extended_image: 0.7.3-dev
dev_dependencies:
flutter_driver:
sdk: flutter