import 'dart:async'; import 'dart:convert'; import 'dart:io' as file; import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_share/flutter_share.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:http/http.dart'; import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/data/models/invoice_model.dart'; import 'package:flutter/foundation.dart'; import 'package:invoiceninja_flutter/data/web_client.dart'; 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:native_pdf_view/native_pdf_view.dart'; import 'package:path_provider/path_provider.dart'; import 'package:invoiceninja_flutter/utils/web_stub.dart' if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart'; Future viewPdf(InvoiceEntity invoice, BuildContext context, {String activityId}) async { showDialog( context: context, builder: (BuildContext context) { return PDFScaffold( invoice: invoice, activityId: activityId, ); }); } class PDFScaffold extends StatefulWidget { const PDFScaffold({@required this.invoice, this.activityId}); final InvoiceEntity invoice; final String activityId; @override _PDFScaffoldState createState() => _PDFScaffoldState(); } class _PDFScaffoldState extends State { String _pdfString; http.Response _response; PdfController _pdfController; int _pageNumber = 1, _pageCount = 1; @override void didChangeDependencies() { super.didChangeDependencies(); _loadPDF(context, widget.invoice, widget.activityId).then((response) { setState(() { _response = response; }); if (kIsWeb) { _pdfString = 'data:application/pdf;base64,' + base64Encode(response.bodyBytes); WebUtils.registerWebView(_pdfString); } else { 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(context); final localization = AppLocalization.of(context); 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: Row( children: [ Text(localization.invoice + ' ' + (invoice.number ?? '')), if (!kIsWeb && _pageCount > 1) ...[ Spacer(), IconButton( icon: Icon(Icons.navigate_before), onPressed: _pageNumber > 1 ? () => _pdfController.previousPage( duration: Duration( milliseconds: kDefaultAnimationDuration), curve: Curves.easeInOutCubic, ) : null, ), Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Text(localization.pdfPageInfo .replaceFirst(':current', '$_pageNumber') .replaceFirst(':total', '$_pageCount')), ), IconButton( icon: Icon(Icons.navigate_next), onPressed: _pageNumber < _pageCount ? () => _pdfController.nextPage( duration: Duration( milliseconds: kDefaultAnimationDuration), curve: Curves.easeInOutCubic, ) : null, ), Spacer(), ] ], ), actions: [ FlatButton( child: Text( localization.download, style: TextStyle(color: store.state.headerTextColor), ), onPressed: _response == null ? null : () async { final fileName = '${invoice.number}.pdf'; if (kIsWeb) { WebUtils.downloadBinaryFile( fileName, _response.bodyBytes); } 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: fileName, filePath: filePath); } }, ), ], ), body: _pdfString == null && _pdfController == null ? LoadingIndicator() : kIsWeb ? HtmlElementView(viewType: _pdfString) : Padding( padding: const EdgeInsets.all(8), child: PdfView( controller: _pdfController, onDocumentLoaded: (document) { setState(() { _pageCount = document.pagesCount; }); }, onPageChanged: (page) { setState(() { _pageNumber = page; }); }, ), )); } } Future _loadPDF( BuildContext context, InvoiceEntity invoice, String activityId) async { http.Response response; if (activityId != null) { final store = StoreProvider.of(context); final credential = store.state.credentials; response = await WebClient().get( '${credential.url}/activities/download_entity/$activityId', credential.token, rawResponse: true); } else { final invitation = invoice.invitations.first; final url = invitation.downloadLink; response = await http.Client().get(url); } if (response.statusCode >= 400) { showErrorDialog( context: context, message: '${response.statusCode}: ${response.reasonPhrase}'); return null; } return response; }