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:shared_preferences/shared_preferences.dart';
|
||||
import '../main.dart';
|
||||
import '../services/collection_service.dart';
|
||||
import '../theme/app_colors.dart';
|
||||
|
|
@ -14,9 +15,12 @@ class CollectionsScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class CollectionsScreenState extends State<CollectionsScreen> {
|
||||
static const _activeCollectionPrefKey = 'active_collection_id';
|
||||
|
||||
List<Collection> _collections = [];
|
||||
bool _isLoading = true;
|
||||
String? _error;
|
||||
String? _activeCollectionId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -35,11 +39,24 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
|||
|
||||
try {
|
||||
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;
|
||||
setState(() {
|
||||
_collections = list;
|
||||
_activeCollectionId = activeId;
|
||||
_isLoading = false;
|
||||
});
|
||||
|
||||
if (activeId != null) {
|
||||
await prefs.setString(_activeCollectionPrefKey, activeId);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
|
|
@ -130,6 +147,7 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
|||
}
|
||||
|
||||
void _openCollection(Collection c) {
|
||||
_setActiveCollection(c.id);
|
||||
navigatorKey.currentState!.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => GarageScreen(
|
||||
|
|
@ -149,6 +167,14 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
|||
).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
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
|
@ -267,6 +293,8 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
|||
final c = _collections[index];
|
||||
return _CollectionCard(
|
||||
collection: c,
|
||||
isActive: _activeCollectionId == c.id,
|
||||
onSetActive: () => _setActiveCollection(c.id),
|
||||
onTap: () => _openCollection(c),
|
||||
onManage: () => _manageCollection(c),
|
||||
);
|
||||
|
|
@ -291,11 +319,15 @@ class CollectionsScreenState extends State<CollectionsScreen> {
|
|||
|
||||
class _CollectionCard extends StatelessWidget {
|
||||
final Collection collection;
|
||||
final bool isActive;
|
||||
final VoidCallback onSetActive;
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback onManage;
|
||||
|
||||
const _CollectionCard({
|
||||
required this.collection,
|
||||
required this.isActive,
|
||||
required this.onSetActive,
|
||||
required this.onTap,
|
||||
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
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings_outlined,
|
||||
color: AppColors.textHint),
|
||||
onPressed: onManage,
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
tooltip: isActive ? 'Already active for scan' : 'Set active for scan',
|
||||
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