Document Scanner in FlutterFlow

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
andScannerDoc
- 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)
- Open App → DocumentScanner widget.
- Click “Scan” → launches camera via
takeScanner
. - User scans pages → list of image paths returned.
- Navigate to ScannerDoc → show images in bottom sheet.
- 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.
