├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── 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-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Runner.xcworkspace │ └── contents.xcworkspacedata ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme └── .gitignore ├── _config.yml ├── lib ├── src │ ├── pages │ │ ├── donors │ │ │ ├── models │ │ │ │ ├── models.dart │ │ │ │ └── donor_model.dart │ │ │ ├── widgets │ │ │ │ ├── widgets.dart │ │ │ │ └── donors_widget.dart │ │ │ └── donors_page.dart │ │ ├── home │ │ │ ├── models │ │ │ │ ├── models.dart │ │ │ │ └── weather_model.dart │ │ │ ├── blocs │ │ │ │ ├── events │ │ │ │ │ ├── events.dart │ │ │ │ │ └── weather_event.dart │ │ │ │ ├── states │ │ │ │ │ ├── states.dart │ │ │ │ │ └── weather_state.dart │ │ │ │ ├── blocs.dart │ │ │ │ └── weather_bloc.dart │ │ │ ├── widgets │ │ │ │ ├── weather_loading_widget.dart │ │ │ │ ├── widgets.dart │ │ │ │ ├── value_tile_widget.dart │ │ │ │ ├── weather_error_widget.dart │ │ │ │ ├── weather_swipe_pager.dart │ │ │ │ ├── weather_finding_location_widget.dart │ │ │ │ ├── temperature_line_chart_widget.dart │ │ │ │ ├── weather_widget.dart │ │ │ │ ├── weather_empty_widget.dart │ │ │ │ ├── forecast_horizontal_widget.dart │ │ │ │ ├── current_conditions_widget.dart │ │ │ │ └── today_forecast_widget.dart │ │ │ └── home_page.dart │ │ ├── radar │ │ │ ├── blocs │ │ │ │ ├── events │ │ │ │ │ ├── events.dart │ │ │ │ │ └── radar_event.dart │ │ │ │ ├── states │ │ │ │ │ ├── states.dart │ │ │ │ │ └── radar_state.dart │ │ │ │ ├── blocs.dart │ │ │ │ └── radar_bloc.dart │ │ │ └── radar_page.dart │ │ ├── forecast │ │ │ └── blocs │ │ │ │ ├── events │ │ │ │ ├── events.dart │ │ │ │ └── forecast_event.dart │ │ │ │ ├── states │ │ │ │ ├── states.dart │ │ │ │ └── forecast_state.dart │ │ │ │ ├── blocs.dart │ │ │ │ └── forecast_bloc.dart │ │ ├── radar_list │ │ │ ├── models │ │ │ │ ├── models.dart │ │ │ │ └── radar_list_model.dart │ │ │ └── radar_list_page.dart │ │ ├── satellite │ │ │ ├── blocs │ │ │ │ ├── events │ │ │ │ │ ├── events.dart │ │ │ │ │ └── satellite_event.dart │ │ │ │ ├── states │ │ │ │ │ ├── states.dart │ │ │ │ │ └── satellite_state.dart │ │ │ │ ├── blocs.dart │ │ │ │ └── satellite_bloc.dart │ │ │ └── satellite_page.dart │ │ ├── satellite_list │ │ │ ├── models │ │ │ │ ├── models.dart │ │ │ │ └── satellite_list_model.dart │ │ │ └── satellite_list_page.dart │ │ ├── marine_forecast │ │ │ └── blocs │ │ │ │ ├── events │ │ │ │ ├── events.dart │ │ │ │ └── marine_forecast_event.dart │ │ │ │ ├── states │ │ │ │ ├── states.dart │ │ │ │ └── marine_forecast_state.dart │ │ │ │ ├── blocs.dart │ │ │ │ └── marine_forecast_bloc.dart │ │ ├── municipality_list │ │ │ ├── blocs │ │ │ │ ├── events │ │ │ │ │ ├── events.dart │ │ │ │ │ └── municipality_list_event.dart │ │ │ │ ├── states │ │ │ │ │ ├── states.dart │ │ │ │ │ └── municipality_list_state.dart │ │ │ │ ├── blocs.dart │ │ │ │ └── municipality_list_bloc.dart │ │ │ └── municipality_list_page.dart │ │ ├── municipality_record │ │ │ ├── blocs │ │ │ │ ├── events │ │ │ │ │ ├── events.dart │ │ │ │ │ └── municipality_record_event.dart │ │ │ │ ├── states │ │ │ │ │ ├── states.dart │ │ │ │ │ └── municipality_record_state.dart │ │ │ │ ├── blocs.dart │ │ │ │ └── municipality_record_bloc.dart │ │ │ └── municipality_record_page.dart │ │ ├── carousel │ │ │ ├── widgets │ │ │ │ ├── widgets.dart │ │ │ │ ├── dots_indicator_widget.dart │ │ │ │ └── carousel_item_widget.dart │ │ │ └── carousel_page.dart │ │ ├── pages.dart │ │ ├── settings │ │ │ └── settings_page.dart │ │ ├── municipality_selection │ │ │ └── municipality_selection_page.dart │ │ ├── splash │ │ │ └── splash_page.dart │ │ ├── whats_new │ │ │ └── show_whats_new.dart │ │ └── information │ │ │ └── information_page.dart │ ├── widgets │ │ ├── widgets.dart │ │ ├── empty_widget.dart │ │ └── responsive_screen.dart │ ├── utils │ │ ├── utils.dart │ │ ├── app_theme.dart │ │ ├── record_utils.dart │ │ ├── app_state_notifier.dart │ │ └── constants.dart │ └── app.dart └── main.dart ├── images ├── banner.png ├── image1.png ├── image2.png ├── image3.png ├── image4.png ├── image5.png ├── logo.png ├── loading.gif ├── no-image.png ├── logo_insmet.png ├── radar │ ├── 6pilon.jpg │ ├── 1labajada.jpg │ ├── 3Puntaeste.jpg │ ├── 5Camaguey.jpg │ ├── 2CasaBlanca.jpg │ ├── 4picoSanjuan.jpg │ ├── 7granPiedra.jpg │ └── 8granPiedra.jpg └── satellite │ ├── cuba-g16.jpg │ ├── latest_eastir.jpg │ ├── latest_eastvis.jpg │ ├── goes.gsfc.nasa.gov.jpg │ └── www.intellicast.com.jpg ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── ic_launcher-web.png │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── values │ │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ └── drawable │ │ │ │ │ └── launch_background.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── codestrange │ │ │ │ │ └── www │ │ │ │ │ └── cuba_weather │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── .gitignore ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── build.gradle ├── .metadata ├── USAGE.md ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── .flutter-plugins-dependencies ├── pubspec.yaml ├── CHANGELOG.md ├── .gitignore ├── README.md ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | title: Cuba Weather 2 | theme: jekyll-theme-cayman 3 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/src/pages/donors/models/models.dart: -------------------------------------------------------------------------------- 1 | export 'donor_model.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/home/models/models.dart: -------------------------------------------------------------------------------- 1 | export 'weather_model.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/donors/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'donors_widget.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/home/blocs/events/events.dart: -------------------------------------------------------------------------------- 1 | export 'weather_event.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/home/blocs/states/states.dart: -------------------------------------------------------------------------------- 1 | export 'weather_state.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/radar/blocs/events/events.dart: -------------------------------------------------------------------------------- 1 | export 'radar_event.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/radar/blocs/states/states.dart: -------------------------------------------------------------------------------- 1 | export 'radar_state.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/forecast/blocs/events/events.dart: -------------------------------------------------------------------------------- 1 | export 'forecast_event.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/forecast/blocs/states/states.dart: -------------------------------------------------------------------------------- 1 | export 'forecast_state.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/radar_list/models/models.dart: -------------------------------------------------------------------------------- 1 | export 'radar_list_model.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/satellite/blocs/events/events.dart: -------------------------------------------------------------------------------- 1 | export 'satellite_event.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/satellite/blocs/states/states.dart: -------------------------------------------------------------------------------- 1 | export 'satellite_state.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/satellite_list/models/models.dart: -------------------------------------------------------------------------------- 1 | export 'satellite_list_model.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/marine_forecast/blocs/events/events.dart: -------------------------------------------------------------------------------- 1 | export 'marine_forecast_event.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/marine_forecast/blocs/states/states.dart: -------------------------------------------------------------------------------- 1 | export 'marine_forecast_state.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_list/blocs/events/events.dart: -------------------------------------------------------------------------------- 1 | export 'municipality_list_event.dart'; -------------------------------------------------------------------------------- /lib/src/pages/municipality_list/blocs/states/states.dart: -------------------------------------------------------------------------------- 1 | export 'municipality_list_state.dart'; -------------------------------------------------------------------------------- /lib/src/pages/municipality_record/blocs/events/events.dart: -------------------------------------------------------------------------------- 1 | export 'municipality_record_event.dart'; -------------------------------------------------------------------------------- /lib/src/pages/municipality_record/blocs/states/states.dart: -------------------------------------------------------------------------------- 1 | export 'municipality_record_state.dart'; -------------------------------------------------------------------------------- /lib/src/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'empty_widget.dart'; 2 | export 'responsive_screen.dart'; 3 | -------------------------------------------------------------------------------- /images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/banner.png -------------------------------------------------------------------------------- /images/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/image1.png -------------------------------------------------------------------------------- /images/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/image2.png -------------------------------------------------------------------------------- /images/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/image3.png -------------------------------------------------------------------------------- /images/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/image4.png -------------------------------------------------------------------------------- /images/image5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/image5.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/logo.png -------------------------------------------------------------------------------- /images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/loading.gif -------------------------------------------------------------------------------- /images/no-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/no-image.png -------------------------------------------------------------------------------- /images/logo_insmet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/logo_insmet.png -------------------------------------------------------------------------------- /images/radar/6pilon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/radar/6pilon.jpg -------------------------------------------------------------------------------- /images/radar/1labajada.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/radar/1labajada.jpg -------------------------------------------------------------------------------- /images/radar/3Puntaeste.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/radar/3Puntaeste.jpg -------------------------------------------------------------------------------- /images/radar/5Camaguey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/radar/5Camaguey.jpg -------------------------------------------------------------------------------- /images/radar/2CasaBlanca.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/radar/2CasaBlanca.jpg -------------------------------------------------------------------------------- /images/radar/4picoSanjuan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/radar/4picoSanjuan.jpg -------------------------------------------------------------------------------- /images/radar/7granPiedra.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/radar/7granPiedra.jpg -------------------------------------------------------------------------------- /images/radar/8granPiedra.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/radar/8granPiedra.jpg -------------------------------------------------------------------------------- /images/satellite/cuba-g16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/satellite/cuba-g16.jpg -------------------------------------------------------------------------------- /lib/src/pages/carousel/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'carousel_item_widget.dart'; 2 | export 'dots_indicator_widget.dart'; 3 | -------------------------------------------------------------------------------- /lib/src/pages/home/blocs/blocs.dart: -------------------------------------------------------------------------------- 1 | export 'events/events.dart'; 2 | export 'states/states.dart'; 3 | export 'weather_bloc.dart'; 4 | -------------------------------------------------------------------------------- /lib/src/pages/radar/blocs/blocs.dart: -------------------------------------------------------------------------------- 1 | export 'events/events.dart'; 2 | export 'states/states.dart'; 3 | export 'radar_bloc.dart'; 4 | -------------------------------------------------------------------------------- /images/satellite/latest_eastir.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/satellite/latest_eastir.jpg -------------------------------------------------------------------------------- /lib/src/pages/forecast/blocs/blocs.dart: -------------------------------------------------------------------------------- 1 | export 'events/events.dart'; 2 | export 'states/states.dart'; 3 | export 'forecast_bloc.dart'; 4 | -------------------------------------------------------------------------------- /images/satellite/latest_eastvis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/satellite/latest_eastvis.jpg -------------------------------------------------------------------------------- /lib/src/pages/satellite/blocs/blocs.dart: -------------------------------------------------------------------------------- 1 | export 'events/events.dart'; 2 | export 'states/states.dart'; 3 | export 'satellite_bloc.dart'; 4 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /images/satellite/goes.gsfc.nasa.gov.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/satellite/goes.gsfc.nasa.gov.jpg -------------------------------------------------------------------------------- /android/app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /images/satellite/www.intellicast.com.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/images/satellite/www.intellicast.com.jpg -------------------------------------------------------------------------------- /lib/src/pages/marine_forecast/blocs/blocs.dart: -------------------------------------------------------------------------------- 1 | export 'events/events.dart'; 2 | export 'states/states.dart'; 3 | export 'marine_forecast_bloc.dart'; 4 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_list/blocs/blocs.dart: -------------------------------------------------------------------------------- 1 | export 'events/events.dart'; 2 | export 'states/states.dart'; 3 | export 'municipality_list_bloc.dart'; -------------------------------------------------------------------------------- /lib/src/utils/utils.dart: -------------------------------------------------------------------------------- 1 | export 'app_state_notifier.dart'; 2 | export 'app_theme.dart'; 3 | export 'constants.dart'; 4 | export 'record_utils.dart'; 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_record/blocs/blocs.dart: -------------------------------------------------------------------------------- 1 | export 'events/events.dart'; 2 | export 'states/states.dart'; 3 | export 'municipality_record_bloc.dart'; -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/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/cuba-weather/cuba-weather-flutter/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/cuba-weather/cuba-weather-flutter/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/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /lib/src/pages/donors/models/donor_model.dart: -------------------------------------------------------------------------------- 1 | class Donor { 2 | String name; 3 | String value; 4 | String url; 5 | 6 | Donor({this.name, this.value, this.url}); 7 | } 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2078DC 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/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/cuba-weather/cuba-weather-flutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cuba-weather/cuba-weather-flutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/src/widgets/empty_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class EmptyWidget extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) => Container(width: 0, height: 0); 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/src/pages/radar_list/models/radar_list_model.dart: -------------------------------------------------------------------------------- 1 | class RadarList { 2 | String source; 3 | String sourceUrl; 4 | String description; 5 | String image; 6 | 7 | 8 | RadarList({this.source, this.sourceUrl, this.image, this.description}); 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/pages/satellite_list/models/satellite_list_model.dart: -------------------------------------------------------------------------------- 1 | class SatelliteList { 2 | String source; 3 | String sourceUrl; 4 | String description; 5 | String image; 6 | 7 | 8 | SatelliteList({this.source, this.sourceUrl, this.image, this.description}); 9 | } 10 | -------------------------------------------------------------------------------- /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.6.2-all.zip 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.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: 9f5ff2306bb3e30b2b98eee79cd231b1336f41f4 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/weather_loading_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class WeatherLoadingWidget extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return Center( 8 | child: CircularProgressIndicator(), 9 | ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/utils/app_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppTheme { 4 | AppTheme._(); 5 | 6 | static final lightTheme = ThemeData.light().copyWith( 7 | backgroundColor: Colors.blue, 8 | primaryColor: Colors.blue, 9 | accentColor: Colors.white, 10 | ); 11 | 12 | static final darkTheme = ThemeData.dark().copyWith( 13 | backgroundColor: Colors.black, 14 | primaryColor: Colors.black, 15 | accentColor: Colors.white, 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/pages/marine_forecast/blocs/events/marine_forecast_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class MarineForecastEvent extends Equatable { 4 | const MarineForecastEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class FetchMarineForecast extends MarineForecastEvent { 11 | final String forecastType; 12 | 13 | const FetchMarineForecast(this.forecastType); 14 | 15 | @override 16 | List get props => [forecastType]; 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/utils/record_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:preferences/preferences.dart'; 2 | import 'constants.dart'; 3 | 4 | void updateRecords(String municipality) async { 5 | List actual = PrefService.getStringList(Constants.municipalityRecord)??List(); 6 | actual.remove(municipality); 7 | actual.insert(0, municipality); 8 | if(actual.length > Constants.municipalityRecordSize) { 9 | actual.removeLast(); 10 | } 11 | PrefService.setStringList(Constants.municipalityRecord, actual); 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'current_conditions_widget.dart'; 2 | export 'forecast_horizontal_widget.dart'; 3 | export 'main_widget.dart'; 4 | export 'temperature_line_chart_widget.dart'; 5 | export 'today_forecast_widget.dart'; 6 | export 'value_tile_widget.dart'; 7 | export 'weather_empty_widget.dart'; 8 | export 'weather_error_widget.dart'; 9 | export 'weather_finding_location_widget.dart'; 10 | export 'weather_loading_widget.dart'; 11 | export 'weather_swipe_pager.dart'; 12 | export 'weather_widget.dart'; 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_list/blocs/events/municipality_list_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class MunicipalityListEvent extends Equatable { 4 | const MunicipalityListEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialMunicipalityListEvent extends MunicipalityListEvent { 11 | const InitialMunicipalityListEvent(); 12 | } 13 | 14 | class FetchMunicipalityListEvent extends MunicipalityListEvent { 15 | const FetchMunicipalityListEvent(); 16 | } 17 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/codestrange/www/cuba_weather/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codestrange.www.cuba_weather 2 | 3 | import androidx.annotation.NonNull 4 | import io.flutter.embedding.android.FlutterActivity 5 | import io.flutter.embedding.engine.FlutterEngine 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 10 | GeneratedPluginRegistrant.registerWith(flutterEngine) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_record/blocs/events/municipality_record_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class MunicipalityRecordEvent extends Equatable { 4 | const MunicipalityRecordEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialMunicipalityRecordEvent extends MunicipalityRecordEvent { 11 | const InitialMunicipalityRecordEvent(); 12 | } 13 | 14 | class FetchMunicipalityRecordEvent extends MunicipalityRecordEvent { 15 | const FetchMunicipalityRecordEvent(); 16 | } 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/src/widgets/responsive_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ResponsiveScreen { 4 | Size screenSize; 5 | 6 | ResponsiveScreen._internal(); 7 | ResponsiveScreen(this.screenSize); 8 | 9 | double wp(percentage) { 10 | return percentage / 100 * screenSize.width; 11 | } 12 | 13 | double hp(percentage) { 14 | return percentage / 100 * screenSize.height; 15 | } 16 | 17 | double getWidthPx(int pixels) { 18 | return (pixels / 3.61) / 100 * screenSize.width; 19 | } 20 | 21 | double getHeightPx(int pixels) { 22 | return (pixels / 3.61) / 100 * screenSize.height; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | # Usage instructions 2 | 3 | ## Setup 4 | 5 | 1. Install and configure the Flutter framework: https://flutter.dev/docs/get-started/install 6 | 7 | 2. Add the Flutter and Dart plugins to your IDE: https://flutter.dev/docs/get-started/editor 8 | 9 | 3. Fork this repository and clone your fork. 10 | 11 | 4. Install all dependencies 12 | ``` 13 | flutter pub get 14 | ``` 15 | 16 | 5. Run the project using your IDE or the Flutter CLI: 17 | ``` 18 | flutter run 19 | ``` 20 | 21 | ## Useful commands 22 | 23 | ### Clean the project's build folders 24 | This can resolve issues where the simulator is not running the latest code. 25 | ```sh 26 | flutter clean 27 | ``` 28 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /lib/src/pages/home/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 5 | 6 | import 'package:cuba_weather/src/pages/home/blocs/blocs.dart'; 7 | import 'package:cuba_weather/src/pages/pages.dart'; 8 | 9 | class HomePage extends StatefulWidget { 10 | @override 11 | HomePageState createState() => HomePageState(); 12 | } 13 | 14 | class HomePageState extends State { 15 | @override 16 | Widget build(BuildContext context) { 17 | return BlocProvider( 18 | create: (context) => WeatherBloc(api: CubaWeather()), 19 | child: MainWidget(), 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature, needs triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/pages/radar/blocs/events/radar_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | 5 | abstract class RadarEvent extends Equatable { 6 | const RadarEvent(); 7 | 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class FetchRadar extends RadarEvent { 13 | final String radarType; 14 | 15 | const FetchRadar(this.radarType); 16 | 17 | @override 18 | List get props => [radarType]; 19 | } 20 | 21 | class SetShowImageRadar extends RadarEvent { 22 | final bool showImage; 23 | final dynamic radar; 24 | 25 | const SetShowImageRadar({ 26 | @required this.radar, 27 | @required this.showImage, 28 | }); 29 | 30 | @override 31 | List get props => [radar, showImage]; 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/utils/app_state_notifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:preferences/preferences.dart'; 3 | 4 | import 'package:cuba_weather/src/utils/utils.dart'; 5 | 6 | class AppStateNotifier extends ChangeNotifier { 7 | String themeMode = 'system'; 8 | 9 | AppStateNotifier() { 10 | themeMode = PrefService.getString(Constants.themeMode) ?? 'system'; 11 | } 12 | 13 | void updateTheme(String themeMode) { 14 | this.themeMode = themeMode; 15 | notifyListeners(); 16 | } 17 | 18 | ThemeMode getTheme() { 19 | switch (themeMode) { 20 | case 'dark': 21 | return ThemeMode.dark; 22 | case 'light': 23 | return ThemeMode.light; 24 | default: 25 | return ThemeMode.system; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/pages/home/blocs/events/weather_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | abstract class WeatherEvent extends Equatable { 5 | const WeatherEvent(); 6 | } 7 | 8 | class FetchWeather extends WeatherEvent { 9 | final String municipality; 10 | 11 | const FetchWeather({@required this.municipality}) 12 | : assert(municipality != null); 13 | 14 | @override 15 | List get props => [municipality]; 16 | } 17 | 18 | class RefreshWeather extends WeatherEvent { 19 | final String municipality; 20 | 21 | const RefreshWeather({@required this.municipality}) 22 | : assert(municipality != null); 23 | 24 | @override 25 | List get props => [municipality]; 26 | } 27 | 28 | class FindLocationWeather extends WeatherEvent { 29 | List get props => []; 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/pages/pages.dart: -------------------------------------------------------------------------------- 1 | export 'donate/donate_page.dart'; 2 | export 'donors/donors_page.dart'; 3 | export 'forecast/forecast_page.dart'; 4 | export 'home/home_page.dart'; 5 | export 'information/information_page.dart'; 6 | export 'carousel/widgets/carousel_item_widget.dart'; 7 | export 'home/widgets/main_widget.dart'; 8 | export 'marine_forecast/marine_forecast_page.dart'; 9 | export 'municipality_selection/municipality_selection_page.dart'; 10 | export 'municipality_record/municipality_record_page.dart'; 11 | export 'municipality_list/municipality_list_page.dart'; 12 | export 'radar_list/radar_list_page.dart'; 13 | export 'radar/radar_page.dart'; 14 | export 'satellite_list/satellite_list_page.dart'; 15 | export 'satellite/satellite_page.dart'; 16 | export 'carousel/carousel_page.dart'; 17 | export 'settings/settings_page.dart'; 18 | export 'splash/splash_page.dart'; 19 | -------------------------------------------------------------------------------- /lib/src/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | 5 | import 'package:cuba_weather/src/pages/pages.dart'; 6 | import 'package:cuba_weather/src/utils/utils.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | class App extends StatelessWidget { 10 | @override 11 | Widget build(BuildContext context) { 12 | SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); 13 | return Consumer( 14 | builder: (context, appState, child) { 15 | return MaterialApp( 16 | title: Constants.appName, 17 | theme: AppTheme.lightTheme, 18 | darkTheme: AppTheme.darkTheme, 19 | home: SplashScreen(), 20 | themeMode: appState.getTheme(), 21 | ); 22 | }, 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '[BUG] ' 5 | labels: 'bug, not reviewed' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Smartphone (please complete the following information):** 27 | - Device: [e.g. iPhone6] 28 | - OS: [e.g. iOS8.1] 29 | - Browser [e.g. stock browser, safari] 30 | - Version [e.g. 22] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /lib/src/pages/forecast/blocs/events/forecast_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 5 | 6 | abstract class ForecastEvent extends Equatable { 7 | const ForecastEvent(); 8 | 9 | @override 10 | List get props => []; 11 | } 12 | 13 | class FetchForecast extends ForecastEvent { 14 | final String forecastType; 15 | 16 | const FetchForecast(this.forecastType); 17 | 18 | @override 19 | List get props => [forecastType]; 20 | } 21 | 22 | class SetShowImageForecast extends ForecastEvent { 23 | final bool showImage; 24 | final InsmetForecastModel forecast; 25 | 26 | const SetShowImageForecast({ 27 | @required this.forecast, 28 | @required this.showImage, 29 | }); 30 | 31 | @override 32 | List get props => [forecast, showImage]; 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/pages/satellite/blocs/events/satellite_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 5 | 6 | abstract class SatelliteEvent extends Equatable { 7 | const SatelliteEvent(); 8 | 9 | @override 10 | List get props => []; 11 | } 12 | 13 | class FetchSatellite extends SatelliteEvent { 14 | final String satelliteType; 15 | 16 | const FetchSatellite(this.satelliteType); 17 | 18 | @override 19 | List get props => [satelliteType]; 20 | } 21 | 22 | class SetShowImageSatellite extends SatelliteEvent { 23 | final bool showImage; 24 | final InsmetSatelliteModel satellite; 25 | 26 | const SetShowImageSatellite({ 27 | @required this.satellite, 28 | @required this.showImage, 29 | }); 30 | 31 | @override 32 | List get props => [satellite, showImage]; 33 | } 34 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | -------------------------------------------------------------------------------- /.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"package_info","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"share","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_web","url_launcher_macos"]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"video_player","dependencies":["video_player_web"]},{"name":"video_player_web","dependencies":[]}]} -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: cuba_weather 2 | description: Application for mobile devices of the Cuba Weather project implemented with Flutter 3 | 4 | version: 1.4.0+3 5 | 6 | environment: 7 | sdk: ">=2.6.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | cuba_weather_dart: ^3.1.1 13 | cupertino_icons: ^0.1.2 14 | flutter_bloc: ^3.1.0 15 | equatable: ^1.0.0 16 | shared_preferences: ^0.5.6 17 | autocomplete_textfield: ^1.7.3 18 | package_info: ^0.4.0+13 19 | url_launcher: ^5.4.1 20 | getflutter: ^1.0.9 21 | weather_icons: ^2.0.0 22 | share: ^0.6.3+5 23 | font_awesome_flutter: ^8.5.0 24 | flutter_speed_dial: ^1.2.5 25 | geolocator: ^5.2.1 26 | preferences: ^5.1.0 27 | flutter_swiper : ^1.1.6 28 | charts_flutter: ^0.9.0 29 | provider: ^4.0.4 30 | story_view: ^0.11.4 31 | flutter_whatsnew: ^1.0.3 32 | 33 | flutter: 34 | 35 | uses-material-design: true 36 | 37 | assets: 38 | - CHANGELOG.md 39 | - images/ 40 | - images/radar/ 41 | - images/satellite/ 42 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_list/blocs/municipality_list_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | 5 | import 'package:cuba_weather_dart/cuba_weather_dart.dart' as cwd; 6 | 7 | import 'package:cuba_weather/src/pages/municipality_list/blocs/blocs.dart'; 8 | 9 | class MunicipalityListBloc 10 | extends Bloc { 11 | 12 | @override 13 | MunicipalityListState get initialState => MunicipalityListInitial(); 14 | 15 | @override 16 | Stream mapEventToState( 17 | MunicipalityListEvent event) async* { 18 | if (event is FetchMunicipalityListEvent) { 19 | yield MunicipalityListLoading(); 20 | try { 21 | List municipalities = cwd.municipalities; 22 | yield MunicipalityListLoaded(municipalities: municipalities); 23 | } catch (e) { 24 | log(e.toString()); 25 | yield MunicipalityListError(errorMessage: e.toString()); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:preferences/preferences.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 9 | 10 | import 'package:cuba_weather/src/app.dart'; 11 | import 'package:cuba_weather/src/utils/utils.dart'; 12 | 13 | void main() async { 14 | WidgetsFlutterBinding.ensureInitialized(); 15 | 16 | BlocSupervisor.delegate = SimpleBlocDelegate(); 17 | 18 | municipalities.sort((a, b) => a.name.compareTo(b.name)); 19 | 20 | await PrefService.init(); 21 | 22 | runApp( 23 | ChangeNotifierProvider( 24 | create: (context) => AppStateNotifier(), 25 | child: App(), 26 | ), 27 | ); 28 | } 29 | 30 | class SimpleBlocDelegate extends BlocDelegate { 31 | @override 32 | onTransition(Bloc bloc, Transition transition) { 33 | super.onTransition(bloc, transition); 34 | log(transition.toString()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/pages/marine_forecast/blocs/states/marine_forecast_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | 5 | abstract class MarineForecastState extends Equatable { 6 | const MarineForecastState(); 7 | 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class MarineForecastInitial extends MarineForecastState {} 13 | 14 | class MarineForecastLoading extends MarineForecastState {} 15 | 16 | class MarineForecastLoaded extends MarineForecastState { 17 | final InsmetMarineForecastModel forecast; 18 | 19 | const MarineForecastLoaded({@required this.forecast}) 20 | : assert(forecast != null); 21 | 22 | @override 23 | List get props => [forecast]; 24 | } 25 | 26 | class MarineForecastError extends MarineForecastState { 27 | final String errorMessage; 28 | 29 | const MarineForecastError({@required this.errorMessage}) 30 | : assert(errorMessage != null); 31 | 32 | @override 33 | List get props => [errorMessage]; 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_record/blocs/states/municipality_record_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | abstract class MunicipalityRecordState extends Equatable { 5 | const MunicipalityRecordState(); 6 | 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class MunicipalityRecordInitial extends MunicipalityRecordState {} 12 | 13 | class MunicipalityRecordLoading extends MunicipalityRecordState {} 14 | 15 | class MunicipalityRecordLoaded extends MunicipalityRecordState { 16 | final List municipalities; 17 | 18 | const MunicipalityRecordLoaded({@required this.municipalities}) 19 | : assert(municipalities != null); 20 | 21 | @override 22 | List get props => [municipalities]; 23 | } 24 | 25 | class MunicipalityRecordError extends MunicipalityRecordState { 26 | final String errorMessage; 27 | 28 | const MunicipalityRecordError({@required this.errorMessage}) 29 | : assert(errorMessage != null); 30 | 31 | @override 32 | List get props => [errorMessage]; 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_record/blocs/municipality_record_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:preferences/preferences.dart'; 5 | 6 | import 'package:cuba_weather/src/utils/utils.dart'; 7 | import 'package:cuba_weather/src/pages/municipality_record/blocs/blocs.dart'; 8 | 9 | class MunicipalityRecordBloc 10 | extends Bloc { 11 | 12 | @override 13 | MunicipalityRecordState get initialState => MunicipalityRecordInitial(); 14 | 15 | @override 16 | Stream mapEventToState( 17 | MunicipalityRecordEvent event) async* { 18 | if (event is FetchMunicipalityRecordEvent) { 19 | yield MunicipalityRecordLoading(); 20 | try { 21 | List municipalities = PrefService.getStringList(Constants.municipalityRecord)??List(); 22 | yield MunicipalityRecordLoaded(municipalities: municipalities); 23 | } catch (e) { 24 | log(e.toString()); 25 | yield MunicipalityRecordError(errorMessage: e.toString()); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_list/blocs/states/municipality_list_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | 5 | abstract class MunicipalityListState extends Equatable { 6 | const MunicipalityListState(); 7 | 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class MunicipalityListInitial extends MunicipalityListState {} 13 | 14 | class MunicipalityListLoading extends MunicipalityListState {} 15 | 16 | class MunicipalityListLoaded extends MunicipalityListState { 17 | final List municipalities; 18 | 19 | const MunicipalityListLoaded({@required this.municipalities}) 20 | : assert(municipalities != null); 21 | 22 | @override 23 | List get props => [municipalities]; 24 | } 25 | 26 | class MunicipalityListError extends MunicipalityListState { 27 | final String errorMessage; 28 | 29 | const MunicipalityListError({@required this.errorMessage}) 30 | : assert(errorMessage != null); 31 | 32 | @override 33 | List get props => [errorMessage]; 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/pages/radar/blocs/states/radar_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | abstract class RadarState extends Equatable { 5 | const RadarState(); 6 | 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class RadarInitial extends RadarState {} 12 | 13 | class RadarLoading extends RadarState { 14 | final bool showAnimation; 15 | 16 | const RadarLoading({ 17 | this.showAnimation = true, 18 | }); 19 | 20 | @override 21 | List get props => [showAnimation]; 22 | } 23 | 24 | class RadarLoaded extends RadarState { 25 | final dynamic radar; 26 | final bool showImage; 27 | final bool showAnimation; 28 | 29 | const RadarLoaded({ 30 | @required this.radar, 31 | @required this.showImage, 32 | this.showAnimation = true, 33 | }) : assert(radar != null, showImage != null); 34 | 35 | @override 36 | List get props => [radar, showImage, showAnimation]; 37 | } 38 | 39 | class RadarError extends RadarState { 40 | final String errorMessage; 41 | 42 | const RadarError({@required this.errorMessage}) 43 | : assert(errorMessage != null); 44 | 45 | @override 46 | List get props => [errorMessage]; 47 | } 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Cuba Weather Changelog 2 | ## [Unreleased] 3 | ### Added 4 | * Initial carussel 5 | * Satellite images 6 | * Radar images 7 | * Preference screen 8 | * Dark mode 9 | * Name of days in weather forecast 10 | * History of the locations consulted 11 | * View last information consulted if offline 12 | * What's new 13 | 14 | ### Changed 15 | * Home page elements position 16 | * Merge donate items in menu into one item 17 | * Change order of the min and max temperature 18 | * MIT licence to GNU General Public License v3.0 (GPL-3.0) 19 | 20 | ### Fixed 21 | * Error in the forecast for the following days 22 | * Right Overflow Bug 23 | * Minor ui bugs 24 | 25 | ### Removed 26 | * Button at side of municipality 27 | 28 | 29 | ## [1.4.0] - February 10, 2020 30 | ### Added 31 | * GPS location 32 | * Forecast for the following days 33 | * Extended forecast of the current day 34 | * Extended forecast the next day 35 | * Weather perspectives 36 | * Donor's List 37 | * Information about donations 38 | * Information from social networks 39 | * Share the application 40 | 41 | ## [1.3.2] - January 28, 2020 42 | ### Added 43 | * InsMet as a data source. 44 | * All municipalities in the country. 45 | * Dynamic icons. 46 | 47 | ## [1.0.0] - January 17, 2020 48 | 49 | * First implementation 50 | -------------------------------------------------------------------------------- /lib/src/pages/home/blocs/states/weather_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | import 'package:cuba_weather/src/pages/home/models/models.dart'; 5 | 6 | abstract class WeatherState extends Equatable { 7 | const WeatherState(); 8 | 9 | @override 10 | List get props => []; 11 | } 12 | 13 | class WeatherEmpty extends WeatherState {} 14 | 15 | class WeatherInitial extends WeatherState { 16 | final String municipality; 17 | 18 | const WeatherInitial({@required this.municipality}) 19 | : assert(municipality != null); 20 | 21 | @override 22 | List get props => [municipality]; 23 | } 24 | 25 | class WeatherFindingLocation extends WeatherState {} 26 | 27 | class WeatherLoading extends WeatherState {} 28 | 29 | class WeatherLoaded extends WeatherState { 30 | final WeatherModel weather; 31 | 32 | const WeatherLoaded({@required this.weather}) : assert(weather != null); 33 | 34 | @override 35 | List get props => [weather]; 36 | } 37 | 38 | class WeatherError extends WeatherState { 39 | final String errorMessage; 40 | 41 | const WeatherError({@required this.errorMessage}) 42 | : assert(errorMessage != null); 43 | 44 | @override 45 | List get props => [errorMessage]; 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/pages/forecast/blocs/states/forecast_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | 5 | abstract class ForecastState extends Equatable { 6 | const ForecastState(); 7 | 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class ForecastInitial extends ForecastState {} 13 | 14 | class ForecastLoading extends ForecastState { 15 | final bool showAnimation; 16 | 17 | const ForecastLoading({ 18 | this.showAnimation = true, 19 | }); 20 | 21 | @override 22 | List get props => [showAnimation]; 23 | } 24 | 25 | class ForecastLoaded extends ForecastState { 26 | final InsmetForecastModel forecast; 27 | final bool showImage; 28 | final bool showAnimation; 29 | 30 | const ForecastLoaded({ 31 | @required this.forecast, 32 | @required this.showImage, 33 | this.showAnimation = true, 34 | }) : assert(forecast != null, showImage != null); 35 | 36 | @override 37 | List get props => [forecast, showImage, showAnimation]; 38 | } 39 | 40 | class ForecastError extends ForecastState { 41 | final String errorMessage; 42 | 43 | const ForecastError({@required this.errorMessage}) 44 | : assert(errorMessage != null); 45 | 46 | @override 47 | List get props => [errorMessage]; 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/pages/satellite/blocs/states/satellite_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | 5 | abstract class SatelliteState extends Equatable { 6 | const SatelliteState(); 7 | 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class SatelliteInitial extends SatelliteState {} 13 | 14 | class SatelliteLoading extends SatelliteState { 15 | final bool showAnimation; 16 | 17 | const SatelliteLoading({ 18 | this.showAnimation = true, 19 | }); 20 | 21 | @override 22 | List get props => [showAnimation]; 23 | } 24 | 25 | class SatelliteLoaded extends SatelliteState { 26 | final InsmetSatelliteModel satellite; 27 | final bool showImage; 28 | final bool showAnimation; 29 | 30 | const SatelliteLoaded({ 31 | @required this.satellite, 32 | @required this.showImage, 33 | this.showAnimation = true, 34 | }) : assert(satellite != null, showImage != null); 35 | 36 | @override 37 | List get props => [satellite, showImage, showAnimation]; 38 | } 39 | 40 | class SatelliteError extends SatelliteState { 41 | final String errorMessage; 42 | 43 | const SatelliteError({@required this.errorMessage}) 44 | : assert(errorMessage != null); 45 | 46 | @override 47 | List get props => [errorMessage]; 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/value_tile_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:cuba_weather/src/widgets/widgets.dart'; 4 | 5 | class ValueTileWidget extends StatelessWidget { 6 | final String label; 7 | final String value; 8 | final IconData iconData; 9 | final Widget widget; 10 | 11 | ValueTileWidget(this.label, this.value, {this.iconData, this.widget}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Column( 16 | mainAxisAlignment: MainAxisAlignment.center, 17 | children: [ 18 | this.label != null 19 | ? Text( 20 | this.label, 21 | textAlign: TextAlign.center, 22 | style: TextStyle( 23 | color: Colors.white, 24 | ), 25 | ) 26 | : EmptyWidget(), 27 | this.iconData != null 28 | ? Icon( 29 | iconData, 30 | color: Colors.white, 31 | size: 20, 32 | ) 33 | : EmptyWidget(), 34 | this.widget != null ? widget : EmptyWidget(), 35 | SizedBox( 36 | height: 10, 37 | ), 38 | Text( 39 | this.value, 40 | style: TextStyle( 41 | color: Colors.white, 42 | ), 43 | ), 44 | ], 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/weather_error_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'package:cuba_weather/src/utils/utils.dart'; 5 | 6 | class WeatherErrorWidget extends StatelessWidget { 7 | final String errorMessage; 8 | 9 | const WeatherErrorWidget(this.errorMessage) : assert(errorMessage != null); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return ListView( 14 | children: [ 15 | Container( 16 | margin: EdgeInsets.all(10), 17 | child: Icon( 18 | Icons.error_outline, 19 | color: Colors.white, 20 | size: 150, 21 | ), 22 | ), 23 | Text( 24 | Constants.errorMessage, 25 | textAlign: TextAlign.center, 26 | style: TextStyle( 27 | color: Colors.white, 28 | fontWeight: FontWeight.bold, 29 | fontSize: 30, 30 | ), 31 | ), 32 | Container( 33 | margin: EdgeInsets.all(20), 34 | child: Container( 35 | padding: EdgeInsets.all(10), 36 | child: Text( 37 | errorMessage, 38 | textAlign: TextAlign.left, 39 | style: TextStyle( 40 | color: Colors.white, 41 | fontWeight: FontWeight.w600, 42 | fontSize: 16, 43 | ), 44 | ), 45 | ), 46 | ), 47 | ], 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/pages/donors/donors_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'package:cuba_weather/src/pages/donors/widgets/widgets.dart'; 5 | 6 | class DonorsPage extends StatefulWidget { 7 | const DonorsPage(); 8 | 9 | @override 10 | State createState() => DonorsPageState(); 11 | } 12 | 13 | class DonorsPageState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | backgroundColor: Theme.of(context).backgroundColor, 18 | appBar: AppBar( 19 | elevation: 0, 20 | title: Text('Donantes'), 21 | centerTitle: true, 22 | ), 23 | body: ListView( 24 | children: [ 25 | DonorWidget(), 26 | Padding(padding: EdgeInsets.only(bottom: 20)), 27 | Center( 28 | child: Text( 29 | 'Total:', 30 | textAlign: TextAlign.center, 31 | style: TextStyle( 32 | color: Colors.white, 33 | fontWeight: FontWeight.bold, 34 | fontSize: 16, 35 | ), 36 | ), 37 | ), 38 | Center( 39 | child: Text( 40 | '1910 CUP - 20 CUC - 12 CUC (Saldo)', 41 | textAlign: TextAlign.center, 42 | style: TextStyle( 43 | color: Colors.white, 44 | fontWeight: FontWeight.w400, 45 | ), 46 | ), 47 | ), 48 | Padding(padding: EdgeInsets.only(bottom: 50)) 49 | ], 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/pages/marine_forecast/blocs/marine_forecast_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:meta/meta.dart'; 5 | import 'package:bloc/bloc.dart'; 6 | 7 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 8 | 9 | import 'package:cuba_weather/src/pages/marine_forecast/blocs/blocs.dart'; 10 | import 'package:cuba_weather/src/utils/utils.dart'; 11 | 12 | class MarineForecastBloc 13 | extends Bloc { 14 | final CubaWeather api; 15 | 16 | MarineForecastBloc({@required this.api}) : assert(api != null); 17 | 18 | @override 19 | MarineForecastState get initialState => MarineForecastInitial(); 20 | 21 | @override 22 | Stream mapEventToState( 23 | MarineForecastEvent event) async* { 24 | if (event is FetchMarineForecast) { 25 | yield MarineForecastLoading(); 26 | try { 27 | InsmetMarineForecastModel forecast; 28 | switch (event.forecastType) { 29 | case 'marine': 30 | forecast = await api.getInsmetMarineForecast(); 31 | break; 32 | default: 33 | forecast = await api.getInsmetMarineForecast(); 34 | break; 35 | } 36 | yield MarineForecastLoaded(forecast: forecast); 37 | } on BadRequestException catch (e) { 38 | log(e.toString()); 39 | yield MarineForecastError( 40 | errorMessage: Constants.errorMessageBadRequestException, 41 | ); 42 | } catch (e) { 43 | log(e.toString()); 44 | yield MarineForecastError(errorMessage: e.toString()); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/weather_swipe_pager.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper/flutter_swiper.dart'; 3 | 4 | import 'package:cuba_weather/src/pages/home/models/models.dart'; 5 | import 'package:cuba_weather/src/pages/home/widgets/widgets.dart'; 6 | import 'package:cuba_weather/src/widgets/widgets.dart'; 7 | 8 | class WeatherSwipePager extends StatelessWidget { 9 | const WeatherSwipePager({ 10 | Key key, 11 | @required this.weather, 12 | }) : super(key: key); 13 | 14 | final WeatherModel weather; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | var todayWeather = weather.getTodayForecast(); 19 | return Container( 20 | width: MediaQuery.of(context).size.width, 21 | height: 350, 22 | child: Swiper( 23 | itemCount: todayWeather == null ? 1 : 2, 24 | index: 0, 25 | itemBuilder: (context, index) { 26 | if (index == 0) { 27 | return CurrentConditionsWidget(weather: weather); 28 | } else if (index == 1 && todayWeather != null) { 29 | return TodayForecastWidget(weather: weather); 30 | } else if (index == 1 && todayWeather == null || index == 2) { 31 | return TemperatureLineChartWidget( 32 | weather.getForecasts(), 33 | animate: true, 34 | ); 35 | } 36 | return EmptyWidget(); 37 | }, 38 | pagination: SwiperPagination( 39 | margin: EdgeInsets.all(5.0), 40 | builder: DotSwiperPaginationBuilder( 41 | size: 5, 42 | activeSize: 5, 43 | color: Colors.white.withOpacity(0.4), 44 | activeColor: Colors.white, 45 | ), 46 | ), 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/ServiceDefinitions.json 65 | **/ios/Runner/GeneratedPluginRegistrant.* 66 | 67 | # Exceptions to above rules. 68 | !**/ios/**/default.mode1v3 69 | !**/ios/**/default.mode2v3 70 | !**/ios/**/default.pbxuser 71 | !**/ios/**/default.perspectivev3 72 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 73 | pubspec.lock 74 | ios/Flutter/flutter_export_environment.sh 75 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | NSLocationWhenInUseUsageDescription 8 | This app needs access to location when open. 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | Cuba Weather 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cuba Weather Flutter 2 | 3 | ![Cuba Weather logo](https://pbs.twimg.com/profile_banners/1220496411332161538/1579824669/600x200) 4 | 5 | [![License:GPL-3.0](https://img.shields.io/github/license/cuba-weather/cuba-weather-flutter?color=brightgreen&label=License)](https://opensource.org/licenses/GPL-3.0) [![Codemagic build status](https://api.codemagic.io/apps/5e2e7e7cb9213d1ee194babb/5e2e7e7cb9213d1ee194baba/status_badge.svg)](https://codemagic.io/apps/5e2e7e7cb9213d1ee194babb/5e2e7e7cb9213d1ee194baba/latest_build) [![Last commit](https://img.shields.io/github/last-commit/cuba-weather/cuba-weather-flutter.svg?style=flat)](https://github.com/cuba-weather/cuba-weather-flutter/commits) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/cuba-weather/cuba-weather-flutter)](https://github.com/cuba-weather/cuba-weather-flutter/commits) [![Github Stars](https://img.shields.io/github/stars/cuba-weather/cuba-weather-flutter?style=flat&logo=github)](https://github.com/cuba-weather/cuba-weather-flutter) [![Github Forks](https://img.shields.io/github/forks/cuba-weather/cuba-weather-flutter?style=flat&logo=github)](https://github.com/cuba-weather/cuba-weather-flutter) [![Github Watchers](https://img.shields.io/github/watchers/cuba-weather/cuba-weather-flutter?style=flat&logo=github)](https://github.com/cuba-weather/cuba-weather-flutter) [![Twitter](https://img.shields.io/twitter/follow/cubaweatherapp?style=flat&logo=twitter)](https://twitter.com/cubaweatherapp) [![Website](https://img.shields.io/website?up_message=online&url=https%3A%2F%2Fcubaweather.app)](https://cubaweather.app) [![GitHub contributors](https://img.shields.io/github/contributors/cuba-weather/cuba-weather-flutter)](https://github.com/cuba-weather/cuba-weather-flutter/graphs/contributors) 6 | 7 | Application for mobile devices of the Cuba Weather project implemented with Flutter. 8 | -------------------------------------------------------------------------------- /lib/src/pages/settings/settings_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:preferences/preferences.dart'; 4 | 5 | import 'package:cuba_weather/src/utils/utils.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class SettingsPage extends StatefulWidget { 9 | SettingsPage({Key key}) : super(key: key); 10 | 11 | @override 12 | State createState() => SettingsPageState(); 13 | } 14 | 15 | class SettingsPageState extends State { 16 | SettingsPageState(); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | backgroundColor: Theme.of(context).backgroundColor, 22 | appBar: AppBar( 23 | elevation: 0, 24 | title: Text('Configuración'), 25 | centerTitle: true, 26 | ), 27 | body: PreferencePage([ 28 | PreferenceTitle( 29 | 'Tema', 30 | style: TextStyle( 31 | color: Colors.white, 32 | ), 33 | ), 34 | RadioPreference( 35 | 'Claro', 36 | 'light', 37 | Constants.themeMode, 38 | onSelect: () { 39 | updateTheme("light"); 40 | }, 41 | ), 42 | RadioPreference( 43 | 'Oscuro', 44 | 'dark', 45 | Constants.themeMode, 46 | onSelect: () { 47 | updateTheme("dark"); 48 | }, 49 | ), 50 | RadioPreference( 51 | 'Sistema', 52 | 'system', 53 | Constants.themeMode, 54 | onSelect: () { 55 | updateTheme("system"); 56 | }, 57 | ), 58 | ]), 59 | ); 60 | } 61 | 62 | void updateTheme(String themeMode) { 63 | Provider.of(context, listen: false) 64 | .updateTheme(themeMode); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/weather_finding_location_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class WeatherFindingLocationWidget extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return Row( 8 | mainAxisAlignment: MainAxisAlignment.center, 9 | children: [ 10 | Column( 11 | mainAxisAlignment: MainAxisAlignment.center, 12 | children: [ 13 | Stack( 14 | alignment: Alignment.center, 15 | children: [ 16 | Container( 17 | margin: EdgeInsets.symmetric( 18 | horizontal: 20, 19 | vertical: 10, 20 | ), 21 | child: Icon( 22 | Icons.my_location, 23 | color: Colors.white, 24 | size: 100, 25 | ), 26 | ), 27 | Container( 28 | margin: EdgeInsets.symmetric( 29 | horizontal: 20, 30 | vertical: 10, 31 | ), 32 | child: CircularProgressIndicator(), 33 | ), 34 | ], 35 | ), 36 | Container( 37 | margin: EdgeInsets.symmetric( 38 | horizontal: 20, 39 | vertical: 10, 40 | ), 41 | child: Text( 42 | 'Buscando el municipio actual', 43 | textAlign: TextAlign.center, 44 | style: TextStyle( 45 | color: Colors.white, 46 | fontWeight: FontWeight.bold, 47 | fontSize: 16, 48 | ), 49 | ), 50 | ), 51 | ], 52 | ) 53 | ], 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 17 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /lib/src/pages/carousel/widgets/dots_indicator_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class DotsIndicatorWidget extends AnimatedWidget { 6 | DotsIndicatorWidget({ 7 | this.controller, 8 | this.itemCount, 9 | this.onPageSelected, 10 | this.color: Colors.white, 11 | }) : super(listenable: controller); 12 | 13 | /// The PageController that this DotsIndicator is representing. 14 | final PageController controller; 15 | 16 | /// The number of items managed by the PageController 17 | final int itemCount; 18 | 19 | /// Called when a dot is tapped 20 | final ValueChanged onPageSelected; 21 | 22 | /// The color of the dots. 23 | /// 24 | /// Defaults to `Colors.white`. 25 | final Color color; 26 | 27 | // The base size of the dots 28 | static const double _kDotSize = 6.0; 29 | 30 | // The increase in the size of the selected dot 31 | static const double _kMaxZoom = 2.0; 32 | 33 | // The distance between the center of each dot 34 | static const double _kDotSpacing = 25.0; 35 | 36 | Widget _buildDot(int index) { 37 | double temp = Curves.easeOut.transform( 38 | max( 39 | 0.0, 40 | 1.0 - ((controller.page ?? controller.initialPage) - index).abs(), 41 | ), 42 | ); 43 | double zoom = 1.0 + (_kMaxZoom - 1.0) * temp; 44 | return new Container( 45 | width: _kDotSpacing, 46 | child: new Center( 47 | child: new Material( 48 | color: color, 49 | type: MaterialType.circle, 50 | child: new Container( 51 | width: _kDotSize * zoom, 52 | height: _kDotSize * zoom, 53 | child: new InkWell( 54 | onTap: () => onPageSelected(index), 55 | ), 56 | ), 57 | ), 58 | ), 59 | ); 60 | } 61 | 62 | Widget build(BuildContext context) { 63 | return new Row( 64 | mainAxisAlignment: MainAxisAlignment.center, 65 | children: new List.generate(itemCount, _buildDot), 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/temperature_line_chart_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:charts_flutter/flutter.dart' as charts; 2 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class TemperatureLineChartWidget extends StatelessWidget { 6 | final List weathers; 7 | final bool animate; 8 | 9 | TemperatureLineChartWidget(this.weathers, {this.animate}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Padding( 14 | padding: const EdgeInsets.all(40.0), 15 | child: charts.TimeSeriesChart( 16 | [ 17 | new charts.Series( 18 | id: 'Temperature', 19 | colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault, 20 | domainFn: (WeatherForecastModel weather, _) { 21 | DateTime now = DateTime.now(); 22 | DateTime lastCurrentDateOfMonth = 23 | new DateTime(now.year, now.month + 1, 0); 24 | DateTime current1day = new DateTime(now.year, now.month, 1); 25 | DateTime next1day = 26 | current1day.add(Duration(days: lastCurrentDateOfMonth.day)); 27 | DateTime currentForecastDate = 28 | new DateTime(now.year, now.month, weather.day); 29 | if (weather.day < now.day) { 30 | currentForecastDate = 31 | new DateTime(next1day.year, next1day.month, weather.day); 32 | } 33 | return currentForecastDate; 34 | }, 35 | measureFn: (WeatherForecastModel weather, _) => 36 | weather.temperatureMin, 37 | data: weathers, 38 | ), 39 | ], 40 | animate: animate, 41 | animationDuration: Duration(milliseconds: 500), 42 | primaryMeasureAxis: new charts.NumericAxisSpec( 43 | tickProviderSpec: new charts.BasicNumericTickProviderSpec( 44 | zeroBound: false, 45 | ), 46 | ), 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/pages/radar/blocs/radar_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:meta/meta.dart'; 5 | import 'package:bloc/bloc.dart'; 6 | import 'package:preferences/preference_service.dart'; 7 | 8 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 9 | 10 | import 'package:cuba_weather/src/pages/radar/blocs/blocs.dart'; 11 | import 'package:cuba_weather/src/utils/utils.dart'; 12 | 13 | class RadarBloc extends Bloc { 14 | final CubaWeather api; 15 | 16 | RadarBloc({@required this.api}) : assert(api != null); 17 | 18 | @override 19 | RadarState get initialState => RadarInitial(); 20 | 21 | @override 22 | Stream mapEventToState(RadarEvent event) async* { 23 | if (event is FetchRadar) { 24 | yield RadarLoading(); 25 | //var keyPref = Constants.showImageForecastPage; 26 | //var showImage = PrefService.getBool(keyPref) ?? false; 27 | try { 28 | // InsmetRadarModel radar; 29 | // switch (event.radarType) { 30 | // case 'latest_eastir.jpg': 31 | // radar = await api.getRadarWisconsinMadisonLatestEastir(); 32 | // break; 33 | // default://www.intellicast.com.jpg 34 | // radar = await api.getRadarWeatherUndergroundIntellicast(); 35 | // break; 36 | // } 37 | // yield RadarLoaded(radar: radar, showImage: showImage); 38 | } on BadRequestException catch (e) { 39 | log(e.toString()); 40 | yield RadarError( 41 | errorMessage: Constants.errorMessageBadRequestException, 42 | ); 43 | } catch (e) { 44 | log(e.toString()); 45 | yield RadarError(errorMessage: e.toString()); 46 | } 47 | } 48 | if (event is SetShowImageRadar) { 49 | PrefService.setBool(Constants.showImageForecastPage, event.showImage); 50 | yield RadarLoading(showAnimation: false); 51 | yield RadarLoaded( 52 | radar: event.radar, 53 | showImage: event.showImage, 54 | showAnimation: false, 55 | ); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/utils/constants.dart: -------------------------------------------------------------------------------- 1 | class Constants { 2 | // Errors 3 | static final String errorMessage = 'Ha ocurrido un error'; 4 | static final String errorMessageBadRequestException = 5 | 'No se ha podido establecer conexión con la red nacional. Por favor, revise su conexión y vuelva a intentarlo.'; 6 | static final String errorMessageParseException = 7 | 'La fuente de datos a cambiado el formato. Espere una nueva actualización de la aplicación para adaptarse a este.'; 8 | 9 | static final String appName = 'Cuba Weather'; 10 | static final String appLogo = 'images/logo.png'; 11 | static final String appSlogan = 'De CUBA para CUBA'; 12 | static final String appSloganLong = 'Tu app meteorológica de CUBA para CUBA'; 13 | static final String authorsPlaceHolderImageAssetURL = 'images/no-image.png'; 14 | static final String loadingPlaceholder = 'images/loading.gif'; 15 | static final String locale = "es_ES"; 16 | // static final String fontFamily = "Exo2"; 17 | static final String fontFamily = "GoogleSans"; 18 | 19 | static final String insmetForecastSource = 'www.insmet.cu'; 20 | 21 | // INSMET Urls 22 | static final String insmetURL = 'http://www.insmet.cu'; 23 | static final String insmetUrlAuthorsImg = 24 | 'http://www.insmet.cu/Imagenes/Meteorologos/'; 25 | static final String insmetUrlTodayForecast = 26 | 'http://www.insmet.cu/asp/link.asp?PRONOSTICO'; 27 | static final String insmetUrlTomorrowForecast = 28 | 'http://www.insmet.cu/asp/genesis.asp?TB0=PLANTILLAS&TB1=PTM&TB2=/Pronostico/Ptm.txt'; 29 | static final String insmetUrlPerspectives = 30 | 'http://www.insmet.cu/asp/genesis.asp?TB0=PLANTILLAS&TB1=PERSPECTIVASTT'; 31 | static final String insmetUrlMarineForecast = 32 | 'http://www.insmet.cu/asp/genesis.asp?TB0=PLANTILLAS&TB1=MAR&TB2=/Pronostico/Marady.txt'; 33 | 34 | // SharedPreferences 35 | static String carouselWasSeen = 'carouselWasSeen'; 36 | static String lastVersion = 'lastVersion'; 37 | static String municipality = 'municipality'; 38 | static String showImageForecastPage = 'showImageForecastPage'; 39 | static String themeMode = 'themeMode'; 40 | static String municipalityRecord = 'municipalityRecord'; 41 | static int municipalityRecordSize = 10; 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/pages/forecast/blocs/forecast_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:meta/meta.dart'; 5 | import 'package:bloc/bloc.dart'; 6 | import 'package:preferences/preference_service.dart'; 7 | 8 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 9 | 10 | import 'package:cuba_weather/src/pages/forecast/blocs/blocs.dart'; 11 | import 'package:cuba_weather/src/utils/utils.dart'; 12 | 13 | class ForecastBloc extends Bloc { 14 | final CubaWeather api; 15 | 16 | ForecastBloc({@required this.api}) : assert(api != null); 17 | 18 | @override 19 | ForecastState get initialState => ForecastInitial(); 20 | 21 | @override 22 | Stream mapEventToState(ForecastEvent event) async* { 23 | if (event is FetchForecast) { 24 | yield ForecastLoading(); 25 | var keyPref = Constants.showImageForecastPage; 26 | var showImage = PrefService.getBool(keyPref) ?? false; 27 | try { 28 | InsmetForecastModel forecast; 29 | switch (event.forecastType) { 30 | case 'today': 31 | forecast = await api.getInsmetTodayForecast(); 32 | break; 33 | case 'tomorrow': 34 | forecast = await api.getInsmetTomorrowForecast(); 35 | break; 36 | case 'perspectives': 37 | forecast = await api.getInsmetPerspectiveForecast(); 38 | break; 39 | default: 40 | forecast = await api.getInsmetTodayForecast(); 41 | break; 42 | } 43 | yield ForecastLoaded(forecast: forecast, showImage: showImage); 44 | } on BadRequestException catch (e) { 45 | log(e.toString()); 46 | yield ForecastError( 47 | errorMessage: Constants.errorMessageBadRequestException, 48 | ); 49 | } catch (e) { 50 | log(e.toString()); 51 | yield ForecastError(errorMessage: e.toString()); 52 | } 53 | } 54 | if (event is SetShowImageForecast) { 55 | PrefService.setBool(Constants.showImageForecastPage, event.showImage); 56 | yield ForecastLoading(showAnimation: false); 57 | yield ForecastLoaded( 58 | forecast: event.forecast, 59 | showImage: event.showImage, 60 | showAnimation: false, 61 | ); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/pages/home/models/weather_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuba_weather_dart/cuba_weather_dart.dart' as cwd; 2 | 3 | class WeatherModel extends cwd.WeatherModel { 4 | cwd.WeatherForecastModel getTodayForecast() { 5 | var now = DateTime.now(); 6 | return forecasts.firstWhere((x) => x.day == now.day, orElse: () => null); 7 | } 8 | 9 | List getForecasts() { 10 | var now = DateTime.now(); 11 | var day = now.day; 12 | var ascendant = true; 13 | for (var i = 1; i < forecasts.length; ++i) { 14 | if (forecasts[i].day < forecasts[i - 1].day) { 15 | ascendant = false; 16 | } 17 | } 18 | var result = List(); 19 | if (ascendant) { 20 | result.addAll(forecasts.where((forecast) => forecast.day > day)); 21 | } else { 22 | var isLeft = true; 23 | var left = List(); 24 | var right = List(); 25 | left.add(forecasts[0]); 26 | for (var i = 1; i < forecasts.length; ++i) { 27 | if (forecasts[i - 1].day > forecasts[i].day) { 28 | isLeft = false; 29 | } 30 | if (isLeft) { 31 | left.add(forecasts[i]); 32 | } else { 33 | right.add(forecasts[i]); 34 | } 35 | } 36 | if (day > right.last.day) { 37 | result.addAll(left.where((item) => day < item.day)); 38 | result.addAll(right); 39 | } else { 40 | result.addAll(right.where((item) => day < item.day)); 41 | } 42 | } 43 | return result; 44 | } 45 | 46 | static WeatherModel getModel(cwd.WeatherModel _weather) { 47 | var weather = WeatherModel(); 48 | weather.cityName = _weather.cityName; 49 | weather.temperature = _weather.temperature; 50 | weather.dateTime = _weather.dateTime; 51 | weather.pressure = _weather.pressure; 52 | weather.humidity = _weather.humidity; 53 | weather.windVelocity = _weather.windVelocity; 54 | weather.windDirection = _weather.windDirection; 55 | weather.windDirectionDescription = _weather.windDirectionDescription; 56 | weather.windDirectionDegree = _weather.windDirectionDegree; 57 | weather.windDirectionRadians = _weather.windDirectionRadians; 58 | weather.weatherForecast = _weather.weatherForecast; 59 | weather.droughtStatus = _weather.droughtStatus; 60 | weather.forecasts = _weather.forecasts; 61 | return weather; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/src/pages/satellite/satellite_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'package:story_view/story_view.dart'; 6 | 7 | class SatellitePage extends StatefulWidget { 8 | final String satelliteType; 9 | final String pageTitle; 10 | 11 | SatellitePage({ 12 | Key key, 13 | @required this.satelliteType, 14 | @required this.pageTitle, 15 | }) : assert(satelliteType != null), 16 | super(key: key); 17 | 18 | @override 19 | _SatellitePageState createState() => _SatellitePageState(); 20 | } 21 | 22 | class _SatellitePageState extends State 23 | with TickerProviderStateMixin { 24 | AnimationController fadeController; 25 | Animation fadeAnimation; 26 | final storyController = StoryController(); 27 | 28 | String url = 'http://www.insmet.cu/SATELITE/TMP/'; 29 | List imgsIndex = List(); 30 | List imgs = [ 31 | '020617.jpg', 32 | '020647.jpg', 33 | '020717.jpg', 34 | '020719.jpg', 35 | '020747.jpg', 36 | '020817.jpg', 37 | '020847.jpg', 38 | '020917.jpg', 39 | '020947.jpg', 40 | '021017.jpg' 41 | ]; 42 | List storyItems = List(); 43 | @override 44 | void initState() { 45 | var rng = new Random(); 46 | for (var i = 0; i < 10; i++) { 47 | imgsIndex.add(rng.nextInt(imgs.length)); 48 | } 49 | imgsIndex.sort(); 50 | for (var i = 0; i < imgsIndex.length; i++) { 51 | storyItems.add(StoryItem.pageGif(url + imgs[imgsIndex[i]], 52 | caption: imgs[imgsIndex[i]], 53 | controller: storyController, 54 | duration: Duration(milliseconds: 500))); 55 | } 56 | } 57 | 58 | @override 59 | void dispose() { 60 | storyController.dispose(); 61 | super.dispose(); 62 | } 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | return Scaffold( 67 | backgroundColor: Theme.of(context).backgroundColor, 68 | appBar: AppBar( 69 | elevation: 0, 70 | title: Text(widget.pageTitle), 71 | centerTitle: true, 72 | ), 73 | body: StoryView( 74 | storyItems, 75 | onStoryShow: (s) { 76 | print("Showing a story"); 77 | }, 78 | onComplete: () { 79 | print("Completed a cycle"); 80 | }, 81 | progressPosition: ProgressPosition.top, 82 | repeat: true, 83 | controller: storyController, 84 | ), 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributions are highly appreciated. 2 | 3 | ### All contributors will be granted credit on the following list: 4 | * Leynier Gutiérrez González ([@leynier](https://github.com/leynier)) 5 | * Norlan Capote Díaz ([@norlancd](https://github.com/norlancd)) 6 | * Roberto Marti Cedeño ([@rmarticedeno](https://github.com/rmarticedeno)) 7 | * Gabriel A. López López ([@glpzzz](https://github.com/glpzzz)) 8 | * Leonel A. García López ([@stdevlag](https://github.com/stdevlag)) 9 | * Carlos Bermudez Porto ([@stdevCbermudez](https://github.com/stdevCbermudez)) 10 | 11 | You can help out by: 12 | - Reporting a bug 13 | - Reviewing the code 14 | - Submitting a fix 15 | - Proposing new features 16 | - Becoming a maintainer 17 | 18 | ## Branches 19 | 20 | We're using the following branches to manage work: 21 | - `develop` is semi-stable and should be used as the branch to fork from 22 | - `master` is stable and ready for prod (or it will be once we merge in the first release) 23 | - `feature`, `bug` branches: unstable development 24 | 25 | ## How to report bugs 26 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https:ithub.//gcom/cuba-weather/cuba-weather-flutter/issues/new/choose); it's that easy! 27 | 28 | ### Write bug reports with detail, background, and sample code 29 | **Great Bug Reports** tend to have: 30 | 31 | - A quick summary and/or background 32 | - Steps to reproduce 33 | - Be specific! 34 | - Give sample code if you can. 35 | - What you expected to happen 36 | - What actually happens 37 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 38 | 39 | We <3 thorough bug reports. 40 | 41 | ## How to contribute code 42 | We use GitHub to host code, to track issues and feature requests, as well as accept pull requests. 43 | 44 | Pull requests are the best way to propose changes to the codebase. We use a loose version of Git Flow 45 | and actively welcome your pull requests: 46 | 47 | 1. Set up your local development environment. See [USAGE.md](USAGE.md) for instructions. 48 | 1. Make sure to use the latest Flutter stable-channel SDK release. 49 | 1. Create a new branch based on `develop`: 50 | - Feature branches should start with `feature/` 51 | - Bugfix branches should start with `bug/` 52 | 1. Implement your changes. 53 | 1. Add tests if applicable. 54 | 1. Make sure your code lints. 55 | 1. Issue that pull request! 56 | 57 | ### Any contributions you make will be under the GPL-3.0 Software License 58 | When you submit code changes, your submissions are understood to be under the same [GPL-3.0](LICENSE) that covers the project. 59 | -------------------------------------------------------------------------------- /lib/src/pages/satellite/blocs/satellite_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:meta/meta.dart'; 5 | import 'package:bloc/bloc.dart'; 6 | import 'package:preferences/preference_service.dart'; 7 | 8 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 9 | 10 | import 'package:cuba_weather/src/pages/satellite/blocs/blocs.dart'; 11 | import 'package:cuba_weather/src/utils/utils.dart'; 12 | 13 | class SatelliteBloc extends Bloc { 14 | final CubaWeather api; 15 | 16 | SatelliteBloc({@required this.api}) : assert(api != null); 17 | 18 | @override 19 | SatelliteState get initialState => SatelliteInitial(); 20 | 21 | @override 22 | Stream mapEventToState(SatelliteEvent event) async* { 23 | if (event is FetchSatellite) { 24 | yield SatelliteLoading(); 25 | var keyPref = Constants.showImageForecastPage; 26 | var showImage = PrefService.getBool(keyPref) ?? false; 27 | try { 28 | InsmetSatelliteModel satellite; 29 | switch (event.satelliteType) { 30 | case 'latest_eastir.jpg': 31 | satellite = await api.getSatelliteWisconsinMadisonLatestEastir(); 32 | break; 33 | case 'latest_eastvis.jpg': 34 | satellite = await api.getSatelliteWisconsinMadisonLatestEastvis(); 35 | break; 36 | case 'goes.gsfc.nasa.gov.jpg': 37 | satellite = await api.getSatelliteWisconsinMadisonGOESGSFC(); 38 | break; 39 | case 'cuba-g16.jpg': 40 | satellite = await api.getSatelliteMarshallSpaceFlightCenterGOESMSFC(); 41 | break; 42 | default://www.intellicast.com.jpg 43 | satellite = await api.getSatelliteWeatherUndergroundIntellicast(); 44 | break; 45 | } 46 | yield SatelliteLoaded(satellite: satellite, showImage: showImage); 47 | } on BadRequestException catch (e) { 48 | log(e.toString()); 49 | yield SatelliteError( 50 | errorMessage: Constants.errorMessageBadRequestException, 51 | ); 52 | } catch (e) { 53 | log(e.toString()); 54 | yield SatelliteError(errorMessage: e.toString()); 55 | } 56 | } 57 | if (event is SetShowImageSatellite) { 58 | PrefService.setBool(Constants.showImageForecastPage, event.showImage); 59 | yield SatelliteLoading(showAnimation: false); 60 | yield SatelliteLoaded( 61 | satellite: event.satellite, 62 | showImage: event.showImage, 63 | showAnimation: false, 64 | ); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/weather_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:weather_icons/weather_icons.dart'; 3 | 4 | import 'package:cuba_weather/src/pages/home/models/models.dart'; 5 | import 'package:cuba_weather/src/pages/home/widgets/widgets.dart'; 6 | 7 | class WeatherWidget extends StatelessWidget { 8 | final WeatherModel weather; 9 | 10 | WeatherWidget({this.weather}) : assert(weather != null); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | var updateTime = TimeOfDay.fromDateTime(weather.dateTime).format(context); 15 | return Center( 16 | child: ListView( 17 | children: [ 18 | SizedBox(height: 20), 19 | Text( 20 | this.weather.cityName.toUpperCase(), 21 | textAlign: TextAlign.center, 22 | style: TextStyle( 23 | fontWeight: FontWeight.w900, 24 | color: Colors.white, 25 | fontSize: 20, 26 | ), 27 | ), 28 | SizedBox(height: 20), 29 | Row( 30 | mainAxisAlignment: MainAxisAlignment.center, 31 | children: [ 32 | Container( 33 | child: Text( 34 | 'Actualizado: $updateTime', 35 | style: TextStyle( 36 | fontWeight: FontWeight.w100, 37 | fontSize: 15, 38 | color: Colors.white, 39 | ), 40 | ), 41 | ), 42 | BoxedIcon( 43 | TimeIcon.fromHour(weather.dateTime.hour), 44 | color: Colors.white, 45 | size: 20, 46 | ), 47 | ], 48 | ), 49 | WeatherSwipePager(weather: weather), 50 | Padding( 51 | child: Divider(color: Colors.white.withAlpha(50)), 52 | padding: EdgeInsets.all(10), 53 | ), 54 | Center( 55 | child: Container( 56 | margin: EdgeInsets.all(20), 57 | child: Text( 58 | 'Pronóstico para los próximos Días:', 59 | style: TextStyle( 60 | fontSize: 16, 61 | fontWeight: FontWeight.bold, 62 | color: Colors.white, 63 | ), 64 | ), 65 | ), 66 | ), 67 | Center( 68 | child: ForecastHorizontal(weathers: weather.getForecasts()), 69 | ), 70 | Padding( 71 | child: Divider( 72 | color: Colors.white.withAlpha(50), 73 | ), 74 | padding: EdgeInsets.all(10), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/pages/radar/radar_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuba_weather/src/utils/utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class RadarPage extends StatefulWidget { 5 | final String placeHolderImage; 6 | final String radarName; 7 | final String imgUrl; 8 | 9 | RadarPage({ 10 | Key key, 11 | @required this.placeHolderImage, 12 | @required this.radarName, 13 | @required this.imgUrl, 14 | }) : assert(placeHolderImage != null), 15 | assert(imgUrl != null), 16 | super(key: key); 17 | 18 | @override 19 | _RadarPageState createState() => _RadarPageState(); 20 | } 21 | 22 | class _RadarPageState extends State with TickerProviderStateMixin { 23 | AnimationController fadeController; 24 | Animation fadeAnimation; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | 30 | fadeController = AnimationController( 31 | duration: const Duration(milliseconds: 1000), 32 | vsync: this, 33 | ); 34 | fadeAnimation = CurvedAnimation( 35 | parent: fadeController, 36 | curve: Curves.easeIn, 37 | ); 38 | } 39 | 40 | @override 41 | void dispose() { 42 | super.dispose(); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return Scaffold( 48 | backgroundColor: Theme.of(context).backgroundColor, 49 | appBar: AppBar( 50 | elevation: 0, 51 | title: Text(widget.radarName), 52 | centerTitle: true, 53 | ), 54 | body: Column( 55 | children: [ 56 | Column( 57 | mainAxisAlignment: MainAxisAlignment.spaceAround, 58 | children: [ 59 | SizedBox( 60 | height: 5.0, 61 | ), 62 | CircleAvatar( 63 | backgroundImage: 64 | AssetImage('images/radar/${widget.placeHolderImage}'), 65 | radius: 70.0, 66 | ), 67 | SizedBox( 68 | height: 20, 69 | ) 70 | ], 71 | ), 72 | Image.network(widget.imgUrl), 73 | Padding( 74 | padding: const EdgeInsets.all(10.0), 75 | child: Center( 76 | child: Text( 77 | 'Fuente: ' + Constants.insmetForecastSource, 78 | textAlign: TextAlign.center, 79 | style: TextStyle( 80 | fontWeight: FontWeight.w300, 81 | fontSize: 14, 82 | color: Colors.white, 83 | ), 84 | ), 85 | ), 86 | ), 87 | ], 88 | )); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /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 plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | def keystoreProperties = new Properties() 29 | def keystorePropertiesFile = rootProject.file('key.properties') 30 | if (keystorePropertiesFile.exists()) { 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | } 33 | 34 | android { 35 | compileSdkVersion 28 36 | 37 | sourceSets { 38 | main.java.srcDirs += 'src/main/kotlin' 39 | } 40 | 41 | lintOptions { 42 | disable 'InvalidPackage' 43 | } 44 | 45 | defaultConfig { 46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 47 | applicationId "com.codestrange.www.cuba_weather" 48 | minSdkVersion 16 49 | targetSdkVersion 28 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 53 | } 54 | 55 | signingConfigs { 56 | release { 57 | keyAlias keystoreProperties['keyAlias'] 58 | keyPassword keystoreProperties['keyPassword'] 59 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 60 | storePassword keystoreProperties['storePassword'] 61 | } 62 | } 63 | buildTypes { 64 | release { 65 | signingConfig signingConfigs.release 66 | } 67 | } 68 | } 69 | 70 | flutter { 71 | source '../..' 72 | } 73 | 74 | dependencies { 75 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 76 | testImplementation 'junit:junit:4.12' 77 | androidTestImplementation 'androidx.test:runner:1.1.1' 78 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 79 | } 80 | -------------------------------------------------------------------------------- /lib/src/pages/carousel/widgets/carousel_item_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:getflutter/getflutter.dart'; 3 | import 'package:preferences/preference_service.dart'; 4 | 5 | import 'package:cuba_weather/src/pages/pages.dart'; 6 | import 'package:cuba_weather/src/utils/constants.dart'; 7 | import 'package:cuba_weather/src/widgets/widgets.dart'; 8 | 9 | class CarouselItemWidget extends StatelessWidget { 10 | final String image; 11 | final String text; 12 | final String title; 13 | final bool isLast; 14 | 15 | CarouselItemWidget({ 16 | @required this.image, 17 | @required this.title, 18 | @required this.text, 19 | this.isLast = false, 20 | }); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | var size = ResponsiveScreen(MediaQuery.of(context).size); 25 | var children = [ 26 | Center( 27 | child: Image.asset( 28 | image, 29 | width: size.getWidthPx(300), 30 | ), 31 | ), 32 | Padding( 33 | padding: EdgeInsets.all(size.getWidthPx(10)), 34 | child: Center( 35 | child: Text( 36 | title, 37 | style: TextStyle( 38 | fontFamily: Constants.fontFamily, 39 | fontWeight: FontWeight.bold, 40 | fontSize: 22, 41 | color: Colors.white, 42 | ), 43 | textAlign: TextAlign.center, 44 | ), 45 | ), 46 | ), 47 | Padding( 48 | padding: EdgeInsets.only( 49 | bottom: size.getWidthPx(80), 50 | left: size.getWidthPx(30), 51 | right: size.getWidthPx(30), 52 | ), 53 | child: Text( 54 | text, 55 | style: TextStyle( 56 | fontFamily: Constants.fontFamily, 57 | fontWeight: FontWeight.normal, 58 | fontSize: 16, 59 | color: Colors.white, 60 | ), 61 | textAlign: TextAlign.center, 62 | ), 63 | ), 64 | ]; 65 | 66 | if (isLast) { 67 | children.add( 68 | Padding( 69 | padding: EdgeInsets.symmetric(horizontal: 50), 70 | child: GFButton( 71 | text: 'Comenzar', 72 | textColor: Colors.white, 73 | color: Colors.white, 74 | size: GFSize.LARGE, 75 | shape: GFButtonShape.pills, 76 | type: GFButtonType.outline2x, 77 | fullWidthButton: true, 78 | onPressed: () { 79 | PrefService.setBool(Constants.carouselWasSeen, true); 80 | Navigator.pushReplacement( 81 | context, 82 | MaterialPageRoute(builder: (context) => HomePage()), 83 | ); 84 | }), 85 | ), 86 | ); 87 | } 88 | 89 | return SafeArea( 90 | child: Column( 91 | mainAxisAlignment: MainAxisAlignment.center, 92 | children: children, 93 | ), 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/src/pages/carousel/carousel_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:cuba_weather/src/pages/pages.dart'; 4 | import 'package:cuba_weather/src/pages/carousel/widgets/widgets.dart'; 5 | import 'package:cuba_weather/src/widgets/widgets.dart'; 6 | 7 | class CarouselPage extends StatefulWidget { 8 | @override 9 | CarouselPageState createState() => CarouselPageState(); 10 | } 11 | 12 | class CarouselPageState extends State { 13 | final controller = PageController(); 14 | ResponsiveScreen size; 15 | List pages; 16 | 17 | CarouselPageState() { 18 | pages = [ 19 | CarouselItemWidget( 20 | image: 'images/image5.png', 21 | title: 'Cuba Weather', 22 | text: 'Es un proyecto pionero en Cuba de código abierto, ' 23 | 'multiplataforma y sin ánimo de lucro.', 24 | ), 25 | CarouselItemWidget( 26 | image: 'images/image3.png', 27 | title: 'Los objetivos son', 28 | text: 'Brindar a los residentes en Cuba una manera cómoda de acceder ' 29 | 'a información meteorológica utilizando solo navegación nacional.', 30 | ), 31 | CarouselItemWidget( 32 | image: 'images/image1.png', 33 | title: 'Mostramos información', 34 | text: 'De las principales variables meteorológicas como la temperatura' 35 | ', la presión atmosférica, la humedad, etc.', 36 | ), 37 | CarouselItemWidget( 38 | image: 'images/image4.png', 39 | title: 'Pronósticos oficiales', 40 | text: 'Para los próximos días, perpéctivas del tiempo, estado de los ' 41 | 'mares, etc obtenidos del Instituto de Meteorología de Cuba.', 42 | isLast: true, 43 | ), 44 | ]; 45 | } 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | size = ResponsiveScreen(MediaQuery.of(context).size); 50 | return Scaffold( 51 | backgroundColor: Theme.of(context).backgroundColor, 52 | body: SafeArea( 53 | child: Stack( 54 | children: [ 55 | pageViewFillWidget(), 56 | bottomDotsWidget(), 57 | ], 58 | ), 59 | )); 60 | } 61 | 62 | Positioned bottomDotsWidget() { 63 | return Positioned( 64 | bottom: size.getWidthPx(20), 65 | left: 0, 66 | right: 0, 67 | child: DotsIndicatorWidget( 68 | controller: controller, 69 | itemCount: pages.length, 70 | color: Colors.white, 71 | onPageSelected: (int page) { 72 | controller.animateToPage( 73 | page, 74 | duration: const Duration(milliseconds: 300), 75 | curve: Curves.ease, 76 | ); 77 | }, 78 | ), 79 | ); 80 | } 81 | 82 | Positioned pageViewFillWidget() { 83 | return Positioned.fill( 84 | child: PageView.builder( 85 | controller: controller, 86 | itemCount: pages.length, 87 | itemBuilder: (BuildContext context, int index) { 88 | return pages[index % pages.length]; 89 | }, 90 | ), 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/weather_empty_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:getflutter/getflutter.dart'; 5 | 6 | import 'package:cuba_weather/src/pages/home/blocs/blocs.dart'; 7 | import 'package:cuba_weather/src/utils/utils.dart'; 8 | 9 | class WeatherEmptyWidget extends StatelessWidget { 10 | @override 11 | Widget build(BuildContext context) { 12 | return Column( 13 | mainAxisAlignment: MainAxisAlignment.center, 14 | children: [ 15 | Container( 16 | margin: EdgeInsets.only( 17 | left: 20, 18 | right: 20, 19 | top: 50, 20 | bottom: 50, 21 | ), 22 | child: Text( 23 | '¡Bienvenido a ${Constants.appName}!', 24 | textAlign: TextAlign.center, 25 | style: TextStyle( 26 | color: Colors.white, 27 | fontWeight: FontWeight.bold, 28 | fontSize: 22, 29 | ), 30 | ), 31 | ), 32 | GestureDetector( 33 | child: Container( 34 | margin: EdgeInsets.symmetric( 35 | horizontal: 20, 36 | vertical: 10, 37 | ), 38 | child: Icon( 39 | Icons.my_location, 40 | color: Colors.white, 41 | size: 100, 42 | ), 43 | ), 44 | onTap: () { 45 | BlocProvider.of(context).add( 46 | FindLocationWeather(), 47 | ); 48 | }, 49 | ), 50 | Container( 51 | margin: EdgeInsets.symmetric( 52 | horizontal: 20, 53 | vertical: 10, 54 | ), 55 | child: GFButton( 56 | text: 'BUSCAR MUNICIPIO ACTUAL', 57 | textColor: Colors.white, 58 | color: Colors.white, 59 | size: GFSize.LARGE, 60 | shape: GFButtonShape.pills, 61 | type: GFButtonType.outline2x, 62 | fullWidthButton: true, 63 | onPressed: () { 64 | BlocProvider.of(context).add( 65 | FindLocationWeather(), 66 | ); 67 | }, 68 | ), 69 | ), 70 | Container( 71 | margin: EdgeInsets.symmetric( 72 | horizontal: 20, 73 | vertical: 50, 74 | ), 75 | child: Container( 76 | padding: EdgeInsets.all(10), 77 | child: Text( 78 | 'Nota: Este cálculo se hace con respecto ' 79 | 'al centro del municipio, por lo que si se ' 80 | 'encuentra en la periferia de un municipio ' 81 | 'puede que la aplicación le indique que se ' 82 | 'encuentra en un municipio aledaño al que se ' 83 | 'encuentra realmente.', 84 | textAlign: TextAlign.left, 85 | style: TextStyle( 86 | color: Colors.white, 87 | fontWeight: FontWeight.w600, 88 | fontSize: 15, 89 | ), 90 | ), 91 | ), 92 | ), 93 | ], 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at leynier41@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/src/pages/satellite_list/satellite_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:getflutter/getflutter.dart'; 4 | 5 | import '../satellite/satellite_page.dart'; 6 | import 'models/models.dart'; 7 | 8 | class SatelliteListPage extends StatefulWidget { 9 | @override 10 | State createState() => SatelliteListPageState(); 11 | } 12 | 13 | class SatelliteListPageState extends State { 14 | List sources; 15 | 16 | @override 17 | void initState() { 18 | sources = getSatelliteImgSources(); 19 | super.initState(); 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Scaffold( 25 | backgroundColor: Theme.of(context).backgroundColor, 26 | appBar: AppBar( 27 | elevation: 0, 28 | title: Text('Imágenes de satélite'), 29 | centerTitle: true, 30 | ), 31 | body: Container( 32 | child: ListView.builder( 33 | itemCount: sources.length, 34 | itemBuilder: (context, index) { 35 | return InkWell( 36 | onTap: () { 37 | Navigator.push( 38 | context, 39 | MaterialPageRoute( 40 | builder: (context) => SatellitePage( 41 | pageTitle: sources[index].source, 42 | satelliteType: sources[index].image), 43 | ), 44 | ); 45 | }, 46 | child: GFListTile( 47 | avatar: Container( 48 | height: 100.0, 49 | width: 130.0, 50 | decoration: BoxDecoration( 51 | borderRadius: BorderRadius.only( 52 | bottomLeft: Radius.circular(5), 53 | topLeft: Radius.circular(5), 54 | bottomRight: Radius.circular(5), 55 | topRight: Radius.circular(5), 56 | ), 57 | image: DecorationImage( 58 | fit: BoxFit.cover, 59 | image: AssetImage( 60 | 'images/satellite/${sources[index].image}'))), 61 | ), 62 | titleText: sources[index].source, 63 | subtitleText: sources[index].description, 64 | icon: Icon(Icons.arrow_right), 65 | color: Colors.white, 66 | margin: EdgeInsets.all(5), 67 | )); 68 | }, 69 | ))); 70 | } 71 | 72 | List getSatelliteImgSources() { 73 | return [ 74 | SatelliteList( 75 | source: 'University of Wisconsin-Madison', 76 | sourceUrl: 'http://www.ssec.wisc.edu/', 77 | image: 'latest_eastir.jpg', 78 | description: 'Atlántico - Canal infrarrojo'), 79 | SatelliteList( 80 | source: 'University of Wisconsin-Madison', 81 | sourceUrl: 'http://www.ssec.wisc.edu/', 82 | image: 'latest_eastvis.jpg', 83 | description: 'Mosaico - Canal visible'), 84 | SatelliteList( 85 | source: 'University of Wisconsin-Madison', 86 | sourceUrl: 'http://www.ssec.wisc.edu/', 87 | image: 'goes.gsfc.nasa.gov.jpg', 88 | description: 'Cono - Canal visible'), 89 | SatelliteList( 90 | source: 'George C. Marshall Space Flight Center', 91 | sourceUrl: 'https://weather.msfc.nasa.gov/GOES/', 92 | image: 'cuba-g16.jpg', 93 | description: 'Caribe - Canal visible'), 94 | SatelliteList( 95 | source: 'Weather Underground - Intellicast', 96 | sourceUrl: 'http://images.intellicast.com', 97 | image: 'www.intellicast.com.jpg', 98 | description: 'Cuba - Canal visible'), 99 | ]; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_selection/municipality_selection_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:autocomplete_textfield/autocomplete_textfield.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 5 | 6 | import 'package:cuba_weather/src/pages/pages.dart'; 7 | import 'package:cuba_weather/src/utils/utils.dart'; 8 | 9 | class MunicipalitySelectionPage extends StatefulWidget { 10 | @override 11 | State createState() => 12 | MunicipalitySelectionPageState(); 13 | } 14 | 15 | class MunicipalitySelectionPageState extends State { 16 | final GlobalKey> key = new GlobalKey(); 17 | final textController = TextEditingController(); 18 | final focusNode = FocusNode(); 19 | 20 | @override 21 | void dispose() { 22 | focusNode.dispose(); 23 | super.dispose(); 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | FocusScope.of(context).autofocus(focusNode); 29 | return Scaffold( 30 | backgroundColor: Theme.of(context).backgroundColor, 31 | appBar: AppBar( 32 | backgroundColor: Theme.of(context).backgroundColor, 33 | elevation: 0, 34 | title: Text('Seleccionar municipio'), 35 | centerTitle: true, 36 | actions: [ 37 | IconButton( 38 | icon: Icon(Icons.format_list_bulleted), 39 | onPressed: () async { 40 | final municipality = await Navigator.push( 41 | context, 42 | MaterialPageRoute( 43 | builder: (context) => MunicipalityListPage(), 44 | ), 45 | ); 46 | if (municipality != null) { 47 | setState(() { 48 | Navigator.pop(context, municipality); 49 | }); 50 | } 51 | }, 52 | ), 53 | IconButton( 54 | icon: Icon(Icons.history), 55 | onPressed: () async { 56 | final municipality = await Navigator.push( 57 | context, 58 | MaterialPageRoute( 59 | builder: (context) => MunicipalityRecordPage(), 60 | ), 61 | ); 62 | if (municipality != null) { 63 | setState(() { 64 | Navigator.pop(context, municipality); 65 | }); 66 | } 67 | }, 68 | ), 69 | ], 70 | ), 71 | body: Column( 72 | children: [ 73 | Row( 74 | children: [ 75 | Expanded( 76 | child: Card( 77 | color: Colors.white, 78 | child: Padding( 79 | padding: EdgeInsets.symmetric(vertical: 5, horizontal: 20), 80 | child: SimpleAutoCompleteTextField( 81 | key: key, 82 | controller: textController, 83 | suggestions: municipalities.map((m) => m.name).toList(), 84 | focusNode: focusNode, 85 | decoration: InputDecoration( 86 | border: InputBorder.none, 87 | hintText: 'Inserte aquí el municipio deseado', 88 | ), 89 | style: TextStyle( 90 | color: Theme.of(context).primaryColor, 91 | fontWeight: FontWeight.bold, 92 | ), 93 | clearOnSubmit: false, 94 | textSubmitted: (text) => setState(() { 95 | updateRecords(text); 96 | Navigator.pop(context, text); 97 | }), 98 | ), 99 | ), 100 | ), 101 | ), 102 | ], 103 | ), 104 | ], 105 | ), 106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/src/pages/splash/splash_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:cuba_weather/src/pages/whats_new/show_whats_new.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:getflutter/getflutter.dart'; 6 | import 'package:package_info/package_info.dart'; 7 | import 'package:preferences/preferences.dart'; 8 | 9 | import 'package:cuba_weather/src/pages/pages.dart'; 10 | import 'package:cuba_weather/src/utils/utils.dart'; 11 | import 'package:cuba_weather/src/widgets/widgets.dart'; 12 | 13 | class SplashScreen extends StatefulWidget { 14 | @override 15 | SplashScreenState createState() => SplashScreenState(); 16 | } 17 | 18 | class SplashScreenState extends State { 19 | ResponsiveScreen size; 20 | String version = ''; 21 | final double textScaleFactor = 1.0; 22 | 23 | @override 24 | initState() { 25 | super.initState(); 26 | start(); 27 | Timer(Duration(seconds: 2), () { 28 | navigateFromSplash(); 29 | }); 30 | } 31 | 32 | void start() async { 33 | var packageInfo = await PackageInfo.fromPlatform(); 34 | setState(() { 35 | version = packageInfo.version; 36 | }); 37 | } 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | size = ResponsiveScreen(MediaQuery.of(context).size); 42 | return Scaffold( 43 | backgroundColor: Theme.of(context).backgroundColor, 44 | body: Center( 45 | child: Column( 46 | mainAxisAlignment: MainAxisAlignment.center, 47 | children: [ 48 | Container( 49 | width: size.getWidthPx(200), 50 | height: size.getWidthPx(200), 51 | child: Image.asset(Constants.appLogo), 52 | ), 53 | Text( 54 | Constants.appName, 55 | style: TextStyle( 56 | fontFamily: Constants.fontFamily, 57 | fontWeight: FontWeight.w500, 58 | fontSize: 26, 59 | color: Colors.white, 60 | ), 61 | textAlign: TextAlign.center, 62 | ), 63 | Text( 64 | Constants.appSlogan.toUpperCase(), 65 | style: TextStyle( 66 | fontFamily: Constants.fontFamily, 67 | fontWeight: FontWeight.w300, 68 | fontSize: 15, 69 | color: Colors.white, 70 | ), 71 | textAlign: TextAlign.center, 72 | ), 73 | Padding(padding: EdgeInsets.all(10)), 74 | Padding( 75 | padding: const EdgeInsets.symmetric(horizontal: 15.0), 76 | child: GFButton( 77 | text: 'Comenzar', 78 | textColor: Colors.white, 79 | color: Colors.white, 80 | size: GFSize.LARGE, 81 | shape: GFButtonShape.pills, 82 | type: GFButtonType.outline2x, 83 | fullWidthButton: true, 84 | onPressed: () { 85 | Navigator.pushReplacement( 86 | context, 87 | MaterialPageRoute(builder: (context) => HomePage()), 88 | ); 89 | }), 90 | ) 91 | ], 92 | ), 93 | ), 94 | ); 95 | } 96 | 97 | Future navigateFromSplash() async { 98 | var isOnBoard = PrefService.getBool(Constants.carouselWasSeen) ?? false; 99 | var lastVersion = int.parse((PrefService.getString(Constants.lastVersion) ?? "-1").replaceAll('.', '')); 100 | int versionNumber = int.parse(version.replaceAll('.', '')); 101 | var showWhatsNew1 = versionNumber > lastVersion; 102 | 103 | var page = isOnBoard 104 | ? showWhatsNew1 105 | ? WhatsNew().showWhatsNewPage(context, version) 106 | : HomePage() 107 | : CarouselPage(); 108 | Navigator.push( 109 | context, 110 | MaterialPageRoute( 111 | builder: (context) => page, fullscreenDialog: showWhatsNew1), 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/forecast_horizontal_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:intl/intl.dart'; 3 | import 'package:weather_icons/weather_icons.dart'; 4 | 5 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 6 | 7 | import 'package:cuba_weather/src/pages/home/widgets/widgets.dart'; 8 | 9 | class ForecastHorizontal extends StatelessWidget { 10 | const ForecastHorizontal({ 11 | Key key, 12 | @required this.weathers, 13 | }) : super(key: key); 14 | 15 | final List weathers; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Container( 20 | height: 100, 21 | child: ListView.separated( 22 | scrollDirection: Axis.horizontal, 23 | shrinkWrap: true, 24 | itemCount: this.weathers.length, 25 | separatorBuilder: (context, index) => Divider( 26 | height: 100, 27 | color: Colors.white, 28 | ), 29 | padding: EdgeInsets.only(left: 10, right: 10), 30 | itemBuilder: (context, index) { 31 | final item = this.weathers[index]; 32 | var weatherIconCode = _weatherIconCodeByState( 33 | item.state, 34 | ); 35 | var now = DateTime.now(); 36 | var lastCurrentDateOfMonth = DateTime(now.year, now.month + 1, 0); 37 | var current1day = new DateTime(now.year, now.month, 1); 38 | var next1day = current1day.add( 39 | Duration(days: lastCurrentDateOfMonth.day), 40 | ); 41 | var currentForecastDate = DateTime(now.year, now.month, item.day); 42 | if (item.day < now.day) { 43 | currentForecastDate = DateTime( 44 | next1day.year, 45 | next1day.month, 46 | item.day, 47 | ); 48 | } 49 | var dateString = _spanishWeekDayString( 50 | DateFormat('EEEE').format(currentForecastDate), 51 | ); 52 | return Padding( 53 | padding: const EdgeInsets.only(left: 10, right: 10), 54 | child: Center( 55 | child: ValueTileWidget( 56 | '$dateString\n${item.day}', 57 | '${item.temperatureMax.round()}°/${item.temperatureMin.round()}°', 58 | iconData: WeatherIcons.fromString( 59 | weatherIconCode, 60 | fallback: WeatherIcons.na, 61 | ), 62 | ), 63 | ), 64 | ); 65 | }, 66 | ), 67 | ); 68 | } 69 | 70 | String _weatherIconCodeByState(InsmetState state) { 71 | String result = ''; 72 | switch (state) { 73 | case InsmetState.OccasionalShowers: 74 | result = 'wi-day-rain'; 75 | break; 76 | case InsmetState.ScatteredShowers: 77 | result = 'wi-rain'; 78 | break; 79 | case InsmetState.IsolatedShowers: 80 | result = 'wi-day-rain'; 81 | break; 82 | case InsmetState.AfternoonShowers: 83 | result = 'wi-showers'; 84 | break; 85 | case InsmetState.RainShowers: 86 | result = 'wi-showers'; 87 | break; 88 | case InsmetState.PartlyCloudy: 89 | result = 'wi-day-cloudy'; 90 | break; 91 | case InsmetState.Cloudy: 92 | result = 'wi-cloudy'; 93 | break; 94 | case InsmetState.Sunny: 95 | result = 'wi-day-sunny'; 96 | break; 97 | case InsmetState.Storms: 98 | result = 'wi-day-thunderstorm'; 99 | break; 100 | case InsmetState.AfternoonStorms: 101 | result = 'wi-thunderstorm'; 102 | break; 103 | case InsmetState.MorningScatteredShowers: 104 | result = 'wi-rain'; 105 | break; 106 | case InsmetState.Winds: 107 | result = 'wi-strong-wind'; 108 | break; 109 | default: 110 | result = 'wi-na'; 111 | } 112 | return result; 113 | } 114 | 115 | String _spanishWeekDayString(String weekDay) { 116 | String result = ''; 117 | weekDay = weekDay.toLowerCase(); 118 | switch (weekDay) { 119 | case 'sunday': 120 | result = 'Domingo'; 121 | break; 122 | case 'monday': 123 | result = 'Lunes'; 124 | break; 125 | case 'tuesday': 126 | result = 'Martes'; 127 | break; 128 | case 'wednesday': 129 | result = 'Miércoles'; 130 | break; 131 | case 'thursday': 132 | result = 'Jueves'; 133 | break; 134 | case 'friday': 135 | result = 'Viernes'; 136 | break; 137 | case 'saturday': 138 | result = 'Sábado'; 139 | break; 140 | default: 141 | result = ''; 142 | } 143 | return result; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /lib/src/pages/whats_new/show_whats_new.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuba_weather/src/utils/constants.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_whatsnew/flutter_whatsnew.dart'; 4 | import 'package:preferences/preference_service.dart'; 5 | 6 | import '../pages.dart'; 7 | 8 | class WhatsNew { 9 | final double textScaleFactor = 1.0; 10 | 11 | Widget showWhatsNewPage(BuildContext context, String version) { 12 | return WhatsNewPage( 13 | title: Text( 14 | "Actualizaciones", 15 | textScaleFactor: textScaleFactor, 16 | textAlign: TextAlign.center, 17 | style: const TextStyle( 18 | fontSize: 22.0, fontWeight: FontWeight.bold, color: Colors.white), 19 | ), 20 | buttonText: Text( 21 | 'Continuar', 22 | textScaleFactor: textScaleFactor, 23 | style: TextStyle(color: Theme.of(context).backgroundColor), 24 | ), 25 | buttonColor: Colors.white, 26 | backgroundColor: Theme.of(context).backgroundColor, 27 | items: [ 28 | ListTile( 29 | leading: const Icon(Icons.color_lens, color: Colors.white), 30 | title: Text( 31 | 'Temas', 32 | textScaleFactor: textScaleFactor, 33 | style: TextStyle(color: Colors.white), 34 | ), 35 | subtitle: Text( 36 | 'Claro, oscuro y sistema (Toque para cambiar)', 37 | textScaleFactor: textScaleFactor, 38 | style: TextStyle(color: Colors.white), 39 | ), 40 | onTap: () { 41 | Navigator.push( 42 | context, 43 | MaterialPageRoute( 44 | builder: (context) => SettingsPage(), 45 | ), 46 | ); 47 | }, 48 | trailing: Icon(Icons.arrow_right), 49 | ), 50 | ListTile( 51 | leading: const Icon(Icons.satellite, color: Colors.white), 52 | title: Text( 53 | 'Satélites', 54 | textScaleFactor: textScaleFactor, 55 | style: TextStyle(color: Colors.white), 56 | ), 57 | subtitle: Text( 58 | 'Imágenes de satelites (Toque para ver)', 59 | textScaleFactor: textScaleFactor, 60 | style: TextStyle(color: Colors.white), 61 | ), 62 | onTap: () { 63 | Navigator.push( 64 | context, 65 | MaterialPageRoute( 66 | builder: (context) => SatelliteListPage(), 67 | ), 68 | ); 69 | }, 70 | ), 71 | ListTile( 72 | leading: 73 | const Icon(Icons.settings_input_antenna, color: Colors.white), 74 | title: Text( 75 | 'Radares', 76 | textScaleFactor: textScaleFactor, 77 | style: TextStyle(color: Colors.white), 78 | ), 79 | subtitle: Text( 80 | 'Imágenes de radar (Toque para ver)', 81 | textScaleFactor: textScaleFactor, 82 | style: TextStyle(color: Colors.white), 83 | ), 84 | onTap: () { 85 | Navigator.push( 86 | context, 87 | MaterialPageRoute( 88 | builder: (context) => RadarListPage(), 89 | ), 90 | ); 91 | }, 92 | ), 93 | ListTile( 94 | title: Text( 95 | '', 96 | textScaleFactor: textScaleFactor, 97 | style: TextStyle(color: Colors.white), 98 | ), 99 | ), 100 | ListTile( 101 | leading: const Icon(Icons.history, color: Colors.white), 102 | title: Text( 103 | 'Ver historial de cambios', 104 | textScaleFactor: textScaleFactor, 105 | style: TextStyle(color: Colors.white), 106 | ), 107 | subtitle: Text( 108 | '(Toque para ver)', 109 | textScaleFactor: textScaleFactor, 110 | style: TextStyle(color: Colors.white), 111 | ), 112 | onTap: () { 113 | Navigator.push( 114 | context, 115 | MaterialPageRoute( 116 | builder: (context) => Scaffold( 117 | body: WhatsNewPage.changelog( 118 | title: Text( 119 | "Historial de cambios", 120 | textScaleFactor: textScaleFactor, 121 | textAlign: TextAlign.center, 122 | style: const TextStyle( 123 | fontSize: 22.0, 124 | fontWeight: FontWeight.bold, 125 | color: Colors.white), 126 | ), 127 | buttonText: Text( 128 | 'Cerrar', 129 | textScaleFactor: textScaleFactor, 130 | style: 131 | TextStyle(color: Theme.of(context).backgroundColor), 132 | ), 133 | buttonColor: Colors.white, 134 | backgroundColor: Theme.of(context).backgroundColor, 135 | ), 136 | ), 137 | fullscreenDialog: true, 138 | ), 139 | ); 140 | }, 141 | ), 142 | ], 143 | onButtonPressed: () { 144 | PrefService.setString(Constants.lastVersion, version); 145 | Navigator.pushReplacement( 146 | context, 147 | MaterialPageRoute( 148 | builder: (context) => HomePage(), 149 | ), 150 | ); 151 | }, 152 | ); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /lib/src/pages/donors/widgets/donors_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 5 | import 'package:url_launcher/url_launcher.dart'; 6 | 7 | import 'package:cuba_weather/src/pages/donors/models/models.dart'; 8 | 9 | class DonorWidget extends StatelessWidget { 10 | const DonorWidget(); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return ListPage(); 15 | } 16 | } 17 | 18 | class ListPage extends StatefulWidget { 19 | ListPage({Key key}) : super(key: key); 20 | 21 | @override 22 | _ListPageState createState() => _ListPageState(); 23 | } 24 | 25 | class _ListPageState extends State { 26 | List donors; 27 | 28 | @override 29 | void initState() { 30 | donors = getDonors(); 31 | super.initState(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | var color = Theme.of(context).accentColor; 37 | Widget makeTrailing(Donor donor) { 38 | return donor.url != null 39 | ? Icon(getSocialIcon(donor.url), color: color) 40 | : Container(); 41 | } 42 | 43 | ListTile makeListTile(Donor donor) => ListTile( 44 | contentPadding: EdgeInsets.symmetric(horizontal: 20.0), 45 | title: Text( 46 | donor.name, 47 | style: TextStyle( 48 | color: color, 49 | fontWeight: FontWeight.bold, 50 | fontSize: 20, 51 | ), 52 | ), 53 | subtitle: Text( 54 | donor.value, 55 | style: TextStyle( 56 | color: color, 57 | fontWeight: FontWeight.normal, 58 | fontSize: 15, 59 | ), 60 | ), 61 | trailing: makeTrailing(donor), 62 | onTap: () async { 63 | if (await canLaunch(donor.url)) { 64 | await launch(donor.url); 65 | } else { 66 | log('Could not launch ${donor.url}'); 67 | } 68 | }, 69 | ); 70 | 71 | Widget makeCard(Donor donor) => Container( 72 | margin: EdgeInsets.symmetric( 73 | horizontal: 10.0, 74 | vertical: 6.0, 75 | ), 76 | child: Container( 77 | child: makeListTile(donor), 78 | ), 79 | ); 80 | 81 | final makeBody = Column( 82 | crossAxisAlignment: CrossAxisAlignment.start, 83 | children: [ 84 | Padding(padding: EdgeInsets.only(bottom: 10)), 85 | Column( 86 | children: donors.map((item) { 87 | return makeCard(item); 88 | }).toList(), 89 | ), 90 | Padding(padding: EdgeInsets.only(bottom: 10)), 91 | ], 92 | ); 93 | 94 | return makeBody; 95 | } 96 | } 97 | 98 | List getDonors() { 99 | return [ 100 | Donor( 101 | name: "BacheCubano", 102 | value: '250.00 CUP', 103 | url: 'https://www.bachecubano.com', 104 | ), 105 | Donor( 106 | name: "DatosCuba", 107 | value: '10.00 CUC (Saldo)', 108 | url: 'https://play.google.com/store/apps/details?id=com.cjamcu.datoscuba', 109 | ), 110 | Donor( 111 | name: "Publicitaria", 112 | value: '100.00 CUP', 113 | url: 'https://twitter.com/cmolinaf96', 114 | ), 115 | Donor( 116 | name: "Daxslab", 117 | value: '20.00 CUC', 118 | url: 'https://www.daxslab.com', 119 | ), 120 | Donor( 121 | name: "Tecnolike+", 122 | value: '125.00 CUP', 123 | url: 'https://tecnolikecuba.com', 124 | ), 125 | Donor( 126 | name: "Anónimo", 127 | value: '25.00 CUP', 128 | url: '', 129 | ), 130 | Donor( 131 | name: "Proyecto Numerazo", 132 | value: '2.00 CUC (Saldo)', 133 | url: 'https://t.me/Akyra0212', 134 | ), 135 | Donor( 136 | name: "Móvil JA Cuba", 137 | value: '125.00 CUP', 138 | url: 'https://moviljacuba.wordpress.com', 139 | ), 140 | Donor( 141 | name: "St. Pauli Bar", 142 | value: '960.00 CUP', 143 | url: 'https://www.facebook.com/Stpaulirestaurantstgo', 144 | ), 145 | Donor( 146 | name: "Tecnolike+", 147 | value: '125.00 CUP', 148 | url: 'https://tecnolikecuba.com', 149 | ), 150 | Donor( 151 | name: "Anónimo", 152 | value: '75.00 CUP', 153 | url: '', 154 | ), 155 | Donor( 156 | name: "Juventud Técnica", 157 | value: '125.00 CUP', 158 | url: 'https://medium.com/juventud-t%C3%A9cnica', 159 | ), 160 | ]; 161 | } 162 | 163 | IconData getSocialIcon(String url) { 164 | if (url == null || url.isEmpty) { 165 | return null; 166 | } 167 | if (url.contains('https://www.facebook.com')) { 168 | return FontAwesomeIcons.facebook; 169 | } else if (url.contains('https://twitter.com')) { 170 | return FontAwesomeIcons.twitter; 171 | } else if (url.contains('https://t.me')) { 172 | return FontAwesomeIcons.telegram; 173 | } else if (url.contains('https://github.com')) { 174 | return FontAwesomeIcons.github; 175 | } else if (url.contains('https://www.linkedin.com')) { 176 | return FontAwesomeIcons.linkedin; 177 | } else if (url.contains('https://www.youtube.com')) { 178 | return FontAwesomeIcons.youtube; 179 | } else if (url.contains('https://www.instagram.com')) { 180 | return FontAwesomeIcons.instagram; 181 | } else if (url.contains('https://medium.com')) { 182 | return FontAwesomeIcons.medium; 183 | } else if (url.contains('https://play.google.com')) { 184 | return FontAwesomeIcons.android; 185 | } else { 186 | return FontAwesomeIcons.chrome; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/src/pages/radar_list/radar_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../radar/radar_page.dart'; 5 | 6 | class RadarListPage extends StatefulWidget { 7 | @override 8 | _RadarListPageState createState() => _RadarListPageState(); 9 | } 10 | 11 | class _RadarListPageState extends State with TickerProviderStateMixin{ 12 | AnimationController fadeController; 13 | Animation fadeAnimation; 14 | 15 | @override 16 | void initState() { 17 | super.initState(); 18 | 19 | fadeController = AnimationController( 20 | duration: const Duration(milliseconds: 1000), 21 | vsync: this, 22 | ); 23 | fadeAnimation = CurvedAnimation( 24 | parent: fadeController, 25 | curve: Curves.easeIn, 26 | ); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | backgroundColor: Theme.of(context).backgroundColor, 33 | appBar: AppBar( 34 | elevation: 0, 35 | title: Text('Imágenes de radar'), 36 | centerTitle: true, 37 | ), 38 | body: Stack( 39 | children: [ 40 | SingleChildScrollView( 41 | child: Column( 42 | children: [_mosaico(context), _radarButtons(context)], 43 | ), 44 | ) 45 | ], 46 | ), 47 | ); 48 | } 49 | 50 | Widget _mosaico(BuildContext context) { 51 | return SafeArea( 52 | child: Table( 53 | children: [ 54 | TableRow(children: [ 55 | _createRadarButton(context, 'Mosaico', '4picoSanjuan.jpg', true, 56 | 'http://www.insmet.cu/Radar/NacComp200Km.gif'), 57 | ]) 58 | ], 59 | )); 60 | } 61 | 62 | Widget _radarButtons(BuildContext context) { 63 | return Table( 64 | children: [ 65 | TableRow(children: [ 66 | _createRadarButton(context, 'La Bajada', '1labajada.jpg', false, ''), 67 | _createRadarButton(context, 'Casablanca', '2CasaBlanca.jpg', true, 68 | 'http://www.insmet.cu/Radar/01Casablanca/csbMAXw01a.gif'), 69 | ]), 70 | TableRow(children: [ 71 | _createRadarButton( 72 | context, 'Punta del Este', '3Puntaeste.jpg', false, ''), 73 | _createRadarButton(context, 'Pico San Juan', '4picoSanjuan.jpg', true, 74 | 'http://www.insmet.cu/Radar/01Casablanca/csbMAXw01a.gif'), 75 | ]), 76 | TableRow(children: [ 77 | _createRadarButton(context, 'Camagüey', '5Camaguey.jpg', true, 78 | 'http://www.insmet.cu/Radar/01Casablanca/csbMAXw01a.gif'), 79 | _createRadarButton(context, 'Pilón', '6pilon.jpg', true, 80 | 'http://www.insmet.cu/Radar/01Casablanca/csbMAXw01a.gif'), 81 | ]), 82 | TableRow(children: [ 83 | _createRadarButton(context, 'Gran Piedra', '7granPiedra.jpg', true, 84 | 'http://www.insmet.cu/Radar/01Casablanca/csbMAXw01a.gif'), 85 | _createRadarButton(context, 'Holguín', '8granPiedra.jpg', false, ''), 86 | ]), 87 | ], 88 | ); 89 | } 90 | 91 | _createRadarButton(BuildContext context, String radarName, 92 | String placeHolderImage, bool active, String animatedGifUrl) { 93 | return InkWell( 94 | onTap: () { 95 | if (active) { 96 | Navigator.push( 97 | context, 98 | MaterialPageRoute( 99 | builder: (context) => RadarPage( 100 | radarName: radarName, 101 | placeHolderImage: placeHolderImage, 102 | imgUrl: animatedGifUrl, 103 | ), 104 | ), 105 | ); 106 | } 107 | }, 108 | child: Padding( 109 | padding: const EdgeInsets.all(10.0), 110 | child: ClipRRect( 111 | borderRadius: BorderRadius.circular(20.0), 112 | child: BackdropFilter( 113 | filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), 114 | child: Container( 115 | height: 180.0, 116 | //margin: EdgeInsets.all(15.0), 117 | decoration: BoxDecoration( 118 | color: Color.fromRGBO(12, 66, 107, 0.5), 119 | //borderRadius: BorderRadius.circular(20.0), 120 | ), 121 | child: Column( 122 | mainAxisAlignment: MainAxisAlignment.spaceAround, 123 | children: [ 124 | SizedBox( 125 | height: 5.0, 126 | ), 127 | CircleAvatar( 128 | backgroundImage: 129 | AssetImage('images/radar/$placeHolderImage'), 130 | radius: 70.0, 131 | ), 132 | Row( 133 | mainAxisAlignment: MainAxisAlignment.center, 134 | children: [ 135 | CircleAvatar( 136 | backgroundColor: 137 | active ? Colors.green : Colors.red[300], 138 | radius: 10.0, 139 | ), 140 | SizedBox( 141 | width: 5.0, 142 | ), 143 | Text( 144 | radarName, 145 | style: TextStyle(color: Colors.white), 146 | ), 147 | ], 148 | ), 149 | SizedBox( 150 | height: 5.0, 151 | ) 152 | ], 153 | ), 154 | ), 155 | ), 156 | ), 157 | ), 158 | ); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /lib/src/pages/home/blocs/weather_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/services.dart'; 5 | import 'package:geolocator/geolocator.dart'; 6 | import 'package:meta/meta.dart'; 7 | import 'package:bloc/bloc.dart'; 8 | import 'package:preferences/preference_service.dart'; 9 | import 'package:shared_preferences/shared_preferences.dart'; 10 | 11 | import 'package:cuba_weather_dart/cuba_weather_dart.dart'; 12 | 13 | import 'package:cuba_weather/src/pages/home/blocs/blocs.dart'; 14 | import 'package:cuba_weather/src/utils/constants.dart'; 15 | import 'package:cuba_weather/src/pages/home/models/models.dart' as models; 16 | 17 | class WeatherBloc extends Bloc { 18 | final CubaWeather api; 19 | 20 | WeatherBloc({@required this.api}) : assert(api != null); 21 | 22 | @override 23 | WeatherState get initialState { 24 | var municipality = PrefService.getString(Constants.municipality); 25 | if (municipality == null) return WeatherEmpty(); 26 | return WeatherInitial(municipality: municipality); 27 | } 28 | 29 | @override 30 | Stream mapEventToState(WeatherEvent event) async* { 31 | if (event is FetchWeather) { 32 | yield WeatherLoading(); 33 | PrefService.setString(Constants.municipality, event.municipality); 34 | try { 35 | final _weather = await api.get(event.municipality); 36 | var weather = models.WeatherModel.getModel(_weather); 37 | yield WeatherLoaded(weather: weather); 38 | } on SocketException catch (e) { 39 | log(e.toString()); 40 | yield WeatherError( 41 | errorMessage: 'No se ha podido establecer conexión con la red ' 42 | 'nacional. Por favor, revise su conexión y vuelva a intentarlo.', 43 | ); 44 | } catch (e) { 45 | log(e.toString()); 46 | yield WeatherError(errorMessage: e.toString()); 47 | } 48 | } 49 | if (event is RefreshWeather) { 50 | try { 51 | final _weather = await api.get(event.municipality); 52 | var weather = models.WeatherModel.getModel(_weather); 53 | yield WeatherLoaded(weather: weather); 54 | } on SocketException catch (e) { 55 | log(e.toString()); 56 | yield WeatherError( 57 | errorMessage: 'No se ha podido establecer conexión con la red ' 58 | 'nacional. Por favor, revise su conexión y vuelva a intentarlo.', 59 | ); 60 | } catch (e) { 61 | log(e.toString()); 62 | yield WeatherError(errorMessage: e.toString()); 63 | } 64 | } 65 | if (event is FindLocationWeather) { 66 | yield WeatherFindingLocation(); 67 | try { 68 | var locator = Geolocator(); 69 | var enabled = await locator.isLocationServiceEnabled(); 70 | if (!enabled) { 71 | yield WeatherError( 72 | errorMessage: 'El servicio de localización ' 73 | 'del dispositivo se encuentra desactivado.\n\nPor favor ' 74 | 'cierre la aplicación, active el servicio y vuelva a ' 75 | 'intentarlo.', 76 | ); 77 | return; 78 | } 79 | Position position; 80 | try { 81 | position = await locator 82 | .getCurrentPosition(desiredAccuracy: LocationAccuracy.high) 83 | .timeout(Duration(seconds: 30)); 84 | } on PlatformException catch (e) { 85 | if (e.code == 'PERMISSION_DENIED') { 86 | yield WeatherError( 87 | errorMessage: 'Ha denegado el permiso a la aplicación para ' 88 | 'obtener la localización del dispositivo.\n\n' 89 | 'Si desea que la aplicación obtenga la información ' 90 | 'del municipio en que se encuentra debe darle el permiso ' 91 | 'de Ubicación a la aplicación.', 92 | ); 93 | return; 94 | } else { 95 | yield WeatherError( 96 | errorMessage: 'Ha ocurrido un error durante la obtención de la ' 97 | 'localización. Puede ser porque apagó el servicio de ' 98 | 'localización, entró en una zona de mala cobertura, etc.\n\n' 99 | 'Por favor cierre la aplicación, active el servicio y ' 100 | 'vuelva a intentarlo.', 101 | ); 102 | return; 103 | } 104 | } catch (e) { 105 | yield WeatherError( 106 | errorMessage: 'Ha ocurrido un error durante la obtención de la ' 107 | 'localización. Puede ser porque apagó el servicio de ' 108 | 'localización, entró en una zona de mala cobertura, etc.\n\n' 109 | 'Por favor cierre la aplicación, active el servicio y ' 110 | 'vuelva a intentarlo.', 111 | ); 112 | return; 113 | } 114 | var bestMunicipality = municipalities[0]; 115 | var bestDistance = await locator.distanceBetween( 116 | position.latitude, 117 | position.longitude, 118 | bestMunicipality.lat, 119 | bestMunicipality.lon, 120 | ); 121 | for (var i = 1; i < municipalities.length; ++i) { 122 | var actualMunicipality = municipalities[i]; 123 | var actualDistance = await locator.distanceBetween( 124 | position.latitude, 125 | position.longitude, 126 | actualMunicipality.lat, 127 | actualMunicipality.lon, 128 | ); 129 | if (actualDistance < bestDistance) { 130 | bestDistance = actualDistance; 131 | bestMunicipality = actualMunicipality; 132 | } 133 | } 134 | try { 135 | var prefs = await SharedPreferences.getInstance(); 136 | await prefs.setString(Constants.municipality, bestMunicipality.name); 137 | } catch (e) { 138 | log(e.toString()); 139 | } 140 | final _weather = await api.get(bestMunicipality.name); 141 | var weather = models.WeatherModel.getModel(_weather); 142 | yield WeatherLoaded(weather: weather); 143 | } catch (e) { 144 | log(e.toString()); 145 | yield WeatherError(errorMessage: e.toString()); 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_list/municipality_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuba_weather/src/widgets/empty_widget.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'package:cuba_weather/src/pages/municipality_list/blocs/blocs.dart'; 6 | import 'package:cuba_weather/src/pages/municipality_record/municipality_record_page.dart'; 7 | import 'package:cuba_weather/src/pages/municipality_selection/municipality_selection_page.dart'; 8 | import 'package:cuba_weather/src/utils/utils.dart'; 9 | 10 | import 'package:flutter_bloc/flutter_bloc.dart'; 11 | 12 | class MunicipalityListPage extends StatefulWidget { 13 | @override 14 | State createState() => MunicipalityListPageState(); 15 | } 16 | 17 | class MunicipalityListPageState extends State { 18 | @override 19 | Widget build(BuildContext context) { 20 | return BlocProvider( 21 | create: (context) => MunicipalityListBloc(), 22 | child: Scaffold( 23 | backgroundColor: Theme.of(context).backgroundColor, 24 | appBar: AppBar( 25 | elevation: 0, 26 | title: Text('Municipios'), 27 | centerTitle: true, 28 | actions: [ 29 | IconButton( 30 | icon: Icon(Icons.search), 31 | onPressed: () async { 32 | final municipality = await Navigator.push( 33 | context, 34 | MaterialPageRoute( 35 | builder: (context) => MunicipalitySelectionPage(), 36 | ), 37 | ); 38 | if (municipality != null) { 39 | setState(() { 40 | Navigator.pop(context, municipality); 41 | }); 42 | } 43 | }, 44 | ), 45 | IconButton( 46 | icon: Icon(Icons.history), 47 | onPressed: () async { 48 | final municipality = await Navigator.push( 49 | context, 50 | MaterialPageRoute( 51 | builder: (context) => MunicipalityRecordPage(), 52 | ), 53 | ); 54 | if (municipality != null) { 55 | setState(() { 56 | Navigator.pop(context, municipality); 57 | }); 58 | } 59 | }, 60 | ), 61 | ], 62 | ), 63 | body: BlocBuilder( 64 | builder: (context, state) { 65 | if (state is MunicipalityListInitial) { 66 | BlocProvider.of(context) 67 | .add(FetchMunicipalityListEvent()); 68 | } 69 | if (state is MunicipalityListLoading) { 70 | return Center(child: CircularProgressIndicator()); 71 | } 72 | if (state is MunicipalityListError) { 73 | return ListView( 74 | children: [ 75 | Container( 76 | margin: EdgeInsets.all(10), 77 | child: Icon( 78 | Icons.error_outline, 79 | color: Colors.white, 80 | size: 150, 81 | ), 82 | ), 83 | Text( 84 | Constants.errorMessage, 85 | textAlign: TextAlign.center, 86 | style: TextStyle( 87 | color: Colors.white, 88 | fontWeight: FontWeight.bold, 89 | fontSize: 30, 90 | ), 91 | ), 92 | Container( 93 | margin: EdgeInsets.all(20), 94 | child: Container( 95 | padding: EdgeInsets.all(10), 96 | child: Text( 97 | state.errorMessage, 98 | textAlign: TextAlign.justify, 99 | style: TextStyle( 100 | color: Colors.white, 101 | fontWeight: FontWeight.w600, 102 | fontSize: 20, 103 | ), 104 | ), 105 | ), 106 | ), 107 | ], 108 | ); 109 | } 110 | if (state is MunicipalityListLoaded) { 111 | return ListView.separated( 112 | itemBuilder: (context, index) { 113 | var p = 10.0; 114 | return FlatButton( 115 | child: Container( 116 | padding: index == 0 117 | ? EdgeInsets.only( 118 | left: p, right: p, bottom: p, top: p * 2) 119 | : index == state.municipalities.length - 1 120 | ? EdgeInsets.only( 121 | left: p, right: p, bottom: p * 2, top: p) 122 | : EdgeInsets.all(p), 123 | child: Text( 124 | state.municipalities[index].name, 125 | style: TextStyle( 126 | color: Colors.white, 127 | fontWeight: FontWeight.bold, 128 | ), 129 | ), 130 | ), 131 | onPressed: () { 132 | setState(() { 133 | updateRecords(state.municipalities[index].name); 134 | Navigator.pop( 135 | context, state.municipalities[index].name); 136 | }); 137 | }, 138 | ); 139 | }, 140 | separatorBuilder: (context, index) { 141 | return Divider( 142 | color: Colors.white, 143 | ); 144 | }, 145 | itemCount: state.municipalities.length, 146 | ); 147 | } 148 | return EmptyWidget(); 149 | }, 150 | ), 151 | ), 152 | ); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /lib/src/pages/information/information_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/painting.dart'; 7 | import 'package:getflutter/components/button/gf_button.dart'; 8 | import 'package:getflutter/getflutter.dart'; 9 | import 'package:package_info/package_info.dart'; 10 | import 'package:url_launcher/url_launcher.dart'; 11 | 12 | import 'package:cuba_weather/src/utils/utils.dart'; 13 | 14 | class InformationPage extends StatefulWidget { 15 | const InformationPage(); 16 | 17 | @override 18 | State createState() => InformationPageState(); 19 | } 20 | 21 | class InformationPageState extends State { 22 | String appName = ''; 23 | String version = ''; 24 | 25 | InformationPageState() { 26 | start(); 27 | } 28 | 29 | void start() async { 30 | var packageInfo = await PackageInfo.fromPlatform(); 31 | setState(() { 32 | appName = packageInfo.appName; 33 | version = packageInfo.version; 34 | }); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return Scaffold( 40 | backgroundColor: Theme.of(context).backgroundColor, 41 | appBar: AppBar( 42 | elevation: 0, 43 | title: Text('Información'), 44 | centerTitle: true, 45 | ), 46 | body: ListView( 47 | children: [ 48 | Container( 49 | margin: EdgeInsets.only(top: 30), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.center, 52 | children: [ 53 | Center( 54 | child: Text( 55 | '$appName', 56 | style: TextStyle( 57 | color: Colors.white, 58 | fontWeight: FontWeight.bold, 59 | fontSize: 30, 60 | ), 61 | ), 62 | ), 63 | Center( 64 | child: Text( 65 | '$version', 66 | style: TextStyle( 67 | color: Colors.white, 68 | fontWeight: FontWeight.w500, 69 | fontSize: 20, 70 | ), 71 | ), 72 | ), 73 | Center( 74 | child: Image.asset( 75 | Constants.appLogo, 76 | width: 180, 77 | ), 78 | ), 79 | ], 80 | ), 81 | ), 82 | Container( 83 | margin: EdgeInsets.only(bottom: 20, left: 20, right: 20), 84 | child: Container( 85 | margin: EdgeInsets.all(10), 86 | child: Center( 87 | child: Text( 88 | 'Está aplicación obtiene datos de las siguientes fuentes:\n\n' 89 | '1. Buscador cubano RedCuba (https://www.redcuba.cu)\n' 90 | '2. Sitio web del Instituto de Meteorología ' 91 | '(http://www.insmet.cu)\n\n' 92 | 'Debido a que todas las fuentes son nacionales solo es ' 93 | 'necesario conexión a la red nacional (utiliza el bono ' 94 | 'nacional de 300 mb).\n\n' 95 | 'Los desarrolladores son personas independientes sin ' 96 | 'ánimo de lucro.\n\n' 97 | 'Para situaciones de tiempo peligrosas consultar las ' 98 | 'fuentes oficiales de información.', 99 | style: TextStyle( 100 | color: Colors.white, 101 | fontWeight: FontWeight.bold, 102 | ), 103 | textAlign: TextAlign.left, 104 | ), 105 | ), 106 | ), 107 | ), 108 | Column( 109 | mainAxisAlignment: MainAxisAlignment.center, 110 | children: [ 111 | Container( 112 | margin: EdgeInsets.symmetric(vertical: 5, horizontal: 30), 113 | child: Center( 114 | child: Text( 115 | 'Para dudas, problemas o sugerencias puede:', 116 | style: TextStyle( 117 | color: Colors.white, 118 | fontWeight: FontWeight.bold, 119 | ), 120 | textAlign: TextAlign.center, 121 | ), 122 | ), 123 | ), 124 | Container( 125 | margin: EdgeInsets.symmetric(horizontal: 20), 126 | child: GFButton( 127 | text: 'Escribir correo al desarrollador', 128 | textColor: Colors.white, 129 | color: Colors.white, 130 | size: GFSize.LARGE, 131 | shape: GFButtonShape.pills, 132 | type: GFButtonType.outline2x, 133 | fullWidthButton: true, 134 | onPressed: () async { 135 | const url = 'mailto:leynier41@gmail.com'; 136 | if (await canLaunch(url)) { 137 | await launch(url); 138 | } else { 139 | log('Could not launch $url'); 140 | } 141 | }, 142 | ), 143 | ), 144 | Container( 145 | margin: EdgeInsets.only(left: 20, right: 20, bottom: 20), 146 | child: GFButton( 147 | text: 'Visitar repositorio en GitHub', 148 | textColor: Colors.white, 149 | color: Colors.white, 150 | size: GFSize.LARGE, 151 | shape: GFButtonShape.pills, 152 | type: GFButtonType.outline2x, 153 | fullWidthButton: true, 154 | onPressed: () async { 155 | const url = 156 | 'https://github.com/cuba-weather/cuba-weather-flutter'; 157 | if (await canLaunch(url)) { 158 | await launch(url); 159 | } else { 160 | log('Could not launch $url'); 161 | } 162 | }, 163 | ), 164 | ), 165 | ], 166 | ), 167 | ], 168 | ), 169 | ); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /lib/src/pages/municipality_record/municipality_record_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuba_weather/src/widgets/empty_widget.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'package:cuba_weather/src/pages/municipality_record/blocs/blocs.dart'; 6 | import 'package:cuba_weather/src/pages/municipality_selection/municipality_selection_page.dart'; 7 | import 'package:cuba_weather/src/pages/municipality_list/municipality_list_page.dart'; 8 | import 'package:cuba_weather/src/utils/utils.dart'; 9 | 10 | import 'package:flutter_bloc/flutter_bloc.dart'; 11 | 12 | class MunicipalityRecordPage extends StatefulWidget { 13 | @override 14 | State createState() => MunicipalityRecordPageState(); 15 | } 16 | 17 | class MunicipalityRecordPageState extends State { 18 | @override 19 | Widget build(BuildContext context) { 20 | return BlocProvider( 21 | create: (context) => MunicipalityRecordBloc(), 22 | child: Scaffold( 23 | backgroundColor: Theme.of(context).backgroundColor, 24 | appBar: AppBar( 25 | elevation: 0, 26 | title: Text('Municipios'), 27 | centerTitle: true, 28 | actions: [ 29 | IconButton( 30 | icon: Icon(Icons.search), 31 | onPressed: () async { 32 | final municipality = await Navigator.push( 33 | context, 34 | MaterialPageRoute( 35 | builder: (context) => MunicipalitySelectionPage(), 36 | ), 37 | ); 38 | if (municipality != null) { 39 | setState(() { 40 | Navigator.pop(context, municipality); 41 | }); 42 | } 43 | }, 44 | ), 45 | IconButton( 46 | icon: Icon(Icons.format_list_bulleted), 47 | onPressed: () async { 48 | final municipality = await Navigator.push( 49 | context, 50 | MaterialPageRoute( 51 | builder: (context) => MunicipalityListPage(), 52 | ), 53 | ); 54 | if (municipality != null) { 55 | setState(() { 56 | Navigator.pop(context, municipality); 57 | }); 58 | } 59 | }, 60 | ), 61 | ], 62 | ), 63 | body: BlocBuilder( 64 | builder: (context, state) { 65 | if (state is MunicipalityRecordInitial) { 66 | BlocProvider.of(context) 67 | .add(FetchMunicipalityRecordEvent()); 68 | } 69 | if (state is MunicipalityRecordLoading) { 70 | return Center(child: CircularProgressIndicator()); 71 | } 72 | if (state is MunicipalityRecordError) { 73 | return ListView( 74 | children: [ 75 | Container( 76 | margin: EdgeInsets.all(10), 77 | child: Icon( 78 | Icons.error_outline, 79 | color: Colors.white, 80 | size: 150, 81 | ), 82 | ), 83 | Text( 84 | Constants.errorMessage, 85 | textAlign: TextAlign.center, 86 | style: TextStyle( 87 | color: Colors.white, 88 | fontWeight: FontWeight.bold, 89 | fontSize: 30, 90 | ), 91 | ), 92 | Container( 93 | margin: EdgeInsets.all(20), 94 | child: Container( 95 | padding: EdgeInsets.all(10), 96 | child: Text( 97 | state.errorMessage, 98 | textAlign: TextAlign.justify, 99 | style: TextStyle( 100 | color: Colors.white, 101 | fontWeight: FontWeight.w600, 102 | fontSize: 20, 103 | ), 104 | ), 105 | ), 106 | ), 107 | ], 108 | ); 109 | } 110 | if (state is MunicipalityRecordLoaded) { 111 | if(state.municipalities.length == 0) { 112 | return ListView( 113 | children: [ 114 | Text( 115 | "No hay Historial", 116 | textAlign: TextAlign.center, 117 | style: TextStyle( 118 | color: Colors.blueGrey, 119 | fontWeight: FontWeight.bold, 120 | fontSize: 30, 121 | ), 122 | ), 123 | ], 124 | ); 125 | } 126 | return ListView.separated( 127 | itemBuilder: (context, index) { 128 | var p = 10.0; 129 | return FlatButton( 130 | child: Container( 131 | padding: index == 0 132 | ? EdgeInsets.only( 133 | left: p, right: p, bottom: p, top: p * 2) 134 | : index == state.municipalities.length - 1 135 | ? EdgeInsets.only( 136 | left: p, right: p, bottom: p * 2, top: p) 137 | : EdgeInsets.all(p), 138 | child: Text( 139 | state.municipalities[index], 140 | style: TextStyle( 141 | color: Colors.white, 142 | fontWeight: FontWeight.bold, 143 | ), 144 | ), 145 | ), 146 | onPressed: () { 147 | setState(() { 148 | updateRecords(state.municipalities[index]); 149 | Navigator.pop( 150 | context, state.municipalities[index]); 151 | }); 152 | }, 153 | ); 154 | }, 155 | separatorBuilder: (context, index) { 156 | return Divider( 157 | color: Colors.white, 158 | ); 159 | }, 160 | itemCount: state.municipalities.length, 161 | ); 162 | } 163 | return EmptyWidget(); 164 | }, 165 | ), 166 | ), 167 | ); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/current_conditions_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:weather_icons/weather_icons.dart'; 3 | 4 | import 'package:cuba_weather_dart/cuba_weather_dart.dart' as cwd; 5 | 6 | import 'package:cuba_weather/src/pages/home/models/models.dart'; 7 | import 'package:cuba_weather/src/pages/home/widgets/widgets.dart'; 8 | 9 | class CurrentConditionsWidget extends StatelessWidget { 10 | final WeatherModel weather; 11 | 12 | const CurrentConditionsWidget({Key key, this.weather}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Column( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | children: [ 19 | Text( 20 | 'Última Información', 21 | style: TextStyle( 22 | fontSize: 16, 23 | fontWeight: FontWeight.bold, 24 | color: Colors.white, 25 | ), 26 | ), 27 | Row( 28 | mainAxisAlignment: MainAxisAlignment.center, 29 | crossAxisAlignment: CrossAxisAlignment.center, 30 | children: [ 31 | Text( 32 | '${weather.temperature.round()}', 33 | style: TextStyle( 34 | fontSize: 100, 35 | fontWeight: FontWeight.w100, 36 | color: Colors.white, 37 | ), 38 | ), 39 | Padding( 40 | padding: const EdgeInsets.only(right: 5), 41 | child: Column( 42 | crossAxisAlignment: CrossAxisAlignment.start, 43 | mainAxisAlignment: MainAxisAlignment.center, 44 | children: [ 45 | Text( 46 | '°C', 47 | style: TextStyle( 48 | fontSize: MediaQuery.of(context).size.width * 0.07, 49 | fontWeight: FontWeight.w600, 50 | color: Colors.white, 51 | ), 52 | ), 53 | BoxedIcon( 54 | WeatherIcons.thermometer, 55 | color: Colors.white, 56 | size: MediaQuery.of(context).size.width * 0.06, 57 | ), 58 | ], 59 | ), 60 | ), 61 | ], 62 | ), 63 | Row( 64 | mainAxisAlignment: MainAxisAlignment.center, 65 | children: [ 66 | ValueTileWidget( 67 | 'velocidad\ndel viento', 68 | '${weather.windVelocity.round()} km/h', 69 | widget: IconButton( 70 | icon: _parseWindVelocity(weather.windVelocity), 71 | color: Colors.white, 72 | iconSize: MediaQuery.of(context).size.width * 0.09, 73 | onPressed: () {}, 74 | ), 75 | ), 76 | Padding( 77 | padding: const EdgeInsets.only(left: 15, right: 15), 78 | child: Center( 79 | child: Container( 80 | width: 1, 81 | height: 30, 82 | color: Colors.white.withAlpha(50), 83 | )), 84 | ), 85 | ValueTileWidget( 86 | 'dirección\ndel viento', 87 | _cardinalToString(weather.windDirection), 88 | widget: Transform.rotate( 89 | angle: weather.windDirectionRadians, 90 | child: IconButton( 91 | icon: Icon(WeatherIcons.direction_down), 92 | color: Colors.white, 93 | iconSize: MediaQuery.of(context).size.width * 0.09, 94 | onPressed: () {}, 95 | ), 96 | ), 97 | ), 98 | Padding( 99 | padding: const EdgeInsets.only(left: 15, right: 15), 100 | child: Center( 101 | child: Container( 102 | width: 1, 103 | height: 30, 104 | color: Colors.white.withAlpha(50), 105 | )), 106 | ), 107 | ValueTileWidget( 108 | 'presión\natmosférica', 109 | '${weather.pressure.round()} hPa', 110 | widget: BoxedIcon( 111 | WeatherIcons.barometer, 112 | color: Colors.white, 113 | size: MediaQuery.of(context).size.width * 0.09, 114 | ), 115 | ), 116 | Padding( 117 | padding: const EdgeInsets.only(left: 15, right: 15), 118 | child: Center( 119 | child: Container( 120 | width: 1, 121 | height: 30, 122 | color: Colors.white.withAlpha(50), 123 | )), 124 | ), 125 | ValueTileWidget( 126 | 'humedad\nrelativa', 127 | '${weather.humidity.round()}%', 128 | widget: BoxedIcon( 129 | WeatherIcons.humidity, 130 | color: Colors.white, 131 | size: MediaQuery.of(context).size.width * 0.09, 132 | ), 133 | ), 134 | ], 135 | ), 136 | ], 137 | ); 138 | } 139 | 140 | static Icon _parseWindVelocity(double x) { 141 | if (x < 2) 142 | return Icon(WeatherIcons.wind_beaufort_0); 143 | else if (x < 6) 144 | return Icon(WeatherIcons.wind_beaufort_1); 145 | else if (x < 12) 146 | return Icon(WeatherIcons.wind_beaufort_2); 147 | else if (x < 20) 148 | return Icon(WeatherIcons.wind_beaufort_3); 149 | else if (x < 29) 150 | return Icon(WeatherIcons.wind_beaufort_4); 151 | else if (x < 39) 152 | return Icon(WeatherIcons.wind_beaufort_5); 153 | else if (x < 50) 154 | return Icon(WeatherIcons.wind_beaufort_6); 155 | else if (x < 62) 156 | return Icon(WeatherIcons.wind_beaufort_7); 157 | else if (x < 75) 158 | return Icon(WeatherIcons.wind_beaufort_8); 159 | else if (x < 89) 160 | return Icon(WeatherIcons.wind_beaufort_9); 161 | else if (x < 103) 162 | return Icon(WeatherIcons.wind_beaufort_10); 163 | else if (x < 118) 164 | return Icon(WeatherIcons.wind_beaufort_11); 165 | else 166 | return Icon(WeatherIcons.wind_beaufort_12); 167 | } 168 | 169 | String _cardinalToString(cwd.CardinalPoint x) { 170 | switch (x) { 171 | case cwd.CardinalPoint.North: 172 | return 'N'; 173 | case cwd.CardinalPoint.North_Northeast: 174 | return 'N-NE'; 175 | case cwd.CardinalPoint.Northeast: 176 | return 'NE'; 177 | case cwd.CardinalPoint.East_Northeast: 178 | return 'E-NE'; 179 | case cwd.CardinalPoint.East: 180 | return 'E'; 181 | case cwd.CardinalPoint.East_Southeast: 182 | return 'E-SE'; 183 | case cwd.CardinalPoint.Southeast: 184 | return 'SE'; 185 | case cwd.CardinalPoint.South_Southeast: 186 | return 'S-SE'; 187 | case cwd.CardinalPoint.South: 188 | return 'S'; 189 | case cwd.CardinalPoint.South_Southwest: 190 | return 'S-SO'; 191 | case cwd.CardinalPoint.Southwest: 192 | return 'SO'; 193 | case cwd.CardinalPoint.West_Southwest: 194 | return 'O-SO'; 195 | case cwd.CardinalPoint.West: 196 | return 'O'; 197 | case cwd.CardinalPoint.West_Northwest: 198 | return 'O-NO'; 199 | case cwd.CardinalPoint.Northwest: 200 | return 'NO'; 201 | case cwd.CardinalPoint.North_Northwest: 202 | return 'N-NO'; 203 | default: 204 | return ''; 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /lib/src/pages/home/widgets/today_forecast_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:weather_icons/weather_icons.dart'; 3 | 4 | import 'package:cuba_weather_dart/cuba_weather_dart.dart' as cwd; 5 | 6 | import 'package:cuba_weather/src/pages/home/models/models.dart'; 7 | 8 | class TodayForecastWidget extends StatelessWidget { 9 | final WeatherModel weather; 10 | 11 | const TodayForecastWidget({Key key, this.weather}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | String weatherIconCode = _weatherIconCodeByState( 16 | weather.getTodayForecast().state, 17 | weather.dateTime, 18 | ); 19 | return Column( 20 | mainAxisAlignment: MainAxisAlignment.center, 21 | children: [ 22 | Text( 23 | 'Pronóstico para Hoy', 24 | style: TextStyle( 25 | fontSize: 16, 26 | fontWeight: FontWeight.bold, 27 | color: Colors.white, 28 | ), 29 | ), 30 | Row( 31 | crossAxisAlignment: CrossAxisAlignment.center, 32 | mainAxisAlignment: MainAxisAlignment.center, 33 | children: [ 34 | Column( 35 | mainAxisAlignment: MainAxisAlignment.center, 36 | crossAxisAlignment: CrossAxisAlignment.center, 37 | children: [ 38 | Text( 39 | 'Máxima', 40 | textAlign: TextAlign.center, 41 | style: TextStyle( 42 | color: Colors.white, 43 | ), 44 | ), 45 | Row( 46 | mainAxisAlignment: MainAxisAlignment.spaceAround, 47 | crossAxisAlignment: CrossAxisAlignment.center, 48 | children: [ 49 | Text( 50 | '${weather.getTodayForecast().temperatureMax.round()}', 51 | style: TextStyle( 52 | fontSize: 40, 53 | fontWeight: FontWeight.w600, 54 | color: Colors.white, 55 | ), 56 | ), 57 | Padding( 58 | padding: const EdgeInsets.only(right: 0), 59 | child: Column( 60 | crossAxisAlignment: CrossAxisAlignment.start, 61 | mainAxisAlignment: MainAxisAlignment.center, 62 | children: [ 63 | Text( 64 | '°C', 65 | style: TextStyle( 66 | fontSize: 10, 67 | fontWeight: FontWeight.w600, 68 | color: Colors.white, 69 | ), 70 | ), 71 | BoxedIcon( 72 | WeatherIcons.thermometer, 73 | color: Colors.white, 74 | size: 10, 75 | ), 76 | ], 77 | ), 78 | ), 79 | ], 80 | ), 81 | ], 82 | ), 83 | BoxedIcon( 84 | WeatherIcons.fromString(weatherIconCode, 85 | fallback: WeatherIcons.na), 86 | size: 100, 87 | color: Colors.white, 88 | ), 89 | Column( 90 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 91 | crossAxisAlignment: CrossAxisAlignment.center, 92 | children: [ 93 | Text( 94 | 'Mínima', 95 | textAlign: TextAlign.center, 96 | style: TextStyle( 97 | color: Colors.white, 98 | ), 99 | ), 100 | Row( 101 | mainAxisAlignment: MainAxisAlignment.spaceAround, 102 | crossAxisAlignment: CrossAxisAlignment.center, 103 | children: [ 104 | Text( 105 | '${weather.getTodayForecast().temperatureMin.round()}', 106 | style: TextStyle( 107 | fontSize: 40, 108 | fontWeight: FontWeight.w600, 109 | color: Colors.white, 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.only(right: 0), 114 | child: Column( 115 | crossAxisAlignment: CrossAxisAlignment.start, 116 | mainAxisAlignment: MainAxisAlignment.center, 117 | children: [ 118 | Text( 119 | '°C', 120 | style: TextStyle( 121 | fontSize: 10, 122 | fontWeight: FontWeight.w600, 123 | color: Colors.white, 124 | ), 125 | ), 126 | BoxedIcon( 127 | WeatherIcons.thermometer, 128 | color: Colors.white, 129 | size: 10, 130 | ), 131 | ], 132 | ), 133 | ), 134 | ], 135 | ), 136 | ], 137 | ), 138 | ], 139 | ), 140 | Center( 141 | child: Text( 142 | weather.getTodayForecast().stateDescription, 143 | style: TextStyle( 144 | fontSize: 20, 145 | fontWeight: FontWeight.w600, 146 | color: Colors.white, 147 | ), 148 | ), 149 | ), 150 | ], 151 | ); 152 | } 153 | 154 | String _weatherIconCodeByState(cwd.InsmetState state, DateTime dateTime) { 155 | bool itsDay = dateTime.hour > 6 && dateTime.hour < 19; 156 | String result = ''; 157 | switch (state) { 158 | case cwd.InsmetState.OccasionalShowers: 159 | result = itsDay ? 'wi-day-rain' : 'wi-night-alt-rain'; 160 | break; 161 | case cwd.InsmetState.ScatteredShowers: 162 | result = itsDay ? 'wi-rain' : 'wi-rain'; 163 | break; 164 | case cwd.InsmetState.IsolatedShowers: 165 | result = itsDay ? 'wi-day-rain' : 'wi-night-alt-rain'; 166 | break; 167 | case cwd.InsmetState.AfternoonShowers: 168 | result = itsDay ? 'wi-showers' : 'wi-showers'; 169 | break; 170 | case cwd.InsmetState.RainShowers: 171 | result = itsDay ? 'wi-showers' : 'wi-night-alt-showers'; 172 | break; 173 | case cwd.InsmetState.PartlyCloudy: 174 | result = itsDay ? 'wi-day-cloudy' : 'wi-night-alt-cloudy'; 175 | break; 176 | case cwd.InsmetState.Cloudy: 177 | result = 'wi-cloudy'; 178 | break; 179 | case cwd.InsmetState.Sunny: 180 | result = 'wi-day-sunny'; 181 | break; 182 | case cwd.InsmetState.Storms: 183 | result = itsDay ? 'wi-day-thunderstorm' : 'wi-night-alt-thunderstorm'; 184 | break; 185 | case cwd.InsmetState.AfternoonStorms: 186 | result = 'wi-thunderstorm'; 187 | break; 188 | case cwd.InsmetState.MorningScatteredShowers: 189 | result = 'wi-rain'; 190 | break; 191 | case cwd.InsmetState.Winds: 192 | result = 'wi-strong-wind'; 193 | break; 194 | default: 195 | result = 'wi-na'; 196 | } 197 | return result; 198 | } 199 | } 200 | --------------------------------------------------------------------------------