Files
HouseHoldKeaper/lib/features/tasks/presentation/task_row.dart
Jean-Luc Makiola d220dbe5ce
All checks were successful
Build and Release to F-Droid / build-and-deploy (push) Successful in 10m30s
test(TaskListScreen): add integration tests for filtered and overdue task states
- Covers empty states, celebration state, and scheduled/overdue task rendering
- Verifies proper checkbox behavior for future tasks
- Tests AppBar for sort dropdown, edit/delete actions, and calendar strip
- Adds necessary test helpers and overrides for room-specific tasks
2026-03-16 23:35:17 +01:00

110 lines
3.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:household_keeper/core/database/database.dart';
import 'package:household_keeper/features/tasks/domain/frequency.dart';
import 'package:household_keeper/features/tasks/domain/relative_date.dart';
import 'package:household_keeper/features/tasks/presentation/task_providers.dart';
/// Warm coral/terracotta color for overdue due date text.
const _overdueColor = Color(0xFFE07A5F);
/// A single task row with leading checkbox, name, relative due date,
/// and frequency label.
///
/// Per user decisions:
/// - Checkbox marks task done immediately (optimistic UI, no undo)
/// - Row tap opens edit form
/// - No swipe gesture
/// - No effort indicator or description preview on list view
/// - Overdue: due date text turns warm coral, rest stays normal
class TaskRow extends ConsumerWidget {
const TaskRow({
super.key,
required this.task,
this.onDelete,
});
final Task task;
final VoidCallback? onDelete;
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
// Check if task is overdue (due date is before today)
final dueDate = DateTime(
task.nextDueDate.year,
task.nextDueDate.month,
task.nextDueDate.day,
);
final isOverdue = dueDate.isBefore(today);
final isFuture = dueDate.isAfter(today);
// Format relative due date in German
final relativeDateText = formatRelativeDate(task.nextDueDate, now);
// Build frequency label
final freq = FrequencyInterval(
intervalType: task.intervalType,
days: task.intervalDays,
);
final frequencyText = freq.label();
return ListTile(
leading: Checkbox(
value: false, // Always unchecked -- completion is immediate + reschedule
onChanged: isFuture
? null // Future tasks cannot be completed yet
: (_) {
// Mark done immediately (optimistic UI, no undo per user decision)
ref.read(taskActionsProvider.notifier).completeTask(task.id);
},
),
title: Text(
task.name,
style: theme.textTheme.titleMedium,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: Row(
children: [
Text(
relativeDateText,
style: theme.textTheme.bodySmall?.copyWith(
color: isOverdue ? _overdueColor : theme.colorScheme.onSurfaceVariant,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6),
child: Text(
'\u00b7', // middle dot separator
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
Flexible(
child: Text(
frequencyText,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
onTap: () {
// Tap row opens edit form
context.go('/rooms/${task.roomId}/tasks/${task.id}');
},
onLongPress: onDelete,
);
}
}