Scanbot SDK has been acquired by Apryse! Learn more

Learn more
Skip to content

How to build a Flutter document scanner app with the flutter_doc_scanner package

Kevin September 8, 2025 11 mins read
Building a Flutter document scanner app with the flutter_doc_scanner package

In this tutorial, you’ll learn how to use Flutter and Dart to build an app for scanning documents and exporting them as PDFs.

For the document scanner functionalities, we’ll use the flutter_doc_scanner package, a wrapper around Google’s ML Kit and Apple’s VisionKit that streamlines their integration into Flutter projects.

Scanning a document and exporting it as a PDF using the flutter_doc_scanner and share_plus packages

To achieve this, we’ll follow these steps:

  1. Preparing the project
  2. Setting the camera permissions
  3. Implementing the document scanning feature
  4. Implementing the PDF export feature

Prerequisites

  • Flutter SDK: Ensure you have the latest version installed.
  • Development tools:
    • For Android: Android Studio with the Android SDK.
    • For iOS: macOS with the latest Xcode and CocoaPods installed.
    • Optional: Visual Studio Code with the Flutter and Dart extensions.

Step 1: Prepare the project

Open your terminal and execute:

flutter create flutter_document_scanner_app
cd flutter_document_scanner_app

Open pubspec.yaml and add the flutter_doc_scanner dependency.

dependencies:
  flutter:
    sdk: flutter
  flutter_doc_scanner: ^0.0.16

Then fetch the package.

flutter pub get

Step 2: Set the camera permissions

We need to access the device camera to scan documents.

For Androidm, open android/app/src/main/AndroidManifest.xml and add the necessary permissions:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

For iOS, open ios/Runner/Info.plist and add:

<key>NSCameraUsageDescription</key>
<string>Grant camera access to scan documents.</string>

Step 3: Implement the document scanning feature

Now we can integrate the document scanner package into our app.

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

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_doc_scanner/flutter_doc_scanner.dart';

void main() {
  runApp(MaterialApp(home: MyApp()));
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  dynamic _scannedDocuments;

  Future<void> scanDocument() async {
    dynamic scannedDocuments;
    try {
      scannedDocuments =
          await FlutterDocScanner().getScannedDocumentAsPdf(page: 4) ??
          'Unknown platform documents';
    } on PlatformException {
      scannedDocuments = 'Failed to get scanned documents.';
    }

    if (!mounted) return;
    setState(() {
      _scannedDocuments = scannedDocuments;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Document Scanner',
      home: Scaffold(
        appBar: AppBar(title: const Text('Flutter Document Scanner')),
        body: Center(
          child: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                _scannedDocuments != null
                    ? Text(_scannedDocuments.toString())
                    : const Text("No Documents Scanned"),
              ],
            ),
          ),
        ),
        floatingActionButton: Padding(
          padding: const EdgeInsets.only(bottom: 16.0),
          child: ElevatedButton(
            onPressed: () {
              scanDocument();
            },
            child: const Text("Scan Documents"),
          ),
        ),
      ),
    );
  }
}

When the user taps on the “Scan Documents” button, the scanDocument() method is called. It uses the FlutterDocScanner().getScannedDocumentAsPdf() method to open the device’s camera and initiate the scanning process with a four-page limit (which you can change to suit your needs). The scanned documents are then processed into a PDF and stored in the _scannedDocuments variable. The file’s URI is presented to the user.

Step 4: Implement the PDF export feature

As it is now, the app only prints the PDF file’s URI to the screen. To automatically show a sharing dialog after the PDF has been created, we’ll use the share_plus package.

First, add the share_plus dependency to pubspec.yaml and run flutter pub get once more.

dependencies:
  flutter:
    sdk: flutter
  scanbot_sdk: ^6.1.2
  share_plus: ^11.1.0

In main.dart, import the package.

import 'package:share_plus/share_plus.dart';

Then add the following function:

Future<void> _sharePdf(String pdfURI) async {
  if (pdfURI.isEmpty) return;

  final file = File(pdfURI);
  if (!file.existsSync()) {
    ScaffoldMessenger.of(
      context,
    ).showSnackBar(SnackBar(content: Text("PDF file not found")));
    return;
  }

  final box = context.findRenderObject() as RenderBox?;
  await Share.shareXFiles(
    [XFile(pdfURI)],
    text: "Share PDF file!",
    sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
  );
}

This function takes a file path as a string (pdfURI) and uses the share_plus package to open the device’s native sharing dialog.

Call this function inside scanDocument() while making sure the URI has the correct format.

Future<void> scanDocument() async {
  dynamic scannedDocuments;
  try {
    scannedDocuments =
        await FlutterDocScanner().getScannedDocumentAsPdf(page: 4) ??
        'Unknown platform documents';
  } on PlatformException {
    scannedDocuments = 'Failed to get scanned documents.';
  }

  // Prepare the URI and call the share function
  final pdfUri = scannedDocuments['pdfUri'].replaceFirst("file://", "");
  await _sharePdf(pdfUri);

  if (!mounted) return;
  setState(() {
    _scannedDocuments = scannedDocuments;
  });
}

