qr_scanner_hybrid 0.1.0+1
qr_scanner_hybrid: ^0.1.0+1 copied to clipboard
High-accuracy QR code scanner using dual scanning engines (Google ML Kit + ZXing) for maximum reliability in various lighting conditions.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:qr_scanner_hybrid/qr_scanner_hybrid.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'QR Scanner Hybrid Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String? _lastScannedCode;
String? _lastEngine;
final List<Map<String, String>> _scanHistory = [];
// Example 1: With callback and auto-close
void _scanWithAutoClose() async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QrScannerHybrid(
autoClose: true,
onDetected: (data, engine) {
print('Detected: $data via $engine');
},
config: const ScannerConfig(
title: 'Scan QR Code',
borderColor: Colors.blue,
scanAreaSize: 0.75,
),
),
),
);
if (result != null && result is Map<String, String>) {
_addToHistory(result['data']!, result['engine']!);
}
}
// Example 2: With custom UI config
void _scanWithCustomUI() async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QrScannerHybrid(
onDetected: (data, engine) {
_showSnackBar('Scanned: ${data.substring(0, 20)}...');
},
config: const ScannerConfig(
title: 'Custom Scanner',
borderColor: Colors.purple,
borderWidth: 4,
borderRadius: 20,
scanAreaSize: 0.65,
appBarColor: Colors.purple,
overlayColor: Colors.black45,
),
),
),
);
if (result != null && result is Map<String, String>) {
_addToHistory(result['data']!, result['engine']!);
}
}
// Example 3: Minimal UI
void _scanMinimal() async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QrScannerHybrid(
autoClose: true,
config: const ScannerConfig(
showAppBar: true,
showStatusText: false,
borderColor: Colors.green,
scanAreaSize: 0.8,
),
),
),
);
if (result != null && result is Map<String, String>) {
_addToHistory(result['data']!, result['engine']!);
}
}
// Example 4: With onClose callback
void _scanWithCloseCallback() async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QrScannerHybrid(
onDetected: (data, engine) {
_addToHistory(data, engine);
},
onClose: () {
print('Scanner closed');
},
config: const ScannerConfig(
title: 'Scan & Track',
borderColor: Colors.orange,
),
),
),
);
}
void _addToHistory(String data, String engine) {
setState(() {
_lastScannedCode = data;
_lastEngine = engine;
_scanHistory.insert(0, {'data': data, 'engine': engine});
if (_scanHistory.length > 10) {
_scanHistory.removeLast();
}
});
}
void _showSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('QR Scanner Hybrid Example'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header
const Icon(
Icons.qr_code_scanner,
size: 80,
color: Colors.blue,
),
const SizedBox(height: 16),
const Text(
'High Accuracy QR Scanner',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
const Text(
'Dual engine scanning with ML Kit + ZXing',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
const SizedBox(height: 32),
// Scan Options
const Text(
'Scan Examples:',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
ElevatedButton.icon(
onPressed: _scanWithAutoClose,
icon: const Icon(Icons.flash_auto),
label: const Text('Auto-Close Scanner'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(16),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _scanWithCustomUI,
icon: const Icon(Icons.palette),
label: const Text('Custom UI Colors'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(16),
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _scanMinimal,
icon: const Icon(Icons.minimize),
label: const Text('Minimal UI'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(16),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _scanWithCloseCallback,
icon: const Icon(Icons.track_changes),
label: const Text('With Callbacks'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(16),
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
),
),
const SizedBox(height: 32),
// Last Scan Result
if (_lastScannedCode != null) ...[
const Text(
'Last Scan:',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.green.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.check_circle,
color: Colors.green, size: 20),
const SizedBox(width: 8),
Text(
'Detected via $_lastEngine',
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
],
),
const SizedBox(height: 12),
SelectableText(
_lastScannedCode!,
style: const TextStyle(
fontSize: 14,
fontFamily: 'monospace',
),
),
],
),
),
const SizedBox(height: 24),
],
// Scan History
if (_scanHistory.isNotEmpty) ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Scan History:',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
TextButton(
onPressed: () {
setState(() {
_scanHistory.clear();
_lastScannedCode = null;
_lastEngine = null;
});
},
child: const Text('Clear'),
),
],
),
const SizedBox(height: 8),
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _scanHistory.length,
itemBuilder: (context, index) {
final scan = _scanHistory[index];
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: CircleAvatar(
child: Text('${index + 1}'),
),
title: Text(
scan['data']!.length > 50
? '${scan['data']!.substring(0, 50)}...'
: scan['data']!,
style: const TextStyle(fontSize: 12),
),
subtitle: Text(
'via ${scan['engine']}',
style: const TextStyle(fontSize: 11),
),
),
);
},
),
],
],
),
),
);
}
}