feat(collections): add active scan collection indicator and quick switch
- Load and display persisted active collection in Collections list - Add Active badge for the currently selected scan destination - Add quick action to set any collection as active for scanner workflow - Auto-set active collection when opening a collection details view
This commit is contained in:
parent
a93694728e
commit
b0b053ed80
1 changed files with 66 additions and 5 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../services/collection_service.dart';
|
import '../services/collection_service.dart';
|
||||||
import '../theme/app_colors.dart';
|
import '../theme/app_colors.dart';
|
||||||
|
|
@ -14,9 +15,12 @@ class CollectionsScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class CollectionsScreenState extends State<CollectionsScreen> {
|
class CollectionsScreenState extends State<CollectionsScreen> {
|
||||||
|
static const _activeCollectionPrefKey = 'active_collection_id';
|
||||||
|
|
||||||
List<Collection> _collections = [];
|
List<Collection> _collections = [];
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
String? _error;
|
String? _error;
|
||||||
|
String? _activeCollectionId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -35,11 +39,24 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final list = await CollectionService.getMyCollections();
|
final list = await CollectionService.getMyCollections();
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final persisted = prefs.getString(_activeCollectionPrefKey);
|
||||||
|
|
||||||
|
String? activeId = persisted;
|
||||||
|
if (activeId != null && !list.any((c) => c.id == activeId)) {
|
||||||
|
activeId = list.isNotEmpty ? list.first.id : null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_collections = list;
|
_collections = list;
|
||||||
|
_activeCollectionId = activeId;
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (activeId != null) {
|
||||||
|
await prefs.setString(_activeCollectionPrefKey, activeId);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -130,6 +147,7 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _openCollection(Collection c) {
|
void _openCollection(Collection c) {
|
||||||
|
_setActiveCollection(c.id);
|
||||||
navigatorKey.currentState!.push(
|
navigatorKey.currentState!.push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GarageScreen(
|
builder: (_) => GarageScreen(
|
||||||
|
|
@ -149,6 +167,14 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
||||||
).then((_) => _load());
|
).then((_) => _load());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _setActiveCollection(String collectionId) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setString(_activeCollectionPrefKey, collectionId);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _activeCollectionId = collectionId);
|
||||||
|
showGlobalSnackBar('Active scan collection set.');
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
@ -267,6 +293,8 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
||||||
final c = _collections[index];
|
final c = _collections[index];
|
||||||
return _CollectionCard(
|
return _CollectionCard(
|
||||||
collection: c,
|
collection: c,
|
||||||
|
isActive: _activeCollectionId == c.id,
|
||||||
|
onSetActive: () => _setActiveCollection(c.id),
|
||||||
onTap: () => _openCollection(c),
|
onTap: () => _openCollection(c),
|
||||||
onManage: () => _manageCollection(c),
|
onManage: () => _manageCollection(c),
|
||||||
);
|
);
|
||||||
|
|
@ -291,11 +319,15 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
||||||
|
|
||||||
class _CollectionCard extends StatelessWidget {
|
class _CollectionCard extends StatelessWidget {
|
||||||
final Collection collection;
|
final Collection collection;
|
||||||
|
final bool isActive;
|
||||||
|
final VoidCallback onSetActive;
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
final VoidCallback onManage;
|
final VoidCallback onManage;
|
||||||
|
|
||||||
const _CollectionCard({
|
const _CollectionCard({
|
||||||
required this.collection,
|
required this.collection,
|
||||||
|
required this.isActive,
|
||||||
|
required this.onSetActive,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
required this.onManage,
|
required this.onManage,
|
||||||
});
|
});
|
||||||
|
|
@ -377,17 +409,46 @@ class _CollectionCard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
if (isActive)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.success.withValues(alpha: 0.14),
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'Active',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: AppColors.success,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Manage button
|
Column(
|
||||||
IconButton(
|
children: [
|
||||||
icon: const Icon(Icons.settings_outlined,
|
IconButton(
|
||||||
color: AppColors.textHint),
|
tooltip: isActive ? 'Already active for scan' : 'Set active for scan',
|
||||||
onPressed: onManage,
|
icon: Icon(
|
||||||
|
isActive ? Icons.my_location : Icons.location_searching,
|
||||||
|
color: isActive ? AppColors.success : AppColors.textHint,
|
||||||
|
),
|
||||||
|
onPressed: isActive ? null : onSetActive,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.settings_outlined,
|
||||||
|
color: AppColors.textHint),
|
||||||
|
onPressed: onManage,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue