This tutorial demonstrates how to build a complete, production-ready barcode scanning application using the barKoder Flutter SDK. The barKoder SDK is a powerful, enterprise-grade barcode scanning solution that leverages native device capabilities to deliver exceptional performance and accuracy.
Why barKoder SDK?
The barKoder SDK stands out because:
-
40+ barcode symbologies (1D and 2D) – From traditional UPC/EAN codes to modern 2D matrix codes, the SDK handles virtually any barcode format you'll encounter in commercial, industrial, or consumer applications.
-
Multiple scanning modes optimized for different use cases – Rather than a one-size-fits-all approach, barKoder provides specialized modes like Continuous Scanning, VIN Scanning, and Document Mode that are pre-configured for specific industries.
-
Advanced Enterprise Features – Leverage high-end capabilities such as Direct Part Marking (DPM) for industrial applications and barKoder's AR Capabilities for interactive, augmented reality overlays.
-
High performance with hardware-accelerated decoding - The SDK utilizes native device capabilities and optimized algorithms to achieve real-time scanning even on mid-range devices.
-
Flexible configuration for any scanning scenario – Every aspect of the scanner behavior can be customized, from decoding speed to resolution and ROI.
What You'll Build
By the end of this tutorial, you'll have built a feature-rich scanning app with:
-
Real-time barcode scanning – Instant barcode detection and decoding with configurable feedback.
-
Gallery image scanning – Scan barcodes from existing photos in your device's gallery.
-
Configurable settings – Full control over scanner behavior, performance, and user experience.
-
Scan history tracking – Persistent storage of scan results with timestamps and images.
-
Multiple specialized scanning modes – Switch between different modes optimized for various use cases.
-
Export functionality – Share scan results via CSV or other formats.
-
Camera controls – Flash, zoom, and camera switching capabilities.
Side-by-side demonstration of the barKoder Flutter SDK app.
Project Setup
Install dependencies
flutter pub get
Make sure pubspec.yaml includes barKoder:
dependencies:
barkoder_flutter: ^1.6.7
flutter_dotenv: ^5.2.1
Add your license key
Create .env in the project root:
BARKODER_LICENSE_KEY=YOUR_LICENSE_KEY
Load it in main.dart:
Future<void> main() async {
await dotenv.load(fileName: ".env");
runApp(const MyApp());
}
Platform permissions
-
Android: camera permission in
android/app/src/main/AndroidManifest.xml
-
iOS:
NSCameraUsageDescription in ios/Runner/Info.plist
Run the app
flutter run
Core Scanner Integration
The scanner view is created with BarKoderView and a callback that gives you the barKoder controller:
BarKoderView(
licenseKey: dotenv.env['BARKODER_LICENSE_KEY'] ?? '',
onBarkoderViewCreated: _onBarkoderViewCreated,
)
void _onBarkoderViewCreated(Barkoder barkoder) async {
_barkoder = barkoder;
await _loadSavedSettings();
_configureBarkoderView();
}
After setup, scanning starts with:
void _startScanning() {
_barkoder?.startScanning((result) => _handleScanResult(result));
}
App Architecture (Quick Overview)
Key files in this project:
-
lib/screens/scanner_screen.dart: live scanning UI and runtime behavior
-
lib/services/barcode_config_service.dart: mode presets and barKoder tuning
-
lib/services/history_service.dart: persistent scan history
-
lib/constants/modes.dart: mode IDs used across the app
This split keeps scanner logic clean: UI is in the screen, while mode-specific barKoder config is centralized in a service.
Handling Scan Results
In this app, every successful decode:
-
Reads
decoderResults
-
Pauses scanning in single-scan modes
-
Saves scan metadata
-
Stores images/thumbnails when available
-
Writes to persistent history
void _handleScanResult(BarkoderResult result) {
if (result.decoderResults.isEmpty) return;
final decoderResult = result.decoderResults.first;
final item = HistoryItem(
text: decoderResult.textualData,
type: decoderResult.barcodeTypeName,
image: null,
timestamp: DateTime.now().millisecondsSinceEpoch,
count: result.decoderResults.length,
);
setState(() => _scannedItems.insert(0, item));
HistoryService.addScan(text: item.text, type: item.type, image: item.image);
}
In single-scan modes the app pauses the session, shows a result card, and then resumes only when the user continues. In continuous-like modes, scanning stays active and deduplication is controlled through threshold settings.
Scanning Modes
Modes are defined in lib/constants/modes.dart and configured in lib/services/barcode_config_service.dart.
-
1d / 2d: targeted symbology groups
-
continuous: repeated scanning without closing session
-
multiscan: multiple results + caching behavior
-
vin: VIN restrictions + focused ROI + slower/high-quality decode
-
dpm: DataMatrix DPM mode tuned for direct-part-marked codes
-
deblur: UPC/EAN deblur for hard labels
-
dotcode: dedicated dotcode mode + narrow ROI
-
ar_mode: interactive AR overlays
-
mrz: ID document scanning mode
Example (VIN and DPM tuning):
case ScannerModes.vin:
barkoder.setEnableVINRestrictions(true);
barkoder.setRegionOfInterest(0, 35, 100, 30);
barkoder.setDecodingSpeed(DecodingSpeed.slow);
barkoder.setBarkoderResolution(BarkoderResolution.FHD);
break;
case ScannerModes.dpm:
barkoder.setDatamatrixDpmModeEnabled(true);
barkoder.setRegionOfInterest(40, 40, 20, 10);
barkoder.setDecodingSpeed(DecodingSpeed.slow);
barkoder.setBarkoderResolution(BarkoderResolution.UHD);
break;
For AR mode, the project enables:
-
setARMode
-
setARLocationType
-
setARHeaderShowMode
-
setAROverlayRefresh
-
setARDoubleTapToFreezeEnabled
For continuous and multiscan flows, it also configures duplicate suppression and maximum result behavior.
Gallery Image Scanning
This project also scans static images from the user gallery.
Flow:
-
Pick image with
image_picker
-
Convert to base64
-
Use barKoder
scanImage for decode
void _onBarkoderViewCreated(Barkoder barkoder) {
_barkoder = barkoder;
_configureBarkoderForGallery(barkoder);
barkoder.scanImage(_handleResult, widget.base64Image);
}
The gallery scanner runs inside a hidden BarkoderView with a timeout fallback, so the user is not stuck waiting indefinitely on problematic images.
Runtime Scanner Controls
The scanner screen supports live controls commonly needed in production apps:
-
Flash toggle (
setFlashEnabled)
-
Zoom toggle (
setZoomFactor)
-
Front/back camera switch (
setCamera)
-
Runtime settings updates (
_applySettingChange)
Settings are persisted per-mode in SharedPreferences (scanner_settings_<mode>), so users keep their preferred behavior across app restarts.
Performance Tuning Tips
The current implementation already includes practical defaults:
-
ROI for faster detection (
setRegionOfInterest)
-
Duplicate suppression in continuous flows (
setThresholdBetweenDuplicatesScans)
-
Mode-based decoding speed (
normal, slow)
-
Mode-based resolution (
HD, FHD, UHD)
-
Optional image/location payload control for throughput-sensitive cases
If you optimize for raw speed, reduce output payloads and tighten ROI first.
-
General retail/logistics:
DecodingSpeed.normal + BarkoderResolution.HD
-
Small or difficult labels:
DecodingSpeed.slow + tighter ROI
-
Image-based scanning:
DecodingSpeed.rigorous
-
Industrial DPM: DPM mode + stricter ROI + slower decoding
Common Pitfalls
-
Missing/invalid
BARKODER_LICENSE_KEY in .env
-
Enabling too many heavy result payloads in high-throughput modes
-
Using broad ROI in workflows that only need center scanning
-
Not separating mode presets from UI logic (harder to maintain at scale)
Conclusion
This Flutter app shows a full barKoder integration pattern:
-
Flexible mode-based scanner configuration
-
Real-time and gallery scanning
-
Persisted settings/history
-
User controls for flash, zoom, camera, and output behavior
For teams building scanner-heavy workflows in logistics, retail, manufacturing, or identity flows, this architecture is a solid baseline you can extend quickly.
Resources