diff --git a/README.md b/README.md
index 6b2b849..fb0681e 100644
--- a/README.md
+++ b/README.md
@@ -1,44 +1,71 @@
-# Overmorrow weather
-![app_gallery](Screenshots/new_feature_graphic_yellow.jpg)
+
+
+
-## Minimalist colorful weather app
+
-![app_gallery](Screenshots/app_gallery4_tranparent.png)
+Overmorrow
+
-| [![Download on Google Play](/Screenshots/play_badge4.png 'Download')](https://play.google.com/store/apps/details?id=com.marotidev.Overmorrow) | [![Download on IzzyOnDroid](/Screenshots/IzzyOnDroid_c.png 'Download')](https://apt.izzysoft.de/fdroid/index/apk/com.marotidev.Overmorrow/) |
-|---|---|
+minimalist colorful weather.
+
+
+
+
+
## Weather providers 🌨️
- [open-meteo](https://open-meteo.com)
- [weatherapi.com](https://www.weatherapi.com)
+- [met-norway](https://api.met.no/)
- [rainvewer](https://www.rainviewer.com/api.html)
Only now after working on Overmorrow for more than 6 months, have I realized that my
app is defined as "non commercial" in open-meteo's documentation 😂. Which is great because i can use it completely for free 😎!
So now i can add 14 days of forecast . Also its one of the most accurate weather providers.
-I get my sunrise sunset times and air quality from weatherapi.com 🍃.
-Also it is offered as a second weather provider, but it only has 3 days of weather data.
+you can also change your provider to met-norway or weatherapi.com.
-And all the radar images are from rainviewer's radar 💧.
+All the radar images are from rainviewer's radar 💧.
## Features 🎉
+- network images that change based on location and weather condition
- accurate weather forecast
- open source
- no ads
- no data collected
- minimalist colorful design
-- detailed forecast
-- sunrise sunset times
-- air quality insights
-- full screen radar
-- 14 days of forecast
-- dynamically adapting color scheme
-- 5 color themes (original, colorful, monochrome, light, dark)
-- languages support
+- sunrise / sunset times, current time in city
+- rain in the next 6 hours with 15 minute precision
+- air quality index, description, summary, pm_2.5, pm10, o3, no2
+- compact and fullscreen radar, with 2 hour past, and 30 minutes of future timestamps.
+- 3 day detailed forecast, with options for temp, precip, wind and uv
+- 14 day compact forecast, with option to open into detailed view.
+- rain charts showing the rain on a given day
+- option to choose from 3 weather providers
+- 5 beautiful color theme options
+- 2 search providers
+
+## Tablet mode
+
+![page48](Screenshots/page48.png)
+![üpage50](Screenshots/page50.png)
## Why make Overmorrow? ❓
I am 15 and i have been programing since the age of 7. I started small (Scratch and NetsBlox)
@@ -53,6 +80,7 @@ So instead here is my take on the weather app ui (but i did kep it free and ad f
- ✅ Add place searching
- ✅ Add radar
- ✅ Add air quality
+- ✅ Add sunrise sunset
- ✅ Add translations
- ✅ 14 day forecast
- ✅ Settings/Info/Donate pages
diff --git a/Screenshots/Overmorrow_white_circle.png b/Screenshots/Overmorrow_white_circle.png
new file mode 100644
index 0000000..fdc6f13
Binary files /dev/null and b/Screenshots/Overmorrow_white_circle.png differ
diff --git a/Screenshots/app_gallery3.png b/Screenshots/app_gallery3.png
deleted file mode 100644
index 5b7cd06..0000000
Binary files a/Screenshots/app_gallery3.png and /dev/null differ
diff --git a/Screenshots/app_gallery4_tranparent.png b/Screenshots/app_gallery4_tranparent.png
deleted file mode 100644
index 4744e39..0000000
Binary files a/Screenshots/app_gallery4_tranparent.png and /dev/null differ
diff --git a/Screenshots/colorful1.png b/Screenshots/colorful1.png
new file mode 100644
index 0000000..ebc93b0
Binary files /dev/null and b/Screenshots/colorful1.png differ
diff --git a/Screenshots/colorful2.png b/Screenshots/colorful2.png
new file mode 100644
index 0000000..2253d00
Binary files /dev/null and b/Screenshots/colorful2.png differ
diff --git a/Screenshots/colorful3.png b/Screenshots/colorful3.png
new file mode 100644
index 0000000..e228549
Binary files /dev/null and b/Screenshots/colorful3.png differ
diff --git a/Screenshots/colorful4.png b/Screenshots/colorful4.png
new file mode 100644
index 0000000..23547c2
Binary files /dev/null and b/Screenshots/colorful4.png differ
diff --git a/Screenshots/colorful5.png b/Screenshots/colorful5.png
new file mode 100644
index 0000000..72ce05d
Binary files /dev/null and b/Screenshots/colorful5.png differ
diff --git a/Screenshots/page48.png b/Screenshots/page48.png
new file mode 100644
index 0000000..277d02f
Binary files /dev/null and b/Screenshots/page48.png differ
diff --git a/Screenshots/page50.png b/Screenshots/page50.png
new file mode 100644
index 0000000..fea5169
Binary files /dev/null and b/Screenshots/page50.png differ
diff --git a/android/app/build.gradle b/android/app/build.gradle
index f2015cd..90dce79 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -41,7 +41,8 @@ android {
compileSdk 34
// compileSdkVersion flutter.compileSdkVersion <- this vas the original one but
// geolocator insisted on it being 33
- ndkVersion flutter.ndkVersion
+ //ndkVersion flutter.ndkVersion
+ ndkVersion "27.0.12077973"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@@ -63,8 +64,8 @@ android {
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
- versionCode 42
- versionName "2.4.2"
+ versionCode 43
+ versionName "2.4.3"
}
buildTypes {
diff --git a/android/gradle.properties b/android/gradle.properties
index 94adc3a..b9a9a24 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,3 +1,6 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
+android.defaults.buildfeatures.buildconfig=true
+android.nonTransitiveRClass=false
+android.nonFinalResIds=false
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 3c472b9..09523c0 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
index adce2b0..175b330 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -18,7 +18,7 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
- id "com.android.application" version "7.3.0" apply false
+ id "com.android.application" version '8.7.0' apply false
id "org.jetbrains.kotlin.android" version "2.0.0" apply false
}
diff --git a/lib/aqi_page.dart b/lib/aqi_page.dart
new file mode 100644
index 0000000..9c5eb44
--- /dev/null
+++ b/lib/aqi_page.dart
@@ -0,0 +1,829 @@
+/*
+Copyright (C) <2024>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+
+
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:loading_animation_widget/loading_animation_widget.dart';
+import 'package:overmorrow/decoders/decode_OM.dart';
+import 'package:overmorrow/settings_page.dart';
+import 'package:overmorrow/ui_helper.dart';
+
+
+class SquigglyCirclePainter extends CustomPainter {
+
+ final Color circleColor;
+
+ SquigglyCirclePainter(this.circleColor);
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final Paint paint = Paint()
+ ..color = circleColor
+ ..style = PaintingStyle.stroke
+ ..strokeCap = StrokeCap.round
+ ..strokeWidth = 2.8;
+
+ final Path path = Path();
+ double radius = size.width / 2;
+ double centerX = size.width / 2;
+ double centerY = size.height / 2;
+
+ double waves = 10;
+ double waveAmplitude = size.width / 50;
+
+ for (double i = 0; i <= 360; i += 0.1) {
+ double angle = i * pi / 180;
+ double x = centerX + (radius + waveAmplitude * sin(waves * angle)) * cos(angle);
+ double y = centerY + (radius + waveAmplitude * sin(waves * angle)) * sin(angle);
+
+ if (i == 0) {
+ path.moveTo(x, y);
+ } else {
+ path.lineTo(x, y);
+ }
+ }
+
+ path.close();
+ canvas.drawPath(path, paint);
+ }
+
+ @override
+ bool shouldRepaint(CustomPainter oldDelegate) => false;
+}
+
+Widget pollenWidget(IconData icon, String name, double value, data) {
+ const categoryBoundaries = [-1, 0, 20, 80, 200];
+ const categoryNames = ["--", "none", "low", "medium", "high"];
+
+ int categoryIndex = 0;
+ for (int i = 0; i < categoryBoundaries.length; i++) {
+ if (value > categoryBoundaries[i]) {
+ categoryIndex = i + 1;
+ }
+ }
+
+ String severity = categoryNames[categoryIndex];
+
+ return Padding(
+ padding: const EdgeInsets.only(left: 10, right: 6, top:6, bottom: 6),
+ child: Row(
+ children: [
+ Icon(icon, size: 22, color: data.current.primaryLight),
+ Padding(
+ padding: const EdgeInsets.only(left: 17),
+ child: comfortatext(name, 17, data.settings, color: data.current.onSurface),
+ ),
+ const Spacer(),
+ Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(12),
+ color: data.current.primaryLighter,
+ ),
+ padding: const EdgeInsets.only(top: 6.5, bottom: 6.5),
+ width: 65,
+ child: Center(child: comfortatext(severity, 15, data.settings, color: data.current.onPrimaryLight))
+ ),
+ ],
+ ),
+ );
+}
+
+
+class ThreeQuarterCirclePainter extends CustomPainter {
+ final double percentage;
+ final Color color;
+ final Color secondColor;
+
+ ThreeQuarterCirclePainter({required this.percentage, required this.color, required this.secondColor});
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ double angle = 2 * 3.14159265359 * (max(min(percentage, 100), 0) / 100) * 0.75; // 3 quarters of a circle
+
+ // Background Circle
+ Paint baseCircle = Paint()
+ ..color = secondColor
+ ..strokeWidth = 9
+ ..strokeCap = StrokeCap.round
+ ..style = PaintingStyle.stroke;
+ canvas.drawArc(
+ Rect.fromLTWH(0, 0, size.width, size.height),
+ -3.14159265359 * 5 / 4,
+ 3.14159265359 * 1.5,
+ false,
+ baseCircle,
+ );
+
+ // Foreground Circle
+ Paint progressCircle = Paint()
+ ..color = color
+ ..strokeWidth = 9
+ ..strokeCap = StrokeCap.round
+ ..style = PaintingStyle.stroke;
+ canvas.drawArc(
+ Rect.fromLTWH(0, 0, size.width, size.height),
+ -3.14159265359 * 5 / 4,
+ angle,
+ false,
+ progressCircle,
+ );
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) {
+ return true;
+ }
+}
+
+Widget pollutantWidget(data, name, value, percent) {
+ return Padding(
+ padding: EdgeInsets.all(14),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(top: 0, bottom: 0),
+ child: Center(
+ child: AspectRatio(
+ aspectRatio: 1,
+ child: CustomPaint(
+ painter: ThreeQuarterCirclePainter(percentage: percent, color: data.current.primaryLight,
+ secondColor: data.current.containerHigh),
+ child: Center(
+ child: comfortatext(value.toString(), 18, data.settings, color: data.current.primary, weight: FontWeight.w600)
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ comfortatext(name, 14, data.settings, color: data.current.onSurface),
+ ],
+ ),
+ );
+}
+
+class AllergensPage extends StatefulWidget {
+ final data;
+
+ const AllergensPage({Key? key, required this.data})
+ : super(key: key);
+
+ @override
+ _AllergensPageState createState() =>
+ _AllergensPageState(data:data);
+}
+
+class _AllergensPageState extends State {
+
+ final data;
+
+ _AllergensPageState({required this.data});
+
+ void goBack() {
+ HapticFeedback.selectionClick();
+ Navigator.pop(context);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Material(
+ color: data.current.surface,
+ child: CustomScrollView(
+ slivers: [
+ SliverAppBar.large(
+ leading: IconButton(
+ icon: Icon(Icons.arrow_back, color: data.current.primary),
+ onPressed: () {
+ goBack();
+ },
+ ),
+ title: comfortatext(translation("Air Quality", data.settings["Language"]), 30, data.settings, color: data.current.primary),
+ backgroundColor: data.current.surface,
+ pinned: false,
+ ),
+ SliverToBoxAdapter(
+ child: FutureBuilder(
+ future: OMExtendedAqi.fromJson(data.lat, data.lng, data.settings),
+ builder: (BuildContext context,
+ AsyncSnapshot snapshot) {
+ if (snapshot.connectionState != ConnectionState.done) {
+ return Padding(
+ padding: const EdgeInsets.only(top: 200),
+ child: Center(
+ child: LoadingAnimationWidget.staggeredDotsWave(
+ color: data.current.primaryLight,
+ size: 40,
+ ),
+ ),
+ );
+ } else if (snapshot.hasError) {
+ print((snapshot.error, snapshot.stackTrace));
+ return Padding(
+ padding: const EdgeInsets.only(top: 100),
+ child: Column(
+ children: [
+ comfortatext("unable to load air quality data", 18, data.settings, color: data.current.primary),
+ Padding(
+ padding: const EdgeInsets.all(30.0),
+ child: comfortatext("${snapshot.error} ${snapshot.stackTrace}", 15, data.settings, color: data.current.onSurface,
+ align: TextAlign.center),
+ )
+ ],
+ ),
+ );
+ }
+ final OMExtendedAqi extendedAqi = snapshot.data!;
+ final highestAqi = extendedAqi.dailyAqi.reduce(max);
+ return Padding(
+ padding: const EdgeInsets.only(left: 30, right: 30),
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 50, right: 50, top: 50, bottom: 30),
+ child: AspectRatio(
+ aspectRatio: 1,
+ child: CustomPaint(
+ painter: SquigglyCirclePainter(data.current.primaryLight),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 15),
+ child: comfortatext(data.aqi.aqi_index.toString(), 52, data.settings, color: data.current.primary, weight: FontWeight.w300),
+ ),
+ comfortatext(data.aqi.aqi_title, 23, data.settings, color: data.current.primary, weight: FontWeight.w600),
+ ],
+ ),
+ ),
+ ),
+ ),
+
+ Padding(
+ padding: const EdgeInsets.only(top: 10, bottom: 20, left: 10, right: 10),
+ child: comfortatext(data.aqi.aqi_desc, 17, data.settings, color: data.current.onSurface, weight: FontWeight.w400, align: TextAlign.center),
+ ),
+
+
+ /*
+ GridView.count(
+ padding: const EdgeInsets.only(top: 15, bottom: 20, left: 10, right: 10),
+ crossAxisSpacing: 10,
+ mainAxisSpacing: 10,
+ crossAxisCount: 3,
+ childAspectRatio: 4.8,
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ children: [
+ NewAqiDataPoints("PM2.5", data.aqi.pm2_5, data, 18.0),
+ NewAqiDataPoints("PM10", data.aqi.pm10, data, 18.0),
+ NewAqiDataPoints("O3", data.aqi.o3, data, 18.0),
+ NewAqiDataPoints("NO2", data.aqi.no2, data, 18.0),
+ NewAqiDataPoints("CO", extendedAqi.co, data, 18.0),
+ NewAqiDataPoints("SO2", extendedAqi.so2, data, 18.0),
+ ]
+ ),
+ */
+
+
+ Padding(
+ padding: const EdgeInsets.only(top: 10, bottom: 5),
+ child: Container(
+ decoration: BoxDecoration(
+ color: data.current.containerLow,
+ //border: Border.all(width: 1.5, color: data.current.containerHigh),
+ borderRadius: BorderRadius.circular(18),
+ ),
+ padding: const EdgeInsets.all(17),
+ child: Row(
+ children: [
+ comfortatext(translation("main pollutant", data.settings["Language"]), 16, data.settings, color: data.current.onSurface),
+ const Spacer(),
+ comfortatext(extendedAqi.mainPollutant, 18, data.settings, color: data.current.primary, weight: FontWeight.w600)
+ ],
+ ),
+ ),
+ ),
+
+ Padding(
+ padding: const EdgeInsets.only(top: 15, bottom: 40),
+ child: Container(
+ decoration: BoxDecoration(
+ //color: data.current.containerLow,
+ border: Border.all(width: 2, color: data.current.containerHigh),
+ borderRadius: BorderRadius.circular(18),
+ ),
+ padding: const EdgeInsets.all(11),
+ child: Column(
+ children: [
+ pollenWidget(Icons.forest_outlined,
+ translation("Alder Pollen", data.settings["Language"]), extendedAqi.alder, data),
+ pollenWidget(Icons.eco_outlined,
+ translation("Birch Pollen", data.settings["Language"]), extendedAqi.birch, data),
+ pollenWidget(Icons.grass_outlined,
+ translation("Grass Pollen", data.settings["Language"]), extendedAqi.grass, data),
+ pollenWidget(Icons.local_florist_outlined,
+ translation("Mugwort Pollen", data.settings["Language"]), extendedAqi.mugwort, data),
+ pollenWidget(Icons.park_outlined,
+ translation("Olive Pollen", data.settings["Language"]), extendedAqi.olive, data),
+ pollenWidget(Icons.filter_vintage_outlined,
+ translation("Ragweed Pollen", data.settings["Language"]), extendedAqi.ragweed, data),
+ ],
+ ),
+ ),
+ ),
+
+ NewHourlyAqi(data: data, extendedAqi: extendedAqi),
+
+ Padding(
+ padding: const EdgeInsets.only(bottom: 20, top: 50),
+ child: Container(
+ decoration: BoxDecoration(
+ //color: data.current.containerLow,
+ border: Border.all(width: 2, color: data.current.containerHigh),
+ borderRadius: BorderRadius.circular(18),
+ ),
+ padding: EdgeInsets.all(10),
+ child: GridView.count(
+ physics: const NeverScrollableScrollPhysics(),
+ padding: EdgeInsets.zero,
+ crossAxisCount: 3,
+ shrinkWrap: true,
+ childAspectRatio: 0.9,
+ children: [
+ pollutantWidget(data, "pm2.5", data.aqi.pm2_5, extendedAqi.pm2_5_p),
+ pollutantWidget(data, "pm10", data.aqi.pm10, extendedAqi.pm10_p),
+ pollutantWidget(data, "o3", data.aqi.o3, extendedAqi.o3_p),
+ pollutantWidget(data, "no2", data.aqi.no2, extendedAqi.no2_p),
+ pollutantWidget(data, "co", extendedAqi.co, extendedAqi.co_p),
+ pollutantWidget(data, "so2", extendedAqi.so2, extendedAqi.so2_p),
+ ],
+ ),
+ ),
+ ),
+
+ Padding(
+ padding: const EdgeInsets.only(top: 0, bottom: 0),
+ child: Row(
+ children: [
+ Expanded(
+ flex: 1,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 4),
+ child: Container(
+ decoration: BoxDecoration(
+ color: data.current.containerLow,
+ //border: Border.all(width: 1.5, color: data.current.containerHigh),
+ borderRadius: BorderRadius.circular(18),
+ ),
+ height: 115,
+ padding: const EdgeInsets.only(left: 14, top: 14, right: 10, bottom: 14),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ comfortatext(translation("european aqi", data.settings["Language"]), 14, data.settings, color: data.current.onSurface),
+ const Spacer(),
+ comfortatext(extendedAqi.european_aqi.toString(), 25, data.settings, color: data.current.primary, weight: FontWeight.w400),
+ Padding(
+ padding: const EdgeInsets.only(left: 2, top: 1),
+ child: comfortatext("good", 15, data.settings, color: data.current.outline, weight: FontWeight.w600),
+ ),
+ ],
+ ),
+ ),
+ )
+ ),
+ Expanded(
+ flex: 1,
+ child: Padding(
+ padding: const EdgeInsets.only(left: 4),
+ child: Container(
+ decoration: BoxDecoration(
+ color: data.current.containerLow,
+ //border: Border.all(width: 1.5, color: data.current.containerHigh),
+ borderRadius: BorderRadius.circular(18),
+ ),
+ height: 115,
+ padding: const EdgeInsets.only(left: 14, top: 14, right: 10, bottom: 14),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ comfortatext(translation("united states aqi", data.settings["Language"]), 14, data.settings, color: data.current.onSurface),
+ const Spacer(),
+ comfortatext(extendedAqi.us_aqi.toString(), 25, data.settings, color: data.current.primary, weight: FontWeight.w400),
+ Padding(
+ padding: const EdgeInsets.only(left: 2, top: 1),
+ child: comfortatext("good", 15, data.settings, color: data.current.outline, weight: FontWeight.w600),
+ ),
+ ],
+ ),
+ ),
+ )
+ )
+ ],
+ ),
+ ),
+
+ Padding(
+ padding: const EdgeInsets.only(bottom: 10, top: 35),
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: comfortatext(translation("daily aqi", data.settings["Language"]), 16, data.settings, color: data.current.primary)
+ ),
+ ),
+
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: List.generate(extendedAqi.dailyAqi.length, (index) {
+ return Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 4, right: 4, bottom: 10),
+ child: SizedBox(
+ height: 130,
+ child: Align(
+ alignment: Alignment.bottomCenter,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(22),
+ color: extendedAqi.dailyAqi[index] == highestAqi ? data.current.surface : data.current.primaryLight,
+ border: Border.all(color: extendedAqi.dailyAqi[index] == highestAqi ? data.current.primaryLight
+ : data.current.surface, width: 2)
+ ),
+ width: 43,
+ alignment: Alignment.topCenter,
+ padding: const EdgeInsets.only(top: 12),
+ //tried to do some null safety and not allowing the bars to be too short
+ height: max(110 / max(highestAqi, 1) * extendedAqi.dailyAqi[index], 42),
+ child: comfortatext(extendedAqi.dailyAqi[index].toString(), 16, data.settings,
+ color: extendedAqi.dailyAqi[index] == highestAqi ? data.current.primaryLight : data.current.surface,
+ weight: FontWeight.w600),
+ ),
+ ),
+ ),
+ ),
+ comfortatext(index == 0 ? translation("now", data.settings["Language"])
+ : "${index}${translation("d", data.settings["Language"])}",
+ 14, data.settings, color: data.current.outline)
+ ],
+ );
+ }
+ )
+ ),
+
+ Padding(
+ padding: const EdgeInsets.only(top: 45, bottom: 0),
+ child: Row(
+ children: [
+ Expanded(
+ flex: 5,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 4),
+ child: Container(
+ decoration: BoxDecoration(
+ //color: data.current.containerLow,
+ border: Border.all(width: 1.5, color: data.current.containerHigh),
+ borderRadius: BorderRadius.circular(18),
+ ),
+ height: 125,
+ padding: const EdgeInsets.only(left: 14, top: 14, right: 10, bottom: 14),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Icon(Icons.grain, size: 18, color: data.current.primaryLight),
+ Padding(
+ padding: const EdgeInsets.only(left: 5),
+ child: comfortatext(translation("dust", data.settings["Language"]), 14, data.settings, color: data.current.onSurface),
+ )
+ ],
+ ),
+ const Spacer(),
+ comfortatext(extendedAqi.dust.toString(), 25, data.settings, color: data.current.primary, weight: FontWeight.w400),
+ Padding(
+ padding: const EdgeInsets.only(left: 2, top: 1),
+ child: comfortatext("μg/m³", 15, data.settings, color: data.current.outline, weight: FontWeight.w600),
+ ),
+ ],
+ ),
+ ),
+ )
+ ),
+ Expanded(
+ flex: 8,
+ child: Padding(
+ padding: const EdgeInsets.only(left: 4),
+ child: Container(
+ decoration: BoxDecoration(
+ //color: data.current.containerLow,
+ border: Border.all(width: 1.5, color: data.current.containerHigh),
+ borderRadius: BorderRadius.circular(18),
+ ),
+ height: 125,
+ padding: const EdgeInsets.only(left: 14, top: 14, right: 10, bottom: 14),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Icon(Icons.grain, size: 18, color: data.current.primaryLight),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(left: 5),
+ child: comfortatext(translation("aerosol optical depth", data.settings["Language"]),
+ 14, data.settings, color: data.current.onSurface),
+ ),
+ )
+ ],
+ ),
+ const Spacer(),
+ comfortatext(extendedAqi.aod.toString(), 25, data.settings, color: data.current.primary, weight: FontWeight.w400),
+ Padding(
+ padding: const EdgeInsets.only(left: 2, top: 1),
+ child: comfortatext(extendedAqi.aod_desc, 15, data.settings, color: data.current.outline, weight: FontWeight.w600),
+ ),
+ ],
+ ),
+ ),
+ )
+ )
+ ],
+ ),
+ ),
+
+ Align(
+ alignment: Alignment.center,
+ child: Padding(
+ padding: const EdgeInsets.only(top: 60, bottom: 70),
+ child: comfortatext("powered by open-meteo", 15, data.settings, color: data.current.outline),
+ ),
+ )
+
+ ],
+ ),
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+
+class NewHourlyAqi extends StatefulWidget {
+ final data;
+ final extendedAqi;
+
+ NewHourlyAqi({Key? key, required this.data, required this.extendedAqi}) : super(key: key);
+
+ @override
+ _NewHourlyAqiState createState() => _NewHourlyAqiState(data, extendedAqi);
+}
+
+class _NewHourlyAqiState extends State with AutomaticKeepAliveClientMixin {
+ final data;
+ final extendedAqi;
+ int _value = 0;
+
+ PageController _pageController = PageController();
+
+ void _onItemTapped(int index) {
+ _pageController.animateToPage(
+ index,
+ duration: const Duration(milliseconds: 400),
+ curve: Curves.fastEaseInToSlowEaseOut,
+ );
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ _value = ["pm2.5", "pm10", "ozone", "carbon monoxide", "sulphur dioxide", "nitrogen dioxide"].
+ indexOf(extendedAqi.mainPollutant);
+ _pageController = PageController(initialPage: _value);
+ }
+
+ @override
+ bool get wantKeepAlive => true;
+
+ _NewHourlyAqiState(this.data, this.extendedAqi);
+
+ @override
+ Widget build(BuildContext context) {
+ super.build(context);
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 5),
+ child: SizedBox(
+ height: 300,
+ child: PageView(
+ physics: const NeverScrollableScrollPhysics(),
+ controller: _pageController,
+ children: [
+ HourlyQqi(data, extendedAqi.pm2_5_h, "PM2.5", extendedAqi),
+ HourlyQqi(data, extendedAqi.pm10_h, "PM10", extendedAqi),
+ HourlyQqi(data, extendedAqi.o3_h, "O3", extendedAqi),
+ HourlyQqi(data, extendedAqi.no2_h, "NO2", extendedAqi),
+ HourlyQqi(data, extendedAqi.co_h, "CO", extendedAqi),
+ HourlyQqi(data, extendedAqi.so2_h, "SO2", extendedAqi),
+ ],
+ ),
+ ),
+ ),
+ Wrap(
+ spacing: 5.0,
+ children: List.generate(
+ 6,
+ (int index) {
+
+ return ChoiceChip(
+ elevation: 0.0,
+ checkmarkColor: data.current.onPrimaryLight,
+ color: WidgetStateProperty.resolveWith((states) {
+ if (index == _value) {
+ return data.current.primaryLighter;
+ }
+ return data.current.surface;
+ }),
+ side: BorderSide(color: data.current.primaryLighter, width: 1.5),
+ label: comfortatext(
+ ['pm2.5', 'pm10', 'o3', 'no2', 'co', 'so2'][index], 14, data.settings,
+ color: _value == index ? data.current.onPrimaryLight : data.current.onSurface),
+ selected: _value == index,
+ onSelected: (bool selected) {
+ _value = index;
+ setState(() {
+ HapticFeedback.lightImpact();
+ _onItemTapped(index);
+ });
+ },
+ );
+ },
+ ).toList(),
+ ),
+ ],
+ );
+
+ }
+}
+
+class AQIGraphPainter extends CustomPainter {
+ final List aqiData;
+ final int maxAQI;
+ final Color color;
+
+ AQIGraphPainter({required this.aqiData, required this.maxAQI, required this.color});
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final paint = Paint()
+ ..color = color
+ ..strokeCap = StrokeCap.round
+ ..strokeWidth = 2.5;
+
+ final double chartHeight = size.height;
+ final double chartWidth = size.width;
+ final double yScale = chartHeight / maxAQI;
+ final double xSpacing = chartWidth / (aqiData.length - 1);
+
+ for (int i = 0; i < aqiData.length - 1; i++) {
+ final startX = i * xSpacing;
+ final startY = chartHeight - (aqiData[i] * yScale);
+ final endX = (i + 1) * xSpacing;
+ final endY = chartHeight - (aqiData[i + 1] * yScale);
+ canvas.drawLine(Offset(startX, startY), Offset(endX, endY), paint);
+ }
+
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) {
+ return true; //if data changes -> repaints
+ }
+}
+
+Widget HourlyQqi(data, hourValues, name, extendedAqi) {
+
+ const List> chartTypes = [
+ [0, 2, 4, 6, 8, 10],
+ [0, 5, 10, 15, 20, 25],
+ [0, 10, 20, 30, 40, 50],
+ [0, 20, 40, 60, 80, 100],
+ [0, 30, 60, 90, 120, 150],
+ [0, 50, 100, 150, 200, 250],
+ [0, 100, 200, 300, 400, 500],
+ [0, 200, 400, 600, 800, 1000]
+ ];
+
+ double valueMax = hourValues.reduce((a, b) => max(a, b));
+ int currentChart = 0;
+
+ for (int i = 0; i < chartTypes.length; i++) {
+ if (valueMax * 1.3 > chartTypes[i][chartTypes[i].length - 1]) { //because it looks weird if it is close to the top
+ currentChart = min(i + 1, chartTypes.length - 1); //just for null safety
+ }
+ }
+
+ int len = chartTypes[currentChart].length;
+
+ return Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 20),
+ child: Row(
+ children: [
+ Icon(Icons.grain, size: 20, color: data.current.primaryLight),
+ Padding(
+ padding: const EdgeInsets.only(left: 5),
+ child: comfortatext(name, 17, data.settings, color: data.current.primary),
+ )
+ ]
+ ),
+ ),
+
+ Stack(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 2, right: 10),
+ child: SizedBox(
+ height: 220,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: List.generate(len, (index) {
+ return Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ comfortatext(chartTypes[currentChart][len - 1 - index].toString(), 14, data.settings,
+ color: data.current.outline),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(left: 15),
+ child: Container(
+ color: data.current.containerHigh,
+ height: 1,
+ ),
+ ),
+ )
+ ],
+ );
+ }),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(left: 40, right: 20),
+ child: CustomPaint(
+ painter: AQIGraphPainter(aqiData: hourValues,
+ maxAQI: chartTypes[currentChart][len - 1],
+ color: data.current.primaryLight),
+ child: const SizedBox(
+ width: double.infinity,
+ height: 220.0,
+ ),
+ ),
+ ),
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 7, bottom: 0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: List.generate(extendedAqi.dailyAqi.length, (index) {
+ return comfortatext(index == 0 ? translation("now", data.settings["Language"])
+ : "${index}${translation("d", data.settings["Language"])}",
+ 14, data.settings, color: data.current.outline);
+ }
+ )
+ ),
+ )
+ ]
+ );
+}
diff --git a/lib/decoders/decode_OM.dart b/lib/decoders/decode_OM.dart
index 453642d..50cb274 100644
--- a/lib/decoders/decode_OM.dart
+++ b/lib/decoders/decode_OM.dart
@@ -492,6 +492,7 @@ class OM15MinutePrecip {
sum += x;
precips.add(x);
+ print(x);
}
sum = max(sum, 0.1); //if there is rain then it shouldn't write 0
@@ -626,28 +627,32 @@ class OMSunstatus {
class OMAqi{
final int aqi_index;
+
final double pm2_5;
final double pm10;
final double o3;
final double no2;
- final String aqi_title;
+
final String aqi_desc;
+ final String aqi_title;
const OMAqi({
+
+ required this.aqi_desc,
+ required this.aqi_title,
+
required this.no2,
required this.o3,
required this.pm2_5,
required this.pm10,
required this.aqi_index,
- required this.aqi_desc,
- required this.aqi_title,
});
- static Future fromJson(item, lat, lng, settings) async {
+ static Future fromJson(lat, lng, settings) async {
final params = {
"latitude": lat.toString(),
"longitude": lng.toString(),
- "current": ["european_aqi", "pm10", "pm2_5", "nitrogen_dioxide", 'ozone'],
+ "current": ["european_aqi", "pm10", "pm2_5", "nitrogen_dioxide", "ozone"],
};
final url = Uri.https("air-quality-api.open-meteo.com", 'v1/air-quality', params);
var file = await cacheManager2.getSingleFile(url.toString(), key: "$lat, $lng, aqi open-meteo").timeout(const Duration(seconds: 6));
@@ -670,6 +675,232 @@ class OMAqi{
}
}
+
+class OMExtendedAqi{ //this data will only be called if you open the Air quality page
+ //this is done to reduce the amount of unused calls to the open-meteo servers
+ final double co;
+ final double so2;
+
+ //percent
+ final double pm2_5_p;
+ final double pm10_p;
+ final double o3_p;
+ final double no2_p;
+ final double co_p;
+ final double so2_p;
+
+ final double alder;
+ final double birch;
+ final double grass;
+ final double mugwort;
+ final double olive;
+ final double ragweed;
+
+ //hourly
+ final List pm2_5_h;
+ final List pm10_h;
+ final List no2_h;
+ final List o3_h;
+ final List co_h;
+ final List so2_h;
+
+ final String mainPollutant;
+
+ final List dailyAqi;
+
+ final int european_aqi;
+ final int us_aqi;
+
+ final double aod;
+ final String aod_desc;
+
+ final double dust;
+
+ const OMExtendedAqi({
+ required this.co,
+ required this.so2,
+ required this.alder,
+ required this.birch,
+ required this.grass,
+ required this.mugwort,
+ required this.olive,
+ required this.ragweed,
+
+ required this.aod,
+ required this.aod_desc,
+
+ required this.dust,
+
+ required this.european_aqi,
+ required this.us_aqi,
+
+ required this.no2_h,
+ required this.o3_h,
+ required this.pm2_5_h,
+ required this.pm10_h,
+ required this.co_h,
+ required this.so2_h,
+
+ required this.pm2_5_p,
+ required this.pm10_p,
+ required this.o3_p,
+ required this.no2_p,
+ required this.co_p,
+ required this.so2_p,
+
+ required this.dailyAqi,
+
+ required this.mainPollutant,
+ });
+
+ static Future fromJson(lat, lng, settings) async {
+ final params = {
+ "latitude": lat.toString(),
+ "longitude": lng.toString(),
+ "current": ['carbon_monoxide', 'sulphur_dioxide',
+ 'alder_pollen', 'birch_pollen', 'grass_pollen', 'mugwort_pollen', 'olive_pollen', 'ragweed_pollen',
+ 'aerosol_optical_depth', 'dust', 'european_aqi', 'us_aqi'],
+ "hourly" : ["pm10", "pm2_5", "nitrogen_dioxide", "ozone", "sulphur_dioxide", "carbon_monoxide"],
+ "timezone": "auto",
+ "forecast_days" : "5",
+ };
+ final url = Uri.https("air-quality-api.open-meteo.com", 'v1/air-quality', params);
+ var file = await cacheManager2.getSingleFile(url.toString(), key: "$lat, $lng, aqi open-meteo extended").timeout(const Duration(seconds: 3));
+ var response = await file.readAsString();
+ final item = jsonDecode(response);
+
+ final no2_h = List.from((item["hourly"]["nitrogen_dioxide"] as List?) ?.map((e) => (e as double?) ?? 0.0) ?? []);
+ final o3_h = List.from((item["hourly"]["ozone"] as List?) ?.map((e) => (e as double?) ?? 0.0) ?? []);
+ final pm2_5_h = List.from((item["hourly"]["pm2_5"] as List?) ?.map((e) => (e as double?) ?? 0.0) ?? []);
+ final pm10_h = List.from((item["hourly"]["pm10"] as List?) ?.map((e) => (e as double?) ?? 0.0) ?? []);
+ final co_h = List.from((item["hourly"]["carbon_monoxide"] as List?) ?.map((e) => (e as double?) ?? 0.0) ?? []);
+ final so2_h = List.from((item["hourly"]["sulphur_dioxide"] as List?) ?.map((e) => (e as double?) ?? 0.0) ?? []);
+
+
+ //determine the individual air quality indexes for each day using the hourly values of the different contaminants
+ // https://www.airnow.gov/publications/air-quality-index/technical-assistance-document-for-reporting-the-daily-aqi/
+
+ const List aqiCategories = [0, 51, 101, 151, 201, 301, 500];
+ const List pollutantNames = ["ozone", "pm2.5", "pm10", "carbon monoxide", "sulphur dioxide", "nitrogen dioxide"];
+ const List> breakpoints = [
+ [0, 0.055, 0.071, 0.086, 0.106, 0.201, 0.604], //o3
+ [0, 9.1, 35.5, 55.5, 125.5, 225.5, 325.4], //pm2.5
+ [0, 55, 155, 255, 355, 425, 604], //pm10
+ [0, 4.5, 9.5, 12.5, 15.5, 30.5, 50.4], //co
+ [0, 36, 76, 186, 305, 605, 1004], //so2
+ [0, 54, 101, 361, 650, 1250, 2049] //no2
+ ];
+
+ List dailyAqi = [];
+ String mainPollutant = "hehe";
+ for (int i = 0; i < item["hourly"]["pm2_5"].length / 24; i++) {
+ //some of the values in the documentation are in ppm so open-meteo's mg/m^3 data has to be converted to ppm
+ //https://teesing.com/en/tools/ppm-mg3-converter <- used this as a reference
+ //the division by 1000 is because is because we're converting micrograms to grams
+
+ List values = [
+ double.parse((o3_h.getRange(i * 24, (i + 1) * 24).reduce(max) * 24.45 / 48 / 1000).toStringAsFixed(3)),
+ double.parse(pm2_5_h.getRange(i * 24, (i + 1) * 24).reduce(max).toStringAsFixed(1)),
+ double.parse(pm10_h.getRange(i * 24, (i + 1) * 24).reduce(max).toStringAsFixed(0)),
+ double.parse((co_h.getRange(i * 24, (i + 1) * 24).reduce(max) * 24.45 / 28.01 / 1000).toStringAsFixed(1)),
+ double.parse((so2_h.getRange(i * 24, (i + 1) * 24).reduce(max) * 24.45 / 64.066 / 1000).toStringAsFixed(0)),
+ double.parse((no2_h.getRange(i * 24, (i + 1) * 24).reduce(max) * 24.45 / 46.0055 / 1000).toStringAsFixed(0)),
+ ];
+
+ List final_indexes = [];
+
+ for (int x = 0; x < 6; x++) {
+
+ double current = values[x];
+
+ //find the above and below breakpoints
+ double bp_hi = 1;
+ double bp_lo = 0;
+
+ int i_hi = 1;
+ int i_lo = 0;
+
+ for (int z = 0; z < breakpoints[x].length - 1; z++) {
+ if (current >= breakpoints[x][z]) {
+ bp_lo = breakpoints[x][z];
+ bp_hi = breakpoints[x][z + 1];
+
+ i_lo = aqiCategories[z];
+ i_hi = aqiCategories[z + 1];
+ }
+ }
+
+ int final_index = (((i_hi - i_lo) / (bp_hi - bp_lo)) * (current - bp_lo) + i_lo).round();
+ final_indexes.add(final_index);
+ }
+ int biggest = final_indexes.reduce(max);
+
+ //determine the main pollutant for today
+ if (i == 0) {
+ print(("final indexes", final_indexes));
+ mainPollutant = pollutantNames[final_indexes.indexOf(biggest)];
+ }
+
+ dailyAqi.add(biggest);
+ }
+
+ const aod_names = ["extremely clear", "very clear", "clear", "slightly hazy", "hazy", "very hazy", "extremely hazy"];
+ const aod_breakpoints = [0, 0.05, 0.1, 0.2, 0.4, 0.7, 1.0];
+
+ final aod_value = item["current"]["aerosol_optical_depth"];
+
+ int aod_index = 0;
+ for (int i = 0; i < aod_breakpoints.length; i++) {
+ if (aod_value > aod_breakpoints[i]) {
+ aod_index = i;
+ }
+ }
+
+ final String aod_desc = aod_names[aod_index];
+
+ return OMExtendedAqi(
+ co: item["current"]["carbon_monoxide"],
+ so2: item["current"]["sulphur_dioxide"],
+
+ alder: item["current"]["alder_pollen"] ?? -1,
+ birch: item["current"]["birch_pollen"] ?? -1,
+ grass: item["current"]["grass_pollen"] ?? -1,
+ mugwort: item["current"]["mugwort_pollen"] ?? -1,
+ olive: item["current"]["olive_pollen"] ?? -1,
+ ragweed: item["current"]["ragweed_pollen"] ?? -1,
+
+ aod: aod_value,
+ aod_desc: aod_desc,
+
+ dust: item["current"]["dust"],
+
+ no2_h: no2_h,
+ o3_h: o3_h,
+ pm2_5_h: pm2_5_h,
+ pm10_h: pm10_h,
+ co_h: co_h,
+ so2_h: so2_h,
+
+ mainPollutant: mainPollutant,
+
+ dailyAqi: dailyAqi,
+
+ european_aqi: item["current"]["european_aqi"],
+ us_aqi: item["current"]["us_aqi"],
+
+
+ //i am looking at the one before last because the last is basically only for calculating the high
+ //and not actually expected to be reached
+ o3_p: o3_h[0] * 24.45 / 48 / 1000 / breakpoints[0][breakpoints[0].length - 2] * 100,
+ pm2_5_p: pm2_5_h[0] / breakpoints[1][breakpoints[1].length - 2] * 100,
+ pm10_p: pm10_h[0] / breakpoints[2][breakpoints[2].length - 2] * 100,
+ co_p: co_h[0] * 24.45 / 28.01 / 1000 / breakpoints[3][breakpoints[3].length - 2] * 100,
+ so2_p: so2_h[0] * 24.45 / 64.066 / 1000 / breakpoints[4][breakpoints[4].length - 2] * 100,
+ no2_p: no2_h[0] * 24.45 / 46.0055 / 1000 / breakpoints[5][breakpoints[5].length - 2] * 100,
+ );
+ }
+}
+
Future OMGetWeatherData(lat, lng, real_loc, settings, placeName) async {
var OM = await OMRequestData(lat, lng, real_loc);
@@ -690,7 +921,7 @@ Future OMGetWeatherData(lat, lng, real_loc, settings, placeName) as
return WeatherData(
radar: await RainviewerRadar.getData(),
- aqi: await OMAqi.fromJson(oMBody, lat, lng, settings),
+ aqi: await OMAqi.fromJson(lat, lng, settings),
sunstatus: sunstatus,
minutely_15_precip: OM15MinutePrecip.fromJson(oMBody, settings),
diff --git a/lib/decoders/decode_mn.dart b/lib/decoders/decode_mn.dart
index 9e6466d..e75d4ab 100644
--- a/lib/decoders/decode_mn.dart
+++ b/lib/decoders/decode_mn.dart
@@ -73,14 +73,16 @@ int metNcalculateFeelsLike(double t, double r, double v) {
}
-String metNGetName(index, settings, item, start) {
+String metNGetName(index, settings, item, start, hourDif) {
if (index < 3) {
const names = ['Today', 'Tomorrow', 'Overmorrow'];
return translation(names[index], settings["Language"]);
}
String x = item["properties"]["timeseries"][start]["time"].split("T")[0];
+ String hour = item["properties"]["timeseries"][start]["time"].split("T")[1].split(":")[0];
List z = x.split("-");
- DateTime time = DateTime(int.parse(z[0]), int.parse(z[1]), int.parse(z[2]));
+ DateTime time_before = DateTime(int.parse(z[0]), int.parse(z[1]), int.parse(z[2]), int.parse(hour));
+ DateTime time = time_before.add(-Duration(hours: hourDif));
const weeks = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
String weekname = translation(weeks[time.weekday - 1], settings["Language"]);
return "$weekname, ${time.month}/${time.day}";
@@ -153,6 +155,7 @@ Future> MetNMakeRequest(double lat, double lng, String real_loc) a
"User-Agent": "Overmorrow weather (com.marotidev.overmorrow)"
};
final MnUrl = Uri.https("api.met.no", 'weatherapi/locationforecast/2.0/complete', MnParams);
+ print(MnUrl);
var MnFile = await cacheManager2.getSingleFile(MnUrl.toString(), key: "$real_loc, met.no", headers: headers).timeout(const Duration(seconds: 6));
var MnResponse = await MnFile.readAsString();
@@ -409,7 +412,7 @@ class MetNDay {
hourly_for_precip: hours,
total_precip: double.parse(precip.reduce((a, b) => a + b).toStringAsFixed(1)),
windspeed: (windspeeds.reduce((a, b) => a + b) / windspeeds.length).round(),
- name: metNGetName(index, settings, item, start),
+ name: metNGetName(index, settings, item, start, hourDif),
text: translation(weather_names[BIndex], settings["Language"]),
icon: metNIconCorrection(weather_names[BIndex]),
iconSize: oMIconSizeCorrection(weather_names[BIndex]),
@@ -551,6 +554,91 @@ class MetNSunstatus {
}
}
+
+class MetN15MinutePrecip { //met norway doesn't actaully have 15 minute forecast, but i figured i could just use the
+ //hourly data and just use some smoothing between the hours to emulate the 15 minutes
+ //still better than not having it
+ final String t_minus;
+ final double precip_sum;
+ final List precips;
+
+ const MetN15MinutePrecip({
+ required this.t_minus,
+ required this.precip_sum,
+ required this.precips,
+ });
+
+ static MetN15MinutePrecip fromJson(item, settings) {
+ int closest = 100;
+ int end = -1;
+ double sum = 0;
+
+ List precips = [];
+ List hourly = [];
+
+ for (int i = 0; i < 6; i++) {
+ double x = double.parse(item["properties"]["timeseries"][i]["data"]["next_1_hours"]["details"]["precipitation_amount"].toStringAsFixed(1));
+
+ if (x > 0.0) {
+ if (closest == 100) {
+ closest = i + 1;
+ }
+ if (i >= end) {
+ end = i + 1;
+ }
+ }
+
+ hourly.add(x);
+ }
+
+ //smooth the hours into 15 minute segments
+
+ for (int i = 0; i < hourly.length - 1; i++) {
+ double now = hourly[i];
+ double next = hourly[i + 1];
+
+ double dif = next - now;
+ for (double x = 0; x <= 1; x += 0.25) {
+ double g = now + (dif * x);
+ sum += g;
+ precips.add(g);
+ }
+ }
+
+ String t_minus = "";
+ if (closest != 100) {
+ if (closest <= 2) {
+ if (end <= 1) {
+ t_minus = translation("rain expected in the next 1 hour", settings["Language"]);
+ }
+ else {
+ String x = " $end ";
+ t_minus = translation("rain expected in the next x hours", settings["Language"]);
+ t_minus = t_minus.replaceAll(" x ", x);
+ }
+ }
+ else if (closest < 1) {
+ t_minus = translation("rain expected in 1 hour", settings["Language"]);
+ }
+ else {
+ String x = " $closest ";
+ t_minus = translation("rain expected in x hours", settings["Language"]);
+ t_minus = t_minus.replaceAll(" x ", x);
+ }
+ }
+
+ sum = max(sum, 0.1); //if there is rain then it shouldn't write 0
+
+ return MetN15MinutePrecip(
+ t_minus: t_minus,
+ precip_sum: unit_coversion(sum, settings["Precipitation"]),
+ precips: precips,
+ );
+
+ }
+
+}
+
Future MetNGetWeatherData(lat, lng, real_loc, settings, placeName) async {
DateTime localTime = await MetNGetLocalTime(lat, lng);
@@ -582,9 +670,9 @@ Future MetNGetWeatherData(lat, lng, real_loc, settings, placeName)
return WeatherData(
radar: await RainviewerRadar.getData(),
- aqi: await OMAqi.fromJson(MnBody, lat, lng, settings),
+ aqi: await OMAqi.fromJson(lat, lng, settings),
sunstatus: sunstatus,
- minutely_15_precip: const OM15MinutePrecip(t_minus: "", precip_sum: 0, precips: []), //because MetN has no 15 minute forecast
+ minutely_15_precip: MetN15MinutePrecip.fromJson(MnBody, settings),
current: await MetNCurrent.fromJson(MnBody, settings, real_loc, lat, lng),
days: days,
diff --git a/lib/decoders/decode_wapi.dart b/lib/decoders/decode_wapi.dart
index 997d494..560c732 100644
--- a/lib/decoders/decode_wapi.dart
+++ b/lib/decoders/decode_wapi.dart
@@ -589,10 +589,10 @@ class WapiAqi {
static WapiAqi fromJson(item) => WapiAqi(
aqi_index: item["current"]["air_quality"]["us-epa-index"],
- pm10: item["current"]["air_quality"]["pm10"],
- pm2_5: item["current"]["air_quality"]["pm2_5"],
- o3: item["current"]["air_quality"]["o3"],
- no2: item["current"]["air_quality"]["no2"],
+ pm10: double.parse(item["current"]["air_quality"]["pm10"].toStringAsFixed(1)),
+ pm2_5: double.parse(item["current"]["air_quality"]["pm2_5"].toStringAsFixed(1)),
+ o3: double.parse(item["current"]["air_quality"]["o3"].toStringAsFixed(1)),
+ no2: double.parse(item["current"]["air_quality"]["no2"].toStringAsFixed(1)),
aqi_title: ['good', 'fair', 'moderate', 'poor', 'very poor', 'unhealthy']
[item["current"]["air_quality"]["us-epa-index"] - 1],
@@ -608,6 +608,110 @@ class WapiAqi {
);
}
+
+class Wapi15MinutePrecip { //weatherapi doesn't actaully have 15 minute forecast, but i figured i could just use the
+ //hourly data and just use some smoothing between the hours to emulate the 15 minutes
+ //still better than not having it
+ final String t_minus;
+ final double precip_sum;
+ final List precips;
+
+ const Wapi15MinutePrecip({
+ required this.t_minus,
+ required this.precip_sum,
+ required this.precips,
+ });
+
+ static Wapi15MinutePrecip fromJson(item, settings) {
+ int closest = 100;
+ int end = -1;
+ double sum = 0;
+
+ List precips = [];
+ List hourly = [];
+
+ int day = 0;
+ int hour = 0;
+
+ int i = 0;
+
+ while (i < 6) {
+ if (item["forecast"]["forecastday"][day]["hour"].length > hour) {
+ double x;
+ if (i == 0) {
+ x = double.parse(item["current"]["precip_mm"].toStringAsFixed(1));
+ }
+ else {
+ x = double.parse(item["forecast"]["forecastday"][day]["hour"][hour]["precip_mm"].toStringAsFixed(1));
+ }
+
+ if (x > 0.0) {
+ if (closest == 100) {
+ closest = i + 1;
+ }
+ if (i >= end) {
+ end = i + 1;
+ }
+ }
+
+ hourly.add(x);
+
+ i += 1;
+ hour += 1;
+ }
+ else {
+ day += 1;
+ }
+ }
+
+ //smooth the hours into 15 minute segments
+
+ for (int i = 0; i < hourly.length - 1; i++) {
+ double now = hourly[i];
+ double next = hourly[i + 1];
+
+ double dif = next - now;
+ for (double x = 0; x <= 1; x += 0.25) {
+ double g = now + (dif * x);
+ sum += g;
+ precips.add(g);
+ }
+ }
+
+ String t_minus = "";
+ if (closest != 100) {
+ if (closest <= 2) {
+ if (end <= 1) {
+ t_minus = translation("rain expected in the next 1 hour", settings["Language"]);
+ }
+ else {
+ String x = " $end ";
+ t_minus = translation("rain expected in the next x hours", settings["Language"]);
+ t_minus = t_minus.replaceAll(" x ", x);
+ }
+ }
+ else if (closest < 1) {
+ t_minus = translation("rain expected in 1 hour", settings["Language"]);
+ }
+ else {
+ String x = " $closest ";
+ t_minus = translation("rain expected in x hours", settings["Language"]);
+ t_minus = t_minus.replaceAll(" x ", x);
+ }
+ }
+
+ sum = max(sum, 0.1); //if there is rain then it shouldn't write 0
+
+ return Wapi15MinutePrecip(
+ t_minus: t_minus,
+ precip_sum: unit_coversion(sum, settings["Precipitation"]),
+ precips: precips,
+ );
+
+ }
+
+}
+
Future WapiGetWeatherData(lat, lng, real_loc, settings, placeName) async {
var wapi = await WapiMakeRequest("$lat,$lng", real_loc);
@@ -643,7 +747,9 @@ Future WapiGetWeatherData(lat, lng, real_loc, settings, placeName)
fetch_datetime: fetch_datetime,
updatedTime: DateTime.now(),
localtime: WapiGetLocalTime(wapi_body),
- minutely_15_precip: const OM15MinutePrecip(t_minus: "", precip_sum: 0, precips: []), //because wapi doesn't have 15 minutely
+ //minutely_15_precip: const OM15MinutePrecip(t_minus: "", precip_sum: 0, precips: []), //because wapi doesn't have 15 minutely
+
+ minutely_15_precip: Wapi15MinutePrecip.fromJson(wapi_body, settings),
//image: Uimage,
);
diff --git a/lib/decoders/extra_info.dart b/lib/decoders/extra_info.dart
index 3005035..ee8bab4 100644
--- a/lib/decoders/extra_info.dart
+++ b/lib/decoders/extra_info.dart
@@ -143,7 +143,12 @@ Future> getUnsplashImage(String _text, String real_loc, double lat
//print(unsplash_body[index]["links"]["html"]);
final String userLink = (unsplash_body[index]["user"]["links"]["html"]) ?? "";
- final String username = unsplash_body[index]["user"]["name"] ?? "";
+
+ //i don't want emojis because they ruin the one color aspect of the app
+ String username = unsplash_body[index]["user"]["name"] ?? "";
+ final RegExp regExp = RegExp(r'(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])');
+ username = username.replaceAll(regExp, "_");
+
final String photoLink = unsplash_body[index]["links"]["html"] ?? "";
//final Color color = HexColor(unsplash_body[index]["color"]);
diff --git a/lib/languages.dart b/lib/languages.dart
index 20b4e69..c3131d8 100644
--- a/lib/languages.dart
+++ b/lib/languages.dart
@@ -1529,6 +1529,207 @@ Map> mainTranslate = {
'30λ',
],
+ "Air Quality": [
+ 'Air Quality',
+ 'Légszennyezettség',
+ 'Calidad del aire',
+ 'Qualité de l\'air',
+ 'Luftqualität',
+ 'Qualità dell\'aria',
+ 'Qualidade do ar',
+ 'Качество воздуха',
+ '空气质量',
+ '大気質',
+ 'Jakość powietrza',
+ 'Ποιότητα αέρα'
+ ],
+ "main pollutant": [
+ 'main pollutant',
+ 'fő szennyezőanyag',
+ 'principal contaminante',
+ 'polluant principal',
+ 'Hauptschadstoff',
+ 'inquinante principale',
+ 'principal poluente',
+ 'основной загрязнитель',
+ '主要污染物',
+ '主要汚染物質',
+ 'główny zanieczyszczający',
+ 'κύριος ρύπος'
+ ],
+ "Alder Pollen": [
+ 'Alder Pollen',
+ 'Éger pollen',
+ 'polen de aliso',
+ 'pollen d\'aulne',
+ 'Erlenpollen',
+ 'polline di ontano',
+ 'pólen de amieiro',
+ 'пыльца ольхи',
+ '桤木花粉',
+ 'ハンノキの花粉',
+ 'pyłek olchy',
+ 'γύρη σκλήθρου'
+ ],
+ "Birch Pollen": [
+ 'Birch Pollen',
+ 'Nyír pollen',
+ 'polen de abedul',
+ 'pollen de bouleau',
+ 'Birkenpollen',
+ 'polline di betulla',
+ 'pólen de bétula',
+ 'пыльца берёзы',
+ '桦木花粉',
+ 'カバノキの花粉',
+ 'pyłek brzozy',
+ 'γύρη σημύδας'
+ ],
+ "Grass Pollen": [
+ 'Grass Pollen',
+ 'Fű pollen',
+ 'polen de hierba',
+ 'pollen de graminées',
+ 'Gräserpollen',
+ 'polline d\'erba',
+ 'pólen de grama',
+ 'пыльца трав',
+ '草花粉',
+ '草の花粉',
+ 'pyłek traw',
+ 'γύρη χόρτου'
+ ],
+ "Mugwort Pollen": [
+ 'Mugwort Pollen',
+ 'Üröm pollen',
+ 'polen de artemisa',
+ 'pollen d\'armoise',
+ 'Beifußpollen',
+ 'polline di artemisia',
+ 'pólen de losna',
+ 'пыльца полыни',
+ '艾蒿花粉',
+ 'ヨモギの花粉',
+ 'pyłek bylicy',
+ 'γύρη αψιθιάς'
+ ],
+ "Olive Pollen": [
+ 'Olive Pollen',
+ 'Olíva pollen',
+ 'polen de olivo',
+ 'pollen d\'olivier',
+ 'Olivenpollen',
+ 'polline di ulivo',
+ 'pólen de oliveira',
+ 'пыльца оливы',
+ '橄榄花粉',
+ 'オリーブの花粉',
+ 'pyłek oliwny',
+ 'γύρη ελιάς'
+ ],
+ "Ragweed Pollen": [
+ 'Ragweed Pollen',
+ 'Parlagfű pollen',
+ 'polen de ambrosía',
+ 'pollen d\'ambroisie',
+ 'Ambrosiapollen',
+ 'polline di ambrosia',
+ 'pólen de ambrósia',
+ 'пыльца амброзии',
+ '豚草花粉',
+ 'ブタクサの花粉',
+ 'pyłek ambrozji',
+ 'γύρη αμβροσίας'
+ ],
+ "daily aqi": [
+ 'daily AQI',
+ 'napi AQI',
+ 'AQI diario',
+ 'AQI quotidien',
+ 'täglicher AQI',
+ 'AQI giornaliero',
+ 'AQI diário',
+ 'ежедневный AQI',
+ '每日空气质量指数',
+ '毎日のAQI',
+ 'dzienne AQI',
+ 'καθημερινό AQI'
+ ],
+
+ //day
+ "d": [
+ "d",
+ "n",
+ "d",
+ "j",
+ "T",
+ "g",
+ "d",
+ "д",
+ "天",
+ "日",
+ "d",
+ "η",
+ ],
+
+ "aerosol optical depth": [
+ "aerosol optical depth",
+ "aeroszoloptikai mélység",
+ "profundidad óptica de aerosol",
+ "profondeur optique d'aérosol",
+ "aerosol-optische tiefe",
+ "profondità ottica degli aerosol",
+ "profundidade óptica do aerossol",
+ "оптическая глубина аэрозолей",
+ "气溶胶光学深度",
+ "エアロゾル光学的厚さ",
+ "głębokość optyczna aerozolu",
+ "οπτικό βάθος αερολύματος"
+ ],
+ "dust": [
+ "dust",
+ "por",
+ "polvo",
+ "poussière",
+ "staub",
+ "polvere",
+ "poeira",
+ "пыль",
+ "灰尘",
+ "ほこり",
+ "pył",
+ "σκόνη"
+ ],
+ "european aqi": [
+ "european aqi",
+ "európai aqi",
+ "aqi europeo",
+ "aqi européen",
+ "eu aqi",
+ "aqi europeo",
+ "aqi europeu",
+ "евро aqi",
+ "欧盟aqi",
+ "eu aqi",
+ "europejski aqi",
+ "ευρωπαϊκός aqi"
+ ],
+ "united states aqi": [
+ "united states aqi",
+ "usa aqi",
+ "aqi de ee. uu.",
+ "aqi des états-unis",
+ "us aqi",
+ "aqi degli stati uniti",
+ "aqi dos eua",
+ "aqi сша",
+ "美国aqi",
+ "アメリカaqi",
+ "amerykański aqi",
+ "aqi ηπα"
+ ],
+
+
'Search translation': [ //used for getting the codes used for translation of city names
//If none are available then en (english) is the default
// for more info visit https://open-meteo.com/en/docs/geocoding-api
diff --git a/lib/main_screens.dart b/lib/main_screens.dart
index 245b0c1..f09303f 100644
--- a/lib/main_screens.dart
+++ b/lib/main_screens.dart
@@ -132,7 +132,7 @@ class _NewMainState extends State {
final Map widgetsMap = {
'sunstatus': NewSunriseSunset(data: data, key: Key(data.place), size: size,),
'rain indicator': NewRain15MinuteIndicator(data),
- 'air quality': NewAirQuality(data),
+ 'air quality': NewAirQuality(data, context),
'radar': RadarSmall(data: data, key: Key("${data.place}, ${data.current.surface}")),
'forecast': buildNewDays(data),
'daily': buildNewGlanceDay(data: data),
@@ -164,7 +164,7 @@ class _NewMainState extends State {
headerData: HeaderData(
//backgroundColor: WHITE,
blurContent: false,
- headerHeight: max(size.height * 0.525, 400),
+ headerHeight: max(size.height * 0.518, 400),
//we don't want it to be smaller than 400
header: ParrallaxBackground(image: data.current.image, key: Key(data.place),
color: data.current.surface == BLACK ? BLACK
@@ -395,7 +395,7 @@ Widget TabletLayout(data, updateLocation, context) {
children: [
NewSunriseSunset(data: data, key: Key(data.place), size: size,),
NewRain15MinuteIndicator(data),
- NewAirQuality(data),
+ NewAirQuality(data, context),
RadarSmall(data: data, key: Key("${data.place}, ${data.current.surface}")),
buildNewGlanceDay(data: data, key: Key("${data.place}, ${data.current.primary}"),),
Padding(
diff --git a/lib/new_displays.dart b/lib/new_displays.dart
index e2fd451..c55eb86 100644
--- a/lib/new_displays.dart
+++ b/lib/new_displays.dart
@@ -23,6 +23,7 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:overmorrow/settings_page.dart';
import 'package:overmorrow/ui_helper.dart';
+import 'aqi_page.dart';
import 'decoders/decode_OM.dart';
class WavePainter extends CustomPainter {
@@ -157,7 +158,7 @@ class _NewSunriseSunsetState extends State with SingleTickerPr
final textWidth = textPainter.width;
return Padding(
- padding: const EdgeInsets.only(left: 25, right: 25, bottom: 23),
+ padding: const EdgeInsets.only(left: 25, right: 25, bottom: 11),
child: Column(
children: [
Padding(
@@ -241,73 +242,98 @@ class _NewSunriseSunsetState extends State with SingleTickerPr
}
}
-Widget NewAirQuality(var data) {
+Widget NewAirQuality(var data, context) {
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20, bottom: 59),
- child: Column(
- children: [
- Align(
- alignment: Alignment.centerLeft,
- child: Padding(
- padding: const EdgeInsets.only(bottom: 6, left: 5),
- child: comfortatext(
- translation('air quality', data.settings["Language"]),
- 16,
- data.settings,
- color: data.current.onSurface),
+ child: GestureDetector(
+ behavior: HitTestBehavior.translucent,
+ onTap: (){
+ Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => AllergensPage(data: data))
+ );
+ },
+ child: Column(
+ children: [
+ Row(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 0, left: 5),
+ child: comfortatext(
+ translation('air quality', data.settings["Language"]),
+ 16,
+ data.settings,
+ color: data.current.onSurface),
+ ),
+ const Spacer(),
+ GestureDetector(
+ onTap: (){
+ Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => AllergensPage(data: data))
+ );
+ },
+ child: Padding(
+ padding: const EdgeInsets.only(bottom: 1),
+ child: SizedBox(
+ width: 40, height: 36,
+ child: Icon(Icons.keyboard_arrow_right, color: data.current.primary, size: 21,)),
+ )
+ ),
+ ],
),
- ),
- Row(
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 5, top: 5, right: 14),
- child: Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(15),
- color: data.current.containerLow),
- width: 65,
- height: 65,
- child: Center(
- child: comfortatext(
- data.aqi.aqi_index.toString(), 32, data.settings,
- color: data.current.primarySecond)),
+ Row(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 5, top: 0, right: 14),
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(15),
+ color: data.current.containerLow),
+ width: 71,
+ height: 71,
+ child: Center(
+ child: comfortatext(
+ data.aqi.aqi_index.toString(), 32, data.settings,
+ color: data.current.primarySecond)),
+ ),
),
- ),
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Align(
- alignment: Alignment.topLeft,
- child: comfortatext(
- data.aqi.aqi_title, 19, data.settings, color: data.current.primarySecond, align: TextAlign.left,
- weight: FontWeight.w500,
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Align(
+ alignment: Alignment.topLeft,
+ child: comfortatext(
+ data.aqi.aqi_title, 19, data.settings, color: data.current.primarySecond, align: TextAlign.left,
+ weight: FontWeight.w500,
+ ),
),
- ),
- Padding(
- padding: const EdgeInsets.only(top: 5, left: 2),
- child: comfortatext(data.aqi.aqi_desc, 14, data.settings,
- color: data.settings["Color mode"] == "light" ? data.current.primary : data.current.onSurface,
- weight: FontWeight.w500),
- ),
- ],
+ Padding(
+ padding: const EdgeInsets.only(top: 5, left: 2),
+ child: comfortatext(data.aqi.aqi_desc, 14, data.settings,
+ color: data.settings["Color mode"] == "light2" ? data.current.primary : data.current.onSurface,
+ weight: FontWeight.w500),
+ ),
+ ],
+ ),
),
- ),
- ],
- ),
- Padding(
- padding: const EdgeInsets.only(top: 15, left: 14, right: 14),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- NewAqiDataPoints("PM2.5", data.aqi.pm2_5, data),
- NewAqiDataPoints("PM10", data.aqi.pm10, data),
- NewAqiDataPoints("O3", data.aqi.o3, data),
- NewAqiDataPoints("NO2", data.aqi.no2, data),
],
),
- )
- ],
+ Padding(
+ padding: const EdgeInsets.only(top: 15, left: 14, right: 14),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ NewAqiDataPoints("PM2.5", data.aqi.pm2_5, data),
+ NewAqiDataPoints("PM10", data.aqi.pm10, data),
+ NewAqiDataPoints("O3", data.aqi.o3, data),
+ NewAqiDataPoints("NO2", data.aqi.no2, data),
+ ],
+ ),
+ )
+ ],
+ ),
),
);
}
@@ -316,7 +342,7 @@ Widget NewRain15MinuteIndicator(var data) {
return Visibility(
visible: data.minutely_15_precip.t_minus != "",
child: Padding(
- padding: const EdgeInsets.only(left: 21, right: 21, bottom: 38),
+ padding: const EdgeInsets.only(left: 21, right: 21, bottom: 33, top: 13),
child: Container(
decoration: BoxDecoration(
color: data.current.containerLow,
@@ -331,7 +357,7 @@ Widget NewRain15MinuteIndicator(var data) {
children: [
Padding(
padding:
- const EdgeInsets.only(left: 5, bottom: 2, right: 3),
+ const EdgeInsets.only(left: 5, bottom: 2, right: 3, top: 1),
child: Icon(
Icons.water_drop_outlined,
color: data.current.primary,
@@ -397,4 +423,4 @@ Widget NewRain15MinuteIndicator(var data) {
),
)
);
-}
\ No newline at end of file
+}
diff --git a/lib/new_forecast.dart b/lib/new_forecast.dart
index 3cf5002..68fbb6a 100644
--- a/lib/new_forecast.dart
+++ b/lib/new_forecast.dart
@@ -69,216 +69,222 @@ class _NewDayState extends State with AutomaticKeepAliveClientMixin {
Color highlight = state ? data.current.containerHigh : data.current.container;
- return Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ return Stack(
children: [
- Padding(
- padding: EdgeInsets.only(left: state ? 15 : 5, top: 0),
- child: Row(
- children: [
- comfortatext(
- day.name, 16,
- data.settings,
- color: data.current.onSurface),
- const Spacer(),
- Visibility(
- visible: state,
- child: Padding(
- padding: const EdgeInsets.only(right: 13),
- child: GestureDetector(
- child: Icon(Icons.expand_less, color: data
- .current.primaryLight, size: 20),
- onTap: () {
- HapticFeedback.selectionClick();
- onExpandTapped(index);
- },
- ),
- ),
- ),
- ],
- ),
+ if (state) GestureDetector(
+ behavior: HitTestBehavior.translucent,
+ onTap: () {
+ onExpandTapped(index);
+ },
+ child: const SizedBox(height: 70, width: double.infinity,)
),
- Padding(
- padding: const EdgeInsets.only(top: 18, left: 23, right: 25),
- child: Row(
- children: [
- SizedBox(
- width: 35,
- child: Icon(day.icon, size: 38.0 * day.iconSize, color: data.current.primary,)),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.only(left: 12.0, top: 3),
- child: comfortatext(day.text, 20, data.settings, color: data.current.onSurface,
- weight: FontWeight.w400),
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(top: 4),
- child: Row(
- children: [
- comfortatext(day.minmaxtemp.split("/")[0], 20, data.settings, color: data.current.primary),
- Padding(
- padding: const EdgeInsets.only(left: 5, right: 7),
- child: comfortatext("/", 19, data.settings, color: data.current.onSurface),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: EdgeInsets.only(left: state ? 15 : 5, top: state ? 20 : 0),
+ child: Row(
+ children: [
+ comfortatext(
+ day.name, 16,
+ data.settings,
+ color: data.current.onSurface),
+ const Spacer(),
+ if (state) Padding(
+ padding: const EdgeInsets.only(right: 13),
+ child: GestureDetector(
+ child: Icon(Icons.expand_less, color: data
+ .current.primaryLight, size: 20),
+ onTap: () {
+ onExpandTapped(index);
+ },
),
- comfortatext(day.minmaxtemp.split("/")[1], 20, data.settings, color: data.current.primary),
- ],
- ),
- )
- ],
- ),
- ),
- Visibility(
- visible: day.mm_precip > 0.1,
- child: RainWidget(data, day, highlight)
- ),
- Padding(
- padding: const EdgeInsets.only(left: 8, right: 8, top: 15, bottom: 10),
- child: Container(
- height: 85,
- padding: const EdgeInsets.only(top: 8, bottom: 8, left: 10, right: 10),
- decoration: BoxDecoration(
- //border: Border.all(width: 1, color: data.current.outline),
- color: state ? data.current.container : data.current.containerLow,
- borderRadius: BorderRadius.circular(18),
+ ),
+ ],
+ ),
),
- child: LayoutBuilder(
- builder: (BuildContext context, BoxConstraints constraints) {
- return GridView.count(
- padding: const EdgeInsets.all(0),
- physics: const NeverScrollableScrollPhysics(),
- crossAxisSpacing: 1,
- mainAxisSpacing: 1,
- crossAxisCount: 2,
- childAspectRatio: constraints.maxWidth / constraints.maxHeight,
+ Padding(
+ padding: const EdgeInsets.only(top: 18, left: 23, right: 25, bottom: 5),
+ child: Row(
+ children: [
+ SizedBox(
+ width: 35,
+ child: Icon(day.icon, size: 38.0 * day.iconSize, color: data.current.primary,)),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(left: 12.0, top: 3),
+ child: comfortatext(day.text, 20, data.settings, color: data.current.onSurface,
+ weight: FontWeight.w400),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 4),
+ child: Row(
children: [
+ comfortatext(day.minmaxtemp.split("/")[0], 20, data.settings, color: data.current.primary),
Padding(
- padding: const EdgeInsets.only(
- left: 8, right: 8),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Icon(Icons.water_drop_outlined,
- color: data.current.primaryLight, size: 21),
- Padding(
- padding: const EdgeInsets.only(left: 10, top: 3),
- child: comfortatext('${day.precip_prob}%', 18, data.settings,
- color: data.current.primary),
- ),
- ],
- ),
+ padding: const EdgeInsets.only(left: 5, right: 7),
+ child: comfortatext("/", 19, data.settings, color: data.current.onSurface),
),
- Padding(
- padding: const EdgeInsets.only(
- left: 8, right: 8),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Icon(
- Icons.water_drop, color: data.current.primaryLight, size: 21),
- Padding(
- padding: const EdgeInsets.only(top: 3, left: 10),
- child: comfortatext(day.total_precip.toString() +
- data.settings["Precipitation"], 18, data.settings,
- color: data.current.primary),
+ comfortatext(day.minmaxtemp.split("/")[1], 20, data.settings, color: data.current.primary),
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ Visibility(
+ visible: day.mm_precip > 0.1,
+ child: RainWidget(data, day, highlight, data.current.containerHigh)
+ ),
+ Padding(
+ padding: const EdgeInsets.only(left: 8, right: 8, top: 18, bottom: 10),
+ child: Container(
+ height: 85,
+ padding: const EdgeInsets.only(top: 8, bottom: 8, left: 10, right: 10),
+ decoration: BoxDecoration(
+ //border: Border.all(width: 1, color: data.current.outline),
+ color: state ? data.current.container : data.current.containerLow,
+ borderRadius: BorderRadius.circular(18),
+ ),
+ child: LayoutBuilder(
+ builder: (BuildContext context, BoxConstraints constraints) {
+ return GridView.count(
+ padding: const EdgeInsets.all(0),
+ physics: const NeverScrollableScrollPhysics(),
+ crossAxisSpacing: 1,
+ mainAxisSpacing: 1,
+ crossAxisCount: 2,
+ childAspectRatio: constraints.maxWidth / constraints.maxHeight,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 8, right: 8),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(Icons.water_drop_outlined,
+ color: data.current.primaryLight, size: 21),
+ Padding(
+ padding: const EdgeInsets.only(left: 10, top: 3),
+ child: comfortatext('${day.precip_prob}%', 18, data.settings,
+ color: data.current.primary),
+ ),
+ ],
),
- ],
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(
- left: 8, right: 8),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Icon(
- CupertinoIcons.wind, color: data.current.primaryLight, size: 21,),
- Padding(
- padding: const EdgeInsets.only(top: 3, left: 10),
- child: comfortatext('${day.windspeed} ${data
- .settings["Wind"]}', 18, data.settings,
- color: data.current.primary),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 8, right: 8),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(
+ Icons.water_drop, color: data.current.primaryLight, size: 21),
+ Padding(
+ padding: const EdgeInsets.only(top: 3, left: 10),
+ child: comfortatext(day.total_precip.toString() +
+ data.settings["Precipitation"], 18, data.settings,
+ color: data.current.primary),
+ ),
+ ],
),
- Padding(
- padding: const EdgeInsets.only(left: 5, right: 3),
- child: RotationTransition(
- turns: AlwaysStoppedAnimation(day.wind_dir / 360),
- child: Icon(CupertinoIcons.arrow_down_circle,
- color: data.current.primaryLight, size: 18,)
- )
+ ),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 8, right: 8),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(
+ CupertinoIcons.wind, color: data.current.primaryLight, size: 21,),
+ Padding(
+ padding: const EdgeInsets.only(top: 3, left: 10),
+ child: comfortatext('${day.windspeed} ${data
+ .settings["Wind"]}', 18, data.settings,
+ color: data.current.primary),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(left: 5, right: 3),
+ child: RotationTransition(
+ turns: AlwaysStoppedAnimation(day.wind_dir / 360),
+ child: Icon(CupertinoIcons.arrow_down_circle,
+ color: data.current.primaryLight, size: 18,)
+ )
+ ),
+ ],
),
- ],
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(
- left: 8, right: 8),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Icon(CupertinoIcons.sun_max,
- color: data.current.primaryLight, size: 21),
- Padding(
- padding: const EdgeInsets.only(top: 3, left: 10),
- child: comfortatext('${day.uv} UV', 18, data.settings,
- color: data.current.primary),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 8, right: 8),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(CupertinoIcons.sun_max,
+ color: data.current.primaryLight, size: 21),
+ Padding(
+ padding: const EdgeInsets.only(top: 3, left: 10),
+ child: comfortatext('${day.uv} UV', 18, data.settings,
+ color: data.current.primary),
+ ),
+ ],
),
- ],
- ),
- ),
- ]
- );
- }
- ),
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 15),
- child: Wrap(
- spacing: 5.0,
- children: List.generate(
- 4,
- (int index) {
-
-
- return ChoiceChip(
- elevation: 0.0,
- checkmarkColor: data.current.onPrimaryLight,
- color: WidgetStateProperty.resolveWith((states) {
- if (index == _value) {
- return data.current.primaryLighter;
+ ),
+ ]
+ );
}
- return state ? data.current.containerLow : data.current.surface;
- }),
- side: BorderSide(color: data.current.primaryLighter, width: 1.5),
- label: comfortatext(
- translation(['temp', 'precip', 'wind', 'uv'][index], data.settings["Language"]), 14, data.settings,
- color: _value == index ? data.current.onPrimaryLight : data.current.onSurface),
- selected: _value == index,
- onSelected: (bool selected) {
- _value = index;
- setState(() {
- HapticFeedback.selectionClick();
- _onItemTapped(index);
- });
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 15),
+ child: Wrap(
+ spacing: 5.0,
+ children: List.generate(
+ 4,
+ (int index) {
+
+ return ChoiceChip(
+ elevation: 0.0,
+ checkmarkColor: data.current.onPrimaryLight,
+ color: WidgetStateProperty.resolveWith((states) {
+ if (index == _value) {
+ return data.current.primaryLighter;
+ }
+ return state ? data.current.containerLow : data.current.surface;
+ }),
+ side: BorderSide(color: data.current.primaryLighter, width: 1.5),
+ label: comfortatext(
+ translation(['temp', 'precip', 'wind', 'uv'][index], data.settings["Language"]), 14, data.settings,
+ color: _value == index ? data.current.onPrimaryLight : data.current.onSurface),
+ selected: _value == index,
+ onSelected: (bool selected) {
+ _value = index;
+ setState(() {
+ HapticFeedback.lightImpact();
+ _onItemTapped(index);
+ });
+ },
+ );
},
- );
- },
- ).toList(),
- ),
- ),
- SizedBox(
- height: state? 280 : 260,
- child: PageView(
- physics: const NeverScrollableScrollPhysics(),
- controller: _pageController,
- children: [
- buildTemp(day.hourly, data, highlight),
- buildPrecip(day.hourly, data, data.current.containerHigh),
- WindReport(hours: day.hourly, data: data, highlight: data.current.containerHigh,),
- buildUV(day.hourly, data, highlight),
- ],
- ),
+ ).toList(),
+ ),
+ ),
+ SizedBox(
+ height: state? 280 : 260,
+ child: PageView(
+ physics: const NeverScrollableScrollPhysics(),
+ controller: _pageController,
+ children: [
+ buildTemp(day.hourly, data, data.current.containerHigh),
+ buildPrecip(day.hourly, data, data.current.containerHigh),
+ WindReport(hours: day.hourly, data: data, highlight: data.current.containerHigh,),
+ buildUV(day.hourly, data, data.current.containerHigh),
+ ],
+ ),
+ ),
+ ],
),
],
);
@@ -693,7 +699,7 @@ class _buildNewGlanceDayState extends State with AutomaticKee
void _onExpandTapped(int index) {
setState(() {
- HapticFeedback.selectionClick();
+ HapticFeedback.lightImpact();
expand[index] = !expand[index];
});
}
@@ -749,7 +755,7 @@ class _buildNewGlanceDayState extends State with AutomaticKee
: BorderRadius.circular(8),
color: data.current.containerLow),
child: Padding(
- padding: const EdgeInsets.only(top: 25, left: 3, right: 3),
+ padding: const EdgeInsets.only(top: 5, left: 3, right: 3),
child: NewDay(data: data, index: index, state: true,
onExpandTapped: _onExpandTapped, day: day,),
),
@@ -772,187 +778,192 @@ class _buildNewGlanceDayState extends State with AutomaticKee
Widget GlanceDayEntry(data, index, day, onExpandTapped) {
- return Container(
- decoration: BoxDecoration(
- borderRadius:
- index == 0 ? const BorderRadius.vertical(
- top: Radius.circular(18.0),
- bottom: Radius.circular(8))
- : index == data.days.length - 4 ? const BorderRadius
- .vertical(bottom: Radius.circular(18.0),
- top: Radius.circular(8))
- : BorderRadius.circular(8),
- color: data.current.containerLow),
- child: Column(
- children: [
- Row(
- children: [
- SizedBox(
- width: 60,
- height: 73,
- child: Padding(
- padding: const EdgeInsets.only(left: 18),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- comfortatext(day.name.split(", ")[0], 18,
- data.settings,
- color: data.current.primary),
- comfortatext(day.name.split(", ")[1], 14,
- data.settings,
- color: data.current.onSurface),
- ],
+ return GestureDetector(
+ onTap: () {
+ onExpandTapped(index);
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius:
+ index == 0 ? const BorderRadius.vertical(
+ top: Radius.circular(18.0),
+ bottom: Radius.circular(8))
+ : index == data.days.length - 4 ? const BorderRadius
+ .vertical(bottom: Radius.circular(18.0),
+ top: Radius.circular(8))
+ : BorderRadius.circular(8),
+ color: data.current.containerLow),
+ child: Column(
+ children: [
+ Row(
+ children: [
+ SizedBox(
+ width: 60,
+ height: 73,
+ child: Padding(
+ padding: const EdgeInsets.only(left: 18),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ comfortatext(day.name.split(", ")[0], 18,
+ data.settings,
+ color: data.current.primary),
+ comfortatext(day.name.split(", ")[1], 14,
+ data.settings,
+ color: data.current.onSurface),
+ ],
+ ),
),
),
- ),
- SizedBox(
- height: 30,
- width: 43,
- child: Icon(
- day.icon,
- color: data.current.primary,
- size: 31.0 * day.iconSize,
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(
- left: 10, top: 2, bottom: 2),
- child: Container(
- height: 56,
+ SizedBox(
+ height: 30,
width: 43,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(13),
- //border: Border.all(width: 1.5, color: data.current.primaryLight)
- color: data.current.primaryLighter
+ child: Icon(
+ day.icon,
+ color: data.current.primary,
+ size: 31.0 * day.iconSize,
),
- child: Row(
- children: [
- Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Padding(
- padding: const EdgeInsets.only(
- bottom: 2),
- child: Icon(Icons.keyboard_arrow_up,
- color: data.current.onPrimaryLight,
- size: 14,),
- ),
- Icon(Icons.keyboard_arrow_down,
- color: data.current.onPrimaryLight, size: 14,),
- ],
- ),
- Expanded(
- child: Column(
- mainAxisAlignment: MainAxisAlignment
- .center,
+ ),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 10, top: 2, bottom: 2),
+ child: Container(
+ height: 56,
+ width: 43,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(13),
+ //border: Border.all(width: 1.5, color: data.current.primaryLight)
+ color: data.current.primaryLighter
+ ),
+ child: Row(
+ children: [
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(
bottom: 2),
- child: comfortatext(
- day.minmaxtemp.split("/")[1], 14,
+ child: Icon(Icons.keyboard_arrow_up,
+ color: data.current.onPrimaryLight,
+ size: 14,),
+ ),
+ Icon(Icons.keyboard_arrow_down,
+ color: data.current.onPrimaryLight, size: 14,),
+ ],
+ ),
+ Expanded(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment
+ .center,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ bottom: 2),
+ child: comfortatext(
+ day.minmaxtemp.split("/")[1], 14,
+ data.settings,
+ color: data.current.onPrimaryLight),
+ ),
+ comfortatext(
+ day.minmaxtemp.split("/")[0], 14,
data.settings,
color: data.current.onPrimaryLight),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ Expanded(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 4),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(Icons.water_drop_outlined,
+ color: data.current.primaryLight,
+ size: 18,),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 2, right: 8),
+ child: comfortatext(
+ '${day.precip_prob}%', 17,
+ data.settings,
+ color: data.current.onSurface),
+ ),
+ Icon(
+ Icons.water_drop,
+ color: data.current.primaryLight,
+ size: 18,),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 2, right: 2),
+ child: comfortatext(
+ day.total_precip.toString() +
+ data.settings["Precipitation"],
+ 17, data.settings,
+ color: data.current.onSurface),
),
- comfortatext(
- day.minmaxtemp.split("/")[0], 14,
- data.settings,
- color: data.current.onPrimaryLight),
],
),
),
- ],
- ),
- ),
- ),
- Expanded(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Padding(
- padding: const EdgeInsets.only(bottom: 4),
- child: Row(
+ Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- Icon(Icons.water_drop_outlined,
+ Icon(
+ CupertinoIcons.wind,
color: data.current.primaryLight,
size: 18,),
Padding(
padding: const EdgeInsets.only(
- left: 2, right: 8),
+ left: 2, right: 2),
child: comfortatext(
- '${day.precip_prob}%', 17,
+ '${day.windspeed} ${data
+ .settings["Wind"]}', 17,
data.settings,
color: data.current.onSurface),
),
- Icon(
- Icons.water_drop,
- color: data.current.primaryLight,
- size: 18,),
Padding(
- padding: const EdgeInsets.only(
- left: 2, right: 2),
- child: comfortatext(
- day.total_precip.toString() +
- data.settings["Precipitation"],
- 17, data.settings,
- color: data.current.onSurface),
+ padding: const EdgeInsets.only(
+ left: 3, right: 3, bottom: 1),
+ child: RotationTransition(
+ turns: AlwaysStoppedAnimation(
+ day.wind_dir / 360),
+ child: Icon(
+ CupertinoIcons.arrow_down_circle,
+ color: data.current.primary,
+ size: 16,)
+ )
),
],
- ),
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Icon(
- CupertinoIcons.wind,
- color: data.current.primaryLight,
- size: 18,),
- Padding(
- padding: const EdgeInsets.only(
- left: 2, right: 2),
- child: comfortatext(
- '${day.windspeed} ${data
- .settings["Wind"]}', 17,
- data.settings,
- color: data.current.onSurface),
- ),
- Padding(
- padding: const EdgeInsets.only(
- left: 3, right: 3, bottom: 1),
- child: RotationTransition(
- turns: AlwaysStoppedAnimation(
- day.wind_dir / 360),
- child: Icon(
- CupertinoIcons.arrow_down_circle,
- color: data.current.primary,
- size: 16,)
- )
- ),
- ],
- )
- ],
+ )
+ ],
+ ),
),
- ),
- Padding(
- padding: const EdgeInsets.only(right: 10),
- child: SizedBox(
- width: 28,
- child: IconButton(
- padding: EdgeInsets.zero,
- constraints: const BoxConstraints(),
- icon: Icon(Icons.expand_more, color: data
- .current.primaryLight, size: 20,),
- onPressed: () {
- onExpandTapped(index);
- },
+ Padding(
+ padding: const EdgeInsets.only(right: 10),
+ child: SizedBox(
+ width: 28,
+ child: IconButton(
+ padding: EdgeInsets.zero,
+ constraints: const BoxConstraints(),
+ icon: Icon(Icons.expand_more, color: data
+ .current.primaryLight, size: 20,),
+ onPressed: () {
+ onExpandTapped(index);
+ },
+ ),
),
),
- ),
- ],
- ),
- ],
+ ],
+ ),
+ ],
+ ),
),
);
}
\ No newline at end of file
diff --git a/lib/radar.dart b/lib/radar.dart
index 53d0856..5529dff 100644
--- a/lib/radar.dart
+++ b/lib/radar.dart
@@ -139,7 +139,7 @@ class _RadarSmallState extends State {
color: data.current.surface,
borderRadius: BorderRadius.circular(18),
border: Border.all(
- width: 2.2, color: data.current.containerHigh)
+ width: 2.5, color: data.current.containerHigh)
),
child: Stack(
children: [
diff --git a/lib/search_screens.dart b/lib/search_screens.dart
index 4cfa759..48c5914 100644
--- a/lib/search_screens.dart
+++ b/lib/search_screens.dart
@@ -68,7 +68,7 @@ Widget searchBar(Color color, List recommend,
elevation: 0,
height: 62,
scrollPadding: const EdgeInsets.only(top: 16, bottom: 56),
- transitionDuration: const Duration(milliseconds: 800),
+ transitionDuration: const Duration(milliseconds: 700),
transitionCurve: Curves.easeInOut,
physics: const BouncingScrollPhysics(),
debounceDelay: const Duration(milliseconds: 500),
diff --git a/lib/ui_helper.dart b/lib/ui_helper.dart
index 8839553..7ecb34c 100644
--- a/lib/ui_helper.dart
+++ b/lib/ui_helper.dart
@@ -387,11 +387,11 @@ class DescriptionCircle extends StatelessWidget {
}
}
-Widget NewAqiDataPoints(String name, double value, var data) {
+Widget NewAqiDataPoints(String name, double value, var data, [double size = 15]) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- comfortatext(name, 15, data.settings, color: data.current.primary,
+ comfortatext(name, size, data.settings, color: data.current.primary,
align: TextAlign.end, weight: FontWeight.w500),
Padding(
padding: const EdgeInsets.all(3.0),
@@ -404,13 +404,13 @@ Widget NewAqiDataPoints(String name, double value, var data) {
),
),
),
- comfortatext(value.toString(), 15, data.settings, color: data.current.primarySecond,
+ comfortatext(value.toString(), size, data.settings, color: data.current.primarySecond,
align: TextAlign.end, weight: FontWeight.w600),
],
);
}
-Widget RainWidget(data, day, highlight) {
+Widget RainWidget(data, day, highlight, border) {
List hours = day.hourly_for_precip;
List precip = [];
@@ -429,7 +429,7 @@ Widget RainWidget(data, day, highlight) {
}
return Padding(
- padding: const EdgeInsets.only(left: 10, right: 10, top: 15),
+ padding: const EdgeInsets.only(left: 8, right: 8, top: 15),
child: Container(
constraints: const BoxConstraints(minWidth: 0, maxWidth: 450),
decoration: BoxDecoration(
@@ -437,14 +437,14 @@ Widget RainWidget(data, day, highlight) {
//color: data.current.containerLow,
border: data.settings["Color mode"] == "dark" || data.settings["Color mode"] == "light"
|| data.settings["Color mode"] == "auto"
- ? Border.all(width: 3, color: highlight)
+ ? Border.all(width: 2.6, color: border)
: Border.all(width: 1.6, color: data.current.primaryLight)
),
child: Column(
children: [
Padding(
- padding: const EdgeInsets.only(top: 14, right: 15, left: 18),
+ padding: const EdgeInsets.only(top: 16, right: 17, left: 17),
child: AspectRatio(
aspectRatio: 2.2,
child: MyChart(precip, data, highlight)
@@ -534,7 +534,8 @@ class BarChartPainter extends CustomPainter {
if (i <= smallerThan) {
canvas.drawArc(
Rect.fromCenter(
- center: Offset(x + barWidth * 0.5, start),
+ center: Offset(x + barWidth * 0.5, start - 0.05), //this small offset is there
+ // to remove the small line between the two half circles
height: barWidth * 0.8,
width: barWidth * 0.8,
),
diff --git a/lib/weather_refact.dart b/lib/weather_refact.dart
index e104143..4655234 100644
--- a/lib/weather_refact.dart
+++ b/lib/weather_refact.dart
@@ -383,14 +383,19 @@ Map textFilter = {
'black and white' : -100000,
'graffiti' : -2000,
'meat' : -5000,
+ 'toy' : -100000,
'man': -10000000, //trying to not have people in images
+ 'men': -10000000,
'male': -1000000,
'couple': -1000000,
'female': -1000000,
'human': -1000000,
'girl': -1000000,
'boy': -1000000,
+ 'kid': -1000000,
+ 'toddler': -1000000,
'woman': -10000000,
+ 'women': -10000000,
'person': -10000000,
'child': -10000000,
'crowd': -10000,