Last active
September 26, 2024 06:28
-
-
Save saliouseck2009/8dfd10145a73c5e5e61998e23de13753 to your computer and use it in GitHub Desktop.
Flutter Barcode Scanner with Mobile Scanner
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:flutter/material.dart'; | |
import 'package:mobile_scanner/mobile_scanner.dart'; | |
class ScannerWidget extends StatefulWidget { | |
final String? title; | |
final void Function(String value) onScan; | |
const ScannerWidget({super.key, required this.onScan, this.title,}); | |
@override | |
State<ScannerWidget> createState() => _ScannerState(); | |
} | |
class _ScannerState extends State<ScannerWidget> with WidgetsBindingObserver { | |
final MobileScannerController controller = MobileScannerController( | |
autoStart: false, | |
torchEnabled: false, | |
useNewCameraSelector: true, | |
); | |
StreamSubscription<Object?>? _subscription; | |
void _handleBarcode(BarcodeCapture barcodes) { | |
if (mounted) { | |
final String? value = barcodes.barcodes.firstOrNull?.displayValue?.trim(); | |
if(value != null) { | |
widget.onScan(value); | |
controller.stop(); | |
} | |
} | |
} | |
@override | |
void initState() { | |
super.initState(); | |
WidgetsBinding.instance.addObserver(this); | |
_subscription = controller.barcodes.listen(_handleBarcode); | |
unawaited(controller.start()); | |
} | |
@override | |
void didChangeAppLifecycleState(AppLifecycleState state) { | |
if (!controller.value.isInitialized) { | |
return; | |
} | |
switch (state) { | |
case AppLifecycleState.detached: | |
case AppLifecycleState.hidden: | |
case AppLifecycleState.paused: | |
return; | |
case AppLifecycleState.resumed: | |
_subscription = controller.barcodes.listen(_handleBarcode); | |
unawaited(controller.start()); | |
case AppLifecycleState.inactive: | |
unawaited(_subscription?.cancel()); | |
_subscription = null; | |
unawaited(controller.stop()); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
final String title = widget.title ?? 'scan_your_badge'; | |
return Scaffold( | |
backgroundColor: Colors.white, | |
body: Stack( | |
children: [ | |
Align( | |
alignment: Alignment.center, | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
crossAxisAlignment: CrossAxisAlignment.center, | |
children: [ | |
Container( | |
width: 300, | |
height: 300, | |
alignment: Alignment.topCenter, | |
decoration: BoxDecoration( | |
border: Border.all(color: Colors.blue, width: 4), | |
color: Colors.blue, | |
), | |
padding: const EdgeInsets.all(5), | |
child: MobileScanner( | |
controller: controller, | |
errorBuilder: (context, error, child) { | |
return Center(child: Text(error.toString())); | |
}, | |
fit: BoxFit.fill, | |
), | |
), | |
const SizedBox(height: 25), | |
Center( | |
child: Container( | |
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 15), | |
decoration: BoxDecoration( | |
color: Colors.white, | |
borderRadius: BorderRadius.circular(10), | |
), | |
child: Text( | |
title, | |
style: const TextStyle(fontSize: 16), | |
), | |
), | |
), | |
] | |
), | |
), | |
Align( | |
alignment: Alignment.bottomCenter, | |
child: Container( | |
alignment: Alignment.center, | |
height: 100, | |
color: Colors.black.withOpacity(0.4), | |
child: Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: [ | |
ToggleFlashlightButton(controller: controller), | |
], | |
), | |
), | |
), | |
], | |
), | |
); | |
} | |
@override | |
Future<void> dispose() async { | |
WidgetsBinding.instance.removeObserver(this); | |
unawaited(_subscription?.cancel()); | |
_subscription = null; | |
super.dispose(); | |
await controller.dispose(); | |
} | |
} | |
class ToggleFlashlightButton extends StatelessWidget { | |
const ToggleFlashlightButton({required this.controller, super.key}); | |
final MobileScannerController controller; | |
@override | |
Widget build(BuildContext context) { | |
return ValueListenableBuilder( | |
valueListenable: controller, | |
builder: (context, state, child) { | |
if (!state.isInitialized || !state.isRunning) { | |
return const SizedBox.shrink(); | |
} | |
switch (state.torchState) { | |
case TorchState.auto: | |
return IconButton( | |
color: Colors.white, | |
iconSize: 32.0, | |
icon: const Icon(Icons.flash_auto), | |
onPressed: () async { | |
await controller.toggleTorch(); | |
}, | |
); | |
case TorchState.off: | |
return IconButton( | |
color: Colors.white, | |
iconSize: 32.0, | |
icon: const Icon(Icons.flash_off), | |
onPressed: () async { | |
await controller.toggleTorch(); | |
}, | |
); | |
case TorchState.on: | |
return IconButton( | |
color: Colors.white, | |
iconSize: 32.0, | |
icon: const Icon(Icons.flash_on), | |
onPressed: () async { | |
await controller.toggleTorch(); | |
}, | |
); | |
case TorchState.unavailable: | |
return const Icon( | |
Icons.no_flash, | |
color: Colors.grey, | |
); | |
} | |
}, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment