feat(scanner): add in-scanner active collection switcher
- Add collection dropdown at top of scanner screen - Wire scanner selection changes back to scan tab active collection state - Reuse persisted active collection behavior while scanning - Keep continuous scan loop intact with collection changes applied immediately
This commit is contained in:
parent
360fd5a705
commit
b9d1d750b6
2 changed files with 53 additions and 1 deletions
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
|
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
|
||||||
|
import 'services/collection_service.dart';
|
||||||
import 'theme/app_colors.dart';
|
import 'theme/app_colors.dart';
|
||||||
|
|
||||||
/// Screen that uses the camera to scan text (OCR) from a Hot Wheels package
|
/// Screen that uses the camera to scan text (OCR) from a Hot Wheels package
|
||||||
|
|
@ -11,8 +12,17 @@ import 'theme/app_colors.dart';
|
||||||
/// The detected ID is returned via Navigator.pop(context, hwId).
|
/// The detected ID is returned via Navigator.pop(context, hwId).
|
||||||
class ScannerScreen extends StatefulWidget {
|
class ScannerScreen extends StatefulWidget {
|
||||||
final Future<bool> Function(String hwId)? onDetected;
|
final Future<bool> Function(String hwId)? onDetected;
|
||||||
|
final List<Collection> collections;
|
||||||
|
final String? activeCollectionId;
|
||||||
|
final ValueChanged<String>? onCollectionChanged;
|
||||||
|
|
||||||
const ScannerScreen({super.key, this.onDetected});
|
const ScannerScreen({
|
||||||
|
super.key,
|
||||||
|
this.onDetected,
|
||||||
|
this.collections = const [],
|
||||||
|
this.activeCollectionId,
|
||||||
|
this.onCollectionChanged,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ScannerScreen> createState() => _ScannerScreenState();
|
State<ScannerScreen> createState() => _ScannerScreenState();
|
||||||
|
|
@ -26,6 +36,7 @@ class _ScannerScreenState extends State<ScannerScreen> {
|
||||||
String? _lastDetected;
|
String? _lastDetected;
|
||||||
bool _scanAccepted = false;
|
bool _scanAccepted = false;
|
||||||
String _statusText = 'Ready to scan';
|
String _statusText = 'Ready to scan';
|
||||||
|
String? _activeCollectionId;
|
||||||
|
|
||||||
// Matches typical Hot Wheels model IDs: 2–5 uppercase letters followed by
|
// Matches typical Hot Wheels model IDs: 2–5 uppercase letters followed by
|
||||||
// 2–4 digits, e.g. JKF21, HCV73, GRX33, FYD83.
|
// 2–4 digits, e.g. JKF21, HCV73, GRX33, FYD83.
|
||||||
|
|
@ -34,10 +45,17 @@ class _ScannerScreenState extends State<ScannerScreen> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_activeCollectionId = widget.activeCollectionId;
|
||||||
_textRecognizer = TextRecognizer();
|
_textRecognizer = TextRecognizer();
|
||||||
_initCamera();
|
_initCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handleCollectionChanged(String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setState(() => _activeCollectionId = value);
|
||||||
|
widget.onCollectionChanged?.call(value);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _initCamera() async {
|
Future<void> _initCamera() async {
|
||||||
final cameras = await availableCameras();
|
final cameras = await availableCameras();
|
||||||
if (cameras.isEmpty) {
|
if (cameras.isEmpty) {
|
||||||
|
|
@ -188,6 +206,37 @@ class _ScannerScreenState extends State<ScannerScreen> {
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
|
if (widget.collections.isNotEmpty)
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.fromLTRB(12, 10, 12, 8),
|
||||||
|
color: Colors.black.withValues(alpha: 0.55),
|
||||||
|
child: DropdownButtonHideUnderline(
|
||||||
|
child: DropdownButton<String>(
|
||||||
|
isExpanded: true,
|
||||||
|
value: _activeCollectionId,
|
||||||
|
dropdownColor: AppColors.navy,
|
||||||
|
iconEnabledColor: Colors.white,
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
hint: const Text(
|
||||||
|
'Select collection',
|
||||||
|
style: TextStyle(color: Colors.white70),
|
||||||
|
),
|
||||||
|
items: widget.collections
|
||||||
|
.map(
|
||||||
|
(c) => DropdownMenuItem<String>(
|
||||||
|
value: c.id,
|
||||||
|
child: Text(
|
||||||
|
c.name,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
onChanged: _isBusy ? null : _handleCollectionChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,9 @@ class ScanTabState extends State<ScanTab> {
|
||||||
await navigatorKey.currentState!.push<void>(
|
await navigatorKey.currentState!.push<void>(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => ScannerScreen(
|
builder: (_) => ScannerScreen(
|
||||||
|
collections: _collections,
|
||||||
|
activeCollectionId: _selectedCollection?.id,
|
||||||
|
onCollectionChanged: _setActiveCollection,
|
||||||
onDetected: (hwId) => _processHwId(hwId),
|
onDetected: (hwId) => _processHwId(hwId),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue