PDFx: Flutter Render and show PDF documents

PDFx

Flutter Render & show PDF documents on Web, MacOs 10.11+, Android 5.0+, iOS and Windows.

Includes 2 api:

  • renderer Work with Pdf document, pages, render page to image
  • viewer Set of flutter widgets & controllers for show renderer result

Showcase

PdfViewPinchPdfView

Getting Started

In your flutter project add the dependency:

flutter pub add pdfx

For web run tool for automatically add pdfjs library (CDN) in index.html:

flutter pub run pdfx:install_web

For windows run tool automatically add override for pdfium version property in CMakeLists.txt file:

flutter pub run pdfx:install_windows

Usage example

import 'package:pdf_view/pdfx.dart';

final pdfPinchController = PdfControllerPinch(
  document: PdfDocument.openAsset('assets/sample.pdf'),
);

// Pdf view with re-render pdf texture on zoom (not loose quality on zoom)
// Not supported on windows
PdfViewPinch(
  controller: pdfPinchController,
);

//-- or --//

final pdfController = PdfController(
  document: PdfDocument.openAsset('assets/sample.pdf'),
);

// Simple Pdf view with one render of page (loose quality on zoom)
PdfView(
  controller: pdfController,
);

Viewer Api

PdfController & PdfControllerPinch

ParameterDescriptionDefault
documentThe document to be displayed-
initialPageThe page to show when first creating the [PdfView]1
viewportFractionThe fraction of the viewport that each page should occupy.1.0

PdfView & PdfViewPinch

ParameterDescriptionPdfViewPinch / PdfView
controllerPages control. See page control and additional pdf info+ / +
onPageChangedCalled whenever the page in the center of the viewport changes. See Document callbacks+ / +
onDocumentLoadedCalled when a document is loaded. See Document callbacks+ / +
onDocumentErrorCalled when a document loading error. Exception is passed in the attributes+ / +
buildersSet of pdf view builders. See Custom builders+ / +
scrollDirectionPage turning direction+ / +
rendererCustom PdfRenderer options. See custom renderer options- / +
pageSnappingSet to false for mouse wheel scroll on web- / +
physicsHow the widgets should respond to user input- / +
paddingPadding for the every page.+ / -

PdfViewBuilders & PdfViewPinchBuilders

ParameterDescriptionPdfViewPinchBuilders / PdfViewBuilders
optionsAdditional options for builder+ / +
documentLoaderBuilderWidget showing when pdf document loading+ / +
pageLoaderBuilderWidget showing when pdf page loading+ / +
errorBuilderShow document loading error message+ / +
builderRoot view builder for animate pdf loading state+ / +
pageBuilderCallback called to render a widget for each page. See custom page builder- / +

Additional examples

Open another document

pdfController.openDocument(PdfDocument.openAsset('assets/sample.pdf'));

Page control:

// Jump to specified page
pdfController.jumpTo(3);

// Animate to specified page
_pdfController.animateToPage(3, duration: Duration(milliseconds: 250), curve: Curves.ease);

// Animate to next page 
_pdfController.nextPage(duration: Duration(milliseconds: 250), curve: Curves.easeIn);

// Animate to previous page
_pdfController.previousPage(duration: Duration(milliseconds: 250), curve: Curves.easeOut);

Additional pdf info:

// Actual showed page
pdfController.page;

// Count of all pages in document
pdfController.pagesCount;

Document callbacks

PdfView(
  controller: pdfController,
  onDocumentLoaded: (document) {},
  onPageChanged: (page) {},
);

Show actual page number & all pages count

PdfPageNumber(
  controller: _pdfController,
  // When `loadingState != PdfLoadingState.success`  `pagesCount` equals null_
  builder: (_, state, loadingState, pagesCount) => Container(
    alignment: Alignment.center,
    child: Text(
      '$page/${pagesCount ?? 0}',
      style: const TextStyle(fontSize: 22),
    ),
  ),
)

Custom renderer options

PdfView(
  controller: pdfController,
  renderer: (PdfPage page) => page.render(
    width: page.width * 2,
    height: page.height * 2,
    format: PdfPageImageFormat.jpeg,
    backgroundColor: '#FFFFFF',
  ),
);

Custom builders

// Need static methods for builders arguments
class SomeWidget {
  static Widget builder(
    BuildContext context,
    PdfViewPinchBuilders builders,
    PdfLoadingState state,
    WidgetBuilder loadedBuilder,
    PdfDocument? document,
    Exception? loadingError,
  ) {
    final Widget content = () {
      switch (state) {
        case PdfLoadingState.loading:
          return KeyedSubtree(
            key: const Key('pdfx.root.loading'),
            child: builders.documentLoaderBuilder?.call(context) ??
                const SizedBox(),
          );
        case PdfLoadingState.error:
          return KeyedSubtree(
            key: const Key('pdfx.root.error'),
            child: builders.errorBuilder?.call(context, loadingError!) ??
                Center(child: Text(loadingError.toString())),
          );
        case PdfLoadingState.success:
          return KeyedSubtree(
            key: Key('pdfx.root.success.${document!.id}'),
            child: loadedBuilder(context),
          );
      }
    }();

    final defaultBuilder =
        builders as PdfViewPinchBuilders<DefaultBuilderOptions>;
    final options = defaultBuilder.options;

    return AnimatedSwitcher(
      duration: options.loaderSwitchDuration,
      transitionBuilder: options.transitionBuilder,
      child: content,
    );
  }

