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:
@@ -1,18 +1,442 @@
|
|||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
/// Placeholder for the task creation/edit form.
|
import '../../../core/database/database.dart';
|
||||||
/// Will be fully implemented in Plan 03.
|
import '../../../core/providers/database_provider.dart';
|
||||||
class TaskFormScreen extends StatelessWidget {
|
import '../../../l10n/app_localizations.dart';
|
||||||
const TaskFormScreen({super.key, this.roomId, this.taskId});
|
import '../domain/effort_level.dart';
|
||||||
|
import '../domain/frequency.dart';
|
||||||
|
import 'task_providers.dart';
|
||||||
|
|
||||||
|
/// Full-screen form for task creation and editing.
|
||||||
|
///
|
||||||
|
/// Pass [roomId] for create mode, or [taskId] for edit mode.
|
||||||
|
class TaskFormScreen extends ConsumerStatefulWidget {
|
||||||
final int? roomId;
|
final int? roomId;
|
||||||
final int? taskId;
|
final int? taskId;
|
||||||
|
|
||||||
|
const TaskFormScreen({super.key, this.roomId, this.taskId});
|
||||||
|
|
||||||
|
bool get isEditing => taskId != null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<TaskFormScreen> createState() => _TaskFormScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TaskFormScreenState extends ConsumerState<TaskFormScreen> {
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
final _nameController = TextEditingController();
|
||||||
|
final _descriptionController = TextEditingController();
|
||||||
|
final _customIntervalController = TextEditingController(text: '2');
|
||||||
|
|
||||||
|
FrequencyInterval? _selectedPreset;
|
||||||
|
bool _isCustomFrequency = false;
|
||||||
|
_CustomUnit _customUnit = _CustomUnit.days;
|
||||||
|
EffortLevel _effortLevel = EffortLevel.medium;
|
||||||
|
DateTime _dueDate = DateTime.now();
|
||||||
|
Task? _existingTask;
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_selectedPreset = FrequencyInterval.presets[3]; // Default: weekly
|
||||||
|
_dueDate = _dateOnly(DateTime.now());
|
||||||
|
if (widget.isEditing) {
|
||||||
|
_loadExistingTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadExistingTask() async {
|
||||||
|
final db = ref.read(appDatabaseProvider);
|
||||||
|
final task = await (db.select(db.tasks)
|
||||||
|
..where((t) => t.id.equals(widget.taskId!)))
|
||||||
|
.getSingle();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_existingTask = task;
|
||||||
|
_nameController.text = task.name;
|
||||||
|
_descriptionController.text = task.description ?? '';
|
||||||
|
_effortLevel = task.effortLevel;
|
||||||
|
_dueDate = task.nextDueDate;
|
||||||
|
|
||||||
|
// Find matching preset
|
||||||
|
_selectedPreset = null;
|
||||||
|
_isCustomFrequency = true;
|
||||||
|
for (final preset in FrequencyInterval.presets) {
|
||||||
|
if (preset.intervalType == task.intervalType &&
|
||||||
|
preset.days == task.intervalDays) {
|
||||||
|
_selectedPreset = preset;
|
||||||
|
_isCustomFrequency = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isCustomFrequency) {
|
||||||
|
// Determine custom unit from stored interval
|
||||||
|
switch (task.intervalType) {
|
||||||
|
case IntervalType.everyNMonths:
|
||||||
|
_customUnit = _CustomUnit.months;
|
||||||
|
_customIntervalController.text = task.intervalDays.toString();
|
||||||
|
case IntervalType.monthly:
|
||||||
|
_customUnit = _CustomUnit.months;
|
||||||
|
_customIntervalController.text = '1';
|
||||||
|
case IntervalType.weekly:
|
||||||
|
_customUnit = _CustomUnit.weeks;
|
||||||
|
_customIntervalController.text = '1';
|
||||||
|
case IntervalType.biweekly:
|
||||||
|
_customUnit = _CustomUnit.weeks;
|
||||||
|
_customIntervalController.text = '2';
|
||||||
|
default:
|
||||||
|
_customUnit = _CustomUnit.days;
|
||||||
|
_customIntervalController.text = task.intervalDays.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_nameController.dispose();
|
||||||
|
_descriptionController.dispose();
|
||||||
|
_customIntervalController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
int get _roomId => widget.roomId ?? _existingTask!.roomId;
|
||||||
|
|
||||||
|
static DateTime _dateOnly(DateTime dt) => DateTime(dt.year, dt.month, dt.day);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = AppLocalizations.of(context);
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Aufgabe')),
|
appBar: AppBar(
|
||||||
body: const Center(child: Text('Demnächst verfügbar')),
|
title: Text(
|
||||||
|
widget.isEditing ? l10n.taskFormEditTitle : l10n.taskFormCreateTitle,
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: _isLoading ? null : _onSave,
|
||||||
|
icon: const Icon(Icons.check),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
// Name field (required, autofocus)
|
||||||
|
TextFormField(
|
||||||
|
controller: _nameController,
|
||||||
|
autofocus: !widget.isEditing,
|
||||||
|
maxLength: 200,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: l10n.taskFormNameLabel,
|
||||||
|
hintText: l10n.taskFormNameHint,
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.trim().isEmpty) {
|
||||||
|
return l10n.taskFormNameRequired;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Frequency selector
|
||||||
|
Text(
|
||||||
|
l10n.taskFormFrequencyLabel,
|
||||||
|
style: theme.textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildFrequencySelector(l10n, theme),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Effort selector
|
||||||
|
Text(
|
||||||
|
l10n.taskFormEffortLabel,
|
||||||
|
style: theme.textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildEffortSelector(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Description (optional)
|
||||||
|
TextFormField(
|
||||||
|
controller: _descriptionController,
|
||||||
|
maxLines: 3,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: l10n.taskFormDescriptionLabel,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Initial due date
|
||||||
|
Text(
|
||||||
|
l10n.taskFormDueDateLabel,
|
||||||
|
style: theme.textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildDueDatePicker(theme),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFrequencySelector(AppLocalizations l10n, ThemeData theme) {
|
||||||
|
final items = <Widget>[];
|
||||||
|
|
||||||
|
// Preset intervals
|
||||||
|
for (final preset in FrequencyInterval.presets) {
|
||||||
|
items.add(
|
||||||
|
ChoiceChip(
|
||||||
|
label: Text(preset.label()),
|
||||||
|
selected: !_isCustomFrequency && _selectedPreset == preset,
|
||||||
|
onSelected: (selected) {
|
||||||
|
if (selected) {
|
||||||
|
setState(() {
|
||||||
|
_selectedPreset = preset;
|
||||||
|
_isCustomFrequency = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom option
|
||||||
|
items.add(
|
||||||
|
ChoiceChip(
|
||||||
|
label: Text(l10n.taskFormFrequencyCustom),
|
||||||
|
selected: _isCustomFrequency,
|
||||||
|
onSelected: (selected) {
|
||||||
|
if (selected) {
|
||||||
|
setState(() {
|
||||||
|
_isCustomFrequency = true;
|
||||||
|
_selectedPreset = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 4,
|
||||||
|
children: items,
|
||||||
|
),
|
||||||
|
if (_isCustomFrequency) ...[
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_buildCustomFrequencyInput(l10n, theme),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCustomFrequencyInput(AppLocalizations l10n, ThemeData theme) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
l10n.taskFormFrequencyEvery,
|
||||||
|
style: theme.textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
SizedBox(
|
||||||
|
width: 60,
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _customIntervalController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: SegmentedButton<_CustomUnit>(
|
||||||
|
segments: [
|
||||||
|
ButtonSegment(
|
||||||
|
value: _CustomUnit.days,
|
||||||
|
label: Text(l10n.taskFormFrequencyUnitDays),
|
||||||
|
),
|
||||||
|
ButtonSegment(
|
||||||
|
value: _CustomUnit.weeks,
|
||||||
|
label: Text(l10n.taskFormFrequencyUnitWeeks),
|
||||||
|
),
|
||||||
|
ButtonSegment(
|
||||||
|
value: _CustomUnit.months,
|
||||||
|
label: Text(l10n.taskFormFrequencyUnitMonths),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
selected: {_customUnit},
|
||||||
|
onSelectionChanged: (newSelection) {
|
||||||
|
setState(() {
|
||||||
|
_customUnit = newSelection.first;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEffortSelector() {
|
||||||
|
return SegmentedButton<EffortLevel>(
|
||||||
|
segments: EffortLevel.values
|
||||||
|
.map((e) => ButtonSegment(
|
||||||
|
value: e,
|
||||||
|
label: Text(e.label()),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
selected: {_effortLevel},
|
||||||
|
onSelectionChanged: (newSelection) {
|
||||||
|
setState(() {
|
||||||
|
_effortLevel = newSelection.first;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDueDatePicker(ThemeData theme) {
|
||||||
|
final dateFormat = DateFormat('dd.MM.yyyy');
|
||||||
|
return InkWell(
|
||||||
|
onTap: _pickDueDate,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: InputDecorator(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
suffixIcon: Icon(Icons.calendar_today),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
dateFormat.format(_dueDate),
|
||||||
|
style: theme.textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _pickDueDate() async {
|
||||||
|
final picked = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: _dueDate,
|
||||||
|
firstDate: DateTime(2020),
|
||||||
|
lastDate: DateTime(2100),
|
||||||
|
locale: const Locale('de'),
|
||||||
|
);
|
||||||
|
if (picked != null) {
|
||||||
|
setState(() {
|
||||||
|
_dueDate = _dateOnly(picked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve the frequency from either selected preset or custom input.
|
||||||
|
({IntervalType type, int days, int? anchorDay}) _resolveFrequency() {
|
||||||
|
if (!_isCustomFrequency && _selectedPreset != null) {
|
||||||
|
final preset = _selectedPreset!;
|
||||||
|
// For calendar-anchored intervals, set anchorDay to due date's day
|
||||||
|
int? anchorDay;
|
||||||
|
if (_isCalendarAnchored(preset.intervalType)) {
|
||||||
|
anchorDay = _dueDate.day;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
type: preset.intervalType,
|
||||||
|
days: preset.days,
|
||||||
|
anchorDay: anchorDay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom frequency
|
||||||
|
final number = int.tryParse(_customIntervalController.text) ?? 1;
|
||||||
|
switch (_customUnit) {
|
||||||
|
case _CustomUnit.days:
|
||||||
|
return (
|
||||||
|
type: IntervalType.everyNDays,
|
||||||
|
days: number,
|
||||||
|
anchorDay: null,
|
||||||
|
);
|
||||||
|
case _CustomUnit.weeks:
|
||||||
|
return (
|
||||||
|
type: IntervalType.everyNDays,
|
||||||
|
days: number * 7,
|
||||||
|
anchorDay: null,
|
||||||
|
);
|
||||||
|
case _CustomUnit.months:
|
||||||
|
return (
|
||||||
|
type: IntervalType.everyNMonths,
|
||||||
|
days: number,
|
||||||
|
anchorDay: _dueDate.day,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isCalendarAnchored(IntervalType type) {
|
||||||
|
return type == IntervalType.monthly ||
|
||||||
|
type == IntervalType.everyNMonths ||
|
||||||
|
type == IntervalType.quarterly ||
|
||||||
|
type == IntervalType.yearly;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSave() async {
|
||||||
|
if (!_formKey.currentState!.validate()) return;
|
||||||
|
|
||||||
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final freq = _resolveFrequency();
|
||||||
|
final actions = ref.read(taskActionsProvider.notifier);
|
||||||
|
|
||||||
|
if (widget.isEditing && _existingTask != null) {
|
||||||
|
final updatedTask = _existingTask!.copyWith(
|
||||||
|
name: _nameController.text.trim(),
|
||||||
|
description: Value(_descriptionController.text.trim().isEmpty
|
||||||
|
? null
|
||||||
|
: _descriptionController.text.trim()),
|
||||||
|
intervalType: freq.type,
|
||||||
|
intervalDays: freq.days,
|
||||||
|
anchorDay: Value(freq.anchorDay),
|
||||||
|
effortLevel: _effortLevel,
|
||||||
|
nextDueDate: _dueDate,
|
||||||
|
);
|
||||||
|
await actions.updateTask(updatedTask);
|
||||||
|
} else {
|
||||||
|
await actions.createTask(
|
||||||
|
roomId: _roomId,
|
||||||
|
name: _nameController.text.trim(),
|
||||||
|
description: _descriptionController.text.trim().isEmpty
|
||||||
|
? null
|
||||||
|
: _descriptionController.text.trim(),
|
||||||
|
intervalType: freq.type,
|
||||||
|
intervalDays: freq.days,
|
||||||
|
anchorDay: freq.anchorDay,
|
||||||
|
effortLevel: _effortLevel,
|
||||||
|
nextDueDate: _dueDate,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unit options for custom frequency input.
|
||||||
|
enum _CustomUnit { days, weeks, months }
|
||||||
|
|||||||
65
lib/features/tasks/presentation/task_providers.dart
Normal file
65
lib/features/tasks/presentation/task_providers.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
lib/features/tasks/presentation/task_providers.g.dart
Normal file
59
lib/features/tasks/presentation/task_providers.g.dart
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'task_providers.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint, type=warning
|
||||||
|
/// Notifier for task mutations: create, update, delete, complete.
|
||||||
|
|
||||||
|
@ProviderFor(TaskActions)
|
||||||
|
final taskActionsProvider = TaskActionsProvider._();
|
||||||
|
|
||||||
|
/// Notifier for task mutations: create, update, delete, complete.
|
||||||
|
final class TaskActionsProvider
|
||||||
|
extends $AsyncNotifierProvider<TaskActions, void> {
|
||||||
|
/// Notifier for task mutations: create, update, delete, complete.
|
||||||
|
TaskActionsProvider._()
|
||||||
|
: super(
|
||||||
|
from: null,
|
||||||
|
argument: null,
|
||||||
|
retry: null,
|
||||||
|
name: r'taskActionsProvider',
|
||||||
|
isAutoDispose: true,
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String debugGetCreateSourceHash() => _$taskActionsHash();
|
||||||
|
|
||||||
|
@$internal
|
||||||
|
@override
|
||||||
|
TaskActions create() => TaskActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$taskActionsHash() => r'62f1739263e3cfb379b83de10d712b17fd087f92';
|
||||||
|
|
||||||
|
/// Notifier for task mutations: create, update, delete, complete.
|
||||||
|
|
||||||
|
abstract class _$TaskActions extends $AsyncNotifier<void> {
|
||||||
|
FutureOr<void> build();
|
||||||
|
@$mustCallSuper
|
||||||
|
@override
|
||||||
|
void runBuild() {
|
||||||
|
final ref = this.ref as $Ref<AsyncValue<void>, void>;
|
||||||
|
final element =
|
||||||
|
ref.element
|
||||||
|
as $ClassProviderElement<
|
||||||
|
AnyNotifier<AsyncValue<void>, void>,
|
||||||
|
AsyncValue<void>,
|
||||||
|
Object?,
|
||||||
|
Object?
|
||||||
|
>;
|
||||||
|
element.handleCreate(ref, build);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,5 +39,25 @@
|
|||||||
"count": { "type": "int" }
|
"count": { "type": "int" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cancel": "Abbrechen"
|
"cancel": "Abbrechen",
|
||||||
|
"taskFormCreateTitle": "Aufgabe erstellen",
|
||||||
|
"taskFormEditTitle": "Aufgabe bearbeiten",
|
||||||
|
"taskFormNameLabel": "Aufgabenname",
|
||||||
|
"taskFormNameHint": "z.B. Staubsaugen, Fenster putzen...",
|
||||||
|
"taskFormNameRequired": "Bitte einen Namen eingeben",
|
||||||
|
"taskFormFrequencyLabel": "Wiederholung",
|
||||||
|
"taskFormFrequencyCustom": "Benutzerdefiniert",
|
||||||
|
"taskFormFrequencyEvery": "Alle",
|
||||||
|
"taskFormFrequencyUnitDays": "Tage",
|
||||||
|
"taskFormFrequencyUnitWeeks": "Wochen",
|
||||||
|
"taskFormFrequencyUnitMonths": "Monate",
|
||||||
|
"taskFormEffortLabel": "Aufwand",
|
||||||
|
"taskFormDescriptionLabel": "Beschreibung (optional)",
|
||||||
|
"taskFormDueDateLabel": "Erstes F\u00e4lligkeitsdatum",
|
||||||
|
"taskDeleteConfirmTitle": "Aufgabe l\u00f6schen?",
|
||||||
|
"taskDeleteConfirmMessage": "Die Aufgabe wird unwiderruflich gel\u00f6scht.",
|
||||||
|
"taskDeleteConfirmAction": "L\u00f6schen",
|
||||||
|
"taskEmptyTitle": "Noch keine Aufgaben",
|
||||||
|
"taskEmptyMessage": "Erstelle die erste Aufgabe f\u00fcr diesen Raum.",
|
||||||
|
"taskEmptyAction": "Aufgabe erstellen"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,6 +273,126 @@ abstract class AppLocalizations {
|
|||||||
/// In de, this message translates to:
|
/// In de, this message translates to:
|
||||||
/// **'Abbrechen'**
|
/// **'Abbrechen'**
|
||||||
String get cancel;
|
String get cancel;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormCreateTitle.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Aufgabe erstellen'**
|
||||||
|
String get taskFormCreateTitle;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormEditTitle.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Aufgabe bearbeiten'**
|
||||||
|
String get taskFormEditTitle;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormNameLabel.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Aufgabenname'**
|
||||||
|
String get taskFormNameLabel;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormNameHint.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'z.B. Staubsaugen, Fenster putzen...'**
|
||||||
|
String get taskFormNameHint;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormNameRequired.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Bitte einen Namen eingeben'**
|
||||||
|
String get taskFormNameRequired;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormFrequencyLabel.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Wiederholung'**
|
||||||
|
String get taskFormFrequencyLabel;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormFrequencyCustom.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Benutzerdefiniert'**
|
||||||
|
String get taskFormFrequencyCustom;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormFrequencyEvery.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Alle'**
|
||||||
|
String get taskFormFrequencyEvery;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormFrequencyUnitDays.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Tage'**
|
||||||
|
String get taskFormFrequencyUnitDays;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormFrequencyUnitWeeks.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Wochen'**
|
||||||
|
String get taskFormFrequencyUnitWeeks;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormFrequencyUnitMonths.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Monate'**
|
||||||
|
String get taskFormFrequencyUnitMonths;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormEffortLabel.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Aufwand'**
|
||||||
|
String get taskFormEffortLabel;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormDescriptionLabel.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Beschreibung (optional)'**
|
||||||
|
String get taskFormDescriptionLabel;
|
||||||
|
|
||||||
|
/// No description provided for @taskFormDueDateLabel.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Erstes Fälligkeitsdatum'**
|
||||||
|
String get taskFormDueDateLabel;
|
||||||
|
|
||||||
|
/// No description provided for @taskDeleteConfirmTitle.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Aufgabe löschen?'**
|
||||||
|
String get taskDeleteConfirmTitle;
|
||||||
|
|
||||||
|
/// No description provided for @taskDeleteConfirmMessage.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Die Aufgabe wird unwiderruflich gelöscht.'**
|
||||||
|
String get taskDeleteConfirmMessage;
|
||||||
|
|
||||||
|
/// No description provided for @taskDeleteConfirmAction.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Löschen'**
|
||||||
|
String get taskDeleteConfirmAction;
|
||||||
|
|
||||||
|
/// No description provided for @taskEmptyTitle.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Noch keine Aufgaben'**
|
||||||
|
String get taskEmptyTitle;
|
||||||
|
|
||||||
|
/// No description provided for @taskEmptyMessage.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Erstelle die erste Aufgabe für diesen Raum.'**
|
||||||
|
String get taskEmptyMessage;
|
||||||
|
|
||||||
|
/// No description provided for @taskEmptyAction.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Aufgabe erstellen'**
|
||||||
|
String get taskEmptyAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|||||||
@@ -103,4 +103,65 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get cancel => 'Abbrechen';
|
String get cancel => 'Abbrechen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormCreateTitle => 'Aufgabe erstellen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormEditTitle => 'Aufgabe bearbeiten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormNameLabel => 'Aufgabenname';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormNameHint => 'z.B. Staubsaugen, Fenster putzen...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormNameRequired => 'Bitte einen Namen eingeben';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormFrequencyLabel => 'Wiederholung';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormFrequencyCustom => 'Benutzerdefiniert';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormFrequencyEvery => 'Alle';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormFrequencyUnitDays => 'Tage';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormFrequencyUnitWeeks => 'Wochen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormFrequencyUnitMonths => 'Monate';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormEffortLabel => 'Aufwand';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormDescriptionLabel => 'Beschreibung (optional)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskFormDueDateLabel => 'Erstes Fälligkeitsdatum';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskDeleteConfirmTitle => 'Aufgabe löschen?';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskDeleteConfirmMessage =>
|
||||||
|
'Die Aufgabe wird unwiderruflich gelöscht.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskDeleteConfirmAction => 'Löschen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskEmptyTitle => 'Noch keine Aufgaben';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskEmptyMessage => 'Erstelle die erste Aufgabe für diesen Raum.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get taskEmptyAction => 'Aufgabe erstellen';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ void main() {
|
|||||||
overrides: [
|
overrides: [
|
||||||
// Override the stream provider to return an empty list immediately
|
// Override the stream provider to return an empty list immediately
|
||||||
// so that the rooms screen shows the empty state without needing a DB.
|
// so that the rooms screen shows the empty state without needing a DB.
|
||||||
// ignore: scoped_providers_should_specify_dependencies
|
|
||||||
roomWithStatsListProvider.overrideWith(
|
roomWithStatsListProvider.overrideWith(
|
||||||
(ref) => Stream.value([]),
|
(ref) => Stream.value([]),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user