- Enlarge calendar priority dots to 10px with pulsing animation - Increase task tile checkbox and menu button containers to 48x48 - Add proper minVerticalPadding to task tiles (72dp height) - Update filter chips with MaterialTapTargetSize.padded - Increase navigation buttons to 48x48 with 28px icons - Update task form with 48dp height segmented button
178 lines
6.5 KiB
Dart
178 lines
6.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:agenda_tasks/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 '../../domain/enums/priority.dart';
|
|
import '../viewmodels/task_form_viewmodel.dart';
|
|
|
|
class TaskFormPage extends StatelessWidget {
|
|
final String? taskId;
|
|
final String? initialDate;
|
|
|
|
const TaskFormPage({
|
|
super.key,
|
|
this.taskId,
|
|
this.initialDate,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ChangeNotifierProvider(
|
|
create: (_) {
|
|
final vm = getIt<TaskFormViewModel>();
|
|
if (taskId != null) {
|
|
vm.initForEdit(taskId!);
|
|
} else {
|
|
final date = initialDate != null
|
|
? DateTime.tryParse(initialDate!)
|
|
: null;
|
|
vm.initForCreate(date);
|
|
}
|
|
return vm;
|
|
},
|
|
child: const _TaskFormView(),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TaskFormView extends StatelessWidget {
|
|
const _TaskFormView();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
final vm = context.watch<TaskFormViewModel>();
|
|
final locale = Localizations.localeOf(context).languageCode;
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(vm.isEditing ? l10n.editTask : l10n.newTask),
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.close),
|
|
onPressed: () => context.pop(),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: vm.status == FormStatus.loading
|
|
? null
|
|
: () async {
|
|
final success = await vm.save();
|
|
if (success && context.mounted) {
|
|
context.pop(true);
|
|
}
|
|
},
|
|
child: Text(l10n.save),
|
|
),
|
|
],
|
|
),
|
|
body: vm.status == FormStatus.loading && vm.isEditing
|
|
? const Center(child: CircularProgressIndicator())
|
|
: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
TextField(
|
|
decoration: InputDecoration(
|
|
labelText: '${l10n.title} *',
|
|
errorText: vm.titleError != null
|
|
? l10n.titleRequired
|
|
: null,
|
|
border: const OutlineInputBorder(),
|
|
),
|
|
controller: TextEditingController(text: vm.title)
|
|
..selection = TextSelection.collapsed(offset: vm.title.length),
|
|
onChanged: vm.setTitle,
|
|
textInputAction: TextInputAction.next,
|
|
),
|
|
const SizedBox(height: 16),
|
|
TextField(
|
|
decoration: InputDecoration(
|
|
labelText: l10n.description,
|
|
border: const OutlineInputBorder(),
|
|
),
|
|
controller: TextEditingController(text: vm.description)
|
|
..selection = TextSelection.collapsed(offset: vm.description.length),
|
|
onChanged: vm.setDescription,
|
|
maxLines: 3,
|
|
),
|
|
const SizedBox(height: 20),
|
|
ConstrainedBox(
|
|
constraints: const BoxConstraints(minHeight: 72),
|
|
child: ListTile(
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
title: Text(l10n.date),
|
|
subtitle: Text(DateFormat.yMMMd(locale).format(vm.date)),
|
|
trailing: const Icon(Icons.calendar_today, size: 24),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
side: BorderSide(color: Theme.of(context).colorScheme.outline),
|
|
),
|
|
onTap: () async {
|
|
final picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: vm.date,
|
|
firstDate: DateTime(2020),
|
|
lastDate: DateTime(2100),
|
|
);
|
|
if (picked != null) {
|
|
vm.setDate(picked);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
Text(
|
|
l10n.priority,
|
|
style: Theme.of(context).textTheme.titleSmall,
|
|
),
|
|
const SizedBox(height: 12),
|
|
SizedBox(
|
|
height: 48,
|
|
child: SegmentedButton<Priority>(
|
|
segments: [
|
|
ButtonSegment(
|
|
value: Priority.low,
|
|
label: Text(l10n.priorityLow),
|
|
),
|
|
ButtonSegment(
|
|
value: Priority.medium,
|
|
label: Text(l10n.priorityMedium),
|
|
),
|
|
ButtonSegment(
|
|
value: Priority.high,
|
|
label: Text(l10n.priorityHigh),
|
|
),
|
|
],
|
|
selected: {vm.priority},
|
|
onSelectionChanged: (selection) {
|
|
vm.setPriority(selection.first);
|
|
},
|
|
),
|
|
),
|
|
if (vm.status == FormStatus.error && vm.failure != null) ...[
|
|
const SizedBox(height: 24),
|
|
Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.errorContainer,
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
vm.failure!.message,
|
|
style: TextStyle(
|
|
color: Theme.of(context).colorScheme.onErrorContainer,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|