├── docs ├── CNAME ├── favicon.png ├── images │ ├── screen.jpg │ └── thumb.png ├── assets │ ├── css │ │ ├── images │ │ │ ├── overlay.jpg │ │ │ ├── pattern-size1.svg │ │ │ ├── pattern-size2.svg │ │ │ └── pattern-size3.svg │ │ └── noscript.css │ ├── webfonts │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.ttf │ │ ├── fa-brands-400.eot │ │ ├── fa-brands-400.ttf │ │ ├── fa-brands-400.woff │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.ttf │ │ ├── fa-solid-900.woff │ │ ├── fa-solid-900.woff2 │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.woff │ │ └── fa-regular-400.woff2 │ ├── sass │ │ ├── layout │ │ │ ├── _main.scss │ │ │ ├── _footer.scss │ │ │ └── _header.scss │ │ ├── components │ │ │ ├── _box.scss │ │ │ ├── _row.scss │ │ │ ├── _icon.scss │ │ │ ├── _list.scss │ │ │ ├── _section.scss │ │ │ ├── _spotlight.scss │ │ │ ├── _wrapper.scss │ │ │ ├── _actions.scss │ │ │ ├── _icons.scss │ │ │ ├── _table.scss │ │ │ ├── _button.scss │ │ │ ├── _image.scss │ │ │ └── _form.scss │ │ ├── noscript.scss │ │ ├── base │ │ │ ├── _page.scss │ │ │ ├── _reset.scss │ │ │ └── _typography.scss │ │ ├── main.scss │ │ └── libs │ │ │ ├── _vars.scss │ │ │ ├── _functions.scss │ │ │ ├── _mixins.scss │ │ │ ├── _html-grid.scss │ │ │ ├── _breakpoints.scss │ │ │ └── _vendor.scss │ └── js │ │ ├── jquery.scrolly.min.js │ │ ├── main.js │ │ ├── browser.min.js │ │ ├── breakpoints.min.js │ │ └── util.js ├── privacy-policy.html └── index.html ├── assets ├── icon │ └── icon.png └── images │ └── header.png ├── readme └── EasyWeather - 3000x1000.png ├── android ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values │ │ │ │ │ ├── colors.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── drawable-hdpi │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── drawable-mdpi │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── launcher_icon.xml │ │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── shyvum │ │ │ │ │ └── easyweather │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── build.gradle └── settings.gradle ├── lib ├── ui │ ├── widgets │ │ ├── weather_metrics_row.dart │ │ ├── weather_content.dart │ │ ├── weather_overview.dart │ │ ├── attribution_footer.dart │ │ ├── today_header.dart │ │ ├── custom_app_bar.dart │ │ ├── location_and_temperature.dart │ │ ├── weather_metric_card.dart │ │ └── weather_details.dart │ └── home_screen.dart ├── main.dart ├── repository │ └── service.dart └── model │ └── weather.dart ├── .gitignore ├── .metadata ├── flutter_launcher_icons.yaml ├── LICENSE ├── analysis_options.yaml ├── README.md ├── pubspec.yaml └── pubspec.lock /docs/CNAME: -------------------------------------------------------------------------------- 1 | easyweather.shyvum.in -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/favicon.png -------------------------------------------------------------------------------- /assets/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/assets/icon/icon.png -------------------------------------------------------------------------------- /docs/images/screen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/images/screen.jpg -------------------------------------------------------------------------------- /docs/images/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/images/thumb.png -------------------------------------------------------------------------------- /assets/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/assets/images/header.png -------------------------------------------------------------------------------- /docs/assets/css/images/overlay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/css/images/overlay.jpg -------------------------------------------------------------------------------- /readme/EasyWeather - 3000x1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/readme/EasyWeather - 3000x1000.png -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/docs/assets/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shyvum/EasyWeather/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/shyvum/easyweather/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.shyvum.easyweather 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jan 04 17:03:23 IST 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /docs/assets/sass/layout/_main.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Main */ 8 | 9 | #main { 10 | @include padding(4em, 0); 11 | 12 | // Small. 13 | @include breakpoint('<=small') { 14 | @include padding(3em, 0); 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(":app") 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/ui/widgets/weather_metrics_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class WeatherMetricsRow extends StatelessWidget { 4 | final Widget firstMetric; 5 | final Widget secondMetric; 6 | 7 | const WeatherMetricsRow({ 8 | super.key, 9 | required this.firstMetric, 10 | required this.secondMetric, 11 | }); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Row( 16 | mainAxisAlignment: MainAxisAlignment.spaceAround, 17 | children: [firstMetric, secondMetric], 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/assets/css/noscript.css: -------------------------------------------------------------------------------- 1 | /* 2 | Fractal by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | /* Header */ 8 | 9 | body.is-preload #header .content { 10 | -moz-transform: none; 11 | -webkit-transform: none; 12 | -ms-transform: none; 13 | transform: none; 14 | opacity: 1; 15 | } 16 | 17 | body.is-preload #header .image { 18 | -moz-transform: none; 19 | -webkit-transform: none; 20 | -ms-transform: none; 21 | transform: none; 22 | opacity: 1; 23 | } 24 | 25 | body.is-preload #header .image img { 26 | opacity: 1; 27 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_box.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Box */ 8 | 9 | .box { 10 | border-radius: _size(border-radius); 11 | border: solid _size(border-width) _palette(border); 12 | margin-bottom: _size(element-margin); 13 | padding: 1.5em; 14 | 15 | > :last-child, 16 | > :last-child > :last-child, 17 | > :last-child > :last-child > :last-child { 18 | margin-bottom: 0; 19 | } 20 | 21 | &.alt { 22 | border: 0; 23 | border-radius: 0; 24 | padding: 0; 25 | } 26 | } -------------------------------------------------------------------------------- /docs/assets/css/images/pattern-size1.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/assets/css/images/pattern-size2.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/assets/css/images/pattern-size3.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/assets/sass/noscript.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'libs/html-grid'; 7 | 8 | /* 9 | Fractal by HTML5 UP 10 | html5up.net | @ajlkn 11 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 12 | */ 13 | 14 | /* Header */ 15 | 16 | #header { 17 | body.is-preload & { 18 | .content { 19 | @include vendor('transform', 'none'); 20 | opacity: 1; 21 | } 22 | 23 | .image { 24 | @include vendor('transform', 'none'); 25 | opacity: 1; 26 | 27 | img { 28 | opacity: 1; 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'ui/home_screen.dart'; 4 | 5 | void main() { 6 | runApp(const MyApp()); 7 | } 8 | 9 | class MyApp extends StatelessWidget { 10 | const MyApp({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return MaterialApp( 15 | debugShowCheckedModeBanner: false, 16 | theme: ThemeData( 17 | scaffoldBackgroundColor: Colors.white, 18 | primaryColor: Colors.black, 19 | primaryIconTheme: const IconThemeData(color: Colors.black), 20 | colorScheme: ColorScheme.fromSwatch(), 21 | useMaterial3: true, 22 | ), 23 | home: const HomeScreen(), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/assets/sass/components/_row.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Row */ 8 | 9 | .row { 10 | @include html-grid(1.5em); 11 | 12 | @include breakpoint('<=xlarge') { 13 | @include html-grid(1.5em, 'xlarge'); 14 | } 15 | 16 | @include breakpoint('<=large') { 17 | @include html-grid(1.5em, 'large'); 18 | } 19 | 20 | @include breakpoint('<=medium') { 21 | @include html-grid(1.5em, 'medium'); 22 | } 23 | 24 | @include breakpoint('<=small') { 25 | @include html-grid(1.5em, 'small'); 26 | } 27 | 28 | @include breakpoint('<=xsmall') { 29 | @include html-grid(1.5em, 'xsmall'); 30 | } 31 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_icon.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icon */ 8 | 9 | .icon { 10 | @include icon; 11 | border-bottom: none; 12 | position: relative; 13 | 14 | > .label { 15 | display: none; 16 | } 17 | 18 | &:before { 19 | line-height: inherit; 20 | } 21 | 22 | &.solid { 23 | &:before { 24 | font-weight: 900; 25 | } 26 | } 27 | 28 | &.brands { 29 | &:before { 30 | font-family: 'Font Awesome 5 Brands'; 31 | } 32 | } 33 | } 34 | 35 | @mixin color-icon($p: null) { 36 | .icon { 37 | color: _palette($p, fg-light); 38 | } 39 | } 40 | 41 | @include color-icon; -------------------------------------------------------------------------------- /docs/assets/js/jquery.scrolly.min.js: -------------------------------------------------------------------------------- 1 | /* jquery.scrolly v1.0.0-dev | (c) @ajlkn | MIT licensed */ 2 | (function(e){function u(s,o){var u,a,f;if((u=e(s))[t]==0)return n;a=u[i]()[r];switch(o.anchor){case"middle":f=a-(e(window).height()-u.outerHeight())/2;break;default:case r:f=Math.max(a,0)}return typeof o[i]=="function"?f-=o[i]():f-=o[i],f}var t="length",n=null,r="top",i="offset",s="click.scrolly",o=e(window);e.fn.scrolly=function(i){var o,a,f,l,c=e(this);if(this[t]==0)return c;if(this[t]>1){for(o=0;o getWeather() async { 11 | var status = await Permission.location.status; 12 | if (!status.isGranted) { 13 | await Permission.location.request(); 14 | } 15 | if (status.isPermanentlyDenied) { 16 | openAppSettings(); 17 | } 18 | Position? position = await Geolocator.getCurrentPosition(); 19 | String url = 20 | 'https://api.openweathermap.org/data/2.5/weather?lat=${position.latitude}&lon=${position.longitude}&appid=6adf87802066a3ee22591eb3f8abfe0c'; 21 | final response = await http.get(Uri.parse(url)); 22 | return weatherFromJson(response.body); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | -------------------------------------------------------------------------------- /docs/assets/sass/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Footer */ 8 | 9 | #footer { 10 | @include color-typography(accent3); 11 | @include padding(4.5em, 0); 12 | color: _palette(accent3, fg-light); 13 | text-align: center; 14 | 15 | a { 16 | color: inherit; 17 | } 18 | 19 | .copyright { 20 | font-size: 0.8em; 21 | } 22 | 23 | // Medium. 24 | @include breakpoint('<=medium') { 25 | @include padding(4em, 4em); 26 | } 27 | 28 | // Small. 29 | @include breakpoint('<=small') { 30 | 31 | // Landscape. 32 | @include orientation(landscape) { 33 | @include padding(3em, 3em); 34 | } 35 | 36 | // Portrait. 37 | @include orientation(portrait) { 38 | @include padding(3em, 1.5em); 39 | } 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /lib/ui/widgets/weather_overview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../model/weather.dart'; 4 | import 'location_and_temperature.dart'; 5 | 6 | class WeatherOverview extends StatelessWidget { 7 | final Weather weather; 8 | 9 | const WeatherOverview({super.key, required this.weather}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container( 14 | alignment: Alignment.centerLeft, 15 | decoration: const BoxDecoration( 16 | color: Colors.white, 17 | image: DecorationImage( 18 | image: AssetImage('assets/images/header.png'), 19 | alignment: Alignment.centerRight, 20 | fit: BoxFit.fitHeight, 21 | ), 22 | ), 23 | child: Column( 24 | crossAxisAlignment: CrossAxisAlignment.start, 25 | mainAxisAlignment: MainAxisAlignment.center, 26 | children: [ 27 | LocationAndTemperature(weather: weather), 28 | ], 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "17025dd88227cd9532c33fa78f5250d548d87e9a" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a 17 | base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a 18 | - platform: android 19 | create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a 20 | base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /flutter_launcher_icons.yaml: -------------------------------------------------------------------------------- 1 | # flutter pub run flutter_launcher_icons 2 | flutter_launcher_icons: 3 | image_path: "assets/icon/icon.png" 4 | 5 | android: "ic_launcher" 6 | image_path_android: "assets/icon/icon.png" 7 | min_sdk_android: 28 # android min sdk min:16, default 21 8 | adaptive_icon_background: "#FFFFFF" 9 | adaptive_icon_foreground: "assets/icon/icon.png" 10 | adaptive_icon_monochrome: "assets/icon/icon.png" 11 | adaptive_icon_foreground_inset: 25 12 | 13 | ios: false 14 | # image_path_ios: "assets/icon/icon.png" 15 | remove_alpha_channel_ios: true 16 | # image_path_ios_dark_transparent: "assets/icon/icon_dark.png" 17 | # image_path_ios_tinted_grayscale: "assets/icon/icon_tinted.png" 18 | # desaturate_tinted_to_grayscale_ios: true 19 | 20 | web: 21 | generate: false 22 | image_path: "path/to/image.png" 23 | background_color: "#hexcode" 24 | theme_color: "#hexcode" 25 | 26 | windows: 27 | generate: false 28 | image_path: "path/to/image.png" 29 | icon_size: 48 # min:48, max:256, default: 48 30 | 31 | macos: 32 | generate: false 33 | image_path: "path/to/image.png" 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Shivam Goyal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/assets/sass/base/_page.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Basic */ 8 | 9 | // MSIE: Required for IEMobile. 10 | @-ms-viewport { 11 | width: device-width; 12 | } 13 | 14 | // MSIE: Prevents scrollbar from overlapping content. 15 | body { 16 | -ms-overflow-style: scrollbar; 17 | } 18 | 19 | // Ensures page width is always >=320px. 20 | @include breakpoint('<=xxsmall') { 21 | html, body { 22 | min-width: 320px; 23 | } 24 | } 25 | 26 | // Set box model to border-box. 27 | // Based on css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice 28 | html { 29 | box-sizing: border-box; 30 | } 31 | 32 | *, *:before, *:after { 33 | box-sizing: inherit; 34 | } 35 | 36 | body { 37 | background: _palette(bg); 38 | 39 | // Stops initial animations until page loads. 40 | &.is-preload { 41 | *, *:before, *:after { 42 | @include vendor('animation', 'none !important'); 43 | @include vendor('transition', 'none !important'); 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /lib/ui/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../model/weather.dart'; 4 | import '../repository/service.dart'; 5 | import 'widgets/custom_app_bar.dart'; 6 | import 'widgets/weather_content.dart'; 7 | 8 | class HomeScreen extends StatefulWidget { 9 | const HomeScreen({super.key}); 10 | 11 | @override 12 | State createState() => _HomeScreenState(); 13 | } 14 | 15 | class _HomeScreenState extends State { 16 | final WeatherService dataState = WeatherService(); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | appBar: CustomAppBar(), 22 | body: FutureBuilder( 23 | future: dataState.getWeather(), 24 | builder: (context, snapshot) { 25 | if (!snapshot.hasData) { 26 | return const Center( 27 | child: CircularProgressIndicator( 28 | color: Colors.black, 29 | backgroundColor: Colors.white, 30 | ), 31 | ); 32 | } 33 | 34 | return WeatherContent(weather: snapshot.data!); 35 | }, 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs/assets/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | Fractal by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | (function($) { 8 | 9 | var $window = $(window), 10 | $body = $('body'); 11 | 12 | // Breakpoints. 13 | breakpoints({ 14 | xlarge: [ '1281px', '1680px' ], 15 | large: [ '981px', '1280px' ], 16 | medium: [ '737px', '980px' ], 17 | small: [ '481px', '736px' ], 18 | xsmall: [ '361px', '480px' ], 19 | xxsmall: [ null, '360px' ] 20 | }); 21 | 22 | // Play initial animations on page load. 23 | $window.on('load', function() { 24 | window.setTimeout(function() { 25 | $body.removeClass('is-preload'); 26 | }, 100); 27 | }); 28 | 29 | // Mobile? 30 | if (browser.mobile) 31 | $body.addClass('is-mobile'); 32 | else { 33 | 34 | breakpoints.on('>medium', function() { 35 | $body.removeClass('is-mobile'); 36 | }); 37 | 38 | breakpoints.on('<=medium', function() { 39 | $body.addClass('is-mobile'); 40 | }); 41 | 42 | } 43 | 44 | // Scrolly. 45 | $('.scrolly') 46 | .scrolly({ 47 | speed: 1500 48 | }); 49 | 50 | })(jQuery); -------------------------------------------------------------------------------- /lib/ui/widgets/attribution_footer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | import 'package:url_launcher/url_launcher.dart'; 4 | 5 | class AttributionFooter extends StatelessWidget { 6 | AttributionFooter({super.key}); 7 | 8 | final Uri _attributionUrl = Uri.parse('https://openweathermap.org/'); 9 | 10 | Future _launchURL(Uri url) async { 11 | if (!await launchUrl(url)) { 12 | throw Exception('Could not launch $url'); 13 | } 14 | } 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Padding( 19 | padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 24.0), 20 | child: InkWell( 21 | onTap: () { 22 | _launchURL(_attributionUrl); 23 | }, 24 | highlightColor: Colors.transparent, 25 | splashColor: Colors.transparent, 26 | child: Text( 27 | 'openweathermap.org', 28 | style: GoogleFonts.titilliumWeb( 29 | color: Colors.grey, 30 | fontWeight: FontWeight.w500, 31 | fontSize: 12.0, 32 | decoration: TextDecoration.underline, 33 | ), 34 | ), 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/assets/sass/components/_list.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* List */ 8 | 9 | ol { 10 | list-style: decimal; 11 | margin: 0 0 _size(element-margin) 0; 12 | padding-left: 1.25em; 13 | 14 | li { 15 | padding-left: 0.25em; 16 | } 17 | } 18 | 19 | ul { 20 | list-style: disc; 21 | margin: 0 0 _size(element-margin) 0; 22 | padding-left: 1em; 23 | 24 | li { 25 | padding-left: 0.5em; 26 | } 27 | 28 | &.alt { 29 | list-style: none; 30 | padding-left: 0; 31 | 32 | li { 33 | border-top-style: solid; 34 | border-top-width: _size(border-width); 35 | padding: 0.5em 0; 36 | 37 | &:first-child { 38 | border-top: 0; 39 | padding-top: 0; 40 | } 41 | } 42 | } 43 | } 44 | 45 | dl { 46 | margin: 0 0 _size(element-margin) 0; 47 | 48 | dt { 49 | display: block; 50 | font-weight: _font(weight-bold); 51 | margin: 0 0 (_size(element-margin) * 0.5) 0; 52 | } 53 | 54 | dd { 55 | margin-left: _size(element-margin); 56 | } 57 | } 58 | 59 | @mixin color-list($p: null) { 60 | ul { 61 | &.alt { 62 | > li { 63 | border-top-color: _palette($p, border); 64 | } 65 | } 66 | } 67 | } 68 | 69 | @include color-list; -------------------------------------------------------------------------------- /docs/assets/sass/components/_section.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Section/Article */ 8 | 9 | section, article { 10 | &.special { 11 | text-align: center; 12 | } 13 | } 14 | 15 | header { 16 | p { 17 | position: relative; 18 | margin: 0 0 (_size(element-margin) * 0.75) 0; 19 | 20 | // Small. 21 | @include breakpoint('<=small') { 22 | br { 23 | display: none; 24 | } 25 | } 26 | 27 | } 28 | 29 | h2 + p { 30 | font-size: 1.25em; 31 | margin-top: (_size(element-margin) * -0.25); 32 | line-height: 1.5em; 33 | } 34 | 35 | h3 + p { 36 | font-size: 1.1em; 37 | margin-top: (_size(element-margin) * -0.125); 38 | line-height: 1.5em; 39 | } 40 | 41 | h4 + p, 42 | h5 + p, 43 | h6 + p { 44 | font-size: 0.9em; 45 | margin-top: (_size(element-margin) * -0.3); 46 | line-height: 1.5em; 47 | } 48 | 49 | &.major { 50 | margin: 0 0 (_size(element-margin) * 1.25) 0; 51 | text-align: center; 52 | 53 | &:after { 54 | content: ''; 55 | border-bottom: solid (_size(border-width) * 2) _palette(border); 56 | display: block; 57 | width: 4em; 58 | margin: _size(element-margin) auto 0 auto; 59 | } 60 | 61 | // Small. 62 | @include breakpoint('<=small') { 63 | margin: 0 0 _size(element-margin) 0; 64 | 65 | p { 66 | font-size: 1em; 67 | } 68 | } 69 | 70 | } 71 | } -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /lib/ui/widgets/today_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | import 'package:intl/intl.dart'; 4 | 5 | class TodayHeader extends StatelessWidget { 6 | const TodayHeader({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Padding( 11 | padding: const EdgeInsets.fromLTRB(16.0, 24.0, 16.0, 8.0), 12 | child: Row( 13 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 14 | children: [ 15 | Card( 16 | shape: RoundedRectangleBorder( 17 | borderRadius: BorderRadius.circular(4.0), 18 | ), 19 | elevation: 0, 20 | color: Colors.orange, 21 | child: Container( 22 | padding: 23 | const EdgeInsets.symmetric(horizontal: 32.0, vertical: 4.0), 24 | alignment: Alignment.center, 25 | child: Text( 26 | 'Today', 27 | style: GoogleFonts.titilliumWeb( 28 | color: Colors.white, 29 | fontWeight: FontWeight.w600, 30 | fontSize: 14.0, 31 | ), 32 | ), 33 | ), 34 | ), 35 | Text( 36 | DateFormat("EEE, d MMMM").format(DateTime.now()), 37 | style: GoogleFonts.titilliumWeb( 38 | color: Colors.black, 39 | fontSize: 18.0, 40 | fontWeight: FontWeight.w600, 41 | ), 42 | ), 43 | ], 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id "dev.flutter.flutter-gradle-plugin" 6 | } 7 | 8 | def keystoreProperties = new Properties() 9 | def keystorePropertiesFile = rootProject.file('key.properties') 10 | if (keystorePropertiesFile.exists()) { 11 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 12 | } 13 | 14 | android { 15 | namespace = "com.shyvum.easyweather" 16 | compileSdk = 35 17 | 18 | compileOptions { 19 | sourceCompatibility = JavaVersion.VERSION_17 20 | targetCompatibility = JavaVersion.VERSION_17 21 | } 22 | 23 | kotlinOptions { 24 | jvmTarget = JavaVersion.VERSION_17 25 | } 26 | 27 | defaultConfig { 28 | applicationId = "com.shyvum.easyweather" 29 | minSdk = 28 30 | targetSdk = 35 31 | versionCode = flutter.versionCode 32 | versionName = flutter.versionName 33 | } 34 | 35 | signingConfigs { 36 | release { 37 | keyAlias = keystoreProperties['keyAlias'] 38 | keyPassword = keystoreProperties['keyPassword'] 39 | storeFile = keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 40 | storePassword = keystoreProperties['storePassword'] 41 | } 42 | } 43 | 44 | buildTypes { 45 | release { 46 | signingConfig = signingConfigs.release 47 | } 48 | } 49 | } 50 | 51 | flutter { 52 | source = "../.." 53 | } 54 | -------------------------------------------------------------------------------- /docs/assets/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'libs/html-grid'; 7 | @import 'fontawesome-all.min.css'; 8 | 9 | /* 10 | Fractal by HTML5 UP 11 | html5up.net | @ajlkn 12 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 13 | */ 14 | 15 | // Breakpoints. 16 | 17 | @include breakpoints(( 18 | xlarge: ( 1281px, 1680px ), 19 | large: ( 981px, 1280px ), 20 | medium: ( 737px, 980px ), 21 | small: ( 481px, 736px ), 22 | xsmall: ( 361px, 480px ), 23 | xxsmall: ( null, 360px ) 24 | )); 25 | 26 | // Mixins. 27 | 28 | @mixin color($p: null) { 29 | @include color-button($p); 30 | @include color-form($p); 31 | @include color-icon($p); 32 | @include color-image($p); 33 | @include color-list($p); 34 | @include color-table($p); 35 | @include color-typography($p); 36 | } 37 | 38 | // Base. 39 | 40 | @import 'base/reset'; 41 | @import 'base/page'; 42 | @import 'base/typography'; 43 | 44 | // Component. 45 | 46 | @import 'components/row'; 47 | @import 'components/box'; 48 | @import 'components/button'; 49 | @import 'components/form'; 50 | @import 'components/icon'; 51 | @import 'components/image'; 52 | @import 'components/list'; 53 | @import 'components/actions'; 54 | @import 'components/icons'; 55 | @import 'components/section'; 56 | @import 'components/spotlight'; 57 | @import 'components/table'; 58 | @import 'components/wrapper'; 59 | 60 | // Layout. 61 | 62 | @import 'layout/header'; 63 | @import 'layout/main'; 64 | @import 'layout/footer'; -------------------------------------------------------------------------------- /lib/ui/widgets/custom_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | import 'package:url_launcher/url_launcher.dart'; 4 | 5 | class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { 6 | CustomAppBar({super.key}); 7 | 8 | final Uri _infoUrl = Uri.parse('https://github.com/shyvum/easyweather'); 9 | final Uri _shareUrl = Uri.parse( 10 | 'https://wa.me/?text=Get%20real-time%20weather%20updates%20with%20EasyWeather!%20Download%20now:%20https://play.google.com/store/apps/details?id=com.shyvum.easyweather'); 11 | 12 | Future _launchURL(Uri url) async { 13 | if (!await launchUrl(url)) { 14 | throw Exception('Could not launch $url'); 15 | } 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return AppBar( 21 | title: Text( 22 | 'EasyWeather', 23 | style: GoogleFonts.titilliumWeb( 24 | fontWeight: FontWeight.w600, 25 | color: Colors.black, 26 | fontSize: 24.0, 27 | ), 28 | ), 29 | actions: [ 30 | IconButton( 31 | icon: const Icon(Icons.info_outline_rounded, color: Colors.black), 32 | onPressed: () { 33 | _launchURL(_infoUrl); 34 | }, 35 | ), 36 | IconButton( 37 | icon: const Icon(Icons.share_outlined, color: Colors.black), 38 | onPressed: () { 39 | _launchURL(_shareUrl); 40 | }, 41 | ), 42 | ], 43 | backgroundColor: Colors.white, 44 | elevation: 32.0, 45 | ); 46 | } 47 | 48 | @override 49 | Size get preferredSize => const Size.fromHeight(kToolbarHeight); 50 | } 51 | -------------------------------------------------------------------------------- /docs/assets/sass/libs/_vars.scss: -------------------------------------------------------------------------------- 1 | // Misc. 2 | $misc: ( 3 | ); 4 | 5 | // Duration. 6 | $duration: ( 7 | transition: 0.2s 8 | ); 9 | 10 | // Size. 11 | $size: ( 12 | border-radius: 6px, 13 | border-width: 1px, 14 | element-height: 2.75em, 15 | element-margin: 2em 16 | ); 17 | 18 | // Font. 19 | $font: ( 20 | family: (Arial, Helvetica, sans-serif), 21 | family-fixed: ('Courier New', monospace), 22 | weight: normal, 23 | weight-bold: bold 24 | ); 25 | 26 | // Palette. 27 | $palette: ( 28 | bg: #ffffff, 29 | fg: #999999, 30 | fg-bold: #828282, 31 | fg-light: #e0e0e0, 32 | border: #e6e6e6, 33 | border-bg: rgba(144,144,144,0.075), 34 | border2: #d6d6d6, 35 | border2-bg: rgba(144,144,144,0.15), 36 | 37 | accent1: ( 38 | bg: #4696e5, 39 | fg: mix(#4696e5, #ffffff, 25%), 40 | fg-bold: #ffffff, 41 | fg-light: mix(#4696e5, #ffffff, 40%), 42 | border: rgba(255,255,255,0.25), 43 | border-bg: rgba(255,255,255,0.075), 44 | border2: rgba(255,255,255,0.5), 45 | border2-bg: rgba(255,255,255,0.2), 46 | invert: true 47 | ), 48 | 49 | accent2: ( 50 | bg: #fbfbfb, 51 | fg: #999999, 52 | fg-bold: #828282, 53 | fg-light: #e0e0e0, 54 | border: #e6e6e6, 55 | border-bg: rgba(144,144,144,0.075), 56 | border2: #d6d6d6, 57 | border2-bg: rgba(144,144,144,0.15), 58 | invert: false 59 | ), 60 | 61 | accent3: ( 62 | bg: #f7f7f7, 63 | fg: #999999, 64 | fg-bold: #828282, 65 | fg-light: #b0b0b0, 66 | border: #d6d6d6, 67 | border-bg: rgba(144,144,144,0.075), 68 | border2: #c6c6c6, 69 | border2-bg: rgba(144,144,144,0.15), 70 | invert: false 71 | ) 72 | ); -------------------------------------------------------------------------------- /docs/assets/sass/components/_spotlight.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Spotlight */ 8 | 9 | .spotlight { 10 | @include vendor('display', 'flex'); 11 | @include vendor('align-items', 'center'); 12 | margin: 0 0 _size(element-margin) 0; 13 | 14 | .image { 15 | width: 30%; 16 | border-radius: 100%; 17 | margin: 0 (_size(element-margin) * 1.5) 0 0; 18 | display: block; 19 | 20 | img { 21 | display: block; 22 | border-radius: 100%; 23 | width: 100%; 24 | } 25 | } 26 | 27 | .content { 28 | width: 70%; 29 | 30 | > :last-child { 31 | margin-bottom: 0; 32 | } 33 | } 34 | 35 | &:nth-child(2n) { 36 | @include vendor('flex-direction', 'row-reverse'); 37 | 38 | .image { 39 | margin: 0 0 0 (_size(element-margin) * 1.5); 40 | } 41 | 42 | .content { 43 | text-align: right; 44 | } 45 | } 46 | 47 | // Small. 48 | @include breakpoint('<=small') { 49 | 50 | // Landscape. 51 | @include orientation(landscape) { 52 | .image { 53 | margin: 0 _size(element-margin) 0 0; 54 | } 55 | 56 | &:nth-child(2n) { 57 | .image { 58 | margin: 0 0 0 _size(element-margin); 59 | } 60 | } 61 | } 62 | 63 | // Portrait. 64 | @include orientation(portrait) { 65 | @include vendor('flex-direction', 'column !important'); 66 | 67 | .image { 68 | width: 100%; 69 | max-width: 60%; 70 | margin: 0 0 _size(element-margin) 0 !important; 71 | } 72 | 73 | .content { 74 | width: 100%; 75 | text-align: center !important; 76 | } 77 | } 78 | 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /docs/assets/js/browser.min.js: -------------------------------------------------------------------------------- 1 | /* browser.js v1.0 | @ajlkn | MIT licensed */ 2 | var browser=function(){"use strict";var e={name:null,version:null,os:null,osVersion:null,touch:null,mobile:null,_canUse:null,canUse:function(n){e._canUse||(e._canUse=document.createElement("div"));var o=e._canUse.style,r=n.charAt(0).toUpperCase()+n.slice(1);return n in o||"Moz"+r in o||"Webkit"+r in o||"O"+r in o||"ms"+r in o},init:function(){var n,o,r,i,t=navigator.userAgent;for(n="other",o=0,r=[["firefox",/Firefox\/([0-9\.]+)/],["bb",/BlackBerry.+Version\/([0-9\.]+)/],["bb",/BB[0-9]+.+Version\/([0-9\.]+)/],["opera",/OPR\/([0-9\.]+)/],["opera",/Opera\/([0-9\.]+)/],["edge",/Edge\/([0-9\.]+)/],["safari",/Version\/([0-9\.]+).+Safari/],["chrome",/Chrome\/([0-9\.]+)/],["ie",/MSIE ([0-9]+)/],["ie",/Trident\/.+rv:([0-9]+)/]],i=0;i0:!!("ontouchstart"in window),e.mobile="wp"==e.os||"android"==e.os||"ios"==e.os||"bb"==e.os}};return e.init(),e}();!function(e,n){"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?module.exports=n():e.browser=n()}(this,function(){return browser}); 3 | -------------------------------------------------------------------------------- /docs/assets/sass/base/_reset.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | // Reset. 8 | // Based on meyerweb.com/eric/tools/css/reset (v2.0 | 20110126 | License: public domain) 9 | 10 | html, body, div, span, applet, object, 11 | iframe, h1, h2, h3, h4, h5, h6, p, blockquote, 12 | pre, a, abbr, acronym, address, big, cite, 13 | code, del, dfn, em, img, ins, kbd, q, s, samp, 14 | small, strike, strong, sub, sup, tt, var, b, 15 | u, i, center, dl, dt, dd, ol, ul, li, fieldset, 16 | form, label, legend, table, caption, tbody, 17 | tfoot, thead, tr, th, td, article, aside, 18 | canvas, details, embed, figure, figcaption, 19 | footer, header, hgroup, menu, nav, output, ruby, 20 | section, summary, time, mark, audio, video { 21 | margin: 0; 22 | padding: 0; 23 | border: 0; 24 | font-size: 100%; 25 | font: inherit; 26 | vertical-align: baseline; 27 | } 28 | 29 | article, aside, details, figcaption, figure, 30 | footer, header, hgroup, menu, nav, section { 31 | display: block; 32 | } 33 | 34 | body { 35 | line-height: 1; 36 | } 37 | 38 | ol, ul { 39 | list-style:none; 40 | } 41 | 42 | blockquote, q { 43 | quotes: none; 44 | 45 | &:before, 46 | &:after { 47 | content: ''; 48 | content: none; 49 | } 50 | } 51 | 52 | table { 53 | border-collapse: collapse; 54 | border-spacing: 0; 55 | } 56 | 57 | body { 58 | -webkit-text-size-adjust: none; 59 | } 60 | 61 | mark { 62 | background-color: transparent; 63 | color: inherit; 64 | } 65 | 66 | input::-moz-focus-inner { 67 | border: 0; 68 | padding: 0; 69 | } 70 | 71 | input, select, textarea { 72 | -moz-appearance: none; 73 | -webkit-appearance: none; 74 | -ms-appearance: none; 75 | appearance: none; 76 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_wrapper.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Wrapper */ 8 | 9 | .wrapper { 10 | @include padding(4.5em, 0); 11 | background-color: _palette(bg); 12 | border-bottom: solid (_size(border-width) * 2) lighten(_palette(border), 3); 13 | 14 | > .inner { 15 | margin-left: auto; 16 | margin-right: auto; 17 | width: 50em; 18 | 19 | &.alt { 20 | > * { 21 | border-top: solid (_size(border-width) * 2) lighten(_palette(border), 3); 22 | margin-bottom: 0; 23 | margin-top: 3em; 24 | padding-top: 3em; 25 | 26 | &:first-child { 27 | border-top: 0; 28 | margin-top: 0; 29 | padding-top: 0; 30 | } 31 | } 32 | } 33 | } 34 | 35 | &.style1 { 36 | @include color(accent1); 37 | } 38 | 39 | &.style2 { 40 | @include color(accent2); 41 | } 42 | 43 | &.style3 { 44 | @include color(accent3); 45 | } 46 | 47 | // Medium. 48 | @include breakpoint('<=medium') { 49 | @include padding(4em, 4em); 50 | 51 | > .inner { 52 | width: 100%; 53 | } 54 | } 55 | 56 | // Small. 57 | @include breakpoint('<=small') { 58 | 59 | // Landscape. 60 | @include orientation(landscape) { 61 | @include padding(3em, 3em); 62 | } 63 | 64 | // Portrait. 65 | @include orientation(portrait) { 66 | @include padding(3em, 1.5em); 67 | } 68 | 69 | } 70 | 71 | // XSmall. 72 | @include breakpoint('<=xsmall') { 73 | 74 | // Landscape. 75 | @include orientation(landscape) { 76 | @include padding(2em, 1.5em); 77 | } 78 | 79 | // Portrait. 80 | @include orientation(portrait) { 81 | @include padding(2em, 1em); 82 | } 83 | 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /lib/ui/widgets/location_and_temperature.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | 4 | import '../../model/weather.dart'; 5 | 6 | class LocationAndTemperature extends StatelessWidget { 7 | final Weather weather; 8 | 9 | const LocationAndTemperature({super.key, required this.weather}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Padding( 14 | padding: const EdgeInsets.all(16.0), 15 | child: Row( 16 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 17 | children: [ 18 | Column( 19 | crossAxisAlignment: CrossAxisAlignment.start, 20 | children: [ 21 | Text( 22 | weather.name, 23 | style: GoogleFonts.titilliumWeb( 24 | color: Colors.black, 25 | fontSize: 42.0, 26 | fontWeight: FontWeight.w600, 27 | ), 28 | overflow: TextOverflow.ellipsis, 29 | ), 30 | const SizedBox(height: 8.0), 31 | Text( 32 | 'Day ${(weather.main.tempMax - 273.15).toStringAsFixed(1)}° ↑\nNight ${(weather.main.tempMin - 273.15).toStringAsFixed(1)}° ↓', 33 | style: GoogleFonts.titilliumWeb( 34 | color: Colors.black, 35 | fontSize: 18.0, 36 | fontWeight: FontWeight.w600, 37 | ), 38 | ), 39 | ], 40 | ), 41 | Text( 42 | '${(weather.main.temp - 273.15).toInt()}°', 43 | style: GoogleFonts.titilliumWeb( 44 | color: Colors.black, 45 | fontSize: 72.0, 46 | fontWeight: FontWeight.w600, 47 | ), 48 | overflow: TextOverflow.ellipsis, 49 | ), 50 | ], 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/ui/widgets/weather_metric_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | 4 | class WeatherMetricCard extends StatelessWidget { 5 | final String title; 6 | final String value; 7 | final String iconId; 8 | final Color color; 9 | 10 | const WeatherMetricCard({ 11 | super.key, 12 | required this.title, 13 | required this.value, 14 | required this.iconId, 15 | required this.color, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Card( 21 | shape: RoundedRectangleBorder( 22 | side: BorderSide(color: color, width: 2.0), 23 | borderRadius: BorderRadius.circular(4.0), 24 | ), 25 | elevation: 0.0, 26 | color: Colors.white, 27 | child: Padding( 28 | padding: const EdgeInsets.all(16.0), 29 | child: Column( 30 | mainAxisAlignment: MainAxisAlignment.center, 31 | children: [ 32 | Container( 33 | height: 4.0, 34 | decoration: BoxDecoration( 35 | borderRadius: BorderRadius.circular(5.0), 36 | color: color, 37 | ), 38 | ), 39 | const SizedBox(height: 16.0), 40 | Row( 41 | mainAxisAlignment: MainAxisAlignment.center, 42 | children: [ 43 | Image.network('https://img.icons8.com/?size=128&id=$iconId&format=png', width: 20), 44 | const SizedBox(width: 6.0), 45 | Text( 46 | title, 47 | style: GoogleFonts.titilliumWeb( 48 | fontWeight: FontWeight.w500, 49 | fontSize: 16.0, 50 | ), 51 | ), 52 | ], 53 | ), 54 | const SizedBox(height: 4.0), 55 | Text( 56 | value, 57 | style: GoogleFonts.titilliumWeb( 58 | fontWeight: FontWeight.w700, 59 | fontSize: 24.0, 60 | ), 61 | ), 62 | const Spacer(), 63 | ], 64 | ), 65 | ), 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/assets/sass/components/_actions.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Actions */ 8 | 9 | ul.actions { 10 | @include vendor('display', 'flex'); 11 | cursor: default; 12 | list-style: none; 13 | margin-left: (_size(element-margin) * -0.5); 14 | padding-left: 0; 15 | 16 | li { 17 | padding: 0 0 0 (_size(element-margin) * 0.5); 18 | vertical-align: middle; 19 | } 20 | 21 | &.special { 22 | @include vendor('justify-content', 'center'); 23 | width: 100%; 24 | margin-left: 0; 25 | 26 | li { 27 | &:first-child { 28 | padding-left: 0; 29 | } 30 | } 31 | } 32 | 33 | &.stacked { 34 | @include vendor('flex-direction', 'column'); 35 | margin-left: 0; 36 | 37 | li { 38 | padding: (_size(element-margin) * 0.65) 0 0 0; 39 | 40 | &:first-child { 41 | padding-top: 0; 42 | } 43 | } 44 | } 45 | 46 | &.fit { 47 | width: calc(100% + #{_size(element-margin) * 0.5}); 48 | 49 | li { 50 | @include vendor('flex-grow', '1'); 51 | @include vendor('flex-shrink', '1'); 52 | width: 100%; 53 | 54 | > * { 55 | width: 100%; 56 | } 57 | } 58 | 59 | &.stacked { 60 | width: 100%; 61 | } 62 | } 63 | 64 | @include breakpoint('<=xsmall') { 65 | &:not(.fixed) { 66 | @include vendor('flex-direction', 'column'); 67 | margin-left: 0; 68 | width: 100% !important; 69 | 70 | li { 71 | @include vendor('flex-grow', '1'); 72 | @include vendor('flex-shrink', '1'); 73 | padding: (_size(element-margin) * 0.5) 0 0 0; 74 | text-align: center; 75 | width: 100%; 76 | 77 | > * { 78 | width: 100%; 79 | } 80 | 81 | &:first-child { 82 | padding-top: 0; 83 | } 84 | 85 | input[type="submit"], 86 | input[type="reset"], 87 | input[type="button"], 88 | button, 89 | .button { 90 | width: 100%; 91 | 92 | &.icon { 93 | &:before { 94 | margin-left: -0.5em; 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /docs/assets/sass/libs/_functions.scss: -------------------------------------------------------------------------------- 1 | /// Removes a specific item from a list. 2 | /// @author Hugo Giraudel 3 | /// @param {list} $list List. 4 | /// @param {integer} $index Index. 5 | /// @return {list} Updated list. 6 | @function remove-nth($list, $index) { 7 | 8 | $result: null; 9 | 10 | @if type-of($index) != number { 11 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 12 | } 13 | @else if $index == 0 { 14 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 15 | } 16 | @else if abs($index) > length($list) { 17 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 18 | } 19 | @else { 20 | 21 | $result: (); 22 | $index: if($index < 0, length($list) + $index + 1, $index); 23 | 24 | @for $i from 1 through length($list) { 25 | 26 | @if $i != $index { 27 | $result: append($result, nth($list, $i)); 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | @return $result; 35 | 36 | } 37 | 38 | /// Gets a value from a map. 39 | /// @author Hugo Giraudel 40 | /// @param {map} $map Map. 41 | /// @param {string} $keys Key(s). 42 | /// @return {string} Value. 43 | @function val($map, $keys...) { 44 | 45 | @if nth($keys, 1) == null { 46 | $keys: remove-nth($keys, 1); 47 | } 48 | 49 | @each $key in $keys { 50 | $map: map-get($map, $key); 51 | } 52 | 53 | @return $map; 54 | 55 | } 56 | 57 | /// Gets a duration value. 58 | /// @param {string} $keys Key(s). 59 | /// @return {string} Value. 60 | @function _duration($keys...) { 61 | @return val($duration, $keys...); 62 | } 63 | 64 | /// Gets a font value. 65 | /// @param {string} $keys Key(s). 66 | /// @return {string} Value. 67 | @function _font($keys...) { 68 | @return val($font, $keys...); 69 | } 70 | 71 | /// Gets a misc value. 72 | /// @param {string} $keys Key(s). 73 | /// @return {string} Value. 74 | @function _misc($keys...) { 75 | @return val($misc, $keys...); 76 | } 77 | 78 | /// Gets a palette value. 79 | /// @param {string} $keys Key(s). 80 | /// @return {string} Value. 81 | @function _palette($keys...) { 82 | @return val($palette, $keys...); 83 | } 84 | 85 | /// Gets a size value. 86 | /// @param {string} $keys Key(s). 87 | /// @return {string} Value. 88 | @function _size($keys...) { 89 | @return val($size, $keys...); 90 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_icons.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icons */ 8 | 9 | ul.icons { 10 | cursor: default; 11 | list-style: none; 12 | padding-left: 0; 13 | 14 | li { 15 | display: inline-block; 16 | padding: 0 1.5em 0 0; 17 | 18 | &:last-child { 19 | padding-right: 0 !important; 20 | } 21 | 22 | .icon { 23 | &:before { 24 | font-size: 1.75rem; 25 | } 26 | } 27 | } 28 | 29 | &.major { 30 | li { 31 | padding: 0 2.5em 0 0; 32 | 33 | .icon { 34 | &:before { 35 | font-size: 2.75rem; 36 | } 37 | } 38 | } 39 | 40 | // Small. 41 | @include breakpoint('<=small') { 42 | li { 43 | padding: 0 1.25em 0 0; 44 | 45 | .icon { 46 | &:before { 47 | font-size: 1.75rem; 48 | } 49 | } 50 | } 51 | } 52 | 53 | } 54 | 55 | &.labeled { 56 | li { 57 | margin: 1em 0; 58 | padding: 0 2em 0 0; 59 | 60 | .icon { 61 | &:before { 62 | vertical-align: middle; 63 | font-size: 2.25rem; 64 | margin-right: 0.5em; 65 | } 66 | 67 | .label { 68 | display: inline-block; 69 | vertical-align: middle; 70 | color: _palette(fg); 71 | } 72 | } 73 | } 74 | 75 | // Small. 76 | @include breakpoint('<=small') { 77 | 78 | // Portrait. 79 | @include orientation(portrait) { 80 | text-align: left; 81 | margin-right: auto; 82 | margin-left: auto; 83 | display: inline-block; 84 | white-space: nowrap; 85 | 86 | li { 87 | display: block; 88 | padding: 0; 89 | margin: 1.5em 0 0 0; 90 | 91 | .icon { 92 | &:before { 93 | width: 1.5em; 94 | display: block; 95 | float: left; 96 | margin-right: 0; 97 | } 98 | 99 | &:after { 100 | content: ''; 101 | display: block; 102 | clear: both; 103 | } 104 | } 105 | 106 | &:first-child { 107 | margin-top: 0; 108 | } 109 | } 110 | } 111 | 112 | } 113 | 114 | } 115 | } -------------------------------------------------------------------------------- /docs/assets/js/breakpoints.min.js: -------------------------------------------------------------------------------- 1 | /* breakpoints.js v1.0 | @ajlkn | MIT licensed */ 2 | var breakpoints=function(){"use strict";function e(e){t.init(e)}var t={list:null,media:{},events:[],init:function(e){t.list=e,window.addEventListener("resize",t.poll),window.addEventListener("orientationchange",t.poll),window.addEventListener("load",t.poll),window.addEventListener("fullscreenchange",t.poll)},active:function(e){var n,a,s,i,r,d,c;if(!(e in t.media)){if(">="==e.substr(0,2)?(a="gte",n=e.substr(2)):"<="==e.substr(0,2)?(a="lte",n=e.substr(2)):">"==e.substr(0,1)?(a="gt",n=e.substr(1)):"<"==e.substr(0,1)?(a="lt",n=e.substr(1)):"!"==e.substr(0,1)?(a="not",n=e.substr(1)):(a="eq",n=e),n&&n in t.list)if(i=t.list[n],Array.isArray(i)){if(r=parseInt(i[0]),d=parseInt(i[1]),isNaN(r)){if(isNaN(d))return;c=i[1].substr(String(d).length)}else c=i[0].substr(String(r).length);if(isNaN(r))switch(a){case"gte":s="screen";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: -1px)";break;case"not":s="screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (max-width: "+d+c+")"}else if(isNaN(d))switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen";break;case"gt":s="screen and (max-width: -1px)";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+")";break;default:s="screen and (min-width: "+r+c+")"}else switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+"), screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (min-width: "+r+c+") and (max-width: "+d+c+")"}}else s="("==i.charAt(0)?"screen and "+i:i;t.media[e]=!!s&&s}return t.media[e]!==!1&&window.matchMedia(t.media[e]).matches},on:function(e,n){t.events.push({query:e,handler:n,state:!1}),t.active(e)&&n()},poll:function(){var e,n;for(e=0;e', '%3E'); 70 | $svg: str-replace($svg, '&', '%26'); 71 | $svg: str-replace($svg, '#', '%23'); 72 | $svg: str-replace($svg, '{', '%7B'); 73 | $svg: str-replace($svg, '}', '%7D'); 74 | $svg: str-replace($svg, ';', '%3B'); 75 | 76 | @return url("data:image/svg+xml;charset=utf8,#{$svg}"); 77 | 78 | } -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 19 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 36 | 37 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/ui/widgets/weather_details.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../model/weather.dart'; 4 | import 'weather_metric_card.dart'; 5 | 6 | class WeatherDetails extends StatelessWidget { 7 | final Weather weather; 8 | 9 | const WeatherDetails({super.key, required this.weather}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Padding( 14 | padding: const EdgeInsets.fromLTRB(16.0, 24.0, 16.0, 0.0), 15 | child: GridView.count( 16 | shrinkWrap: true, 17 | physics: const NeverScrollableScrollPhysics(), 18 | crossAxisCount: 2, 19 | mainAxisSpacing: 8, 20 | crossAxisSpacing: 8, 21 | childAspectRatio: 1.5, 22 | children: [ 23 | WeatherMetricCard( 24 | title: 'Visibility', 25 | value: '${weather.visibility} m', 26 | iconId: 'BflHxCZO3yb2', 27 | color: Colors.orangeAccent, 28 | ), 29 | WeatherMetricCard( 30 | title: 'Wind', 31 | value: '${weather.wind.speed.toStringAsFixed(1)} km/h', 32 | iconId: 'kkGqzPJkguIf', 33 | color: Colors.purpleAccent, 34 | ), 35 | WeatherMetricCard( 36 | title: 'Pressure', 37 | value: '${weather.main.pressure} hPa', 38 | iconId: 'HtAZFTKSquVD', 39 | color: Colors.pinkAccent, 40 | ), 41 | WeatherMetricCard( 42 | title: 'Humidity', 43 | value: '${weather.main.humidity}%', 44 | iconId: 'MhkU1HOeZffe', 45 | color: Colors.lightBlueAccent, 46 | ), 47 | WeatherMetricCard( 48 | title: 'Sea Level', 49 | value: '${weather.main.seaLevel} m', 50 | iconId: '09mzTarWhdwO', 51 | color: Colors.indigoAccent, 52 | ), 53 | WeatherMetricCard( 54 | title: 'Ground Level', 55 | value: '${weather.main.grndLevel} m', 56 | iconId: '7pyBnIFIx5OY', 57 | color: Colors.red, 58 | ), 59 | WeatherMetricCard( 60 | title: 'Latitude', 61 | value: '${weather.coord.lat.toStringAsFixed(2)}° N', 62 | iconId: 'XieTOK4V0QEI', 63 | color: Colors.green, 64 | ), 65 | WeatherMetricCard( 66 | title: 'Longitude', 67 | value: '${weather.coord.lon.toStringAsFixed(2)}° E', 68 | iconId: 'XieTOK4V0QEI', 69 | color: Colors.pinkAccent, 70 | ), 71 | ], 72 | ), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /docs/assets/sass/components/_button.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Button */ 8 | 9 | input[type="submit"], 10 | input[type="reset"], 11 | input[type="button"], 12 | button, 13 | .button { 14 | @include vendor('appearance', 'none'); 15 | @include vendor('transition', 'background-color #{_duration(transition)} ease-in-out, color #{_duration(transition)} ease-in-out'); 16 | border-radius: _size(border-radius); 17 | border: 0; 18 | cursor: pointer; 19 | display: inline-block; 20 | font-weight: _font(weight-bold); 21 | height: 2.85em; 22 | line-height: 2.95em; 23 | padding: 0 1.75em; 24 | text-align: center; 25 | text-decoration: none; 26 | white-space: nowrap; 27 | 28 | &.icon { 29 | padding-left: 1.35em; 30 | 31 | &:before { 32 | margin-right: 0.5em; 33 | } 34 | } 35 | 36 | &.fit { 37 | width: 100%; 38 | } 39 | 40 | &.small { 41 | font-size: 0.8em; 42 | } 43 | 44 | &.large { 45 | font-size: 1.35em; 46 | } 47 | 48 | &.disabled, 49 | &:disabled { 50 | @include vendor('pointer-events', 'none'); 51 | cursor: default; 52 | opacity: 0.25; 53 | } 54 | 55 | // XSmall. 56 | @include breakpoint('<=xsmall') { 57 | 58 | &.icon { 59 | padding: 0; 60 | } 61 | 62 | // Portrait. 63 | @include orientation(portrait) { 64 | padding: 0; 65 | } 66 | 67 | } 68 | 69 | } 70 | 71 | @mixin color-button($p: null) { 72 | input[type="submit"], 73 | input[type="reset"], 74 | input[type="button"], 75 | button, 76 | .button { 77 | background-color: transparent; 78 | color: _palette($p, fg-bold) !important; 79 | box-shadow: inset 0 0 0 (_size(border-width) * 2) _palette($p, border); 80 | 81 | &:hover { 82 | background-color: _palette($p, border-bg); 83 | } 84 | 85 | &:active { 86 | background-color: _palette($p, border2-bg); 87 | } 88 | 89 | &.primary { 90 | box-shadow: none; 91 | 92 | @if _palette($p, invert) == true { 93 | background-color: _palette($p, fg-bold); 94 | color: _palette($p, bg) !important; 95 | 96 | &:hover { 97 | } 98 | 99 | &:active { 100 | } 101 | } 102 | @else { 103 | background-color: _palette(accent1, bg); 104 | color: _palette(accent1, fg-bold) !important; 105 | 106 | &:hover { 107 | background-color: lighten(_palette(accent1, bg), 5); 108 | } 109 | 110 | &:active { 111 | background-color: darken(_palette(accent1, bg), 5); 112 | } 113 | } 114 | } 115 | } 116 | } 117 | 118 | @include color-button; -------------------------------------------------------------------------------- /docs/assets/sass/components/_image.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Image */ 8 | 9 | .image { 10 | border-radius: _size(border-radius); 11 | border: 0; 12 | display: inline-block; 13 | position: relative; 14 | overflow: hidden; 15 | 16 | img { 17 | border-radius: _size(border-radius); 18 | display: block; 19 | } 20 | 21 | &.left, 22 | &.right { 23 | max-width: 40%; 24 | 25 | img { 26 | width: 100%; 27 | } 28 | } 29 | 30 | &.left { 31 | float: left; 32 | margin: 0 1.5em 1em 0; 33 | top: 0.25em; 34 | } 35 | 36 | &.right { 37 | float: right; 38 | margin: 0 0 1em 1.5em; 39 | top: 0.25em; 40 | } 41 | 42 | &.fit { 43 | display: block; 44 | margin: 0 0 _size(element-margin) 0; 45 | width: 100%; 46 | 47 | img { 48 | width: 100%; 49 | } 50 | } 51 | 52 | &.main { 53 | display: block; 54 | margin: 0 0 (_size(element-margin) * 1.5) 0; 55 | width: 100%; 56 | 57 | img { 58 | width: 100%; 59 | } 60 | } 61 | 62 | &.phone { 63 | box-shadow: none; 64 | border-radius: 0; 65 | margin: 0 0 (_size(element-margin) * 1.5) 0; 66 | width: 13em; 67 | 68 | img { 69 | border-radius: 0; 70 | display: block; 71 | position: relative; 72 | width: 100%; 73 | } 74 | 75 | .inner, &:before, &:after { 76 | display: block; 77 | border: solid (_size(border-width) * 2) _palette(accent1, border); 78 | } 79 | 80 | .inner { 81 | position: relative; 82 | 83 | &:before, &:after { 84 | content: ''; 85 | position: absolute; 86 | left: 50%; 87 | } 88 | 89 | &:before { 90 | top: -1.375em; 91 | margin-left: -1em; 92 | width: 2em; 93 | height: 0.25em; 94 | border-radius: 0.5em; 95 | background: _palette(accent1, border); 96 | } 97 | 98 | &:after { 99 | bottom: -2.75em; 100 | margin-left: -1em; 101 | width: 2em; 102 | height: 2em; 103 | border-radius: 100%; 104 | border: solid (_size(border-width) * 2) _palette(accent1, border); 105 | } 106 | } 107 | 108 | &:before, &:after { 109 | content: ''; 110 | } 111 | 112 | &:before { 113 | height: 2.5em; 114 | border-radius: 1em 1em 0 0; 115 | border-bottom: 0; 116 | } 117 | 118 | &:after { 119 | height: 3.5em; 120 | border-radius: 0 0 1em 1em; 121 | border-top: 0; 122 | } 123 | } 124 | } 125 | 126 | @mixin color-image($p: null) { 127 | .image { 128 | box-shadow: 0 0 0 4px _palette($p, bg), 0 0 0 5px _palette($p, border); 129 | 130 | &.phone { 131 | box-shadow: none; 132 | 133 | .inner, &:before, &:after { 134 | border-color: _palette($p, border); 135 | } 136 | 137 | .inner { 138 | &:before { 139 | background: _palette($p, border); 140 | } 141 | 142 | &:after { 143 | border-color: _palette($p, border); 144 | } 145 | } 146 | } 147 | } 148 | } 149 | 150 | @include color-image; -------------------------------------------------------------------------------- /docs/assets/sass/libs/_html-grid.scss: -------------------------------------------------------------------------------- 1 | // html-grid.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Mixins. 4 | 5 | /// Initializes the current element as an HTML grid. 6 | /// @param {mixed} $gutters Gutters (either a single number to set both column/row gutters, or a list to set them individually). 7 | /// @param {mixed} $suffix Column class suffix (optional; either a single suffix or a list). 8 | @mixin html-grid($gutters: 1.5em, $suffix: '') { 9 | 10 | // Initialize. 11 | $cols: 12; 12 | $multipliers: 0, 0.25, 0.5, 1, 1.50, 2.00; 13 | $unit: 100% / $cols; 14 | 15 | // Suffixes. 16 | $suffixes: null; 17 | 18 | @if (type-of($suffix) == 'list') { 19 | $suffixes: $suffix; 20 | } 21 | @else { 22 | $suffixes: ($suffix); 23 | } 24 | 25 | // Gutters. 26 | $guttersCols: null; 27 | $guttersRows: null; 28 | 29 | @if (type-of($gutters) == 'list') { 30 | 31 | $guttersCols: nth($gutters, 1); 32 | $guttersRows: nth($gutters, 2); 33 | 34 | } 35 | @else { 36 | 37 | $guttersCols: $gutters; 38 | $guttersRows: 0; 39 | 40 | } 41 | 42 | // Row. 43 | display: flex; 44 | flex-wrap: wrap; 45 | box-sizing: border-box; 46 | align-items: stretch; 47 | 48 | // Columns. 49 | > * { 50 | box-sizing: border-box; 51 | } 52 | 53 | // Gutters. 54 | &.gtr-uniform { 55 | > * { 56 | > :last-child { 57 | margin-bottom: 0; 58 | } 59 | } 60 | } 61 | 62 | // Alignment. 63 | &.aln-left { 64 | justify-content: flex-start; 65 | } 66 | 67 | &.aln-center { 68 | justify-content: center; 69 | } 70 | 71 | &.aln-right { 72 | justify-content: flex-end; 73 | } 74 | 75 | &.aln-top { 76 | align-items: flex-start; 77 | } 78 | 79 | &.aln-middle { 80 | align-items: center; 81 | } 82 | 83 | &.aln-bottom { 84 | align-items: flex-end; 85 | } 86 | 87 | // Step through suffixes. 88 | @each $suffix in $suffixes { 89 | 90 | // Suffix. 91 | @if ($suffix != '') { 92 | $suffix: '-' + $suffix; 93 | } 94 | @else { 95 | $suffix: ''; 96 | } 97 | 98 | // Row. 99 | 100 | // Important. 101 | > .imp#{$suffix} { 102 | order: -1; 103 | } 104 | 105 | // Columns, offsets. 106 | @for $i from 1 through $cols { 107 | > .col-#{$i}#{$suffix} { 108 | width: $unit * $i; 109 | } 110 | 111 | > .off-#{$i}#{$suffix} { 112 | margin-left: $unit * $i; 113 | } 114 | } 115 | 116 | // Step through multipliers. 117 | @each $multiplier in $multipliers { 118 | 119 | // Gutters. 120 | $class: null; 121 | 122 | @if ($multiplier != 1) { 123 | $class: '.gtr-' + ($multiplier * 100); 124 | } 125 | 126 | &#{$class} { 127 | margin-top: ($guttersRows * $multiplier * -1); 128 | margin-left: ($guttersCols * $multiplier * -1); 129 | 130 | > * { 131 | padding: ($guttersRows * $multiplier) 0 0 ($guttersCols * $multiplier); 132 | } 133 | 134 | // Uniform. 135 | &.gtr-uniform { 136 | margin-top: $guttersCols * $multiplier * -1; 137 | 138 | > * { 139 | padding-top: $guttersCols * $multiplier; 140 | } 141 | } 142 | 143 | } 144 | 145 | } 146 | 147 | } 148 | 149 | } -------------------------------------------------------------------------------- /docs/privacy-policy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Privacy Policy - EasyWeather 7 | 8 | 30 | 31 | 32 |
33 |