  static Widget transitionBuilder(Widget child, Animation<double> animation) =>
      FadeTransition(opacity: animation, child: child);

  static PhotoViewGalleryPageOptions pageBuilder(
    BuildContext context,
    Future<PdfPageImage> pageImage,
    int index,
    PdfDocument document,
  ) =>
      PhotoViewGalleryPageOptions(
        imageProvider: PdfPageImageProvider(
          pageImage,
          index,
          document.id,
        ),
        minScale: PhotoViewComputedScale.contained * 1,
        maxScale: PhotoViewComputedScale.contained * 3.0,
        initialScale: PhotoViewComputedScale.contained * 1.0,
        heroAttributes: PhotoViewHeroAttributes(tag: '${document.id}-$index'),
      );
}

PdfViewPinch(
  controller: pdfPinchController,
  builders: PdfViewPinchBuilders<DefaultBuilderOptions>(
    options: const DefaultBuilderOptions(
      loaderSwitchDuration: const Duration(seconds: 1),
      transitionBuilder: SomeWidget.transitionBuilder,
    ),
    documentLoaderBuilder: (_) =>
        const Center(child: CircularProgressIndicator()),
    pageLoaderBuilder: (_) =>
        const Center(child: CircularProgressIndicator()),
    errorBuilder: (_, error) => Center(child: Text(error.toString())),
    builder: SomeWidget.builder,
  ),
)

PdfView(
  controller: pdfController,
  builders: PdfViewBuilders<DefaultBuilderOptions>(
    // All from `PdfViewPinch` and:
    pageBuilder: SomeWidget.pageBuilder,
  ),
);

Renderer Api

PdfDocument

ParameterDescriptionDefault
sourceNameNeeded for toString method. Contains a method for opening a document (file, data or asset)-
idDocument unique id. Generated when opening document.-
pagesCountAll pages count in document. Starts from 1.-
isClosedIs the document closed-

Local document open:

// From assets (Android, Ios, MacOs, Web)
final document = await PdfDocument.openAsset('assets/sample.pdf')

// From file (Android, Ios, MacOs)
final document = await PdfDocument.openFile('path/to/file/on/device')

// From data (Android, Ios, MacOs, Web)
final document = await PdfDocument.openData((FutureOr<Uint8List>) data)

Network document open:

Install [network_file] package (supports all platforms):

flutter pub add internet_file

And use it

import 'package:internet_file/internet_file.dart';

PdfDocument.openData(InternetFile.get('https://github.com/ScerIO/packages.flutter/raw/fd0c92ac83ee355255acb306251b1adfeb2f2fd6/packages/native_pdf_renderer/example/assets/sample.pdf'))

Open page:

final page = document.getPage(pageNumber); // Starts from 1

Close document:

document.close();

PdfPage

ParameterDescriptionDefault
documentParent documentParent
idPage unique id. Needed for rendering and closing page. Generated when opening page.-
widthPage source width in pixels, int-
heightPage source height in pixels, int-
isClosedIs the page closedfalse

Render image:

final pageImage = page.render(
  // rendered image width resolution, required
  width: page.width * 2,
  // rendered image height resolution, required
  height: page.height * 2,

  // Rendered image compression format, also can be PNG, WEBP*
  // Optional, default: PdfPageImageFormat.PNG
  // Web not supported
  format: PdfPageImageFormat.JPEG,

  // Image background fill color for JPEG
  // Optional, default '#ffffff'
  // Web not supported
  backgroundColor: '#ffffff',

  // Crop rect in image for render
  // Optional, default null
  // Web not supported
  cropRect: Rect.fromLTRB(left, top, right, bottom),
);

PdfPageImage

ParameterDescriptionDefault
idPage unique id. Needed for rendering and closing page. Generated when render page.-
pageNumberPage number. The first page is 1.-
widthWidth of the rendered area in pixels, int-
heightHeight of the rendered area in pixels, int-
bytesRendered image result, Uint8List-
formatRendered image compression format, for web always PNGPdfPageImageFormat.PNG

Close page: 
Before open new page android asks to close the past. 
If this is not done, the application may crash with an error

page.close();

* PdfPageImageFormat.WEBP support only on android

Rendering additional info

On Web

This plugin uses the PDF.js

On Android

This plugin uses the Android native PdfRenderer

On Ios & MacOs

This plugin uses the iOS & MacOs native CGPDFPage

On Windows

This plugin uses PDFium


Download details:

Author: K1yoshiSho
Source: https://github.com/K1yoshiSho/pdfx_plus

License: View license

#flutter #dart 

PDFx: Flutter Render and show PDF documents
8.85 GEEK