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

pub package

Showcase

PdfViewPinch PdfView

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:pdfx/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

Parameter Description Default
document The document to be displayed -
initialPage The page to show when first creating the PdfView 1
viewportFraction The fraction of the viewport that each page should occupy. 1.0

PdfView & PdfViewPinch

Parameter Description PdfViewPinch / PdfView
controller Pages control. See page control and additional pdf info + / +
onPageChanged Called whenever the page in the center of the viewport changes. See Document callbacks + / +
onDocumentLoaded Called when a document is loaded. See Document callbacks + / +
onDocumentError Called when a document loading error. Exception is passed in the attributes + / +
builders Set of pdf view builders. See Custom builders + / +
scrollDirection Page turning direction + / +
reverse Reverse scroll direction, useful for RTL support - / +
renderer Custom PdfRenderer options. See custom renderer options - / +
pageSnapping Set to false for mouse wheel scroll on web - / +
physics How the widgets should respond to user input - / +
padding Padding for the every page. + / -

PdfViewBuilders & PdfViewPinchBuilders

Parameter Description PdfViewPinchBuilders / PdfViewBuilders
options Additional options for builder + / +
documentLoaderBuilder Widget showing when pdf document loading + / +
pageLoaderBuilder Widget showing when pdf page loading + / +
errorBuilder Show document loading error message + / +
builder Root view builder for animate pdf loading state + / +
pageBuilder Callback 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

Parameter Description Default
sourceName Needed for toString method. Contains a method for opening a document (file, data or asset) -
id Document unique id. Generated when opening document. -
pagesCount All pages count in document. Starts from 1. -
isClosed Is 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](https://pub.dev/packages/internet_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

Parameter Description Default
document Parent document Parent
id Page unique id. Needed for rendering and closing page. Generated when opening page. -
width Page source width in pixels, int -
height Page source height in pixels, int -
isClosed Is the page closed false

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

Parameter Description Default
id Page unique id. Needed for rendering and closing page. Generated when render page. -
pageNumber Page number. The first page is 1. -
width Width of the rendered area in pixels, int -
height Height of the rendered area in pixels, int -
bytes Rendered image result, Uint8List -
format Rendered image compression format, for web always PNG PdfPageImageFormat.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

Libraries

pdfx_plugin