Privacy Policy

34 |

Effective Date: Jan 01, 2025

35 | 36 |

Introduction

37 |

Welcome to EasyWeather, a mobile application developed by Shivam Goyal. This open-source app, available at GitHub, provides weather updates based on your current location. We are committed to protecting your privacy and ensuring transparency about how we handle your information.

38 | 39 |

Information We Collect

40 |

EasyWeather requires access to your device's location to provide weather updates for your current location. The app uses your location data solely for this purpose and does not collect, store, or share this information with any third party.

41 | 42 |

How We Use Your Information

43 |

Your location data is used in real-time to fetch weather information from trusted weather service providers. Once the weather data is displayed, your location data is discarded. No information is stored or retained by the app.

44 | 45 |

Data Sharing

46 |

EasyWeather does not share your location data or any other personal information with third parties.

47 | 48 |

Data Accuracy

49 |

EasyWeather uses weather data from OpenWeatherMap. While we strive to provide accurate information, we are not responsible for any incorrect data provided by the service.

50 | 51 |

Open Source Commitment

52 |

EasyWeather is an open-source project. You can review the source code and contribute to the project at GitHub. This ensures transparency in how the app processes data.

53 | 54 |

Your Consent

55 |

By using EasyWeather, you consent to the app accessing your device's location for the sole purpose of fetching weather data. If you do not agree with this policy, please do not use the app.

