import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:tatlock_ui/features/front_hall/data/models/environment_model.dart'; /// Forecast widget displaying multi-day weather outlook. /// /// Shows a horizontal scrollable list of forecast days with /// high/low temperatures and weather icons. class ForecastWidget extends StatelessWidget { const ForecastWidget({ super.key, this.forecast, this.compact = false, }); /// Forecast data to display. final List? forecast; /// Whether to use compact layout. final bool compact; @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; if (forecast == null || forecast!.isEmpty) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Header Row( children: [ Icon( Icons.calendar_today, size: 20, color: colorScheme.onSurfaceVariant, ), const SizedBox(width: 8), Text( 'Forecast', style: Theme.of(context).textTheme.titleSmall?.copyWith( color: colorScheme.onSurfaceVariant, ), ), ], ), const SizedBox(height: 8), // Match sun position card height (170px content area) SizedBox( height: 170, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.event_busy_outlined, size: 32, color: colorScheme.outline, ), const SizedBox(height: 8), Text( 'No data available', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: colorScheme.outline, ), ), ], ), ), ), ], ), ), ); } if (compact) { return _buildCompact(context, colorScheme); } return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Header Row( children: [ Icon( Icons.calendar_today, size: 20, color: colorScheme.onSurfaceVariant, ), const SizedBox(width: 8), Text( 'Forecast', style: Theme.of(context).textTheme.titleSmall?.copyWith( color: colorScheme.onSurfaceVariant, ), ), ], ), const SizedBox(height: 8), // Content area with fixed height, card bottoms at horizon (50px from bottom) SizedBox( height: 170, child: Padding( padding: const EdgeInsets.only(bottom: 50), child: Align( alignment: Alignment.bottomCenter, child: SizedBox( height: 110, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: forecast!.length, separatorBuilder: (_, i) => const SizedBox(width: 12), itemBuilder: (context, index) { return _ForecastDayCard(day: forecast![index]); }, ), ), ), ), ), ], ), ), ); } Widget _buildCompact(BuildContext context, ColorScheme colorScheme) { // Show just first 3 days in compact mode final days = forecast!.take(3).toList(); return Card( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.calendar_today, size: 20, color: colorScheme.onSurfaceVariant, ), const SizedBox(width: 8), ...days.map((day) => Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( _getDayName(day.date), style: Theme.of(context).textTheme.labelSmall, ), Icon( _getWeatherIcon(day.conditions), size: 16, color: _getWeatherColor(day.conditions), ), Text( '${day.high?.round() ?? '--'}°', style: Theme.of(context).textTheme.labelSmall, ), ], ), )), ], ), ), ); } String _getDayName(String dateStr) { try { final date = DateTime.parse(dateStr); final now = DateTime.now(); if (date.day == now.day && date.month == now.month && date.year == now.year) { return 'Today'; } final tomorrow = now.add(const Duration(days: 1)); if (date.day == tomorrow.day && date.month == tomorrow.month && date.year == tomorrow.year) { return 'Tmrw'; } return DateFormat('E').format(date); } catch (_) { return dateStr.length > 3 ? dateStr.substring(0, 3) : dateStr; } } IconData _getWeatherIcon(String? conditions) { final condition = conditions?.toLowerCase() ?? ''; if (condition.contains('clear') || condition.contains('sunny')) { return Icons.wb_sunny; } else if (condition.contains('cloud') || condition.contains('overcast')) { return Icons.cloud; } else if (condition.contains('rain') || condition.contains('drizzle')) { return Icons.grain; } else if (condition.contains('storm') || condition.contains('thunder')) { return Icons.thunderstorm; } else if (condition.contains('snow') || condition.contains('sleet')) { return Icons.ac_unit; } else if (condition.contains('fog') || condition.contains('mist')) { return Icons.blur_on; } return Icons.cloud; } Color? _getWeatherColor(String? conditions) { final condition = conditions?.toLowerCase() ?? ''; if (condition.contains('clear') || condition.contains('sunny')) { return Colors.amber; } else if (condition.contains('cloud')) { return Colors.blueGrey; } else if (condition.contains('rain')) { return Colors.blue; } else if (condition.contains('storm')) { return Colors.deepPurple; } else if (condition.contains('snow')) { return Colors.lightBlue; } return null; } } class _ForecastDayCard extends StatelessWidget { const _ForecastDayCard({required this.day}); final ForecastDay day; @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final icon = _getWeatherIcon(day.conditions); final color = _getWeatherColor(day.conditions) ?? colorScheme.primary; return Container( width: 72, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 12), decoration: BoxDecoration( color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(12), ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Day name Text( _getDayName(day.date), style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w600, ), ), // Weather icon Icon( icon, size: 24, color: color, ), // High/Low temps Column( children: [ Text( '${day.high?.round() ?? '--'}°', style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.bold, ), ), Text( '${day.low?.round() ?? '--'}°', style: Theme.of(context).textTheme.labelSmall?.copyWith( color: colorScheme.onSurfaceVariant, ), ), ], ), ], ), ); } String _getDayName(String dateStr) { try { final date = DateTime.parse(dateStr); final now = DateTime.now(); if (date.day == now.day && date.month == now.month && date.year == now.year) { return 'Today'; } final tomorrow = now.add(const Duration(days: 1)); if (date.day == tomorrow.day && date.month == tomorrow.month && date.year == tomorrow.year) { return 'Tmrw'; } return DateFormat('EEE').format(date); } catch (_) { return dateStr.length > 3 ? dateStr.substring(0, 3) : dateStr; } } IconData _getWeatherIcon(String? conditions) { final condition = conditions?.toLowerCase() ?? ''; if (condition.contains('clear') || condition.contains('sunny')) { return Icons.wb_sunny; } else if (condition.contains('cloud') || condition.contains('overcast')) { return Icons.cloud; } else if (condition.contains('rain') || condition.contains('drizzle')) { return Icons.grain; } else if (condition.contains('storm') || condition.contains('thunder')) { return Icons.thunderstorm; } else if (condition.contains('snow') || condition.contains('sleet')) { return Icons.ac_unit; } else if (condition.contains('fog') || condition.contains('mist')) { return Icons.blur_on; } return Icons.cloud; } Color? _getWeatherColor(String? conditions) { final condition = conditions?.toLowerCase() ?? ''; if (condition.contains('clear') || condition.contains('sunny')) { return Colors.amber; } else if (condition.contains('cloud')) { return Colors.blueGrey; } else if (condition.contains('rain')) { return Colors.blue; } else if (condition.contains('storm')) { return Colors.deepPurple; } else if (condition.contains('snow')) { return Colors.lightBlue; } return null; } }