Blog Post

How to Download and Display a PDF Document in Flutter with PSPDFKit

Illustration: How to Download and Display a PDF Document in Flutter with PSPDFKit

Our Flutter PDF SDK enables you to open and edit a local PDF document on Android and iOS. In this article, you’ll learn how to download a PDF file from the internet and then display and edit that file.

In this example, you’ll use a Dart package called Dio to download a PDF file from the internet. Then, you’ll use PSPDFKit for Flutter to view, annotate, and edit that file.

For this tutorial, you should have already followed the installation instructions for Flutter and have the environment correctly set up. You can doublecheck the requirements for running PSPDFKit for Flutter in our getting started guide. If you don’t already have Android Studio and Xcode installed, you’ll need to install those as well. If you’re in a hurry, you can use our ready-to-run example project and skip to the Running the Example section of the blog.

ℹ️ Note: If you encounter any issues, Flutter offers a useful command for detecting the most common environment problems. From your terminal app, type flutter doctor -v and flutter doctor will scan your system and provide a short description of the most common problems.

The Use Case

The use case is straightforward: You want to download a PDF file from a URL and then display it. You also want to optionally edit and annotate the file. In the video below, you can see how to first download a PDF file using Dio and then open it using Flutter PDF SDK.

Now, it’s time to look at the steps involved!

Setting Up the Project

First, you’ll set up a new project and configure it.

  1. Create an empty project — Use the flutter create command to set up a brand-new project for your example. If you already have a project you’re integrating PSPDFKit into, you can skip this step:

    flutter create --org com.pspdfkit.flutter_example pspdfkit_flutter_example
  2. Add the dependencies — Add the required dependencies to the pubspec.yaml file.

    Navigate to the newly created example folder:

    cd pspdfkit_flutter_example

    Open the pubspec.yaml file:

    open pubspec.yaml

    Then, add the following lines corresponding to the plugins you want to add. Here, the path_provider plugin helps fetch the temporary directory on both Android and iOS — you’ll be storing the downloaded PDF file in the temporary directory:

    dependencies:
      flutter:
        sdk: flutter
    + pspdfkit_flutter:
    + path_provider:
    + dio: ^4.0.0
  3. Fetch the dependencies — Run flutter packages get to fetch the dependencies.

Android and iOS Configuration

In addition to the steps above, you also have to configure both Android and iOS. This is detailed in the next sections.

