Flutterexperts

Empowering Vision with FlutterExperts' Expertise

In the digital age, being able to scan and store documents on-the-go is more than a convenience — it’s a necessity. Whether it’s students scanning their notes, professionals saving receipts, or individuals digitizing important papers, having a document scanner built directly into a mobile app adds incredible value.

In this blog, we’ll explore how to create a fully functional document scanner inside FlutterFlow, using a native plugin (cunning_document_scanner) and custom widgets/actions for scanning, previewing, and downloading images.

What You’ll Build

We’re going to build a FlutterFlow Document Scanner app that:

  • 📸 Scans books or documents via the camera.
  • 🖼️ Shows the scanned image in a bottom sheet.
  • ⬇️ Lets users download the scanned image to their device.

All this will be built using:

  • 2 Widgets: DocumentScanner and ScannerDoc
  • 1 Custom Action: takeScanner
  • Plugin: cunning_document_scanner: ^1.2.3

🛠️ Tools & Packages Required

To enable scanning and saving functionality, we’ll use:

dependencies:
cunning_document_scanner: ^1.2.3

Make sure these packages are added in your FlutterFlow project’s pubspec.yaml file via the “Custom Code > Dependencies” tab.

Widget Structure

DocumentScanner Widget

This is the main screen of the app. It contains a button or icon that initiates the scan process.

What it does:

  • Calls the takeScanner custom action when tapped.
  • Stores the list of scanned image paths.
  • Navigates to the ScannerDoc screen to show results.

🔧 Dart Code:

// Automatic FlutterFlow imports
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

import 'dart:io';

import 'package:cunning_document_scanner/cunning_document_scanner.dart';

class DocumentScanner extends StatefulWidget {
const DocumentScanner({
super.key,
this.width,
this.height,
});

final double? width;
final double? height;

@override
State<DocumentScanner> createState() => _DocumentScannerState();
}

class _DocumentScannerState extends State<DocumentScanner> {
List<String> _pictures = [];
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: SingleChildScrollView(
child: Column(
children: [
ElevatedButton(
onPressed: onPressed, child: const Text("Add Pictures")),
for (var picture in _pictures) Image.file(File(picture))
],
)),
),
);
}

void onPressed() async {
List<String> pictures;
try {
pictures = await CunningDocumentScanner.getPictures() ?? [];
if (!mounted) return;
setState(() {
_pictures = pictures;
});
} catch (exception) {
// Handle exception here
}
}
}

ScannerDoc Widget

This widget displays the scanned images in a bottom sheet.

Features:

  • Scrollable image viewer (for multiple scans).
  • Tappable image preview.
  • Download button next to each image for saving.

🔧 Dart Code:

// Automatic FlutterFlow imports
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

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

class ScannerDoc extends StatefulWidget {
const ScannerDoc({
super.key,
this.width,
this.height,
});

final double? width;
final double? height;

@override
State<ScannerDoc> createState() => _ScannerDocState();
}

class _ScannerDocState extends State<ScannerDoc> {
List<String> _pictures = [];
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: SingleChildScrollView(
child: Column(
children: [
ElevatedButton(
onPressed: onPressed, child: const Text("Add Pictures")),
for (var picture in _pictures) Image.file(File(picture))
],
)),
),
);
}

void onPressed() async {
List<String> pictures;
try {
pictures = await CunningDocumentScanner.getPictures() ?? [];
if (!mounted) return;
setState(() {
_pictures = pictures;
});
} catch (exception) {
// Handle exception here
}
}
}

Custom Action: takeScanner

This action integrates the cunning_document_scanner plugin and launches the native camera interface.

🔧 Dart Code:

// Automatic FlutterFlow imports
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/actions/index.dart'; // Imports other custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom action code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

import 'dart:io';
import 'dart:async';
import 'package:cunning_document_scanner/cunning_document_scanner.dart';

Future<List<FFUploadedFile>?> takeScanner() async {
// Add your function code here!
try {
// Panggil scanner untuk mengambil gambar dokumen
List<String>? pictures = await CunningDocumentScanner.getPictures();

// Jika hasilnya kosong, return null
if (pictures == null || pictures.isEmpty) {
return null;
}

// Konversi hasil path gambar menjadi List<FFUploadedFile>
List<FFUploadedFile> uploadedFiles = pictures.map((path) {
File file = File(path);
return FFUploadedFile(
name: path.split('/').last, // Mengambil nama file dari path
bytes: file.readAsBytesSync(), // Membaca file sebagai bytes
);
}).toList();

return uploadedFiles;
} catch (e) {
print('Error scanning document: $e');
return null;
}
}

How It Works:

  • Calls the scanner.
  • Returns a list of scanned image paths.
  • Passes these paths back to the widget for display.

Adding the Download Button

Inside the ScannerDoc bottom sheet, we add a Download button. This button triggers another custom action that saves the image to the user’s gallery.

Putting It All Together (User Flow)

  1. Open App → DocumentScanner widget.
  2. Click “Scan” → launches camera via takeScanner.
  3. User scans pages → list of image paths returned.
  4. Navigate to ScannerDoc → show images in bottom sheet.
  5. User taps “Download” → image saved to gallery.

Customizing the UI

FlutterFlow allows you to:

  • Use custom containers to style your bottom sheet
  • Add animation when showing the scanned image
  • Support light/dark themes
  • Add optional text fields (e.g. label your scans)

You can also:

  • Combine this with OCR tools to extract text
  • Convert images to PDF using another custom action
  • Sync with Firebase to store scanned files in the cloud

Permissions to Handle

Be sure to request permissions for:

  • Camera Access
  • Storage Access (Android only)

Use permission_handler to manage this properly in custom code.

Final Testing Tips

  • Test on a real device (scanning and file saving might not work on emulators).
  • Verify permissions are granted.
  • Check for multiple image support if needed.
  • Add error handling for denied permissions.

Conclusion

By combining FlutterFlow’s visual development power with native Flutter packages like cunning_document_scanner, you can create a production-ready document scanner in just a few hours. With reusable widgets, clean actions, and smooth UI, your app can offer a premium scanning experience without depending on third-party apps.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire Flutterflow developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on Facebook, GitHub, Twitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.

Leave comment

Your email address will not be published. Required fields are marked with *.