Writing Flutter plugin package — 1

Kristian Tuusjarvi
May 24, 2020 · 8 min read

Introduction

I ran into an issue some time ago. I wanted to build Flutter application that could convert PDF files to speech, to help me with school work. The idea was simple; read the text from PDF file and convert it to speech with the flutter_tts plugin. However, I ran into a problem, the PDF plugins for Flutter don’t extract the text from the file they just open native view and show the file as PDF. So to get around this issue I decided to build my own PDF text extractor plugin.

I will split this tutorial to two parts, first part will cover the Android side and second will cover iOS. I will also go over how package plugins work and how they are structured.

Requirements

  • I expect you to have a basic understanding of Flutter.

How do Flutter packages work?

Flutter defines two types of packages; dart packages and plugin packages. Dart packages are plain dart code that you can add to your application, plugin packages are packages that run code on the native side; Android and iOS. We are going to be writing a plugin package.

Flutter uses MethodChannels to talk to the native code. There is a host on the native side that listens for calls from the Flutter- side and responds to those calls. This guide is based on the official documentation by Flutter, I recommend checking it out.

Image for post
Image for post

Creating a plugin package

First run Flutter doctor to check that everything is working.

flutter doctor

If needed upgrade Flutter to the latest version.

flutter upgrade

I am using the stable build of Flutter for this project. The stable build should have less bugs in it. We can check the build channel, and change the channel to stable using these commands.

flutter channelflutter channel stable

We can use the command below to create a new plugin package. The package includes an example project that shows the platform version of your device.

flutter create --template=plugin -i swift -a java pdf_text_package

We can use the -i and -a to set the languages that we want to use for our package. I chose Swift for iOS and Java for Android because I’m more familiar with those languages. If you want you can also use Objective-C or Kotlin.

File structure

After creating the package we can open the package with our favorite code editor (for me VS Code). You should see something like this (obviously with your package name). I use Material Icon Theme in VS Code, that’s why the icons might look different.

Image for post
Image for post

The most important directories here are: android, ios, example and lib directories.

Image for post
Image for post

Android and iOS directories

In the android directory we have an Android Java project but with some boilerplate code to connect with Flutter.

Image for post
Image for post

From the android directory we can find the PdfTextPackagePlugin.java (depending on your package name), this file hosts the Java code that we want to run from Flutter. In the Java file we have four methods and one variable.

Image for post
Image for post
  • channel, this reference variable is used to hold a reference to the Flutter package, to register and detach from it.

The ios directory hosts a Swift project, we are not going to be covering it in this article. But the idea is the same as with the android directory.

Image for post
Image for post

Lib directory

The lib directory hosts the Dart code for our plugin package.

Image for post
Image for post
Image for post
Image for post

In here we find more code generated by Flutter. The platformVersiongetter is used to make a method call in the native code and MethodChannel _channel is used to connect to the Android host.

Example directory

In the example directory we have an example application which we can use to test the package. It is already configured to run our PdfTextPackagefrom the lib directory. We can run the example application from the main.dart file.

Image for post
Image for post
Image for post
Image for post
In the lib directory we have main.dart that calls our package.

Summary

In summary, here are the files we need to change in order to call code from the Android-side.

Image for post
Image for post

Running the example

Now that we understand the structure of the package we can run the example application that Flutter has generated for us. Navigate to the example directory inside the package with CMD and run the command below. You will need to have your device running.

flutter run
Image for post
Image for post
When running example you should see something like this

Writing the package

We are going to start by writing the Java code. What I want to do is parse the text out of PDF documents. However, Android doesn’t support this functionality out of the box, so I need to add the PDFbox library to my Android project to parse the text.

Adding PDFbox to the Android project

We can do this by adding the following lines to your build.gradle file.

dependencies{ 
implementation ‘com.tom_roush:pdfbox-android:1.8.10.1’
}
Image for post
Image for post

Import the PDFbox package and other libraries to the Java file.

Image for post
Image for post

Initializing PDFbox

In the PDFbox documentation they instruct us to do the following.

“Before calls to PDFBox are made it is highly recommended to initialize the library’s resource loader. Add the following line before calling PDFBox methods”

This means that we need to add the following lines to onAttachedToEngine() and registerWith() methods.

Image for post
Image for post

Package Java code

Now we can add the following code to onMethodCall method in the Java file.

  • Basically what is happening here is that we are getting the method call from Dart using.
call.method.equal("getPDFtext") 
  • Then we get the path of the PDF file from arguments of the call using.
final String path = call.argument("path") 
  • We create new File object and set that to the path and load that file.
File renderFile = new File(path);document = PDDocument.load(renderFile);
  • We create PDF stripper and parse the text from the PDF document.
PDFTextStripper pdfStripper = new PDFTextStripper();parsedText = pdfStripper.getText(document);
  • When we have the text we can close the document.
if (document != null) document.close();
  • Lastly, we can return the text to the Dart side.
result.success(parsedText);

Package Dart code

After the Java code is done we can navigate to the Dart code of our package, and add the getPDFtext static method. We can use key-value pairs to pass in the path of the PDF file.

Image for post
Image for post

Example app code

Navigate to the example directory. Add file_picker to the project’s pubspec.yaml file and fetch the plugin. We are using File Picker to get the path of our PDF file.

Image for post
Image for post
flutter pub get

Import file_picker and dart.io to the main.dart file.

import 'package:file_picker/file_picker.dart';import 'dart:io';

Next we are going to finish the example app, by rewriting the main.dart file. You can copy paste from here.

Here we have a method getPDFtext(path) which is responsible for calling the native Android code. It is using the static method PdfTextPackge.getPDFtext() that we defined earlier.

text = await PdfTextPackage.getPDFtext(path);

When clicking the FlatButton we call FilePicker.getFile() method that is used to get the path of our PDF file, file picker returns the path, and getPDFtext method is called. When the native code has parsed the text from the PDF document, it is returned to Dart side and set into the _pdfText variable.

Final test

Now when we run the application, then select the PDF document, we should get back the text to the Text-widget.

Image for post
Image for post
Running the example application
Image for post
Image for post
Clicking the “get pdf text” button and selecting file
Image for post
Image for post
The text from the pdf is returned to Text-widget

Forewords

This was an interesting experience, writing a plugin package. I learned a lot and I recommend package development for everybody. :)

It needs to be noted that this plugin is not suitable for real use, the PDFbox library will cause Flutter UI to freeze. To get around this we would have to implement threading in the Android code. If you would like to see how that could be done you can visit the GitHub of my read_pdf_text package, which is a more advanced version of the plugin that we developed in this article.

Please leave comments and questions if there is something unclear or you have some critique. I am always trying to improve! :D

>>> Writing Flutter plugin package — 2 <<<

Links

My Youtube

My website

GitHub for the code

The Startup

Medium's largest active publication, followed by +771K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store