Files
Prisma/lib/features/collections/presentation/collections_screen.dart
T

176 lines
6.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../backend/models/collection.dart';
import '../../../shared/widgets/adaptive_scaffold.dart';
import '../../../shared/widgets/confirm_dialog.dart';
import '../../../shared/widgets/empty_view.dart';
import '../../../shared/widgets/error_view.dart';
import 'collection_form_dialog.dart';
import 'collections_controller.dart';
class CollectionsScreen extends ConsumerWidget {
const CollectionsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final collections = ref.watch(collectionsControllerProvider);
return AdaptiveScaffold(
title: 'Collections',
actions: [
IconButton(
tooltip: 'New collection',
icon: const Icon(Icons.add_rounded),
onPressed: () => showCollectionFormDialog(context, ref),
),
],
body: collections.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => ErrorView(message: error.toString()),
data: (items) => items.isEmpty
? const EmptyView(title: 'No collections yet')
: GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 320,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 1.35,
),
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return _CollectionCard(
collection: item,
onOpen: () => context.go('/collections/${item.id}'),
onEdit: () => showCollectionFormDialog(
context,
ref,
collection: item,
),
onDelete: () async {
final ok = await showConfirmDialog(
context,
title: 'Delete collection',
message: 'Remove ${item.name}?',
);
if (ok) {
await ref
.read(collectionsControllerProvider.notifier)
.delete(item.id);
}
},
);
},
),
),
);
}
}
class _CollectionCard extends StatelessWidget {
const _CollectionCard({
required this.collection,
required this.onOpen,
required this.onEdit,
required this.onDelete,
});
final Collection collection;
final VoidCallback onOpen;
final VoidCallback onEdit;
final VoidCallback onDelete;
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Card(
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: onOpen,
child: Stack(
children: [
Positioned.fill(
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 54,
child: Align(
alignment: Alignment.centerLeft,
child: DecoratedBox(
decoration: BoxDecoration(
color:
scheme.primaryContainer.withValues(alpha: 0.56),
borderRadius: BorderRadius.circular(12),
),
child: const Padding(
padding: EdgeInsets.all(12),
child: Icon(
Icons.collections_bookmark_rounded,
size: 28,
),
),
),
),
),
const Spacer(),
Text(
collection.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 4),
Text(
collection.description ?? 'No description',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: scheme.onSurfaceVariant,
),
),
],
),
),
),
Positioned(
top: 6,
right: 6,
child: PopupMenuButton<String>(
tooltip: 'Collection actions',
icon: const Icon(Icons.more_horiz_rounded),
onSelected: (value) {
if (value == 'edit') onEdit();
if (value == 'delete') onDelete();
},
itemBuilder: (context) => const [
PopupMenuItem(
value: 'edit',
child: ListTile(
dense: true,
leading: Icon(Icons.edit_rounded),
title: Text('Edit'),
),
),
PopupMenuItem(
value: 'delete',
child: ListTile(
dense: true,
leading: Icon(Icons.delete_rounded),
title: Text('Delete'),
),
),
],
),
),
],
),
),
);
}
}