feat(02-03): create task providers, form screen with frequency and effort selectors

- TaskActions AsyncNotifier for create, update, delete, complete task mutations
- tasksInRoomProvider manual StreamProvider.family wrapping TasksDao.watchTasksInRoom
- TaskFormScreen with name, frequency (10 presets + custom), effort (3-way segmented),
  description, and initial due date picker (German DD.MM.YYYY format)
- Custom frequency: number + unit picker (Tage/Wochen/Monate)
- Calendar-anchored intervals auto-set anchorDay from due date
- Edit mode loads existing task and pre-fills all fields
- 19 new German localization keys for task form, delete, and empty state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 22:07:53 +01:00
parent 519a56bef7
commit 652ff0123f
7 changed files with 756 additions and 8 deletions

View File

@@ -0,0 +1,65 @@
import 'package:drift/drift.dart' show Value;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:household_keeper/core/database/database.dart';
import 'package:household_keeper/core/providers/database_provider.dart';
import 'package:household_keeper/features/tasks/domain/effort_level.dart';
import 'package:household_keeper/features/tasks/domain/frequency.dart';
part 'task_providers.g.dart';
/// Stream provider family for tasks in a specific room, sorted by due date.
///
/// Defined manually because riverpod_generator has trouble with drift's
/// generated [Task] type in family provider return types.
final tasksInRoomProvider =
StreamProvider.family.autoDispose<List<Task>, int>((ref, roomId) {
final db = ref.watch(appDatabaseProvider);
return db.tasksDao.watchTasksInRoom(roomId);
});
/// Notifier for task mutations: create, update, delete, complete.
@riverpod
class TaskActions extends _$TaskActions {
@override
FutureOr<void> build() {}
Future<int> createTask({
required int roomId,
required String name,
String? description,
required IntervalType intervalType,
required int intervalDays,
int? anchorDay,
required EffortLevel effortLevel,
required DateTime nextDueDate,
}) async {
final db = ref.read(appDatabaseProvider);
return db.tasksDao.insertTask(TasksCompanion.insert(
roomId: roomId,
name: name,
description: Value(description),
intervalType: intervalType,
intervalDays: Value(intervalDays),
anchorDay: Value(anchorDay),
effortLevel: effortLevel,
nextDueDate: nextDueDate,
));
}
Future<void> updateTask(Task task) async {
final db = ref.read(appDatabaseProvider);
await db.tasksDao.updateTask(task);
}
Future<void> deleteTask(int taskId) async {
final db = ref.read(appDatabaseProvider);
await db.tasksDao.deleteTask(taskId);
}
Future<void> completeTask(int taskId) async {
final db = ref.read(appDatabaseProvider);
await db.tasksDao.completeTask(taskId);
}
}