AgendaTasks/lib/features/tasks/presentation/pages/daily_agenda_page.dart
m3mo cb308bbf68 Initial project setup with Clean Architecture
- Flutter frontend with Provider state management
- FastAPI backend with SQLAlchemy ORM
- Internationalization support (EN/DE)
- Clean Architecture folder structure
- GoRouter for navigation
- GetIt for dependency injection
2026-02-02 16:43:37 +01:00

181 lines
5.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../../../../core/di/injection_container.dart';
import '../viewmodels/daily_tasks_viewmodel.dart';
import '../widgets/task_tile.dart';
import '../widgets/filter_chips.dart';
class DailyAgendaPage extends StatelessWidget {
const DailyAgendaPage({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => getIt<DailyTasksViewModel>()..loadTasks(),
child: const _DailyAgendaView(),
);
}
}
class _DailyAgendaView extends StatelessWidget {
const _DailyAgendaView();
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final vm = context.watch<DailyTasksViewModel>();
final locale = Localizations.localeOf(context).languageCode;
return Scaffold(
appBar: AppBar(
title: Text(l10n.appTitle),
actions: [
IconButton(
icon: const Icon(Icons.calendar_month),
onPressed: () => context.push('/calendar'),
tooltip: l10n.calendar,
),
IconButton(
icon: const Icon(Icons.settings),
onPressed: () => context.push('/settings'),
tooltip: l10n.settings,
),
],
),
body: Column(
children: [
_DateNavigation(
date: vm.selectedDate,
locale: locale,
onPrevious: vm.previousDay,
onNext: vm.nextDay,
),
FilterChips(
currentFilter: vm.filter,
onFilterChanged: vm.setFilter,
),
Expanded(
child: _buildBody(context, vm, l10n),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
final dateStr = DateFormat('yyyy-MM-dd').format(vm.selectedDate);
context.push('/task/new?date=$dateStr');
},
child: const Icon(Icons.add),
),
);
}
Widget _buildBody(BuildContext context, DailyTasksViewModel vm, AppLocalizations l10n) {
switch (vm.status) {
case TasksStatus.initial:
case TasksStatus.loading:
return const Center(child: CircularProgressIndicator());
case TasksStatus.error:
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
vm.failure?.message ?? l10n.errorOccurred,
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: vm.loadTasks,
child: Text(l10n.retry),
),
],
),
);
case TasksStatus.success:
if (vm.tasks.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.task_alt,
size: 64,
color: Theme.of(context).colorScheme.outline,
),
const SizedBox(height: 16),
Text(
l10n.noTasks,
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
);
}
return RefreshIndicator(
onRefresh: vm.loadTasks,
child: ListView.builder(
padding: const EdgeInsets.only(bottom: 80),
itemCount: vm.tasks.length,
itemBuilder: (context, index) {
final task = vm.tasks[index];
return TaskTile(
task: task,
onToggle: () => vm.toggleTask(task.id),
onTap: () => context.push('/task/${task.id}/edit'),
onDelete: () => vm.deleteTask(task.id),
onReschedule: () => vm.rescheduleToTomorrow(task.id),
);
},
),
);
}
}
}
class _DateNavigation extends StatelessWidget {
final DateTime date;
final String locale;
final VoidCallback onPrevious;
final VoidCallback onNext;
const _DateNavigation({
required this.date,
required this.locale,
required this.onPrevious,
required this.onNext,
});
@override
Widget build(BuildContext context) {
final dateFormat = DateFormat.yMMMMEEEEd(locale);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(Icons.chevron_left),
onPressed: onPrevious,
),
Expanded(
child: Text(
dateFormat.format(date),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium,
),
),
IconButton(
icon: const Icon(Icons.chevron_right),
onPressed: onNext,
),
],
),
);
}
}