├── .flutter-plugins
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── app
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── jhomlala
│ │ │ └── feather
│ │ │ └── MainActivity.java
│ │ └── res
│ │ ├── drawable
│ │ ├── feather_loading.png
│ │ └── launch_background.xml
│ │ ├── 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
│ │ └── styles.xml
├── build.gradle
├── feather_android.iml
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle
├── assets
├── icon_barometer.png
├── icon_cloud.png
├── icon_cloud_little_rain.png
├── icon_cloud_sun.png
├── icon_dust.png
├── icon_logo.png
├── icon_rain.png
├── icon_snow.png
├── icon_sun.png
├── icon_thermometer.png
├── icon_thunder.png
└── icon_wind.png
├── feather.iml
├── ios
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ └── contents.xcworkspacedata
└── Runner
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-App-1024x1024@1x.png
│ │ ├── Icon-App-20x20@1x.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@1x.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ ├── Icon-App-76x76@1x.png
│ │ ├── Icon-App-76x76@2x.png
│ │ └── Icon-App-83.5x83.5@2x.png
│ └── LaunchImage.imageset
│ │ ├── Contents.json
│ │ ├── LaunchImage.png
│ │ ├── LaunchImage@2x.png
│ │ ├── LaunchImage@3x.png
│ │ └── README.md
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ └── main.m
├── l10n.yaml
├── lib
├── main.dart
└── src
│ ├── data
│ ├── model
│ │ ├── internal
│ │ │ ├── application_error.dart
│ │ │ ├── chart_data.dart
│ │ │ ├── chart_line.dart
│ │ │ ├── forecast_navigation_params.dart
│ │ │ ├── geo_position.dart
│ │ │ ├── navigation_route.dart
│ │ │ ├── overflow_menu_element.dart
│ │ │ ├── pair.dart
│ │ │ ├── point.dart
│ │ │ ├── settings_navigation_params.dart
│ │ │ ├── unit.dart
│ │ │ └── weather_forecast_holder.dart
│ │ └── remote
│ │ │ ├── city.dart
│ │ │ ├── clouds.dart
│ │ │ ├── coordinates.dart
│ │ │ ├── main_weather_data.dart
│ │ │ ├── overall_weather_data.dart
│ │ │ ├── rain.dart
│ │ │ ├── system.dart
│ │ │ ├── weather_forecast_list_response.dart
│ │ │ ├── weather_forecast_response.dart
│ │ │ ├── weather_response.dart
│ │ │ └── wind.dart
│ └── repository
│ │ ├── local
│ │ ├── application_local_repository.dart
│ │ ├── location_manager.dart
│ │ ├── location_provider.dart
│ │ ├── storage_manager.dart
│ │ ├── storage_provider.dart
│ │ ├── weather_helper.dart
│ │ └── weather_local_repository.dart
│ │ └── remote
│ │ ├── weather_api_provider.dart
│ │ └── weather_remote_repository.dart
│ ├── l10n
│ ├── app_en.arb
│ └── app_pl.arb
│ ├── resources
│ └── config
│ │ ├── application_colors.dart
│ │ ├── application_config.dart
│ │ ├── assets.dart
│ │ ├── dimensions.dart
│ │ └── ids.dart
│ ├── ui
│ ├── about
│ │ ├── about_screen.dart
│ │ └── bloc
│ │ │ ├── about_screen_bloc.dart
│ │ │ ├── about_screen_event.dart
│ │ │ └── about_screen_state.dart
│ ├── app
│ │ ├── app_bloc.dart
│ │ ├── app_event.dart
│ │ └── app_state.dart
│ ├── forecast
│ │ ├── weather_forecast_screen.dart
│ │ └── widget
│ │ │ ├── chart_widget.dart
│ │ │ ├── weather_forecast_base_page.dart
│ │ │ ├── weather_forecast_pressure_page.dart
│ │ │ ├── weather_forecast_rain_page.dart
│ │ │ ├── weather_forecast_temperature_page.dart
│ │ │ ├── weather_forecast_widget.dart
│ │ │ └── weather_forecast_wind_page.dart
│ ├── main
│ │ ├── bloc
│ │ │ ├── main_screen_bloc.dart
│ │ │ ├── main_screen_event.dart
│ │ │ └── main_screen_state.dart
│ │ ├── main_screen.dart
│ │ └── widget
│ │ │ ├── sun_path_widget.dart
│ │ │ ├── weather_forecast_thumbnail_list_widget.dart
│ │ │ ├── weather_forecast_thumbnail_widget.dart
│ │ │ └── weather_main_sun_path_widget.dart
│ ├── navigation
│ │ ├── bloc
│ │ │ ├── navigation_bloc.dart
│ │ │ ├── navigation_event.dart
│ │ │ └── navigation_state.dart
│ │ └── navigation_provider.dart
│ ├── settings
│ │ ├── bloc
│ │ │ ├── settings_screen_bloc.dart
│ │ │ ├── settings_screen_event.dart
│ │ │ └── settings_screen_state.dart
│ │ └── settings_screen.dart
│ └── widget
│ │ ├── animated_gradient.dart
│ │ ├── animated_state.dart
│ │ ├── animated_text_widget.dart
│ │ ├── current_weather_widget.dart
│ │ ├── empty_animation.dart
│ │ ├── loading_widget.dart
│ │ ├── transparent_app_bar.dart
│ │ └── widget_helper.dart
│ └── utils
│ ├── app_logger.dart
│ ├── date_time_helper.dart
│ └── types_helper.dart
├── media
├── 1.png
├── 10.png
├── 11.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── 6.png
├── 7.png
├── 8.png
├── 9.png
├── logo.png
└── video.gif
├── pubspec.lock
├── pubspec.yaml
└── test
├── data
├── model
│ └── weather_utils.dart
└── repository
│ ├── local
│ ├── application_local_repository_test.dart
│ ├── fake_location_provider.dart
│ ├── fake_storage_manager.dart
│ ├── fake_storage_provider.dart
│ ├── location_manager_test.dart
│ ├── storage_manager_test.dart
│ ├── weather_helper_test.dart
│ └── weather_local_repository_test.dart
│ └── remote
│ ├── fake_weather_api_provider.dart
│ ├── weather_api_provider_test.dart
│ └── weather_remote_repository_test.dart
├── test_helper.dart
├── ui
├── about
│ ├── about_screen_bloc_test.dart
│ └── about_screen_test.dart
├── app
│ └── app_bloc_test.dart
├── forecast
│ ├── chart_widget_test.dart
│ ├── weather_forecast_pressure_page_test.dart
│ ├── weather_forecast_rain_page_test.dart
│ ├── weather_forecast_temperature_page_test.dart
│ ├── weather_forecast_widget_test.dart
│ └── weather_forecast_wind_page_test.dart
├── main
│ ├── bloc
│ │ └── main_screen_bloc_test.dart
│ ├── main_screen_test.dart
│ └── widget
│ │ ├── sun_path_widget_test.dart
│ │ ├── weather_current_widget_test.dart
│ │ ├── weather_forecast_thumbnail_list_widget_test.dart
│ │ ├── weather_forecast_thumbnail_widget_test.dart
│ │ └── weather_main_sun_path_page_test.dart
├── navigation
│ └── bloc
│ │ ├── fake_navigation_provider.dart
│ │ ├── navigation_bloc_test.dart
│ │ └── navigation_provider_test.dart
├── settings
│ ├── bloc
│ │ └── settings_bloc_test.dart
│ └── widget
│ │ └── settings_screen_test.dart
└── widget
│ └── gradient_cycle_test.dart
├── utils
└── types_helper_test.dart
└── weather_api_provider_test.dart
/.flutter-plugins:
--------------------------------------------------------------------------------
1 | # This is a generated file; do not edit or check into version control.
2 | app_settings=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/app_settings-4.1.0/
3 | geolocator=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator-7.0.3/
4 | geolocator_web=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/geolocator_web-2.0.3/
5 | package_info=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/package_info-2.0.0/
6 | path_provider_linux=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.0.0/
7 | path_provider_windows=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.0.0/
8 | shared_preferences=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.5/
9 | shared_preferences_linux=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_linux-2.0.0/
10 | shared_preferences_macos=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_macos-2.0.0/
11 | shared_preferences_web=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_web-2.0.0/
12 | shared_preferences_windows=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_windows-2.0.0/
13 | url_launcher=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.3/
14 | url_launcher_linux=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-2.0.0/
15 | url_launcher_macos=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-2.0.0/
16 | url_launcher_web=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-2.0.0/
17 | url_launcher_windows=/Users/jakubhomlala/development/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-2.0.0/
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 | .idea
4 |
5 | .packages
6 | .pub/
7 | .idea/
8 |
9 | build/
10 | ios/.generated/
11 | ios/Flutter/Generated.xcconfig
12 | ios/Runner/GeneratedPluginRegistrant.*
13 |
14 | *.txt
15 | android/.gradle
16 |
17 | .flutter-plugins
18 | *.lock
19 | *.lock
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - linux
3 | sudo: false
4 | addons:
5 | apt:
6 | # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18
7 | sources:
8 | - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version
9 | packages:
10 | #- libstdc++6
11 | - lib32stdc++6
12 | - fonts-droid
13 | before_script:
14 | - git clone https://github.com/flutter/flutter.git
15 | - ./flutter/bin/flutter doctor
16 | script:
17 | - ./flutter/bin/flutter test
18 | cache:
19 | directories:
20 | - $HOME/.pub-cache
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # :sunny: Feather
6 | [](https://github.com/jhomlala/feather)
7 | [](https://github.com/Solido/awesome-flutter)
8 |
9 |
10 | Beautiful Flutter weather application. Entirely written in Dart and Flutter. Application is ready for Android and iOS.
11 |
12 | ### :camera: Media
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 | |
48 |
49 |
50 | |
51 |
52 |
53 | |
54 |
55 |
56 |
57 | ## :cloud: Features
58 | :heavy_check_mark: Beautiful UI and great UX
59 | :heavy_check_mark: Current weather: current temperature, max and min temperature, humidity, pressure, wind
60 | :heavy_check_mark: Current sun/moon position, animated countdown until sunset/sunrise, time of sunset/sunrise
61 | :heavy_check_mark: Weather forecast for 5 days (temperature, wind, rain and pressure)
62 | :heavy_check_mark: Custom-written chart with animation
63 | :heavy_check_mark: Sun/moon animation
64 | :heavy_check_mark: App background based on day cycle
65 | :heavy_check_mark: Automatically picks user location (also error handling when location can't be selected!)
66 | :heavy_check_mark: Persist location and weather data in local storage
67 | :heavy_check_mark: Works offline (user need to download data before)
68 | :heavy_check_mark: Automatically refresh data every 15 minutes
69 | :heavy_check_mark: I18n support (currently PL and EN)
70 | :heavy_check_mark: Bloc architecture, Dio
71 | :heavy_check_mark: Unit and widget tests
72 | :heavy_check_mark: Bitrise CI/CD
73 |
74 | ## :cloud: Credits
75 | API: OpenWeatherAPI
76 | Icons: Icons8, FlatIcon
77 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:lint/analysis_options_package.yaml
2 |
3 | analyzer:
4 | strong-mode:
5 | implicit-dynamic: false
6 |
7 | linter:
8 | rules:
9 | close_sinks: true
10 | sort_constructors_first: false
11 | avoid_classes_with_only_static_members: false
12 | avoid_void_async: false
13 | avoid_positional_boolean_parameters: false
14 | avoid_function_literals_in_foreach_calls: false
15 | prefer_constructors_over_static_methods: false
16 | sort_unnamed_constructors_first: false
17 | sized_box_for_whitespace: false
18 | invalid_dependency: false
19 | sort_pub_dependencies: false
20 | implicit-dynamic: false
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 29
29 |
30 | lintOptions {
31 | disable 'InvalidPackage'
32 | }
33 |
34 | defaultConfig {
35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
36 | applicationId "com.jhomlala.feather"
37 | minSdkVersion 16
38 | targetSdkVersion 29
39 | versionCode flutterVersionCode.toInteger()
40 | versionName flutterVersionName
41 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
42 | }
43 |
44 | buildTypes {
45 | release {
46 | // TODO: Add your own signing config for the release build.
47 | // Signing with the debug keys for now, so `flutter run --release` works.
48 | signingConfig signingConfigs.debug
49 | }
50 | }
51 | }
52 |
53 | flutter {
54 | source '../..'
55 | }
56 |
57 | dependencies {
58 | testImplementation 'junit:junit:4.12'
59 | }
60 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
16 |
19 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/jhomlala/feather/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.jhomlala.feather;
2 |
3 | import io.flutter.embedding.android.FlutterActivity;
4 |
5 | public class MainActivity extends FlutterActivity {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/feather_loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/android/app/src/main/res/drawable/feather_loading.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | -
9 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.5.3'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/android/feather_android.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/local.properties:
--------------------------------------------------------------------------------
1 | sdk.dir=/Users/jakubhomlala/Library/Android/sdk
2 | flutter.sdk=/Users/jakubhomlala/development/flutter
3 | flutter.versionName=2.0.0
4 | flutter.buildMode=debug
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/assets/icon_barometer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_barometer.png
--------------------------------------------------------------------------------
/assets/icon_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_cloud.png
--------------------------------------------------------------------------------
/assets/icon_cloud_little_rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_cloud_little_rain.png
--------------------------------------------------------------------------------
/assets/icon_cloud_sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_cloud_sun.png
--------------------------------------------------------------------------------
/assets/icon_dust.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_dust.png
--------------------------------------------------------------------------------
/assets/icon_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_logo.png
--------------------------------------------------------------------------------
/assets/icon_rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_rain.png
--------------------------------------------------------------------------------
/assets/icon_snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_snow.png
--------------------------------------------------------------------------------
/assets/icon_sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_sun.png
--------------------------------------------------------------------------------
/assets/icon_thermometer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_thermometer.png
--------------------------------------------------------------------------------
/assets/icon_thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_thunder.png
--------------------------------------------------------------------------------
/assets/icon_wind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/assets/icon_wind.png
--------------------------------------------------------------------------------
/feather.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
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 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhomlala/feather/c69cdc625dd630411dacc121854ddb5d3149e746/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
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 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | feather
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | NSLocationWhenInUseUsageDescription
45 | This app needs access to location when open. Location is used to select weather for user location.
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/l10n.yaml:
--------------------------------------------------------------------------------
1 | arb-dir: lib/src/l10n
2 | template-arb-file: app_en.arb
3 | output-localization-file: app_localizations.dart
--------------------------------------------------------------------------------
/lib/src/data/model/internal/application_error.dart:
--------------------------------------------------------------------------------
1 | enum ApplicationError {
2 | apiError,
3 | connectionError,
4 | locationNotSelectedError,
5 | }
6 |
--------------------------------------------------------------------------------
/lib/src/data/model/internal/chart_line.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | class ChartLine {
4 | final String? _label;
5 | final Offset _textOffset;
6 | final Offset _lineStartOffset;
7 | final Offset _lineEndOffset;
8 |
9 | ChartLine(this._label, this._textOffset, this._lineStartOffset,
10 | this._lineEndOffset);
11 |
12 | Offset get lineEndOffset => _lineEndOffset;
13 |
14 | Offset get lineStartOffset => _lineStartOffset;
15 |
16 | Offset get textOffset => _textOffset;
17 |
18 | String? get label => _label;
19 | }
20 |
--------------------------------------------------------------------------------
/lib/src/data/model/internal/forecast_navigation_params.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/data/model/internal/weather_forecast_holder.dart';
2 |
3 | class ForecastNavigationParams {
4 | final WeatherForecastHolder weatherForecastHolder;
5 |
6 | ForecastNavigationParams(this.weatherForecastHolder);
7 | }
8 |
--------------------------------------------------------------------------------
/lib/src/data/model/internal/geo_position.dart:
--------------------------------------------------------------------------------
1 | import 'package:geolocator/geolocator.dart';
2 |
3 | class GeoPosition {
4 | final double? lat;
5 | final double? long;
6 |
7 | GeoPosition(this.lat, this.long);
8 |
9 | GeoPosition.fromJson(Map json)
10 | : lat = json["lat"] as double?,
11 | long = json["long"] as double?;
12 |
13 | GeoPosition.fromPosition(Position position)
14 | : lat = position.latitude,
15 | long = position.longitude;
16 |
17 | Map toJson() => {
18 | "lat": lat,
19 | "long": long,
20 | };
21 |
22 | @override
23 | String toString() {
24 | return 'GeoPosition{lat: $lat, long: $long}';
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/src/data/model/internal/navigation_route.dart:
--------------------------------------------------------------------------------
1 | enum NavigationRoute {
2 | mainScreen,
3 | forecastScreen,
4 | aboutScreen,
5 | settingsScreen,
6 | }
7 |
--------------------------------------------------------------------------------
/lib/src/data/model/internal/overflow_menu_element.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 |
3 | class PopupMenuElement {
4 | final Key? key;
5 | final String? title;
6 |
7 | const PopupMenuElement({this.key,this.title});
8 | }
9 |
--------------------------------------------------------------------------------
/lib/src/data/model/internal/pair.dart:
--------------------------------------------------------------------------------
1 | class Pair{
2 | T first;
3 | V second;
4 |
5 | Pair(this.first, this.second);
6 |
7 | }
--------------------------------------------------------------------------------
/lib/src/data/model/internal/point.dart:
--------------------------------------------------------------------------------
1 | class Point {
2 | final double x;
3 | final double y;
4 |
5 | Point(this.x, this.y);
6 |
7 | @override
8 | String toString() {
9 | return 'Point{_x: $x, _y: $y}';
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/lib/src/data/model/internal/settings_navigation_params.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class SettingsNavigationParams {
4 | final List? startGradientColors;
5 |
6 | SettingsNavigationParams(this.startGradientColors);
7 | }
8 |
--------------------------------------------------------------------------------
/lib/src/data/model/internal/unit.dart:
--------------------------------------------------------------------------------
1 | enum Unit {
2 | metric,
3 | imperial,
4 | }
5 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/city.dart:
--------------------------------------------------------------------------------
1 | class City {
2 | final int? id;
3 | final String? name;
4 |
5 | City(this.id, this.name);
6 |
7 | City.fromJson(Map json)
8 | : id = json["id"] as int?,
9 | name = json["name"] as String?;
10 |
11 | Map toJson() => {
12 | "id": id,
13 | "name": name,
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/clouds.dart:
--------------------------------------------------------------------------------
1 | class Clouds {
2 | final int? all;
3 |
4 | Clouds(this.all);
5 |
6 | Clouds.fromJson(Map json) : all = json["all"] as int?;
7 |
8 | Map toJson() => {
9 | "all": all,
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/coordinates.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/utils/types_helper.dart';
2 |
3 | class Coordinates {
4 | final double longitude;
5 | final double latitude;
6 |
7 | Coordinates(this.longitude, this.latitude);
8 |
9 | Coordinates.fromJson(Map json)
10 | : longitude = TypesHelper.toDouble(json["lon"] as num?),
11 | latitude = TypesHelper.toDouble(json["lat"] as num?);
12 |
13 | Map toJson() =>
14 | {"longitude": longitude, "latitude": latitude};
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/main_weather_data.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/utils/types_helper.dart';
2 |
3 | class MainWeatherData {
4 | final double temp;
5 | final double pressure;
6 | final double humidity;
7 | final double tempMin;
8 | final double tempMax;
9 | final double pressureSeaLevel;
10 | final double pressureGroundLevel;
11 |
12 | MainWeatherData(this.temp, this.pressure, this.humidity, this.tempMin,
13 | this.tempMax, this.pressureSeaLevel, this.pressureGroundLevel);
14 |
15 | MainWeatherData.fromJson(Map json)
16 | : temp = TypesHelper.toDouble(json["temp"] as num?),
17 | pressure = TypesHelper.toDouble(json["pressure"] as num?),
18 | humidity = TypesHelper.toDouble(json["humidity"] as num?),
19 | tempMin = TypesHelper.toDouble(json["temp_min"] as num?),
20 | tempMax = TypesHelper.toDouble(json["temp_max"] as num?),
21 | pressureSeaLevel = TypesHelper.toDouble(json["sea_level"] as num?),
22 | pressureGroundLevel =
23 | TypesHelper.toDouble(json["ground_level"] as num?);
24 |
25 | Map toJson() => {
26 | "temp": temp,
27 | "pressure": pressure,
28 | "humidity": humidity,
29 | "temp_min": tempMin,
30 | "temp_max": tempMax,
31 | "sea_level": pressureSeaLevel,
32 | "ground_level": pressureGroundLevel
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/overall_weather_data.dart:
--------------------------------------------------------------------------------
1 | class OverallWeatherData {
2 | final int? id;
3 | final String? main;
4 | final String? description;
5 | final String? icon;
6 |
7 | OverallWeatherData(this.id, this.main, this.description, this.icon);
8 |
9 | OverallWeatherData.fromJson(Map json)
10 | : id = json["id"] as int?,
11 | main = json["main"] as String?,
12 | description = json["description"] as String?,
13 | icon = json["icon"] as String?;
14 |
15 | Map toJson() => {
16 | "id": id,
17 | "main": main,
18 | "description": description,
19 | "icon": icon,
20 | };
21 |
22 | @override
23 | String toString() {
24 | return toJson().toString();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/rain.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/utils/types_helper.dart';
2 |
3 | class Rain {
4 | final double amount;
5 |
6 | Rain(this.amount);
7 |
8 | Rain.fromJson(Map json)
9 | : amount = TypesHelper.toDouble(json["3h"] as num?);
10 |
11 | Map toJson() => {
12 | "3h": amount,
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/system.dart:
--------------------------------------------------------------------------------
1 | class System {
2 | final String? country;
3 | final int? sunrise;
4 | final int? sunset;
5 |
6 | System(this.country, this.sunrise, this.sunset);
7 |
8 | System.fromJson(Map json)
9 | : country = json["country"] as String?,
10 | sunrise = json["sunrise"] as int?,
11 | sunset = json["sunset"] as int?;
12 |
13 | Map toJson() => {
14 | "country": country,
15 | "sunrise": sunrise,
16 | "sunset": sunset
17 | };
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/weather_forecast_list_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/data/model/internal/application_error.dart';
2 | import 'package:feather/src/data/model/remote/city.dart';
3 | import 'package:feather/src/data/model/remote/weather_forecast_response.dart';
4 |
5 | class WeatherForecastListResponse {
6 | final List? list;
7 | final City? city;
8 | ApplicationError? _errorCode;
9 |
10 | WeatherForecastListResponse(this.list, this.city);
11 |
12 | WeatherForecastListResponse.fromJson(Map json)
13 | : list = (json["list"] as List)
14 | .map((dynamic data) =>
15 | WeatherForecastResponse.fromJson(data as Map))
16 | .toList(),
17 | city = City.fromJson(json["city"] as Map);
18 |
19 | Map toJson() =>
20 | {"list": list, "city": city};
21 |
22 | static WeatherForecastListResponse withErrorCode(ApplicationError errorCode) {
23 | final WeatherForecastListResponse response =
24 | WeatherForecastListResponse(null, null);
25 | response._errorCode = errorCode;
26 | return response;
27 | }
28 |
29 | ApplicationError? get errorCode => _errorCode;
30 | }
31 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/weather_forecast_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/data/model/remote/clouds.dart';
2 | import 'package:feather/src/data/model/remote/main_weather_data.dart';
3 | import 'package:feather/src/data/model/remote/overall_weather_data.dart';
4 | import 'package:feather/src/data/model/remote/rain.dart';
5 | import 'package:feather/src/data/model/remote/wind.dart';
6 |
7 | class WeatherForecastResponse {
8 | final MainWeatherData? mainWeatherData;
9 | final List? overallWeatherData;
10 | final Clouds? clouds;
11 | final Wind? wind;
12 | final DateTime dateTime;
13 | final Rain? rain;
14 | final Rain? snow;
15 |
16 | WeatherForecastResponse(this.mainWeatherData, this.overallWeatherData,
17 | this.clouds, this.wind, this.dateTime, this.rain, this.snow);
18 |
19 | WeatherForecastResponse.fromJson(Map json)
20 | : overallWeatherData = (json["weather"] as List)
21 | .map((dynamic data) =>
22 | OverallWeatherData.fromJson(data as Map))
23 | .toList(),
24 | mainWeatherData =
25 | MainWeatherData.fromJson(json["main"] as Map),
26 | wind = Wind.fromJson(json["wind"] as Map),
27 | clouds = Clouds.fromJson(json["clouds"] as Map),
28 | dateTime = DateTime.parse(json["dt_txt"] as String),
29 | rain = _getRain(json["rain"]),
30 | snow = _getRain(json["snow"]);
31 |
32 | static Rain _getRain(dynamic json) {
33 | if (json == null) {
34 | return Rain(0);
35 | } else {
36 | return Rain.fromJson(json as Map);
37 | }
38 | }
39 |
40 | Map toJson() => {
41 | "weather": overallWeatherData,
42 | "main": mainWeatherData,
43 | "clouds": clouds!.toJson(),
44 | "wind": wind!.toJson(),
45 | "dt_txt": dateTime.toIso8601String(),
46 | "rain": rain!.toJson(),
47 | "snow": snow!.toJson()
48 | };
49 |
50 | @override
51 | String toString() {
52 | return toJson().toString();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/weather_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/data/model/internal/application_error.dart';
2 | import 'package:feather/src/data/model/remote/clouds.dart';
3 | import 'package:feather/src/data/model/remote/coordinates.dart';
4 | import 'package:feather/src/data/model/remote/main_weather_data.dart';
5 | import 'package:feather/src/data/model/remote/overall_weather_data.dart';
6 | import 'package:feather/src/data/model/remote/system.dart';
7 | import 'package:feather/src/data/model/remote/wind.dart';
8 |
9 | class WeatherResponse {
10 | final Coordinates? cord;
11 | final List? overallWeatherData;
12 | final MainWeatherData? mainWeatherData;
13 | final Wind? wind;
14 | final Clouds? clouds;
15 | final System? system;
16 | final int? id;
17 | final String? name;
18 | final int? cod;
19 | final String? station;
20 | ApplicationError? _errorCode;
21 |
22 | WeatherResponse({
23 | this.cord,
24 | this.overallWeatherData,
25 | this.mainWeatherData,
26 | this.wind,
27 | this.clouds,
28 | this.system,
29 | this.id,
30 | this.name,
31 | this.cod,
32 | this.station,
33 | });
34 |
35 | WeatherResponse.fromJson(Map json)
36 | : cord = Coordinates.fromJson(json["coord"] as Map),
37 | system = System.fromJson(json["sys"] as Map),
38 | overallWeatherData = (json["weather"] as List)
39 | .map((dynamic data) =>
40 | OverallWeatherData.fromJson(data as Map))
41 | .toList(),
42 | mainWeatherData =
43 | MainWeatherData.fromJson(json["main"] as Map),
44 | wind = Wind.fromJson(json["wind"] as Map),
45 | clouds = Clouds.fromJson(json["clouds"] as Map),
46 | id = json["id"] as int?,
47 | name = json["name"] as String?,
48 | cod = json["cod"] as int?,
49 | station = json["station"] as String?;
50 |
51 | Map toJson() => {
52 | "coord": cord,
53 | "sys": system,
54 | "weather": overallWeatherData,
55 | "main": mainWeatherData,
56 | "wind": wind,
57 | "clouds": clouds,
58 | "id": id,
59 | "name": name,
60 | "cod": cod,
61 | "station": station,
62 | };
63 |
64 | static WeatherResponse withErrorCode(ApplicationError errorCode) {
65 | final WeatherResponse response = WeatherResponse();
66 | response._errorCode = errorCode;
67 | return response;
68 | }
69 |
70 | ApplicationError? get errorCode => _errorCode;
71 | }
72 |
--------------------------------------------------------------------------------
/lib/src/data/model/remote/wind.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/utils/types_helper.dart';
2 |
3 | class Wind {
4 | final double speed;
5 | final double deg;
6 |
7 | Wind(this.speed, this.deg);
8 |
9 | Wind.fromJson(Map json)
10 | : speed = TypesHelper.toDouble(json["speed"] as num),
11 | deg = TypesHelper.toDouble(json["deg"] as num);
12 |
13 | Map toJson() => {
14 | "speed": speed,
15 | "deg": deg,
16 | };
17 |
18 | String getDegCode() {
19 | if (deg == 0.0) {
20 | return "N";
21 | }
22 | if (deg >= 0 && deg < 45) {
23 | return "N";
24 | } else if (deg >= 45 && deg < 90) {
25 | return "NE";
26 | } else if (deg >= 90 && deg < 135) {
27 | return "E";
28 | } else if (deg >= 135 && deg < 180) {
29 | return "SE";
30 | } else if (deg >= 180 && deg < 225) {
31 | return "S";
32 | } else if (deg >= 225 && deg < 270) {
33 | return "SW";
34 | } else if (deg >= 270 && deg < 315) {
35 | return "W";
36 | } else if (deg >= 315 && deg <= 360) {
37 | return "NW";
38 | } else {
39 | return "N";
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/src/data/repository/local/application_local_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/data/model/internal/unit.dart';
2 | import 'package:feather/src/data/repository/local/storage_manager.dart';
3 |
4 | class ApplicationLocalRepository {
5 | final StorageManager _storageManager;
6 |
7 | ApplicationLocalRepository(this._storageManager);
8 |
9 | Future getSavedUnit() async {
10 | return _storageManager.getUnit();
11 | }
12 |
13 | void saveUnit(Unit unit) {
14 | _storageManager.saveUnit(unit);
15 | }
16 |
17 | Future getSavedRefreshTime() async {
18 | return _storageManager.getRefreshTime();
19 | }
20 |
21 | void saveRefreshTime(int refreshTime) {
22 | _storageManager.saveRefreshTime(refreshTime);
23 | }
24 |
25 | Future getLastRefreshTime() {
26 | return _storageManager.getLastRefreshTime();
27 | }
28 |
29 | void saveLastRefreshTime(int lastRefreshTime) {
30 | _storageManager.saveLastRefreshTime(lastRefreshTime);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/src/data/repository/local/location_manager.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/data/repository/local/location_provider.dart';
2 | import 'package:feather/src/utils/app_logger.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:geolocator/geolocator.dart';
5 |
6 | ///Class used to provide location of the device.
7 | class LocationManager {
8 | final LocationProvider _locationProvider;
9 | Position? _lastPosition;
10 |
11 | LocationManager(this._locationProvider);
12 |
13 | Future getLocation() async {
14 | try {
15 | if (_lastPosition != null) {
16 | return _lastPosition;
17 | }
18 | // ignore: join_return_with_assignment
19 | _lastPosition = await _locationProvider.providePosition();
20 | return _lastPosition;
21 | } catch (exc, stackTrace) {
22 | Log.e("Exception occurred: $exc in $stackTrace");
23 | return null;
24 | }
25 | }
26 |
27 | Future isLocationEnabled() {
28 | return _locationProvider.isLocationEnabled();
29 | }
30 |
31 | Future checkLocationPermission() async{
32 | return _locationProvider.checkLocationPermission();
33 | }
34 |
35 | Future requestLocationPermission() async{
36 | return _locationProvider.requestLocationPermission();
37 | }
38 |
39 |
40 | @visibleForTesting
41 | Position? get lastPosition => _lastPosition;
42 | }
43 |
--------------------------------------------------------------------------------
/lib/src/data/repository/local/location_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:geolocator/geolocator.dart';
2 |
3 | class LocationProvider {
4 | Future providePosition() async {
5 | return Geolocator.getCurrentPosition(
6 | desiredAccuracy: LocationAccuracy.high);
7 | }
8 |
9 | Future isLocationEnabled() {
10 | return Geolocator.isLocationServiceEnabled();
11 | }
12 |
13 | Future checkLocationPermission() async {
14 | return Geolocator.checkPermission();
15 | }
16 |
17 | Future requestLocationPermission() async {
18 | return Geolocator.requestPermission();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/src/data/repository/local/storage_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:shared_preferences/shared_preferences.dart';
2 |
3 | class StorageProvider {
4 | Future getInt(String key) async {
5 | final sharedPreferences = await SharedPreferences.getInstance();
6 | return sharedPreferences.getInt(key);
7 | }
8 |
9 | Future setInt(String key, int value) async {
10 | final sharedPreferences = await SharedPreferences.getInstance();
11 | return sharedPreferences.setInt(key, value);
12 | }
13 |
14 | Future getString(String key) async {
15 | final sharedPreferences = await SharedPreferences.getInstance();
16 | return sharedPreferences.getString(key);
17 | }
18 |
19 | Future setString(String key, String value) async {
20 | final sharedPreferences = await SharedPreferences.getInstance();
21 | return sharedPreferences.setString(key, value);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/src/data/repository/local/weather_helper.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/data/model/remote/system.dart';
2 | import 'package:feather/src/data/model/remote/weather_forecast_response.dart';
3 | import 'package:feather/src/resources/config/assets.dart';
4 |
5 | class WeatherHelper {
6 | static String getWeatherIcon(int code) {
7 | String asset = Assets.iconCloud;
8 | if (code >= 200 && code <= 299) {
9 | asset = Assets.iconThunder;
10 | } else if (code >= 300 && code <= 399) {
11 | asset = Assets.iconCloudLittleRain;
12 | } else if (code >= 500 && code <= 599) {
13 | asset = Assets.iconRain;
14 | } else if (code >= 600 && code <= 699) {
15 | asset = Assets.iconSnow;
16 | } else if (code >= 700 && code <= 799) {
17 | asset = Assets.iconDust;
18 | } else if (code == 800) {
19 | asset = Assets.iconSun;
20 | } else if (code == 801) {
21 | asset = Assets.iconCloudSun;
22 | } else if (code >= 802) {
23 | asset = Assets.iconCloud;
24 | }
25 | return asset;
26 | }
27 |
28 | static Map> getMapForecastsForSameDay(
29 | List forecastList) {
30 | final Map> map = {};
31 | for (int i = 0; i < forecastList.length; i++) {
32 | final WeatherForecastResponse response = forecastList[i];
33 | final String dayKey = _getDayKey(response.dateTime);
34 | if (!map.containsKey(dayKey)) {
35 | map[dayKey] = [];
36 | }
37 | map[dayKey]!.add(response);
38 | }
39 | return map;
40 | }
41 |
42 | static String _getDayKey(DateTime dateTime) {
43 | return "${dateTime.day.toString()}-${dateTime.month.toString()}-${dateTime.year.toString()}";
44 | }
45 |
46 | static String formatTemperature({
47 | double? temperature,
48 | int positions = 0,
49 | bool round = true,
50 | bool metricUnits = true,
51 | }) {
52 | var unit = "°C";
53 | var temperatureValue = temperature;
54 |
55 | if (!metricUnits) {
56 | unit = "°F";
57 | }
58 |
59 | if (round) {
60 | temperatureValue = temperature!.floor().toDouble();
61 | }
62 |
63 | return "${temperatureValue!.toStringAsFixed(positions)} $unit";
64 | }
65 |
66 | static double convertCelsiusToFahrenheit(double temperature) {
67 | return 32 + temperature * 1.8;
68 | }
69 |
70 |
71 | static double convertFahrenheitToCelsius(double temperature) {
72 | return (temperature - 32) * 5/9;
73 | }
74 |
75 | static String formatPressure(double pressure, bool isMetricUnits) {
76 | String unit = "hPa";
77 | if (!isMetricUnits) {
78 | unit = "mbar";
79 | }
80 | return "${pressure.toStringAsFixed(0)} $unit";
81 | }
82 |
83 | static double convertMetersPerSecondToKilometersPerHour(double? speed) {
84 | if (speed != null) {
85 | return speed * 3.6;
86 | } else {
87 | return 0;
88 | }
89 | }
90 |
91 | static double convertMetersPerSecondToMilesPerHour(double? speed) {
92 | if (speed != null) {
93 | return speed * 2.236936292;
94 | } else {
95 | return 0;
96 | }
97 | }
98 |
99 | static String formatRain(double rain) {
100 | return "${rain.toStringAsFixed(2)} mm/h";
101 | }
102 |
103 | static String formatWind(double wind, bool isMetricUnits) {
104 | String unit = "km/h";
105 | if (!isMetricUnits) {
106 | unit = "mi/h";
107 | }
108 | return "${wind.toStringAsFixed(1)} $unit";
109 | }
110 |
111 | static String formatHumidity(double humidity) {
112 | return "${humidity.toStringAsFixed(0)}%";
113 | }
114 |
115 | static int getDayMode(System system) {
116 | final int sunrise = system.sunrise! * 1000;
117 | final int sunset = system.sunset! * 1000;
118 | return getDayModeFromSunriseSunset(sunrise, sunset);
119 | }
120 |
121 | static int getDayModeFromSunriseSunset(int sunrise, int? sunset) {
122 | final int now = DateTime.now().millisecondsSinceEpoch;
123 | if (now >= sunrise && now <= sunset!) {
124 | return 0;
125 | } else if (now >= sunrise) {
126 | return 1;
127 | } else {
128 | return -1;
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/lib/src/data/repository/local/weather_local_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:feather/src/data/model/internal/geo_position.dart';
2 | import 'package:feather/src/data/model/remote/weather_forecast_list_response.dart';
3 | import 'package:feather/src/data/model/remote/weather_response.dart';
4 | import 'package:feather/src/data/repository/local/storage_manager.dart';
5 |
6 | class WeatherLocalRepository {
7 | final StorageManager _storageManager;
8 |
9 | WeatherLocalRepository(this._storageManager);
10 |
11 | Future saveLocation(GeoPosition geoPosition) async {
12 | await _storageManager.saveLocation(geoPosition);
13 | }
14 |
15 | Future getLocation() async {
16 | return _storageManager.getLocation();
17 | }
18 |
19 | Future saveWeather(WeatherResponse response) async {
20 | await _storageManager.saveWeather(response);
21 | }
22 |
23 | Future getWeather() async {
24 | return _storageManager.getWeather();
25 | }
26 |
27 | Future saveWeatherForecast(WeatherForecastListResponse response) async {
28 | await _storageManager.saveWeatherForecast(response);
29 | }
30 |
31 | Future getWeatherForecast() async {
32 | return _storageManager.getWeatherForecast();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/src/data/repository/remote/weather_api_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:dio/dio.dart';
2 | import 'package:feather/src/data/model/internal/application_error.dart';
3 | import 'package:feather/src/data/model/remote/weather_forecast_list_response.dart';
4 | import 'package:feather/src/data/model/remote/weather_response.dart';
5 | import 'package:feather/src/resources/config/application_config.dart';
6 | import 'package:feather/src/utils/app_logger.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:pretty_dio_logger/pretty_dio_logger.dart';
9 | // ignore: argument_type_not_assignable
10 |
11 | class WeatherApiProvider {
12 | final String _apiBaseUrl = "api.openweathermap.org";
13 | final String _apiPath = "/data/2.5";
14 | final String _apiWeatherEndpoint = "/weather";
15 | final String _apiWeatherForecastEndpoint = "/forecast";
16 | final Dio _dio = Dio();
17 |
18 | Future fetchWeather(
19 | double? latitude, double? longitude) async {
20 | try {
21 | final Uri uri = _buildUri(_apiWeatherEndpoint, latitude, longitude);
22 | final Response