Using PDFView to Display PDF Files in Flutter

In mobile app development, displaying PDF files is a common requirement. Whether you’re building a document reader, an e-book application, or an app that needs to show reports, being able to render PDF files is essential. In Flutter, this can be achieved using the flutter_pdfview package. This article guides you on how to use flutter_pdfview to display PDF files in your Flutter application effectively.

Why Display PDF Files in Flutter?

  • Document Display: Render various types of documents like user manuals, reports, and invoices directly within the app.
  • E-Book Integration: Develop e-book readers capable of displaying PDF format e-books.
  • Offline Accessibility: Allow users to access critical documents even without an internet connection by storing the PDF files locally.

Prerequisites

Before diving in, ensure you have:

  • Flutter SDK installed on your machine.
  • An IDE such as VS Code or Android Studio.
  • Basic knowledge of Flutter development.

Step-by-Step Guide to Using flutter_pdfview

Step 1: Add flutter_pdfview Dependency

Add the flutter_pdfview package to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  flutter_pdfview: ^3.2.0 # Use the latest version

Then, run flutter pub get to install the package.

Step 2: Import the Package

In your Dart file, import the flutter_pdfview package:

import 'package:flutter_pdfview/flutter_pdfview.dart';

Step 3: Basic PDF Display

To display a PDF file, you need to create a PDFView widget. Here’s a basic example to load a PDF file from an asset:

import 'package:flutter/material.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';

class PDFScreen extends StatefulWidget {
  @override
  _PDFScreenState createState() => _PDFScreenState();
}

class _PDFScreenState extends State {
  String assetPath = '';

  @override
  void initState() {
    super.initState();
    loadAsset();
  }

  Future loadAsset() async {
    // Load PDF file from assets
    final assetData = await rootBundle.load('assets/sample.pdf');
    final bytes = assetData.buffer.asUint8List();

    // Get the application documents directory
    final appDir = await getApplicationDocumentsDirectory();
    final file = File('${appDir.path}/sample.pdf');

    // Write the PDF data to the file
    await file.writeAsBytes(bytes, flush: true);

    setState(() {
      assetPath = file.path;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("PDF Viewer"),
      ),
      body: assetPath.isEmpty
          ? Center(child: CircularProgressIndicator())
          : PDFView(
              filePath: assetPath,
            ),
    );
  }
}

Ensure that the PDF file sample.pdf is in your assets directory, and that the assets directory is properly declared in your pubspec.yaml file:

flutter:
  assets:
    - assets/sample.pdf

Explanation:

  • The code loads a PDF from the assets folder using rootBundle.load.
  • It then writes the data to a temporary file in the app’s documents directory using path_provider.
  • The PDFView widget is then used to display the PDF file from the local path.

Step 4: Load PDF from Network

To load a PDF from a URL, use the PDFView widget with the fromUrl method. Here’s how:

import 'package:flutter/material.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';

class PDFScreen extends StatelessWidget {
  final String remotePDFpath = "https://www.africau.edu/images/default/sample.pdf"; // Replace with a valid PDF URL

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("PDF from URL"),
      ),
      body: PDFView(
        filePath: remotePDFpath,
        enableSwipe: true,
        swipeHorizontal: true,
        autoSpacing: false,
        pageFling: false,
      ),
    );
  }
}

Step 5: Handle PDF Loading States and Errors

Use PDFViewController to manage the PDF view, load pages, and handle errors:

import 'package:flutter/material.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';

class PDFScreen extends StatefulWidget {
  final String assetPath;

  PDFScreen({required this.assetPath});

  @override
  _PDFScreenState createState() => _PDFScreenState();
}

class _PDFScreenState extends State {
  bool _isLoading = true;
  PDFViewController? _pdfViewController;
  int _totalPages = 0;
  int _currentPage = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('PDF Viewer'),
        actions: [
          if (_totalPages > 0)
            Center(child: Text('$_currentPage/$_totalPages', style: TextStyle(fontSize: 18))),
          SizedBox(width: 16)
        ],
      ),
      body: Stack(
        children: [
          PDFView(
            filePath: widget.assetPath,
            enableSwipe: true,
            swipeHorizontal: false,
            autoSpacing: false,
            pageFling: true,
            onRender: (_pages) {
              setState(() {
                _totalPages = _pages!;
                _isLoading = false;
              });
            },
            onViewCreated: (PDFViewController vc) {
              _pdfViewController = vc;
            },
            onPageChanged: (int? page, int? total) {
              setState(() {
                _currentPage = page!;
              });
            },
          ),
          _isLoading ? Center(child: CircularProgressIndicator()) : Container(),
        ],
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            child: Icon(Icons.chevron_left, size: 30,),
            onPressed: () {
              if (_currentPage > 0) {
                _pdfViewController?.setPage(_currentPage - 1);
              }
            },
          ),
          SizedBox(width: 10),
          FloatingActionButton(
            child: Icon(Icons.chevron_right, size: 30,),
            onPressed: () {
              if (_currentPage < _totalPages) {
                _pdfViewController?.setPage(_currentPage + 1);
              }
            },
          ),
        ],
      ),
    );
  }
}

Key functionalities and explanations:

  • Loading State Management: Displays a CircularProgressIndicator while the PDF is loading using the _isLoading flag.
  • PDFViewController: Manages the PDF viewer, allowing programmatic control such as changing pages.
  • Total Pages and Current Page: Tracks and displays the current page number and total page count.
  • Page Navigation: Floating action buttons for navigating between pages.
  • Callbacks:
    • onRender: Called when the PDF is rendered, updating the total number of pages and setting the loading state to false.
    • onViewCreated: Called when the PDFView is created, providing the PDFViewController instance.
    • onPageChanged: Called when the page changes, updating the current page number.

Handling Errors

Add error handling to gracefully manage issues such as PDF loading failures:

PDFView(
  filePath: path,
  onError: (error) {
    print('PDF load error: $error');
    // Handle error appropriately
  },
  onPageError: (page, error) {
      print('Error on page $page: $error');
      // Handle error for a specific page
  }
)

Conclusion

Displaying PDF files in Flutter using the flutter_pdfview package is straightforward. Whether loading from assets or a remote URL, this guide provides the essential steps to get you started. By utilizing the PDFView widget along with handling states and errors, you can create a robust and user-friendly PDF viewing experience in your Flutter applications.