diff --git a/lib/main.dart b/lib/main.dart index 2f7d53c3a..db1b45899 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -70,15 +70,10 @@ void main() async { DeviceOrientation.portraitDown, ]); +// WebViewPlatform.instance ??= SurfaceAndroidWebView();; + - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - SystemChrome.setSystemUIOverlayStyle( - const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - systemNavigationBarColor: Colors.transparent, - ), - ); final dir = await getApplicationDocumentsDirectory(); Hive.init(dir.path); @@ -164,8 +159,21 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { final themeController = Provider.of(context); + final isLight = Theme.of(context).brightness == Brightness.light; + + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + statusBarColor: Color(0x01000000), + statusBarIconBrightness: isLight ? Brightness.dark : Brightness.light, + systemNavigationBarIconBrightness: isLight ? Brightness.dark : Brightness.light, + systemNavigationBarColor: MediaQuery.of(context).systemGestureInsets.left > 0 ? Color(0x01000000) : isLight ? Color(0x01000000) : Color.fromRGBO(0, 0, 0, 0.3) + + ), + ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + + - return MaterialApp( title: 'WeatherMaster', debugShowCheckedModeBanner: false, @@ -235,13 +243,7 @@ class MyApp extends StatelessWidget { } -class LocationPromptScreen extends StatelessWidget { - const LocationPromptScreen({super.key}); - - - @override - Widget build(BuildContext context) { - ColorScheme customDarkScheme = ColorScheme( +ColorScheme customDarkScheme = ColorScheme( brightness: Brightness.dark, primary: Color(paletteStartScreen.primary.get(80)), onPrimary: Color(paletteStartScreen.primary.get(20)), @@ -274,16 +276,14 @@ class LocationPromptScreen extends StatelessWidget { surfaceContainerLowest: Color(paletteStartScreen.neutral.get(4)) ); - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); +class LocationPromptScreen extends StatelessWidget { + const LocationPromptScreen({super.key}); + + + @override + Widget build(BuildContext context) { + - SystemChrome.setSystemUIOverlayStyle( - SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - statusBarIconBrightness: Theme.of(context).brightness == Brightness.light ? Brightness.dark : Brightness.light, - systemNavigationBarColor: Colors.transparent, - - ), - ); @@ -309,6 +309,8 @@ class LocationPromptScreen extends StatelessWidget { } + + return Scaffold( backgroundColor: customDarkScheme.surfaceContainerLow, @@ -389,8 +391,21 @@ class LocationPromptScreen extends StatelessWidget { minimumSize: WidgetStateProperty.all(const Size(300, 60)), ), onPressed: () async { + + final dialogKey = GlobalKey(); + try { + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => LoadingDialog( + key: dialogKey, + initialMessage: "Getting your location...", + ), + ); + + final position = await getCurrentPosition(); final geoData = await reverseGeocode(position.latitude, position.longitude); @@ -402,6 +417,9 @@ class LocationPromptScreen extends StatelessWidget { country: geoData['country']!, ); + dialogKey.currentState?.updateMessage("Found\n ${geoData['city']}, ${geoData['country']}\n Loading weather..."); + + saveLocation(saved); final cacheKey = @@ -418,6 +436,8 @@ class LocationPromptScreen extends StatelessWidget { })); + + final weatherService = WeatherService(); await weatherService.fetchWeather( saved.latitude, @@ -425,6 +445,9 @@ class LocationPromptScreen extends StatelessWidget { locationName: cacheKey, ); + + Navigator.of(context, rootNavigator: true).pop(); + Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) => WeatherHome( @@ -438,6 +461,7 @@ class LocationPromptScreen extends StatelessWidget { ), ); } catch (e) { + Navigator.of(context, rootNavigator: true).pop(); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error: ${e.toString()}')), ); @@ -468,3 +492,53 @@ class LocationPromptScreen extends StatelessWidget { ); } } + + +class LoadingDialog extends StatefulWidget { + final String initialMessage; + final GlobalKey key; + + const LoadingDialog({ + required this.key, + required this.initialMessage, + }) : super(key: key); + + @override + LoadingDialogState createState() => LoadingDialogState(); +} + + +class LoadingDialogState extends State { + late String message; + + @override + void initState() { + super.initState(); + message = widget.initialMessage; + } + + void updateMessage(String newMessage) { + if (mounted) { + setState(() { + message = newMessage; + }); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: customDarkScheme.surfaceContainerHigh, + + content: Column( + mainAxisSize: MainAxisSize.min, + spacing: 10, + children: [ + const SizedBox(height: 6,), + CircularProgressIndicator(color: customDarkScheme.primary, year2023: false,), + Text(message, style: TextStyle(color: customDarkScheme.onSurface,), textAlign: TextAlign.center,), + ], + ), + ); + } +} diff --git a/lib/screens/daily_forecast.dart b/lib/screens/daily_forecast.dart index c70b9a176..2fc8a54e8 100644 --- a/lib/screens/daily_forecast.dart +++ b/lib/screens/daily_forecast.dart @@ -4,8 +4,6 @@ import 'package:hive/hive.dart'; import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; import '../utils/unit_converter.dart'; -import 'package:provider/provider.dart'; -import '../notifiers/unit_settings_notifier.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; import '../utils/icon_map.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -190,21 +188,21 @@ class _DailyForecastPageState extends State { SizedBox( child: ConditionsWidgets( selectedContainerBgIndex: Theme.of(context).colorScheme.surfaceContainerLowest.toARGB32(), - currentHumidity: current['relative_humidity_2m'], - currentDewPoint: hourly['dew_point_2m'][0].toDouble(), - currentSunrise: daily['sunrise'][0], - currentSunset: daily['sunset'][0], - currentPressure: current['pressure_msl'], - currentVisibility: hourly['visibility'][0], - currentWindSpeed: current['wind_speed_10m'], - currentWindDirc: current['wind_direction_10m'], + currentHumidity: current['relative_humidity_2m'] ?? 0.0000001, + currentDewPoint: hourly['dew_point_2m'][0].toDouble() ?? 0.0000001, + currentSunrise: daily['sunrise'][0] ?? 0.0000001, + currentSunset: daily['sunset'][0] ?? 0.0000001, + currentPressure: current['pressure_msl'] ?? 0.0000001, + currentVisibility: hourly['visibility'][0] ?? 0.0000001, + currentWindSpeed: current['wind_speed_10m'] ?? 0.0000001, + currentWindDirc: current['wind_direction_10m'] ?? 0.0000001, timezone: weather['timezone'].toString(), utcOffsetSeconds: weather['utc_offset_seconds'].toString(), - currentUvIndex: daily['uv_index_max'][0], - currentAQIUSA: weather['air_quality']['current']['us_aqi'], - currentAQIEURO: weather['air_quality']['current']['european_aqi'], - currentTotalPrec: daily['precipitation_sum'][0], - currentDayLength: daily['daylight_duration'][0], + currentUvIndex: daily['uv_index_max'][0] ?? 0.0000001, + currentAQIUSA: weather['air_quality']['current']['us_aqi'] ?? 0.0000001, + currentAQIEURO: weather['air_quality']['current']['european_aqi'] ?? 0.0000001, + currentTotalPrec: daily['precipitation_sum'][0] ?? 0.0000001, + currentDayLength: daily['daylight_duration'][0] ?? 0.0000001, isFromHome: false, ), ) @@ -214,15 +212,15 @@ class _DailyForecastPageState extends State { selectedContainerBgIndex: Theme.of(context).colorScheme.surfaceContainerLowest.toARGB32(), currentSunrise: daily['sunrise']?[selectedIndex] ?? '', currentSunset: daily['sunset']?[selectedIndex] ?? '', - currentPressure: hourly['pressure_msl']?[firstIndex] ?? 0, - currentVisibility: hourly['visibility']?[firstIndex] ?? 0, - currentWindSpeed: daily['wind_speed_10m_max']?[selectedIndex] ?? 0, - currentWindDirc: hourly['wind_direction_10m']?[firstIndex] ?? 0, + currentPressure: hourly['pressure_msl']?[firstIndex] ?? 0.0000001, + currentVisibility: hourly['visibility']?[firstIndex] ?? 0.0000001, + currentWindSpeed: daily['wind_speed_10m_max']?[selectedIndex] ?? 0.0000001, + currentWindDirc: hourly['wind_direction_10m']?[firstIndex] ?? 0.0000001, timezone: weather['timezone'].toString(), utcOffsetSeconds: weather['utc_offset_seconds'].toString(), - currentUvIndex: daily['uv_index_max']?[selectedIndex] ?? 0, - currentTotalPrec: daily['precipitation_sum']?[selectedIndex] ?? 0, - currentTotalPrecProb: daily['precipitation_probability_max']?[selectedIndex] ?? 0, + currentUvIndex: daily['uv_index_max']?[selectedIndex] ?? 0.0000001, + currentTotalPrec: daily['precipitation_sum']?[selectedIndex] ?? 0.0000001, + currentTotalPrecProb: daily['precipitation_probability_max']?[selectedIndex] ?? 0.0000001, ) ), SizedBox(height: MediaQuery.of(context).padding.bottom + 16,) @@ -315,7 +313,7 @@ class _DailyForecastCardState extends State { - final tempUnit = context.watch().tempUnit; + final tempUnit = PreferencesHelper.getString("selectedTempUnit") ?? "Celsius"; num convert(num celsius) => tempUnit == "Fahrenheit" ? UnitConverter.celsiusToFahrenheit(celsius.toDouble()).round() @@ -485,9 +483,10 @@ class HourlyCardForecast extends StatelessWidget { if (startIndex == -1) startIndex = 0; + final timeUnit = PreferencesHelper.getString("selectedTimeUnit") ?? '12 hr'; + final tempUnit = PreferencesHelper.getString("selectedTempUnit") ?? "Celsius"; - print(startIndex); return Container( decoration: BoxDecoration( @@ -532,8 +531,8 @@ class HourlyCardForecast extends StatelessWidget { forecastLocal.hour, ); - final hour = context.watch().timeUnit == '24 hr' ? "${roundedDisplayTime.hour.toString().padLeft(2, '0')}:00" : UnitConverter.formatTo12Hour(roundedDisplayTime); - final temp = context.watch().tempUnit == 'Fahrenheit' ? UnitConverter.celsiusToFahrenheit(hourlyTemps[dataIndex].toDouble()).round() : hourlyTemps[dataIndex].toDouble().round(); + final hour = timeUnit == '24 hr' ? "${roundedDisplayTime.hour.toString().padLeft(2, '0')}:00" : UnitConverter.formatTo12Hour(roundedDisplayTime); + final temp = tempUnit == 'Fahrenheit' ? UnitConverter.celsiusToFahrenheit(hourlyTemps[dataIndex].toDouble()).round() : hourlyTemps[dataIndex].toDouble().round(); final code = hourlyWeatherCodes[dataIndex]; final precipProb = hourlyPrecpProb[dataIndex]; @@ -588,6 +587,11 @@ class ForecastDetailsHeader extends StatelessWidget { @override Widget build(BuildContext context) { + + + final tempUnit = PreferencesHelper.getString("selectedTempUnit") ?? "Celsius"; + + if (selectedDayData == null) { return const Text('No data selected.'); } @@ -599,8 +603,8 @@ class ForecastDetailsHeader extends StatelessWidget { Padding(padding: EdgeInsets.only(left: 12, right: 12, top: 3), child: Text(getCityFromPreferences(), style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 22, fontWeight: FontWeight.w500),)), Padding(padding: EdgeInsets.only(left: 12, right: 12), - child: Row(children: [Text(context.watch().tempUnit == 'Fahrenheit' ? "${UnitConverter.celsiusToFahrenheit(selectedDayData!['maxTemp']).round()}°/" : "${selectedDayData!['maxTemp'].round()}°/", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 56, fontWeight: FontWeight.w500)), - Text(context.watch().tempUnit == 'Fahrenheit' ? "${UnitConverter.celsiusToFahrenheit(selectedDayData!['minTemp']).round()}°" : "${selectedDayData!['minTemp'].round()}°", style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 56, fontWeight: FontWeight.w500)), + child: Row(children: [Text(tempUnit == 'Fahrenheit' ? "${UnitConverter.celsiusToFahrenheit(selectedDayData!['maxTemp']).round()}°/" : "${selectedDayData!['maxTemp'].round()}°/", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 56, fontWeight: FontWeight.w500)), + Text(tempUnit == 'Fahrenheit' ? "${UnitConverter.celsiusToFahrenheit(selectedDayData!['minTemp']).round()}°" : "${selectedDayData!['minTemp'].round()}°", style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 56, fontWeight: FontWeight.w500)), SizedBox(width: 10,), SvgPicture.asset( WeatherIconMapper.getIcon(selectedDayData!['weatherCode'], 1), @@ -692,23 +696,30 @@ class ConditionsWidgetsForecast extends StatelessWidget { now.microsecond, ); - final sunriseFormat = context.watch().timeUnit == '24 hr' ? DateFormat.Hm().format(sunrise) : DateFormat.jm().format(sunrise); - final sunsetFormat = context.watch().timeUnit == '24 hr' ? DateFormat.Hm().format(sunset) : DateFormat.jm().format(sunset); + final timeUnit = PreferencesHelper.getString("selectedTimeUnit") ?? '12 hr'; + final pressureUnit = PreferencesHelper.getString("selectedPressureUnit") ?? "hPa"; + final precipUnit = PreferencesHelper.getString("selectedPrecipitationUnit") ?? "mm"; + final visibilityUnit = PreferencesHelper.getString("selectedVisibilityUnit") ?? "Km"; + final windUnit = PreferencesHelper.getString("selectedWindUnit") ?? "Km/h"; - final convertedPressure = context.watch().pressureUnit == 'inHg' + + final sunriseFormat = timeUnit == '24 hr' ? DateFormat.Hm().format(sunrise) : DateFormat.jm().format(sunrise); + final sunsetFormat = timeUnit == '24 hr' ? DateFormat.Hm().format(sunset) : DateFormat.jm().format(sunset); + + final convertedPressure = pressureUnit == 'inHg' ? UnitConverter.hPaToInHg(currentPressure) - : context.watch().pressureUnit == 'mmHg' + : pressureUnit == 'mmHg' ? UnitConverter.hPaToMmHg(currentPressure) : currentPressure; - final convertedPrecip = context.watch().precipitationUnit == 'cm' + final convertedPrecip = precipUnit == 'cm' ? UnitConverter.mmToCm(currentTotalPrec) - : context.watch().precipitationUnit == 'in' + : precipUnit == 'in' ? UnitConverter.mmToIn(currentTotalPrec) : currentTotalPrec; - final convertedVisibility = context.watch().visibilityUnit == 'Mile' + final convertedVisibility = visibilityUnit == 'Mile' ? UnitConverter.mToMiles(currentVisibility.toDouble()) : UnitConverter.mToKm(currentVisibility.toDouble()); @@ -717,9 +728,9 @@ class ConditionsWidgetsForecast extends StatelessWidget { } - final convertedwindSpeed = context.watch().windUnit == 'Mph' + final convertedwindSpeed = windUnit == 'Mph' ? UnitConverter.kmhToMph(currentWindSpeed) - : context.watch().windUnit == 'M/s' + : windUnit == 'M/s' ? UnitConverter.kmhToMs(currentWindSpeed) : currentWindSpeed.toStringAsFixed(0); @@ -825,13 +836,13 @@ class ConditionsWidgetsForecast extends StatelessWidget { Align( alignment: Alignment.center, - child: Text("${convertedPressure.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), + child: Text(currentPressure == 0.0000001 ? '--' : "${convertedPressure.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), ), Padding( padding: EdgeInsets.only(bottom: 30), child: Align( alignment: Alignment.bottomCenter, - child: Text(context.watch().pressureUnit, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 18),), + child: Text(pressureUnit, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 18),), ), ), ] @@ -859,13 +870,13 @@ class ConditionsWidgetsForecast extends StatelessWidget { Align( alignment: Alignment.center, - child: Text("${convertedVisibility.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onTertiaryContainer, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), + child: Text(currentVisibility == 0.0000001 ? '--' : "${convertedVisibility.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onTertiaryContainer, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), ), Padding( padding: EdgeInsets.only(bottom: 30), child: Align( alignment: Alignment.bottomCenter, - child: Text(context.watch().visibilityUnit, style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 18),), + child: Text(visibilityUnit, style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 18),), ), ), ], @@ -882,12 +893,12 @@ class ConditionsWidgetsForecast extends StatelessWidget { ), child: Stack( children: [ - WindCompassWidget(currentWindDirc: currentWindDirc, backgroundColor: Color(selectedContainerBgIndex)), + WindCompassWidget(currentWindDirc: currentWindDirc, backgroundColor: Color(selectedContainerBgIndex)), headerWidgetConditions(headerText: "direction".tr(), headerIcon: Symbols.explore,), Align( alignment: Alignment.center, - child: Text(getCompassDirection(currentWindDirc), style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), + child: Text(currentWindDirc == 0.0000001 ? '--' : getCompassDirection(currentWindDirc), style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), ), Align( @@ -895,7 +906,7 @@ class ConditionsWidgetsForecast extends StatelessWidget { child: Container( margin: EdgeInsets.only(left: 20, right: 20), height: 55, - child: Text("$convertedwindSpeed ${context.watch().windUnit}", style: + child: Text(currentWindSpeed == 0.0000001 ? '--' : "$convertedwindSpeed $windUnit", style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 16), maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,), ), ), @@ -926,13 +937,13 @@ class ConditionsWidgetsForecast extends StatelessWidget { Align( alignment: Alignment.center, - child: Text("${currentUvIndex.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), + child: Text(currentUvIndex == 0.0000001 ? '--' : "${currentUvIndex.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), ), Padding( padding: EdgeInsets.only(bottom: MediaQuery.of(context).size.width * 0.11), child: Align( alignment: Alignment.bottomCenter, - child: Text(getUvIndexType(currentUvIndex.round()), style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 15),), + child: Text(currentUvIndex == 0.0000001 ? '--' : getUvIndexType(currentUvIndex.round()), style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 15),), ), ), ], @@ -960,10 +971,10 @@ class ConditionsWidgetsForecast extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text("${double.parse(convertedPrecip.toStringAsFixed(2))}", style: TextStyle(fontSize: MediaQuery.of(context).size.width * 0.11, fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.onSurface),), + Text(currentTotalPrec == 0.0000001 ? '--' : "${double.parse(convertedPrecip.toStringAsFixed(2))}", style: TextStyle(fontSize: MediaQuery.of(context).size.width * 0.11, fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.onSurface),), Padding( padding: EdgeInsets.only(top: 15), - child: Text(context.watch().precipitationUnit, style: TextStyle(fontSize: 20, color: Theme.of(context).colorScheme.secondary),), + child: Text(precipUnit, style: TextStyle(fontSize: 20, color: Theme.of(context).colorScheme.secondary),), ), ], ), @@ -979,7 +990,7 @@ class ConditionsWidgetsForecast extends StatelessWidget { SizedBox( width: 100, child: Text("Total rain for the day", style: TextStyle(height: 1.2, color: Theme.of(context).colorScheme.onSurfaceVariant))), - Text("$currentTotalPrecProb%", style: TextStyle(fontWeight: FontWeight.w700, color: Theme.of(context).colorScheme.primary),) + Text(currentTotalPrecProb == 0.0000001 ? '--' : "$currentTotalPrecProb%", style: TextStyle(fontWeight: FontWeight.w700, color: Theme.of(context).colorScheme.primary),) ], ) ) diff --git a/lib/screens/extended_widgets.dart b/lib/screens/extended_widgets.dart index 2ec8be4de..9ee1b81e5 100644 --- a/lib/screens/extended_widgets.dart +++ b/lib/screens/extended_widgets.dart @@ -799,8 +799,9 @@ final duskFormatted = formatInstantToLocalTime(dusk, timeUnit: timeFormatDUSKDAW final data = snapshot.data!; final weather = data['data']; final hourly = weather['hourly']; + final List hourlyTime = hourly['time']; - final currentVisibility = hourly['visibility'][0]; + final currentVisibility = hourly['visibility'][getStartIndex(weather['utc_offset_seconds'].toString(), hourlyTime)] ?? 0.0; final visibilityUnit = PreferencesHelper.getString("selectedVisibilityUnit") ?? "Km"; final convertedVisibility = visibilityUnit == 'Mile' @@ -1145,7 +1146,8 @@ Widget buildUVExtended(){ final hourly = weather['hourly']; final List hourlyTime = hourly['time']; - final List uvIndexes = hourly['uv_index']; +final List uvIndexes = hourly['uv_index']; + final offset = Duration(seconds: int.parse(weather['utc_offset_seconds'].toString())); @@ -1164,8 +1166,11 @@ Widget buildUVExtended(){ if (startIndex == -1) startIndex = 0; -final double minUv = uvIndexes.reduce((a, b) => a < b ? a : b); -final double maxUv = uvIndexes.reduce((a, b) => a > b ? a : b); + +final nonNullUvIndexes = uvIndexes.whereType().toList(); +final double minUv = nonNullUvIndexes.isNotEmpty ? nonNullUvIndexes.reduce((a, b) => a < b ? a : b).toDouble() : 0; +final double maxUv = nonNullUvIndexes.isNotEmpty ? nonNullUvIndexes.reduce((a, b) => a > b ? a : b).toDouble() : 1; + List todayUvIndexes = []; @@ -1202,9 +1207,9 @@ final double maxUv = uvIndexes.reduce((a, b) => a > b ? a : b); Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Text("${avgUv.round()}", style: TextStyle(fontSize: 50, color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.w500),), + Text(avgUv != null ? "${avgUv.round()}" : "--", style: TextStyle(fontSize: 50, color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.w500),), Padding(padding: EdgeInsets.only(bottom: 11, left: 8), - child: Text(getUvIndexType(avgUv.round()), style: TextStyle(fontSize: 20, color: Theme.of(context).colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500),)) + child: Text(avgUv != null ? getUvIndexType(avgUv.round()) : "Not available", style: TextStyle(fontSize: 20, color: Theme.of(context).colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500),)) ], ) @@ -1236,9 +1241,11 @@ final double maxUv = uvIndexes.reduce((a, b) => a > b ? a : b); final uv = uvIndexes[dataIndex]; - - final double uvValue = (uv is num) ? uv.toDouble() : 0; - final double uvPercentage = ((uvValue - minUv) / (maxUv - minUv)) * 100; + final bool isValidUv = uv is num; + final double? uvValue = isValidUv ? uv.toDouble() : null; + final double uvPercentage = uvValue != null && maxUv != minUv + ? ((uvValue - minUv) / (maxUv - minUv)) * 100 + : 0; EdgeInsets itemMargin = EdgeInsets.only( @@ -1281,7 +1288,7 @@ final double maxUv = uvIndexes.reduce((a, b) => a > b ? a : b); width: 43, height: math.max((uvPercentage / 100) * 160, 48), decoration: BoxDecoration( - color: getUvColor(uvValue, context), + color: getUvColor(uvValue ?? 0, context), borderRadius: BorderRadius.circular(50), ), child: Align( @@ -1316,7 +1323,7 @@ final double maxUv = uvIndexes.reduce((a, b) => a > b ? a : b); ), const SizedBox(height: 10), - Text("${uv.round()}", style: TextStyle(fontSize: 14, color: Theme.of(context).colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500)), + Text(uvValue != null ? "${uvValue.round()}" : "--", style: TextStyle(fontSize: 14, color: Theme.of(context).colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500)), Text(hour, style: TextStyle(fontSize: 15, color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.w500)), ], ), @@ -1985,4 +1992,24 @@ class AQISliderBar extends StatelessWidget { // ], ); } +} + +int getStartIndex(utc_offset_seconds, hourlyTime) { + final offset = Duration(seconds: int.parse(utc_offset_seconds)); + final nowUtc = DateTime.now().toUtc(); + final nowLocal = nowUtc.add(offset); + + final timeUnit = PreferencesHelper.getString("selectedTimeUnit") ?? '12 hr'; + + + final roundedNow = DateTime(nowLocal.year, nowLocal.month, nowLocal.day, nowLocal.hour); + + int startIndex = hourlyTime.indexWhere((timeStr) { + final forecastLocal = DateTime.parse(timeStr); + return !forecastLocal.isBefore(roundedNow); + }); + + if (startIndex == -1) startIndex = 0; + + return startIndex; } \ No newline at end of file diff --git a/lib/widgets/current_conditions_card.dart b/lib/widgets/current_conditions_card.dart index f09634ce9..b6067f876 100644 --- a/lib/widgets/current_conditions_card.dart +++ b/lib/widgets/current_conditions_card.dart @@ -64,6 +64,7 @@ class _ConditionsWidgetsState extends State { List itemOrder = []; + final String orderPrefsKey = 'tile_order'; @override @@ -193,7 +194,7 @@ List gridItems = itemOrder.map((i) { padding: EdgeInsets.only(left: 10), child: Align( alignment: Alignment.centerLeft, - child: Text("${widget.currentHumidity}%", + child: Text("${widget.currentHumidity == 0.0000001 ? '--' : widget.currentHumidity}%", style: TextStyle( color: Theme.of(context).colorScheme.onTertiaryContainer, fontSize: MediaQuery.of(context).size.width * 0.13, fontWeight: FontWeight.w500), ), @@ -213,7 +214,7 @@ List gridItems = itemOrder.map((i) { color: Theme.of(context).colorScheme.tertiary, borderRadius: BorderRadius.circular(50), ), - child: Center(child: Text("$dewpointConverted°", style: TextStyle(color: Theme.of(context).colorScheme.onTertiary, fontSize: 16))), + child: Center(child: Text(widget.currentDewPoint == 0.0000001 ? '--' : "$dewpointConverted°", style: TextStyle(color: Theme.of(context).colorScheme.onTertiary, fontSize: 16))), ), Text("dew_point".tr(), style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.w500)) ], @@ -225,11 +226,20 @@ List gridItems = itemOrder.map((i) { ), ), onTap: () { - widget.isFromHome ? Navigator.of(context).push( + if(widget.isFromHome){ + if(widget.currentPressure == 0.0000001){ + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Row(spacing: 10, children: [Icon(Symbols.error, color: Theme.of(context).colorScheme.onInverseSurface,), Text("No data available")],)) + ); + } else{ + Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ExtendWidget('humidity_widget'), fullscreenDialog: true), - ) : null; - }, - ); + ); + } + } + }, + ); case 1: return GestureDetector( @@ -280,7 +290,7 @@ List gridItems = itemOrder.map((i) { decoration: BoxDecoration( border: Border( top: BorderSide( - color: Theme.of(context).colorScheme.outline, // Or any color you need + color: Theme.of(context).colorScheme.outline, width: 1.5, ), ), @@ -342,7 +352,7 @@ List gridItems = itemOrder.map((i) { Align( alignment: Alignment.center, - child: Text("${convertedPressure.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), + child: Text(widget.currentPressure == 0.0000001 ? '--' : "${convertedPressure.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), ), Padding( padding: EdgeInsets.only(bottom: 30), @@ -355,10 +365,19 @@ List gridItems = itemOrder.map((i) { ), onTap: () { - widget.isFromHome ? Navigator.of(context).push( + if(widget.isFromHome){ + if(widget.currentPressure == 0.0000001){ + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Row(spacing: 10, children: [Icon(Symbols.error, color: Theme.of(context).colorScheme.onInverseSurface,), Text("No data available")],)) + ); + } else{ + Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ExtendWidget('pressure_widget'), fullscreenDialog: true), - ) : null; - }, + ); + } + } + }, ); case 3: @@ -387,7 +406,7 @@ List gridItems = itemOrder.map((i) { Align( alignment: Alignment.center, - child: Text("${convertedVisibility.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onTertiaryContainer, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), + child: Text(widget.currentVisibility == 0.0000001 ? '--' : "${convertedVisibility.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onTertiaryContainer, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), ), Padding( padding: EdgeInsets.only(bottom: 30), @@ -401,10 +420,19 @@ List gridItems = itemOrder.map((i) { ), ), onTap: () { - widget.isFromHome ? Navigator.of(context).push( + if(widget.isFromHome){ + if(widget.currentVisibility== 0.0000001){ + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Row(spacing: 10, children: [Icon(Symbols.error, color: Theme.of(context).colorScheme.onInverseSurface,), Text("No data available")],)) + ); + } else{ + Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ExtendWidget('visibility_widget'), fullscreenDialog: true), - ) : null; - }, + ); + } + } + } ); case 4: @@ -425,7 +453,7 @@ List gridItems = itemOrder.map((i) { headerWidgetConditions(headerText: "direction".tr(), headerIcon: Symbols.explore,), Align( alignment: Alignment.center, - child: Text(getCompassDirection(widget.currentWindDirc), style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), + child: Text(widget.currentWindDirc == 0.0000001 ? '--' : getCompassDirection(widget.currentWindDirc), style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), ), Align( @@ -433,7 +461,7 @@ List gridItems = itemOrder.map((i) { child: Container( margin: EdgeInsets.only(left: 20, right: 20), height: 55, - child: Text(getWindSpeedType(widget.currentWindSpeed), style: + child: Text(widget.currentWindSpeed == 0.0000001 ? '--' : getWindSpeedType(widget.currentWindSpeed), style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 16), maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,), ), ), @@ -442,10 +470,19 @@ List gridItems = itemOrder.map((i) { ) ), onTap: () { - widget.isFromHome ? Navigator.of(context).push( + if(widget.isFromHome){ + if(widget.currentWindSpeed == 0.0000001){ + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Row(spacing: 10, children: [Icon(Symbols.error, color: Theme.of(context).colorScheme.onInverseSurface,), Text("No data available")],)) + ); + } else{ + Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ExtendWidget('winddirc_widget'), fullscreenDialog: true), - ) : null; - }, + ); + } + } + }, ); case 5: @@ -472,13 +509,13 @@ List gridItems = itemOrder.map((i) { Align( alignment: Alignment.center, - child: Text("${widget.currentUvIndex.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), + child: Text("${widget.currentUvIndex == 0.0000001 ? '--' : widget.currentUvIndex.round()}", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: MediaQuery.of(context).size.width * 0.1, fontWeight: FontWeight.w500),), ), Padding( padding: EdgeInsets.only(bottom: 40), child: Align( alignment: Alignment.bottomCenter, - child: Text(getUvIndexType(widget.currentUvIndex.round()), style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 15),), + child: Text(widget.currentUvIndex == 0.0000001 ? '--' : getUvIndexType(widget.currentUvIndex.round()), style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 15),), ), ), ], @@ -486,10 +523,19 @@ List gridItems = itemOrder.map((i) { ), ), onTap: () { - widget.isFromHome ? Navigator.of(context).push( + if(widget.isFromHome){ + if(widget.currentUvIndex == 0.0000001){ + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Row(spacing: 10, children: [Icon(Symbols.error, color: Theme.of(context).colorScheme.onInverseSurface,), Text("No data available")],)) + ); + } else{ + Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ExtendWidget('uv_widget'), fullscreenDialog: true), - ) : null; - }, + ); + } + } + } ); case 6: @@ -512,7 +558,7 @@ List gridItems = itemOrder.map((i) { alignment: Alignment.centerRight, child: Padding( padding: EdgeInsets.only(right: 10, bottom: 12), - child: Text(aqiFormat.toString(), style: TextStyle(fontSize: MediaQuery.of(context).size.width * 0.11, fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.onSurface),), + child: Text(aqiFormat == 0.0000001 ? '--' : aqiFormat.toString(), style: TextStyle(fontSize: MediaQuery.of(context).size.width * 0.11, fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.onSurface),), ), ), AQISliderBar( @@ -523,17 +569,26 @@ List gridItems = itemOrder.map((i) { alignment: Alignment.bottomRight, child: Padding( padding: EdgeInsets.only(right: 10, bottom: 25), - child: Text(getAQIIndexType(aqiFormat, aqiUnit == 'European' ? true : false), style: TextStyle(fontSize: 17, color: Theme.of(context).colorScheme.onSurfaceVariant),), + child: Text(aqiFormat == 0.0000001 ? '--' : getAQIIndexType(aqiFormat, aqiUnit == 'European' ? true : false), style: TextStyle(fontSize: 17, color: Theme.of(context).colorScheme.onSurfaceVariant),), ), ), ], ), ), onTap: () { - widget.isFromHome ? Navigator.of(context).push( + if(widget.isFromHome){ + if(aqiFormat == 0.0000001){ + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Row(spacing: 10, children: [Icon(Symbols.error, color: Theme.of(context).colorScheme.onInverseSurface,), Text("No data available")],)) + ); + } else{ + Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ExtendWidget('aqi_widget'), fullscreenDialog: true), - ) : null; - }, + ); + } + } + }, ); case 7: @@ -558,7 +613,7 @@ List gridItems = itemOrder.map((i) { child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text("${double.parse(convertedPrecip.toStringAsFixed(2))}", style: TextStyle(fontSize: MediaQuery.of(context).size.width * 0.11, fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.onSurface),), + Text(widget.currentTotalPrec == 0.0000001 ? '--' : "${double.parse(convertedPrecip.toStringAsFixed(2))}", style: TextStyle(fontSize: MediaQuery.of(context).size.width * 0.11, fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.onSurface),), Padding( padding: EdgeInsets.only(top: 15), child: Text(precipitationUnit, style: TextStyle(fontSize: 20, color: Theme.of(context).colorScheme.secondary),), @@ -586,10 +641,19 @@ List gridItems = itemOrder.map((i) { ), ), onTap: () { - widget.isFromHome ? Navigator.of(context).push( + if(widget.isFromHome){ + if(widget.currentTotalPrec == 0.0000001){ + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Row(spacing: 10, children: [Icon(Symbols.error, color: Theme.of(context).colorScheme.onInverseSurface,), Text("No data available")],)) + ); + } else{ + Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ExtendWidget('precip_widget'), fullscreenDialog: true), - ) : null; - }, + ); + } + } + }, ); default: return const SizedBox(); @@ -1025,7 +1089,7 @@ String buildUVSvg(Color colorSurface, uvValue) { '''; - } + } return returnSvg;