Android Configuration

  1. Make sure you’re in the newly created Flutter project directory.

  2. Open the app’s Gradle build file, android/app/build.gradle:

    open android/app/build.gradle
  3. Modify the minimum SDK version and enable multidex. All this is done inside the android section:

    android {
    defaultConfig {
    -   minSdkVersion 16
    +   minSdkVersion 21
    +   multiDexEnabled true
    ...
    }

iOS Configuration

  1. Make sure you’re in the newly created Flutter project directory.

  2. Open the Xcode project’s workspace file:

    open ios/Runner.xcworkspace
  3. Set the iOS deployment target to 13.0 or higher.

    deployment-target

  4. Change View controller-based status bar appearance to YES in Info.plist.

    view-controller-appearance

  5. Open iOS/Podfile:

    open ios/Podfile
  6. In Podfile, update the platform to iOS 13 and add the PSPDFKit podspec:

    -# platform :ios, '9.0'
    +# platform :ios, '13.0'
    
    target 'Runner' do
      use_frameworks!
      use_modular_headers!
    
      flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
    + pod 'PSPDFKit', podspec:'https://customers.pspdfkit.com/pspdfkit-ios/latest.podspec'
    end

With that, you’ve finished configuring both platforms. Now, it’s time to actually start building the app!

Updating the Code

After setting up your project, you can get to the core part of the app. You’ll create a very simple app, with one button to download a file from the internet, and another button to display that file using PSPDFKit.

For the sake of simplicity, you won’t be adding elaborate error handling code or a mechanism to prevent users from downloading multiple copies of the file. Keep in mind that for production apps, you should definitely make sure that all possible error paths are taken care of. There should always be a recovery path (such as the ability to restart a download, delete a corrupted file, etc.) in case something goes wrong.

In the code snippet, there are comments in relevant places, which will help shed light on the reasoning behind the respective lines of code.

Open lib/main.dart and replace it with the following code snippet:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pspdfkit_flutter/src/main.dart';

// Filename of the PDF you'll download and save.
const fileName = '/pspdfkit-flutter-quickstart-guide.pdf';

// URL of the PDF file you'll download.
const imageUrl = 'https://pspdfkit.com/downloads' + fileName;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Download and Display a PDF',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Download and Display a PDF'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  // Track the progress of a downloaded file here.
  double progress = 0;

  // Track if the PDF was downloaded here.
  bool didDownloadPDF = false;

  // Show the progress status to the user.
  String progressString = 'File has not been downloaded yet.';

  // This method uses Dio to download a file from the given URL
  // and saves the file to the provided `savePath`.
  Future download(Dio dio, String url, String savePath) async {
    try {
      Response response = await dio.get(
        url,
        onReceiveProgress: updateProgress,
        options: Options(
            responseType: ResponseType.bytes,
            followRedirects: false,
            validateStatus: (status) { return status! < 500; }
            ),
      );
      var file = File(savePath).openSync(mode: FileMode.write);
      file.writeFromSync(response.data);
      await file.close();

	    // Here, you're catching an error and printing it. For production
	    // apps, you should display the warning to the user and give them a
	    // way to restart the download.
    } catch (e) {
      print(e);
    }
  }

  // You can update the download progress here so that the user is
  // aware of the long-running task.
  void updateProgress(done, total) {
    progress = done / total;
    setState(() {
      if (progress >= 1) {
        progressString = '✅ File has finished downloading. Try opening the file.';
        didDownloadPDF = true;
      } else {
        progressString = 'Download progress: ' + (progress * 100).toStringAsFixed(0) + '% done.';
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'First, download a PDF file. Then open it.',
            ),
            TextButton(
              // Here, you download and store the PDF file in the temporary
              // directory.
              onPressed: didDownloadPDF ? null : () async {
                  var tempDir = await getTemporaryDirectory();
                  download(Dio(), imageUrl, tempDir.path + fileName);
                },
              child: Text('Download a PDF file'),
            ),
            Text(
              progressString,
            ),
            TextButton(
              // Disable the button if no PDF is downloaded yet. Once the
              // PDF file is downloaded, you can then open it using PSPDFKit.
              onPressed: !didDownloadPDF ? null : () async {
                  var tempDir = await getTemporaryDirectory();
                  await Pspdfkit.present(tempDir.path + fileName);
                },
              child: Text('Open the downloaded file using PSPDFKit'),
            ),
          ],
        ),
      ),
    );
  }
}

With the code updated, it’s time to move on to the next step.

Running the Example

Finally, you can go ahead and run the example.

First check the emulators that flutter has by running:

flutter emulators

This command will output the list of the emulators, which should look something like the following:

apple_ios_simulator • iOS Simulator  • Apple  • ios
Pixel_4_API_30      • Pixel 4 API 30 • Google • android

To start the Android emulator, run the following command. Replace Pixel_4_API_30 with the title of the emulator you have on your machine:

flutter emulators --launch Pixel_4_API_30

To start the iOS simulator, run the following command:

flutter emulators --launch apple_ios_simulator

After the emulator launches, start the app:

flutter run

That’s it!! You should be able to download a PDF file from the internet and view it using PSPDFKit. You can also add annotations such as text, markup, and ink to the file. PSPDFKit also provides built-in support for a variety of PDF features such as bookmarks, outlines, and printing.

Conclusion

We hope this post helped you with downloading and opening PDF documents in your Flutter project. Although we didn’t cover a lot of the features of our Flutter PDF library in this example, you can experiment with them in the example project itself. Some of the main features are:

  • Annotation Support — Your users can create, update, and delete annotations.
  • Interactive Forms — PSPDFKit for Flutter comes with form editing support, so your users call fill out forms in a PDF document.
  • Digital Signatures — Digital signatures are also supported. They’re used to verify the authenticity of a signed PDF.
  • Long-Term Support — At PSPDFKit, we release regular updates to add new features and enhancements; fix bugs; and maintain compatibility with the latest Flutter changes, dependencies, and operating system updates. PSPDFKit for Flutter supports the latest Android and iOS SDKs.
  • Great Documentation and Easy Integration — We care a lot about our documentation and constantly improve our guides and integration steps. We also continuously strive to make the integration fast and smooth. Because feature parity is extremely important to us, we always try to make our features available for both Android and iOS via the same Dart API.

If you have any questions about our PDF SDKs, please don’t hesitate to reach out to us. We’re happy to help!

Related Products
Share Post
Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
TUTORIAL  |  Flutter • How To • PDF Viewer

How to Build a Flutter PDF Viewer

PRODUCTS  |  Flutter • Releases

PSPDFKit 3.1 for Flutter Unifies Platform APIs

PRODUCTS  |  Flutter • Releases

PSPDFKit 3 for Flutter Adds TIFF Image Documents