Integrating the Scanbot SDK in a Flutter app

In our first article, we already praised the versatility and ease of use of Flutter. To get you even more excited, you should check out this small tutorial for yourself. You’ll be surprised how easy it is to add complex functionality like our Scanner SDK for Flutter with a few lines of code. Let’s go …

Getting started

Requirements

For Android apps

For iOS apps


You can use Android Studio as IDE for developing Flutter apps. Also, there are Flutter IDE plugins available for VS Code and IntelliJ IDEA. In this tutorial, we will use the Flutter CLI (Command Line Interface) tools, as this works pretty well independently of any IDE plugins.

Once you have installed Flutter you can run the command flutter doctor to verify that all required tools are properly setup.

Create a “Hello World” Flutter app

$ flutter create --androidx my_awesome_flutter_app

This command generates a simple Flutter scaffold project for Android and iOS.

Please note the option –androidx, which generates the Android project using the new AndroidX libraries. It’s not only required in order to use the latest version of the Scanbot Android SDK but also highly recommended for every new Android Flutter project.

(If you need to migrate your Flutter project to AndroidX, please check out the respective Flutter docs “AndroidX Migration”).

Test run

Connect a mobile device via USB and run the app.

Android

$ cd my_awesome_flutter_app/
$ flutter run -d <DEVICE_ID>

You can get the IDs of all connected devices via flutter devices.

iOS

Open the Xcode workspace ios/Runner.xcworkspace and adjust the Signing / Developer Account settings in Xcode IDE. Then build and run the app in Xcode.

Both on Android and iOS you should see the same “Hello World” app which shows a simple UI with a button and label widgets as well as the functionality to increment a counter.

Add the Scanbot SDK Flutter plugin

As the next step, we will add the Scanbot Scanner SDK for Flutter Plugin which is provided as a public Flutter package. Flutter plugins are available as packages and provide access to native system APIs like camera, network, battery status, etc. as well as 3rd-party platform SDKs like for example Firebase.

There are a lot of packages hosted on the official repository pub.dev, which is an equivalent to npmjs.org for Node packages or nuget.org for Xamarin packages. For more details about Flutter packages refer to the Flutter docs “Using packages”.

So, let’s add the scanbot_sdk package to our project. Just open the file pubspec.yaml and define the package under dependencies:

dependencies:
  scanbot_sdk: ^1.0.0


After adding new packages you need to fetch and install them. For that we return again to the Flutter CLI and run the command:

$ flutter pub get

Android tunings

To support the latest Scanbot Android SDK we have to adjust some Gradle settings.

As the Flutter generated Android project is based on an older Kotlin version 1.2.x we need to raise this version to 1.3.x. In the top-level android/build.gradle file:

buildscript {
    ext.kotlin_version = '1.3.41'
    ...
}


In the app-level android/app/build.gradle file enable multidex and add the following packagingOptions:

android {
    ...
    defaultConfig {
      applicationId "com.example.myAwesomeFlutterApp"
      ...
      multiDexEnabled true
    }

    packagingOptions {
        pickFirst 'META-INF/atomicfu.kotlin_module'
        pickFirst 'META-INF/proguard/coroutines.pro'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/NOTICE'
    }
}


In the android/app/src/main/AndroidManifest.xml file add the CAMERA permission:

<manifest ...>
  <uses-permission android:name="android.permission.CAMERA" />
  ...
</manifest>

iOS setup

Specify a global platform version in the ios/Podfile file, like:

platform :ios, '9.0'


Then install the Pods by running:

$ cd ios/
$ pod install


And finally add the required NSCameraUsageDescription property in Xcode in the Info settings tab of the project (aka. Info.plist file):

NSCameraUsageDescription "Privacy - Camera Usage Description"

It’s coding time

Now, let’s write some Dart code! We are going to adjust the lib/main.dart file, which is the entry point of your Flutter app.

Import Scanbot SDK classes

Add the following imports of some Scanbot SDK packages and Flutter UI widgets we will use:

import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:scanbot_sdk/common_data.dart';
import 'package:scanbot_sdk/document_scan_data.dart';
import 'package:scanbot_sdk/scanbot_sdk.dart';
import 'package:scanbot_sdk/scanbot_sdk_models.dart';
import 'package:scanbot_sdk/scanbot_sdk_ui.dart';

Initialize the Scanbot SDK

The Scanner SDK for Flutter must be initialized before usage. Use the ScanbotSdk.initScanbotSdk(config) SDK API method for that. We initialize the Scanbot SDK when the main app widget is built:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    ScanbotSdk.initScanbotSdk(ScanbotSdkConfig(
      loggingEnabled: true,
      licenseKey: "", // see the license key notes below!
    ));

    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}


