import 'package:flutter/material.dart'; import '../controllers/today_screen_controller.dart'; import '../models/today_screen_models.dart'; import '../theme/focus_flow_theme.dart'; import '../theme/focus_flow_tokens.dart'; import '../widgets/app_shell.dart'; import '../widgets/required_banner.dart'; import '../widgets/sidebar.dart'; import '../widgets/task_selection_modal.dart'; import '../widgets/timeline/timeline_view.dart'; import '../widgets/top_bar.dart'; import 'demo_scheduler_composition.dart'; class FocusFlowApp extends StatelessWidget { const FocusFlowApp({required this.composition, super.key}); final DemoSchedulerComposition composition; @override Widget build(BuildContext context) { return MaterialApp( title: 'FocusFlow', debugShowCheckedModeBanner: false, theme: FocusFlowTheme.dark(), home: FocusFlowHome(composition: composition), ); } } class FocusFlowHome extends StatefulWidget { const FocusFlowHome({required this.composition, super.key}); final DemoSchedulerComposition composition; @override State createState() => _FocusFlowHomeState(); } class _FocusFlowHomeState extends State { late final TodayScreenController controller = widget.composition .createTodayScreenController(); @override void initState() { super.initState(); controller.load(); } @override void dispose() { controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: FocusFlowTokens.appBackground, body: AppShell( sidebar: const Sidebar(), child: AnimatedBuilder( animation: controller, builder: (context, _) { return switch (controller.state) { TodayScreenLoading() => const Center( child: CircularProgressIndicator(), ), TodayScreenFailure(:final message) => _PlanIssue( message: message, ), TodayScreenEmpty() => _TodayScreenScaffold( data: TodayScreenData.empty( dateLabel: widget.composition.dateLabel, ), selectedCard: controller.selectedCard, onSelectCard: controller.selectCard, onClearSelection: controller.clearSelection, ), TodayScreenReady(:final data) => _TodayScreenScaffold( data: data, selectedCard: controller.selectedCard, onSelectCard: controller.selectCard, onClearSelection: controller.clearSelection, ), }; }, ), ), ); } } class _PlanIssue extends StatelessWidget { const _PlanIssue({required this.message}); final String message; @override Widget build(BuildContext context) { return Center( child: Text(message, style: Theme.of(context).textTheme.titleMedium), ); } } class _TodayScreenScaffold extends StatelessWidget { const _TodayScreenScaffold({ required this.data, required this.selectedCard, required this.onSelectCard, required this.onClearSelection, }); final TodayScreenData data; final TimelineCardModel? selectedCard; final ValueChanged onSelectCard; final VoidCallback onClearSelection; @override Widget build(BuildContext context) { return Stack( children: [ Padding( padding: const EdgeInsets.fromLTRB( FocusFlowTokens.mainGutter, 32, FocusFlowTokens.mainGutter, 28, ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TopBar(dateLabel: data.dateLabel), const SizedBox(height: 24), RequiredBanner(model: data.requiredBanner), const SizedBox(height: 22), Expanded( child: TimelineView( cards: data.cards, range: data.timelineRange, onCardSelected: onSelectCard, ), ), ], ), ), if (selectedCard != null) ...[ Positioned.fill( child: GestureDetector( key: const ValueKey('modal-click-away'), behavior: HitTestBehavior.opaque, onTap: onClearSelection, child: const ColoredBox(color: Colors.transparent), ), ), Center( child: TaskSelectionModal( card: selectedCard!, onClose: onClearSelection, ), ), ], ], ); } }