Bridge Nutrient native Android, iOS, and Web APIs in Flutter

Nutrient Flutter SDK is built on top of the Nutrient iOS, Android, and Web SDKs, which offer a comprehensive set of APIs to interact with document viewers and editors. Nutrient Flutter SDK exposes a subset of the most frequently used APIs, but in some cases, you may need to access additional native APIs that aren’t directly available. This guide will show you how to bridge Nutrient’s native APIs for iOS and Android using Pigeon and for the Web using the dart:js package.

Prerequisites

Before proceeding, ensure your development environment includes the following:

Forking and cloning the Nutrient Flutter plugin repository

The Nutrient Flutter plugin source code is publicly available on GitHub. Follow the steps below.

  1. Fork the repository— Fork the Nutrient Flutter repository to your GitHub account.

  2. Clone the forked repository — Clone the repository to your local machine:

git clone https://github.com/<YOUR-GITHUB-USERNAME>/pspdfkit-flutter.git
  • If you’re new to forking repositories, check out this guide on GitHub for detailed instructions on forking and keeping your fork in sync with the upstream repository.

Once you have your fork ready, proceed to set up the project structure.

Setting up the project structure

For ease of development, organize your project in a structure where the Flutter app and the plugin sit as sibling directories:

app-root/
  ├── myapp/
  ├── pspdfkit-flutter/
  1. Create a new Flutter app — In the app-root directory, run the following commands to create a new Flutter app and clone the forked repository:

flutter create myapp
  1. Add the Nutrient Flutter plugin as a dependency in your pubspec.yaml file:

dependencies:
   pspdfkit_flutter:
      path: ../pspdfkit-flutter

Defining a new API in Dart with Pigeon

Pigeon is a code generator that simplifies the process of bridging APIs between Flutter and native platforms. It generates platform-specific code for iOS and Android based on the API definitions you provide in Dart.

To bridge a new API, you need to define the API in a Dart file and then generate the platform-specific code using the Pigeon tool.

  • Edit the Pigeon file — Open the pspdfkit-flutter/pigeons/pspdfkit.dart file and define the new API. As an example, this guide will show how to bridge the PdfDocument.getPageCount() API :

abstract class PdfDocumentApi {
  // Other existing APIs.
  @async
  int getPageCount();
}

Generating platform-specific code using Pigeon

With the API defined, generate the platform-specific code using the Pigeon tool.

  1. Install Pigeon — Ensure an up-to-date version of the Pigeon package is added to the pspdfkit-flutter pubspec.yaml file under the dev_dependencies section:

dev_dependencies:
   pigeon: ^22.4.0
  1. Run the Pigeon command — Switch to the pspdfkit-flutter directory and run the following command:

dart run pigeon --input pigeons/pspdfkit.dart

This will generate or update the following files to include the new API:

  • ./lib/api/pspdfkit.g.dart — The Dart API file interface

  • ./ios/Classes/api/PspdfkitApis.g.swift — The iOS API interface file

  • ./android/src/main/java/com/pspdfkit/flutter/pspdfkit/api/PspdfkitApi.g.kt — The Android API interface file

These generated files will contain all the necessary code to bridge the new API between Flutter and the native platforms.

Implementing the API on Android

To implement the API on Android, go to the Kotlin file where the native Android code will interact with Flutter: pspdfkit-flutter/android/src/main/java/com/pspdfkit/flutter/pspdfkit/FlutterPdfDocument.kt.

  • Edit the FlutterPdfDocument class — This class implements the generated PdfDocumentApi interface. Implement the getPageCount() method as follows:

class FlutterPdfDocument (
  private val pdfDocument: PdfDocument
) : PdfDocumentApi {

	// Other existing API implementations.
	override fun getPageCount(callback: (Result<Long>) -> Unit) {
		// The `PdfDocument` instance is already available, so you can get the page count directly.
		callback(Result.success(pdfDocument.pageCount.toLong()))
	}
}

Here, pdfDocument is an instance of the PdfDocument class, which is already available in the Android codebase. You just had to call the getPageCount property to get the total number of pages in the document.

Implementing the API on iOS

To implement the API on iOS, you need to update the Swift file at pspdfkit-flutter/ios/Classes/FlutterPdfDocument.swift.

  • Edit the FlutterPdfDocument class — This class implements the generated PdfDocumentApi protocol. Implement the getPageCount() method as follows:

class FlutterPdfDocument: NSObject, PdfDocumentApi {
  // Other existing API implementations.

  func getPageCount(completion: @escaping (Result<Int64, any Error>) -> Void) {

      if let pageCount = document?.pageCount {
          completion(.success(Int64(pageCount)))
      } else {
          let error = PspdfkitApiError(code: "", message: "Failed to get page count.", details:   nil )
          completion(.failure(error))
      }
  }
}

Here, document is an instance of the Document class, which is already available in the iOS codebase. You just had to call the pageCount property to get the total number of pages in the document.

Implementing the API in Flutter

Now, you’ll update the Dart files to expose the newly added getPageCount method in the Flutter Nutrient plugin.

  1. Update the PdfDocument interface — Edit lib/src/document/pdf_document.dart and define the new API method as follows:

abstract class PdfDocument {
  // Other existing APIs

  Future<int> getPageCount();
}
  1. Update the PdfDocumentNative class — Implement the new method in lib/src/document/pdf_document_native.dart:

class PdfDocumentNative extends PdfDocument {
  // Other existing APIs.

  @override
  Future<int> getPageCount() {
    return _api.getPageCount();
  }
}

Implementing the API for Web

Unlike iOS and Android, the Web platform doesn’t require platform channels or Pigeon to communicate with the JavaScript APIs. Instead, use the built-in dart:js package to interact with the Web platform.

To implement the getPageCount API for the Web platform, update the following files.

  1. Edit the PspdfkitWebInstance class — This is where you’ll add the web-specific logic to get the document title. Update lib/src/web/pspdfkit_web_instance.dart as follows:

class PspdfkitWebInstance {
  // Other existing APIs.

  Future<int> getPageCount() async {
    try {
		// Access the PSPDFKit JavaScript instance to get the page count.
      var count = _pspdfkitInstance['totalPageCount'];
      return Future.value(count);
    } catch (e) {
      throw Exception('Failed to get document title: $e');
    }
  }
}

Here _pspdfkitInstance is the JsObject reference to the JavaScript instance of the PSPDFKit.Instance, which is already available in the Web codebase. You just had to access the totalPageCount property to get the total number of pages in the document. To learn more about dart:js, refer to the official documentation.

  1. Edit PdfDocumentWeb — Next, update lib/src/document/pdf_document_web.dart to add access to the new API on the Web platform:

class PdfDocumentWeb extends PdfDocument {
  // Other existing APIs.

    @override
  Future<int> getPageCount() {
    return _instance.getPageCount();
  }
}

Using the new API in your Flutter app

Now that you’ve implemented the getPageCount method across all platforms, you can use it in your Flutter app. For example:

PspdfkitWidget(
  document: document,
  onDocumentLoaded: (pdfDocument) async {
    var pageCount = await pdfDocument.getPageCount();
    print('Document page count: $pageCount');
  },
)

Backward compatibility

If you’re currently using method channels for bridging APIs and aren’t ready to fully transition to Pigeon, you can continue using the existing method channels alongside the new Pigeon APIs. To do this, set useLegacy to true when initializing Nutrient:

await Pspdfkit.initialize(
  useLegacy: true,
);

This will ensure the existing method channels are used for communication with the native platform.