jv_easy_pdf_viewer

A Flutter plugin for handling PDF files on Android and iOS. PDF pages are rendered natively (PdfRenderer on Android, CGPDF on iOS) and exposed as ordinary Flutter widgets - no in-app viewer process, no WebView.

Maintained by @jitendravaishnavs. Originally forked from pdf_viewer.

Pub Package


Installation

flutter pub add jv_easy_pdf_viewer

This code produces the following view:

demo

Platform notes

  • Android - no permissions required; previews are written to the app's cache directory.
  • iOS - no permissions required; previews are written to the app's Caches directory. Text search is powered by PDFKit (iOS 11+).

Loading a PDF

PDFDocument is the entry point. Pick whichever loader matches your source:

// From assets
PDFDocument doc = await PDFDocument.fromAsset('assets/test.pdf');

// From a URL (cached on disk automatically)
PDFDocument doc = await PDFDocument.fromURL(
  'https://example.com/file.pdf',
  headers: {'Authorization': 'Bearer ...'},
);

// From a local file
PDFDocument doc = await PDFDocument.fromFile(File('/path/to/file.pdf'));

// From raw in-memory bytes
PDFDocument doc = await PDFDocument.fromBytes(myUint8List);

For URL loading with a download progress callback, use PDFDocument.fromURLWithDownloadProgress:

PDFDocument.fromURLWithDownloadProgress(
  'https://example.com/file.pdf',
  downloadProgress: (progress) {
    // progress.progress is a nullable 0.0-1.0 value from flutter_cache_manager.
  },
  onDownloadComplete: (PDFDocument doc) {
    // Use the document once the download finishes.
  },
);

By default, each loader clears the rendered page preview cache before opening a new document. Pass clearPreviewCache: false when you need multiple documents open at the same time, then call PDFDocument.clearPreviewCache() when you are done with those previews.

Loading individual pages

PDFPage page = await doc.get(page: 1);

Or stream every page in order:

await for (final page in doc.getAll()) {
  // ...
}

Pre-built viewer

The PDFViewer widget gives you a complete viewer with a page indicator, page-picker FAB, 4-button navigation bar, and (on iOS) a text search panel:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('PDF')),
    body: _isLoading
        ? const Center(child: CircularProgressIndicator())
        : PDFViewer(document: document),
  );
}

Viewer parameters

Parameter Default Description
document required The PDFDocument to display.
showIndicator true Show the "page / total" chip.
showPicker true Show the jump-to-page FAB.
showNavigation true Show the 4-button bottom bar.
showSearch true Show the search icon (opens the search overlay).
indicatorPosition topRight Where to place the page indicator chip.
enableSwipeNavigation true Allow swiping between pages.
scrollDirection horizontal Axis.horizontal or Axis.vertical.
lazyLoad true Render pages on demand rather than all at once.
controller null Provide your own PageController.
navigationBuilder null Replace the default bottom bar with a custom widget.
indicatorBuilder null Replace the default indicator chip with a custom widget.
progressIndicator null Custom loading widget shown while a page renders.
onPageChanged null Called with the 0-indexed page number on every swipe.
onZoomChanged null Called with the current scale factor on every pinch.
backgroundColor null Background color behind each page.
pickerButtonColor / pickerIconColor null Override the jump-to-page FAB colors.
numberPickerConfirmWidget Text('OK') Confirm button used in the page picker dialog.
tooltip PDFViewerTooltip() Labels for navigation and picker controls.
resizeToAvoidBottomInset true Passed to the internal Scaffold.
zoomSteps / minScale / maxScale / panLimit null Zoom and pan tuning.

Text search (iOS)

Tap the search icon (top-left) to open the inline search overlay:

  • Debounced auto-search - results appear 400 ms after you stop typing.
  • Aa toggle - case-sensitive matching.
  • W toggle - whole-word matching (uses \b regex boundaries via PDFKit).
  • Match navigation - previous/next buttons cycle through every match across all pages; the viewer jumps to the page automatically.
  • "N of M, X on page" counter - always shows where you are.
  • Results list - tap the expand icon to see all matches with a highlighted context snippet; tap any row to jump directly to that page.
  • Search history - the last 10 queries are shown when the field is empty and focused.

Android - text search is not yet supported. Calling search() returns null; the viewer shows an informational notice instead of crashing.

Using the API directly

// Returns null on Android, empty list when nothing found,
// or one PDFSearchResult per match on iOS.
final List<PDFSearchResult>? hits = await doc.search(
  'flutter',
  caseSensitive: false,
  wholeWord: false,
);

if (hits == null) {
  // platform does not support text search
} else {
  for (final hit in hits) {
    print('page ${hit.page}: ${hit.context}');
  }
}

Sharing / printing

PDFDocument.filePath exposes the absolute path of the file that the plugin is reading from. Pass it to share_plus, printing or any other package:

import 'package:share_plus/share_plus.dart';

await Share.shareXFiles([XFile(doc.filePath!)]);

Dependencies

Package Purpose
path_provider Resolving app-local file system paths.
flutter_cache_manager Caching and re-using PDFs downloaded from a URL.
numberpicker The "jump to page" picker dialog.
plugin_platform_interface Boilerplate for the platform-channel plugin.