import 'package:flutter/material.dart'; import 'package:tatlock_ui/features/front_hall/data/models/environment_model.dart' as api; /// Weather widget displaying current conditions. /// /// Displays weather data from the Core API environment endpoint. class WeatherWidget extends StatelessWidget { const WeatherWidget({ super.key, this.apiData, this.data, this.compact = false, }); /// Weather data from API. Takes priority over legacy data. final api.WeatherData? apiData; /// Legacy weather data to display. Uses mock data if null. final WeatherData? data; /// Whether to use compact layout. final bool compact; @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; // Convert API data to local model, or use legacy data final weather = apiData != null ? _fromApiData(apiData!) : (data ?? WeatherData.mock()); if (compact) { return _buildCompact(context, colorScheme, weather); } return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Header Row( children: [ Icon( Icons.wb_sunny_outlined, size: 20, color: colorScheme.onSurfaceVariant, ), const SizedBox(width: 8), Expanded( child: Text( weather.location, style: Theme.of(context).textTheme.titleSmall?.copyWith( color: colorScheme.onSurfaceVariant, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 16), // Main weather display (matching AQI layout) Row( children: [ // Weather icon in box (like AQI number box) Container( width: 64, height: 64, decoration: BoxDecoration( color: (weather.iconColor ?? colorScheme.primary) .withValues(alpha: 0.15), borderRadius: BorderRadius.circular(12), border: Border.all( color: (weather.iconColor ?? colorScheme.primary) .withValues(alpha: 0.3), width: 2, ), ), child: Center( child: Icon( weather.icon, size: 32, color: weather.iconColor ?? colorScheme.primary, ), ), ), const SizedBox(width: 16), // Temperature and condition Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${weather.temperature.round()}°${weather.unit.symbol}', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), const SizedBox(height: 4), Text( weather.condition, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: colorScheme.onSurfaceVariant, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ], ), // Details if (weather.humidity != null || weather.windSpeed != null) ...[ const SizedBox(height: 16), const Divider(height: 1), const SizedBox(height: 12), Wrap( spacing: 16, runSpacing: 8, children: [ if (weather.humidity != null) _DetailChip( label: 'Humidity', value: '${weather.humidity}%', ), if (weather.windSpeed != null) _DetailChip( label: 'Wind', value: '${weather.windSpeed!.round()} ${weather.windUnit}', ), if (weather.feelsLike != null) _DetailChip( label: 'Feels', value: '${weather.feelsLike!.round()}°${weather.unit.symbol}', ), ], ), ], ], ), ), ); } Widget _buildCompact( BuildContext context, ColorScheme colorScheme, WeatherData weather, ) { return Card( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( weather.icon, size: 24, color: weather.iconColor ?? colorScheme.primary, ), const SizedBox(width: 8), Text( '${weather.temperature.round()}°${weather.unit.symbol}', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), ), ); } } class _DetailChip extends StatelessWidget { const _DetailChip({ required this.label, required this.value, }); final String label; final String value; @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Row( mainAxisSize: MainAxisSize.min, children: [ Text( label, style: Theme.of(context).textTheme.labelSmall?.copyWith( color: colorScheme.outline, ), ), const SizedBox(width: 4), Text( value, style: Theme.of(context).textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w500, ), ), ], ); } } /// Converts API weather data to local WeatherData model. WeatherData _fromApiData(api.WeatherData data) { return WeatherData( location: data.location ?? 'Unknown', temperature: data.temperature ?? 0, condition: data.conditions ?? 'Unknown', icon: _getWeatherIcon(data.conditions, data.icon), humidity: data.humidity, windSpeed: data.windSpeed, feelsLike: data.feelsLike, iconColor: _getWeatherColor(data.conditions), ); } /// Maps weather condition to icon. IconData _getWeatherIcon(String? conditions, String? iconCode) { 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; } else if (condition.contains('wind')) { return Icons.air; } return Icons.cloud; } /// Maps weather condition to color. 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; } /// Temperature unit for weather display. enum TemperatureUnit { celsius('C'), fahrenheit('F'); const TemperatureUnit(this.symbol); final String symbol; } /// Weather data model. class WeatherData { const WeatherData({ required this.location, required this.temperature, required this.condition, required this.icon, this.unit = TemperatureUnit.celsius, this.humidity, this.windSpeed, this.windUnit = 'km/h', this.feelsLike, this.iconColor, }); final String location; final double temperature; final String condition; final IconData icon; final TemperatureUnit unit; final int? humidity; final double? windSpeed; final String windUnit; final double? feelsLike; final Color? iconColor; /// Mock weather data for development. factory WeatherData.mock() { return const WeatherData( location: 'Rotterdam, NL', temperature: 8, condition: 'Partly Cloudy', icon: Icons.cloud, humidity: 72, windSpeed: 18, feelsLike: 5, iconColor: Colors.blueGrey, ); } }