Files
HouseHoldKeaper/lib/features/tasks/presentation/task_history_sheet.dart
Jean-Luc Makiola 9f902ff2c7 feat(06-01): build task history sheet, wire into TaskFormScreen, add CalendarTaskRow navigation
- Create task_history_sheet.dart: showTaskHistorySheet() modal bottom sheet
- Sheet uses StreamBuilder on watchCompletionsForTask, shows dates in dd.MM.yyyy + HH:mm format
- Empty state: Icons.history + 'Noch nie erledigt' message
- Count summary shown above list when completions exist
- Add Verlauf ListTile to TaskFormScreen (edit mode only) opening history sheet
- Add onTap to CalendarTaskRow navigating to /rooms/:roomId/tasks/:taskId
- All 106 tests pass, zero analyze issues
2026-03-16 21:57:11 +01:00

137 lines
4.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import '../../../core/database/database.dart';
import '../../../core/providers/database_provider.dart';
import '../../../l10n/app_localizations.dart';
/// Shows a modal bottom sheet displaying the completion history for a task.
///
/// The sheet displays all past completions in reverse-chronological order
/// (newest first). If the task has never been completed, an empty state is shown.
Future<void> showTaskHistorySheet({
required BuildContext context,
required int taskId,
}) {
return showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
builder: (context) => _TaskHistorySheet(taskId: taskId),
);
}
class _TaskHistorySheet extends ConsumerWidget {
const _TaskHistorySheet({required this.taskId});
final int taskId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final l10n = AppLocalizations.of(context);
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),
),
),
// Title
Text(
l10n.taskHistoryTitle,
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 16),
// Completion list via StreamBuilder
StreamBuilder<List<TaskCompletion>>(
stream: ref
.read(appDatabaseProvider)
.tasksDao
.watchCompletionsForTask(taskId),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final completions = snapshot.data!;
if (completions.isEmpty) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.history,
size: 48,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(height: 8),
Text(
l10n.taskHistoryEmpty,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
);
}
// Show count summary
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
l10n.taskHistoryCount(completions.length),
style: theme.textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 8),
ConstrainedBox(
constraints: BoxConstraints(
maxHeight:
MediaQuery.of(context).size.height * 0.4,
),
child: ListView.builder(
shrinkWrap: true,
itemCount: completions.length,
itemBuilder: (context, index) {
final completion = completions[index];
final dateStr = DateFormat('dd.MM.yyyy', 'de')
.format(completion.completedAt);
final timeStr = DateFormat('HH:mm', 'de')
.format(completion.completedAt);
return ListTile(
leading: Icon(
Icons.check_circle_outline,
color: colorScheme.primary,
),
title: Text(dateStr),
subtitle: Text(timeStr),
);
},
),
),
],
);
},
),
const SizedBox(height: 8),
],
),
),
);
}
}