import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:focusflow_shared/focusflow_shared.dart'; import '../../../../core/theme/app_colors.dart'; import '../../../../core/widgets/reward_popup.dart'; import '../bloc/next_task_cubit.dart'; /// THE core ADHD feature — "Just do the next thing." /// /// - Single task displayed, nothing else. /// - Large title text centered. /// - Energy level indicator. /// - Estimated time. /// - Big "Done!" button (green, satisfying). /// - "Skip" button (smaller, gray, no guilt). /// - "I need a break" option. /// - Timer showing elapsed time (gentle, not anxiety-inducing). /// - On completion: reward popup appears. /// - After reward: auto-loads next task or shows "All done!" celebration. class FocusModeScreen extends StatefulWidget { const FocusModeScreen({super.key}); @override State createState() => _FocusModeScreenState(); } class _FocusModeScreenState extends State { late final NextTaskCubit _cubit; final Stopwatch _stopwatch = Stopwatch(); Timer? _timer; bool _showReward = false; int _elapsedSeconds = 0; @override void initState() { super.initState(); _cubit = NextTaskCubit()..loadNext(); _startTimer(); } void _startTimer() { _stopwatch.start(); _timer = Timer.periodic(const Duration(seconds: 1), (_) { if (mounted) { setState(() => _elapsedSeconds = _stopwatch.elapsed.inSeconds); } }); } void _resetTimer() { _stopwatch.reset(); setState(() => _elapsedSeconds = 0); } @override void dispose() { _timer?.cancel(); _stopwatch.stop(); _cubit.close(); super.dispose(); } String _formatElapsed() { final minutes = _elapsedSeconds ~/ 60; final seconds = _elapsedSeconds % 60; return '${minutes}m ${seconds.toString().padLeft(2, '0')}s so far'; } Color _energyColor(String level) { switch (level) { case 'low': return AppColors.energyLow; case 'high': return AppColors.energyHigh; default: return AppColors.energyMedium; } } String _energyEmoji(String level) { switch (level) { case 'low': return 'Low energy'; case 'high': return 'High energy'; default: return 'Medium energy'; } } Future _onComplete() async { _stopwatch.stop(); setState(() => _showReward = true); } void _dismissReward() { setState(() => _showReward = false); _resetTimer(); _stopwatch.start(); _cubit.complete(); } void _onSkip() { _resetTimer(); _cubit.skip(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return BlocProvider.value( value: _cubit, child: Scaffold( backgroundColor: theme.scaffoldBackgroundColor, appBar: AppBar( leading: IconButton( icon: const Icon(Icons.close_rounded), onPressed: () => context.go('/'), tooltip: 'Exit focus mode', ), title: const Text('Focus Mode'), centerTitle: true, ), body: Stack( children: [ BlocBuilder( builder: (context, state) { if (state is NextTaskLoading) { return const Center(child: CircularProgressIndicator()); } if (state is NextTaskError) { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.cloud_off_rounded, size: 56, color: AppColors.skipped), const SizedBox(height: 12), Text(state.message, style: theme.textTheme.bodyLarge, textAlign: TextAlign.center), const SizedBox(height: 16), FilledButton( onPressed: () => _cubit.loadNext(), child: const Text('Try again'), ), ], ), ), ); } if (state is NextTaskEmpty) { return _AllDoneView(onGoBack: () => context.go('/')); } if (state is NextTaskLoaded) { return _TaskFocusView( task: state.task, elapsed: _formatElapsed(), energyColor: _energyColor(state.task.energyLevel), energyLabel: _energyEmoji(state.task.energyLevel), onComplete: _onComplete, onSkip: _onSkip, onBreak: () { _stopwatch.stop(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text( 'Take your time. Tap anywhere when you\'re ready.'), action: SnackBarAction( label: 'Resume', onPressed: () => _stopwatch.start(), ), ), ); }, ); } return const SizedBox.shrink(); }, ), // ── Reward overlay ─────────────────────────────────── if (_showReward) Container( color: Colors.black.withAlpha(120), child: RewardPopup( reward: Reward( id: 'local-${DateTime.now().millisecondsSinceEpoch}', userId: '', rewardType: 'points', magnitude: 10, title: 'Task complete!', createdAt: DateTime.now(), ), onDismiss: _dismissReward, ), ), ], ), ), ); } } // ── Single-task focus view ───────────────────────────────────────── class _TaskFocusView extends StatelessWidget { final Task task; final String elapsed; final Color energyColor; final String energyLabel; final VoidCallback onComplete; final VoidCallback onSkip; final VoidCallback onBreak; const _TaskFocusView({ required this.task, required this.elapsed, required this.energyColor, required this.energyLabel, required this.onComplete, required this.onSkip, required this.onBreak, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 28), child: Column( children: [ const Spacer(flex: 2), // ── Energy indicator ───────────────────────────────── Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), decoration: BoxDecoration( color: energyColor.withAlpha(30), borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.bolt_rounded, size: 18, color: energyColor), const SizedBox(width: 4), Text(energyLabel, style: TextStyle( color: energyColor, fontWeight: FontWeight.w600)), ], ), ), const SizedBox(height: 24), // ── Task title ─────────────────────────────────────── Text( task.title, style: theme.textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.w700, ), textAlign: TextAlign.center, ), const SizedBox(height: 12), // ── Estimated time ─────────────────────────────────── if (task.estimatedMinutes != null) Text( '~${task.estimatedMinutes} min estimated', style: theme.textTheme.bodyLarge?.copyWith( color: AppColors.primary, ), ), const SizedBox(height: 20), // ── Elapsed time (gentle) ──────────────────────────── Text( elapsed, style: theme.textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.w500, color: AppColors.primary, ), ), const Spacer(flex: 3), // ── Done button ────────────────────────────────────── SizedBox( width: double.infinity, height: 64, child: FilledButton( onPressed: onComplete, style: FilledButton.styleFrom( backgroundColor: AppColors.completed, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), textStyle: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, color: Colors.white, ), ), child: const Text('Done!', style: TextStyle(fontSize: 22, color: Colors.white)), ), ), const SizedBox(height: 12), // ── Skip button (smaller, gray, no guilt) ──────────── SizedBox( width: double.infinity, height: 52, child: OutlinedButton( onPressed: onSkip, style: OutlinedButton.styleFrom( foregroundColor: AppColors.skipped, side: const BorderSide(color: AppColors.skipped), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), child: const Text('Skip for now'), ), ), const SizedBox(height: 8), // ── Break button ───────────────────────────────────── TextButton.icon( onPressed: onBreak, icon: const Icon(Icons.self_improvement_rounded, size: 20), label: const Text('I need a break'), ), const Spacer(), ], ), ), ); } } // ── All done celebration ─────────────────────────────────────────── class _AllDoneView extends StatelessWidget { final VoidCallback onGoBack; const _AllDoneView({required this.onGoBack}); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.celebration_rounded, size: 80, color: AppColors.rewardGold), const SizedBox(height: 20), Text( 'All done for today!', style: theme.textTheme.headlineMedium?.copyWith(fontWeight: FontWeight.w700), textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( 'You showed up and that matters. Enjoy your free time!', style: theme.textTheme.bodyLarge, textAlign: TextAlign.center, ), const SizedBox(height: 28), FilledButton( onPressed: onGoBack, child: const Text('Back to dashboard'), ), ], ), ), ); } }