feat(02-02): create room providers, form screen, icon picker, and router routes

- Add Riverpod providers (roomWithStatsList, RoomActions) connecting to RoomsDao
- Create RoomFormScreen with name field, icon picker preview, create/edit modes
- Create IconPickerSheet bottom sheet with curated Material Icons grid
- Add nested GoRouter routes: /rooms/new, /rooms/:roomId, /rooms/:roomId/edit
- Add placeholder TaskListScreen and TaskFormScreen for Plan 03 routes
- Add 11 new German localization keys for room management UI
- Add flutter_reorderable_grid_view dependency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 22:00:57 +01:00
parent ead53b4c02
commit 32e61e4bec
11 changed files with 652 additions and 1 deletions

View File

@@ -0,0 +1,94 @@
import 'package:flutter/material.dart';
import 'package:household_keeper/features/rooms/domain/room_icons.dart';
/// Shows a modal bottom sheet with a grid of curated Material Icons.
///
/// Returns the selected icon name, or null if dismissed.
Future<String?> showIconPickerSheet({
required BuildContext context,
String? selectedIconName,
}) {
return showModalBottomSheet<String>(
context: context,
isScrollControlled: true,
builder: (context) => IconPickerSheet(
selectedIconName: selectedIconName,
onIconSelected: (name) => Navigator.of(context).pop(name),
),
);
}
/// Grid of curated household Material Icons for room icon selection.
class IconPickerSheet extends StatelessWidget {
const IconPickerSheet({
super.key,
this.selectedIconName,
required this.onIconSelected,
});
final String? selectedIconName;
final ValueChanged<String> onIconSelected;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Drag handle
Container(
width: 32,
height: 4,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.4),
borderRadius: BorderRadius.circular(2),
),
),
Text(
'Symbol w\u00e4hlen',
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 16),
GridView.count(
crossAxisCount: 5,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
mainAxisSpacing: 8,
crossAxisSpacing: 8,
children: curatedRoomIcons.map((entry) {
final isSelected = entry.name == selectedIconName;
return InkWell(
onTap: () => onIconSelected(entry.name),
borderRadius: BorderRadius.circular(12),
child: Container(
decoration: BoxDecoration(
color: isSelected
? colorScheme.primaryContainer
: Colors.transparent,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
entry.icon,
size: 28,
color: isSelected
? colorScheme.onPrimaryContainer
: colorScheme.onSurface,
),
),
);
}).toList(),
),
const SizedBox(height: 8),
],
),
),
);
}
}