Your final main.dart will look like this:

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_doc_scanner/flutter_doc_scanner.dart';
import 'package:share_plus/share_plus.dart';

void main() {
  runApp(MaterialApp(home: MyApp()));
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  dynamic _scannedDocuments;

  Future<void> scanDocument() async {
    dynamic scannedDocuments;
    try {
      scannedDocuments =
          await FlutterDocScanner().getScannedDocumentAsPdf(page: 4) ??
          'Unknown platform documents';
    } on PlatformException {
      scannedDocuments = 'Failed to get scanned documents.';
    }

    final pdfUri = scannedDocuments['pdfUri'].replaceFirst("file://", "");
    await _sharePdf(pdfUri);

    if (!mounted) return;
    setState(() {
      _scannedDocuments = scannedDocuments;
    });
  }

  Future<void> _sharePdf(String pdfURI) async {
    if (pdfURI.isEmpty) return;

    final file = File(pdfURI);
    if (!file.existsSync()) {
      ScaffoldMessenger.of(
        context,
      ).showSnackBar(SnackBar(content: Text("PDF file not found")));
      return;
    }

    final box = context.findRenderObject() as RenderBox?;
    await Share.shareXFiles(
      [XFile(pdfURI)],
      text: "Share PDF file!",
      sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Document Scanner',
      home: Scaffold(
        appBar: AppBar(title: const Text('Flutter Document Scanner')),
        body: Center(
          child: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                _scannedDocuments != null
                    ? Text(_scannedDocuments.toString())
                    : const Text("No Documents Scanned"),
              ],
            ),
          ),
        ),
        floatingActionButton: Padding(
          padding: const EdgeInsets.only(bottom: 16.0),
          child: ElevatedButton(
            onPressed: () {
              scanDocument();
            },
            child: const Text("Scan Documents"),
          ),
        ),
      ),
    );
  }
}

Now use the flutter run command to test your app!

Scanning a document and exporting it as a PDF using the flutter_doc_scanner and share_plus packages

Conclusion

This concludes our tutorial on how to set up a document scanning app in Flutter using flutter_doc_scanner.

Free solutions like this one can be great for prototyping and personal projects. However, they have their drawbacks.

Since flutter_doc_scanner is a wrapper for Google’s ML Kit and Apple’s VisionKit, its functionality depends entirely on these two libraries. This also means document scanning will look and behave differently on Android and iOS devices.

For companies looking to integrate a document scanning solution into their mobile apps, there’s an additional challenge: Neither Google nor Apple offer dedicated support for their ML Kit and Vision Kit libraries. This means you cannot submit feature requests nor count on help when things don’t work as expected.

We developed the Scanbot Document Scanner SDK to help companies overcome these hurdles. Our goal was to provide a developer-friendly solution for a wide range of platforms that consistently delivers high-quality results, even in challenging circumstances – enterprise-grade support included.

In the following tutorial, we’ll show you how to set up a document scanning app using the Scanbot Flutter Document Scanner SDK.

Building a Flutter document scanner app with the Scanbot SDK

To set up our app, we’ll follow these steps:

  1. Preparing the project
  2. Initializing the SDK
  3. Implementing the scanning feature
  4. Implementing the PDF export feature

Thanks to the SDK’s Ready-to-Use UI Components, we’ll have an intuitive user interface out of the box.

Scanning a document and exporting it as a PDF

Let’s get started!

Step 1: Prepare the project

After you’ve created a new Flutter project, add the scanbot_sdk dependency to pubspec.yaml.

dependencies:
  flutter:
    sdk: flutter
  scanbot_sdk: ^7.0.1

Then fetch the package.

flutter pub get

💡 We use Document Scanner SDK version 7.0.1 in this tutorial. You can find the latest version in the changelog.

Now set the necessary camera permissions in android/app/src/main/AndroidManifest.xml

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

… and ios/Runner/Info.plist.

<key>NSCameraUsageDescription</key>
<string>Grant camera access to scan documents.</string>

Step 2: Initialize the SDK

Before we can use the Document Scanner SDK, we need to initialize it. Make sure to call the initialization after entering the main widget creation. This ensures the SDK is correctly initialized.

In lib/main.dart, import the Scanbot SDK package:

import 'package:scanbot_sdk/scanbot_sdk.dart';
import 'package:scanbot_sdk/scanbot_sdk_ui_v2.dart';

Within your main widget, initialize the Scanbot SDK. This is typically done in the initState method of your main widget’s state class:

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    _initScanbotSdk();
  }

  Future<void> _initScanbotSdk() async {
    var config = ScanbotSdkConfig(
      licenseKey: "",
      loggingEnabled: true,
    );

    try {
      await ScanbotSdk.initScanbotSdk(config);
      print('Scanbot SDK initialized successfully');
    } catch (e) {
      print('Error initializing Scanbot SDK: $e');
    }
  }
  //...

💡 Without a license key, our SDK only runs for 60 seconds per session. This is more than enough for the purposes of our tutorial, but if you like, you can generate a license key using your app identifier.

Step 3: Implement the scanning feature

Now we’ll create a simple user interface that includes a button to initiate the document scanning process.

