timetracker/lib/features/timer/presentation/widgets/quick_access_grid.dart
2026-08-03 21:51:48 +02:00

156 lines
4.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:timetrack/features/projects/domain/project.dart';
import 'package:timetrack/features/timer/domain/frequent_projects_provider.dart';
import 'package:timetrack/features/timer/domain/timer_notifier.dart';
import 'package:timetrack/features/timer/domain/timer_state.dart';
class QuickAccessGrid extends ConsumerWidget {
const QuickAccessGrid({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final projects = ref.watch(frequentProjectsProvider);
if (projects.isEmpty) return const SizedBox.shrink();
final timerState = ref.watch(timerNotifierProvider);
final runningProject =
timerState is TimerRunning ? timerState.project : null;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Quick Access',
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
letterSpacing: 0.8,
),
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: projects.map((project) {
return _ProjectChip(
project: project,
isRunning: runningProject?.id == project.id,
onTap: () => _onChipTap(context, ref, project, runningProject),
);
}).toList(),
),
],
),
);
}
Future<void> _onChipTap(
BuildContext context,
WidgetRef ref,
Project tapped,
Project? runningProject,
) async {
final notifier = ref.read(timerNotifierProvider.notifier);
if (runningProject?.id == tapped.id) {
// Toggle: stop the running timer
await notifier.stop();
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${tapped.name} gestoppt'),
duration: const Duration(seconds: 2),
),
);
}
} else if (runningProject != null) {
// Switch: stop old, start new
final stoppedName = runningProject.name;
await notifier.start(tapped);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('$stoppedName gestoppt, ${tapped.name} gestartet'),
duration: const Duration(seconds: 2),
),
);
}
} else {
// Start fresh
await notifier.start(tapped);
}
}
}
class _ProjectChip extends StatelessWidget {
const _ProjectChip({
required this.project,
required this.isRunning,
required this.onTap,
});
final Project project;
final bool isRunning;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final projectColor = Color(project.colorValue);
final colorScheme = Theme.of(context).colorScheme;
final backgroundColor = isRunning
? projectColor.withValues(alpha: 0.15)
: colorScheme.surfaceContainerHighest;
final borderColor = isRunning ? projectColor : Colors.transparent;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(20),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: borderColor,
width: isRunning ? 2 : 0,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: projectColor,
shape: BoxShape.circle,
),
),
const SizedBox(width: 6),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 120),
child: Text(
project.name,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontWeight:
isRunning ? FontWeight.w600 : FontWeight.normal,
color: isRunning
? projectColor
: colorScheme.onSurfaceVariant,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
),
),
);
}
}