💡 For the full API reference of the Scanbot SDK Flutter Plugin please check out the documentation.

Integrate the Document Scanner UI

The Scanbot Scanner SDK for Flutter Plugin provides a scanner UI for document scanning. The Document Scanner is a complete and ready-to-use screen component and can be easily integrated with just a few lines of code using the API method ScanbotSdkUi.startDocumentScanner(config).

Let’s put the SDK call in a simple function named scanDocument() so we can bind it to a button:

class _MyHomePageState extends State<MyHomePage> {

  void scanDocument() async {
    var config = DocumentScannerConfiguration(
      multiPageEnabled: false,
      bottomBarBackgroundColor: Colors.blueAccent,
      cancelButtonTitle: "Cancel",
      // see further configs ...
    );
    var result = await ScanbotSdkUi.startDocumentScanner(config);

    if (result.operationResult == OperationResult.SUCCESS) {
      // get and use the scanned images as pages: result.pages[n] ...
      displayPageImage(result.pages[0]);
    }
  }

  ...
}


💡 Btw: we really like and recommend the async/await pattern in Dart, which is also known from other languages like C#, TypeScript, JavaScript, etc.

Now add a Flutter widget CupertinoButton and bind our scanDocument() method as onPressed event:

CupertinoButton(
  child: Text("Scan a Document"),
  onPressed: scanDocument,
),


With the DocumentScannerConfiguration you can pass config parameters to customize colors, text resources and some feature behavior of the Document Scanner. See the API docs for more details. 

As a result, the Document Scanner returns an array of Page objects which contain the scanned document and original images. A document image is the cropped and perspective corrected image (hi-res), while the original image is the unprocessed image (hi-res). All images are stored as JPG files by default and represented as file URIs (file:///…). Furthermore, a Page object provides preview images (low-res) which should bed used as thumbnails for displaying.

For simplicity in this example, we have disabled the multipage feature of the Document Scanner via the config parameter multiPageEnabled: false. So the Scanner UI will automatically close after one single scan and we can use the first Page element from the array result.pages[0] to display its cropped document image.

Display the scanned image

Use the JPG file URI of a Page object page.documentPreviewImageFileUri to show a preview image. For that, we add another Flutter widget Image and store an instance as a variable named currentPreviewImage. To show the last scanned Page image we implement a simple method displayPageImage(page) which updates the state of the currentPreviewImage widget:

class _MyHomePageState extends State<MyHomePage> {

  Image currentPreviewImage;

  void scanDocument() async { ... }

  void displayPageImage(Page page) {
    setState(() {
      currentPreviewImage = Image.file(File.fromUri(page.documentPreviewImageFileUri), width: 300, height: 300);
    });
  }

  ....
}


And finally render the currentPreviewImage widget conditionally right below our CupertinoButton:

...
CupertinoButton(
  child: Text("Scan a Document"),
  onPressed: scanDocument,
),
if (currentPreviewImage != null) ... [
  Text("Document image:"),
  currentPreviewImage,
],

Complete example projects

To get off to a flying start check out the following example projects on GitHub:

  • A full example project: https://github.com/doo/scanbot-sdk-example-flutter
    It demonstrates the integration of all API methods of the Scanbot SDK Flutter Plugin, such as Cropping UI, Image Filtering, PDF and TIFF Rendering, Optical Character Recognition, Barcode, and QR Code Scanning, MRZ Scanner (Machine Readable Zones), and EHIC Scanning (European Health Insurance Card).

Scanbot SDK (trial) license key

Please note: The Scanbot SDK will run without a licenseKey for one minute per session! After the trial period is over all Scanbot Scanner SDK for Flutter functions as well as the UI components will stop working or may be terminated. You can get an unrestricted “no-strings-attached” 30-day trial license for free. See the Trial License Form on our website.

As the Scanbot SDK license key is bound to an App Identifier (aka. Application ID on Android and Bundle Identifier on iOS), you will have to use the default generated App Identifier com.example.myAwesomeFlutterApp of this tutorial app. Of course, you can also use the ID of your app to get a trial license. Please also note that app IDs are case sensitive!

To request one license key for both platforms, Android and iOS, for this tutorial app, make sure the app IDs are the same. Typically you will need to change the app ID of the default generated Android project from com.example.my_awesome_flutter_app to com.example.myAwesomeFlutterApp, as Flutter apparently generates different IDs for those two platforms.

Where to find the App Identifier:

  • For Android see applicationId … in the android/app/build.gradle file.
  • For iOS see Bundle Identifier in the General settings tab in Xcode IDE.

Happy coding! 👩🏾‍💻👨‍💻