focus-flow/apps/focus_flow_flutter/lib/widgets/task_selection_modal.dart

208 lines
6.5 KiB
Dart

import 'package:flutter/material.dart';
import '../models/today_screen_models.dart';
import '../theme/focus_flow_tokens.dart';
import 'timeline/difficulty_bars.dart';
import 'timeline/reward_icon.dart';
class TaskSelectionModal extends StatelessWidget {
const TaskSelectionModal({
required this.card,
required this.onClose,
super.key,
});
final TimelineCardModel card;
final VoidCallback onClose;
@override
Widget build(BuildContext context) {
final accent = _accentFor(card.visualKind);
return Material(
color: Colors.transparent,
child: Container(
key: const ValueKey('task-selection-modal'),
width: 548,
padding: const EdgeInsets.fromLTRB(24, 22, 24, 20),
decoration: BoxDecoration(
color: FocusFlowTokens.elevatedPanel.withValues(alpha: 0.98),
borderRadius: BorderRadius.circular(FocusFlowTokens.modalRadius),
border: Border.all(color: accent.withValues(alpha: 0.86)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: accent, width: 4),
),
),
const SizedBox(width: 28),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
card.title,
style: const TextStyle(
color: FocusFlowTokens.textPrimary,
fontSize: FocusFlowTokens.modalTitleSize,
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 6),
Text(
'${card.typeLabel} - ${card.timeText}',
style: const TextStyle(
color: FocusFlowTokens.textMuted,
fontSize: 17,
),
),
],
),
),
IconButton(
key: const ValueKey('close-task-modal'),
onPressed: onClose,
style: IconButton.styleFrom(
backgroundColor: FocusFlowTokens.glassPanelStrong,
foregroundColor: FocusFlowTokens.textPrimary,
),
icon: const Icon(Icons.close),
),
],
),
const SizedBox(height: 26),
Row(
children: [
Expanded(
child: _InfoTile(
label: card.rewardLabel,
child: RewardIcon(color: FocusFlowTokens.accentMagenta),
),
),
const SizedBox(width: 28),
Expanded(
child: _InfoTile(
label: card.effortLabel,
child: DifficultyBars(
difficulty: card.difficultyIconToken,
color: FocusFlowTokens.restPurple,
),
),
),
],
),
const SizedBox(height: 30),
GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 26,
mainAxisSpacing: 16,
childAspectRatio: 4.1,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: const [
_ActionButton(icon: Icons.check, label: 'Done'),
_ActionButton(icon: Icons.arrow_forward, label: 'Push'),
_ActionButton(
icon: Icons.inventory_2_outlined,
label: 'Move to Backlog',
),
_ActionButton(icon: Icons.apps, label: 'Break up'),
],
),
const SizedBox(height: 14),
const Text(
'Options apply after confirmation.',
style: TextStyle(color: FocusFlowTokens.textMuted, fontSize: 14),
),
],
),
),
);
}
}
class _InfoTile extends StatelessWidget {
const _InfoTile({required this.child, required this.label});
final Widget child;
final String label;
@override
Widget build(BuildContext context) {
return Container(
height: 58,
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(FocusFlowTokens.smallButtonRadius),
border: Border.all(color: FocusFlowTokens.subtleBorder),
),
child: Row(
children: [
child,
const SizedBox(width: 18),
Flexible(
child: Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: FocusFlowTokens.textPrimary,
fontSize: 17,
fontWeight: FontWeight.w700,
),
),
),
],
),
);
}
}
class _ActionButton extends StatelessWidget {
const _ActionButton({required this.icon, required this.label});
final IconData icon;
final String label;
@override
Widget build(BuildContext context) {
return OutlinedButton.icon(
onPressed: () {},
style: OutlinedButton.styleFrom(
foregroundColor: FocusFlowTokens.textPrimary,
side: const BorderSide(color: FocusFlowTokens.subtleBorder),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
FocusFlowTokens.smallButtonRadius,
),
),
),
icon: Icon(icon),
label: Text(
label,
style: const TextStyle(
fontSize: FocusFlowTokens.modalButtonSize,
fontWeight: FontWeight.w700,
),
),
);
}
}
Color _accentFor(TaskVisualKind kind) {
return switch (kind) {
TaskVisualKind.flexible => FocusFlowTokens.flexibleGreen,
TaskVisualKind.required => FocusFlowTokens.requiredPink,
TaskVisualKind.appointment => FocusFlowTokens.appointmentBlue,
TaskVisualKind.freeSlot => FocusFlowTokens.restPurple,
TaskVisualKind.completedSurprise => FocusFlowTokens.completedGray,
};
}