166 lines
6.1 KiB
Dart
166 lines
6.1 KiB
Dart
|
|
import 'dart:async';
|
||
|
|
|
||
|
|
import 'package:cb_prestige_qr/core/presentation/widgets/start_loading_overlay.dart';
|
||
|
|
import 'package:cb_prestige_qr/features/scan/presentation/manager/scan_controller.dart';
|
||
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||
|
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
||
|
|
|
||
|
|
class ScanPage extends ConsumerStatefulWidget {
|
||
|
|
const ScanPage({super.key});
|
||
|
|
|
||
|
|
@override
|
||
|
|
ConsumerState<ScanPage> createState() => _ScanPageState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _ScanPageState extends ConsumerState<ScanPage> {
|
||
|
|
late final MobileScannerController _scannerController;
|
||
|
|
|
||
|
|
@override
|
||
|
|
void initState() {
|
||
|
|
super.initState();
|
||
|
|
_scannerController = MobileScannerController();
|
||
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
|
|
if (!mounted) return;
|
||
|
|
ref.read(scanControllerProvider.notifier).resumeScanning();
|
||
|
|
unawaited(_scannerController.start());
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
void dispose() {
|
||
|
|
_scannerController.dispose();
|
||
|
|
super.dispose();
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
ref.listen<bool>(scanControllerProvider.select((s) => s.isScanning), (
|
||
|
|
previous,
|
||
|
|
next,
|
||
|
|
) {
|
||
|
|
if (previous == next) return;
|
||
|
|
if (next) {
|
||
|
|
unawaited(_scannerController.start());
|
||
|
|
} else {
|
||
|
|
unawaited(_scannerController.stop());
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
final scanState = ref.watch(scanControllerProvider);
|
||
|
|
|
||
|
|
void onDetect(BarcodeCapture capture) {
|
||
|
|
if (!scanState.isScanning) return;
|
||
|
|
final value = capture.barcodes.isEmpty
|
||
|
|
? null
|
||
|
|
: capture.barcodes.first.rawValue;
|
||
|
|
if (value == null || value.isEmpty) return;
|
||
|
|
|
||
|
|
unawaited(
|
||
|
|
ref.read(scanControllerProvider.notifier).onBarcodeDetected(value),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return StartLoadingOverlay(
|
||
|
|
child: Scaffold(
|
||
|
|
appBar: AppBar(title: const Text('Scan QR')),
|
||
|
|
body: Stack(
|
||
|
|
children: [
|
||
|
|
MobileScanner(controller: _scannerController, onDetect: onDetect),
|
||
|
|
Align(
|
||
|
|
alignment: Alignment.center,
|
||
|
|
child: Container(
|
||
|
|
width: 250,
|
||
|
|
height: 250,
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
border: Border.all(color: Colors.white, width: 3),
|
||
|
|
borderRadius: BorderRadius.circular(12),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
if (scanState.isProcessing)
|
||
|
|
const Positioned.fill(
|
||
|
|
child: ColoredBox(
|
||
|
|
color: Color(0x66000000),
|
||
|
|
child: Center(child: CircularProgressIndicator()),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
if (!scanState.isProcessing &&
|
||
|
|
(scanState.scannedValue != null ||
|
||
|
|
scanState.errorMessage != null))
|
||
|
|
Positioned.fill(
|
||
|
|
child: ColoredBox(
|
||
|
|
color: const Color(0x66000000),
|
||
|
|
child: SafeArea(
|
||
|
|
child: Center(
|
||
|
|
child: ConstrainedBox(
|
||
|
|
constraints: const BoxConstraints(maxWidth: 420),
|
||
|
|
child: Padding(
|
||
|
|
padding: const EdgeInsets.all(16),
|
||
|
|
child: Material(
|
||
|
|
borderRadius: BorderRadius.circular(16),
|
||
|
|
clipBehavior: Clip.antiAlias,
|
||
|
|
child: Padding(
|
||
|
|
padding: const EdgeInsets.all(16),
|
||
|
|
child: Column(
|
||
|
|
mainAxisSize: MainAxisSize.min,
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
|
|
children: [
|
||
|
|
Text(
|
||
|
|
scanState.errorMessage == null
|
||
|
|
? 'Scanned Result'
|
||
|
|
: 'Scan Error',
|
||
|
|
style: Theme.of(
|
||
|
|
context,
|
||
|
|
).textTheme.titleLarge,
|
||
|
|
),
|
||
|
|
const SizedBox(height: 12),
|
||
|
|
SelectableText(
|
||
|
|
scanState.errorMessage ??
|
||
|
|
scanState.scannedValue ??
|
||
|
|
'',
|
||
|
|
),
|
||
|
|
const SizedBox(height: 16),
|
||
|
|
Row(
|
||
|
|
children: [
|
||
|
|
Expanded(
|
||
|
|
child: OutlinedButton(
|
||
|
|
onPressed: () {
|
||
|
|
ref
|
||
|
|
.read(
|
||
|
|
scanControllerProvider
|
||
|
|
.notifier,
|
||
|
|
)
|
||
|
|
.resumeScanning();
|
||
|
|
},
|
||
|
|
child: const Text('Scan Again'),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(width: 12),
|
||
|
|
Expanded(
|
||
|
|
child: FilledButton(
|
||
|
|
onPressed: () {
|
||
|
|
Navigator.pop(context);
|
||
|
|
},
|
||
|
|
child: const Text('Back'),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|