pdf_viewer_plus 2.0.0
pdf_viewer_plus: ^2.0.0 copied to clipboard
A PDF viewer with enhanced features including a thumbnail sidebar for easy navigation.
PDF Viewer Plus #
pdf_viewer_plus is a Flutter package that provides a comprehensive PDF viewer with a collapsible thumbnail sidebar, accessibility support, and intuitive navigation features.
Features #
- Complete PDF Viewer — View PDF documents with smooth page transitions
- Reading-Flow View —
PdfFlowView: pages stacked vertically as a content-sized block with no internal scroll, made to live inside your ownCustomScrollView/ListView; renders pages lazily and frees off-screen ones - Collapsible Thumbnail Sidebar — Navigate through pages using thumbnails, shown or hidden with a smooth animation
- Flexible Source Support — Load PDFs from URLs or local assets
- Double-Tap to Zoom — Double-tap anywhere to zoom in (2.5×) centred on the tap; double-tap again to reset
- Page Indicator — "current / total" overlay displayed at the bottom of the viewer
- Keyboard Navigation —
←/→arrow keys to change page - Resume from any page — Open the document at a specific page with
initialPage - Page change callback — React to page changes with
onPageChanged - Robust Error Handling — Clear on-screen (and screen-reader) error state for load/parse failures, with
onErrorcallback and customisableerrorBuilder - Accessibility — Full screen reader support (VoiceOver / TalkBack) with customisable semantic labels
- Customizable Appearance — Sidebar width, thumbnail height, background color, selected page decoration, and more
- Optimized Performance — PDF data loaded once and shared between viewer and thumbnails
Installation #
Add this to your pubspec.yaml:
dependencies:
pdf_viewer_plus: ^2.0.0
Then run:
flutter pub get
Usage #
import 'package:flutter/material.dart';
import 'package:pdf_viewer_plus/pdf_viewer_plus.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('PDF Viewer')),
body: PdfViewer(
pdfPath: 'https://example.com/sample.pdf',
initialPage: 3,
initialSidebarOpen: true,
onPageChanged: (page) => print('Now on page $page'),
),
);
}
}
Customization Options #
| Parameter | Type | Default | Description |
|---|---|---|---|
pdfPath |
String? |
one required | URL or asset path to the PDF file (provide this or pdfData) |
pdfData |
Uint8List? |
one required | Raw PDF bytes (provide this or pdfPath) |
initialPage |
int |
1 |
Page shown when the document first opens |
initialSidebarOpen |
bool |
false |
Whether the sidebar is initially open |
sidebarWidth |
double |
160 |
Width of the thumbnail sidebar |
thumbnailHeight |
double |
150 |
Height of each thumbnail |
sidebarBackgroundColor |
Color |
Colors.grey |
Background color of the sidebar |
selectedPageDecoration |
BoxDecoration? |
null |
Custom decoration for the selected thumbnail |
showZoomHint |
bool |
true |
Show the pinch-to-zoom hint animation on first load |
showPageIndicator |
bool |
true |
Show the "current / total" page overlay |
panAxis |
PanAxis |
PanAxis.free |
Constrains panning; PanAxis.aligned locks each gesture to its dominant axis |
backgroundColor |
Color |
Colors.grey |
Color shown behind and between the pages |
onPageChanged |
void Function(int)? |
null |
Called whenever the current page changes |
onScroll |
void Function(double)? |
null |
Reports the vertical scroll offset (view px, 0 = top) |
onError |
void Function(Object)? |
null |
Called when the document fails to load or parse |
errorBuilder |
Widget Function(BuildContext, Object)? |
icon + message | Custom widget shown on failure |
semanticLabels |
PdfSemanticLabels |
English defaults | Labels announced by screen readers |
Reading-flow view (PdfFlowView) #
The interactive PdfViewer owns its own scroll and pinch-to-zoom, which means it
cannot flow like the rest of a scrolling page (collapsing headers, content that
runs on, etc.). PdfFlowView is the alternative: it renders every page as a
vertical column of images, sized to the height of its content and with no
internal scrolling of its own. Drop it inside a scrolling parent — the parent
handles the scroll, and the widget never steals the gesture. It's the PDF
counterpart of a content-sized WebView with internal scrolling disabled.
Pages render lazily (only when they get close to the viewport) and their images
are released again when they scroll far off screen, so even a large document
stays memory-friendly. Tapping a page can, for example, open the interactive
PdfViewer for zooming.
CustomScrollView(
slivers: [
const SliverAppBar(title: Text('Document'), floating: true),
SliverToBoxAdapter(
child: PdfFlowView(
pdfPath: 'assets/sample.pdf',
pageSpacing: 12,
padding: const EdgeInsets.all(16),
backgroundColor: const Color(0xFFEEEEEE),
// 1-indexed — open the interactive zoom viewer for the tapped page.
onPageTap: (page) => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: PdfViewer(pdfPath: 'assets/sample.pdf', initialPage: page),
),
),
),
pagePlaceholderBuilder: (context, page) => const ColoredBox(
color: Color(0xFFDDDDDD),
),
),
),
],
)
| Parameter | Type | Default | Description |
|---|---|---|---|
pdfData / pdfPath |
Uint8List? / String? |
one required | PDF source — provide exactly one |
pageSpacing |
double |
8 |
Vertical space between pages |
padding |
EdgeInsets |
EdgeInsets.zero |
Padding around the column of pages |
backgroundColor |
Color? |
null |
Background behind pages / default placeholder |
onPageTap |
void Function(int)? |
null |
Called with the 1-indexed page when tapped |
pagePlaceholderBuilder |
Widget Function(BuildContext, int)? |
grey skeleton | Per-page placeholder while rendering |
cacheExtent |
double |
600 |
How far off-screen (px) a page stays rendered |
onError |
void Function(Object)? |
null |
Called when the document fails to load or parse |
errorBuilder |
Widget Function(BuildContext, Object)? |
error icon | Custom widget shown on failure |
Note:
PdfFlowViewmust not be wrapped in its own scrollable — it is a static, content-sized block by design. Put it inside the scrollable that owns the page (e.g.SliverToBoxAdapter).
Error handling #
Both PdfViewer and PdfFlowView handle failures uniformly — whether the PDF
fails to download / load from assets or turns out to be corrupt or encrypted
and fails inside the renderer. By default a clear error state is shown on screen
(and announced to screen readers). Use onError to react programmatically, and
errorBuilder to fully customise what is displayed:
PdfViewer(
pdfPath: 'assets/sample.pdf',
onError: (error) {
debugPrint('PDF failed: $error');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Impossible d\'ouvrir le document')),
);
},
errorBuilder: (context, error) => const Center(
child: Text('Le document est indisponible'),
),
),
Accessibility #
All interactive elements are fully labelled for screen readers. You can override every label by passing a custom PdfSemanticLabels:
PdfViewer(
pdfPath: '...',
semanticLabels: PdfSemanticLabels(
toggleSidebar: 'Afficher / masquer le panneau',
pageLabel: (page, total) => 'Page $page sur $total',
zoomHint: 'Pincez pour zoomer',
loadingDocument: 'Chargement du document…',
loadingThumbnails: 'Chargement des miniatures…',
thumbnailList: 'Miniatures des pages',
loadingThumbnailsError: 'Erreur de chargement des miniatures',
loadingDocumentError: 'Erreur de chargement du document',
),
),
Dependencies #
Contributing #
Contributions are welcome! Feel free to open an issue or submit a pull request.
License #
This package is distributed under the MIT License. See the LICENSE file for more information.
About UserAgents #
UserAgents is a company specialized in developing high-quality mobile applications with Flutter using Lean & Agile methodologies. If you need a solution tailored to your requirements, don't hesitate to contact us via email or through our contact form!
We'd be delighted to discuss your projects and support you in your development journey.