Still in lib/main.dart, and in your main widget’s state class, define a widget with a button labeled “Scan Document”:

class _MyHomePageState extends State<MyHomePage> {
  // ... (existing code)

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Document Scanner'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: _startDocumentScanner,
          child: Text('Scan Document'),
        ),
      ),
    );
  }

  Future<void> _startDocumentScanner() async {
    // To be implemented in the next step
  }
}

Next, let’s connect the button with our RTU UI’s scanning screen.

Within the _startDocumentScanner method, configure and launch the Document Scanner UI. This involves creating a DocumentScanningFlow configuration and starting the scanner:

Future<void> _startDocumentScanner() async {
  
  // Launch the Scanbot Document Scanner UI
    var configuration = multiPageScanningFlow();
    var documentResult = await ScanbotSdkUiV2.startDocumentScanner(configuration);
    
    // Check if the scanning operation was successful
    if(documentResult.status == OperationStatus.OK) {
    }
}

DocumentScanningFlow multiPageScanningFlow() {
    // Create the default configuration object.
    var configuration = DocumentScanningFlow();

    // Customize text resources, behavior and UI:
    // ...

    return configuration;
    }
}

In this tutorial, we use a default configuration object. It will start the Document Scanner UI with the default settings: in multi-page scanning mode with an acknowledge screen after scanning each page. You can customize the UI and behavior of the Document Scanner by modifying the configuration object. For more information on how to customize the Document Scanner UI, please refer to the RTU UI documentation.

If you want, you can now run the app to try out the scanner without the PDF export feature.

Cross-platform document scanning app for Android and iOS

Step 4: Implement the PDF export feature

Once again, we’ll use the share_plus package to implement the share dialog. Add the dependency to pubspec.yaml and run flutter pub get once more.

dependencies:
  flutter:
    sdk: flutter
  scanbot_sdk: ^7.0.1
  share_plus: ^11.1.0

Back in main.dart, import the package.

import 'package:share_plus/share_plus.dart';

To enable users to scan documents, generate a PDF, and share it, we need to modify the _startDocumentScanner method. This method will first launch the document scanner, then process the scanned document to generate a PDF, and finally provide an option to share it.

Future<void> _startDocumentScanner() async {
    var configuration = multiPageScanningFlow();
    var documentResult = await ScanbotSdkUiV2.startDocumentScanner(
      configuration,
    );

    if (documentResult.status == OperationStatus.OK) {

      // Convert the scanned document into a PDF file
      var result = await ScanbotSdk.document.createPDFForDocument(
        PDFFromDocumentParams(
          documentID: documentResult.data!.uuid,
          pdfConfiguration: PdfConfiguration(
          pageSize: PageSize.A4,
          pageDirection: PageDirection.PORTRAIT
          )
        )
      );

      // Extract the PDF file URI and trigger the sharing process
      final pdfURI = result.pdfFileUri.replaceFirst("file://", "");
      await _sharePdf(pdfURI);
    }
  }

  // Method to share the generated PDF file
  Future<void> _sharePdf(String pdfURI) async {
    if (pdfURI.isEmpty) return;

    final file = File(pdfURI);
    if (!file.existsSync()) {
      ScaffoldMessenger.of(
        context,
      ).showSnackBar(SnackBar(content: Text("PDF file not found")));
      return;
    }

    // Locate the UI box to ensure proper sharing dialog positioning
    final box = context.findRenderObject() as RenderBox?;
    await Share.shareXFiles(
      [XFile(pdfURI)],
      text: "Share PDF file!",
      sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
    );
  }

  DocumentScanningFlow multiPageScanningFlow() {
    var configuration = DocumentScanningFlow();
    return configuration;
  }

Now you can share a scanned document as a PDF file and use it. For example, you can send it via email or save it to a cloud storage.

Scanning a document and exporting it as a PDF

Conclusion

And that’s it! You’ve successfully integrated a fully functional document scanner into your app 🎉

If this tutorial has piqued your interest in integrating document scanning functionalities into your Flutter app, make sure to take a look at the other neat features in the Flutter Document Scanner SDK’s documentation – or run our example project for a more hands-on experience.

Should you have questions about this tutorial or run into any issues, we’re happy to help! Just shoot us an email via tutorial-support@scanbot.io.

Happy scanning! 🤳

Related blog posts

Experience our demo apps

Barcode Icon Art

Barcode Scanner SDK

Scan 1D and 2D barcodes reliably in under 0.04s. Try features like Batch Scanning, Scan & Count, and our AR Overlays.

Launch Web Demo

Scan the code to launch the web demo on your phone.

Web QR Code

Also available to download from:

Document Icon Art

Document Scanner SDK

Scan documents quickly and accurately with our free demo app. Create crisp digital scans in seconds.

Launch Web Demo

Scan the code to launch the web demo on your phone.

Black and white QR code. Scan this code for quick access to information.

Also available to download from:

Data_capture Icon Art

Data Capture Modules

Try fast, accurate data capture with our demo app. Extract data from any document instantly – 100% secure.

Launch Web Demo

Scan the code to launch the web demo on your phone.

Black and white QR code. Scan this quick response code with your smartphone.

Also available to download from: