import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../nutrition/domain/entities/meal_type.dart'; import '../controllers/meal_plan_calendar_controller.dart'; import '../controllers/recipe_import_controller.dart'; import '../widgets/assign_recipe_sheet.dart'; import '../widgets/weekly_calendar_view.dart'; /// GLP-1 meal planner: the responsive 7-day weekly calendar (Monday–Sunday) /// with per-day protein rollups. Recipe import and browsing live in the /// Recipe Vault; slots are filled via the searchable "+ Assign Recipe" /// sheet. class MealPlannerScreen extends ConsumerStatefulWidget { const MealPlannerScreen({super.key}); @override ConsumerState createState() => _MealPlannerScreenState(); } class _MealPlannerScreenState extends ConsumerState { /// Body height below which the page collapses into a single scrollable /// column: the week header plus a minimal usable calendar area. Below /// this, a plain `Column` would overflow and paint the yellow/black /// hatched stripes. static const double _kFullScrollThreshold = 360; /// Calendar height used in the fully-scrollable fallback layout; tall /// enough for the day header plus four meal slots. static const double _kCalendarScrollHeight = 560; @override Widget build(BuildContext context) { final calendarState = ref.watch(mealPlanCalendarControllerProvider); ref.listen(mealPlanCalendarControllerProvider, (previous, next) { if (next.errorMessage != null && next.errorMessage != previous?.errorMessage) { _showErrorSnack(next.errorMessage!); } }); return Scaffold( appBar: AppBar(title: const Text('Meal Planner')), body: LayoutBuilder( builder: (context, constraints) { // Short windows (e.g. resized desktop windows) can no longer fit // a useful calendar area; collapse the whole page into a single // scroll view instead of overflowing. if (constraints.maxHeight < _kFullScrollThreshold) { return SingleChildScrollView( padding: const EdgeInsets.only(bottom: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _WeekNavigationHeader(state: calendarState), SizedBox( height: _kCalendarScrollHeight, child: _buildCalendar(calendarState), ), ], ), ); } return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _WeekNavigationHeader(state: calendarState), const Divider(height: 1), Expanded(child: _buildCalendar(calendarState)), ], ); }, ), ); } /// Weekly calendar grid (or the initial loading spinner). Widget _buildCalendar(MealPlanCalendarState calendarState) => !calendarState.hasLoaded ? const Center(child: CircularProgressIndicator()) : WeeklyCalendarView( state: calendarState, onSlotTapped: _onSlotTapped, onTogglePrepared: (slot) => ref .read(mealPlanCalendarControllerProvider.notifier) .togglePrepared(slot), onClearSlot: (slot) => ref .read(mealPlanCalendarControllerProvider.notifier) .clearSlot(slot), ); void _showErrorSnack(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: Theme.of(context).colorScheme.error, ), ); } /// Opens the searchable assign sheet, populated from the Recipe Vault. Future _onSlotTapped(DateTime day, MealType mealType) async { final calendarState = ref.read(mealPlanCalendarControllerProvider); final importState = ref.read(recipeImportControllerProvider); await showAssignRecipeSheet( context, day: day, mealType: mealType, savedRecipes: importState.savedRecipes, existingSlot: calendarState.slotFor(day, mealType), ); } } /// Week navigation row: prev/next arrows, the visible week label, and a /// "Today" shortcut. class _WeekNavigationHeader extends StatelessWidget { const _WeekNavigationHeader({required this.state}); final MealPlanCalendarState state; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Row( children: [ IconButton( tooltip: 'Previous week', icon: const Icon(Icons.chevron_left), onPressed: state.isLoading ? null : () => _readController(context).previousWeek(), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( state.weekLabel, style: theme.textTheme.titleSmall ?.copyWith(fontWeight: FontWeight.bold), ), Text( 'Monday – Sunday', style: theme.textTheme.labelSmall ?.copyWith(color: theme.colorScheme.outline), ), ], ), ), TextButton( onPressed: state.isLoading ? null : () => _readController(context).goToToday(), child: const Text('Today'), ), IconButton( tooltip: 'Next week', icon: const Icon(Icons.chevron_right), onPressed: state.isLoading ? null : () => _readController(context).nextWeek(), ), if (state.isLoading) ...[ const SizedBox(width: 4), const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ), const SizedBox(width: 8), ], ], ), ); } MealPlanCalendarController _readController(BuildContext context) => ProviderScope.containerOf(context, listen: false) .read(mealPlanCalendarControllerProvider.notifier); }