From 72cfba953ee4b88e0ba08a4058dc4e433008a68d Mon Sep 17 00:00:00 2001 From: Pranshul Date: Fri, 29 Aug 2025 21:31:50 +0530 Subject: [PATCH] cleaned up settings page, added strings --- assets/translations/source.json | 8 +- lib/screens/daily_forecast.dart | 77 ++--- lib/screens/extended_widgets.dart | 88 +++++ lib/screens/home.dart | 78 ++--- .../setting_screens/appearance_screen.dart | 322 ++++++++++++++++++ lib/screens/settings.dart | 304 ++--------------- lib/services/fetch_data.dart | 20 +- lib/utils/theme_controller.dart | 42 ++- lib/utils/visual_utils.dart | 53 +++ lib/widgets/current_conditions_card.dart | 310 ++++++++++++++++- lib/widgets/top_summary_block.dart | 163 ++++----- lib/widgets/top_weather_card.dart | 12 + pubspec.lock | 16 +- pubspec.yaml | 2 +- .../.gradle/8.9/fileHashes/fileHashes.bin | Bin 28497 -> 28497 bytes .../.gradle/8.9/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../8.9/fileHashes/resourceHashesCache.bin | Bin 22271 -> 22339 bytes .../cxx/create_cxx_tasks_38_timing.txt | 22 ++ 18 files changed, 1027 insertions(+), 490 deletions(-) create mode 100644 lib/screens/setting_screens/appearance_screen.dart diff --git a/assets/translations/source.json b/assets/translations/source.json index 1ac59e033..cf45615c9 100644 --- a/assets/translations/source.json +++ b/assets/translations/source.json @@ -549,5 +549,11 @@ "system_font": "System", "use_ai_for_summary": "Use AI for summary", "use_ai_for_summary_sub": "GPT-4o Mini generates summaries of hourly, daily, and current weather, refreshing every 2 hours", - "restart_for_changes": "Restart to apply changes" + "restart_for_changes": "Restart to apply changes", + "appearance": "Appearance", + "appearance_sub": "Themes, animations, and app layout", + "background_updates_sub": "Widget updates, update interval, and scheduled updates", + "animations": "Animations", + "layout": "Layout", + "use_expressive_palette": "Use expressive palette" } diff --git a/lib/screens/daily_forecast.dart b/lib/screens/daily_forecast.dart index dcde61e38..dd915a3d5 100644 --- a/lib/screens/daily_forecast.dart +++ b/lib/screens/daily_forecast.dart @@ -218,44 +218,45 @@ class _DailyForecastPageState extends State { .toString())))) ? SizedBox( child: ConditionsWidgets( - selectedContainerBgIndex: Theme.of(context) - .colorScheme - .surfaceContainerLowest - .toARGB32(), - 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] ?? 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, - ), + selectedContainerBgIndex: Theme.of(context) + .colorScheme + .surfaceContainerLowest + .toARGB32(), + 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] ?? 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, + moonrise: weather['astronomy']['astronomy']['astro']['moonrise'], + moonset: weather['astronomy']['astronomy']['astro']['moonset'], + moonPhase: weather['astronomy']['astronomy']['astro']['moon_phase'], + cloudCover: current['cloud_cover'].toString()), ) : SizedBox( child: ConditionsWidgetsForecast( diff --git a/lib/screens/extended_widgets.dart b/lib/screens/extended_widgets.dart index 7ee5c40c4..97d6676ab 100644 --- a/lib/screens/extended_widgets.dart +++ b/lib/screens/extended_widgets.dart @@ -69,6 +69,10 @@ class _ExtendWidgetState extends State { child = buildPrecipExtended(); extendedTitle = 'precipitation'.tr(); iconData = Symbols.rainy_heavy; + } else if (widget.widgetType == 'moon_widget') { + child = buildMoonExtended(); + extendedTitle = 'moon'.tr(); + iconData = Symbols.nightlight; } else { child = const Center(child: Text('Unknown widget type')); extendedTitle = 'Error'; @@ -1216,6 +1220,7 @@ class _ExtendWidgetState extends State { .round() : windUnit == 'Kt' ? UnitConverter.kmhToKt(windSpeed) + .toStringAsFixed(1) : windSpeed.toStringAsFixed(1); Widget windArrow = Transform.rotate( @@ -2299,6 +2304,89 @@ class _ExtendWidgetState extends State { ]); }); } + + // MOON + + Widget buildMoonExtended() { + return FutureBuilder?>( + future: getWeatherWidgets(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } else if (!snapshot.hasData || snapshot.data == null) { + return Center(child: Text('no_data_available'.tr())); + } + + final data = snapshot.data!; + final weather = data['data']; + + return Column( + children: [ + Container( + clipBehavior: Clip.hardEdge, + height: 305, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainer, + borderRadius: BorderRadius.circular(18), + ), + padding: EdgeInsets.only(top: 12, bottom: 0), + margin: EdgeInsets.fromLTRB(12, 0, 12, 0), + child: Column( + children: [ + Container( + padding: EdgeInsets.fromLTRB(20, 10, 20, 0), + alignment: Alignment.centerLeft, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("current_conditions".tr(), + style: TextStyle( + fontSize: 20, + color: + Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.w500)), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + "", + style: TextStyle( + fontSize: 50, + color: + Theme.of(context).colorScheme.secondary, + ), + ), + Padding( + padding: EdgeInsets.only(bottom: 11, left: 8), + child: Text( + "", + style: TextStyle( + fontSize: 20, + color: Theme.of(context) + .colorScheme + .onSurfaceVariant, + ), + )) + ], + ), + ], + ), + ), + SizedBox( + height: 20, + ), + ], + ), + ), + SizedBox( + height: 20, + ), + ], + ); + }); + } } Widget buildAirQualityBox(String label, double value, String type) { diff --git a/lib/screens/home.dart b/lib/screens/home.dart index f967bf1a4..581b30f91 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -938,7 +938,6 @@ class _WeatherHomeState extends State { maybeUpdateWeatherAnimation(current); } }); - print("CODEEEEEEEEEEEEEEEEE: ${weatherCodeFroggy}"); } else { _isLoadingFroggy == true; _loadWeatherIconFroggy(weatherCodeFroggy, isDayFroggy, @@ -1145,46 +1144,43 @@ class _WeatherHomeState extends State { return SizedBox( // width: 380, child: ConditionsWidgets( - selectedContainerBgIndex: useFullMaterialScheme - ? Theme.of(context) - .colorScheme - .surfaceContainerLowest - .toARGB32() - : weatherContainerColors[selectedContainerBgIndex], - currentHumidity: - current['relative_humidity_2m'] ?? 0.0000001, - currentDewPoint: hourly['dew_point_2m'][getStartIndex( - weather['utc_offset_seconds'].toString(), - hourlyTime)] - .toDouble() ?? - 0.0000001, - currentSunrise: daily['sunrise'][0] ?? 0.0000001, - currentSunset: daily['sunset'][0] ?? 0.0000001, - currentPressure: current['pressure_msl'] ?? 0.0000001, - currentVisibility: hourly['visibility'][getStartIndex( - weather['utc_offset_seconds'].toString(), - hourlyTime)] ?? - 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: hourly['uv_index'][getStartIndex( - weather['utc_offset_seconds'].toString(), - hourlyTime)] ?? - 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: true, - ), + selectedContainerBgIndex: useFullMaterialScheme + ? Theme.of(context) + .colorScheme + .surfaceContainerLowest + .toARGB32() + : weatherContainerColors[selectedContainerBgIndex], + currentHumidity: + current['relative_humidity_2m'] ?? 0.0000001, + currentDewPoint: + hourly['dew_point_2m'][getStartIndex(weather['utc_offset_seconds'].toString(), hourlyTime)] + .toDouble() ?? + 0.0000001, + currentSunrise: daily['sunrise'][0] ?? 0.0000001, + currentSunset: daily['sunset'][0] ?? 0.0000001, + currentPressure: current['pressure_msl'] ?? 0.0000001, + currentVisibility: hourly['visibility'][getStartIndex( + weather['utc_offset_seconds'].toString(), + hourlyTime)] ?? + 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: hourly['uv_index'] + [getStartIndex(weather['utc_offset_seconds'].toString(), hourlyTime)] ?? + 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: true, + moonrise: weather['astronomy']['astronomy']['astro']['moonrise'], + moonset: weather['astronomy']['astronomy']['astro']['moonset'], + moonPhase: weather['astronomy']['astronomy']['astro']['moon_phase'], + cloudCover: current['cloud_cover'].toString()), ); case LayoutBlockType.pollen: diff --git a/lib/screens/setting_screens/appearance_screen.dart b/lib/screens/setting_screens/appearance_screen.dart new file mode 100644 index 000000000..af7f1f95c --- /dev/null +++ b/lib/screens/setting_screens/appearance_screen.dart @@ -0,0 +1,322 @@ +import 'package:flutter/material.dart'; +import 'package:weather_master_app/utils/preferences_helper.dart'; +import '../home_location.dart'; +import 'package:settings_tiles/settings_tiles.dart'; +import '../settings_unit.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; +import '../../notifiers/unit_settings_notifier.dart'; +import 'package:provider/provider.dart'; +import '../../utils/theme_controller.dart'; +import '../languages_page.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:restart_app/restart_app.dart'; +import '../../utils/snack_util.dart'; +import '../about_page.dart'; +import '../meteo_models.dart'; +import '../edit_layout_page.dart'; +import '../../services/data_backup_service.dart'; +import '../../screens/background_updates.dart'; + +class AppearanceScreen extends StatefulWidget { + const AppearanceScreen({super.key}); + + @override + State createState() => _AppearanceScreenState(); +} + +class _AppearanceScreenState extends State { + bool _showTile = PreferencesHelper.getBool("usingCustomSeed") ?? false; + bool _useCustomTile = + PreferencesHelper.getBool("DynamicColors") == true ? false : true; + @override + Widget build(BuildContext context) { + final themeController = Provider.of(context); + final currentMode = themeController.themeMode; + + final isSupported = themeController.isDynamicColorSupported; + + final optionsTheme = { + "Auto": "theme_auto".tr(), + "Dark": "theme_dark".tr(), + "Light": "theme_light".tr(), + }; + return Scaffold( + backgroundColor: Theme.of(context).colorScheme.surface, + body: CustomScrollView(slivers: [ + SliverAppBar.large( + title: Text('Appearance'), + titleSpacing: 0, + backgroundColor: Theme.of(context).colorScheme.surface, + scrolledUnderElevation: 1, + ), + SliverToBoxAdapter( + child: Column(children: [ + SettingSection( + styleTile: true, + title: SettingSectionTitle("app_looks".tr(), noPadding: true), + tiles: [ + SettingSingleOptionTile( + icon: Icon(Symbols.routine), + title: Text('app_theme_dark_light'.tr()), + dialogTitle: 'app_theme_dark_light'.tr(), + value: SettingTileValue( + optionsTheme[currentMode == ThemeMode.light + ? "Light" + : currentMode == ThemeMode.system + ? "Auto" + : "Dark"]!, + ), + options: optionsTheme.values.toList(), + initialOption: optionsTheme[currentMode == ThemeMode.light + ? "Light" + : currentMode == ThemeMode.system + ? "Auto" + : "Dark"]!, + onSubmitted: (value) { + setState(() { + final selectedKey = optionsTheme.entries + .firstWhere((e) => e.value == value) + .key; + PreferencesHelper.setString("AppTheme", selectedKey); + themeController.setThemeMode( + selectedKey == "Dark" + ? ThemeMode.dark + : selectedKey == "Auto" + ? ThemeMode.system + : ThemeMode.light, + ); + }); + }, + ), + SettingSwitchTile( + enabled: _useCustomTile, + icon: Icon(Symbols.colorize, fill: 1, weight: 500), + title: Text("use_custom_color".tr()), + toggled: + PreferencesHelper.getBool("usingCustomSeed") ?? false, + onChanged: (value) { + setState(() { + PreferencesHelper.setBool("usingCustomSeed", value); + if (value == true) { + Provider.of( + context, + listen: false, + ).setSeedColor( + PreferencesHelper.getColor( + "CustomMaterialColor", + ) ?? + Colors.blue, + ); + } else { + Provider.of( + context, + listen: false, + ).setSeedColor( + PreferencesHelper.getColor("weatherThemeColor") ?? + Colors.blue, + ); + } + }); + _showTile = value; + }, + ), + SettingColorTile( + enabled: _showTile, + icon: Icon(Symbols.colors, fill: 1, weight: 500), + title: Text('primary_color'.tr()), + description: Text('primary_color_sub'.tr()), + dialogTitle: 'Color', + initialColor: + PreferencesHelper.getColor("CustomMaterialColor") ?? + Colors.blue, + colorPickers: [ColorPickerType.primary], + onSubmitted: (value) { + setState(() { + PreferencesHelper.setColor( + "CustomMaterialColor", + value, + ); + Provider.of( + context, + listen: false, + ).setSeedColor(value); + }); + }, + ), + SettingSwitchTile( + enabled: isSupported + ? _showTile + ? false + : true + : false, + icon: Icon(Symbols.wallpaper, fill: 1, weight: 500), + title: Text("dynamic_colors".tr()), + description: Text( + "${"dynamic_colors_sub".tr()} ${isSupported ? "" : "(Android 12+)"}", + ), + toggled: + PreferencesHelper.getBool("DynamicColors") ?? false, + onChanged: (value) async { + final themeController = context.read(); + + PreferencesHelper.setBool("DynamicColors", value); + + if (value) { + await themeController.loadDynamicColors(); + } else { + Provider.of( + context, + listen: false, + ).setSeedColor( + PreferencesHelper.getColor("weatherThemeColor") ?? + Colors.blue, + ); + } + setState(() { + if (value) { + _useCustomTile = false; + } else { + _useCustomTile = true; + } + }); + }, + ), + SettingSwitchTile( + icon: Icon(Symbols.palette, fill: 1, weight: 500), + title: Text("material_scheme_only".tr()), + description: Text('material_scheme_only_sub'.tr()), + toggled: PreferencesHelper.getBool("OnlyMaterialScheme") ?? + false, + onChanged: (value) { + PreferencesHelper.setBool("OnlyMaterialScheme", value); + setState(() { + SnackUtil.showSnackBar( + context: context, + message: "Restarting app", + ); + Future.delayed(Duration(seconds: 2), () { + Restart.restartApp(); + }); + }); + }, + ), + SettingSwitchTile( + icon: Icon(Symbols.cards, fill: 1, weight: 500), + title: Text('make_the_card_background_darker'.tr()), + toggled: PreferencesHelper.getBool( + "useDarkerCardBackground", + ) ?? + false, + onChanged: (value) { + context + .read() + .updateUseDarkerBackground(value); + setState(() {}); + }, + ), + ]), + SizedBox( + height: 10, + ), + SettingSection( + styleTile: true, + title: SettingSectionTitle("Animations", noPadding: true), + tiles: [ + SettingSwitchTile( + icon: Icon(Symbols.thermometer_add, fill: 1, weight: 500), + title: Text("temperature_animation".tr()), + description: Text("temp_animation_sub_pref".tr()), + toggled: + PreferencesHelper.getBool("useTempAnimation") ?? true, + onChanged: (value) { + setState(() { + PreferencesHelper.setBool("useTempAnimation", value); + }); + }, + ), + SettingSwitchTile( + icon: Icon( + Symbols.assistant_navigation, + fill: 1, + weight: 500, + ), + title: Text("use_device_compass".tr()), + description: Text("use_device_compass_sub".tr()), + toggled: + PreferencesHelper.getBool("useDeviceCompass") ?? false, + onChanged: (value) { + context + .read() + .updateUseDeviceCompass(value); + setState(() {}); + }, + ), + SettingSwitchTile( + icon: Icon(Symbols.animation, fill: 1, weight: 500), + title: Text("background_card_animation".tr()), + toggled: PreferencesHelper.getBool( + "CardBackgroundAnimations", + ) ?? + true, + onChanged: (value) { + context + .read() + .updateuseCardBackgroundAnimations(value); + setState(() {}); + }, + ), + ], + ), + SizedBox( + height: 10, + ), + SettingSection( + styleTile: true, + title: SettingSectionTitle("Layout", noPadding: true), + tiles: [ + SettingSwitchTile( + icon: Icon( + Symbols.responsive_layout, + fill: 1, + weight: 500, + ), + title: Text("froggy_layout".tr()), + toggled: PreferencesHelper.getBool("showFroggy") ?? true, + onChanged: (value) { + context.read().updateShowFroggy( + value, + ); + setState(() {}); + }, + ), + SettingSwitchTile( + icon: Icon(Symbols.taunt, fill: 1, weight: 500), + title: Text('froggy_insights'.tr()), + description: Text('froggy_insights_sub'.tr()), + toggled: + PreferencesHelper.getBool("useFroggyInsights") ?? false, + onChanged: (value) { + setState(() { + PreferencesHelper.setBool("useFroggyInsights", value); + }); + }, + ), + SettingActionTile( + icon: Icon(Symbols.view_agenda, fill: 1, weight: 500), + title: Text('edit_layout'.tr()), + trailing: Icon(Icons.chevron_right), + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => EditLayoutPage(), + fullscreenDialog: true, + ), + ); + }, + ), + ]), + SizedBox(height: 200), + ])) + ])); + } +} diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 6e2293e42..42ff12ecf 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -16,6 +16,7 @@ import 'meteo_models.dart'; import 'edit_layout_page.dart'; import '../services/data_backup_service.dart'; import '../screens/background_updates.dart'; +import 'setting_screens/appearance_screen.dart'; class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @@ -25,9 +26,6 @@ class SettingsScreen extends StatefulWidget { } class _SettingsScreenState extends State { - bool _showTile = PreferencesHelper.getBool("usingCustomSeed") ?? false; - bool _useCustomTile = - PreferencesHelper.getBool("DynamicColors") == true ? false : true; String? city; String? country; @@ -47,17 +45,6 @@ class _SettingsScreenState extends State { @override Widget build(BuildContext context) { - final themeController = Provider.of(context); - final currentMode = themeController.themeMode; - - final isSupported = themeController.isDynamicColorSupported; - - final optionsTheme = { - "Auto": "theme_auto".tr(), - "Dark": "theme_dark".tr(), - "Light": "theme_light".tr(), - }; - return Scaffold( backgroundColor: Theme.of(context).colorScheme.surface, body: CustomScrollView( @@ -73,214 +60,29 @@ class _SettingsScreenState extends State { children: [ SettingSection( styleTile: true, - title: SettingSectionTitle("app_looks".tr(), noPadding: true), tiles: [ - SettingSingleOptionTile( - icon: Icon(Symbols.routine), - title: Text('app_theme_dark_light'.tr()), - dialogTitle: 'app_theme_dark_light'.tr(), - value: SettingTileValue( - optionsTheme[currentMode == ThemeMode.light - ? "Light" - : currentMode == ThemeMode.system - ? "Auto" - : "Dark"]!, - ), - options: optionsTheme.values.toList(), - initialOption: optionsTheme[currentMode == ThemeMode.light - ? "Light" - : currentMode == ThemeMode.system - ? "Auto" - : "Dark"]!, - onSubmitted: (value) { - setState(() { - final selectedKey = optionsTheme.entries - .firstWhere((e) => e.value == value) - .key; - PreferencesHelper.setString("AppTheme", selectedKey); - themeController.setThemeMode( - selectedKey == "Dark" - ? ThemeMode.dark - : selectedKey == "Auto" - ? ThemeMode.system - : ThemeMode.light, - ); - }); - }, - ), - SettingSwitchTile( - icon: Icon(Symbols.thermometer_add, fill: 1, weight: 500), - title: Text("temperature_animation".tr()), - description: Text("temp_animation_sub_pref".tr()), - toggled: - PreferencesHelper.getBool("useTempAnimation") ?? true, - onChanged: (value) { - setState(() { - PreferencesHelper.setBool("useTempAnimation", value); - }); - }, - ), - SettingSwitchTile( - enabled: _useCustomTile, - icon: Icon(Symbols.colorize, fill: 1, weight: 500), - title: Text("use_custom_color".tr()), - toggled: - PreferencesHelper.getBool("usingCustomSeed") ?? false, - onChanged: (value) { - setState(() { - PreferencesHelper.setBool("usingCustomSeed", value); - if (value == true) { - Provider.of( - context, - listen: false, - ).setSeedColor( - PreferencesHelper.getColor( - "CustomMaterialColor", - ) ?? - Colors.blue, - ); - } else { - Provider.of( - context, - listen: false, - ).setSeedColor( - PreferencesHelper.getColor("weatherThemeColor") ?? - Colors.blue, - ); - } - }); - _showTile = value; - }, - ), - SettingColorTile( - enabled: _showTile, - icon: Icon(Symbols.colors, fill: 1, weight: 500), - title: Text('primary_color'.tr()), - description: Text('primary_color_sub'.tr()), - dialogTitle: 'Color', - initialColor: - PreferencesHelper.getColor("CustomMaterialColor") ?? - Colors.blue, - colorPickers: [ColorPickerType.primary], - onSubmitted: (value) { - setState(() { - PreferencesHelper.setColor( - "CustomMaterialColor", - value, - ); - Provider.of( - context, - listen: false, - ).setSeedColor(value); - }); - }, - ), - SettingSwitchTile( - enabled: isSupported - ? _showTile - ? false - : true - : false, - icon: Icon(Symbols.wallpaper, fill: 1, weight: 500), - title: Text("dynamic_colors".tr()), - description: Text( - "${"dynamic_colors_sub".tr()} ${isSupported ? "" : "(Android 12+)"}", - ), - toggled: - PreferencesHelper.getBool("DynamicColors") ?? false, - onChanged: (value) async { - final themeController = context.read(); - - PreferencesHelper.setBool("DynamicColors", value); - - if (value) { - await themeController.loadDynamicColors(); - } else { - Provider.of( - context, - listen: false, - ).setSeedColor( - PreferencesHelper.getColor("weatherThemeColor") ?? - Colors.blue, - ); - } - setState(() { - if (value) { - _useCustomTile = false; - } else { - _useCustomTile = true; - } - }); - }, - ), - SettingSwitchTile( - icon: Icon(Symbols.palette, fill: 1, weight: 500), - title: Text("material_scheme_only".tr()), - description: Text('material_scheme_only_sub'.tr()), - toggled: - PreferencesHelper.getBool("OnlyMaterialScheme") ?? - false, - onChanged: (value) { - PreferencesHelper.setBool("OnlyMaterialScheme", value); - setState(() { - SnackUtil.showSnackBar( - context: context, - message: "Restarting app", - ); - Future.delayed(Duration(seconds: 2), () { - Restart.restartApp(); - }); - }); - }, - ), - SettingSwitchTile( - icon: Icon( - Symbols.responsive_layout, - fill: 1, - weight: 500, - ), - title: Text("froggy_layout".tr()), - toggled: PreferencesHelper.getBool("showFroggy") ?? true, - onChanged: (value) { - context.read().updateShowFroggy( - value, - ); - setState(() {}); - }, - ), - SettingSwitchTile( - icon: Icon(Symbols.cards, fill: 1, weight: 500), - title: Text('make_the_card_background_darker'.tr()), - toggled: PreferencesHelper.getBool( - "useDarkerCardBackground", - ) ?? - false, - onChanged: (value) { - context - .read() - .updateUseDarkerBackground(value); - setState(() {}); - }, - ), SettingActionTile( - icon: Icon(Symbols.view_agenda, fill: 1, weight: 500), - title: Text('edit_layout'.tr()), - trailing: Icon(Icons.chevron_right), - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => EditLayoutPage(), - fullscreenDialog: true, - ), - ); - }, - ), + icon: Icon( + Symbols.format_paint, + fill: 1, + weight: 500, + ), + title: Text("Appearance"), + description: Text("Themes, animations, and app layout"), + trailing: Icon(Icons.chevron_right), + onTap: () async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => const AppearanceScreen(), + ), + ); + }) ], ), - SizedBox(height: 10), + SizedBox(height: 16), SettingSection( styleTile: true, - title: SettingSectionTitle('weather'.tr(), noPadding: true), + // title: SettingSectionTitle('weather'.tr(), noPadding: true), tiles: [ SettingActionTile( icon: Icon(Symbols.home_pin, fill: 1, weight: 500), @@ -324,6 +126,8 @@ class _SettingsScreenState extends State { SettingActionTile( icon: Icon(Symbols.update, fill: 1, weight: 500), title: Text('background_updates'.tr()), + description: Text( + "Widget updates, update interval, and scheduled updates"), trailing: Icon(Icons.chevron_right), onTap: () { Navigator.of(context).push( @@ -333,68 +137,6 @@ class _SettingsScreenState extends State { ); }, ), - SettingSwitchTile( - icon: Icon( - Symbols.text_ad, - fill: 1, - weight: 500, - ), - title: Text("Use AI for summary"), - description: Text( - "GPT-4o Mini generates summaries of hourly, daily, and current weather, refreshing every 2 hours"), - toggled: - PreferencesHelper.getBool("useAISummary") ?? false, - onChanged: (value) { - PreferencesHelper.setBool("useAISummary", value); - SnackUtil.showSnackBar( - context: context, - message: "Restart to apply changes"); - setState(() {}); - }, - ), - SettingSwitchTile( - icon: Icon( - Symbols.assistant_navigation, - fill: 1, - weight: 500, - ), - title: Text("use_device_compass".tr()), - description: Text("use_device_compass_sub".tr()), - toggled: PreferencesHelper.getBool("useDeviceCompass") ?? - false, - onChanged: (value) { - context - .read() - .updateUseDeviceCompass(value); - setState(() {}); - }, - ), - SettingSwitchTile( - icon: Icon(Symbols.taunt, fill: 1, weight: 500), - title: Text('froggy_insights'.tr()), - description: Text('froggy_insights_sub'.tr()), - toggled: PreferencesHelper.getBool("useFroggyInsights") ?? - false, - onChanged: (value) { - setState(() { - PreferencesHelper.setBool("useFroggyInsights", value); - }); - }, - ), - SettingSwitchTile( - icon: Icon(Symbols.animation, fill: 1, weight: 500), - title: Text("background_card_animation".tr()), - toggled: PreferencesHelper.getBool( - "CardBackgroundAnimations", - ) ?? - true, - onChanged: (value) { - context - .read() - .updateuseCardBackgroundAnimations(value); - setState(() {}); - }, - ), SettingActionTile( icon: Icon( Symbols.nest_farsight_weather, @@ -414,13 +156,9 @@ class _SettingsScreenState extends State { ), ], ), - SizedBox(height: 10), + SizedBox(height: 16), SettingSection( styleTile: true, - title: SettingSectionTitle( - 'additional'.tr(), - noPadding: true, - ), tiles: [ SettingActionTile( icon: Icon(Symbols.language, fill: 1, weight: 500), diff --git a/lib/services/fetch_data.dart b/lib/services/fetch_data.dart index 7928cd6bc..0e55912f5 100644 --- a/lib/services/fetch_data.dart +++ b/lib/services/fetch_data.dart @@ -55,10 +55,14 @@ class WeatherService { 'forecast_hours': '1', }); - // Prepare list of HTTP requests (always include weather and air quality) + final astronomyUri = Uri.parse( + 'https://api.weatherapi.com/v1/astronomy.json?key=${dotenv.env['API_KEY_WEATHERAPI']!.toString()}&q=$lat,$lon'); + + // Prepare list of HTTP requests final requests = >[ http.get(uri), http.get(airQualityUri), + http.get(astronomyUri), ]; final responses = await Future.wait(requests); @@ -67,6 +71,8 @@ class WeatherService { final weatherData = json.decode(responses[0].body) as Map; final airQualityData = json.decode(responses[1].body) as Map; + final astronomyData = + json.decode(responses[2].body) as Map; // Check if we need fallback data for missing fields Map finalWeatherData = weatherData; @@ -79,11 +85,13 @@ class WeatherService { finalWeatherData['hourly'] = sanitizeHourly(finalWeatherData['hourly']); finalWeatherData['daily'] = sanitizeDaily(finalWeatherData['daily']); - if (finalWeatherData['error'] == true || airQualityData['error'] == true) { + if (finalWeatherData['error'] == true || + airQualityData['error'] == true || + astronomyData['error'] == true) { final reason = finalWeatherData['reason'] ?? airQualityData['reason'] ?? + astronomyData['reason'] ?? 'Unknown error'; - if (context != null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -107,6 +115,7 @@ class WeatherService { final combinedDataForView = { ...finalWeatherData, 'air_quality': airQualityData, + 'astronomy': astronomyData, }; final nowForView = DateTime.now().toIso8601String(); @@ -129,7 +138,9 @@ class WeatherService { if (json.encode(cachedData['current']) == json.encode(finalWeatherData['current']) && json.encode(cachedData['air_quality'] ?? {}) == - json.encode(airQualityData)) { + json.encode(airQualityData) && + json.encode(cachedData['astronomy'] ?? {}) == + json.encode(astronomyData)) { log("Hive: No update needed for $key"); return { 'data': cachedData, @@ -143,6 +154,7 @@ class WeatherService { final combinedData = { ...finalWeatherData, 'air_quality': airQualityData, + 'astronomy': astronomyData, }; final wrappedData = { diff --git a/lib/utils/theme_controller.dart b/lib/utils/theme_controller.dart index dac6845c6..986d245e8 100644 --- a/lib/utils/theme_controller.dart +++ b/lib/utils/theme_controller.dart @@ -4,33 +4,38 @@ import '../utils/preferences_helper.dart'; import 'package:dynamic_color/dynamic_color.dart'; class ThemeController extends ChangeNotifier { - Color? _seedColor; + Color? _seedColor; CorePalette? _corePalette; ThemeMode _themeMode = PreferencesHelper.getString("AppTheme") == "Light" - ? ThemeMode.light : PreferencesHelper.getString("AppTheme") == "Auto" ? ThemeMode.system - : ThemeMode.dark; + ? ThemeMode.light + : PreferencesHelper.getString("AppTheme") == "Auto" + ? ThemeMode.system + : ThemeMode.dark; bool isCustom = PreferencesHelper.getBool("usingCustomSeed") ?? false; bool _isUsingDynamicColor = false; bool _isDynamicColorSupported = false; -bool get isDynamicColorSupported => _isDynamicColorSupported; + bool get isDynamicColorSupported => _isDynamicColorSupported; -Future checkDynamicColorSupport() async { - _isDynamicColorSupported = (await DynamicColorPlugin.getCorePalette()) != null; -} + Future checkDynamicColorSupport() async { + _isDynamicColorSupported = + (await DynamicColorPlugin.getCorePalette()) != null; + } ThemeController(); - Future initialize({Color fallbackColor = const Color(0xFF012E7C)}) async { + Future initialize( + {Color fallbackColor = const Color(0xFF012E7C)}) async { bool useDynamicColors = PreferencesHelper.getBool("DynamicColors") ?? false; if (useDynamicColors) { await loadDynamicColors(); } else if (isCustom) { - setSeedColor(PreferencesHelper.getColor("CustomMaterialColor") ?? Colors.blue); + setSeedColor( + PreferencesHelper.getColor("CustomMaterialColor") ?? Colors.blue); } else { setSeedColor(fallbackColor); } @@ -48,12 +53,11 @@ Future checkDynamicColorSupport() async { notifyListeners(); } -void setSeedColorSilently(Color newColor) { - _seedColor = newColor; - _corePalette = CorePalette.of(newColor.value); - _isUsingDynamicColor = false; -} - + void setSeedColorSilently(Color newColor) { + _seedColor = newColor; + _corePalette = CorePalette.of(newColor.value); + _isUsingDynamicColor = false; + } void setThemeMode(ThemeMode mode) { _themeMode = mode; @@ -75,7 +79,13 @@ void setSeedColorSilently(Color newColor) { final corePalette = await DynamicColorPlugin.getCorePalette(); if (corePalette != null) { _corePalette = corePalette; - _seedColor = Color(corePalette.primary.get(40)); + + final brightness = currentBrightness; + + int primaryTone = brightness == Brightness.light ? 40 : 80; + int secondaryTone = brightness == Brightness.light ? 40 : 80; + + _seedColor = Color(corePalette.primary.get(primaryTone)); _isUsingDynamicColor = true; notifyListeners(); } diff --git a/lib/utils/visual_utils.dart b/lib/utils/visual_utils.dart index dab31f69e..c54d78da6 100644 --- a/lib/utils/visual_utils.dart +++ b/lib/utils/visual_utils.dart @@ -227,6 +227,19 @@ String buildHumidity(Color color, int humidityValue) { return returnSvg; } +// Build cloud svg + +String buildCloudCoverSvg(Color color) { + final hexColor = + '#${color.value.toRadixString(16).padLeft(8, '0').substring(2)}'; + + return ''' + + + + '''; +} + // Return SVG representing the sun's path from sunrise to sunset String buildSunPathWithIcon({ @@ -294,3 +307,43 @@ String buildNowHourSvg(Color colorPrimary) { return returnSvg; } + +// Moon path + +String buildMoonPathWithIcon({ + required Color pathColor, + required double percent, + required Color outLineColor, + bool showMoon = true, + int moonIllumination = 100, +}) { + final hexColor = + '#${pathColor.value.toRadixString(16).padLeft(8, '0').substring(2)}'; + final hexColoroutLine = + '#${outLineColor.value.toRadixString(16).padLeft(8, '0').substring(2)}'; + + const svgWidth = 176.0; + + final shift = -0.0; + final stretch = 1.0; + final t = ((percent + shift) * stretch).clamp(0.0, 1.0); + final moonX = t * svgWidth; + final moonY = 70 - 70 * sin(pi * t); + final moonYEndStart = 60 - 70 * sin(pi * t); + + final moonFill = showMoon + ? 'radialGradient(circle at center, white ${moonIllumination}%, transparent 100%)' + : 'transparent'; + + return ''' + + + + + + +'''; +} diff --git a/lib/widgets/current_conditions_card.dart b/lib/widgets/current_conditions_card.dart index 5376ef16b..09c8c6692 100644 --- a/lib/widgets/current_conditions_card.dart +++ b/lib/widgets/current_conditions_card.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:weather_master_app/utils/preferences_helper.dart'; import '../notifiers/unit_settings_notifier.dart'; import '../utils/unit_converter.dart'; import 'package:provider/provider.dart'; @@ -16,6 +17,7 @@ import '../screens/extended_widgets.dart'; import '../helper/locale_helper.dart'; import '../utils/visual_utils.dart'; import 'package:animations/animations.dart'; +import 'package:moon_phase/moon_widget.dart'; class ConditionsWidgets extends StatefulWidget { final int selectedContainerBgIndex; @@ -35,6 +37,10 @@ class ConditionsWidgets extends StatefulWidget { final double currentTotalPrec; final double currentDayLength; final bool isFromHome; + final String moonrise; + final String moonset; + final String moonPhase; + final String cloudCover; const ConditionsWidgets( {super.key, @@ -54,7 +60,11 @@ class ConditionsWidgets extends StatefulWidget { required this.currentAQIEURO, required this.currentTotalPrec, required this.currentDayLength, - required this.isFromHome}); + required this.isFromHome, + required this.moonrise, + required this.moonset, + required this.moonPhase, + required this.cloudCover}); @override State createState() => _ConditionsWidgetsState(); @@ -64,11 +74,12 @@ class _ConditionsWidgetsState extends State { List itemOrder = []; final String orderPrefsKey = 'tile_order'; - + bool _initialized = false; @override void initState() { super.initState(); _loadTileOrder(); + _initializePreferences(); } Future _loadTileOrder() async { @@ -80,13 +91,34 @@ class _ConditionsWidgetsState extends State { itemOrder = savedList.map(int.parse).toList(); }); } else { - // Default order setState(() { - itemOrder = List.generate(8, (index) => index); + itemOrder = List.generate(10, (index) => index); }); } } + Future _initializePreferences() async { + if (_initialized) return; + _initialized = true; + + final prefs = await SharedPreferences.getInstance(); + List? savedList = prefs.getStringList(orderPrefsKey); + + if (savedList != null) { + List intList = savedList.map(int.parse).toList(); + List zeroToSeven = List.generate(8, (i) => i); + + if (intList.toSet().containsAll(zeroToSeven) && + intList.every((e) => e >= 0 && e <= 7)) { + await prefs.remove(orderPrefsKey); + } + } + + List newList = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + await prefs.setStringList(orderPrefsKey, newList); + print('New list saved'); + } + @override Widget build(BuildContext context) { // return const Placeholder(); @@ -109,6 +141,8 @@ class _ConditionsWidgetsState extends State { DateTime now = utcNow.add(Duration(seconds: offsetSeconds)); DateTime sunrise = DateTime.parse(widget.currentSunrise); DateTime sunset = DateTime.parse(widget.currentSunset); + DateTime moonrise = DateFormat.jm().parse(widget.moonrise); + DateTime moonset = DateFormat.jm().parse(widget.moonset); now = DateTime( now.year, @@ -131,6 +165,12 @@ class _ConditionsWidgetsState extends State { ? DateFormat.Hm().format(sunset) : DateFormat.jm().format(sunset); + final moonriseFormat = timeUnit == '24 hr' + ? DateFormat.Hm().format(moonrise) + : DateFormat.jm().format(moonrise); + final moonsetFormat = timeUnit == '24 hr' + ? DateFormat.Hm().format(moonset) + : DateFormat.jm().format(moonset); final pressureUnit = context.watch().pressureUnit; final precipitationUnit = context.watch().precipitationUnit; @@ -153,6 +193,18 @@ class _ConditionsWidgetsState extends State { ? UnitConverter.mToMiles(widget.currentVisibility.toDouble()) : UnitConverter.mToKm(widget.currentVisibility.toDouble()); + final windUnit = context.watch().windUnit; + + final formattedWindSpeed = windUnit == 'Mph' + ? UnitConverter.kmhToMph(widget.currentWindSpeed) + : windUnit == 'M/s' + ? UnitConverter.kmhToMs(widget.currentWindSpeed) + : windUnit == 'Bft' + ? UnitConverter.kmhToBeaufort(widget.currentWindSpeed) + : windUnit == 'Kt' + ? UnitConverter.kmhToKt(widget.currentWindSpeed) + : widget.currentWindSpeed; + if (sunset.isBefore(sunrise)) { sunset = sunset.add(Duration(days: 1)); } @@ -166,6 +218,21 @@ class _ConditionsWidgetsState extends State { final colorTheme = Theme.of(context).colorScheme; + moonrise = + DateTime(now.year, now.month, now.day, moonrise.hour, moonrise.minute); + moonset = + DateTime(now.year, now.month, now.day, moonset.hour, moonset.minute); + + if (moonset.isBefore(moonrise)) { + moonset = moonset.add(Duration(days: 1)); + } + + double moonPercent = ((now.difference(moonrise).inSeconds) / + (moonset.difference(moonrise).inSeconds)) + .clamp(0, 1); + + // int moonIllumination = int.parse(widget.moonPhase); + List gridItems = itemOrder.map((i) { switch (i) { case 0: @@ -689,7 +756,7 @@ class _ConditionsWidgetsState extends State { child: Text( widget.currentWindSpeed == 0.0000001 ? '--' - : getWindSpeedType(widget.currentWindSpeed), + : '${windUnit == 'M/s' ? formattedWindSpeed.toStringAsFixed(1) : formattedWindSpeed.round()} ${localizeWindUnit(windUnit, context.locale)}', style: TextStyle( color: Theme.of(context) .colorScheme @@ -1071,6 +1138,239 @@ class _ConditionsWidgetsState extends State { ); }, ); + case 8: + return OpenContainer( + transitionType: ContainerTransitionType.fadeThrough, + closedElevation: 0, + closedShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + openShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + openElevation: 0, + transitionDuration: Duration(milliseconds: 500), + closedColor: Color(widget.selectedContainerBgIndex), + openColor: colorTheme.surface, + openBuilder: (context, _) { + return ExtendWidget('moon_widget'); + }, + closedBuilder: (context, openContainer) { + return GestureDetector( + child: Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Color(widget.selectedContainerBgIndex), + borderRadius: BorderRadius.circular(20), + ), + child: Stack( + children: [ + ListTile( + leading: MoonWidget( + date: now, + resolution: 128, + size: 22, + moonColor: Colors.amber, + earthshineColor: Colors.blueGrey.shade900, + ), + horizontalTitleGap: 5, + contentPadding: EdgeInsets.only(left: 10, bottom: 0), + title: Text("moon".tr(), + style: TextStyle( + color: Theme.of(context).brightness == + Brightness.dark + ? Colors.white + : Colors.black), + maxLines: 1, + overflow: TextOverflow.ellipsis), + ), + Positioned( + bottom: -1, + left: 0, + right: 0, + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.12, + child: SvgPicture.string( + clipBehavior: Clip.none, + buildMoonPathWithIcon( + pathColor: Theme.of(context) + .colorScheme + .secondaryContainer, + percent: moonPercent, + outLineColor: + Theme.of(context).colorScheme.outline, + ), + allowDrawingOutsideViewBox: true, + fit: BoxFit.fill, + ), + ), + ), + Positioned( + bottom: 0, + right: 0, + left: -1, + child: Container( + height: 65, + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: Theme.of(context).colorScheme.outline, + width: 1.5, + ), + ), + color: const Color.fromRGBO(0, 0, 0, 0.5), + // borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)) + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + spacing: 5, + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Symbols.vertical_align_top, + weight: 500, + size: 17, + color: Colors.white, + ), + Text(moonriseFormat, + style: TextStyle( + color: Colors.white, + fontSize: 14, + )) + ], + ), + Row( + spacing: 5, + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Symbols.vertical_align_bottom, + weight: 500, + size: 17, + color: Colors.white, + ), + Text(moonsetFormat, + style: TextStyle( + color: Colors.white, fontSize: 14)), + ], + ), + SizedBox( + height: 10, + ) + ], + )), + ], + ), + ), + onTap: () { + widget.isFromHome ? openContainer() : null; + }, + ); + }, + ); + case 9: + return OpenContainer( + transitionType: ContainerTransitionType.fadeThrough, + closedElevation: 0, + closedShape: const CircleBorder(), + openShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + openElevation: 0, + transitionDuration: Duration(milliseconds: 500), + closedColor: Color(widget.selectedContainerBgIndex), + openColor: colorTheme.surface, + openBuilder: (context, _) { + return SizedBox.shrink(); + }, + closedBuilder: (context, openContainer) { + return GestureDetector( + child: ClipRRect( + borderRadius: BorderRadius.circular(999), + child: Container( + height: 160, + decoration: BoxDecoration( + color: Color(widget.selectedContainerBgIndex), + borderRadius: BorderRadius.circular(999), + ), + child: Stack( + children: [ + AspectRatio( + aspectRatio: 1, + child: SvgPicture.string( + buildCloudCoverSvg(Theme.of(context) + .colorScheme + .tertiaryContainer), + fit: BoxFit.contain, + ), + ), + headerWidgetConditions( + headerText: "cloudiness".tr(), + headerIcon: Symbols.cloud, + ), + Align( + alignment: Alignment.center, + child: Text( + widget.currentVisibility == 0.0000001 + ? '--' + : widget.cloudCover, + style: TextStyle( + color: Theme.of(context) + .colorScheme + .onTertiaryContainer, + fontSize: + MediaQuery.of(context).size.width * 0.1, + ), + ), + ), + Padding( + padding: EdgeInsets.only(bottom: 30), + child: Align( + alignment: Alignment.bottomCenter, + child: Text( + "%", + style: TextStyle( + color: + Theme.of(context).colorScheme.onSurface, + fontSize: 20, + ), + ), + ), + ), + ], + ), + ), + ), + onTap: () { + 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 { + openContainer(); + } + } + }); + }); default: return const SizedBox(); } diff --git a/lib/widgets/top_summary_block.dart b/lib/widgets/top_summary_block.dart index 5afbff0bb..039c882fa 100644 --- a/lib/widgets/top_summary_block.dart +++ b/lib/widgets/top_summary_block.dart @@ -334,8 +334,7 @@ class _SummaryCardState extends State { Text( _headline ?? '', style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - ), + color: Theme.of(context).colorScheme.onSurface, fontSize: 14.5), textAlign: TextAlign.left, ), // ), @@ -537,32 +536,10 @@ class _SummaryCardState extends State { isSummaryLoaded = true; } - final windUnit = context.watch().windUnit; + // final windUnit = context.watch().windUnit; - final gustRaw = widget.currentData['wind_gusts_10m']; - final gustValue = (gustRaw is num) ? gustRaw.toDouble() : 0.000000001; - - final formattedWindGust = windUnit == 'Mph' - ? UnitConverter.kmhToMph(gustValue) - : windUnit == 'M/s' - ? UnitConverter.kmhToMs(gustValue) - : windUnit == 'Bft' - ? UnitConverter.kmhToBeaufort(gustValue) - : windUnit == 'Kt' - ? UnitConverter.kmhToKt(gustValue) - : gustValue; - - final formattedWindSpeed = windUnit == 'Mph' - ? UnitConverter.kmhToMph(widget.currentData['wind_speed_10m']) - : windUnit == 'M/s' - ? UnitConverter.kmhToMs(widget.currentData['wind_speed_10m']) - : windUnit == 'Bft' - ? UnitConverter.kmhToBeaufort( - widget.currentData['wind_speed_10m']) - : windUnit == 'Kt' - ? UnitConverter.kmhToKt( - widget.currentData['wind_speed_10m']) - : widget.currentData['wind_speed_10m']; + // final gustRaw = widget.currentData['wind_gusts_10m']; + // final gustValue = (gustRaw is num) ? gustRaw.toDouble() : 0.000000001; return Container( decoration: BoxDecoration( @@ -625,72 +602,72 @@ class _SummaryCardState extends State { }, child: buildWeatherSummaryWidget(context, isExpanded), ), - Padding( - padding: EdgeInsets.only(left: 10, right: 10, top: 5, bottom: 8), - child: Row( - children: [ - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 4, - children: [ - Icon( - Symbols.air, - weight: 500, - color: Theme.of(context).colorScheme.onSurface, - size: 19, - ), - Text( - '${windUnit == 'M/s' ? formattedWindSpeed.toStringAsFixed(1) : formattedWindSpeed.round()} ${localizeWindUnit(windUnit, context.locale)}', - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - fontSize: 15, - )) - ], - ), - ), - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 4, - children: [ - Icon(Symbols.wind_power, - weight: 500, - color: Theme.of(context).colorScheme.onSurface, - size: 19), - Text( - gustValue == 0.000000001 - ? '--' - : "${windUnit == 'M/s' ? formattedWindGust.toStringAsFixed(1) : formattedWindGust.round()} ${localizeWindUnit(windUnit, context.locale)}", - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - fontSize: 15, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ) - ], - ), - ), - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 4, - children: [ - Icon(Symbols.cloud, - weight: 500, - color: Theme.of(context).colorScheme.onSurface, - size: 19), - Text("${widget.currentData['cloud_cover']}%", - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - fontSize: 15, - )) - ], - ), - ), - ], - )) + // Padding( + // padding: EdgeInsets.only(left: 10, right: 10, top: 5, bottom: 8), + // child: Row( + // children: [ + // Expanded( + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // spacing: 4, + // children: [ + // Icon( + // Symbols.air, + // weight: 500, + // color: Theme.of(context).colorScheme.onSurface, + // size: 19, + // ), + // Text( + // '${windUnit == 'M/s' ? formattedWindSpeed.toStringAsFixed(1) : formattedWindSpeed.round()} ${localizeWindUnit(windUnit, context.locale)}', + // style: TextStyle( + // color: Theme.of(context).colorScheme.onSurface, + // fontSize: 15, + // )) + // ], + // ), + // ), + // Expanded( + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // spacing: 4, + // children: [ + // Icon(Symbols.wind_power, + // weight: 500, + // color: Theme.of(context).colorScheme.onSurface, + // size: 19), + // Text( + // gustValue == 0.000000001 + // ? '--' + // : "${windUnit == 'M/s' ? formattedWindGust.toStringAsFixed(1) : formattedWindGust.round()} ${localizeWindUnit(windUnit, context.locale)}", + // style: TextStyle( + // color: Theme.of(context).colorScheme.onSurface, + // fontSize: 15, + // ), + // maxLines: 1, + // overflow: TextOverflow.ellipsis, + // ) + // ], + // ), + // ), + // Expanded( + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // spacing: 4, + // children: [ + // Icon(Symbols.cloud, + // weight: 500, + // color: Theme.of(context).colorScheme.onSurface, + // size: 19), + // Text("${widget.currentData['cloud_cover']}%", + // style: TextStyle( + // color: Theme.of(context).colorScheme.onSurface, + // fontSize: 15, + // )) + // ], + // ), + // ), + // ], + // )) ], ), ); diff --git a/lib/widgets/top_weather_card.dart b/lib/widgets/top_weather_card.dart index 1a8db10cf..5a5decf0a 100644 --- a/lib/widgets/top_weather_card.dart +++ b/lib/widgets/top_weather_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:moon_phase/moon_phase.dart'; import '../utils/icon_map.dart'; import '../utils/condition_label_map.dart'; import '../utils/preferences_helper.dart'; @@ -7,6 +8,8 @@ import '../utils/unit_converter.dart'; import '../notifiers/unit_settings_notifier.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:moon_phase/moon_widget.dart'; +import 'package:moon_phase/moon_phase.dart'; class WeatherTopCard extends StatefulWidget { final num currentTemp; @@ -101,6 +104,15 @@ class _WeatherTopCardState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // MoonWidget( + // date: , + // resolution: 128, + // size: 64, + // moonColor: Colors.amber, + // earthshineColor: Colors.blueGrey.shade900, + + // ), + Text( "now".tr(), style: TextStyle( diff --git a/pubspec.lock b/pubspec.lock index 6677fe4b0..6a6a3f1e1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -437,6 +437,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.16.0" + moon_phase: + dependency: "direct main" + description: + name: moon_phase + sha256: "41de1d39bcbb45b9c4d86fa8405b231188fcbf6a095edd294db22cdee2d5e095" + url: "https://pub.dev" + source: hosted + version: "1.0.2+9" nested: dependency: transitive description: @@ -684,14 +692,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" - shimmer: - dependency: "direct main" - description: - name: shimmer - sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9" - url: "https://pub.dev" - source: hosted - version: "3.0.0" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 004e446d0..afa87e4e9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -63,7 +63,7 @@ dependencies: flutter_dotenv: ^5.2.1 home_widget: ^0.8.0 permission_handler: ^12.0.1 - shimmer: ^3.0.0 + moon_phase: ^1.0.2+9 settings_tiles: path: ./settings_tiles_custom diff --git a/settings_tiles_custom/example/android/.gradle/8.9/fileHashes/fileHashes.bin b/settings_tiles_custom/example/android/.gradle/8.9/fileHashes/fileHashes.bin index 6d07f3b34091dda59475eb43791524db6b73a994..38c35ae2a391fe2d01877fbf85a5022d554bc3bb 100644 GIT binary patch delta 1929 zcmYk5e@xVM9LBj#bjxVbm-*w0m^J&wgt2l=H&<57Zv5)x>^$Va^F%~Ia_geHA)#)> zOfP(FY0fsrALSp-it(3gjaqYNmmGMPq(PPnVx*K%Xj|R)dH8I1f4!d1^SqzW_w)UH zFE-+cjW|B*E1owKI=BznC3!aNL3xQ2=_OgVIAd1NCRc4c@c`Nh15xw}zQ3=gL9x1bGOjTWlQEW06we3uiEvMf6hQ^J>xz$Ob{ zEAbANviW#PFBzK9#} zRNSiBhOu-52X3HPa1*TtP6}?} zvH|NAhW|v7;4icoIAN@df+=Kp8U6#$8rUV6M33Ni#09spU?anEtP)J4LGTZb3;xDA z0}dawqD%AJy^TZ!WTZixLKT0><;bu?*ZKB#vTPl^srefPx)_57FN+2WxAbh*jA)kvNR&${oQ}koGay zcAcO0L&+K^j!HQ3xu22Ok`Y3P+5Bo|7hzpBSDmIx_P4SNQ>&gaRd1Rq8=N>$WBT0wPxA_oY}>;UWsO$4e(^`tX;f?5eb(>B?4O*dDYYDP-2seuAC;Iu5bh8$(a8KhEN|o^c9=TZJ+dP>B|Cyi0XV&?iPM0) ziPHzMw<+(QnNWNsKWqn<`Ll3Np~LpXlKaOW@dRsgnlIhTT)ils@nBk-$~w!!tc$$)YG^+w{pTI^FkD@JJ0_Q?vH)b delta 1929 zcmYk6U2KzO9K}~$sq6z7@1$F$n>Yu^vLT{Qgt-xdWLej(-DW9UH%b{}pTrBp1wpM| z7-GmmPE0gr7Y!GN3xm4sirA*oLX25RgPI^_1dSmg9UZH~ZA^Th|JjplSHJU|^PK1F z?NU>g)Rg7xiKg64_Jc(@T{Q|8(Bg8y>MAxxc-!)5c)K~O>_iG-f#}~DC)~rIg!`~J zt4#Dh8VL_^nvlU)8g3IFpvYZQV1}o2eWuT!= zT|}$^zBbvg4QB}jxJ+1sG~sEKw@W4;9zvcu;=owDdc&9v6C7QQy8_WiDD995g=iua z;f#h$1Os;n8(_RG;f**zD8VU$9Ty4D;1}!qHjm z7Km-aAmMq85uU?!4HmyNmthydiC(|D&6tB1m;WL59WwB(UoNf!KQLB_MZycHJ}eDe z&_mdY^Mq}>Lp2LVR!;_VqZB z>QXO}?7&~l`7sj`I{;<(1(cnbGHV?8oZ?q}6OhPDW<=m+$?oWu>@PUjtws~S=_=Yu z+K*RO&FiXHb=7N9b*@MKyb_OieYsU=>E5C$7jz|K6-Q+ArBc~Ju~Edw6nmvDeL`Y; zkvgJYC21^w&eezn4U`4dt0e8Zx=!YVgEFUD+QuoiHm_V6fwStQ&-ZiJ0>j~q~#>VIuQ|yH>BkT#U2sX zUYSz}XRqvXy{_DYFh>fcVUoEu`lG4UA5E>)gpNy%4X2MwO`fiKUF>CeNB<|rR^V7QS70LSK`4V%|a+YEikrT2sBg-$Wz9I+8`_)2;3~Kvj z`K+#^Jt20o0h3}Un^&H=NuSuOPwbV>O9Qg`MqJ~s$fY}J9~fM=)$^XBcuRUOQnczi zkJJ?oNxlJm&soFLq2Q^HV2Av zU5CsoDXEAa_W9@RcPjg?=C1lZG?J5J`7D!-m!WjTRkHtXy=QRoT*NzYYpgj|l%RQa J31>!r`X6QnnwJ0o diff --git a/settings_tiles_custom/example/android/.gradle/8.9/fileHashes/fileHashes.lock b/settings_tiles_custom/example/android/.gradle/8.9/fileHashes/fileHashes.lock index b3c06def75eb954c25e52764d07b37a708165cfb..9aa8ab9232ed224328eac717cfd52cbd26610fe5 100644 GIT binary patch literal 17 VcmZRMz56`-dT#I<1~71b3;;fk1^EB~ literal 17 VcmZRMz56`-dT#I<1~70c1pq!C1&ROw diff --git a/settings_tiles_custom/example/android/.gradle/8.9/fileHashes/resourceHashesCache.bin b/settings_tiles_custom/example/android/.gradle/8.9/fileHashes/resourceHashesCache.bin index f5d71b9327d1378b56a225c40d789010a5b571c0..de4074024c00d1726e355d0727ba640687967adf 100644 GIT binary patch delta 78 zcmeyrmhtd9#tkMCjD?#`B?OoRRxb^`UfUEc$N&am|0ml@DG02+Ch@kAFFFk(7_PWE cR4SN*apgwEAL1Jg3V1g=dhFwy9Pi@-0QWu_HUIzs delta 40 wcmX@Sj`9Cm#tkMCjQN{QB?Ooz$4W_T?v?W9V65Dz_(Oc7!2;gRjvo8?04;P5UH||9 diff --git a/settings_tiles_custom/example/build/app/intermediates/cxx/create_cxx_tasks_38_timing.txt b/settings_tiles_custom/example/build/app/intermediates/cxx/create_cxx_tasks_38_timing.txt index 8212570fc..603d8d079 100644 --- a/settings_tiles_custom/example/build/app/intermediates/cxx/create_cxx_tasks_38_timing.txt +++ b/settings_tiles_custom/example/build/app/intermediates/cxx/create_cxx_tasks_38_timing.txt @@ -565,3 +565,25 @@ create_cxx_tasks [gap of 20ms] create_cxx_tasks completed in 194ms +# C/C++ build system timings +create_cxx_tasks + create-initial-cxx-model + create-module-model 40ms + create-variant-model 16ms + create-ARMEABI_V7A-model 44ms + [gap of 101ms] + create-initial-cxx-model completed in 207ms + [gap of 22ms] +create_cxx_tasks completed in 239ms + +# C/C++ build system timings +create_cxx_tasks + create-initial-cxx-model + create-module-model 45ms + create-variant-model 15ms + create-ARMEABI_V7A-model 45ms + [gap of 87ms] + create-initial-cxx-model completed in 197ms + [gap of 23ms] +create_cxx_tasks completed in 231ms +