156 lines
4.7 KiB
Dart
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,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|