56 | 57 |

Changes to This Policy

58 |

We may update this privacy policy from time to time. Any changes will be posted on this page, and the effective date will be updated accordingly. We encourage you to review the policy periodically.

59 | 60 |

Contact Us

61 |

If you have any questions or concerns about this privacy policy, please contact Shivam Goyal at hey@shyvum.in.

62 | 63 |

Thank you for using EasyWeather!

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | EasyWeather 4 | 5 |

6 |

7 | 8 | EasyWeather is released under the GNU General Public License. 9 | 10 | 11 | Latest Version Available. 12 | 13 | 14 | GitHub Repository size. 15 | 16 | 17 | Total downloads on GitHub. 18 | 19 |

20 |

21 | EasyWeather uses OpenWeatherMap.org API to get the realtime weather statistics. 22 |

23 |

24 | Show some love 💜 and Star ⭐️ the Repository to support the project. 25 |

26 | 27 | ## :bookmark_tabs: What’s In This Document 28 | 29 | - [Get Up and Running in 5 Minutes](#rocket-get-up-and-running-in-5-minutes) 30 | - [Flutter / Dart Packages Used](#ballot_box-flutter--dart-packages-used) 31 | - [Versioning](#label-versioning) 32 | - [Queries / Bugs](#question-queries--bugs) 33 | - [License](#memo-license) 34 | - [Thanks to Contributors and Sponsors](#purple_heart-thanks) 35 | 36 | ## :rocket: Get Up and Running in 5 Minutes 37 | 38 | You can get the EasyWeather up and running on your local dev environment in 5 minutes with these four steps: 39 | 40 | 1. **Install the Flutter SDK & Android Studio. [Instructions](https://blog.shyvum.in/install-flutter-on-windows-and-mac).** 41 | 42 | 2. **Open Terminal & navigate to the root directory of the project.** 43 | 44 | ```shell 45 | cd EasyWeather 46 | ``` 47 | 48 | 3. **Run and launch the build on preferred device.** 49 | 50 | ```shell 51 | flutter run 52 | ``` 53 | 54 | 4. **Open the source code and start editing!** 55 | 56 | Your app is now running on your physical device or emulator. Open the `lib` directory in Android Studio itself and edit `*.dart` files. Hot Reload your changes, and the app will update in real time! 57 | 58 | At this point, you’ve got the fully functional EasyWeather App running. For additional information on how you can customize your Flutter Apps, see [Google Codelabs](https://codelabs.developers.google.com/codelabs/flutter/) and [HackerNoon Guide](https://hackernoon.com/making-the-most-of-flutter-from-basics-to-customization-433171581d01). 59 | 60 | ## :ballot_box: Flutter / Dart Packages Used 61 | 62 | - [cupertino_icons](https://pub.dev/packages/cupertino_icons) 63 | - [intl](https://pub.dev/packages/intl) 64 | - [geolocator](https://pub.dev/packages/geolocator) 65 | - [share](https://pub.dev/packages/share) 66 | - [http](https://pub.dev/packages/http) 67 | - [url_launcher](https://pub.dev/packages/url_launcher) 68 | 69 | ## :label: Versioning 70 | I've used [Semantic Versioning 2.0.0](https://semver.org/) for versioning throughout the project. 71 | 72 | ## :question: Queries / Bugs 73 | If you got any queries or found a bug, open an [Issue](https://github.com/shyvum/EasyWeather/issues/new) or ping me over on [hey@shyvum.in](mailto:hey@shyvum.in) 74 | 75 | ## :memo: License 76 | Licensed under the [MIT License](./LICENSE). 77 | 78 | ## :purple_heart: Thanks 79 | Thanks to all contributors and to sponsors for supporting the project. 80 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: easyweather 2 | description: "Is it Sunny, duh?" 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 1.0.1+2 20 | 21 | environment: 22 | sdk: ^3.6.0 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | http: ^1.2.2 34 | geolocator: ^13.0.2 35 | url_launcher: ^6.3.1 36 | intl: ^0.20.1 37 | google_fonts: ^6.2.1 38 | permission_handler: ^11.3.1 39 | connectivity_plus: ^6.1.1 40 | flutter_launcher_icons: ^0.14.2 41 | 42 | dev_dependencies: 43 | flutter_test: 44 | sdk: flutter 45 | 46 | # The "flutter_lints" package below contains a set of recommended lints to 47 | # encourage good coding practices. The lint set provided by the package is 48 | # activated in the `analysis_options.yaml` file located at the root of your 49 | # package. See that file for information about deactivating specific lint 50 | # rules and activating additional ones. 51 | flutter_lints: ^5.0.0 52 | 53 | # For information on the generic Dart part of this file, see the 54 | # following page: https://dart.dev/tools/pub/pubspec 55 | 56 | # The following section is specific to Flutter packages. 57 | flutter: 58 | 59 | # The following line ensures that the Material Icons font is 60 | # included with your application, so that you can use the icons in 61 | # the material Icons class. 62 | uses-material-design: true 63 | 64 | # To add assets to your application, add an assets section, like this: 65 | assets: 66 | - assets/images/ 67 | 68 | # An image asset can refer to one or more resolution-specific "variants", see 69 | # https://flutter.dev/to/resolution-aware-images 70 | 71 | # For details regarding adding assets from package dependencies, see 72 | # https://flutter.dev/to/asset-from-package 73 | 74 | # To add custom fonts to your application, add a fonts section here, 75 | # in this "flutter" section. Each entry in this list should have a 76 | # "family" key with the font family name, and a "fonts" key with a 77 | # list giving the asset and other descriptors for the font. For 78 | # example: 79 | # fonts: 80 | # - family: Schyler 81 | # fonts: 82 | # - asset: fonts/Schyler-Regular.ttf 83 | # - asset: fonts/Schyler-Italic.ttf 84 | # style: italic 85 | # - family: Trajan Pro 86 | # fonts: 87 | # - asset: fonts/TrajanPro.ttf 88 | # - asset: fonts/TrajanPro_Bold.ttf 89 | # weight: 700 90 | # 91 | # For details regarding fonts from package dependencies, 92 | # see https://flutter.dev/to/font-from-package 93 | -------------------------------------------------------------------------------- /lib/model/weather.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | Weather weatherFromJson(String str) => Weather.fromJson(json.decode(str)); 4 | 5 | String weatherToJson(Weather data) => json.encode(data.toJson()); 6 | 7 | class Weather { 8 | final Coord coord; 9 | final List weather; 10 | final Main main; 11 | final int visibility; 12 | final Wind wind; 13 | final String name; 14 | 15 | Weather({ 16 | required this.coord, 17 | required this.weather, 18 | required this.main, 19 | required this.visibility, 20 | required this.wind, 21 | required this.name, 22 | }); 23 | 24 | factory Weather.fromJson(Map json) => Weather( 25 | coord: Coord.fromJson(json["coord"]), 26 | weather: List.from( 27 | json["weather"].map((x) => WeatherElement.fromJson(x))), 28 | main: Main.fromJson(json["main"]), 29 | visibility: json["visibility"], 30 | wind: Wind.fromJson(json["wind"]), 31 | name: json["name"], 32 | ); 33 | 34 | Map toJson() => { 35 | "coord": coord.toJson(), 36 | "weather": List.from(weather.map((x) => x.toJson())), 37 | "main": main.toJson(), 38 | "visibility": visibility, 39 | "wind": wind.toJson(), 40 | "name": name, 41 | }; 42 | } 43 | 44 | class Coord { 45 | final double lon; 46 | final double lat; 47 | 48 | Coord({ 49 | required this.lon, 50 | required this.lat, 51 | }); 52 | 53 | factory Coord.fromJson(Map json) => Coord( 54 | lon: json["lon"]?.toDouble(), 55 | lat: json["lat"]?.toDouble(), 56 | ); 57 | 58 | Map toJson() => { 59 | "lon": lon, 60 | "lat": lat, 61 | }; 62 | } 63 | 64 | class Main { 65 | final double temp; 66 | final double feelsLike; 67 | final double tempMin; 68 | final double tempMax; 69 | final int pressure; 70 | final int humidity; 71 | final int seaLevel; 72 | final int grndLevel; 73 | 74 | Main({ 75 | required this.temp, 76 | required this.feelsLike, 77 | required this.tempMin, 78 | required this.tempMax, 79 | required this.pressure, 80 | required this.humidity, 81 | required this.seaLevel, 82 | required this.grndLevel, 83 | }); 84 | 85 | factory Main.fromJson(Map json) => Main( 86 | temp: json["temp"]?.toDouble(), 87 | feelsLike: json["feels_like"]?.toDouble(), 88 | tempMin: json["temp_min"]?.toDouble(), 89 | tempMax: json["temp_max"]?.toDouble(), 90 | pressure: json["pressure"], 91 | humidity: json["humidity"], 92 | seaLevel: json["sea_level"], 93 | grndLevel: json["grnd_level"], 94 | ); 95 | 96 | Map toJson() => { 97 | "temp": temp, 98 | "feels_like": feelsLike, 99 | "temp_min": tempMin, 100 | "temp_max": tempMax, 101 | "pressure": pressure, 102 | "humidity": humidity, 103 | "sea_level": seaLevel, 104 | "grnd_level": grndLevel, 105 | }; 106 | } 107 | 108 | class WeatherElement { 109 | final int id; 110 | final String description; 111 | final String icon; 112 | 113 | WeatherElement({ 114 | required this.id, 115 | required this.description, 116 | required this.icon, 117 | }); 118 | 119 | factory WeatherElement.fromJson(Map json) => WeatherElement( 120 | id: json["id"], 121 | description: json["description"], 122 | icon: json["icon"], 123 | ); 124 | 125 | Map toJson() => { 126 | "id": id, 127 | "description": description, 128 | "icon": icon, 129 | }; 130 | } 131 | 132 | class Wind { 133 | final double speed; 134 | final int deg; 135 | 136 | Wind({ 137 | required this.speed, 138 | required this.deg, 139 | }); 140 | 141 | factory Wind.fromJson(Map json) => Wind( 142 | speed: json["speed"]?.toDouble(), 143 | deg: json["deg"], 144 | ); 145 | 146 | Map toJson() => { 147 | "speed": speed, 148 | "deg": deg, 149 | }; 150 | } 151 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | EasyWeather 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /docs/assets/sass/base/_typography.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Type */ 8 | 9 | html, body { 10 | background: _palette(bg-alt); 11 | } 12 | 13 | body { 14 | color: _palette(fg); 15 | } 16 | 17 | html, input, select, textarea { 18 | font-size: 16pt; 19 | 20 | // XLarge. 21 | @include breakpoint('<=xlarge') { 22 | font-size: 13pt; 23 | } 24 | 25 | // Large. 26 | @include breakpoint('<=large') { 27 | font-size: 12pt; 28 | } 29 | 30 | // XXSmall. 31 | @include breakpoint('<=xxsmall') { 32 | font-size: 11pt; 33 | } 34 | 35 | } 36 | 37 | body, input, select, textarea { 38 | font-family: _font(family); 39 | font-weight: _font(weight); 40 | line-height: 1.5em; 41 | letter-spacing: -0.015em; 42 | } 43 | 44 | a { 45 | @include vendor('transition', ('color #{_duration(transition)} ease-in-out', 'border-bottom-color #{_duration(transition)} ease-in-out')); 46 | text-decoration: none; 47 | border-bottom: dotted 1px; 48 | 49 | &:hover { 50 | border-bottom-color: transparent !important; 51 | } 52 | } 53 | 54 | strong, b { 55 | font-weight: _font(weight-bold); 56 | } 57 | 58 | em, i { 59 | font-style: italic; 60 | } 61 | 62 | p { 63 | margin: 0 0 _size(element-margin) 0; 64 | } 65 | 66 | h1, h2, h3, h4, h5, h6 { 67 | font-weight: _font(weight); 68 | line-height: 1em; 69 | margin: 0 0 (_size(element-margin) * 0.5) 0; 70 | letter-spacing: -0.0125em; 71 | 72 | a { 73 | color: inherit; 74 | text-decoration: none; 75 | border-bottom: none; 76 | } 77 | 78 | // Medium. 79 | @include breakpoint('<=medium') { 80 | br { 81 | display: none; 82 | } 83 | } 84 | 85 | } 86 | 87 | h2 { 88 | font-size: 1.75em; 89 | line-height: 1.5em; 90 | } 91 | 92 | h3 { 93 | font-size: 1.5em; 94 | line-height: 1.5em; 95 | } 96 | 97 | h4 { 98 | font-size: 1.1em; 99 | line-height: 1.5em; 100 | } 101 | 102 | h5 { 103 | font-size: 0.9em; 104 | line-height: 1.5em; 105 | } 106 | 107 | h6 { 108 | font-size: 0.7em; 109 | line-height: 1.5em; 110 | } 111 | 112 | // Small. 113 | @include breakpoint('<=small') { 114 | h2 { 115 | font-size: 1.25em; 116 | } 117 | 118 | h3 { 119 | font-size: 1.2em; 120 | } 121 | 122 | h4 { 123 | font-size: 1em; 124 | } 125 | } 126 | 127 | sub { 128 | font-size: 0.8em; 129 | position: relative; 130 | top: 0.5em; 131 | } 132 | 133 | sup { 134 | font-size: 0.8em; 135 | position: relative; 136 | top: -0.5em; 137 | } 138 | 139 | blockquote { 140 | border-left-style: solid; 141 | border-left-width: (_size(border-width) * 4); 142 | font-style: italic; 143 | margin: 0 0 _size(element-margin) 0; 144 | padding: (_size(element-margin) / 4) 0 (_size(element-margin) / 4) _size(element-margin); 145 | } 146 | 147 | code { 148 | border-radius: _size(border-radius); 149 | border-style: solid; 150 | border-width: _size(border-width); 151 | font-family: _font(family-fixed); 152 | font-size: 0.9em; 153 | margin: 0 0.25em; 154 | padding: 0.25em 0.65em; 155 | } 156 | 157 | pre { 158 | -webkit-overflow-scrolling: touch; 159 | font-family: _font(family-fixed); 160 | font-size: 0.9em; 161 | margin: 0 0 _size(element-margin) 0; 162 | 163 | code { 164 | display: block; 165 | line-height: 1.75em; 166 | padding: 1em 1.5em; 167 | overflow-x: auto; 168 | } 169 | } 170 | 171 | hr { 172 | border: 0; 173 | border-bottom-style: solid; 174 | border-bottom-width: _size(border-width); 175 | margin: _size(element-margin) 0; 176 | 177 | &.major { 178 | margin: (_size(element-margin) * 1.5) 0; 179 | } 180 | } 181 | 182 | .align-left { 183 | text-align: left; 184 | } 185 | 186 | .align-center { 187 | text-align: center; 188 | } 189 | 190 | .align-right { 191 | text-align: right; 192 | } 193 | 194 | @mixin color-typography($p: null) { 195 | @if $p != null { 196 | background-color: _palette($p, bg); 197 | color: _palette($p, fg); 198 | } 199 | 200 | input, select, textarea { 201 | color: _palette($p, fg); 202 | } 203 | 204 | a { 205 | color: inherit; 206 | border-bottom-color: _palette($p, border2); 207 | 208 | @if _palette($p, invert) == true { 209 | color: _palette($p, fg-bold); 210 | } 211 | @else { 212 | &:hover { 213 | color: _palette(accent1, bg); 214 | } 215 | } 216 | } 217 | 218 | strong, b { 219 | color: _palette($p, fg-bold); 220 | } 221 | 222 | h1, h2, h3, h4, h5, h6 { 223 | color: _palette($p, fg-bold); 224 | } 225 | 226 | blockquote { 227 | border-left-color: _palette($p, border); 228 | } 229 | 230 | code { 231 | background: _palette($p, border-bg); 232 | border-color: _palette($p, border); 233 | } 234 | 235 | hr { 236 | border-bottom-color: _palette($p, border); 237 | } 238 | } 239 | 240 | @include color-typography; -------------------------------------------------------------------------------- /docs/assets/sass/layout/_header.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Header */ 8 | 9 | #header { 10 | @include color(accent1); 11 | @include padding(4.5em, 0); 12 | @include vendor('align-items', 'center'); 13 | @include vendor('display', 'flex'); 14 | @include vendor('justify-content', 'center'); 15 | background-image: url('images/overlay.png'), url('images/pattern-size1.svg'); 16 | background-position: top left, center center; 17 | background-attachment: fixed, fixed; 18 | background-size: auto, cover; 19 | height: 100vh; 20 | min-height: 35em; 21 | 22 | h1 { 23 | font-size: 3.25em; 24 | font-weight: _font(weight-bold); 25 | margin-bottom: _size(element-margin) * 0.25; 26 | } 27 | 28 | p { 29 | font-size: 1.5em; 30 | line-height: 1.35em; 31 | 32 | br { 33 | display: inline; 34 | } 35 | } 36 | 37 | .content { 38 | @include vendor('transition', ('opacity 0.5s ease', 'transform 1s ease')); 39 | @include vendor('transform', 'translateX(0)'); 40 | opacity: 1; 41 | text-align: right; 42 | margin: (_size(element-margin) * -1.75) (_size(element-margin) * 2) 0 0; 43 | 44 | > :last-child { 45 | margin-bottom: 0; 46 | } 47 | 48 | .actions { 49 | @include vendor('justify-content', 'flex-end'); 50 | } 51 | } 52 | 53 | .image { 54 | @include vendor('transition', ('opacity 1s ease', 'transform 1s ease')); 55 | @include vendor('transform', 'translateY(0)'); 56 | opacity: 1; 57 | 58 | img { 59 | @include vendor('transition', 'opacity 0.75s ease'); 60 | @include vendor('transition-delay', '0.75s'); 61 | opacity: 1; 62 | } 63 | } 64 | 65 | // Large. 66 | @include breakpoint('<=large') { 67 | background-image: url('images/overlay.png'), url('images/pattern-size2.svg'); 68 | } 69 | 70 | // Medium. 71 | @include breakpoint('<=medium') { 72 | height: auto; 73 | min-height: 0; 74 | 75 | .content { 76 | margin: (_size(element-margin) * -1) (_size(element-margin) * 2) 0 0; 77 | 78 | .actions { 79 | @include vendor('justify-content', 'center'); 80 | } 81 | } 82 | 83 | // Portrait. 84 | @include orientation(portrait) { 85 | @include padding(6em, 3em); 86 | @include vendor('flex-direction', 'column-reverse'); 87 | 88 | .content { 89 | display: block; 90 | width: 100%; 91 | text-align: center; 92 | margin: 0 0 _size(element-margin) 0; 93 | } 94 | 95 | .image { 96 | overflow: hidden; 97 | } 98 | } 99 | 100 | } 101 | 102 | // Small. 103 | @include breakpoint('<=small') { 104 | background-image: url('images/overlay.png'), url('images/pattern-size3.svg'); 105 | 106 | h1 { 107 | font-size: 2em; 108 | } 109 | 110 | p { 111 | font-size: 1em; 112 | line-height: inherit; 113 | } 114 | 115 | // Landscape. 116 | @include orientation(landscape) { 117 | @include padding(2em, 2em); 118 | 119 | .content { 120 | margin: 0 _size(element-margin) _size(element-margin) 0; 121 | } 122 | 123 | .image { 124 | font-size: 0.8em; 125 | width: 15em; 126 | max-width: 20vw; 127 | 128 | .inner, &:before, &:after { 129 | font-size: 0.8em; 130 | } 131 | } 132 | } 133 | 134 | // Portrait. 135 | @include orientation(portrait) { 136 | @include padding(3em, 1.5em); 137 | 138 | .image { 139 | font-size: 0.6em; 140 | max-width: 60vw; 141 | } 142 | } 143 | 144 | } 145 | 146 | // XSmall. 147 | @include breakpoint('<=xsmall') { 148 | 149 | // Landscape. 150 | @include orientation(landscape) { 151 | @include padding(2em, 1.5em); 152 | 153 | .image { 154 | font-size: 0.7em; 155 | } 156 | 157 | .actions { 158 | font-size: 0.8em; 159 | } 160 | } 161 | 162 | // Portrait. 163 | @include orientation(portrait) { 164 | @include padding(2em, 1em); 165 | } 166 | 167 | } 168 | 169 | // Mobile. 170 | body.is-mobile & { 171 | background-attachment: scroll, scroll; 172 | } 173 | 174 | // Preload. 175 | body.is-preload & { 176 | .content { 177 | @include vendor('transform', 'translateX(2em)'); 178 | opacity: 0; 179 | } 180 | 181 | .image { 182 | @include vendor('transform', 'translateY(4em)'); 183 | opacity: 0; 184 | 185 | img { 186 | opacity: 0; 187 | } 188 | } 189 | 190 | // Medium. 191 | @include breakpoint('<=medium') { 192 | 193 | // Portrait. 194 | @include orientation(portrait) { 195 | .content { 196 | @include vendor('transform', 'none'); 197 | opacity: 1; 198 | } 199 | 200 | .image { 201 | @include vendor('transform', 'translateY(2em)'); 202 | } 203 | } 204 | 205 | } 206 | 207 | } 208 | 209 | } -------------------------------------------------------------------------------- /docs/assets/sass/libs/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | // breakpoints.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Breakpoints. 6 | /// @var {list} 7 | $breakpoints: () !global; 8 | 9 | // Mixins. 10 | 11 | /// Sets breakpoints. 12 | /// @param {map} $x Breakpoints. 13 | @mixin breakpoints($x: ()) { 14 | $breakpoints: $x !global; 15 | } 16 | 17 | /// Wraps @content in a @media block targeting a specific orientation. 18 | /// @param {string} $orientation Orientation. 19 | @mixin orientation($orientation) { 20 | @media screen and (orientation: #{$orientation}) { 21 | @content; 22 | } 23 | } 24 | 25 | /// Wraps @content in a @media block using a given query. 26 | /// @param {string} $query Query. 27 | @mixin breakpoint($query: null) { 28 | 29 | $breakpoint: null; 30 | $op: null; 31 | $media: null; 32 | 33 | // Determine operator, breakpoint. 34 | 35 | // Greater than or equal. 36 | @if (str-slice($query, 0, 2) == '>=') { 37 | 38 | $op: 'gte'; 39 | $breakpoint: str-slice($query, 3); 40 | 41 | } 42 | 43 | // Less than or equal. 44 | @elseif (str-slice($query, 0, 2) == '<=') { 45 | 46 | $op: 'lte'; 47 | $breakpoint: str-slice($query, 3); 48 | 49 | } 50 | 51 | // Greater than. 52 | @elseif (str-slice($query, 0, 1) == '>') { 53 | 54 | $op: 'gt'; 55 | $breakpoint: str-slice($query, 2); 56 | 57 | } 58 | 59 | // Less than. 60 | @elseif (str-slice($query, 0, 1) == '<') { 61 | 62 | $op: 'lt'; 63 | $breakpoint: str-slice($query, 2); 64 | 65 | } 66 | 67 | // Not. 68 | @elseif (str-slice($query, 0, 1) == '!') { 69 | 70 | $op: 'not'; 71 | $breakpoint: str-slice($query, 2); 72 | 73 | } 74 | 75 | // Equal. 76 | @else { 77 | 78 | $op: 'eq'; 79 | $breakpoint: $query; 80 | 81 | } 82 | 83 | // Build media. 84 | @if ($breakpoint and map-has-key($breakpoints, $breakpoint)) { 85 | 86 | $a: map-get($breakpoints, $breakpoint); 87 | 88 | // Range. 89 | @if (type-of($a) == 'list') { 90 | 91 | $x: nth($a, 1); 92 | $y: nth($a, 2); 93 | 94 | // Max only. 95 | @if ($x == null) { 96 | 97 | // Greater than or equal (>= 0 / anything) 98 | @if ($op == 'gte') { 99 | $media: 'screen'; 100 | } 101 | 102 | // Less than or equal (<= y) 103 | @elseif ($op == 'lte') { 104 | $media: 'screen and (max-width: ' + $y + ')'; 105 | } 106 | 107 | // Greater than (> y) 108 | @elseif ($op == 'gt') { 109 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 110 | } 111 | 112 | // Less than (< 0 / invalid) 113 | @elseif ($op == 'lt') { 114 | $media: 'screen and (max-width: -1px)'; 115 | } 116 | 117 | // Not (> y) 118 | @elseif ($op == 'not') { 119 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 120 | } 121 | 122 | // Equal (<= y) 123 | @else { 124 | $media: 'screen and (max-width: ' + $y + ')'; 125 | } 126 | 127 | } 128 | 129 | // Min only. 130 | @else if ($y == null) { 131 | 132 | // Greater than or equal (>= x) 133 | @if ($op == 'gte') { 134 | $media: 'screen and (min-width: ' + $x + ')'; 135 | } 136 | 137 | // Less than or equal (<= inf / anything) 138 | @elseif ($op == 'lte') { 139 | $media: 'screen'; 140 | } 141 | 142 | // Greater than (> inf / invalid) 143 | @elseif ($op == 'gt') { 144 | $media: 'screen and (max-width: -1px)'; 145 | } 146 | 147 | // Less than (< x) 148 | @elseif ($op == 'lt') { 149 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 150 | } 151 | 152 | // Not (< x) 153 | @elseif ($op == 'not') { 154 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 155 | } 156 | 157 | // Equal (>= x) 158 | @else { 159 | $media: 'screen and (min-width: ' + $x + ')'; 160 | } 161 | 162 | } 163 | 164 | // Min and max. 165 | @else { 166 | 167 | // Greater than or equal (>= x) 168 | @if ($op == 'gte') { 169 | $media: 'screen and (min-width: ' + $x + ')'; 170 | } 171 | 172 | // Less than or equal (<= y) 173 | @elseif ($op == 'lte') { 174 | $media: 'screen and (max-width: ' + $y + ')'; 175 | } 176 | 177 | // Greater than (> y) 178 | @elseif ($op == 'gt') { 179 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 180 | } 181 | 182 | // Less than (< x) 183 | @elseif ($op == 'lt') { 184 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 185 | } 186 | 187 | // Not (< x and > y) 188 | @elseif ($op == 'not') { 189 | $media: 'screen and (max-width: ' + ($x - 1) + '), screen and (min-width: ' + ($y + 1) + ')'; 190 | } 191 | 192 | // Equal (>= x and <= y) 193 | @else { 194 | $media: 'screen and (min-width: ' + $x + ') and (max-width: ' + $y + ')'; 195 | } 196 | 197 | } 198 | 199 | } 200 | 201 | // String. 202 | @else { 203 | 204 | // Missing a media type? Prefix with "screen". 205 | @if (str-slice($a, 0, 1) == '(') { 206 | $media: 'screen and ' + $a; 207 | } 208 | 209 | // Otherwise, use as-is. 210 | @else { 211 | $media: $a; 212 | } 213 | 214 | } 215 | 216 | } 217 | 218 | // Output. 219 | @media #{$media} { 220 | @content; 221 | } 222 | 223 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_form.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fractal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Form */ 8 | 9 | form { 10 | margin: 0 0 _size(element-margin) 0; 11 | } 12 | 13 | label { 14 | display: block; 15 | font-size: 0.9em; 16 | font-weight: _font(weight-bold); 17 | margin: 0 0 (_size(element-margin) * 0.5) 0; 18 | } 19 | 20 | input[type="text"], 21 | input[type="password"], 22 | input[type="email"], 23 | select, 24 | textarea { 25 | @include vendor('appearance', 'none'); 26 | border-radius: _size(border-radius); 27 | border: none; 28 | border-style: solid; 29 | border-width: _size(border-width); 30 | color: inherit; 31 | display: block; 32 | outline: 0; 33 | padding: 0 1em; 34 | text-decoration: none; 35 | width: 100%; 36 | 37 | &:invalid { 38 | box-shadow: none; 39 | } 40 | } 41 | 42 | select { 43 | background-image: svg-url(""); 44 | background-size: 1.25rem; 45 | background-repeat: no-repeat; 46 | background-position: calc(100% - 1rem) center; 47 | height: _size(element-height); 48 | padding-right: _size(element-height); 49 | text-overflow: ellipsis; 50 | 51 | option { 52 | color: _palette(fg-bold); 53 | background: _palette(bg); 54 | } 55 | 56 | &:focus { 57 | &::-ms-value { 58 | background-color: transparent; 59 | } 60 | } 61 | 62 | &::-ms-expand { 63 | display: none; 64 | } 65 | } 66 | 67 | input[type="text"], 68 | input[type="password"], 69 | input[type="email"], 70 | select { 71 | height: _size(element-height); 72 | } 73 | 74 | textarea { 75 | padding: 0.75em 1em; 76 | } 77 | 78 | input[type="checkbox"], 79 | input[type="radio"], { 80 | @include vendor('appearance', 'none'); 81 | display: block; 82 | float: left; 83 | margin-right: -2em; 84 | opacity: 0; 85 | width: 1em; 86 | z-index: -1; 87 | 88 | & + label { 89 | @include icon(false, solid); 90 | cursor: pointer; 91 | display: inline-block; 92 | font-size: 1em; 93 | font-weight: _font(weight); 94 | padding-left: (_size(element-height) * 0.6) + 0.75em; 95 | padding-right: 0.75em; 96 | position: relative; 97 | 98 | &:before { 99 | border-radius: _size(border-radius); 100 | border-style: solid; 101 | border-width: _size(border-width); 102 | content: ''; 103 | display: inline-block; 104 | font-size: 0.8em; 105 | height: (_size(element-height) * 0.75); 106 | left: 0; 107 | line-height: (_size(element-height) * 0.75); 108 | position: absolute; 109 | text-align: center; 110 | top: 0; 111 | width: (_size(element-height) * 0.75); 112 | } 113 | } 114 | 115 | &:checked + label { 116 | &:before { 117 | content: '\f00c'; 118 | } 119 | } 120 | } 121 | 122 | input[type="checkbox"] { 123 | & + label { 124 | &:before { 125 | border-radius: _size(border-radius); 126 | } 127 | } 128 | } 129 | 130 | input[type="radio"] { 131 | & + label { 132 | &:before { 133 | border-radius: 100%; 134 | } 135 | } 136 | } 137 | 138 | ::-webkit-input-placeholder { 139 | opacity: 1.0; 140 | } 141 | 142 | :-moz-placeholder { 143 | opacity: 1.0; 144 | } 145 | 146 | ::-moz-placeholder { 147 | opacity: 1.0; 148 | } 149 | 150 | :-ms-input-placeholder { 151 | opacity: 1.0; 152 | } 153 | 154 | .formerize-placeholder { 155 | opacity: 1.0; 156 | } 157 | 158 | @mixin color-form($p: null) { 159 | 160 | label { 161 | color: _palette($p, fg-bold); 162 | } 163 | 164 | input[type="text"], 165 | input[type="password"], 166 | input[type="email"], 167 | select, 168 | textarea { 169 | color: inherit; 170 | background-color: _palette($p, border-bg); 171 | border-color: _palette($p, border); 172 | 173 | &:focus { 174 | @if _palette($p, invert) == true { 175 | border-color: _palette($p, fg-bold); 176 | box-shadow: 0 0 0 _size(border-width) _palette($p, fg-bold); 177 | } 178 | @else { 179 | border-color: _palette(accent1, bg); 180 | box-shadow: 0 0 0 _size(border-width) _palette(accent1, bg); 181 | } 182 | } 183 | } 184 | 185 | .select-wrapper:before { 186 | color: _palette($p, border); 187 | } 188 | 189 | input[type="checkbox"], 190 | input[type="radio"], { 191 | 192 | & + label { 193 | color: _palette($p, fg); 194 | 195 | &:before { 196 | background-color: _palette($p, border-bg); 197 | border-color: _palette($p, border); 198 | } 199 | } 200 | 201 | &:checked + label { 202 | &:before { 203 | @if _palette($p, invert) == true { 204 | background-color: _palette($p, fg-bold); 205 | border-color: _palette($p, fg-bold); 206 | color: _palette($p, bg); 207 | } 208 | @else { 209 | background-color: _palette(accent1, bg); 210 | border-color: _palette(accent1, bg); 211 | color: _palette(accent1, fg-bold); 212 | } 213 | } 214 | } 215 | 216 | &:focus + label { 217 | &:before { 218 | @if _palette($p, invert) == true { 219 | border-color: _palette($p, fg-bold); 220 | box-shadow: 0 0 0 _size(border-width) _palette($p, fg-bold); 221 | } 222 | @else { 223 | border-color: _palette(accent1, bg); 224 | box-shadow: 0 0 0 _size(border-width) _palette(accent1, bg); 225 | } 226 | } 227 | } 228 | } 229 | 230 | ::-webkit-input-placeholder { 231 | color: _palette($p, fg-light) !important; 232 | } 233 | 234 | :-moz-placeholder { 235 | color: _palette($p, fg-light) !important; 236 | } 237 | 238 | ::-moz-placeholder { 239 | color: _palette($p, fg-light) !important; 240 | } 241 | 242 | :-ms-input-placeholder { 243 | color: _palette($p, fg-light) !important; 244 | } 245 | 246 | .formerize-placeholder { 247 | color: _palette($p, fg-light) !important; 248 | } 249 | } 250 | 251 | @include color-form; -------------------------------------------------------------------------------- /docs/assets/sass/libs/_vendor.scss: -------------------------------------------------------------------------------- 1 | // vendor.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Vendor prefixes. 6 | /// @var {list} 7 | $vendor-prefixes: ( 8 | '-moz-', 9 | '-webkit-', 10 | '-ms-', 11 | '' 12 | ); 13 | 14 | /// Properties that should be vendorized. 15 | /// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org 16 | /// @var {list} 17 | $vendor-properties: ( 18 | 19 | // Animation. 20 | 'animation', 21 | 'animation-delay', 22 | 'animation-direction', 23 | 'animation-duration', 24 | 'animation-fill-mode', 25 | 'animation-iteration-count', 26 | 'animation-name', 27 | 'animation-play-state', 28 | 'animation-timing-function', 29 | 30 | // Appearance. 31 | 'appearance', 32 | 33 | // Backdrop filter. 34 | 'backdrop-filter', 35 | 36 | // Background image options. 37 | 'background-clip', 38 | 'background-origin', 39 | 'background-size', 40 | 41 | // Box sizing. 42 | 'box-sizing', 43 | 44 | // Clip path. 45 | 'clip-path', 46 | 47 | // Filter effects. 48 | 'filter', 49 | 50 | // Flexbox. 51 | 'align-content', 52 | 'align-items', 53 | 'align-self', 54 | 'flex', 55 | 'flex-basis', 56 | 'flex-direction', 57 | 'flex-flow', 58 | 'flex-grow', 59 | 'flex-shrink', 60 | 'flex-wrap', 61 | 'justify-content', 62 | 'order', 63 | 64 | // Font feature. 65 | 'font-feature-settings', 66 | 'font-language-override', 67 | 'font-variant-ligatures', 68 | 69 | // Font kerning. 70 | 'font-kerning', 71 | 72 | // Fragmented borders and backgrounds. 73 | 'box-decoration-break', 74 | 75 | // Grid layout. 76 | 'grid-column', 77 | 'grid-column-align', 78 | 'grid-column-end', 79 | 'grid-column-start', 80 | 'grid-row', 81 | 'grid-row-align', 82 | 'grid-row-end', 83 | 'grid-row-start', 84 | 'grid-template-columns', 85 | 'grid-template-rows', 86 | 87 | // Hyphens. 88 | 'hyphens', 89 | 'word-break', 90 | 91 | // Masks. 92 | 'mask', 93 | 'mask-border', 94 | 'mask-border-outset', 95 | 'mask-border-repeat', 96 | 'mask-border-slice', 97 | 'mask-border-source', 98 | 'mask-border-width', 99 | 'mask-clip', 100 | 'mask-composite', 101 | 'mask-image', 102 | 'mask-origin', 103 | 'mask-position', 104 | 'mask-repeat', 105 | 'mask-size', 106 | 107 | // Multicolumn. 108 | 'break-after', 109 | 'break-before', 110 | 'break-inside', 111 | 'column-count', 112 | 'column-fill', 113 | 'column-gap', 114 | 'column-rule', 115 | 'column-rule-color', 116 | 'column-rule-style', 117 | 'column-rule-width', 118 | 'column-span', 119 | 'column-width', 120 | 'columns', 121 | 122 | // Object fit. 123 | 'object-fit', 124 | 'object-position', 125 | 126 | // Regions. 127 | 'flow-from', 128 | 'flow-into', 129 | 'region-fragment', 130 | 131 | // Scroll snap points. 132 | 'scroll-snap-coordinate', 133 | 'scroll-snap-destination', 134 | 'scroll-snap-points-x', 135 | 'scroll-snap-points-y', 136 | 'scroll-snap-type', 137 | 138 | // Shapes. 139 | 'shape-image-threshold', 140 | 'shape-margin', 141 | 'shape-outside', 142 | 143 | // Tab size. 144 | 'tab-size', 145 | 146 | // Text align last. 147 | 'text-align-last', 148 | 149 | // Text decoration. 150 | 'text-decoration-color', 151 | 'text-decoration-line', 152 | 'text-decoration-skip', 153 | 'text-decoration-style', 154 | 155 | // Text emphasis. 156 | 'text-emphasis', 157 | 'text-emphasis-color', 158 | 'text-emphasis-position', 159 | 'text-emphasis-style', 160 | 161 | // Text size adjust. 162 | 'text-size-adjust', 163 | 164 | // Text spacing. 165 | 'text-spacing', 166 | 167 | // Transform. 168 | 'transform', 169 | 'transform-origin', 170 | 171 | // Transform 3D. 172 | 'backface-visibility', 173 | 'perspective', 174 | 'perspective-origin', 175 | 'transform-style', 176 | 177 | // Transition. 178 | 'transition', 179 | 'transition-delay', 180 | 'transition-duration', 181 | 'transition-property', 182 | 'transition-timing-function', 183 | 184 | // Unicode bidi. 185 | 'unicode-bidi', 186 | 187 | // User select. 188 | 'user-select', 189 | 190 | // Writing mode. 191 | 'writing-mode', 192 | 193 | ); 194 | 195 | /// Values that should be vendorized. 196 | /// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org 197 | /// @var {list} 198 | $vendor-values: ( 199 | 200 | // Cross fade. 201 | 'cross-fade', 202 | 203 | // Element function. 204 | 'element', 205 | 206 | // Filter function. 207 | 'filter', 208 | 209 | // Flexbox. 210 | 'flex', 211 | 'inline-flex', 212 | 213 | // Grab cursors. 214 | 'grab', 215 | 'grabbing', 216 | 217 | // Gradients. 218 | 'linear-gradient', 219 | 'repeating-linear-gradient', 220 | 'radial-gradient', 221 | 'repeating-radial-gradient', 222 | 223 | // Grid layout. 224 | 'grid', 225 | 'inline-grid', 226 | 227 | // Image set. 228 | 'image-set', 229 | 230 | // Intrinsic width. 231 | 'max-content', 232 | 'min-content', 233 | 'fit-content', 234 | 'fill', 235 | 'fill-available', 236 | 'stretch', 237 | 238 | // Sticky position. 239 | 'sticky', 240 | 241 | // Transform. 242 | 'transform', 243 | 244 | // Zoom cursors. 245 | 'zoom-in', 246 | 'zoom-out', 247 | 248 | ); 249 | 250 | // Functions. 251 | 252 | /// Removes a specific item from a list. 253 | /// @author Hugo Giraudel 254 | /// @param {list} $list List. 255 | /// @param {integer} $index Index. 256 | /// @return {list} Updated list. 257 | @function remove-nth($list, $index) { 258 | 259 | $result: null; 260 | 261 | @if type-of($index) != number { 262 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 263 | } 264 | @else if $index == 0 { 265 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 266 | } 267 | @else if abs($index) > length($list) { 268 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 269 | } 270 | @else { 271 | 272 | $result: (); 273 | $index: if($index < 0, length($list) + $index + 1, $index); 274 | 275 | @for $i from 1 through length($list) { 276 | 277 | @if $i != $index { 278 | $result: append($result, nth($list, $i)); 279 | } 280 | 281 | } 282 | 283 | } 284 | 285 | @return $result; 286 | 287 | } 288 | 289 | /// Replaces a substring within another string. 290 | /// @author Hugo Giraudel 291 | /// @param {string} $string String. 292 | /// @param {string} $search Substring. 293 | /// @param {string} $replace Replacement. 294 | /// @return {string} Updated string. 295 | @function str-replace($string, $search, $replace: '') { 296 | 297 | $index: str-index($string, $search); 298 | 299 | @if $index { 300 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); 301 | } 302 | 303 | @return $string; 304 | 305 | } 306 | 307 | /// Replaces a substring within each string in a list. 308 | /// @param {list} $strings List of strings. 309 | /// @param {string} $search Substring. 310 | /// @param {string} $replace Replacement. 311 | /// @return {list} Updated list of strings. 312 | @function str-replace-all($strings, $search, $replace: '') { 313 | 314 | @each $string in $strings { 315 | $strings: set-nth($strings, index($strings, $string), str-replace($string, $search, $replace)); 316 | } 317 | 318 | @return $strings; 319 | 320 | } 321 | 322 | // Mixins. 323 | 324 | /// Wraps @content in vendorized keyframe blocks. 325 | /// @param {string} $name Name. 326 | @mixin keyframes($name) { 327 | 328 | @-moz-keyframes #{$name} { @content; } 329 | @-webkit-keyframes #{$name} { @content; } 330 | @-ms-keyframes #{$name} { @content; } 331 | @keyframes #{$name} { @content; } 332 | 333 | } 334 | 335 | /// Vendorizes a declaration's property and/or value(s). 336 | /// @param {string} $property Property. 337 | /// @param {mixed} $value String/list of value(s). 338 | @mixin vendor($property, $value) { 339 | 340 | // Determine if property should expand. 341 | $expandProperty: index($vendor-properties, $property); 342 | 343 | // Determine if value should expand (and if so, add '-prefix-' placeholder). 344 | $expandValue: false; 345 | 346 | @each $x in $value { 347 | @each $y in $vendor-values { 348 | @if $y == str-slice($x, 1, str-length($y)) { 349 | 350 | $value: set-nth($value, index($value, $x), '-prefix-' + $x); 351 | $expandValue: true; 352 | 353 | } 354 | } 355 | } 356 | 357 | // Expand property? 358 | @if $expandProperty { 359 | @each $vendor in $vendor-prefixes { 360 | #{$vendor}#{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; 361 | } 362 | } 363 | 364 | // Expand just the value? 365 | @elseif $expandValue { 366 | @each $vendor in $vendor-prefixes { 367 | #{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; 368 | } 369 | } 370 | 371 | // Neither? Treat them as a normal declaration. 372 | @else { 373 | #{$property}: #{$value}; 374 | } 375 | 376 | } -------------------------------------------------------------------------------- /docs/assets/js/util.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | /** 4 | * Generate an indented list of links from a nav. Meant for use with panel(). 5 | * @return {jQuery} jQuery object. 6 | */ 7 | $.fn.navList = function() { 8 | 9 | var $this = $(this); 10 | $a = $this.find('a'), 11 | b = []; 12 | 13 | $a.each(function() { 14 | 15 | var $this = $(this), 16 | indent = Math.max(0, $this.parents('li').length - 1), 17 | href = $this.attr('href'), 18 | target = $this.attr('target'); 19 | 20 | b.push( 21 | '' + 26 | '' + 27 | $this.text() + 28 | '' 29 | ); 30 | 31 | }); 32 | 33 | return b.join(''); 34 | 35 | }; 36 | 37 | /** 38 | * Panel-ify an element. 39 | * @param {object} userConfig User config. 40 | * @return {jQuery} jQuery object. 41 | */ 42 | $.fn.panel = function(userConfig) { 43 | 44 | // No elements? 45 | if (this.length == 0) 46 | return $this; 47 | 48 | // Multiple elements? 49 | if (this.length > 1) { 50 | 51 | for (var i=0; i < this.length; i++) 52 | $(this[i]).panel(userConfig); 53 | 54 | return $this; 55 | 56 | } 57 | 58 | // Vars. 59 | var $this = $(this), 60 | $body = $('body'), 61 | $window = $(window), 62 | id = $this.attr('id'), 63 | config; 64 | 65 | // Config. 66 | config = $.extend({ 67 | 68 | // Delay. 69 | delay: 0, 70 | 71 | // Hide panel on link click. 72 | hideOnClick: false, 73 | 74 | // Hide panel on escape keypress. 75 | hideOnEscape: false, 76 | 77 | // Hide panel on swipe. 78 | hideOnSwipe: false, 79 | 80 | // Reset scroll position on hide. 81 | resetScroll: false, 82 | 83 | // Reset forms on hide. 84 | resetForms: false, 85 | 86 | // Side of viewport the panel will appear. 87 | side: null, 88 | 89 | // Target element for "class". 90 | target: $this, 91 | 92 | // Class to toggle. 93 | visibleClass: 'visible' 94 | 95 | }, userConfig); 96 | 97 | // Expand "target" if it's not a jQuery object already. 98 | if (typeof config.target != 'jQuery') 99 | config.target = $(config.target); 100 | 101 | // Panel. 102 | 103 | // Methods. 104 | $this._hide = function(event) { 105 | 106 | // Already hidden? Bail. 107 | if (!config.target.hasClass(config.visibleClass)) 108 | return; 109 | 110 | // If an event was provided, cancel it. 111 | if (event) { 112 | 113 | event.preventDefault(); 114 | event.stopPropagation(); 115 | 116 | } 117 | 118 | // Hide. 119 | config.target.removeClass(config.visibleClass); 120 | 121 | // Post-hide stuff. 122 | window.setTimeout(function() { 123 | 124 | // Reset scroll position. 125 | if (config.resetScroll) 126 | $this.scrollTop(0); 127 | 128 | // Reset forms. 129 | if (config.resetForms) 130 | $this.find('form').each(function() { 131 | this.reset(); 132 | }); 133 | 134 | }, config.delay); 135 | 136 | }; 137 | 138 | // Vendor fixes. 139 | $this 140 | .css('-ms-overflow-style', '-ms-autohiding-scrollbar') 141 | .css('-webkit-overflow-scrolling', 'touch'); 142 | 143 | // Hide on click. 144 | if (config.hideOnClick) { 145 | 146 | $this.find('a') 147 | .css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)'); 148 | 149 | $this 150 | .on('click', 'a', function(event) { 151 | 152 | var $a = $(this), 153 | href = $a.attr('href'), 154 | target = $a.attr('target'); 155 | 156 | if (!href || href == '#' || href == '' || href == '#' + id) 157 | return; 158 | 159 | // Cancel original event. 160 | event.preventDefault(); 161 | event.stopPropagation(); 162 | 163 | // Hide panel. 164 | $this._hide(); 165 | 166 | // Redirect to href. 167 | window.setTimeout(function() { 168 | 169 | if (target == '_blank') 170 | window.open(href); 171 | else 172 | window.location.href = href; 173 | 174 | }, config.delay + 10); 175 | 176 | }); 177 | 178 | } 179 | 180 | // Event: Touch stuff. 181 | $this.on('touchstart', function(event) { 182 | 183 | $this.touchPosX = event.originalEvent.touches[0].pageX; 184 | $this.touchPosY = event.originalEvent.touches[0].pageY; 185 | 186 | }) 187 | 188 | $this.on('touchmove', function(event) { 189 | 190 | if ($this.touchPosX === null 191 | || $this.touchPosY === null) 192 | return; 193 | 194 | var diffX = $this.touchPosX - event.originalEvent.touches[0].pageX, 195 | diffY = $this.touchPosY - event.originalEvent.touches[0].pageY, 196 | th = $this.outerHeight(), 197 | ts = ($this.get(0).scrollHeight - $this.scrollTop()); 198 | 199 | // Hide on swipe? 200 | if (config.hideOnSwipe) { 201 | 202 | var result = false, 203 | boundary = 20, 204 | delta = 50; 205 | 206 | switch (config.side) { 207 | 208 | case 'left': 209 | result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta); 210 | break; 211 | 212 | case 'right': 213 | result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta)); 214 | break; 215 | 216 | case 'top': 217 | result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta); 218 | break; 219 | 220 | case 'bottom': 221 | result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta)); 222 | break; 223 | 224 | default: 225 | break; 226 | 227 | } 228 | 229 | if (result) { 230 | 231 | $this.touchPosX = null; 232 | $this.touchPosY = null; 233 | $this._hide(); 234 | 235 | return false; 236 | 237 | } 238 | 239 | } 240 | 241 | // Prevent vertical scrolling past the top or bottom. 242 | if (($this.scrollTop() < 0 && diffY < 0) 243 | || (ts > (th - 2) && ts < (th + 2) && diffY > 0)) { 244 | 245 | event.preventDefault(); 246 | event.stopPropagation(); 247 | 248 | } 249 | 250 | }); 251 | 252 | // Event: Prevent certain events inside the panel from bubbling. 253 | $this.on('click touchend touchstart touchmove', function(event) { 254 | event.stopPropagation(); 255 | }); 256 | 257 | // Event: Hide panel if a child anchor tag pointing to its ID is clicked. 258 | $this.on('click', 'a[href="#' + id + '"]', function(event) { 259 | 260 | event.preventDefault(); 261 | event.stopPropagation(); 262 | 263 | config.target.removeClass(config.visibleClass); 264 | 265 | }); 266 | 267 | // Body. 268 | 269 | // Event: Hide panel on body click/tap. 270 | $body.on('click touchend', function(event) { 271 | $this._hide(event); 272 | }); 273 | 274 | // Event: Toggle. 275 | $body.on('click', 'a[href="#' + id + '"]', function(event) { 276 | 277 | event.preventDefault(); 278 | event.stopPropagation(); 279 | 280 | config.target.toggleClass(config.visibleClass); 281 | 282 | }); 283 | 284 | // Window. 285 | 286 | // Event: Hide on ESC. 287 | if (config.hideOnEscape) 288 | $window.on('keydown', function(event) { 289 | 290 | if (event.keyCode == 27) 291 | $this._hide(event); 292 | 293 | }); 294 | 295 | return $this; 296 | 297 | }; 298 | 299 | /** 300 | * Apply "placeholder" attribute polyfill to one or more forms. 301 | * @return {jQuery} jQuery object. 302 | */ 303 | $.fn.placeholder = function() { 304 | 305 | // Browser natively supports placeholders? Bail. 306 | if (typeof (document.createElement('input')).placeholder != 'undefined') 307 | return $(this); 308 | 309 | // No elements? 310 | if (this.length == 0) 311 | return $this; 312 | 313 | // Multiple elements? 314 | if (this.length > 1) { 315 | 316 | for (var i=0; i < this.length; i++) 317 | $(this[i]).placeholder(); 318 | 319 | return $this; 320 | 321 | } 322 | 323 | // Vars. 324 | var $this = $(this); 325 | 326 | // Text, TextArea. 327 | $this.find('input[type=text],textarea') 328 | .each(function() { 329 | 330 | var i = $(this); 331 | 332 | if (i.val() == '' 333 | || i.val() == i.attr('placeholder')) 334 | i 335 | .addClass('polyfill-placeholder') 336 | .val(i.attr('placeholder')); 337 | 338 | }) 339 | .on('blur', function() { 340 | 341 | var i = $(this); 342 | 343 | if (i.attr('name').match(/-polyfill-field$/)) 344 | return; 345 | 346 | if (i.val() == '') 347 | i 348 | .addClass('polyfill-placeholder') 349 | .val(i.attr('placeholder')); 350 | 351 | }) 352 | .on('focus', function() { 353 | 354 | var i = $(this); 355 | 356 | if (i.attr('name').match(/-polyfill-field$/)) 357 | return; 358 | 359 | if (i.val() == i.attr('placeholder')) 360 | i 361 | .removeClass('polyfill-placeholder') 362 | .val(''); 363 | 364 | }); 365 | 366 | // Password. 367 | $this.find('input[type=password]') 368 | .each(function() { 369 | 370 | var i = $(this); 371 | var x = $( 372 | $('
') 373 | .append(i.clone()) 374 | .remove() 375 | .html() 376 | .replace(/type="password"/i, 'type="text"') 377 | .replace(/type=password/i, 'type=text') 378 | ); 379 | 380 | if (i.attr('id') != '') 381 | x.attr('id', i.attr('id') + '-polyfill-field'); 382 | 383 | if (i.attr('name') != '') 384 | x.attr('name', i.attr('name') + '-polyfill-field'); 385 | 386 | x.addClass('polyfill-placeholder') 387 | .val(x.attr('placeholder')).insertAfter(i); 388 | 389 | if (i.val() == '') 390 | i.hide(); 391 | else 392 | x.hide(); 393 | 394 | i 395 | .on('blur', function(event) { 396 | 397 | event.preventDefault(); 398 | 399 | var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]'); 400 | 401 | if (i.val() == '') { 402 | 403 | i.hide(); 404 | x.show(); 405 | 406 | } 407 | 408 | }); 409 | 410 | x 411 | .on('focus', function(event) { 412 | 413 | event.preventDefault(); 414 | 415 | var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', '') + ']'); 416 | 417 | x.hide(); 418 | 419 | i 420 | .show() 421 | .focus(); 422 | 423 | }) 424 | .on('keypress', function(event) { 425 | 426 | event.preventDefault(); 427 | x.val(''); 428 | 429 | }); 430 | 431 | }); 432 | 433 | // Events. 434 | $this 435 | .on('submit', function() { 436 | 437 | $this.find('input[type=text],input[type=password],textarea') 438 | .each(function(event) { 439 | 440 | var i = $(this); 441 | 442 | if (i.attr('name').match(/-polyfill-field$/)) 443 | i.attr('name', ''); 444 | 445 | if (i.val() == i.attr('placeholder')) { 446 | 447 | i.removeClass('polyfill-placeholder'); 448 | i.val(''); 449 | 450 | } 451 | 452 | }); 453 | 454 | }) 455 | .on('reset', function(event) { 456 | 457 | event.preventDefault(); 458 | 459 | $this.find('select') 460 | .val($('option:first').val()); 461 | 462 | $this.find('input,textarea') 463 | .each(function() { 464 | 465 | var i = $(this), 466 | x; 467 | 468 | i.removeClass('polyfill-placeholder'); 469 | 470 | switch (this.type) { 471 | 472 | case 'submit': 473 | case 'reset': 474 | break; 475 | 476 | case 'password': 477 | i.val(i.attr('defaultValue')); 478 | 479 | x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]'); 480 | 481 | if (i.val() == '') { 482 | i.hide(); 483 | x.show(); 484 | } 485 | else { 486 | i.show(); 487 | x.hide(); 488 | } 489 | 490 | break; 491 | 492 | case 'checkbox': 493 | case 'radio': 494 | i.attr('checked', i.attr('defaultValue')); 495 | break; 496 | 497 | case 'text': 498 | case 'textarea': 499 | i.val(i.attr('defaultValue')); 500 | 501 | if (i.val() == '') { 502 | i.addClass('polyfill-placeholder'); 503 | i.val(i.attr('placeholder')); 504 | } 505 | 506 | break; 507 | 508 | default: 509 | i.val(i.attr('defaultValue')); 510 | break; 511 | 512 | } 513 | }); 514 | 515 | }); 516 | 517 | return $this; 518 | 519 | }; 520 | 521 | /** 522 | * Moves elements to/from the first positions of their respective parents. 523 | * @param {jQuery} $elements Elements (or selector) to move. 524 | * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations. 525 | */ 526 | $.prioritize = function($elements, condition) { 527 | 528 | var key = '__prioritize'; 529 | 530 | // Expand $elements if it's not already a jQuery object. 531 | if (typeof $elements != 'jQuery') 532 | $elements = $($elements); 533 | 534 | // Step through elements. 535 | $elements.each(function() { 536 | 537 | var $e = $(this), $p, 538 | $parent = $e.parent(); 539 | 540 | // No parent? Bail. 541 | if ($parent.length == 0) 542 | return; 543 | 544 | // Not moved? Move it. 545 | if (!$e.data(key)) { 546 | 547 | // Condition is false? Bail. 548 | if (!condition) 549 | return; 550 | 551 | // Get placeholder (which will serve as our point of reference for when this element needs to move back). 552 | $p = $e.prev(); 553 | 554 | // Couldn't find anything? Means this element's already at the top, so bail. 555 | if ($p.length == 0) 556 | return; 557 | 558 | // Move element to top of parent. 559 | $e.prependTo($parent); 560 | 561 | // Mark element as moved. 562 | $e.data(key, $p); 563 | 564 | } 565 | 566 | // Moved already? 567 | else { 568 | 569 | // Condition is true? Bail. 570 | if (condition) 571 | return; 572 | 573 | $p = $e.data(key); 574 | 575 | // Move element back to its original location (using our placeholder). 576 | $e.insertAfter($p); 577 | 578 | // Unmark element as moved. 579 | $e.removeData(key); 580 | 581 | } 582 | 583 | }); 584 | 585 | }; 586 | 587 | })(jQuery); -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "4.0.2" 12 | args: 13 | dependency: transitive 14 | description: 15 | name: args 16 | sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.6.0" 20 | async: 21 | dependency: transitive 22 | description: 23 | name: async 24 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.11.0" 28 | boolean_selector: 29 | dependency: transitive 30 | description: 31 | name: boolean_selector 32 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "2.1.1" 36 | characters: 37 | dependency: transitive 38 | description: 39 | name: characters 40 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.3.0" 44 | checked_yaml: 45 | dependency: transitive 46 | description: 47 | name: checked_yaml 48 | sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "2.0.3" 52 | cli_util: 53 | dependency: transitive 54 | description: 55 | name: cli_util 56 | sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "0.4.2" 60 | clock: 61 | dependency: transitive 62 | description: 63 | name: clock 64 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.1.1" 68 | collection: 69 | dependency: transitive 70 | description: 71 | name: collection 72 | sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "1.19.0" 76 | connectivity_plus: 77 | dependency: "direct main" 78 | description: 79 | name: connectivity_plus 80 | sha256: e0817759ec6d2d8e57eb234e6e57d2173931367a865850c7acea40d4b4f9c27d 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "6.1.1" 84 | connectivity_plus_platform_interface: 85 | dependency: transitive 86 | description: 87 | name: connectivity_plus_platform_interface 88 | sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "2.0.1" 92 | crypto: 93 | dependency: transitive 94 | description: 95 | name: crypto 96 | sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "3.0.6" 100 | dbus: 101 | dependency: transitive 102 | description: 103 | name: dbus 104 | sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "0.7.10" 108 | fake_async: 109 | dependency: transitive 110 | description: 111 | name: fake_async 112 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "1.3.1" 116 | ffi: 117 | dependency: transitive 118 | description: 119 | name: ffi 120 | sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "2.1.3" 124 | fixnum: 125 | dependency: transitive 126 | description: 127 | name: fixnum 128 | sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be 129 | url: "https://pub.dev" 130 | source: hosted 131 | version: "1.1.1" 132 | flutter: 133 | dependency: "direct main" 134 | description: flutter 135 | source: sdk 136 | version: "0.0.0" 137 | flutter_launcher_icons: 138 | dependency: "direct main" 139 | description: 140 | name: flutter_launcher_icons 141 | sha256: "31cd0885738e87c72d6f055564d37fabcdacee743b396b78c7636c169cac64f5" 142 | url: "https://pub.dev" 143 | source: hosted 144 | version: "0.14.2" 145 | flutter_lints: 146 | dependency: "direct dev" 147 | description: 148 | name: flutter_lints 149 | sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" 150 | url: "https://pub.dev" 151 | source: hosted 152 | version: "5.0.0" 153 | flutter_test: 154 | dependency: "direct dev" 155 | description: flutter 156 | source: sdk 157 | version: "0.0.0" 158 | flutter_web_plugins: 159 | dependency: transitive 160 | description: flutter 161 | source: sdk 162 | version: "0.0.0" 163 | geolocator: 164 | dependency: "direct main" 165 | description: 166 | name: geolocator 167 | sha256: d2ec66329cab29cb297d51d96c067d457ca519dca8589665fa0b82ebacb7dbe4 168 | url: "https://pub.dev" 169 | source: hosted 170 | version: "13.0.2" 171 | geolocator_android: 172 | dependency: transitive 173 | description: 174 | name: geolocator_android 175 | sha256: "7aefc530db47d90d0580b552df3242440a10fe60814496a979aa67aa98b1fd47" 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "4.6.1" 179 | geolocator_apple: 180 | dependency: transitive 181 | description: 182 | name: geolocator_apple 183 | sha256: "6154ea2682563f69fc0125762ed7e91e7ed85d0b9776595653be33918e064807" 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "2.3.8+1" 187 | geolocator_platform_interface: 188 | dependency: transitive 189 | description: 190 | name: geolocator_platform_interface 191 | sha256: "386ce3d9cce47838355000070b1d0b13efb5bc430f8ecda7e9238c8409ace012" 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "4.2.4" 195 | geolocator_web: 196 | dependency: transitive 197 | description: 198 | name: geolocator_web 199 | sha256: "2ed69328e05cd94e7eb48bb0535f5fc0c0c44d1c4fa1e9737267484d05c29b5e" 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "4.1.1" 203 | geolocator_windows: 204 | dependency: transitive 205 | description: 206 | name: geolocator_windows 207 | sha256: "53da08937d07c24b0d9952eb57a3b474e29aae2abf9dd717f7e1230995f13f0e" 208 | url: "https://pub.dev" 209 | source: hosted 210 | version: "0.2.3" 211 | google_fonts: 212 | dependency: "direct main" 213 | description: 214 | name: google_fonts 215 | sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82 216 | url: "https://pub.dev" 217 | source: hosted 218 | version: "6.2.1" 219 | http: 220 | dependency: "direct main" 221 | description: 222 | name: http 223 | sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 224 | url: "https://pub.dev" 225 | source: hosted 226 | version: "1.2.2" 227 | http_parser: 228 | dependency: transitive 229 | description: 230 | name: http_parser 231 | sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" 232 | url: "https://pub.dev" 233 | source: hosted 234 | version: "4.1.2" 235 | image: 236 | dependency: transitive 237 | description: 238 | name: image 239 | sha256: "8346ad4b5173924b5ddddab782fc7d8a6300178c8b1dc427775405a01701c4a6" 240 | url: "https://pub.dev" 241 | source: hosted 242 | version: "4.5.2" 243 | intl: 244 | dependency: "direct main" 245 | description: 246 | name: intl 247 | sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50" 248 | url: "https://pub.dev" 249 | source: hosted 250 | version: "0.20.1" 251 | json_annotation: 252 | dependency: transitive 253 | description: 254 | name: json_annotation 255 | sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" 256 | url: "https://pub.dev" 257 | source: hosted 258 | version: "4.9.0" 259 | leak_tracker: 260 | dependency: transitive 261 | description: 262 | name: leak_tracker 263 | sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" 264 | url: "https://pub.dev" 265 | source: hosted 266 | version: "10.0.7" 267 | leak_tracker_flutter_testing: 268 | dependency: transitive 269 | description: 270 | name: leak_tracker_flutter_testing 271 | sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" 272 | url: "https://pub.dev" 273 | source: hosted 274 | version: "3.0.8" 275 | leak_tracker_testing: 276 | dependency: transitive 277 | description: 278 | name: leak_tracker_testing 279 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 280 | url: "https://pub.dev" 281 | source: hosted 282 | version: "3.0.1" 283 | lints: 284 | dependency: transitive 285 | description: 286 | name: lints 287 | sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 288 | url: "https://pub.dev" 289 | source: hosted 290 | version: "5.1.1" 291 | matcher: 292 | dependency: transitive 293 | description: 294 | name: matcher 295 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 296 | url: "https://pub.dev" 297 | source: hosted 298 | version: "0.12.16+1" 299 | material_color_utilities: 300 | dependency: transitive 301 | description: 302 | name: material_color_utilities 303 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 304 | url: "https://pub.dev" 305 | source: hosted 306 | version: "0.11.1" 307 | meta: 308 | dependency: transitive 309 | description: 310 | name: meta 311 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 312 | url: "https://pub.dev" 313 | source: hosted 314 | version: "1.15.0" 315 | nm: 316 | dependency: transitive 317 | description: 318 | name: nm 319 | sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" 320 | url: "https://pub.dev" 321 | source: hosted 322 | version: "0.5.0" 323 | path: 324 | dependency: transitive 325 | description: 326 | name: path 327 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 328 | url: "https://pub.dev" 329 | source: hosted 330 | version: "1.9.0" 331 | path_provider: 332 | dependency: transitive 333 | description: 334 | name: path_provider 335 | sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" 336 | url: "https://pub.dev" 337 | source: hosted 338 | version: "2.1.5" 339 | path_provider_android: 340 | dependency: transitive 341 | description: 342 | name: path_provider_android 343 | sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" 344 | url: "https://pub.dev" 345 | source: hosted 346 | version: "2.2.15" 347 | path_provider_foundation: 348 | dependency: transitive 349 | description: 350 | name: path_provider_foundation 351 | sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" 352 | url: "https://pub.dev" 353 | source: hosted 354 | version: "2.4.1" 355 | path_provider_linux: 356 | dependency: transitive 357 | description: 358 | name: path_provider_linux 359 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 360 | url: "https://pub.dev" 361 | source: hosted 362 | version: "2.2.1" 363 | path_provider_platform_interface: 364 | dependency: transitive 365 | description: 366 | name: path_provider_platform_interface 367 | sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" 368 | url: "https://pub.dev" 369 | source: hosted 370 | version: "2.1.2" 371 | path_provider_windows: 372 | dependency: transitive 373 | description: 374 | name: path_provider_windows 375 | sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 376 | url: "https://pub.dev" 377 | source: hosted 378 | version: "2.3.0" 379 | permission_handler: 380 | dependency: "direct main" 381 | description: 382 | name: permission_handler 383 | sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb" 384 | url: "https://pub.dev" 385 | source: hosted 386 | version: "11.3.1" 387 | permission_handler_android: 388 | dependency: transitive 389 | description: 390 | name: permission_handler_android 391 | sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1" 392 | url: "https://pub.dev" 393 | source: hosted 394 | version: "12.0.13" 395 | permission_handler_apple: 396 | dependency: transitive 397 | description: 398 | name: permission_handler_apple 399 | sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0 400 | url: "https://pub.dev" 401 | source: hosted 402 | version: "9.4.5" 403 | permission_handler_html: 404 | dependency: transitive 405 | description: 406 | name: permission_handler_html 407 | sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" 408 | url: "https://pub.dev" 409 | source: hosted 410 | version: "0.1.3+5" 411 | permission_handler_platform_interface: 412 | dependency: transitive 413 | description: 414 | name: permission_handler_platform_interface 415 | sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9 416 | url: "https://pub.dev" 417 | source: hosted 418 | version: "4.2.3" 419 | permission_handler_windows: 420 | dependency: transitive 421 | description: 422 | name: permission_handler_windows 423 | sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" 424 | url: "https://pub.dev" 425 | source: hosted 426 | version: "0.2.1" 427 | petitparser: 428 | dependency: transitive 429 | description: 430 | name: petitparser 431 | sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 432 | url: "https://pub.dev" 433 | source: hosted 434 | version: "6.0.2" 435 | platform: 436 | dependency: transitive 437 | description: 438 | name: platform 439 | sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" 440 | url: "https://pub.dev" 441 | source: hosted 442 | version: "3.1.6" 443 | plugin_platform_interface: 444 | dependency: transitive 445 | description: 446 | name: plugin_platform_interface 447 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 448 | url: "https://pub.dev" 449 | source: hosted 450 | version: "2.1.8" 451 | posix: 452 | dependency: transitive 453 | description: 454 | name: posix 455 | sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a 456 | url: "https://pub.dev" 457 | source: hosted 458 | version: "6.0.1" 459 | sky_engine: 460 | dependency: transitive 461 | description: flutter 462 | source: sdk 463 | version: "0.0.0" 464 | source_span: 465 | dependency: transitive 466 | description: 467 | name: source_span 468 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 469 | url: "https://pub.dev" 470 | source: hosted 471 | version: "1.10.0" 472 | sprintf: 473 | dependency: transitive 474 | description: 475 | name: sprintf 476 | sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" 477 | url: "https://pub.dev" 478 | source: hosted 479 | version: "7.0.0" 480 | stack_trace: 481 | dependency: transitive 482 | description: 483 | name: stack_trace 484 | sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" 485 | url: "https://pub.dev" 486 | source: hosted 487 | version: "1.12.0" 488 | stream_channel: 489 | dependency: transitive 490 | description: 491 | name: stream_channel 492 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 493 | url: "https://pub.dev" 494 | source: hosted 495 | version: "2.1.2" 496 | string_scanner: 497 | dependency: transitive 498 | description: 499 | name: string_scanner 500 | sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" 501 | url: "https://pub.dev" 502 | source: hosted 503 | version: "1.3.0" 504 | term_glyph: 505 | dependency: transitive 506 | description: 507 | name: term_glyph 508 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 509 | url: "https://pub.dev" 510 | source: hosted 511 | version: "1.2.1" 512 | test_api: 513 | dependency: transitive 514 | description: 515 | name: test_api 516 | sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" 517 | url: "https://pub.dev" 518 | source: hosted 519 | version: "0.7.3" 520 | typed_data: 521 | dependency: transitive 522 | description: 523 | name: typed_data 524 | sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 525 | url: "https://pub.dev" 526 | source: hosted 527 | version: "1.4.0" 528 | url_launcher: 529 | dependency: "direct main" 530 | description: 531 | name: url_launcher 532 | sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" 533 | url: "https://pub.dev" 534 | source: hosted 535 | version: "6.3.1" 536 | url_launcher_android: 537 | dependency: transitive 538 | description: 539 | name: url_launcher_android 540 | sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" 541 | url: "https://pub.dev" 542 | source: hosted 543 | version: "6.3.14" 544 | url_launcher_ios: 545 | dependency: transitive 546 | description: 547 | name: url_launcher_ios 548 | sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626" 549 | url: "https://pub.dev" 550 | source: hosted 551 | version: "6.3.2" 552 | url_launcher_linux: 553 | dependency: transitive 554 | description: 555 | name: url_launcher_linux 556 | sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" 557 | url: "https://pub.dev" 558 | source: hosted 559 | version: "3.2.1" 560 | url_launcher_macos: 561 | dependency: transitive 562 | description: 563 | name: url_launcher_macos 564 | sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" 565 | url: "https://pub.dev" 566 | source: hosted 567 | version: "3.2.2" 568 | url_launcher_platform_interface: 569 | dependency: transitive 570 | description: 571 | name: url_launcher_platform_interface 572 | sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" 573 | url: "https://pub.dev" 574 | source: hosted 575 | version: "2.3.2" 576 | url_launcher_web: 577 | dependency: transitive 578 | description: 579 | name: url_launcher_web 580 | sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" 581 | url: "https://pub.dev" 582 | source: hosted 583 | version: "2.3.3" 584 | url_launcher_windows: 585 | dependency: transitive 586 | description: 587 | name: url_launcher_windows 588 | sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" 589 | url: "https://pub.dev" 590 | source: hosted 591 | version: "3.1.3" 592 | uuid: 593 | dependency: transitive 594 | description: 595 | name: uuid 596 | sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff 597 | url: "https://pub.dev" 598 | source: hosted 599 | version: "4.5.1" 600 | vector_math: 601 | dependency: transitive 602 | description: 603 | name: vector_math 604 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 605 | url: "https://pub.dev" 606 | source: hosted 607 | version: "2.1.4" 608 | vm_service: 609 | dependency: transitive 610 | description: 611 | name: vm_service 612 | sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b 613 | url: "https://pub.dev" 614 | source: hosted 615 | version: "14.3.0" 616 | web: 617 | dependency: transitive 618 | description: 619 | name: web 620 | sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb 621 | url: "https://pub.dev" 622 | source: hosted 623 | version: "1.1.0" 624 | xdg_directories: 625 | dependency: transitive 626 | description: 627 | name: xdg_directories 628 | sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" 629 | url: "https://pub.dev" 630 | source: hosted 631 | version: "1.1.0" 632 | xml: 633 | dependency: transitive 634 | description: 635 | name: xml 636 | sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 637 | url: "https://pub.dev" 638 | source: hosted 639 | version: "6.5.0" 640 | yaml: 641 | dependency: transitive 642 | description: 643 | name: yaml 644 | sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce 645 | url: "https://pub.dev" 646 | source: hosted 647 | version: "3.1.3" 648 | sdks: 649 | dart: ">=3.6.0 <4.0.0" 650 | flutter: ">=3.24.0" 651 | --------------------------------------------------------------------------------