123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- import 'dart:async';
- import 'package:flutter/material.dart';
- import 'package:go_router/go_router.dart';
- import 'package:mobile_scanner/mobile_scanner.dart';
- import 'package:flutter_gen/gen_l10n/app_localizations.dart';
- class ScanPage extends StatelessWidget {
- const ScanPage({super.key, required this.title});
- final String title;
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(title: Text(title)),
- body: ScanView(
- resultHandler: (capture, resumeScanner) {
- context.pop(capture.barcodes.first.rawValue);
- },
- ),
- );
- }
- }
- typedef ScanViewResultHandler = void Function(
- BarcodeCapture capture,
- void Function() resumeScanner,
- );
- class ScanView extends StatefulWidget {
- const ScanView({super.key, required this.resultHandler});
- /// handle the QR code result.
- ///
- /// The scanner will stop each time the result is complete.
- /// Process the result here and then resume the scanner by calling `resumeScanner`.
- final ScanViewResultHandler resultHandler;
- @override
- State<ScanView> createState() => _ScanViewState();
- }
- class _ScanViewState extends State<ScanView> with WidgetsBindingObserver {
- final scannerController = MobileScannerController(
- formats: [BarcodeFormat.qrCode],
- );
- StreamSubscription? subscription;
- bool hasResult = false;
- void resumeScanner() {
- hasResult = false;
- scannerController.start();
- }
- void handleQRcode(BarcodeCapture capture) {
- if (hasResult) return;
- hasResult = true;
- scannerController.stop().then((_) {
- widget.resultHandler(capture, resumeScanner);
- });
- }
- @override
- void initState() {
- super.initState();
- // Start listening to lifecycle changes.
- WidgetsBinding.instance.addObserver(this);
- // Start listening to the barcode events.
- subscription = scannerController.barcodes.listen(handleQRcode);
- // Finally, start the scanner itself.
- scannerController.start();
- }
- @override
- void didChangeAppLifecycleState(AppLifecycleState state) {
- // If the controller is not ready, do not try to start or stop it.
- // Permission dialogs can trigger lifecycle changes before the controller is ready.
- if (!scannerController.value.isInitialized) return;
- switch (state) {
- case AppLifecycleState.detached:
- case AppLifecycleState.hidden:
- case AppLifecycleState.paused:
- return;
- case AppLifecycleState.resumed:
- // Restart the scanner when the app is resumed.
- // Don't forget to resume listening to the barcode events.
- subscription = scannerController.barcodes.listen(handleQRcode);
- scannerController.start();
- break;
- case AppLifecycleState.inactive:
- // Stop the scanner when the app is paused.
- // Also stop the barcode events subscription.
- subscription?.cancel();
- subscription = null;
- scannerController.stop();
- break;
- }
- }
- @override
- Widget build(BuildContext context) {
- return MobileScanner(
- controller: scannerController,
- errorBuilder: (context, error, _) {
- final scheme = Theme.of(context).colorScheme;
- return ColoredBox(
- color: scheme.surface,
- child: Center(
- child: switch (error.errorCode) {
- MobileScannerErrorCode.controllerAlreadyInitialized ||
- MobileScannerErrorCode.controllerDisposed ||
- MobileScannerErrorCode.controllerUninitialized ||
- MobileScannerErrorCode.genericError ||
- MobileScannerErrorCode.unsupported =>
- Icon(Icons.error, color: scheme.error),
- MobileScannerErrorCode.permissionDenied => Text(
- AppLocalizations.of(context)!.scanPagePermissionDeniedMsg,
- textAlign: TextAlign.center,
- ),
- },
- ),
- );
- },
- );
- }
- @override
- void dispose() async {
- // Stop listening to lifecycle changes.
- WidgetsBinding.instance.removeObserver(this);
- // Stop listening to the barcode events.
- subscription?.cancel();
- subscription = null;
- super.dispose();
- // Finally, dispose the controller.
- scannerController.dispose();
- }
- }
|