ultra_qr_scanner 3.0.2
ultra_qr_scanner: ^3.0.2 copied to clipboard
Ultra-fast, low-latency QR code & Barcode scanner plugin for Flutter with native performance optimization.
Ultra QR Scanner ๐ฑโก #
Ultra-fast, low-latency QR code scanner plugin for Flutter with native camera preview and performance optimization.
๐ฏ Goal: Open scanner โ show camera preview โ detect QR instantly โ return result โ done โ
โจ Features #
๐ Ultra-fast startup - Preload scanner during app initialization for instant access
โก Native performance - CameraX (Android) + AVCapture (iOS) with platform views
๐ธ Live camera preview - Real-time camera feed with customizable overlay
๐ค Auto-start scanning - Optional automatic scanning when widget appears
๐ Manual controls - User-controlled start/stop with customizable UI
๐ฑ Simple API - Single scan or continuous stream modes
๐ฆ Flash control - Toggle flashlight on supported devices
๐ท Camera switching - Front/back camera support
๐ก๏ธ Production ready - Comprehensive error handling & memory management
๐จ Customizable UI - Custom overlays, buttons, and scanning frames
๐ Performance Optimizations #
Feature | Description | Benefit |
---|---|---|
Native Camera APIs | CameraX on Android, AVCaptureSession on iOS | Maximum hardware utilization |
Platform Views | Native camera preview rendering | Seamless integration with Flutter UI |
ML Frameworks | MLKit Barcode Scanning (Android), Vision API (iOS) | Optimized QR detection |
Threading | Background processing with main thread UI updates | Non-blocking UI performance |
Auto-start Mode | Begin scanning immediately when widget appears | Zero user interaction needed |
Auto-stop | Immediate camera shutdown after detection | Zero waste of resources |
Preloading | Initialize during app startup | < 50ms to first scan |
Memory Management | Proper cleanup and lifecycle handling | Leak-free operation |
๐ Benchmarks #
Metric | Target | Typical Result |
---|---|---|
Cold Start Time | < 300ms | ~200ms |
Auto-Start Time | < 100ms | ~50ms |
QR Detection Speed | < 100ms | ~50ms |
Memory Usage | < 50MB | ~35MB |
Battery Impact | Minimal | 2-3% per hour |
Frame Rate | 30 FPS | Stable 30 FPS |
๐ Installation #
Add to your pubspec.yaml
:
dependencies:
ultra_qr_scanner: ^3.0.2
flutter pub get
๐โโ๏ธ Quick Start #
1. Initialize Scanner (Optional but Recommended) #
import 'package:ultra_qr_scanner/ultra_qr_scanner.dart';
// Best practice: Initialize during app startup for faster access
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Request camera permission and prepare scanner
final hasPermission = await UltraQrScanner.requestPermissions();
if (hasPermission) {
await UltraQrScanner.prepareScanner();
}
runApp(MyApp());
}
2. Auto-Start Scanner (Recommended for Quick Scanning) #
import 'package:ultra_qr_scanner/ultra_qr_scanner_widget.dart';
class QuickScannerPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Quick QR Scanner')),
body: Container(
width: 300,
height: 300,
child: UltraQrScannerWidget(
onCodeDetected:(code , type) {
print('QR Code detected: $qrCode');
Navigator.pop(context, qrCode);
},
showFlashToggle: true, // Show flash button
autoStop: true, // Auto-stop after detection
showStartStopButton: false, // Hide manual controls
autoStart: true, // Start scanning immediately
),
),
);
}
}
3. Manual Scanner (Traditional User-Controlled) #
class ManualScannerPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Manual QR Scanner')),
body: Container(
width: 300,
height: 300,
child: UltraQrScannerWidget(
onCodeDetected:(code , type) {
print('QR Code detected: $qrCode');
Navigator.pop(context, qrCode);
},
showFlashToggle: true, // Show flash button
autoStop: true, // Auto-stop after detection
showStartStopButton: true, // Show start/stop button
autoStart: false, // Wait for user to start
),
),
);
}
}
4. Programmatic Scanning (Alternative) #
// Single scan
Future<void> scanQRCode() async {
try {
final qrCode = await UltraQrScanner.scanOnce();
if (qrCode != null) {
print('Scanned QR Code: $qrCode');
}
} catch (e) {
print('Scan failed: $e');
}
}
// Continuous scanning stream
StreamSubscription<String>? _scanSubscription;
void startContinuousScanning() {
_scanSubscription = UltraQrScanner.scanStream().listen(
(qrCode) {
print('Detected QR Code: $qrCode');
},
onError: (error) {
print('Scan error: $error');
},
);
}
void stopScanning() {
_scanSubscription?.cancel();
UltraQrScanner.stopScanner();
}
๐จ Widget Customization #
Scanning Modes #
The widget supports two main scanning modes:
๐ค Auto-Start Mode (Instant Scanning)
Perfect for quick scanning scenarios where you want immediate results:
UltraQrScannerWidget(
onCodeDetected:(code , type) => handleQRCode(qrCode),
autoStart: true, // Start scanning immediately
showStartStopButton: false, // Hide manual controls
autoStop: true, // Stop after first detection
showFlashToggle: true, // Optional flash control
)
๐ Manual Mode (User-Controlled)
Traditional scanning with user controls:
UltraQrScannerWidget(
onCodeDetected:(code , type) => handleQRCode(qrCode),
autoStart: false, // Wait for user action
showStartStopButton: true, // Show start/stop button
autoStop: true, // Stop after first detection
showFlashToggle: true, // Optional flash control
)
๐ Hybrid Mode (Best of Both)
Auto-start with manual controls available:
UltraQrScannerWidget(
onCodeDetected:(code , type) => handleQRCode(qrCode),
autoStart: true, // Start immediately
showStartStopButton: true, // But also show controls
autoStop: false, // Continuous scanning
showFlashToggle: true, // Flash control
)
Widget Configuration Options #
Parameter | Type | Default | Description |
---|---|---|---|
onQrDetected |
Function(String) |
Required | Callback when QR code is detected |
autoStart |
bool |
false |
Start scanning automatically when widget appears |
showStartStopButton |
bool |
true |
Show/hide the start/stop scan button |
autoStop |
bool |
true |
Stop scanning after first QR code detection |
showFlashToggle |
bool |
false |
Show/hide flash/torch toggle button |
overlay |
Widget? |
null |
Custom overlay widget (uses default if null) |
Custom Overlay Example #
UltraQrScannerWidget(
onCodeDetected:(code , type) (qrCode) => handleQRCode(qrCode),
autoStart: true,
showStartStopButton: false,
overlay: Stack(
children: [
// Semi-transparent background
Container(color: Colors.black54),
// Custom scanning frame
Center(
child: Container(
width: 250,
height: 250,
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 4),
borderRadius: BorderRadius.circular(20),
),
child: Stack(
children: [
// Corner decorations
...buildCornerDecorations(),
// Center dot
Center(
child: Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
),
),
),
],
),
),
),
// Custom instructions
Positioned(
top: 60,
left: 0,
right: 0,
child: Column(
children: [
Icon(Icons.qr_code_scanner, color: Colors.white, size: 48),
SizedBox(height: 16),
Text(
'Scan QR Code',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'Position the QR code within the frame',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white70,
fontSize: 16,
),
),
],
),
),
],
),
)
Usage Scenarios #
๐ Instant QR Scanner (No UI Friction)
// Perfect for: Payment apps, quick actions, URL scanning
UltraQrScannerWidget(
onCodeDetected:(code , type) => processPayment(code),
autoStart: true,
showStartStopButton: false,
autoStop: true,
)
๐ท Traditional Scanner (User Control)
// Perfect for: Document scanning, batch operations, careful scanning
UltraQrScannerWidget(
onCodeDetected:(code , type) => addToList(code),
autoStart: false,
showStartStopButton: true,
autoStop: false, // Continuous scanning
)
๐ Full-Featured Scanner
// Perfect for: Professional apps, feature-rich scanning
UltraQrScannerWidget(
oonCodeDetected:(code , type) => handleCode(code),
autoStart: true,
showStartStopButton: true,
showFlashToggle: true,
autoStop: true,
)
๐ง Advanced Features #
Flash Control #
// Toggle flashlight
await UltraQrScanner.toggleFlash(true); // Turn on
await UltraQrScanner.toggleFlash(false); // Turn off
Camera Switching #
// Switch between front and back camera
await UltraQrScanner.switchCamera('front'); // Use front camera
await UltraQrScanner.switchCamera('back'); // Use back camera
Scanner Lifecycle Management #
// Check if scanner is ready
if (UltraQrScanner.isPrepared) {
// Ready to scan
} else {
// Need to prepare first
await UltraQrScanner.prepareScanner();
}
// Manually stop scanner
await UltraQrScanner.stopScanner();
๐ Permissions Setup #
Android #
Add to android/app/src/main/AndroidManifest.xml
:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus" />
Add to android/app/build.gradle
:
dependencies {
implementation 'androidx.camera:camera-camera2:1.2.3'
implementation 'androidx.camera:camera-lifecycle:1.2.3'
implementation 'androidx.camera:camera-view:1.2.3'
implementation 'com.google.mlkit:barcode-scanning:17.2.0'
}
iOS #
Add to ios/Runner/Info.plist
:
<key>NSCameraUsageDescription</key>
<string>Camera access is required to scan QR codes</string>
Ensure minimum iOS version 11.0+ in ios/Podfile
:
platform :ios, '11.0'
๐ฑ Platform Support #
Platform | Camera API | ML Framework | Preview | Min Version | Status |
---|---|---|---|---|---|
Android | CameraX + PreviewView | MLKit Barcode | โ Native | API 21 (Android 5.0) | โ Fully Supported |
iOS | AVCapture + PreviewLayer | Vision Framework | โ Native | iOS 11.0+ | โ Fully Supported |
๐ฏ Complete Example App #
import 'package:flutter/material.dart';
import 'package:ultra_qr_scanner/ultra_qr_scanner.dart';
import 'package:ultra_qr_scanner/ultra_qr_scanner_widget.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Ultra QR Scanner Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String? lastQRCode;
bool useAutoMode = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Ultra QR Scanner'),
centerTitle: true,
actions: [
IconButton(
icon: Icon(useAutoMode ? Icons.auto_awesome : Icons.touch_app),
onPressed: () {
setState(() {
useAutoMode = !useAutoMode;
});
},
tooltip: useAutoMode ? 'Switch to Manual' : 'Switch to Auto',
),
],
),
body: Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Mode indicator
Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: useAutoMode ? Colors.green.shade100 : Colors.blue.shade100,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: useAutoMode ? Colors.green : Colors.blue,
width: 2,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
useAutoMode ? Icons.flash_auto : Icons.touch_app,
color: useAutoMode ? Colors.green.shade700 : Colors.blue.shade700,
size: 20,
),
SizedBox(width: 8),
Text(
useAutoMode ? '๐ AUTO MODE' : '๐ MANUAL MODE',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: useAutoMode ? Colors.green.shade700 : Colors.blue.shade700,
),
),
],
),
),
SizedBox(height: 10),
Text(
useAutoMode
? 'Scanning starts automatically when camera opens'
: 'Tap the Start Scan button to begin scanning',
style: TextStyle(fontSize: 16),
textAlign: TextAlign.center,
),
SizedBox(height: 20),
// Scanner Widget
Container(
width: 300,
height: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: UltraQrScannerWidget(
onCodeDetected:(code , type) {
setState(() {
lastQRCode = code;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Scanned: $code'),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
duration: Duration(seconds: 2),
),
);
},
showFlashToggle: true,
autoStop: true,
// Dynamic mode switching
autoStart: useAutoMode,
showStartStopButton: !useAutoMode,
),
),
SizedBox(height: 20),
// Results Display
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade300),
),
child: Column(
children: [
Text(
'Last Scanned Code:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey.shade700,
),
),
SizedBox(height: 8),
Text(
lastQRCode ?? 'No QR code scanned yet',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
],
),
),
SizedBox(height: 20),
// Mode comparison
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'๐ก Scanning Modes:',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
SizedBox(height: 8),
Text('๐ Auto Mode: Instant scanning, no buttons needed'),
Text('๐ Manual Mode: User-controlled start/stop'),
Text('โก Both modes: Auto-stop after detection'),
Text('๐ฆ Flash control available in both modes'),
Text('๐ท Camera switching available in both modes'),
],
),
),
],
),
),
),
);
}
}
๐ Error Handling #
try {
await UltraQrScanner.prepareScanner();
final result = await UltraQrScanner.scanOnce();
// Handle success
} on UltraQrScannerException catch (e) {
// Handle scanner-specific errors
switch (e.code) {
case 'PERMISSION_DENIED':
// Show permission dialog
break;
case 'NO_CAMERA':
// Handle no camera available
break;
case 'NOT_PREPARED':
// Scanner needs initialization
break;
default:
print('Scanner error: ${e.message}');
}
} catch (e) {
// Handle other errors
print('General error: $e');
}
๐ Performance Tips #
1. Choose the Right Mode for Your Use Case #
// โ
GOOD: Auto-mode for quick actions (payments, URLs)
UltraQrScannerWidget(
autoStart: true,
showStartStopButton: false,
autoStop: true,
)
// โ
GOOD: Manual mode for careful scanning (documents, batch)
UltraQrScannerWidget(
autoStart: false,
showStartStopButton: true,
autoStop: false,
)
2. Initialize Early (Optional but Recommended) #
// โ
GOOD: Initialize during app startup for faster access
void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (await UltraQrScanner.requestPermissions()) {
await UltraQrScanner.prepareScanner();
}
runApp(MyApp());
}
3. Use Widget for Better Performance #
// โ
RECOMMENDED: Use UltraQrScannerWidget
UltraQrScannerWidget(
onQrDetected: (code) => handleCode(code),
autoStart: true,
)
// โ ALTERNATIVE: Programmatic scanning (requires more setup)
final result = await UltraQrScanner.scanOnce();
๐ Troubleshooting #
Common Issues #
1. Camera Permission Denied
- Ensure permissions are added to AndroidManifest.xml (Android) and Info.plist (iOS)
- Request permissions before using scanner
- Test on physical device (camera not available in simulators)
2. Black Screen in Auto Mode
- Updated in v2.1.0 with better initialization timing
- Ensure proper widget constraints (width/height)
- Check console for platform view creation logs
3. QR Codes Not Detected
- Ensure good lighting conditions
- Check if QR code is clearly visible and not damaged
- Verify QR code format is supported
- Try manual mode if auto-mode has issues
4. Controls Not Responding
- Verify widget configuration parameters
- Check if scanner is properly initialized
- Ensure proper error handling in callbacks
Platform-Specific Issues #
Android:
- Minimum SDK version 21+ required
- Add camera dependencies to build.gradle
- Test on multiple device orientations
iOS:
- Minimum iOS 11.0+ required
- Test on physical device only (simulator has no camera)
- Ensure proper Info.plist configuration
๐ What's New #
[2.1.0] - Latest #
- ๐ค Auto-start scanning mode - Begin scanning immediately when widget appears
- ๐ Enhanced manual controls - Better user-controlled scanning experience
- ๐๏ธ Flexible UI options - Show/hide start/stop button independently
- โฑ๏ธ Improved initialization - Better timing for auto-start mode
- ๐ง Better error handling - More robust initialization and state management
- ๐ฑ Enhanced examples - Complete auto/manual mode demonstrations
[2.0.0] - Previous Major Release #
- ๐ Live camera preview with native platform views
- ๐ธ Camera switching between front/back cameras
- ๐ฆ Flash/torch control for better scanning in low light
- ๐จ Customizable overlays and scanning frames
- ๐งน Improved memory management and lifecycle handling
- ๐ Better performance with native camera integration
๐ค Contributing #
We welcome contributions! Please see our Contributing Guide for details.
Development Setup #
git clone https://github.com/shariaralphabyte/ultra_qr_scanner.git
cd ultra_qr_scanner
flutter pub get
cd example
flutter run
Running Tests #
flutter test # Unit tests
flutter drive --target=test_driver/app.dart # Integration tests
๐ License #
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments #
- MLKit Barcode Scanning for Android QR detection
- Vision Framework for iOS QR detection
- CameraX for Android camera handling
- AVFoundation for iOS camera management
- Flutter team for platform views and native integration
๐ Support #
- ๐ Documentation
- ๐ Issue Tracker
- ๐ฌ Discussions
- ๐ง Email: contact.shariar.cse@gmail.com