Improve touch targets with 48dp minimum size for mobile
- 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
This commit is contained in:
parent
da873afae0
commit
5cd79e096d
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:agenda_tasks/l10n/app_localizations.dart';
|
import 'package:agenda_tasks/l10n/app_localizations.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
import '../../../../core/di/injection_container.dart';
|
import '../../../../core/di/injection_container.dart';
|
||||||
import '../../domain/entities/task_entity.dart';
|
import '../../domain/entities/task_entity.dart';
|
||||||
@ -35,7 +34,6 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
final repository = getIt<TaskRepository>();
|
final repository = getIt<TaskRepository>();
|
||||||
final Map<String, List<TaskEntity>> tasks = {};
|
final Map<String, List<TaskEntity>> tasks = {};
|
||||||
|
|
||||||
final firstDay = DateTime(_focusedMonth.year, _focusedMonth.month, 1);
|
|
||||||
final lastDay = DateTime(_focusedMonth.year, _focusedMonth.month + 1, 0);
|
final lastDay = DateTime(_focusedMonth.year, _focusedMonth.month + 1, 0);
|
||||||
|
|
||||||
for (int day = 1; day <= lastDay.day; day++) {
|
for (int day = 1; day <= lastDay.day; day++) {
|
||||||
@ -60,19 +58,24 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Priority? _getHighestPriority(List<TaskEntity>? tasks) {
|
List<Priority> _getUniquePriorities(List<TaskEntity>? tasks) {
|
||||||
if (tasks == null || tasks.isEmpty) return null;
|
if (tasks == null || tasks.isEmpty) return [];
|
||||||
|
|
||||||
final incompleteTasks = tasks.where((t) => !t.isDone).toList();
|
final incompleteTasks = tasks.where((t) => !t.isDone).toList();
|
||||||
if (incompleteTasks.isEmpty) return null;
|
if (incompleteTasks.isEmpty) return [];
|
||||||
|
|
||||||
|
// Get unique priorities, sorted by importance (high first)
|
||||||
|
final priorities = <Priority>[];
|
||||||
if (incompleteTasks.any((t) => t.priority == Priority.high)) {
|
if (incompleteTasks.any((t) => t.priority == Priority.high)) {
|
||||||
return Priority.high;
|
priorities.add(Priority.high);
|
||||||
}
|
}
|
||||||
if (incompleteTasks.any((t) => t.priority == Priority.medium)) {
|
if (incompleteTasks.any((t) => t.priority == Priority.medium)) {
|
||||||
return Priority.medium;
|
priorities.add(Priority.medium);
|
||||||
}
|
}
|
||||||
return Priority.low;
|
if (incompleteTasks.any((t) => t.priority == Priority.low)) {
|
||||||
|
priorities.add(Priority.low);
|
||||||
|
}
|
||||||
|
return priorities;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -114,7 +117,7 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
focusedMonth: _focusedMonth,
|
focusedMonth: _focusedMonth,
|
||||||
selectedDate: _selectedDate,
|
selectedDate: _selectedDate,
|
||||||
tasksByDate: _tasksByDate,
|
tasksByDate: _tasksByDate,
|
||||||
getHighestPriority: _getHighestPriority,
|
getUniquePriorities: _getUniquePriorities,
|
||||||
onDateSelected: (date) {
|
onDateSelected: (date) {
|
||||||
setState(() => _selectedDate = date);
|
setState(() => _selectedDate = date);
|
||||||
},
|
},
|
||||||
@ -126,6 +129,8 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
? SafeArea(
|
? SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 56,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final dateStr = DateFormat('yyyy-MM-dd').format(_selectedDate!);
|
final dateStr = DateFormat('yyyy-MM-dd').format(_selectedDate!);
|
||||||
@ -134,6 +139,7 @@ class _CalendarPageState extends State<CalendarPage> {
|
|||||||
child: Text(l10n.goToDay),
|
child: Text(l10n.goToDay),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
@ -158,22 +164,30 @@ class _MonthNavigation extends StatelessWidget {
|
|||||||
final monthFormat = DateFormat.yMMMM(locale);
|
final monthFormat = DateFormat.yMMMM(locale);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
SizedBox(
|
||||||
icon: const Icon(Icons.chevron_left),
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.chevron_left, size: 28),
|
||||||
onPressed: onPrevious,
|
onPressed: onPrevious,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
monthFormat.format(month),
|
monthFormat.format(month),
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
IconButton(
|
SizedBox(
|
||||||
icon: const Icon(Icons.chevron_right),
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.chevron_right, size: 28),
|
||||||
onPressed: onNext,
|
onPressed: onNext,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -216,14 +230,14 @@ class _CalendarGrid extends StatelessWidget {
|
|||||||
final DateTime focusedMonth;
|
final DateTime focusedMonth;
|
||||||
final DateTime? selectedDate;
|
final DateTime? selectedDate;
|
||||||
final Map<String, List<TaskEntity>> tasksByDate;
|
final Map<String, List<TaskEntity>> tasksByDate;
|
||||||
final Priority? Function(List<TaskEntity>?) getHighestPriority;
|
final List<Priority> Function(List<TaskEntity>?) getUniquePriorities;
|
||||||
final ValueChanged<DateTime> onDateSelected;
|
final ValueChanged<DateTime> onDateSelected;
|
||||||
|
|
||||||
const _CalendarGrid({
|
const _CalendarGrid({
|
||||||
required this.focusedMonth,
|
required this.focusedMonth,
|
||||||
required this.selectedDate,
|
required this.selectedDate,
|
||||||
required this.tasksByDate,
|
required this.tasksByDate,
|
||||||
required this.getHighestPriority,
|
required this.getUniquePriorities,
|
||||||
required this.onDateSelected,
|
required this.onDateSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -237,10 +251,12 @@ class _CalendarGrid extends StatelessWidget {
|
|||||||
final today = DateTime.now();
|
final today = DateTime.now();
|
||||||
|
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 7,
|
crossAxisCount: 7,
|
||||||
childAspectRatio: 1,
|
childAspectRatio: 1,
|
||||||
|
mainAxisSpacing: 4,
|
||||||
|
crossAxisSpacing: 4,
|
||||||
),
|
),
|
||||||
itemCount: rowCount * 7,
|
itemCount: rowCount * 7,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
@ -260,12 +276,11 @@ class _CalendarGrid extends StatelessWidget {
|
|||||||
date.day == selectedDate!.day;
|
date.day == selectedDate!.day;
|
||||||
|
|
||||||
final tasks = tasksByDate[dateStr];
|
final tasks = tasksByDate[dateStr];
|
||||||
final highestPriority = getHighestPriority(tasks);
|
final priorities = getUniquePriorities(tasks);
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => onDateSelected(date),
|
onTap: () => onDateSelected(date),
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.all(2),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
@ -288,10 +303,10 @@ class _CalendarGrid extends StatelessWidget {
|
|||||||
fontWeight: isToday || isSelected ? FontWeight.bold : null,
|
fontWeight: isToday || isSelected ? FontWeight.bold : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (highestPriority != null)
|
if (priorities.isNotEmpty)
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 4,
|
bottom: 6,
|
||||||
child: _PulsingDot(priority: highestPriority),
|
child: _PriorityDots(priorities: priorities),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -302,6 +317,25 @@ class _CalendarGrid extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PriorityDots extends StatelessWidget {
|
||||||
|
final List<Priority> priorities;
|
||||||
|
|
||||||
|
const _PriorityDots({required this.priorities});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: priorities
|
||||||
|
.map((priority) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
child: _PulsingDot(priority: priority),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _PulsingDot extends StatefulWidget {
|
class _PulsingDot extends StatefulWidget {
|
||||||
final Priority priority;
|
final Priority priority;
|
||||||
|
|
||||||
@ -354,15 +388,15 @@ class _PulsingDotState extends State<_PulsingDot>
|
|||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: _animation.value,
|
opacity: _animation.value,
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 6,
|
width: 10,
|
||||||
height: 6,
|
height: 10,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: _getPriorityColor(),
|
color: _getPriorityColor(),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: _getPriorityColor().withOpacity(0.5),
|
color: _getPriorityColor().withValues(alpha: 0.5),
|
||||||
blurRadius: 2,
|
blurRadius: 3,
|
||||||
spreadRadius: 1,
|
spreadRadius: 1,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -79,9 +79,12 @@ class _DailyAgendaView extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
final dateStr = DateFormat('yyyy-MM-dd').format(vm.selectedDate);
|
final dateStr = DateFormat('yyyy-MM-dd').format(vm.selectedDate);
|
||||||
context.push('/task/new?date=$dateStr');
|
final result = await context.push('/task/new?date=$dateStr');
|
||||||
|
if (result == true) {
|
||||||
|
vm.loadTasks();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
@ -140,7 +143,12 @@ class _DailyAgendaView extends StatelessWidget {
|
|||||||
return TaskTile(
|
return TaskTile(
|
||||||
task: task,
|
task: task,
|
||||||
onToggle: () => vm.toggleTask(task.id),
|
onToggle: () => vm.toggleTask(task.id),
|
||||||
onTap: () => context.push('/task/${task.id}/edit'),
|
onTap: () async {
|
||||||
|
final result = await context.push('/task/${task.id}/edit');
|
||||||
|
if (result == true) {
|
||||||
|
vm.loadTasks();
|
||||||
|
}
|
||||||
|
},
|
||||||
onDelete: () => vm.deleteTask(task.id),
|
onDelete: () => vm.deleteTask(task.id),
|
||||||
onReschedule: () => vm.rescheduleToTomorrow(task.id),
|
onReschedule: () => vm.rescheduleToTomorrow(task.id),
|
||||||
);
|
);
|
||||||
@ -169,14 +177,18 @@ class _DateNavigation extends StatelessWidget {
|
|||||||
final dateFormat = DateFormat.yMMMMEEEEd(locale);
|
final dateFormat = DateFormat.yMMMMEEEEd(locale);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
SizedBox(
|
||||||
icon: const Icon(Icons.chevron_left),
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.chevron_left, size: 28),
|
||||||
onPressed: onPrevious,
|
onPressed: onPrevious,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
dateFormat.format(date),
|
dateFormat.format(date),
|
||||||
@ -184,10 +196,14 @@ class _DateNavigation extends StatelessWidget {
|
|||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
SizedBox(
|
||||||
icon: const Icon(Icons.chevron_right),
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.chevron_right, size: 28),
|
||||||
onPressed: onNext,
|
onPressed: onNext,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -99,11 +99,14 @@ class _TaskFormView extends StatelessWidget {
|
|||||||
onChanged: vm.setDescription,
|
onChanged: vm.setDescription,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 20),
|
||||||
ListTile(
|
ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minHeight: 72),
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
title: Text(l10n.date),
|
title: Text(l10n.date),
|
||||||
subtitle: Text(DateFormat.yMMMd(locale).format(vm.date)),
|
subtitle: Text(DateFormat.yMMMd(locale).format(vm.date)),
|
||||||
trailing: const Icon(Icons.calendar_today),
|
trailing: const Icon(Icons.calendar_today, size: 24),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
side: BorderSide(color: Theme.of(context).colorScheme.outline),
|
side: BorderSide(color: Theme.of(context).colorScheme.outline),
|
||||||
@ -120,13 +123,16 @@ class _TaskFormView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
Text(
|
Text(
|
||||||
l10n.priority,
|
l10n.priority,
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 12),
|
||||||
SegmentedButton<Priority>(
|
SizedBox(
|
||||||
|
height: 48,
|
||||||
|
child: SegmentedButton<Priority>(
|
||||||
segments: [
|
segments: [
|
||||||
ButtonSegment(
|
ButtonSegment(
|
||||||
value: Priority.low,
|
value: Priority.low,
|
||||||
@ -146,6 +152,7 @@ class _TaskFormView extends StatelessWidget {
|
|||||||
vm.setPriority(selection.first);
|
vm.setPriority(selection.first);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
if (vm.status == FormStatus.error && vm.failure != null) ...[
|
if (vm.status == FormStatus.error && vm.failure != null) ...[
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Container(
|
Container(
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import '../../../../core/errors/failures.dart';
|
import '../../../../core/errors/failures.dart';
|
||||||
import '../../../../core/logging/app_logger.dart';
|
import '../../../../core/logging/app_logger.dart';
|
||||||
import '../../domain/entities/task_entity.dart';
|
import '../../domain/entities/task_entity.dart';
|
||||||
|
import '../../domain/enums/priority.dart';
|
||||||
import '../../domain/repositories/task_repository.dart';
|
import '../../domain/repositories/task_repository.dart';
|
||||||
|
|
||||||
enum TasksStatus { initial, loading, success, error }
|
enum TasksStatus { initial, loading, success, error }
|
||||||
@ -31,13 +32,31 @@ class DailyTasksViewModel extends ChangeNotifier {
|
|||||||
TaskFilter get filter => _filter;
|
TaskFilter get filter => _filter;
|
||||||
|
|
||||||
List<TaskEntity> get _filteredTasks {
|
List<TaskEntity> get _filteredTasks {
|
||||||
|
List<TaskEntity> filtered;
|
||||||
switch (_filter) {
|
switch (_filter) {
|
||||||
case TaskFilter.all:
|
case TaskFilter.all:
|
||||||
return _tasks;
|
filtered = List.from(_tasks);
|
||||||
|
break;
|
||||||
case TaskFilter.active:
|
case TaskFilter.active:
|
||||||
return _tasks.where((t) => !t.isDone).toList();
|
filtered = _tasks.where((t) => !t.isDone).toList();
|
||||||
|
break;
|
||||||
case TaskFilter.completed:
|
case TaskFilter.completed:
|
||||||
return _tasks.where((t) => t.isDone).toList();
|
filtered = _tasks.where((t) => t.isDone).toList();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Sort by priority: high first, then medium, then low
|
||||||
|
filtered.sort((a, b) => _priorityOrder(a.priority).compareTo(_priorityOrder(b.priority)));
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _priorityOrder(Priority priority) {
|
||||||
|
switch (priority) {
|
||||||
|
case Priority.high:
|
||||||
|
return 0;
|
||||||
|
case Priority.medium:
|
||||||
|
return 1;
|
||||||
|
case Priority.low:
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,18 +26,21 @@ class FilterChips extends StatelessWidget {
|
|||||||
label: Text(l10n.filterAll),
|
label: Text(l10n.filterAll),
|
||||||
selected: currentFilter == TaskFilter.all,
|
selected: currentFilter == TaskFilter.all,
|
||||||
onSelected: (_) => onFilterChanged(TaskFilter.all),
|
onSelected: (_) => onFilterChanged(TaskFilter.all),
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
FilterChip(
|
FilterChip(
|
||||||
label: Text(l10n.filterActive),
|
label: Text(l10n.filterActive),
|
||||||
selected: currentFilter == TaskFilter.active,
|
selected: currentFilter == TaskFilter.active,
|
||||||
onSelected: (_) => onFilterChanged(TaskFilter.active),
|
onSelected: (_) => onFilterChanged(TaskFilter.active),
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
FilterChip(
|
FilterChip(
|
||||||
label: Text(l10n.filterCompleted),
|
label: Text(l10n.filterCompleted),
|
||||||
selected: currentFilter == TaskFilter.completed,
|
selected: currentFilter == TaskFilter.completed,
|
||||||
onSelected: (_) => onFilterChanged(TaskFilter.completed),
|
onSelected: (_) => onFilterChanged(TaskFilter.completed),
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -69,28 +69,33 @@ class TaskTile extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
onDismissed: (_) => onDelete(),
|
onDismissed: (_) => onDelete(),
|
||||||
child: Card(
|
child: Card(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minHeight: 72),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 4,
|
width: 5,
|
||||||
height: 48,
|
height: 56,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: priorityColor,
|
color: priorityColor,
|
||||||
borderRadius: BorderRadius.circular(2),
|
borderRadius: BorderRadius.circular(3),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
SizedBox(
|
||||||
Checkbox(
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
child: Checkbox(
|
||||||
value: task.isDone,
|
value: task.isDone,
|
||||||
onChanged: (_) => onToggle(),
|
onChanged: (_) => onToggle(),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -118,7 +123,11 @@ class TaskTile extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuButton<String>(
|
SizedBox(
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
child: PopupMenuButton<String>(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
onSelected: (value) async {
|
onSelected: (value) async {
|
||||||
if (value == 'reschedule') {
|
if (value == 'reschedule') {
|
||||||
onReschedule();
|
onReschedule();
|
||||||
@ -147,32 +156,36 @@ class TaskTile extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
|
height: 56,
|
||||||
value: 'reschedule',
|
value: 'reschedule',
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.schedule),
|
const Icon(Icons.schedule, size: 24),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 12),
|
||||||
Text(l10n.rescheduleToTomorrow),
|
Text(l10n.rescheduleToTomorrow),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
|
height: 56,
|
||||||
value: 'delete',
|
value: 'delete',
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.delete, color: Theme.of(context).colorScheme.error),
|
Icon(Icons.delete, size: 24, color: Theme.of(context).colorScheme.error),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 12),
|
||||||
Text(l10n.delete, style: TextStyle(color: Theme.of(context).colorScheme.error)),
|
Text(l10n.delete, style: TextStyle(color: Theme.of(context).colorScheme.error)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user