cleaned up settings page, added strings

This commit is contained in:
Pranshul committed 2025-08-29 21:31:50 +05:30
1 parent 4c0771dd05
commit 72cfba953e
18 files changed
+1027 -490

No files matched your search

+7 -1
View File
@@ -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"
}
+39 -38
View File
@@ -218,44 +218,45 @@ class _DailyForecastPageState extends State<DailyForecastPage> {
.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(
+88
View File
@@ -69,6 +69,10 @@ class _ExtendWidgetState extends State<ExtendWidget> {
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<ExtendWidget> {
.round()
: windUnit == 'Kt'
? UnitConverter.kmhToKt(windSpeed)
.toStringAsFixed(1)
: windSpeed.toStringAsFixed(1);
Widget windArrow = Transform.rotate(
@@ -2299,6 +2304,89 @@ class _ExtendWidgetState extends State<ExtendWidget> {
]);
});
}
// MOON
Widget buildMoonExtended() {
return FutureBuilder<Map<String, dynamic>?>(
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) {
+37 -41
View File
@@ -938,7 +938,6 @@ class _WeatherHomeState extends State<WeatherHome> {
maybeUpdateWeatherAnimation(current);
}
});
print("CODEEEEEEEEEEEEEEEEE: ${weatherCodeFroggy}");
} else {
_isLoadingFroggy == true;
_loadWeatherIconFroggy(weatherCodeFroggy, isDayFroggy,
@@ -1145,46 +1144,43 @@ class _WeatherHomeState extends State<WeatherHome> {
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:
@@ -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<AppearanceScreen> createState() => _AppearanceScreenState();
}
class _AppearanceScreenState extends State<AppearanceScreen> {
bool _showTile = PreferencesHelper.getBool("usingCustomSeed") ?? false;
bool _useCustomTile =
PreferencesHelper.getBool("DynamicColors") == true ? false : true;
@override
Widget build(BuildContext context) {
final themeController = Provider.of<ThemeController>(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<ThemeController>(
context,
listen: false,
).setSeedColor(
PreferencesHelper.getColor(
"CustomMaterialColor",
) ??
Colors.blue,
);
} else {
Provider.of<ThemeController>(
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<ThemeController>(
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<ThemeController>();
PreferencesHelper.setBool("DynamicColors", value);
if (value) {
await themeController.loadDynamicColors();
} else {
Provider.of<ThemeController>(
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<UnitSettingsNotifier>()
.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<UnitSettingsNotifier>()
.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<UnitSettingsNotifier>()
.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<UnitSettingsNotifier>().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),
]))
]));
}
}
+21 -283
View File
@@ -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<SettingsScreen> {
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<SettingsScreen> {
@override
Widget build(BuildContext context) {
final themeController = Provider.of<ThemeController>(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<SettingsScreen> {
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<ThemeController>(
context,
listen: false,
).setSeedColor(
PreferencesHelper.getColor(
"CustomMaterialColor",
) ??
Colors.blue,
);
} else {
Provider.of<ThemeController>(
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<ThemeController>(
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<ThemeController>();
PreferencesHelper.setBool("DynamicColors", value);
if (value) {
await themeController.loadDynamicColors();
} else {
Provider.of<ThemeController>(
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<UnitSettingsNotifier>().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<UnitSettingsNotifier>()
.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<SettingsScreen> {
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<SettingsScreen> {
);
},
),
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<UnitSettingsNotifier>()
.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<UnitSettingsNotifier>()
.updateuseCardBackgroundAnimations(value);
setState(() {});
},
),
SettingActionTile(
icon: Icon(
Symbols.nest_farsight_weather,
@@ -414,13 +156,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
],
),
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),
+16 -4
View File
@@ -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 = <Future<http.Response>>[
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<String, dynamic>;
final airQualityData =
json.decode(responses[1].body) as Map<String, dynamic>;
final astronomyData =
json.decode(responses[2].body) as Map<String, dynamic>;
// Check if we need fallback data for missing fields
Map<String, dynamic> 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 = {
+26 -16
View File
@@ -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<void> checkDynamicColorSupport() async {
_isDynamicColorSupported = (await DynamicColorPlugin.getCorePalette()) != null;
}
Future<void> checkDynamicColorSupport() async {
_isDynamicColorSupported =
(await DynamicColorPlugin.getCorePalette()) != null;
}
ThemeController();
Future<void> initialize({Color fallbackColor = const Color(0xFF012E7C)}) async {
Future<void> 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<void> 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();
}
+53
View File
@@ -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 '''<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M63.5616 25.8468C78.2888 11.595 101.711 11.595 116.438 25.8468C120.945 30.2077 126.466 33.3858 132.508 35.0962C152.251 40.6859 163.962 60.9091 158.946 80.7506C157.411 86.822 157.411 93.1781 158.946 99.2495C163.962 119.091 152.251 139.314 132.508 144.904C126.466 146.614 120.945 149.792 116.438 154.153C101.711 168.405 78.2888 168.405 63.5616 154.153C59.0552 149.792 53.5339 146.614 47.4926 144.904C27.7492 139.314 16.0379 119.091 21.0541 99.2495C22.5891 93.1781 22.5891 86.822 21.0541 80.7506C16.0379 60.9091 27.7492 40.6859 47.4926 35.0962C53.5339 33.3858 59.0552 30.2077 63.5616 25.8468Z" fill="$hexColor"/>
</svg>
''';
}
// 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 '''
<svg width="176" height="110" viewBox="0 0 176 110" style="overflow: visible;" xmlns="http://www.w3.org/2000/svg">
<path fill="$hexColor" d="M176.5,54.44V89.86C176.5,100.91 167.52,109.86 156.44,109.86H20.06C8.98,109.86 0,100.91 0,89.86V52.57C7.79,51.81 15.41,48.77 21.78,43.46L62.06,9.91C76.95,-2.49 98.6,-2.49 113.49,9.91L156.22,45.51C162.18,50.47 169.23,53.45 176.49,54.44H176.5Z"/>
<circle cx="$moonX" cy="${percent == 0 || percent == 1 ? '$moonYEndStart' : '$moonY'}"
fill="${showMoon ? 'white' : 'transparent'}"
r="10" stroke="$hexColoroutLine"
stroke-width="${showMoon ? '3' : '0'} "/>
</svg>
''';
}
+305 -5
View File
@@ -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<ConditionsWidgets> createState() => _ConditionsWidgetsState();
@@ -64,11 +74,12 @@ class _ConditionsWidgetsState extends State<ConditionsWidgets> {
List<int> itemOrder = [];
final String orderPrefsKey = 'tile_order';
bool _initialized = false;
@override
void initState() {
super.initState();
_loadTileOrder();
_initializePreferences();
}
Future<void> _loadTileOrder() async {
@@ -80,13 +91,34 @@ class _ConditionsWidgetsState extends State<ConditionsWidgets> {
itemOrder = savedList.map(int.parse).toList();
});
} else {
// Default order
setState(() {
itemOrder = List.generate(8, (index) => index);
itemOrder = List.generate(10, (index) => index);
});
}
}
Future<void> _initializePreferences() async {
if (_initialized) return;
_initialized = true;
final prefs = await SharedPreferences.getInstance();
List<String>? savedList = prefs.getStringList(orderPrefsKey);
if (savedList != null) {
List<int> intList = savedList.map(int.parse).toList();
List<int> zeroToSeven = List.generate(8, (i) => i);
if (intList.toSet().containsAll(zeroToSeven) &&
intList.every((e) => e >= 0 && e <= 7)) {
await prefs.remove(orderPrefsKey);
}
}
List<String> 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<ConditionsWidgets> {
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<ConditionsWidgets> {
? 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<UnitSettingsNotifier>().pressureUnit;
final precipitationUnit =
context.watch<UnitSettingsNotifier>().precipitationUnit;
@@ -153,6 +193,18 @@ class _ConditionsWidgetsState extends State<ConditionsWidgets> {
? UnitConverter.mToMiles(widget.currentVisibility.toDouble())
: UnitConverter.mToKm(widget.currentVisibility.toDouble());
final windUnit = context.watch<UnitSettingsNotifier>().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<ConditionsWidgets> {
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<Widget> gridItems = itemOrder.map((i) {
switch (i) {
case 0:
@@ -689,7 +756,7 @@ class _ConditionsWidgetsState extends State<ConditionsWidgets> {
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<ConditionsWidgets> {
);
},
);
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();
}
+70 -93
View File
@@ -334,8 +334,7 @@ class _SummaryCardState extends State<SummaryCard> {
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<SummaryCard> {
isSummaryLoaded = true;
}
final windUnit = context.watch<UnitSettingsNotifier>().windUnit;
// final windUnit = context.watch<UnitSettingsNotifier>().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<SummaryCard> {
},
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,
// ))
// ],
// ),
// ),
// ],
// ))
],
),
);
+12
View File
@@ -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<WeatherTopCard> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// MoonWidget(
// date: ,
// resolution: 128,
// size: 64,
// moonColor: Colors.amber,
// earthshineColor: Colors.blueGrey.shade900,
// ),
Text(
"now".tr(),
style: TextStyle(
+8 -8
View File
@@ -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
+1 -1
View File
@@ -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
@@ -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