├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── CLIMA_BUG_REPORT.yaml │ ├── CLIMA_FEATURE_REQUEST.yml │ └── config.yml └── SECURITY.md ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── clima │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── app_icon.png │ │ │ ├── drawable-mdpi │ │ │ └── app_icon.png │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ └── app_icon.png │ │ │ ├── drawable-xxhdpi │ │ │ └── app_icon.png │ │ │ ├── drawable-xxxhdpi │ │ │ └── app_icon.png │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── raw │ │ │ ├── bubble.wav │ │ │ └── keep.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── fonts │ └── manrope │ │ ├── Manrope-Bold.ttf │ │ ├── Manrope-Light.ttf │ │ ├── Manrope-Medium.ttf │ │ └── Manrope-Regular.ttf ├── images │ ├── Cover.png │ ├── Thumbnail.png │ ├── logo.png │ ├── preview.mp4 │ └── weather │ │ ├── Day Clouds.webp │ │ ├── Day Rain.webp │ │ ├── Day Snow.webp │ │ ├── Day Storm.webp │ │ ├── Day Sun.webp │ │ ├── Day Wind.webp │ │ ├── Night Clouds.webp │ │ ├── Night Moon.webp │ │ ├── Night Rain.webp │ │ ├── Night Snow.webp │ │ ├── Night Storm.webp │ │ ├── Night Wind.webp │ │ ├── day_blured.svg │ │ ├── humidity.svg │ │ ├── night_blured.svg │ │ ├── rain.svg │ │ ├── snow.svg │ │ ├── sun.svg │ │ ├── sunny.svg │ │ └── wind.svg ├── lottie │ ├── day_cloudy.json │ ├── day_rain.json │ ├── day_snow.json │ ├── faliure.json │ ├── loading_weather.json │ ├── location.json │ ├── location_not_found.json │ ├── location_service.json │ ├── night.json │ ├── night_cloudy.json │ ├── night_rain.json │ ├── night_snow.json │ ├── no_internet.json │ ├── storm.json │ ├── sun.json │ └── wind.json └── map │ └── map_dark_theme.json ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── AppIcon-20@2x.png │ │ │ ├── AppIcon-20@2x~ipad.png │ │ │ ├── AppIcon-20@3x.png │ │ │ ├── AppIcon-20~ipad.png │ │ │ ├── AppIcon-29.png │ │ │ ├── AppIcon-29@2x.png │ │ │ ├── AppIcon-29@2x~ipad.png │ │ │ ├── AppIcon-29@3x.png │ │ │ ├── AppIcon-29~ipad.png │ │ │ ├── AppIcon-40@2x.png │ │ │ ├── AppIcon-40@2x~ipad.png │ │ │ ├── AppIcon-40@3x.png │ │ │ ├── AppIcon-40~ipad.png │ │ │ ├── AppIcon-60@2x~car.png │ │ │ ├── AppIcon-60@3x~car.png │ │ │ ├── AppIcon-83.5@2x~ipad.png │ │ │ ├── AppIcon@2x.png │ │ │ ├── AppIcon@2x~ipad.png │ │ │ ├── AppIcon@3x.png │ │ │ ├── AppIcon~ios-marketing.png │ │ │ ├── AppIcon~ipad.png │ │ │ └── Contents.json │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h └── RunnerTests │ └── RunnerTests.swift ├── lib ├── app │ ├── app.dart │ ├── bloc │ │ ├── bloc.dart │ │ ├── location │ │ │ ├── location_bloc.dart │ │ │ ├── location_event.dart │ │ │ └── location_state.dart │ │ └── theme │ │ │ ├── theme_cubit.dart │ │ │ └── theme_state.dart │ └── widgets │ │ ├── location_service_disabled.dart │ │ ├── permission_denied_widget.dart │ │ ├── waiting_permission_widget.dart │ │ └── widgets.dart ├── core │ ├── animations │ │ ├── button_animation.dart │ │ ├── fade_slide_animation.dart │ │ └── play_lottie.dart │ ├── common │ │ ├── common.dart │ │ ├── failure_widget.dart │ │ ├── loading_widget.dart │ │ ├── primary_button.dart │ │ ├── temperature_text.dart │ │ └── weather_image.dart │ ├── enum │ │ └── weather_state.dart │ ├── extensions │ │ ├── map_weather_code_extensions.dart │ │ └── weather_message_extension.dart │ ├── helper │ │ ├── date_helper.dart │ │ ├── env_helper.dart │ │ ├── functions.dart │ │ ├── location_helper.dart │ │ └── lotte_cach_helper.dart │ ├── managers │ │ ├── dependency_manager.dart │ │ ├── managers.dart │ │ └── notification_manager.dart │ ├── navigation │ │ ├── app_routes.dart │ │ ├── navigation.dart │ │ └── navigation_helper.dart │ ├── network │ │ ├── endpoints.dart │ │ ├── error_handling.dart │ │ ├── network.dart │ │ ├── network_helper.dart │ │ └── response │ │ │ ├── code.dart │ │ │ ├── extension.dart │ │ │ ├── failure.dart │ │ │ ├── message.dart │ │ │ └── type.dart │ └── utils │ │ ├── app_colors.dart │ │ ├── app_decoration.dart │ │ ├── app_dimn.dart │ │ ├── app_images.dart │ │ ├── app_themes.dart │ │ ├── app_typography.dart │ │ └── utils.dart ├── features │ ├── daily_forecast │ │ ├── cubit │ │ │ ├── daily_forecast_cubit.dart │ │ │ └── daily_forecast_state.dart │ │ ├── data │ │ │ ├── models │ │ │ │ ├── daily_weather_model.dart │ │ │ │ └── open_meteo_daily_response.dart │ │ │ └── repo │ │ │ │ ├── daily_forecast_repo.dart │ │ │ │ └── daily_forecast_repo_impl.dart │ │ └── screens │ │ │ ├── daily_forecast_screen.dart │ │ │ └── widgets │ │ │ ├── daily_forecast_widget.dart │ │ │ ├── daily_widget.dart │ │ │ ├── date_widget.dart │ │ │ └── widgets.dart │ ├── home │ │ ├── cubit │ │ │ ├── home_cubit.dart │ │ │ └── home_state.dart │ │ ├── data │ │ │ ├── model │ │ │ │ ├── weather.dart │ │ │ │ ├── weather_model.dart │ │ │ │ └── weather_theme.dart │ │ │ └── repo │ │ │ │ ├── home_repo.dart │ │ │ │ └── home_repo_impl.dart │ │ └── screens │ │ │ ├── home_screen.dart │ │ │ └── widgets │ │ │ ├── home_widget.dart │ │ │ ├── time_and_location.dart │ │ │ ├── weather_status.dart │ │ │ └── widgets.dart │ ├── hourly_forecast │ │ ├── cubit │ │ │ ├── hourly_forecast_cubit.dart │ │ │ └── hourly_forecast_state.dart │ │ ├── data │ │ │ ├── models │ │ │ │ ├── forecast_info_model.dart │ │ │ │ ├── open_meteo_hourly_response_model.dart │ │ │ │ ├── weather_daily_model.dart │ │ │ │ ├── weather_hourly_model.dart │ │ │ │ └── weather_news_model.dart │ │ │ └── repo │ │ │ │ ├── hourly_forecast_repo.dart │ │ │ │ └── hourly_forecast_repo_impl.dart │ │ └── screens │ │ │ ├── hourly_forecast_screen.dart │ │ │ └── widgets │ │ │ ├── custom_app_bar.dart │ │ │ ├── day_and_night_widget.dart │ │ │ ├── forecast_info_widget.dart │ │ │ ├── hourly_forecast_details.dart │ │ │ ├── hourly_forecast_widget.dart │ │ │ ├── news.dart │ │ │ ├── news_widget.dart │ │ │ └── widgets.dart │ ├── landing_page │ │ ├── bloc │ │ │ └── nav_bar │ │ │ │ ├── nav_bar_bloc.dart │ │ │ │ ├── nav_bar_event.dart │ │ │ │ └── nav_bar_state.dart │ │ ├── landing_screen.dart │ │ └── widgets │ │ │ ├── bottom_nav_bar_list.dart │ │ │ └── screens.dart │ └── map │ │ └── screens │ │ └── map_screen.dart └── main.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/CLIMA_BUG_REPORT.yaml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: Issue something wrong 3 | title: "[BUG]: " 4 | labels: ["bug", "triage"] 5 | assignees: ["moha-b"] 6 | body: 7 | - type: textarea 8 | id: bug-description 9 | attributes: 10 | label: Description of the bug 11 | description: Give us a brief description of what happened and what should have happened. 12 | placeholder: | 13 | Tell us what you see! 14 | validations: 15 | required: true 16 | 17 | - type: textarea 18 | id: steps-to-reproduce 19 | attributes: 20 | label: Steps To Reproduce 21 | description: Steps to reproduce the behavior. 22 | placeholder: | 23 | 1. Go to '...' 24 | 2. Click on '...' 25 | 3. Scroll down to '...' 26 | 4. See error 27 | validations: 28 | required: true 29 | 30 | - type: textarea 31 | id: additional-information 32 | attributes: 33 | label: Additional Information 34 | description: | 35 | Provide any additional information such as logs, screenshots, likes, scenarios in which the bug occurs so that it facilitates resolving the issue. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/CLIMA_FEATURE_REQUEST.yml: -------------------------------------------------------------------------------- 1 | name: 🌟 Feature Request 2 | description: Submit a feature request 3 | title: "[FEATURE]: <title>" 4 | labels: ["feature", "triage"] 5 | assignees: ["moha-b"] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to submit this feature request! 11 | - type: dropdown 12 | attributes: 13 | multiple: false 14 | label: Type of Feature 15 | description: Select the type of feature request. 16 | options: 17 | - "✨ New Feature" 18 | - "📝 Documentation" 19 | - "🎨 Style and UI" 20 | - "🔨 Code Refactor" 21 | - "⚡ Performance Improvements" 22 | - "✅ New Test" 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: description 27 | attributes: 28 | label: Description 29 | description: Describe the new feature in detail. 30 | placeholder: Provide a comprehensive overview of the proposed feature. 31 | validations: 32 | required: true 33 | - type: textarea 34 | id: benefits 35 | attributes: 36 | label: Benefits 37 | description: Explain the benefits of implementing this feature. 38 | placeholder: Describe how users and the project would benefit from this addition. 39 | validations: 40 | required: true 41 | - type: textarea 42 | id: use-cases 43 | attributes: 44 | label: Use Cases 45 | description: Outline the use cases or scenarios where this feature would be beneficial. 46 | placeholder: Describe specific situations where the feature would be valuable. 47 | validations: 48 | required: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | /lib/core/api/keys.dart 46 | /assets/env/ 47 | -------------------------------------------------------------------------------- /.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: "ead455963c12b453cdb2358cad34969c76daf180" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: ead455963c12b453cdb2358cad34969c76daf180 17 | base_revision: ead455963c12b453cdb2358cad34969c76daf180 18 | - platform: android 19 | create_revision: ead455963c12b453cdb2358cad34969c76daf180 20 | base_revision: ead455963c12b453cdb2358cad34969c76daf180 21 | - platform: ios 22 | create_revision: ead455963c12b453cdb2358cad34969c76daf180 23 | base_revision: ead455963c12b453cdb2358cad34969c76daf180 24 | - platform: web 25 | create_revision: ead455963c12b453cdb2358cad34969c76daf180 26 | base_revision: ead455963c12b453cdb2358cad34969c76daf180 27 | - platform: windows 28 | create_revision: ead455963c12b453cdb2358cad34969c76daf180 29 | base_revision: ead455963c12b453cdb2358cad34969c76daf180 30 | 31 | # User provided section 32 | 33 | # List of Local paths (relative to this file) that should be 34 | # ignored by the migrate tool. 35 | # 36 | # Files that are not part of the templates will be ignored by default. 37 | unmanaged_files: 38 | - 'lib/main.dart' 39 | - 'ios/Runner.xcodeproj/project.pbxproj' 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 hab 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | def dartEnv = [ 26 | GOOGLE_MAPS_API: '', 27 | NEWS_API_KEY: '' 28 | ]; 29 | if (project.hasProperty('dart-defines')) { 30 | dartEnv = dartEnv + project.property('dart-defines') 31 | .split(',') 32 | .collectEntries { entry -> 33 | def pair = new String(entry.decodeBase64(), 'UTF-8').split('=') 34 | [(pair.first()): pair.last()] 35 | } 36 | } 37 | 38 | 39 | android { 40 | namespace "com.example.clima" 41 | compileSdkVersion 34 42 | ndkVersion flutter.ndkVersion 43 | 44 | compileOptions { 45 | coreLibraryDesugaringEnabled true 46 | sourceCompatibility JavaVersion.VERSION_1_8 47 | targetCompatibility JavaVersion.VERSION_1_8 48 | } 49 | 50 | kotlinOptions { 51 | jvmTarget = '1.8' 52 | } 53 | 54 | sourceSets { 55 | main.java.srcDirs += 'src/main/kotlin' 56 | } 57 | 58 | defaultConfig { 59 | multiDexEnabled true 60 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 61 | applicationId "com.example.clima" 62 | // You can update the following values to match your application needs. 63 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 64 | minSdkVersion 21 65 | targetSdkVersion flutter.targetSdkVersion 66 | versionCode flutterVersionCode.toInteger() 67 | versionName flutterVersionName 68 | } 69 | 70 | buildTypes { 71 | release { 72 | // TODO: Add your own signing config for the release build. 73 | // Signing with the debug keys for now, so `flutter run --release` works. 74 | signingConfig signingConfigs.debug 75 | } 76 | } 77 | } 78 | 79 | flutter { 80 | source '../..' 81 | } 82 | 83 | dependencies { 84 | implementation 'com.google.android.gms:play-services-maps:18.2.0' 85 | coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2' 86 | } 87 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2 | <!-- The INTERNET permission is required for development. Specifically, 3 | the Flutter tool needs it to communicate with the running application 4 | to allow setting breakpoints, to provide hot reload, etc. 5 | --> 6 | <uses-permission android:name="android.permission.INTERNET"/> 7 | </manifest> 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | <manifest xmlns:tools="http://schemas.android.com/tools" 2 | xmlns:android="http://schemas.android.com/apk/res/android"> 3 | <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 4 | <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> 5 | <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> 6 | <uses-permission android:name="android.permission.INTERNET" /> 7 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> 8 | <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> 9 | <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> 10 | <application 11 | android:label="Clima" 12 | android:name="${applicationName}" 13 | android:icon="@mipmap/ic_launcher"> 14 | <activity 15 | android:name=".MainActivity" 16 | android:exported="true" 17 | android:showWhenLocked="true" 18 | android:turnScreenOn="true" 19 | android:launchMode="singleTop" 20 | android:theme="@style/LaunchTheme" 21 | android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" 22 | android:hardwareAccelerated="true" 23 | android:windowSoftInputMode="adjustResize" 24 | tools:targetApi="o_mr1"> 25 | <!-- Specifies an Android theme to apply to this Activity as soon as 26 | the Android process has started. This theme is visible to the user 27 | while the Flutter UI initializes. After that, this theme continues 28 | to determine the Window background behind the Flutter UI. --> 29 | <meta-data 30 | android:name="io.flutter.embedding.android.NormalTheme" 31 | android:resource="@style/NormalTheme" 32 | /> 33 | <meta-data 34 | android:name="com.google.firebase.messaging.default_notification_icon" 35 | android:resource="@drawable/app_icon" /> 36 | <intent-filter> 37 | <action android:name="android.intent.action.MAIN"/> 38 | <category android:name="android.intent.category.LAUNCHER"/> 39 | </intent-filter> 40 | </activity> 41 | <receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" /> 42 | <receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver"> 43 | <intent-filter> 44 | <action android:name="android.intent.action.BOOT_COMPLETED"/> 45 | <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/> 46 | <action android:name="android.intent.action.QUICKBOOT_POWERON" /> 47 | <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> 48 | </intent-filter> 49 | </receiver> 50 | <meta-data android:name="com.google.android.geo.API_KEY" 51 | android:value="AIzaSyB--blUyx_UMbgfkRczDQ-cLU4SDe6C8r4"/> 52 | <!-- Don't delete the meta-data below. 53 | This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> 54 | <meta-data 55 | android:name="flutterEmbedding" 56 | android:value="2" /> 57 | </application> 58 | </manifest> 59 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/clima/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.clima 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/drawable-hdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/drawable-mdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <!-- Modify this file to customize your launch splash screen --> 3 | <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> 4 | <item android:drawable="?android:colorBackground" /> 5 | 6 | <!-- You can insert your own image assets here --> 7 | <!-- <item> 8 | <bitmap 9 | android:gravity="center" 10 | android:src="@mipmap/launch_image" /> 11 | </item> --> 12 | </layer-list> 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/drawable-xhdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/drawable-xxhdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/drawable-xxxhdpi/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <!-- Modify this file to customize your launch splash screen --> 3 | <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> 4 | <item android:drawable="@android:color/white" /> 5 | 6 | <!-- You can insert your own image assets here --> 7 | <!-- <item> 8 | <bitmap 9 | android:gravity="center" 10 | android:src="@mipmap/launch_image" /> 11 | </item> --> 12 | </layer-list> 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> 3 | <background android:drawable="@mipmap/ic_launcher_background"/> 4 | <foreground android:drawable="@mipmap/ic_launcher_foreground"/> 5 | <monochrome android:drawable="@mipmap/ic_launcher_monochrome"/> 6 | </adaptive-icon> -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/raw/bubble.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/android/app/src/main/res/raw/bubble.wav -------------------------------------------------------------------------------- /android/app/src/main/res/raw/keep.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <resources xmlns:tools="http://schemas.android.com/tools" 3 | tools:keep="@raw/bubble" 4 | /> -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <resources> 3 | <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> 4 | <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> 5 | <!-- Show a splash screen on the activity. Automatically removed when 6 | the Flutter engine draws its first frame --> 7 | <item name="android:windowBackground">@drawable/launch_background</item> 8 | </style> 9 | <!-- Theme applied to the Android Window as soon as the process has started. 10 | This theme determines the color of the Android Window while your 11 | Flutter UI initializes, as well as behind your Flutter UI while its 12 | running. 13 | 14 | This Theme is only used starting with V2 of Flutter's Android embedding. --> 15 | <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> 16 | <item name="android:windowBackground">?android:colorBackground</item> 17 | </style> 18 | </resources> 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <resources> 3 | <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> 4 | <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> 5 | <!-- Show a splash screen on the activity. Automatically removed when 6 | the Flutter engine draws its first frame --> 7 | <item name="android:windowBackground">@drawable/launch_background</item> 8 | </style> 9 | <!-- Theme applied to the Android Window as soon as the process has started. 10 | This theme determines the color of the Android Window while your 11 | Flutter UI initializes, as well as behind your Flutter UI while its 12 | running. 13 | 14 | This Theme is only used starting with V2 of Flutter's Android embedding. --> 15 | <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> 16 | <item name="android:windowBackground">?android:colorBackground</item> 17 | </style> 18 | </resources> 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2 | <!-- The INTERNET permission is required for development. Specifically, 3 | the Flutter tool needs it to communicate with the running application 4 | to allow setting breakpoints, to provide hot reload, etc. 5 | --> 6 | <uses-permission android:name="android.permission.INTERNET"/> 7 | </manifest> 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | allprojects { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | rootProject.buildDir = '../build' 10 | subprojects { 11 | project.buildDir = "${rootProject.buildDir}/${project.name}" 12 | } 13 | subprojects { 14 | project.evaluationDependsOn(':app') 15 | } 16 | 17 | tasks.register("clean", Delete) { 18 | delete rootProject.buildDir 19 | } 20 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "7.3.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.9.0" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /assets/fonts/manrope/Manrope-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/fonts/manrope/Manrope-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/manrope/Manrope-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/fonts/manrope/Manrope-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/manrope/Manrope-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/fonts/manrope/Manrope-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/manrope/Manrope-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/fonts/manrope/Manrope-Regular.ttf -------------------------------------------------------------------------------- /assets/images/Cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/Cover.png -------------------------------------------------------------------------------- /assets/images/Thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/Thumbnail.png -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/logo.png -------------------------------------------------------------------------------- /assets/images/preview.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/preview.mp4 -------------------------------------------------------------------------------- /assets/images/weather/Day Clouds.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Day Clouds.webp -------------------------------------------------------------------------------- /assets/images/weather/Day Rain.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Day Rain.webp -------------------------------------------------------------------------------- /assets/images/weather/Day Snow.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Day Snow.webp -------------------------------------------------------------------------------- /assets/images/weather/Day Storm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Day Storm.webp -------------------------------------------------------------------------------- /assets/images/weather/Day Sun.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Day Sun.webp -------------------------------------------------------------------------------- /assets/images/weather/Day Wind.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Day Wind.webp -------------------------------------------------------------------------------- /assets/images/weather/Night Clouds.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Night Clouds.webp -------------------------------------------------------------------------------- /assets/images/weather/Night Moon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Night Moon.webp -------------------------------------------------------------------------------- /assets/images/weather/Night Rain.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Night Rain.webp -------------------------------------------------------------------------------- /assets/images/weather/Night Snow.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Night Snow.webp -------------------------------------------------------------------------------- /assets/images/weather/Night Storm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Night Storm.webp -------------------------------------------------------------------------------- /assets/images/weather/Night Wind.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/assets/images/weather/Night Wind.webp -------------------------------------------------------------------------------- /assets/images/weather/day_blured.svg: -------------------------------------------------------------------------------- 1 | <svg width="3774" height="3774" viewBox="0 0 3774 3774" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <g filter="url(#filter0_f_166_6)"> 3 | <circle cx="1887" cy="1887" r="887" fill="url(#paint0_radial_166_6)"/> 4 | </g> 5 | <defs> 6 | <filter id="filter0_f_166_6" x="0" y="0" width="3774" height="3774" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> 7 | <feFlood flood-opacity="0" result="BackgroundImageFix"/> 8 | <feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> 9 | <feGaussianBlur stdDeviation="500" result="effect1_foregroundBlur_166_6"/> 10 | </filter> 11 | <radialGradient id="paint0_radial_166_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(1887.53 1887.53) rotate(-42.3421) scale(1066.32)"> 12 | <stop stop-color="#EFC5B4"/> 13 | <stop offset="1" stop-color="#F2E477" stop-opacity="0.2"/> 14 | </radialGradient> 15 | </defs> 16 | </svg> 17 | -------------------------------------------------------------------------------- /assets/images/weather/humidity.svg: -------------------------------------------------------------------------------- 1 | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 2 | <!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools --> 3 | <svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 4 | <g id="SVGRepo_bgCarrier" stroke-width="0"/> 5 | <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/> 6 | <g id="SVGRepo_iconCarrier"> <path opacity="0.5" d="M14.381 8.02721C14.9767 7.81911 15.6178 7.70588 16.2857 7.70588C16.9404 7.70588 17.5693 7.81468 18.1551 8.01498M7.11616 10.6089C6.8475 10.5567 6.56983 10.5294 6.28571 10.5294C3.91878 10.5294 2 12.4256 2 14.7647C2 16.6611 3.26124 18.2664 5 18.8061M7.11616 10.6089C6.88706 9.9978 6.7619 9.33687 6.7619 8.64706C6.7619 5.52827 9.32028 3 12.4762 3C15.4159 3 17.8371 5.19371 18.1551 8.01498M7.11616 10.6089C7.68059 10.7184 8.20528 10.9374 8.66667 11.2426M18.1551 8.01498C20.393 8.78024 22 10.8811 22 13.3529C22 15.6958 20.5562 17.7055 18.5 18.5604" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path d="M11 20.3336C11 21.2541 10.3284 22.0002 9.5 22.0002C8.67157 22.0002 8 21.2541 8 20.3336C8 19.8287 8.45122 19.1758 8.85871 18.689C9.19832 18.2833 9.80168 18.2833 10.1413 18.689C10.5488 19.1758 11 19.8287 11 20.3336Z" stroke="#00B36A" stroke-width="1.5"/> <path d="M16 20.3336C16 21.2541 15.3284 22.0002 14.5 22.0002C13.6716 22.0002 13 21.2541 13 20.3336C13 19.8287 13.4512 19.1758 13.8587 18.689C14.1983 18.2833 14.8017 18.2833 15.1413 18.689C15.5488 19.1758 16 19.8287 16 20.3336Z" stroke="#00B36A" stroke-width="1.5"/> <path d="M13.5 15.3336C13.5 16.2541 12.8284 17.0002 12 17.0002C11.1716 17.0002 10.5 16.2541 10.5 15.3336C10.5 14.8287 10.9512 14.1758 11.3587 13.689C11.6983 13.2833 12.3017 13.2833 12.6413 13.689C13.0488 14.1758 13.5 14.8287 13.5 15.3336Z" stroke="#00B36A" stroke-width="1.5"/> </g> 7 | </svg> -------------------------------------------------------------------------------- /assets/images/weather/night_blured.svg: -------------------------------------------------------------------------------- 1 | <svg width="3329" height="3329" viewBox="0 0 3329 3329" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <g filter="url(#filter0_f_166_11)"> 3 | <circle cx="1664.5" cy="1664.5" r="664.5" fill="url(#paint0_radial_166_11)"/> 4 | </g> 5 | <defs> 6 | <filter id="filter0_f_166_11" x="0" y="0" width="3329" height="3329" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> 7 | <feFlood flood-opacity="0" result="BackgroundImageFix"/> 8 | <feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> 9 | <feGaussianBlur stdDeviation="500" result="effect1_foregroundBlur_166_11"/> 10 | </filter> 11 | <radialGradient id="paint0_radial_166_11" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(1664.89 1664.89) rotate(-42.3421) scale(798.835)"> 12 | <stop stop-color="#E2E0EF"/> 13 | <stop offset="1" stop-color="#EEF0F2"/> 14 | </radialGradient> 15 | </defs> 16 | </svg> 17 | -------------------------------------------------------------------------------- /assets/images/weather/rain.svg: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> 3 | <svg width="800px" height="800px" viewBox="0 0 1024 1024" class="icon" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M747.2 359.1c-21.1 0-41.4 3.9-60.2 11.1-9.7-112.1-98.4-200-206.5-200-104.8 0-191.4 82.6-205.4 189.8-5.9-0.6-11.8-0.9-17.8-0.9-98.3 0-177.9 84.6-177.9 188.9s77.5 189.3 175.7 189.3c1.7 0 8.5-0.3 11.4-0.5h-1.3v-0.2c3.3 0 3.1 0.1 1.3 0.2l442.2 0.5c12.9 0 24.5-0.6 38.5-0.6 98.2 0 177.8-84.5 177.8-188.8s-79.6-188.8-177.8-188.8z" fill="#00B36A" /><path d="M108.3 458.9c-13.4 26.5-21 56.8-21 89 0 104.3 77.5 189.3 175.7 189.3 1.7 0 8.5-0.3 11.4-0.5h-1.3v-0.2c3.3 0 3.1 0.1 1.3 0.2l442.2 0.5c5.9 0 11.6-0.1 17.3-0.2l1.7-2.4c-366.3 0.1-563.1-40.3-627.3-275.7z" fill="#009957" /><path d="M333.9 876.6c-4 0-8.1-1.2-11.6-3.7-8.9-6.4-10.9-18.8-4.5-27.7l119-165.6c6.4-8.9 18.8-10.9 27.7-4.5 8.9 6.4 10.9 18.8 4.5 27.7L350 868.4c-3.8 5.3-9.9 8.2-16.1 8.2zM437.8 876.6c-4 0-8.1-1.2-11.6-3.7-8.9-6.4-10.9-18.8-4.5-27.7l119-165.6c6.4-8.9 18.8-10.9 27.7-4.5 8.9 6.4 10.9 18.8 4.5 27.7l-119 165.6c-3.8 5.3-9.9 8.2-16.1 8.2zM541.7 876.6c-4 0-8.1-1.2-11.6-3.7-8.9-6.4-10.9-18.8-4.5-27.7l119-165.6c6.4-8.9 18.8-10.9 27.7-4.5 8.9 6.4 10.9 18.8 4.5 27.7l-119 165.6c-3.8 5.3-9.9 8.2-16.1 8.2z" fill="" /><path d="M718.5 757.5c-11.9 0-21.5-9.6-21.5-21.5s9.6-21.5 21.5-21.5c6.2 0 12-0.1 18-0.3 6.4-0.1 13.1-0.3 20.4-0.3 86.2 0 156.3-75.1 156.3-167.3S843.1 379.3 757 379.3c-18 0-35.7 3.3-52.5 9.7l-26.7 10.2-2.5-28.5c-8.9-102.8-88.4-180.3-185.1-180.3-92.2 0-171.3 73.5-184.1 171l-2.7 20.8-20.9-2.2c-5.2-0.6-10.4-0.8-15.6-0.8-86.2 0-156.3 75.1-156.3 167.3 0 88.3 63 160.9 142.7 167.3l21.5-0.2h2.7c0.8 0 1.7 0.1 2.5 0.2l39.5 0.1c11.9 0 21.5 9.7 21.5 21.6 0 11.9-9.7 21.5-21.5 21.5l-42.6-0.1c-1.1 0.1-2.5 0.1-4 0.2-3.3 0.1-6.9 0.3-8.1 0.3-53.4 0-103.3-22.2-140.5-62.5-36.6-39.6-56.7-92.3-56.7-148.4 0-115.9 89.3-210.3 199.2-210.4 23.1-109 116-188.8 223.4-188.8 57.7 0 112.8 22.7 155.2 64 35.6 34.7 59.7 80 69.2 129.6 13.9-3.2 28-4.8 42.3-4.8 109.9 0 199.3 94.4 199.3 210.4s-89.4 210.4-199.3 210.4c-6.9 0-13.3 0.1-19.5 0.3-6.2 0.2-12.4 0.3-18.9 0.3z" fill="#1A1A1A" /></svg> -------------------------------------------------------------------------------- /assets/images/weather/sun.svg: -------------------------------------------------------------------------------- 1 | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 2 | <!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools --> 3 | <svg width="800px" height="800px" viewBox="-2.4 -2.4 28.80 28.80" fill="none" xmlns="http://www.w3.org/2000/svg"> 4 | <g id="SVGRepo_bgCarrier" stroke-width="0" transform="translate(8.04,8.04), scale(0.33)"> 5 | <rect x="-2.4" y="-2.4" width="28.80" height="28.80" rx="14.4" fill="#00B36A" strokewidth="0"/> 6 | </g> 7 | <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/> 8 | <g id="SVGRepo_iconCarrier"> <circle cx="12" cy="12" r="6" stroke="#00B36A" stroke-width="1.5"/> <path d="M12 2V3" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path d="M12 21V22" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path d="M22 12L21 12" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path d="M3 12L2 12" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path opacity="0.5" d="M19.0708 4.92969L18.678 5.32252" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path opacity="0.5" d="M5.32178 18.6777L4.92894 19.0706" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path opacity="0.5" d="M19.0708 19.0703L18.678 18.6775" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path opacity="0.5" d="M5.32178 5.32227L4.92894 4.92943" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> </g> 9 | </svg> -------------------------------------------------------------------------------- /assets/images/weather/sunny.svg: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> 3 | <svg width="800px" height="800px" viewBox="0 0 1024 1024" class="icon" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M513.5 515.7m-217.1 0a217.1 217.1 0 1 0 434.2 0 217.1 217.1 0 1 0-434.2 0Z" fill="#00B36A" /><path d="M513.5 732.8c48.5 0 93.3-15.9 129.5-42.8-178.2-12.6-294.8-111-334.1-246.9-8.1 22.7-12.5 47.1-12.5 72.6-0.1 119.9 97.1 217.1 217.1 217.1z" fill="#009957" /><path d="M511.9 750.3c-132 0-239.3-107.4-239.3-239.3S380 271.7 511.9 271.7c29.3 0 57.9 5.2 85.1 15.6 11.5 4.4 17.2 17.2 12.9 28.7-4.4 11.5-17.2 17.2-28.7 12.9-22.1-8.4-45.4-12.7-69.3-12.7C404.4 316.1 317 403.5 317 511s87.4 194.9 194.9 194.9S706.8 618.5 706.8 511c0-11.5-1-23.1-3-34.3-2.1-12.1 5.9-23.6 18-25.8 12.1-2.1 23.6 5.9 25.8 18 2.4 13.8 3.7 28 3.7 42.1-0.1 132-107.4 239.3-239.4 239.3z" fill="#1A1A1A" /><path d="M512.8 215.6c-14.1 0-25.7-11.6-25.7-25.7V92.4c0-14.1 11.6-25.7 25.7-25.7s25.7 11.6 25.7 25.7v97.5c0 14.1-11.5 25.7-25.7 25.7zM512.8 811.1c-14.1 0-25.7 11.6-25.7 25.7v97.5c0 14.1 11.6 25.7 25.7 25.7s25.7-11.6 25.7-25.7v-97.5c0-14.1-11.5-25.7-25.7-25.7zM807.3 510.1c0 14.1 11.6 25.7 25.7 25.7h97.5c14.1 0 25.7-11.6 25.7-25.7s-11.6-25.7-25.7-25.7H833c-14.1 0-25.7 11.5-25.7 25.7zM218 510.1c0 14.1-11.6 25.7-25.7 25.7H94.8c-14.1 0-25.7-11.6-25.7-25.7s11.6-25.7 25.7-25.7h97.5c14.2 0 25.7 11.5 25.7 25.7zM723.3 302.9c-10-10-10-26.3 0-36.3l56.6-56.6c10-10 26.3-10 36.3 0 10 10 10 26.3 0 36.3l-56.6 56.6c-9.9 10-26.3 10-36.3 0zM302 302.9c10-10 10-26.3 0-36.3L245.4 210c-10-10-26.3-10-36.3 0-10 10-10 26.3 0 36.3l56.6 56.6c10 10 26.3 10 36.3 0z" fill="#1A1A1A" /><path d="M723.3 717.6c-10 10-10 26.3 0 36.3l56.6 56.6c10 10 26.3 10 36.3 0 10-10 10-26.3 0-36.3l-56.6-56.6c-9.9-10-26.3-10-36.3 0z" fill="#1A1A1A" /><path d="M302 717.6c10 10 10 26.3 0 36.3l-56.6 56.6c-10 10-26.3 10-36.3 0-10-10-10-26.3 0-36.3l56.6-56.6c10-10 26.3-10 36.3 0z" fill="#1A1A1A" /></svg> -------------------------------------------------------------------------------- /assets/images/weather/wind.svg: -------------------------------------------------------------------------------- 1 | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 2 | <!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools --> 3 | <svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 4 | <g id="SVGRepo_bgCarrier" stroke-width="0"/> 5 | <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/> 6 | <g id="SVGRepo_iconCarrier"> <path d="M3 8H5M7 5.85714V5.5C7 4.11929 8.11929 3 9.5 3C10.8807 3 12 4.11929 12 5.5C12 6.88071 10.8807 8 9.5 8H8" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path d="M4 14H5M15 17V17.5C15 19.433 16.567 21 18.5 21C20.433 21 22 19.433 22 17.5C22 15.567 20.433 14 18.5 14H9" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> <path d="M2 11H8M15 8V7.5C15 5.567 16.567 4 18.5 4C20.433 4 22 5.567 22 7.5C22 9.433 20.433 11 18.5 11H12.25" stroke="#00B36A" stroke-width="1.5" stroke-linecap="round"/> </g> 7 | </svg> -------------------------------------------------------------------------------- /assets/map/map_dark_theme.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "elementType": "geometry", 4 | "stylers": [ 5 | { 6 | "color": "#242f3e" 7 | } 8 | ] 9 | }, 10 | { 11 | "elementType": "labels.text.fill", 12 | "stylers": [ 13 | { 14 | "color": "#746855" 15 | } 16 | ] 17 | }, 18 | { 19 | "elementType": "labels.text.stroke", 20 | "stylers": [ 21 | { 22 | "color": "#242f3e" 23 | } 24 | ] 25 | }, 26 | { 27 | "featureType": "administrative.locality", 28 | "elementType": "labels.text.fill", 29 | "stylers": [ 30 | { 31 | "color": "#d59563" 32 | } 33 | ] 34 | }, 35 | { 36 | "featureType": "poi", 37 | "elementType": "labels.text.fill", 38 | "stylers": [ 39 | { 40 | "color": "#d59563" 41 | } 42 | ] 43 | }, 44 | { 45 | "featureType": "poi.park", 46 | "elementType": "geometry", 47 | "stylers": [ 48 | { 49 | "color": "#263c3f" 50 | } 51 | ] 52 | }, 53 | { 54 | "featureType": "poi.park", 55 | "elementType": "labels.text.fill", 56 | "stylers": [ 57 | { 58 | "color": "#6b9a76" 59 | } 60 | ] 61 | }, 62 | { 63 | "featureType": "road", 64 | "elementType": "geometry", 65 | "stylers": [ 66 | { 67 | "color": "#38414e" 68 | } 69 | ] 70 | }, 71 | { 72 | "featureType": "road", 73 | "elementType": "geometry.stroke", 74 | "stylers": [ 75 | { 76 | "color": "#212a37" 77 | } 78 | ] 79 | }, 80 | { 81 | "featureType": "road", 82 | "elementType": "labels.text.fill", 83 | "stylers": [ 84 | { 85 | "color": "#9ca5b3" 86 | } 87 | ] 88 | }, 89 | { 90 | "featureType": "road.highway", 91 | "elementType": "geometry", 92 | "stylers": [ 93 | { 94 | "color": "#746855" 95 | } 96 | ] 97 | }, 98 | { 99 | "featureType": "road.highway", 100 | "elementType": "geometry.stroke", 101 | "stylers": [ 102 | { 103 | "color": "#1f2835" 104 | } 105 | ] 106 | }, 107 | { 108 | "featureType": "road.highway", 109 | "elementType": "labels.text.fill", 110 | "stylers": [ 111 | { 112 | "color": "#f3d19c" 113 | } 114 | ] 115 | }, 116 | { 117 | "featureType": "transit", 118 | "elementType": "geometry", 119 | "stylers": [ 120 | { 121 | "color": "#2f3948" 122 | } 123 | ] 124 | }, 125 | { 126 | "featureType": "transit.station", 127 | "elementType": "labels.text.fill", 128 | "stylers": [ 129 | { 130 | "color": "#d59563" 131 | } 132 | ] 133 | }, 134 | { 135 | "featureType": "water", 136 | "elementType": "geometry", 137 | "stylers": [ 138 | { 139 | "color": "#17263c" 140 | } 141 | ] 142 | }, 143 | { 144 | "featureType": "water", 145 | "elementType": "labels.text.fill", 146 | "stylers": [ 147 | { 148 | "color": "#515c6d" 149 | } 150 | ] 151 | }, 152 | { 153 | "featureType": "water", 154 | "elementType": "labels.text.stroke", 155 | "stylers": [ 156 | { 157 | "color": "#17263c" 158 | } 159 | ] 160 | } 161 | ] -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>CFBundleDevelopmentRegion</key> 6 | <string>en</string> 7 | <key>CFBundleExecutable</key> 8 | <string>App</string> 9 | <key>CFBundleIdentifier</key> 10 | <string>io.flutter.flutter.app</string> 11 | <key>CFBundleInfoDictionaryVersion</key> 12 | <string>6.0</string> 13 | <key>CFBundleName</key> 14 | <string>App</string> 15 | <key>CFBundlePackageType</key> 16 | <string>FMWK</string> 17 | <key>CFBundleShortVersionString</key> 18 | <string>1.0</string> 19 | <key>CFBundleSignature</key> 20 | <string>????</string> 21 | <key>CFBundleVersion</key> 22 | <string>1.0</string> 23 | <key>MinimumOSVersion</key> 24 | <string>11.0</string> 25 | </dict> 26 | </plist> 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <Workspace 3 | version = "1.0"> 4 | <FileRef 5 | location = "self:"> 6 | </FileRef> 7 | </Workspace> 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>IDEDidComputeMac32BitWarning</key> 6 | <true/> 7 | </dict> 8 | </plist> 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>PreviewsEnabled</key> 6 | <false/> 7 | </dict> 8 | </plist> 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <Workspace 3 | version = "1.0"> 4 | <FileRef 5 | location = "group:Runner.xcodeproj"> 6 | </FileRef> 7 | </Workspace> 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>IDEDidComputeMac32BitWarning</key> 6 | <true/> 7 | </dict> 8 | </plist> 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>PreviewsEnabled</key> 6 | <false/> 7 | </dict> 8 | </plist> 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | import GoogleMaps 4 | 5 | @UIApplicationMain 6 | @objc class AppDelegate: FlutterAppDelegate { 7 | override func application( 8 | _ application: UIApplication, 9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 10 | ) -> Bool { 11 | GMSServices.provideAPIKey("AIzaSyB--blUyx_UMbgfkRczDQ-cLU4SDe6C8r4") 12 | GeneratedPluginRegistrant.register(with: self) 13 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 14 | } 15 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "AppIcon@2x.png", 5 | "idiom": "iphone", 6 | "scale": "2x", 7 | "size": "60x60" 8 | }, 9 | { 10 | "filename": "AppIcon@3x.png", 11 | "idiom": "iphone", 12 | "scale": "3x", 13 | "size": "60x60" 14 | }, 15 | { 16 | "filename": "AppIcon~ipad.png", 17 | "idiom": "ipad", 18 | "scale": "1x", 19 | "size": "76x76" 20 | }, 21 | { 22 | "filename": "AppIcon@2x~ipad.png", 23 | "idiom": "ipad", 24 | "scale": "2x", 25 | "size": "76x76" 26 | }, 27 | { 28 | "filename": "AppIcon-83.5@2x~ipad.png", 29 | "idiom": "ipad", 30 | "scale": "2x", 31 | "size": "83.5x83.5" 32 | }, 33 | { 34 | "filename": "AppIcon-40@2x.png", 35 | "idiom": "iphone", 36 | "scale": "2x", 37 | "size": "40x40" 38 | }, 39 | { 40 | "filename": "AppIcon-40@3x.png", 41 | "idiom": "iphone", 42 | "scale": "3x", 43 | "size": "40x40" 44 | }, 45 | { 46 | "filename": "AppIcon-40~ipad.png", 47 | "idiom": "ipad", 48 | "scale": "1x", 49 | "size": "40x40" 50 | }, 51 | { 52 | "filename": "AppIcon-40@2x~ipad.png", 53 | "idiom": "ipad", 54 | "scale": "2x", 55 | "size": "40x40" 56 | }, 57 | { 58 | "filename": "AppIcon-20@2x.png", 59 | "idiom": "iphone", 60 | "scale": "2x", 61 | "size": "20x20" 62 | }, 63 | { 64 | "filename": "AppIcon-20@3x.png", 65 | "idiom": "iphone", 66 | "scale": "3x", 67 | "size": "20x20" 68 | }, 69 | { 70 | "filename": "AppIcon-20~ipad.png", 71 | "idiom": "ipad", 72 | "scale": "1x", 73 | "size": "20x20" 74 | }, 75 | { 76 | "filename": "AppIcon-20@2x~ipad.png", 77 | "idiom": "ipad", 78 | "scale": "2x", 79 | "size": "20x20" 80 | }, 81 | { 82 | "filename": "AppIcon-29.png", 83 | "idiom": "iphone", 84 | "scale": "1x", 85 | "size": "29x29" 86 | }, 87 | { 88 | "filename": "AppIcon-29@2x.png", 89 | "idiom": "iphone", 90 | "scale": "2x", 91 | "size": "29x29" 92 | }, 93 | { 94 | "filename": "AppIcon-29@3x.png", 95 | "idiom": "iphone", 96 | "scale": "3x", 97 | "size": "29x29" 98 | }, 99 | { 100 | "filename": "AppIcon-29~ipad.png", 101 | "idiom": "ipad", 102 | "scale": "1x", 103 | "size": "29x29" 104 | }, 105 | { 106 | "filename": "AppIcon-29@2x~ipad.png", 107 | "idiom": "ipad", 108 | "scale": "2x", 109 | "size": "29x29" 110 | }, 111 | { 112 | "filename": "AppIcon-60@2x~car.png", 113 | "idiom": "car", 114 | "scale": "2x", 115 | "size": "60x60" 116 | }, 117 | { 118 | "filename": "AppIcon-60@3x~car.png", 119 | "idiom": "car", 120 | "scale": "3x", 121 | "size": "60x60" 122 | }, 123 | { 124 | "filename": "AppIcon~ios-marketing.png", 125 | "idiom": "ios-marketing", 126 | "scale": "1x", 127 | "size": "1024x1024" 128 | } 129 | ], 130 | "info": { 131 | "author": "iconkitchen", 132 | "version": 1 133 | } 134 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 | <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> 3 | <dependencies> 4 | <deployment identifier="iOS"/> 5 | <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> 6 | </dependencies> 7 | <scenes> 8 | <!--View Controller--> 9 | <scene sceneID="EHf-IW-A2E"> 10 | <objects> 11 | <viewController id="01J-lp-oVM" sceneMemberID="viewController"> 12 | <layoutGuides> 13 | <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/> 14 | <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/> 15 | </layoutGuides> 16 | <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> 17 | <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> 18 | <subviews> 19 | <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"> 20 | </imageView> 21 | </subviews> 22 | <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> 23 | <constraints> 24 | <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/> 25 | <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/> 26 | </constraints> 27 | </view> 28 | </viewController> 29 | <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> 30 | </objects> 31 | <point key="canvasLocation" x="53" y="375"/> 32 | </scene> 33 | </scenes> 34 | <resources> 35 | <image name="LaunchImage" width="168" height="185"/> 36 | </resources> 37 | </document> 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 | <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> 3 | <dependencies> 4 | <deployment identifier="iOS"/> 5 | <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> 6 | </dependencies> 7 | <scenes> 8 | <!--Flutter View Controller--> 9 | <scene sceneID="tne-QT-ifu"> 10 | <objects> 11 | <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController"> 12 | <layoutGuides> 13 | <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> 14 | <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> 15 | </layoutGuides> 16 | <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> 17 | <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> 18 | <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> 19 | <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> 20 | </view> 21 | </viewController> 22 | <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> 23 | </objects> 24 | </scene> 25 | </scenes> 26 | </document> 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>CFBundleDevelopmentRegion</key> 6 | <string>$(DEVELOPMENT_LANGUAGE)</string> 7 | <key>CFBundleDisplayName</key> 8 | <string>Clima</string> 9 | <key>CFBundleExecutable</key> 10 | <string>$(EXECUTABLE_NAME)</string> 11 | <key>CFBundleIdentifier</key> 12 | <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> 13 | <key>CFBundleInfoDictionaryVersion</key> 14 | <string>6.0</string> 15 | <key>CFBundleName</key> 16 | <string>clima</string> 17 | <key>CFBundlePackageType</key> 18 | <string>APPL</string> 19 | <key>CFBundleShortVersionString</key> 20 | <string>$(FLUTTER_BUILD_NAME)</string> 21 | <key>CFBundleSignature</key> 22 | <string>????</string> 23 | <key>CFBundleVersion</key> 24 | <string>$(FLUTTER_BUILD_NUMBER)</string> 25 | <key>LSRequiresIPhoneOS</key> 26 | <true/> 27 | <key>UILaunchStoryboardName</key> 28 | <string>LaunchScreen</string> 29 | <key>UIMainStoryboardFile</key> 30 | <string>Main</string> 31 | <key>UISupportedInterfaceOrientations</key> 32 | <array> 33 | <string>UIInterfaceOrientationPortrait</string> 34 | <string>UIInterfaceOrientationLandscapeLeft</string> 35 | <string>UIInterfaceOrientationLandscapeRight</string> 36 | </array> 37 | <key>UISupportedInterfaceOrientations~ipad</key> 38 | <array> 39 | <string>UIInterfaceOrientationPortrait</string> 40 | <string>UIInterfaceOrientationPortraitUpsideDown</string> 41 | <string>UIInterfaceOrientationLandscapeLeft</string> 42 | <string>UIInterfaceOrientationLandscapeRight</string> 43 | </array> 44 | <key>CADisableMinimumFrameDurationOnPhone</key> 45 | <true/> 46 | <key>UIApplicationSupportsIndirectInputEvents</key> 47 | <true/> 48 | </dict> 49 | </plist> 50 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /lib/app/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/app/bloc/bloc.dart'; 2 | import 'package:clima/app/widgets/widgets.dart'; 3 | import 'package:clima/core/navigation/navigation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | 7 | import '../features/landing_page/landing_screen.dart'; 8 | 9 | class MyApp extends StatelessWidget { 10 | const MyApp({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return MultiBlocProvider( 15 | providers: [ 16 | BlocProvider( 17 | create: (context) => LocationBloc()..add(GetLocationEvent()), 18 | ), 19 | BlocProvider( 20 | create: (context) => ThemeCubit(), 21 | ), 22 | ], 23 | child: BlocBuilder<ThemeCubit, ThemeData>( 24 | builder: (context, theme) { 25 | return MaterialApp( 26 | title: "Clima", 27 | theme: theme, 28 | debugShowCheckedModeBanner: false, 29 | navigatorKey: NavigationHelper.navigatorKey, 30 | onGenerateRoute: NavigationHelper.generateRoute, 31 | navigatorObservers: [NavigationHelper.routeObserver], 32 | home: BlocBuilder<LocationBloc, LocationState>( 33 | builder: (BuildContext context, LocationState state) { 34 | if (state is AskForLocationPermissionState) { 35 | return const WaitingPermissionWidget(); 36 | } else if (state is LocationPermissionDeniedState) { 37 | return const PermissionDeniedWidget(); 38 | } else if (state is LocationServiceDisabledState) { 39 | return const LocationServiceDisabledWidget(); 40 | } else if (state is FetchCurrentLocationState) { 41 | return const LandingScreen(); 42 | } else { 43 | return const Scaffold( 44 | body: Center( 45 | child: Text("Unexpected Error"), 46 | ), 47 | ); 48 | } 49 | }, 50 | ), 51 | ); 52 | }, 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/app/bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'location/location_bloc.dart'; 2 | export 'theme/theme_cubit.dart'; 3 | -------------------------------------------------------------------------------- /lib/app/bloc/location/location_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/helper/location_helper.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | 5 | part 'location_event.dart'; 6 | part 'location_state.dart'; 7 | 8 | class LocationBloc extends Bloc<LocationEvent, LocationState> { 9 | LocationBloc() : super(AskForLocationPermissionState()) { 10 | // get current location 11 | on<GetLocationEvent>(_getLocation); 12 | // re-request permission 13 | on<RetryPermissionEvent>(_reRequestPermission); 14 | // enable location service 15 | on<EnableLocationServiceEvent>(_enableLocationService); 16 | } 17 | _getLocation(GetLocationEvent event, Emitter<LocationState> emit) async { 18 | var locationHelper = LocationHelper.instance; 19 | bool locationService = await locationHelper.isLocationServiceEnabled(); 20 | if (locationService) { 21 | bool locationPermission = await locationHelper.checkPermission(); 22 | if (locationPermission) { 23 | var position = await locationHelper.getLatLong(); 24 | await locationHelper.getPositionDetails(currentPosition: position); 25 | emit(FetchCurrentLocationState()); 26 | } else { 27 | emit(LocationPermissionDeniedState()); 28 | } 29 | } else { 30 | emit(LocationServiceDisabledState()); 31 | } 32 | } 33 | 34 | _reRequestPermission( 35 | RetryPermissionEvent event, Emitter<LocationState> emit) async { 36 | await LocationHelper.instance.openAppSettings(); 37 | bool isAppSettingsOpened = 38 | await LocationHelper.instance.isAppSettingsOpens(); 39 | if (isAppSettingsOpened) { 40 | await _getLocation(GetLocationEvent(), emit); 41 | } else { 42 | emit(LocationPermissionDeniedState()); 43 | } 44 | } 45 | 46 | _enableLocationService( 47 | EnableLocationServiceEvent event, Emitter<LocationState> emit) async { 48 | await LocationHelper.instance.openLocationSettings(); 49 | bool isSettingsOpened = 50 | await LocationHelper.instance.isLocationSettingsOpens(); 51 | if (isSettingsOpened) { 52 | await _getLocation(GetLocationEvent(), emit); 53 | } else { 54 | emit(LocationServiceDisabledState()); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/app/bloc/location/location_event.dart: -------------------------------------------------------------------------------- 1 | part of 'location_bloc.dart'; 2 | 3 | abstract class LocationEvent extends Equatable { 4 | const LocationEvent(); 5 | List<Object> get props => []; 6 | } 7 | 8 | class GetLocationEvent extends LocationEvent {} 9 | 10 | class RetryPermissionEvent extends LocationEvent {} 11 | 12 | class EnableLocationServiceEvent extends LocationEvent {} 13 | -------------------------------------------------------------------------------- /lib/app/bloc/location/location_state.dart: -------------------------------------------------------------------------------- 1 | part of 'location_bloc.dart'; 2 | 3 | abstract class LocationState extends Equatable { 4 | const LocationState(); 5 | @override 6 | List<Object> get props => []; 7 | } 8 | 9 | class AskForLocationPermissionState extends LocationState {} 10 | 11 | class FetchCurrentLocationState extends LocationState {} 12 | 13 | class LocationPermissionDeniedState extends LocationState {} 14 | 15 | class LocationServiceDisabledState extends LocationState {} 16 | -------------------------------------------------------------------------------- /lib/app/bloc/theme/theme_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import '../../../core/utils/utils.dart'; 5 | 6 | part 'theme_state.dart'; 7 | 8 | class ThemeCubit extends Cubit<ThemeData> { 9 | ThemeCubit() : super(AppThemes.basic); 10 | 11 | void switchTheme(bool isDay) { 12 | if (isDay) { 13 | emit(AppThemes.light); 14 | } else { 15 | emit(AppThemes.dark); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/app/bloc/theme/theme_state.dart: -------------------------------------------------------------------------------- 1 | part of 'theme_cubit.dart'; 2 | 3 | @immutable 4 | abstract class ThemeState {} 5 | 6 | class ThemeInitial extends ThemeState {} 7 | -------------------------------------------------------------------------------- /lib/app/widgets/location_service_disabled.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import '../../../core/animations/play_lottie.dart'; 5 | import '../../core/common/common.dart'; 6 | import '../../core/utils/utils.dart'; 7 | import '../bloc/location/location_bloc.dart'; 8 | 9 | class LocationServiceDisabledWidget extends StatelessWidget { 10 | const LocationServiceDisabledWidget({ 11 | super.key, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | floatingActionButton: PrimaryButton( 18 | onTap: () => 19 | context.read<LocationBloc>().add(EnableLocationServiceEvent()), 20 | isBorder: true, 21 | color: AppColors.white, 22 | text: "Enable Location", 23 | ), 24 | floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, 25 | body: const Center( 26 | child: Padding( 27 | padding: EdgeInsets.all(16), 28 | child: Column( 29 | mainAxisAlignment: MainAxisAlignment.center, 30 | children: [ 31 | PlayLottie( 32 | lottie: AppLottie.locationService, 33 | isPlayBackAndForward: true), 34 | Text( 35 | "Hey! 🌦️ Boost your weather app experience—enable location services for hyper-local forecasts! 🌍📱", 36 | textAlign: TextAlign.center), 37 | ], 38 | ), 39 | ), 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/app/widgets/permission_denied_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import '../../core/animations/play_lottie.dart'; 5 | import '../../core/common/common.dart'; 6 | import '../../core/utils/utils.dart'; 7 | import '../bloc/location/location_bloc.dart'; 8 | 9 | class PermissionDeniedWidget extends StatelessWidget { 10 | const PermissionDeniedWidget({ 11 | super.key, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | floatingActionButton: PrimaryButton( 18 | onTap: () => context.read<LocationBloc>().add(RetryPermissionEvent()), 19 | isBorder: true, 20 | color: AppColors.white, 21 | text: "Location Permission", 22 | ), 23 | floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, 24 | body: const Center( 25 | child: Padding( 26 | padding: EdgeInsets.all(16), 27 | child: Column( 28 | mainAxisAlignment: MainAxisAlignment.center, 29 | children: [ 30 | PlayLottie( 31 | lottie: AppLottie.locationNotFound, 32 | isPlayBackAndForward: true), 33 | Text( 34 | "The app requires access to your location in order to provide accurate weather information.\n Please enable location services to enjoy the app's full functionality.", 35 | textAlign: TextAlign.center), 36 | ], 37 | ), 38 | ), 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/app/widgets/waiting_permission_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/animations/play_lottie.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../core/utils/utils.dart'; 5 | 6 | class WaitingPermissionWidget extends StatelessWidget { 7 | const WaitingPermissionWidget({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return const Scaffold( 12 | body: Center( 13 | child: Column( 14 | mainAxisAlignment: MainAxisAlignment.center, 15 | children: [ 16 | PlayLottie(lottie: AppLottie.fetchLocation), 17 | Text("Looking for your location"), 18 | ], 19 | ), 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/app/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'location_service_disabled.dart'; 2 | export 'permission_denied_widget.dart'; 3 | export 'waiting_permission_widget.dart'; 4 | -------------------------------------------------------------------------------- /lib/core/animations/button_animation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ButtonAnimation extends StatefulWidget { 4 | final VoidCallback onTap; 5 | final Widget child; 6 | const ButtonAnimation({required this.child, required this.onTap, super.key}); 7 | 8 | @override 9 | State<ButtonAnimation> createState() => _ButtonAnimationState(); 10 | } 11 | 12 | class _ButtonAnimationState extends State<ButtonAnimation> 13 | with SingleTickerProviderStateMixin { 14 | late AnimationController _controller; 15 | final Duration _animationDuration = const Duration(milliseconds: 300); 16 | final Tween<double> _tween = Tween<double>(begin: 1.0, end: 0.95); 17 | @override 18 | void initState() { 19 | _controller = AnimationController( 20 | vsync: this, 21 | duration: _animationDuration, 22 | )..addListener(() { 23 | setState(() {}); 24 | }); 25 | super.initState(); 26 | } 27 | 28 | @override 29 | void dispose() { 30 | _controller.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return GestureDetector( 37 | onTap: () { 38 | _controller.forward().then((_) { 39 | _controller.reverse(); 40 | }); 41 | widget.onTap(); 42 | }, 43 | child: ScaleTransition( 44 | scale: _tween.animate( 45 | CurvedAnimation( 46 | parent: _controller, 47 | curve: Curves.easeOut, 48 | reverseCurve: Curves.easeIn, 49 | ), 50 | ), 51 | child: widget.child), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/core/animations/fade_slide_animation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class FadeSlideAnimation extends StatelessWidget { 4 | final AnimationController controller; 5 | final Widget child; 6 | 7 | const FadeSlideAnimation({ 8 | super.key, 9 | required this.controller, 10 | required this.child, 11 | }); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return RepaintBoundary( 16 | child: AnimatedBuilder( 17 | animation: controller, 18 | builder: (context, child) { 19 | double animValue = controller.value * 2; 20 | animValue = (animValue.clamp(0.0, 1.0) * 2 - 1).abs(); 21 | 22 | return FadeTransition( 23 | opacity: Tween<double>(begin: 0.0, end: 1.0).animate( 24 | CurvedAnimation( 25 | parent: controller, 26 | curve: const Interval(0.0, 1.0, curve: Curves.easeInOut), 27 | ), 28 | ), 29 | child: SlideTransition( 30 | position: Tween<Offset>( 31 | begin: const Offset(0.0, 0.5), 32 | end: Offset.zero, 33 | ).animate( 34 | CurvedAnimation( 35 | parent: controller, 36 | curve: const Interval(0.0, 1.0, curve: Curves.easeInOut), 37 | ), 38 | ), 39 | child: child, 40 | ), 41 | ); 42 | }, 43 | child: child, 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/core/animations/play_lottie.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:lottie/lottie.dart'; 3 | 4 | class PlayLottie extends StatefulWidget { 5 | const PlayLottie( 6 | {super.key, 7 | required this.lottie, 8 | this.isPlayBackAndForward = false, 9 | this.repeat = false}); 10 | final String lottie; 11 | final bool isPlayBackAndForward; 12 | final bool repeat; 13 | @override 14 | State<PlayLottie> createState() => _PlayLottieState(); 15 | } 16 | 17 | class _PlayLottieState extends State<PlayLottie> with TickerProviderStateMixin { 18 | late final AnimationController _controller; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | _controller = AnimationController(vsync: this); 24 | if (widget.isPlayBackAndForward) { 25 | // Add a listener to handle animation status changes 26 | _controller.addStatusListener((status) { 27 | if (status == AnimationStatus.completed) { 28 | // When animation completes, reverse it after a delay 29 | Future.delayed(const Duration(seconds: 2), () { 30 | if (mounted) { 31 | _controller.reverse(); 32 | } 33 | }); 34 | } else if (status == AnimationStatus.dismissed) { 35 | // When animation is reversed completely, play it again 36 | if (mounted) { 37 | _controller.forward(); 38 | } 39 | } 40 | }); 41 | } 42 | 43 | if (widget.repeat) { 44 | _controller.addStatusListener((status) { 45 | if (status == AnimationStatus.completed) { 46 | // When animation completes, reverse it after a delay 47 | Future.delayed(const Duration(seconds: 2), () { 48 | if (mounted) { 49 | _controller.repeat(); 50 | } 51 | }); 52 | } else if (status == AnimationStatus.dismissed) { 53 | // When animation is reversed completely, play it again 54 | if (mounted) { 55 | _controller.forward(); 56 | } 57 | } 58 | }); 59 | } 60 | } 61 | 62 | @override 63 | Widget build(BuildContext context) { 64 | return Lottie.asset( 65 | widget.lottie, 66 | fit: BoxFit.cover, 67 | controller: _controller, 68 | onLoaded: (composition) { 69 | _controller.duration = composition.duration; 70 | _controller.forward(); 71 | }, 72 | ); 73 | } 74 | 75 | @override 76 | void dispose() { 77 | _controller.dispose(); 78 | super.dispose(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/core/common/common.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/utils/utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:lottie/lottie.dart'; 5 | 6 | import '../../features/home/cubit/home_cubit.dart'; 7 | import '../animations/button_animation.dart'; 8 | 9 | part 'failure_widget.dart'; 10 | part 'loading_widget.dart'; 11 | part 'primary_button.dart'; 12 | part 'temperature_text.dart'; 13 | part 'weather_image.dart'; 14 | -------------------------------------------------------------------------------- /lib/core/common/failure_widget.dart: -------------------------------------------------------------------------------- 1 | part of 'common.dart'; 2 | 3 | class FailureWidget extends StatelessWidget { 4 | const FailureWidget({ 5 | super.key, 6 | this.text, 7 | }); 8 | final String? text; 9 | @override 10 | Widget build(BuildContext context) { 11 | return Center( 12 | child: Column( 13 | mainAxisAlignment: MainAxisAlignment.center, 14 | children: [ 15 | Lottie.asset(AppLottie.failure), 16 | Text(text ?? "Error"), 17 | ], 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/core/common/loading_widget.dart: -------------------------------------------------------------------------------- 1 | part of 'common.dart'; 2 | 3 | class LoadingWidget extends StatelessWidget { 4 | const LoadingWidget({ 5 | super.key, 6 | }); 7 | @override 8 | Widget build(BuildContext context) { 9 | return Center( 10 | child: Column( 11 | mainAxisAlignment: MainAxisAlignment.center, 12 | children: [ 13 | Lottie.asset(AppLottie.loadingWeather), 14 | const Text("Loading..."), 15 | ], 16 | ), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/core/common/primary_button.dart: -------------------------------------------------------------------------------- 1 | part of 'common.dart'; 2 | 3 | class PrimaryButton extends StatelessWidget { 4 | final VoidCallback onTap; 5 | final String text; 6 | final double? width; 7 | final double? height; 8 | final double? borderRadius; 9 | final double? fontSize; 10 | final Color? color; 11 | final bool isBorder; 12 | const PrimaryButton({ 13 | required this.onTap, 14 | required this.text, 15 | this.height, 16 | this.width, 17 | this.borderRadius, 18 | this.isBorder = false, 19 | this.fontSize, 20 | this.color, 21 | Key? key, 22 | }) : super(key: key); 23 | @override 24 | Widget build(BuildContext context) { 25 | return ButtonAnimation( 26 | onTap: onTap, 27 | child: Container( 28 | margin: const EdgeInsets.symmetric(horizontal: 16), 29 | height: height ?? 55, 30 | alignment: Alignment.center, 31 | width: width ?? double.maxFinite, 32 | decoration: BoxDecoration( 33 | color: color ?? AppColors.primary, 34 | borderRadius: BorderRadius.circular(borderRadius ?? 12), 35 | border: isBorder ? Border.all(color: AppColors.primary) : null), 36 | child: Text( 37 | text, 38 | style: const TextStyle(color: AppColors.primary), 39 | ), 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/core/common/temperature_text.dart: -------------------------------------------------------------------------------- 1 | part of 'common.dart'; 2 | 3 | class TemperatureText extends StatelessWidget { 4 | const TemperatureText({ 5 | super.key, 6 | required this.temperature, 7 | this.style, 8 | }); 9 | 10 | final String? temperature; 11 | final TextStyle? style; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return RepaintBoundary( 16 | child: GradientText( 17 | "${temperature!}°", 18 | style: style ?? AppTypography.bold144(), 19 | gradient: context.read<HomeCubit>().isDay 20 | ? const LinearGradient( 21 | begin: Alignment.topCenter, 22 | end: Alignment.bottomCenter, 23 | stops: [0.5, 1.0], 24 | colors: [ 25 | AppColors.primary, 26 | AppColors.white, 27 | ], 28 | ) 29 | : const LinearGradient( 30 | begin: Alignment.bottomCenter, 31 | end: Alignment.topCenter, 32 | stops: [0.3, 1], 33 | colors: [ 34 | AppColors.white, 35 | AppColors.primary, 36 | ], 37 | ), 38 | ), 39 | ); 40 | } 41 | } 42 | 43 | class GradientText extends StatelessWidget { 44 | const GradientText( 45 | this.text, { 46 | super.key, 47 | required this.gradient, 48 | this.style, 49 | }); 50 | 51 | final String text; 52 | final TextStyle? style; 53 | final Gradient gradient; 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | return ShaderMask( 58 | blendMode: BlendMode.srcIn, 59 | shaderCallback: (bounds) => gradient.createShader( 60 | Rect.fromLTWH(0, 0, bounds.width, bounds.height), 61 | ), 62 | child: Text( 63 | text, 64 | style: style, 65 | overflow: TextOverflow.ellipsis, 66 | ), 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/core/enum/weather_state.dart: -------------------------------------------------------------------------------- 1 | enum WeatherState { 2 | Storm, 3 | Rain, 4 | Snow, 5 | Wind, 6 | Clear, 7 | Clouds, 8 | Unknown, 9 | } 10 | -------------------------------------------------------------------------------- /lib/core/extensions/map_weather_code_extensions.dart: -------------------------------------------------------------------------------- 1 | import '../enum/weather_state.dart'; 2 | import '../utils/utils.dart'; 3 | 4 | extension MapWeatherCode on int { 5 | WeatherState mapToWeatherState() { 6 | switch (this) { 7 | case 0: 8 | return WeatherState.Clear; 9 | case 1: 10 | case 2: 11 | case 3: 12 | case 45: 13 | case 48: 14 | return WeatherState.Clouds; 15 | case 51: 16 | case 53: 17 | case 55: 18 | case 56: 19 | case 57: 20 | return WeatherState.Rain; 21 | case 61: 22 | case 63: 23 | case 65: 24 | case 66: 25 | case 67: 26 | return WeatherState.Rain; 27 | case 71: 28 | case 73: 29 | case 75: 30 | case 77: 31 | return WeatherState.Snow; 32 | case 80: 33 | case 81: 34 | case 82: 35 | case 85: 36 | case 86: 37 | return WeatherState.Rain; 38 | case 95: 39 | return WeatherState.Storm; 40 | case 96: 41 | case 99: 42 | return WeatherState.Storm; 43 | default: 44 | return WeatherState.Unknown; 45 | } 46 | } 47 | } 48 | 49 | extension MapWeatherStateToImage on WeatherState { 50 | String getImages(bool isDay) { 51 | switch (this) { 52 | case WeatherState.Storm: 53 | return AppLottie.dailyStorm; 54 | case WeatherState.Rain: 55 | return isDay ? AppLottie.dailyDayRain : AppLottie.dailyNightRain; 56 | case WeatherState.Snow: 57 | return isDay ? AppLottie.dailyDaySnow : AppLottie.dailyNightSnow; 58 | case WeatherState.Wind: 59 | return AppLottie.dailyWind; 60 | case WeatherState.Clear: 61 | return isDay ? AppLottie.dailyDay : AppLottie.dailyNight; 62 | case WeatherState.Clouds: 63 | return isDay ? AppLottie.dailyDayCloud : AppLottie.dailyNightCloud; 64 | default: 65 | return AppLottie.dailyStorm; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/core/extensions/weather_message_extension.dart: -------------------------------------------------------------------------------- 1 | import '../enum/weather_state.dart'; 2 | 3 | extension WeatherStateExtension on WeatherState { 4 | String getMessage() { 5 | String comparisonMessage = ''; 6 | 7 | // 8 | // if (todayTemperature > yesterdayTemperature) { 9 | // comparisonMessage = 'It\'s a bit warmer than yesterday'; 10 | // } else if (todayTemperature < yesterdayTemperature) { 11 | // comparisonMessage = 'It\'s a bit cooler than yesterday'; 12 | // } else { 13 | // comparisonMessage = 'It\'s the same as yesterday'; 14 | // } 15 | 16 | switch (this) { 17 | case WeatherState.Storm: 18 | return 'There\'s a storm today! $comparisonMessage.'; 19 | case WeatherState.Rain: 20 | return 'It\'s raining today. Don\'t forget your umbrella! $comparisonMessage.'; 21 | case WeatherState.Snow: 22 | return 'It\'s a snowy day. Bundle up! $comparisonMessage.'; 23 | case WeatherState.Wind: 24 | return 'It\'s windy out there! $comparisonMessage.'; 25 | case WeatherState.Clear: 26 | return 'It\'s a clear day! $comparisonMessage.'; 27 | case WeatherState.Clouds: 28 | return 'It\'s a cloudy day. Be careful, it might rain! $comparisonMessage.'; 29 | case WeatherState.Unknown: 30 | return 'Unknown weather condition.'; 31 | default: 32 | return 'Unknown weather condition.'; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/core/helper/date_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | class DateHelper { 4 | static String formatDate(String inputDate, String format) { 5 | return DateFormat(format).format(DateTime.parse(inputDate)); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/core/helper/env_helper.dart: -------------------------------------------------------------------------------- 1 | abstract class EnvHelper { 2 | static const GOOGLE_MAPS_API = String.fromEnvironment('GOOGLE_MAPS_API'); 3 | static const NEWS_API_KEY = String.fromEnvironment('NEWS_API_KEY'); 4 | } 5 | -------------------------------------------------------------------------------- /lib/core/helper/functions.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/helper/lotte_cach_helper.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:wakelock_plus/wakelock_plus.dart'; 4 | 5 | import '../managers/managers.dart'; 6 | 7 | T isNotNull<T>(T variable) { 8 | return variable ?? -1 as T; 9 | } 10 | 11 | num calculateAverage(dynamic value1, dynamic value2) { 12 | return (isNotNull(value1) + isNotNull(value2)) ~/ 2; 13 | } 14 | 15 | bool isNull(dynamic object, String property) { 16 | List<String> propertyParts = property.split('.'); 17 | dynamic currentObject = object; 18 | 19 | for (String part in propertyParts) { 20 | if (currentObject == null) { 21 | return true; 22 | } 23 | 24 | if (currentObject is Map) { 25 | currentObject = currentObject[part]; 26 | } else if (currentObject is List) { 27 | int? index = int.tryParse(part); 28 | if (index != null && index >= 0 && index < currentObject.length) { 29 | currentObject = currentObject[index]; 30 | } else { 31 | return true; 32 | } 33 | } else { 34 | return true; 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | initialization() async { 41 | WidgetsFlutterBinding.ensureInitialized(); 42 | WakelockPlus.enable(); 43 | DependencyManager.init(); 44 | await Future.wait([ 45 | NotificationManager.init(), 46 | LottieCache.init(), 47 | ]); 48 | } 49 | -------------------------------------------------------------------------------- /lib/core/helper/lotte_cach_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/utils/utils.dart'; 2 | import 'package:lottie/lottie.dart'; 3 | 4 | class LottieCache { 5 | static final Map<String, LottieComposition> _compositions = {}; 6 | 7 | /// Caches the given [LottieAsset]s. 8 | 9 | static Future<void> add(String assetName) async { 10 | _compositions[assetName] = await AssetLottie(assetName).load(); 11 | } 12 | 13 | static Future<void> init() async { 14 | await add(AppLottie.fetchLocation); 15 | await add(AppLottie.locationNotFound); 16 | await add(AppLottie.noInternet); 17 | await add(AppLottie.loadingWeather); 18 | await add(AppLottie.failure); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/core/managers/dependency_manager.dart: -------------------------------------------------------------------------------- 1 | part of 'managers.dart'; 2 | 3 | class DependencyManager { 4 | static final GetIt _getIt = GetIt.instance; 5 | 6 | static void init() { 7 | _getIt.registerLazySingleton<HomeRepository>(() => HomeRepoImpl()); 8 | _getIt.registerLazySingleton<HourlyForecastRepository>( 9 | () => HourlyForecastRepoImpl()); 10 | _getIt.registerLazySingleton<DailyForecastRepository>( 11 | () => DailyForecastRepoImpl()); 12 | log("initialized Successfully", name: "Dependency Manager"); 13 | } 14 | 15 | static T get<T extends Object>() { 16 | return _getIt.get<T>(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/core/managers/managers.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:developer'; 3 | import 'dart:io'; 4 | 5 | import 'package:clima/core/helper/location_helper.dart'; 6 | import 'package:flutter/services.dart'; 7 | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 8 | import 'package:get_it/get_it.dart'; 9 | import 'package:path_provider/path_provider.dart'; 10 | import 'package:timezone/data/latest.dart' as tz; 11 | import 'package:timezone/timezone.dart' as tz; 12 | 13 | import '../../features/daily_forecast/data/repo/daily_forecast_repo.dart'; 14 | import '../../features/daily_forecast/data/repo/daily_forecast_repo_impl.dart'; 15 | import '../../features/home/data/repo/home_repo.dart'; 16 | import '../../features/home/data/repo/home_repo_impl.dart'; 17 | import '../../features/hourly_forecast/data/repo/hourly_forecast_repo.dart'; 18 | import '../../features/hourly_forecast/data/repo/hourly_forecast_repo_impl.dart'; 19 | 20 | part 'dependency_manager.dart'; 21 | part 'notification_manager.dart'; 22 | -------------------------------------------------------------------------------- /lib/core/managers/notification_manager.dart: -------------------------------------------------------------------------------- 1 | part of 'managers.dart'; 2 | 3 | class NotificationManager { 4 | static final FlutterLocalNotificationsPlugin 5 | _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); 6 | 7 | static Future<void> init() async { 8 | const AndroidInitializationSettings initializationSettingsAndroid = 9 | AndroidInitializationSettings('app_icon'); 10 | const InitializationSettings initializationSettings = 11 | InitializationSettings(android: initializationSettingsAndroid); 12 | await _flutterLocalNotificationsPlugin.initialize(initializationSettings); 13 | tz.initializeTimeZones(); 14 | log("initialized Successfully", name: "Notification Manager"); 15 | } 16 | 17 | static sendBasicNotification({ 18 | required String title, 19 | required String body, 20 | required String imageAssetPath, 21 | }) async { 22 | final tempDir = await getTemporaryDirectory(); 23 | final imagePath = '${tempDir.path}/image.webp'; 24 | // Copy the image from assets to the temporary directory 25 | ByteData data = await rootBundle.load(imageAssetPath); 26 | List<int> bytes = data.buffer.asUint8List(); 27 | await File(imagePath).writeAsBytes(bytes); 28 | NotificationDetails details = NotificationDetails( 29 | android: AndroidNotificationDetails( 30 | 'morning_channel_001', 31 | 'notification', 32 | importance: Importance.max, 33 | largeIcon: FilePathAndroidBitmap(imagePath), 34 | playSound: true, 35 | sound: const RawResourceAndroidNotificationSound('bubble'), 36 | priority: Priority.high, 37 | colorized: true, 38 | ), 39 | ); 40 | await _flutterLocalNotificationsPlugin.show( 41 | 1, 42 | title, 43 | body, 44 | details, 45 | payload: "payload data", 46 | ); 47 | } 48 | 49 | static Future<void> scheduleNotifications({ 50 | required String body, 51 | required String imageAssetPath, 52 | }) async { 53 | final tempDir = await getTemporaryDirectory(); 54 | final imagePath = '${tempDir.path}/image.webp'; 55 | // Copy the image from assets to the temporary directory 56 | ByteData data = await rootBundle.load(imageAssetPath); 57 | List<int> bytes = data.buffer.asUint8List(); 58 | await File(imagePath).writeAsBytes(bytes); 59 | final now = tz.TZDateTime.now(tz.local); 60 | final scheduledTime = tz.TZDateTime( 61 | tz.local, 62 | now.year, 63 | now.month, 64 | now.day, 65 | 6, 66 | 0, 67 | ); 68 | 69 | await _flutterLocalNotificationsPlugin.zonedSchedule( 70 | 001, 71 | "Today's weather in ${Location.instance.city}", 72 | body, 73 | scheduledTime, 74 | NotificationDetails( 75 | android: AndroidNotificationDetails( 76 | 'morning_channel_001', 77 | 'schedule notification', 78 | importance: Importance.max, 79 | largeIcon: FilePathAndroidBitmap(imagePath), 80 | playSound: true, 81 | sound: const RawResourceAndroidNotificationSound('bubble'), 82 | priority: Priority.high, 83 | colorized: true, 84 | ), 85 | ), 86 | androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, 87 | uiLocalNotificationDateInterpretation: 88 | UILocalNotificationDateInterpretation.absoluteTime, 89 | matchDateTimeComponents: DateTimeComponents.time, 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/core/navigation/app_routes.dart: -------------------------------------------------------------------------------- 1 | part of 'navigation.dart'; 2 | 3 | class AppRoute { 4 | static const String LOGIN = "LOGIN"; 5 | static const String ONBOARDING = "ONBOARDING"; 6 | static const String HOME = "HOME"; 7 | } 8 | -------------------------------------------------------------------------------- /lib/core/navigation/navigation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../app/app.dart'; 4 | 5 | part 'app_routes.dart'; 6 | part 'navigation_helper.dart'; 7 | -------------------------------------------------------------------------------- /lib/core/navigation/navigation_helper.dart: -------------------------------------------------------------------------------- 1 | part of 'navigation.dart'; 2 | 3 | class NavigationHelper { 4 | static final GlobalKey<NavigatorState> navigatorKey = 5 | GlobalKey<NavigatorState>(); 6 | 7 | static final RouteObserver<PageRoute> routeObserver = 8 | RouteObserver<PageRoute>(); 9 | 10 | static final GlobalKey<ScaffoldMessengerState> scaffoldState = 11 | GlobalKey<ScaffoldMessengerState>(); 12 | 13 | static Future<dynamic> navigateTo(String routeName, 14 | {Map<String, dynamic>? arguments, bool clean = false}) { 15 | if (!clean) { 16 | return navigatorKey.currentState! 17 | .pushNamed(routeName, arguments: arguments); 18 | } else { 19 | return navigatorKey.currentState! 20 | .pushReplacementNamed(routeName, arguments: arguments); 21 | } 22 | } 23 | 24 | static void goBack() { 25 | return navigatorKey.currentState?.pop(); 26 | } 27 | 28 | static Route<dynamic> generateRoute(RouteSettings settings) { 29 | switch (settings.name) { 30 | default: 31 | return MaterialPageRoute(builder: (_) => const MyApp()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/core/network/endpoints.dart: -------------------------------------------------------------------------------- 1 | part of 'network.dart'; 2 | 3 | const String WEATHER_BASE_URL = "https://api.open-meteo.com/v1"; 4 | const String NEWS_BASE_URL = "https://newsapi.org/v2"; 5 | 6 | abstract class EndPoints { 7 | static const String forecast = "/forecast"; 8 | static const String weatherNews = "/top-headlines"; 9 | } 10 | -------------------------------------------------------------------------------- /lib/core/network/error_handling.dart: -------------------------------------------------------------------------------- 1 | part of 'network.dart'; 2 | 3 | class ErrorHandler implements Exception { 4 | Failure? failure; 5 | 6 | ErrorHandler.handle(dynamic error) { 7 | if (error is DioException) { 8 | failure = _handleError(error); 9 | } else { 10 | failure = NetworkResponse.DEFAULT.getMessage(); 11 | } 12 | } 13 | } 14 | 15 | Failure _handleError(DioException error) { 16 | switch (error.type) { 17 | case DioExceptionType.connectionTimeout: 18 | return NetworkResponse.CONNECT_TIMEOUT.getMessage(); 19 | case DioExceptionType.sendTimeout: 20 | return NetworkResponse.SEND_TIMEOUT.getMessage(); 21 | case DioExceptionType.receiveTimeout: 22 | return NetworkResponse.RECIEVE_TIMEOUT.getMessage(); 23 | case DioExceptionType.badResponse: 24 | if (error.response != null && 25 | error.response?.statusCode != null && 26 | error.response?.statusMessage != null) { 27 | return Failure(error.response?.statusCode ?? 0, 28 | error.response?.data["message"] ?? ""); 29 | } else { 30 | return NetworkResponse.DEFAULT.getMessage(); 31 | } 32 | case DioExceptionType.cancel: 33 | return NetworkResponse.CANCEL.getMessage(); 34 | default: 35 | return NetworkResponse.DEFAULT.getMessage(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/core/network/network.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:pretty_dio_logger/pretty_dio_logger.dart'; 4 | 5 | part 'endpoints.dart'; 6 | part 'error_handling.dart'; 7 | part 'network_helper.dart'; 8 | part 'response/code.dart'; 9 | part 'response/extension.dart'; 10 | part 'response/failure.dart'; 11 | part 'response/message.dart'; 12 | part 'response/type.dart'; 13 | -------------------------------------------------------------------------------- /lib/core/network/network_helper.dart: -------------------------------------------------------------------------------- 1 | part of 'network.dart'; 2 | 3 | class NetworkHelper { 4 | late final Dio _dio; 5 | 6 | NetworkHelper._() : _dio = _configureDio(); 7 | 8 | static final NetworkHelper instance = NetworkHelper._(); 9 | 10 | factory NetworkHelper() => instance; 11 | 12 | static Dio _configureDio() { 13 | Dio dio = Dio( 14 | BaseOptions( 15 | baseUrl: WEATHER_BASE_URL, 16 | receiveDataWhenStatusError: true, 17 | connectTimeout: const Duration(seconds: 5), 18 | ), 19 | ); 20 | 21 | if (!kReleaseMode) { 22 | dio.interceptors.add(PrettyDioLogger( 23 | requestHeader: true, 24 | requestBody: true, 25 | responseHeader: true, 26 | )); 27 | } 28 | return dio; 29 | } 30 | 31 | Future<Response> get({ 32 | required String endPoint, 33 | Map<String, dynamic>? params, 34 | }) async { 35 | var response = await _dio.get(endPoint, queryParameters: params); 36 | return response; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/core/network/response/code.dart: -------------------------------------------------------------------------------- 1 | part of '../network.dart'; 2 | 3 | class ResponseCode { 4 | static const int SUCCESS = 200; 5 | static const int NO_CONTENT = 201; 6 | static const int BAD_REQUEST = 400; 7 | static const int UNAUTORISED = 401; 8 | static const int FORBIDDEN = 403; 9 | static const int INTERNAL_SERVER_ERROR = 500; 10 | static const int NOT_FOUND = 404; 11 | static const int CONNECT_TIMEOUT = -1; 12 | static const int CANCEL = -2; 13 | static const int RECIEVE_TIMEOUT = -3; 14 | static const int SEND_TIMEOUT = -4; 15 | static const int CACHE_ERROR = -5; 16 | static const int NO_INTERNET_CONNECTION = -6; 17 | static const int DEFAULT = -7; 18 | } 19 | -------------------------------------------------------------------------------- /lib/core/network/response/extension.dart: -------------------------------------------------------------------------------- 1 | part of '../network.dart'; 2 | 3 | extension NetworkResponseExtension on NetworkResponse { 4 | Failure getMessage() { 5 | switch (this) { 6 | case NetworkResponse.SUCCESS: 7 | return Failure(ResponseCode.SUCCESS, ResponseMessage.SUCCESS); 8 | case NetworkResponse.NO_CONTENT: 9 | return Failure(ResponseCode.NO_CONTENT, ResponseMessage.NO_CONTENT); 10 | case NetworkResponse.BAD_REQUEST: 11 | return Failure(ResponseCode.BAD_REQUEST, ResponseMessage.BAD_REQUEST); 12 | case NetworkResponse.FORBIDDEN: 13 | return Failure(ResponseCode.FORBIDDEN, ResponseMessage.FORBIDDEN); 14 | case NetworkResponse.UNAUTORISED: 15 | return Failure(ResponseCode.UNAUTORISED, ResponseMessage.UNAUTHORIZED); 16 | case NetworkResponse.NOT_FOUND: 17 | return Failure(ResponseCode.NOT_FOUND, ResponseMessage.NOT_FOUND); 18 | case NetworkResponse.INTERNAL_SERVER_ERROR: 19 | return Failure(ResponseCode.INTERNAL_SERVER_ERROR, 20 | ResponseMessage.INTERNAL_SERVER_ERROR); 21 | case NetworkResponse.CONNECT_TIMEOUT: 22 | return Failure( 23 | ResponseCode.CONNECT_TIMEOUT, ResponseMessage.CONNECT_TIMEOUT); 24 | case NetworkResponse.CANCEL: 25 | return Failure(ResponseCode.CANCEL, ResponseMessage.CANCEL); 26 | case NetworkResponse.RECIEVE_TIMEOUT: 27 | return Failure( 28 | ResponseCode.RECIEVE_TIMEOUT, ResponseMessage.RECEIVE_TIMEOUT); 29 | case NetworkResponse.SEND_TIMEOUT: 30 | return Failure(ResponseCode.SEND_TIMEOUT, ResponseMessage.SEND_TIMEOUT); 31 | case NetworkResponse.CACHE_ERROR: 32 | return Failure(ResponseCode.CACHE_ERROR, ResponseMessage.CACHE_ERROR); 33 | case NetworkResponse.NO_INTERNET_CONNECTION: 34 | return Failure(ResponseCode.NO_INTERNET_CONNECTION, 35 | ResponseMessage.NO_INTERNET_CONNECTION); 36 | case NetworkResponse.DEFAULT: 37 | return Failure(ResponseCode.DEFAULT, ResponseMessage.DEFAULT); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/core/network/response/failure.dart: -------------------------------------------------------------------------------- 1 | part of '../network.dart'; 2 | 3 | class Failure { 4 | int code; 5 | String message; 6 | 7 | Failure(this.code, this.message); 8 | } 9 | -------------------------------------------------------------------------------- /lib/core/network/response/message.dart: -------------------------------------------------------------------------------- 1 | part of '../network.dart'; 2 | 3 | class ResponseMessage { 4 | static const String SUCCESS = "Operation successful!"; 5 | static const String NO_CONTENT = 6 | "Operation successful, but no content found."; 7 | static const String BAD_REQUEST = 8 | "Invalid request. Please check your input and try again."; 9 | static const String UNAUTHORIZED = 10 | "You are not authorized to perform this action. Please log in and try again."; 11 | static const String FORBIDDEN = 12 | "Access forbidden. Please contact support for assistance."; 13 | static const String INTERNAL_SERVER_ERROR = 14 | "Oops! Something went wrong on our end. Please try again later."; 15 | static const String NOT_FOUND = 16 | "Requested resource not found. Please check the URL and try again."; 17 | static const String CONNECT_TIMEOUT = 18 | "Request timed out. Please check your internet connection and try again."; 19 | static const String CANCEL = "Request canceled."; 20 | static const String RECEIVE_TIMEOUT = 21 | "Data retrieval timed out. Please try again later."; 22 | static const String SEND_TIMEOUT = 23 | "Sending data timed out. Please try again later."; 24 | static const String CACHE_ERROR = 25 | "Error while fetching cached data. Please try again later."; 26 | static const String NO_INTERNET_CONNECTION = 27 | "No internet connection. Please connect to the internet and try again."; 28 | static const String DEFAULT = 29 | "An unexpected error occurred. Please try again."; 30 | } 31 | -------------------------------------------------------------------------------- /lib/core/network/response/type.dart: -------------------------------------------------------------------------------- 1 | part of '../network.dart'; 2 | 3 | enum NetworkResponse { 4 | SUCCESS, 5 | NO_CONTENT, 6 | BAD_REQUEST, 7 | FORBIDDEN, 8 | UNAUTORISED, 9 | NOT_FOUND, 10 | INTERNAL_SERVER_ERROR, 11 | CONNECT_TIMEOUT, 12 | CANCEL, 13 | RECIEVE_TIMEOUT, 14 | SEND_TIMEOUT, 15 | CACHE_ERROR, 16 | NO_INTERNET_CONNECTION, 17 | DEFAULT 18 | } 19 | -------------------------------------------------------------------------------- /lib/core/utils/app_colors.dart: -------------------------------------------------------------------------------- 1 | part of 'utils.dart'; 2 | 3 | class AppColors { 4 | static const Color primary = Color(0xFF111827); 5 | static const Color dayPrimary = Color(0xffF8F8F8); 6 | static const Color dayBorderPrimary = Color(0xffe7e7e7); 7 | static const Color secondary = Color(0xFF6B7280); 8 | static const Color basic = Color(0xFF121315); 9 | static const Color dark = Color(0xFF131417); 10 | static const Color black = Colors.black; 11 | // 12 | static const Color nightPrimary = Color(0xff0D131F); 13 | static const Color nightBorderPrimary = Color(0xff0A0F18); 14 | static const Color white = Colors.white; 15 | 16 | // 17 | static const Color clear = Color(0xFFFE8E26); 18 | static const Color cloud = Color(0xFF8EC1DD); 19 | static const Color snow = Color(0xFFF875AA); 20 | static const Color wind = Color(0xFFA2C579); 21 | static const Color rain = Color(0xFF00CD85); 22 | static const Color thunderstorm = Color(0xFFBF8EDD); 23 | } 24 | -------------------------------------------------------------------------------- /lib/core/utils/app_decoration.dart: -------------------------------------------------------------------------------- 1 | part of 'utils.dart'; 2 | 3 | class AppDecoration { 4 | static BoxDecoration container(BuildContext context, 5 | {bool isBordered = false}) { 6 | return BoxDecoration( 7 | borderRadius: BorderRadius.circular(10), 8 | color: Theme.of(context).focusColor, 9 | border: 10 | isBordered ? Border.all(color: Theme.of(context).cardColor) : null, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/core/utils/app_dimn.dart: -------------------------------------------------------------------------------- 1 | part of 'utils.dart'; 2 | 3 | double _width = 375; 4 | double _height = 812; 5 | 6 | /// A utility class for handling media queries in the application. 7 | class AppDimensions { 8 | /// Gets the current [MediaQueryData] instance from the navigator context. 9 | static MediaQueryData get _mediaQuery => 10 | MediaQuery.of(NavigationHelper.navigatorKey.currentContext!); 11 | 12 | /// Gets the width of the application window. 13 | static double get width => _mediaQuery.size.width; 14 | 15 | /// Gets the height of the application window. 16 | static double get height => _mediaQuery.size.height; 17 | 18 | /// Checks if the device is in landscape mode. 19 | static bool get isLandscape => 20 | _mediaQuery.orientation == Orientation.landscape; 21 | } 22 | 23 | /// An extension for handling dimensions with respect to the screen size for [double] values. 24 | extension DoubleDimensionUtilExtension on double { 25 | /// Converts the value to a width relative to the screen width. 26 | double get w => AppDimensions.width * this / _width; 27 | 28 | /// Converts the value to a height relative to the screen height. 29 | double get h => AppDimensions.height * this / _height; 30 | 31 | /// Converts the value to its proportional value based on a reference width and height. 32 | double get r => AppDimensions.isLandscape 33 | ? AppDimensions.width * this / _height 34 | : AppDimensions.width * this / _width; 35 | 36 | /// Converts the value to a scaled pixel value based on the screen width. 37 | double get sp => this * (AppDimensions.width / _width); 38 | } 39 | 40 | /// An extension for creating [EdgeInsets] with a specified value for [double] values. 41 | extension DoubleEdgeInsetsExtension on double { 42 | /// Creates [EdgeInsets] with the same value applied to all sides. 43 | EdgeInsets get allInsets => EdgeInsets.all(this); 44 | 45 | /// Creates [EdgeInsets] with the specified horizontal value. 46 | EdgeInsets get horizontalInsets => EdgeInsets.symmetric(horizontal: this); 47 | 48 | /// Creates [EdgeInsets] with the specified vertical value. 49 | EdgeInsets get verticalInsets => EdgeInsets.symmetric(vertical: this); 50 | } 51 | 52 | /// An extension for handling dimensions with respect to the screen size for [int] values. 53 | extension IntDimensionUtilExtension on int { 54 | /// Converts the value to a width relative to the screen width. 55 | double get w => toDouble() * (AppDimensions.width / 375); 56 | 57 | /// Converts the value to a height relative to the screen height. 58 | double get h => toDouble() * (AppDimensions.height / _height); 59 | 60 | /// Converts the value to its proportional value based on a reference width and height. 61 | double get r => AppDimensions.isLandscape 62 | ? toDouble() * (AppDimensions.width / _height) 63 | : toDouble() * (AppDimensions.width / _width); 64 | 65 | /// Converts the value to a scaled pixel value based on the screen width. 66 | double get sp => toDouble() * (AppDimensions.width / _width); 67 | } 68 | 69 | /// An extension for creating [EdgeInsets] with a specified value for int values. 70 | extension IntEdgeInsetsExtension on int { 71 | /// Creates [EdgeInsets] with the same value applied to all sides. 72 | EdgeInsets get allInsets => EdgeInsets.all(toDouble()); 73 | 74 | /// Creates [EdgeInsets] with the specified horizontal value. 75 | EdgeInsets get horizontalInsets => 76 | EdgeInsets.symmetric(horizontal: toDouble()); 77 | 78 | /// Creates [EdgeInsets] with the specified vertical value. 79 | EdgeInsets get verticalInsets => EdgeInsets.symmetric(vertical: toDouble()); 80 | } 81 | -------------------------------------------------------------------------------- /lib/core/utils/app_images.dart: -------------------------------------------------------------------------------- 1 | part of 'utils.dart'; 2 | 3 | class AppImages { 4 | static const String _weatherImagesPath = "assets/images/weather"; 5 | static const String windSvg = "$_weatherImagesPath/wind.svg"; 6 | static const String sunSvg = "$_weatherImagesPath/sun.svg"; 7 | static const String sunnySvg = "$_weatherImagesPath/sunny.svg"; 8 | static const String rainSvg = "$_weatherImagesPath/rain.svg"; 9 | static const String snowSvg = "$_weatherImagesPath/snow.svg"; 10 | static const String humiditySvg = "$_weatherImagesPath/humidity.svg"; 11 | // 12 | static const String daySun = "$_weatherImagesPath/Day Sun.webp"; 13 | static const String dayClouds = "$_weatherImagesPath/Day Clouds.webp"; 14 | static const String dayRain = "$_weatherImagesPath/Day Rain.webp"; 15 | static const String daySnow = "$_weatherImagesPath/Day Snow.webp"; 16 | static const String dayStorm = "$_weatherImagesPath/Day Storm.webp"; 17 | static const String dayWind = "$_weatherImagesPath/Day Wind.webp"; 18 | static const String dayBlur = "$_weatherImagesPath/day_blured.svg"; 19 | // 20 | static const String nightMoon = "$_weatherImagesPath/Night Moon.webp"; 21 | static const String nightClouds = "$_weatherImagesPath/Night Clouds.webp"; 22 | static const String nightRain = "$_weatherImagesPath/Night Rain.webp"; 23 | static const String nightSnow = "$_weatherImagesPath/Night Snow.webp"; 24 | static const String nightStorm = "$_weatherImagesPath/Night Storm.webp"; 25 | static const String nightWind = "$_weatherImagesPath/Night Wind.webp"; 26 | static const String nightBlur = "$_weatherImagesPath/night_blured.svg"; 27 | } 28 | 29 | class AppLottie { 30 | static const String _lottiePath = "assets/lottie"; 31 | // 32 | static const String fetchLocation = "$_lottiePath/location.json"; 33 | static const String locationService = "$_lottiePath/location_service.json"; 34 | static const String locationNotFound = "$_lottiePath/location_not_found.json"; 35 | static const String failure = "$_lottiePath/faliure.json"; 36 | static const String noInternet = "$_lottiePath/no_internet.json"; 37 | static const String loadingWeather = "$_lottiePath/loading_weather.json"; 38 | // 39 | static const String dailyNight = "$_lottiePath/night.json"; 40 | static const String dailyNightSnow = "$_lottiePath/night_snow.json"; 41 | static const String dailyNightRain = "$_lottiePath/night_rain.json"; 42 | static const String dailyNightCloud = "$_lottiePath/night_cloudy.json"; 43 | // 44 | static const String dailyDay = "$_lottiePath/sun.json"; 45 | static const String dailyDaySnow = "$_lottiePath/day_snow.json"; 46 | static const String dailyDayRain = "$_lottiePath/day_rain.json"; 47 | static const String dailyDayCloud = "$_lottiePath/day_cloudy.json"; 48 | // 49 | static const String dailyWind = "$_lottiePath/wind.json"; 50 | static const String dailyStorm = "$_lottiePath/storm.json"; 51 | } 52 | -------------------------------------------------------------------------------- /lib/core/utils/app_themes.dart: -------------------------------------------------------------------------------- 1 | part of 'utils.dart'; 2 | 3 | class AppThemes { 4 | static ThemeData light = ThemeData( 5 | scaffoldBackgroundColor: AppColors.white, 6 | primaryColor: AppColors.primary, 7 | useMaterial3: true, 8 | fontFamily: 'manrope', 9 | focusColor: AppColors.dayPrimary, 10 | cardColor: AppColors.dayBorderPrimary, 11 | brightness: Brightness.light, 12 | bottomNavigationBarTheme: BottomNavigationBarThemes.light, 13 | appBarTheme: AppBarThemes.basic, 14 | ); 15 | static ThemeData dark = ThemeData( 16 | scaffoldBackgroundColor: AppColors.primary, 17 | primaryColor: AppColors.white, 18 | brightness: Brightness.dark, 19 | useMaterial3: true, 20 | fontFamily: 'manrope', 21 | cardColor: AppColors.nightBorderPrimary, 22 | focusColor: AppColors.nightPrimary, 23 | bottomNavigationBarTheme: BottomNavigationBarThemes.dark, 24 | appBarTheme: AppBarThemes.basic, 25 | ); 26 | static ThemeData basic = ThemeData( 27 | scaffoldBackgroundColor: AppColors.basic, 28 | primaryColor: AppColors.white, 29 | useMaterial3: true, 30 | brightness: Brightness.dark, 31 | bottomNavigationBarTheme: BottomNavigationBarThemes.dark, 32 | ); 33 | } 34 | 35 | class BottomNavigationBarThemes { 36 | static BottomNavigationBarThemeData light = 37 | const BottomNavigationBarThemeData( 38 | backgroundColor: Colors.transparent, 39 | elevation: 0, 40 | selectedItemColor: AppColors.black, 41 | ); 42 | static BottomNavigationBarThemeData dark = const BottomNavigationBarThemeData( 43 | backgroundColor: Colors.transparent, 44 | elevation: 0, 45 | selectedItemColor: AppColors.white, 46 | unselectedItemColor: AppColors.secondary, 47 | ); 48 | } 49 | 50 | class AppBarThemes { 51 | static AppBarTheme basic = const AppBarTheme( 52 | backgroundColor: Colors.transparent, 53 | elevation: 0, 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /lib/core/utils/app_typography.dart: -------------------------------------------------------------------------------- 1 | part of 'utils.dart'; 2 | 3 | class AppTypography { 4 | // font weight 5 | static const FontWeight _w800 = FontWeight.w800; 6 | static const FontWeight _w500 = FontWeight.w500; 7 | 8 | // 144 9 | static TextStyle bold144({Color? color}) => 10 | TextStyle(fontSize: 144.sp, fontWeight: _w800, color: color); 11 | // 72 12 | static TextStyle bold72({Color? color}) => 13 | TextStyle(fontSize: 72.sp, fontWeight: _w800, color: color); 14 | // 56 15 | static TextStyle bold56({Color? color}) => 16 | TextStyle(fontSize: 56.sp, fontWeight: _w800, color: color); 17 | // 48 18 | static TextStyle bold48({Color? color}) => 19 | TextStyle(fontSize: 48.sp, fontWeight: _w800, color: color); 20 | // 24 21 | static TextStyle bold24({Color? color}) => 22 | TextStyle(fontSize: 24.sp, fontWeight: _w800, color: color); 23 | static TextStyle medium24({Color? color}) => 24 | TextStyle(fontSize: 24.sp, fontWeight: _w500, color: color); 25 | static TextStyle bold28({Color? color}) => 26 | TextStyle(fontSize: 28.sp, fontWeight: _w800, color: color); 27 | // 18 28 | static TextStyle medium18({Color? color}) => 29 | TextStyle(fontSize: 18.sp, fontWeight: _w500, color: color); 30 | // 14 31 | static TextStyle medium14({Color? color}) => 32 | TextStyle(fontSize: 14.sp, fontWeight: _w500, color: color); 33 | static TextStyle bold14({Color? color}) => 34 | TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold, color: color); 35 | 36 | // 12 37 | static TextStyle medium12({Color? color}) => 38 | TextStyle(fontSize: 12.sp, fontWeight: FontWeight.w400, color: color); 39 | 40 | static TextStyle thin14({Color? color}) => 41 | TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w300, color: color); 42 | } 43 | -------------------------------------------------------------------------------- /lib/core/utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../navigation/navigation.dart'; 4 | 5 | part 'app_colors.dart'; 6 | part 'app_decoration.dart'; 7 | part 'app_dimn.dart'; 8 | part 'app_images.dart'; 9 | part 'app_themes.dart'; 10 | part 'app_typography.dart'; 11 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/cubit/daily_forecast_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/extensions/map_weather_code_extensions.dart'; 2 | import 'package:clima/core/helper/functions.dart'; 3 | import 'package:clima/core/helper/location_helper.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:meta/meta.dart'; 6 | 7 | import '../data/models/daily_weather_model.dart'; 8 | import '../data/repo/daily_forecast_repo.dart'; 9 | 10 | part 'daily_forecast_state.dart'; 11 | 12 | class DailyForecastCubit extends Cubit<DailyForecastState> { 13 | final DailyForecastRepository _repository; 14 | 15 | DailyForecastCubit(this._repository) : super(DailyForecastInitial()); 16 | 17 | fetchDailyData() async { 18 | var result = await _repository.fetchWeatherData( 19 | Location.instance.position!.latitude, 20 | Location.instance.position!.longitude, 21 | ); 22 | emit(DailyForecastInitial()); 23 | result.fold( 24 | (failure) => emit(DailyForecastFailure(failure.message)), 25 | (response) { 26 | emit( 27 | DailyForecastSuccess( 28 | dailyWeather: DailyWeatherModel( 29 | temperature: List.generate( 30 | response.temperature2mMin.length, 31 | (index) => calculateAverage( 32 | response.temperature2mMin[index], 33 | response.temperature2mMax[index], 34 | )), 35 | apparentTemperature: List.generate( 36 | response.apparentTemperatureMax.length, 37 | (index) => calculateAverage( 38 | response.apparentTemperatureMax[index], 39 | response.apparentTemperatureMin[index], 40 | )), 41 | image: List.generate( 42 | response.weatherCode.length, 43 | (index) => (response.weatherCode[index] ?? -1) 44 | .mapToWeatherState() 45 | .getImages(false), 46 | ), 47 | uvIndexMax: response.uvIndexMax 48 | .map((uvIndex) => isNotNull(uvIndex)) 49 | .toList(), 50 | windSpeed10mMax: response.windSpeed10mMax 51 | .map((windSpeed) => isNotNull(windSpeed)) 52 | .toList(), 53 | rainSum: response.rainSum 54 | .map((rainSum) => isNotNull(rainSum)) 55 | .toList(), 56 | snowfallSum: response.snowfallSum 57 | .map((snowfallSum) => isNotNull(snowfallSum)) 58 | .toList(), 59 | time: response.time.map((data) => isNotNull(data)).toList(), 60 | ), 61 | ), 62 | ); 63 | }, 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/cubit/daily_forecast_state.dart: -------------------------------------------------------------------------------- 1 | part of 'daily_forecast_cubit.dart'; 2 | 3 | @immutable 4 | abstract class DailyForecastState {} 5 | 6 | class DailyForecastInitial extends DailyForecastState {} 7 | 8 | class DailyForecastSuccess extends DailyForecastState { 9 | final DailyWeatherModel dailyWeather; 10 | 11 | DailyForecastSuccess({required this.dailyWeather}); 12 | } 13 | 14 | class DailyForecastFailure extends DailyForecastState { 15 | final String message; 16 | 17 | DailyForecastFailure(this.message); 18 | } 19 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/data/models/daily_weather_model.dart: -------------------------------------------------------------------------------- 1 | class DailyWeatherModel { 2 | List<String?> time; 3 | List<String?> image; 4 | List<num?> temperature; 5 | List<num?> apparentTemperature; 6 | List<num?> uvIndexMax; 7 | List<num?> rainSum; 8 | List<num?> snowfallSum; 9 | List<num?> windSpeed10mMax; 10 | 11 | DailyWeatherModel({ 12 | required this.time, 13 | required this.image, 14 | required this.temperature, 15 | required this.apparentTemperature, 16 | required this.uvIndexMax, 17 | required this.rainSum, 18 | required this.snowfallSum, 19 | required this.windSpeed10mMax, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/data/models/open_meteo_daily_response.dart: -------------------------------------------------------------------------------- 1 | class OpenMeteoDailyResponse { 2 | List<String?> time; 3 | List<int?> weatherCode; 4 | List<num?> temperature2mMax; 5 | List<num?> temperature2mMin; 6 | List<num?> apparentTemperatureMax; 7 | List<num?> apparentTemperatureMin; 8 | List<num?> uvIndexMax; 9 | List<num?> rainSum; 10 | List<num?> snowfallSum; 11 | List<num?> windSpeed10mMax; 12 | 13 | OpenMeteoDailyResponse({ 14 | required this.time, 15 | required this.weatherCode, 16 | required this.temperature2mMax, 17 | required this.temperature2mMin, 18 | required this.apparentTemperatureMax, 19 | required this.apparentTemperatureMin, 20 | required this.uvIndexMax, 21 | required this.rainSum, 22 | required this.snowfallSum, 23 | required this.windSpeed10mMax, 24 | }); 25 | 26 | factory OpenMeteoDailyResponse.fromJson(Map<String, dynamic> json) { 27 | return OpenMeteoDailyResponse( 28 | time: List<String?>.from(json['daily']['time']), 29 | weatherCode: List<int?>.from(json['daily']['weather_code']), 30 | temperature2mMax: List<num?>.from(json['daily']['temperature_2m_max']), 31 | temperature2mMin: List<num?>.from(json['daily']['temperature_2m_min']), 32 | apparentTemperatureMax: 33 | List<num?>.from(json['daily']['apparent_temperature_max']), 34 | apparentTemperatureMin: 35 | List<num?>.from(json['daily']['apparent_temperature_min']), 36 | uvIndexMax: List<num?>.from(json['daily']['uv_index_max']), 37 | rainSum: List<num?>.from(json['daily']['rain_sum']), 38 | snowfallSum: List<num?>.from(json['daily']['snowfall_sum']), 39 | windSpeed10mMax: List<num?>.from(json['daily']['wind_speed_10m_max']), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/data/repo/daily_forecast_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:fpdart/fpdart.dart'; 2 | 3 | import '../../../../core/network/network.dart'; 4 | import '../models/open_meteo_daily_response.dart'; 5 | 6 | abstract class DailyForecastRepository { 7 | Future<Either<Failure, OpenMeteoDailyResponse>> fetchWeatherData( 8 | double? latitude, double? longitude); 9 | } 10 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/data/repo/daily_forecast_repo_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/features/daily_forecast/data/models/open_meteo_daily_response.dart'; 2 | import 'package:clima/features/daily_forecast/data/repo/daily_forecast_repo.dart'; 3 | import 'package:fpdart/fpdart.dart'; 4 | 5 | import '../../../../core/network/network.dart'; 6 | 7 | class DailyForecastRepoImpl extends DailyForecastRepository { 8 | @override 9 | Future<Either<Failure, OpenMeteoDailyResponse>> fetchWeatherData( 10 | double? latitude, double? longitude) async { 11 | try { 12 | var result = await NetworkHelper.instance.get( 13 | endPoint: EndPoints.forecast, 14 | params: { 15 | 'daily': 16 | 'weather_code,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,uv_index_max,rain_sum,snowfall_sum,wind_speed_10m_max', 17 | 'timezone': 'auto', 18 | 'forecast_days': 16, 19 | 'forecast_hours': 24, 20 | 'latitude': latitude, 21 | 'longitude': longitude 22 | }, 23 | ); 24 | return right(OpenMeteoDailyResponse.fromJson(result.data)); 25 | } catch (error) { 26 | return left(ErrorHandler.handle(error).failure!); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/screens/daily_forecast_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/common/common.dart'; 2 | import 'package:clima/features/daily_forecast/cubit/daily_forecast_cubit.dart'; 3 | import 'package:clima/features/daily_forecast/screens/widgets/widgets.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | 7 | class DailyForecastScreen extends StatelessWidget { 8 | const DailyForecastScreen({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return BlocBuilder<DailyForecastCubit, DailyForecastState>( 13 | builder: (context, state) { 14 | if (state is DailyForecastInitial) { 15 | return const LoadingWidget(); 16 | } else if (state is DailyForecastSuccess) { 17 | return DailyForecastWidget(weatherModel: state.dailyWeather); 18 | } else if (state is DailyForecastFailure) { 19 | return FailureWidget(text: state.message); 20 | } 21 | return const Center( 22 | child: Text("BANKAI"), 23 | ); 24 | }, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/screens/widgets/daily_forecast_widget.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class DailyForecastWidget extends StatefulWidget { 4 | const DailyForecastWidget({ 5 | super.key, 6 | required this.weatherModel, 7 | }); 8 | 9 | final DailyWeatherModel weatherModel; 10 | @override 11 | _DailyForecastWidgetState createState() => _DailyForecastWidgetState(); 12 | } 13 | 14 | class _DailyForecastWidgetState extends State<DailyForecastWidget> 15 | with SingleTickerProviderStateMixin { 16 | late AnimationController _controller; 17 | 18 | @override 19 | void initState() { 20 | super.initState(); 21 | _controller = AnimationController( 22 | vsync: this, 23 | duration: const Duration(seconds: 1), 24 | ); 25 | _controller.forward(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text("Weekly Forecast"), 33 | ), 34 | body: ListView.separated( 35 | itemCount: widget.weatherModel.image.length, 36 | padding: EdgeInsets.symmetric( 37 | vertical: 8.h, 38 | horizontal: 16.w, 39 | ), 40 | itemBuilder: (context, index) { 41 | return FadeSlideAnimation( 42 | controller: _controller, 43 | child: DailyWidget(weatherModel: widget.weatherModel, index: index), 44 | ); 45 | }, 46 | separatorBuilder: (BuildContext context, int index) => 47 | SizedBox(height: 16.h), 48 | ), 49 | ); 50 | } 51 | 52 | @override 53 | void dispose() { 54 | _controller.dispose(); 55 | super.dispose(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/screens/widgets/daily_widget.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class DailyWidget extends StatelessWidget { 4 | const DailyWidget({ 5 | super.key, 6 | required this.weatherModel, 7 | required this.index, 8 | }); 9 | final DailyWeatherModel weatherModel; 10 | final int index; 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container( 14 | height: 150.h, 15 | padding: 8.allInsets, 16 | decoration: AppDecoration.container(context, isBordered: true), 17 | child: Row( 18 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 19 | children: [ 20 | Expanded( 21 | child: Column( 22 | crossAxisAlignment: CrossAxisAlignment.start, 23 | children: [ 24 | Text( 25 | "${DateHelper.formatDate(weatherModel.time[index]!, 'EEEE')} • ${DateHelper.formatDate(weatherModel.time[index]!, 'MM-dd')}", 26 | style: AppTypography.medium18()), 27 | Expanded( 28 | child: Row( 29 | children: [ 30 | Expanded(child: Lottie.asset(weatherModel.image[index]!)), 31 | Expanded( 32 | child: Column( 33 | crossAxisAlignment: CrossAxisAlignment.start, 34 | children: [ 35 | Expanded( 36 | child: FittedBox( 37 | fit: BoxFit.scaleDown, 38 | child: TemperatureText( 39 | temperature: 40 | "${weatherModel.temperature[index]}"), 41 | ), 42 | ), 43 | Text( 44 | "feels like ${weatherModel.apparentTemperature[index]}"), 45 | ], 46 | ), 47 | ), 48 | ], 49 | ), 50 | ), 51 | ], 52 | ), 53 | ), 54 | Column( 55 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 56 | crossAxisAlignment: CrossAxisAlignment.start, 57 | children: [ 58 | DataWidget( 59 | apparentTemperature: 60 | "${weatherModel.windSpeed10mMax[index]}km/h", 61 | icon: AppImages.windSvg), 62 | DataWidget( 63 | apparentTemperature: "${weatherModel.snowfallSum[index]}%", 64 | icon: AppImages.snowSvg), 65 | DataWidget( 66 | apparentTemperature: "${weatherModel.rainSum[index]}%", 67 | icon: AppImages.rainSvg), 68 | DataWidget( 69 | apparentTemperature: "${weatherModel.rainSum[index]}°", 70 | icon: AppImages.sunnySvg), 71 | ], 72 | ), 73 | const SizedBox(width: 16), 74 | ], 75 | ), 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/screens/widgets/date_widget.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class DataWidget extends StatelessWidget { 4 | const DataWidget({ 5 | super.key, 6 | required this.apparentTemperature, 7 | required this.icon, 8 | }); 9 | 10 | final String apparentTemperature; 11 | final String icon; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Row( 16 | children: [ 17 | SvgPicture.asset( 18 | icon, 19 | width: AppDimensions.width! * 0.07, 20 | fit: BoxFit.cover, 21 | ), 22 | const SizedBox(width: 5), 23 | Text( 24 | apparentTemperature, 25 | style: AppTypography.medium12(), 26 | ), 27 | ], 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/features/daily_forecast/screens/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/utils/utils.dart'; 2 | import 'package:clima/features/daily_forecast/data/models/daily_weather_model.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_svg/svg.dart'; 5 | import 'package:lottie/lottie.dart'; 6 | 7 | import '../../../../core/animations/fade_slide_animation.dart'; 8 | import '../../../../core/common/common.dart'; 9 | import '../../../../core/helper/date_helper.dart'; 10 | 11 | part 'daily_forecast_widget.dart'; 12 | part 'daily_widget.dart'; 13 | part 'date_widget.dart'; 14 | -------------------------------------------------------------------------------- /lib/features/home/cubit/home_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/extensions/map_weather_code_extensions.dart'; 2 | import 'package:clima/core/helper/date_helper.dart'; 3 | import 'package:clima/core/helper/location_helper.dart'; 4 | import 'package:clima/core/utils/utils.dart'; 5 | import 'package:clima/features/home/data/model/weather_model.dart'; 6 | import 'package:clima/features/home/data/model/weather_theme.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_bloc/flutter_bloc.dart'; 9 | 10 | import '../data/repo/home_repo.dart'; 11 | 12 | part 'home_state.dart'; 13 | 14 | class HomeCubit extends Cubit<HomeState> { 15 | final HomeRepository _repository; 16 | bool isDay = false; 17 | WeatherTheme? theme; 18 | HomeCubit(this._repository) : super(HomeLoadingState()); 19 | fetchWeatherData() async { 20 | var result = await _repository.fetchCurrentWeather( 21 | Location.instance.position?.latitude, 22 | Location.instance.position?.longitude, 23 | ); 24 | result.fold( 25 | (failure) => emit(HomeErrorState(error: failure.message)), 26 | (response) { 27 | isDay = response.isDay == 1 ? true : false; 28 | theme = WeatherTheme.mapWeatherStateToTheme( 29 | response.weatherCode.mapToWeatherState(), isDay); 30 | emit(HomeSuccessState( 31 | weather: WeatherData( 32 | // to modify the date to appear as 'month, day, year' 33 | date: DateHelper.formatDate(response.time, 'yMMMMd'), 34 | temperature: response.temperature.ceil(), 35 | // contains the image and text color based on it's a day or night 36 | theme: theme!, 37 | weatherState: response.weatherCode.mapToWeatherState(), 38 | isDay: isDay), 39 | )); 40 | }, 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/features/home/cubit/home_state.dart: -------------------------------------------------------------------------------- 1 | part of 'home_cubit.dart'; 2 | 3 | @immutable 4 | abstract class HomeState {} 5 | 6 | class HomeInitialState extends HomeState {} 7 | 8 | class HomeLoadingState extends HomeState { 9 | final String lottie = AppLottie.loadingWeather; 10 | final String text = "Loading..."; 11 | } 12 | 13 | class HomeSuccessState extends HomeState { 14 | final WeatherData weather; 15 | HomeSuccessState({required this.weather}); 16 | } 17 | 18 | class HomeErrorState extends HomeState { 19 | final String lottie = AppLottie.failure; 20 | final String error; 21 | 22 | HomeErrorState({required this.error}); 23 | } 24 | -------------------------------------------------------------------------------- /lib/features/home/data/model/weather.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | class Weather { 4 | String temperature; 5 | String date; 6 | String image; 7 | Color textColor; 8 | String weatherState; 9 | String cityName; 10 | 11 | Weather(this.temperature, this.date, this.image, this.textColor, 12 | this.weatherState, this.cityName); 13 | } 14 | -------------------------------------------------------------------------------- /lib/features/home/data/model/weather_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/enum/weather_state.dart'; 2 | import 'package:clima/features/home/data/model/weather_theme.dart'; 3 | 4 | class OpenMeteoCurrentResponse { 5 | String time; 6 | num temperature; 7 | int isDay; 8 | int weatherCode; 9 | 10 | OpenMeteoCurrentResponse({ 11 | required this.time, 12 | required this.temperature, 13 | required this.isDay, 14 | required this.weatherCode, 15 | }); 16 | 17 | factory OpenMeteoCurrentResponse.fromJson(Map<String, dynamic> json) { 18 | return OpenMeteoCurrentResponse( 19 | time: json['current']['time'], 20 | temperature: json['current']['temperature_2m'], 21 | isDay: json['current']['is_day'], 22 | weatherCode: json['current']['weather_code'], 23 | ); 24 | } 25 | } 26 | 27 | class WeatherData { 28 | final String date; 29 | final num temperature; 30 | final WeatherState weatherState; 31 | final WeatherTheme theme; 32 | final bool isDay; 33 | 34 | WeatherData({ 35 | required this.date, 36 | required this.temperature, 37 | required this.weatherState, 38 | required this.theme, 39 | required this.isDay, 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /lib/features/home/data/model/weather_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/utils/utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../../../core/enum/weather_state.dart'; 5 | 6 | class WeatherTheme { 7 | final String image; 8 | final Color textColor; 9 | 10 | WeatherTheme({ 11 | required this.image, 12 | required this.textColor, 13 | }); 14 | 15 | factory WeatherTheme.mapWeatherStateToTheme(WeatherState state, bool isDay) { 16 | String getImageBasedOnDayTime(String dayImage, String nightImage) { 17 | return isDay ? dayImage : nightImage; 18 | } 19 | 20 | switch (state) { 21 | case WeatherState.Storm: 22 | return WeatherTheme( 23 | image: 24 | getImageBasedOnDayTime(AppImages.dayStorm, AppImages.nightStorm), 25 | textColor: AppColors.thunderstorm, 26 | ); 27 | case WeatherState.Rain: 28 | return WeatherTheme( 29 | image: getImageBasedOnDayTime(AppImages.dayRain, AppImages.nightRain), 30 | textColor: AppColors.rain, 31 | ); 32 | case WeatherState.Snow: 33 | return WeatherTheme( 34 | image: getImageBasedOnDayTime(AppImages.daySnow, AppImages.nightSnow), 35 | textColor: AppColors.snow, 36 | ); 37 | case WeatherState.Wind: 38 | return WeatherTheme( 39 | image: getImageBasedOnDayTime(AppImages.dayWind, AppImages.nightWind), 40 | textColor: AppColors.wind, 41 | ); 42 | case WeatherState.Clear: 43 | return WeatherTheme( 44 | image: getImageBasedOnDayTime(AppImages.daySun, AppImages.nightMoon), 45 | textColor: AppColors.clear, 46 | ); 47 | case WeatherState.Clouds: 48 | return WeatherTheme( 49 | image: getImageBasedOnDayTime( 50 | AppImages.dayClouds, AppImages.nightClouds), 51 | textColor: AppColors.cloud, 52 | ); 53 | case WeatherState.Unknown: 54 | // TODO: Add a custom image in case anything goes wrong. 55 | throw 0; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/features/home/data/repo/home_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:fpdart/fpdart.dart'; 2 | 3 | import '../../../../core/network/network.dart'; 4 | import '../model/weather_model.dart'; 5 | 6 | abstract class HomeRepository { 7 | Future<Either<Failure, OpenMeteoCurrentResponse>> fetchCurrentWeather( 8 | double? latitude, double? longitude); 9 | } 10 | -------------------------------------------------------------------------------- /lib/features/home/data/repo/home_repo_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/network/network.dart'; 2 | import 'package:clima/features/home/data/model/weather_model.dart'; 3 | import 'package:clima/features/home/data/repo/home_repo.dart'; 4 | import 'package:fpdart/fpdart.dart'; 5 | 6 | class HomeRepoImpl extends HomeRepository { 7 | @override 8 | Future<Either<Failure, OpenMeteoCurrentResponse>> fetchCurrentWeather( 9 | double? latitude, double? longitude) async { 10 | try { 11 | var result = await NetworkHelper.instance.get( 12 | endPoint: EndPoints.forecast, 13 | params: { 14 | 'current': 'temperature_2m,is_day,weather_code', 15 | 'timezone': 'auto', 16 | 'forecast_days': 1, 17 | 'forecast_hours': 1, 18 | 'latitude': latitude, 19 | 'longitude': longitude, 20 | }, 21 | ); 22 | return right(OpenMeteoCurrentResponse.fromJson(result.data)); 23 | } catch (e) { 24 | return left(ErrorHandler.handle(e).failure!); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/features/home/screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/app/bloc/theme/theme_cubit.dart'; 2 | import 'package:clima/core/common/common.dart'; 3 | import 'package:clima/features/home/cubit/home_cubit.dart'; 4 | import 'package:clima/features/home/screens/widgets/widgets.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_bloc/flutter_bloc.dart'; 7 | 8 | class HomeScreen extends StatelessWidget { 9 | const HomeScreen({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return BlocConsumer<HomeCubit, HomeState>( 14 | listener: (context, state) async { 15 | if (state is HomeSuccessState) { 16 | BlocProvider.of<ThemeCubit>(context).switchTheme(state.weather.isDay); 17 | } 18 | }, 19 | builder: (context, state) { 20 | if (state is HomeLoadingState) { 21 | return const LoadingWidget(); 22 | } else if (state is HomeSuccessState) { 23 | return HomeWidget(weather: state.weather); 24 | } else if (state is HomeErrorState) { 25 | return FailureWidget(text: state.error); 26 | } else { 27 | return const Center( 28 | child: Text( 29 | "Bankai", 30 | style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold), 31 | ), 32 | ); 33 | } 34 | }, 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/features/home/screens/widgets/home_widget.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class HomeWidget extends StatelessWidget { 4 | const HomeWidget({ 5 | super.key, 6 | required this.weather, 7 | }); 8 | final WeatherData weather; 9 | @override 10 | Widget build(BuildContext context) { 11 | return Padding( 12 | padding: 24.horizontalInsets, 13 | child: Column( 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | Expanded( 17 | child: TimeAndLocation( 18 | date: weather.date, 19 | cityName: Location.instance.city, 20 | ), 21 | ), 22 | Expanded( 23 | flex: 2, 24 | child: WeatherImage( 25 | image: weather.theme.image, 26 | ), 27 | ), 28 | Expanded( 29 | flex: 2, 30 | child: WeatherStatus( 31 | temperature: weather.temperature.toString(), 32 | weatherState: weather.weatherState.name.toString(), 33 | textColor: weather.theme.textColor, 34 | ), 35 | ), 36 | ], 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/features/home/screens/widgets/time_and_location.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class TimeAndLocation extends StatelessWidget { 4 | const TimeAndLocation({ 5 | super.key, 6 | this.date, 7 | this.cityName, 8 | }); 9 | final String? date; 10 | final String? cityName; 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | mainAxisSize: MainAxisSize.min, 16 | children: [ 17 | SizedBox(height: 16.h), 18 | Row( 19 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 20 | children: [ 21 | Text( 22 | date!, 23 | style: AppTypography.medium18( 24 | color: AppColors.secondary, 25 | ), 26 | ), 27 | IconButton( 28 | onPressed: () { 29 | Navigator.push( 30 | context, 31 | MaterialPageRoute(builder: (context) => Map()), 32 | ); 33 | }, 34 | icon: const Icon(Iconsax.location), 35 | ), 36 | ], 37 | ), 38 | Expanded( 39 | child: FittedBox( 40 | fit: BoxFit.scaleDown, 41 | child: Text( 42 | cityName!, 43 | style: 44 | AppTypography.bold48(color: Theme.of(context).primaryColor), 45 | overflow: TextOverflow.ellipsis, 46 | ), 47 | ), 48 | ), 49 | ], 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/features/home/screens/widgets/weather_status.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class WeatherStatus extends StatelessWidget { 4 | const WeatherStatus({ 5 | super.key, 6 | required this.temperature, 7 | required this.weatherState, 8 | this.textColor, 9 | }); 10 | final String? temperature; 11 | final String? weatherState; 12 | final Color? textColor; 13 | @override 14 | Widget build(BuildContext context) { 15 | return Column( 16 | crossAxisAlignment: CrossAxisAlignment.start, 17 | children: [ 18 | TemperatureText(temperature: temperature), 19 | Text(weatherState!, 20 | style: AppTypography.bold28( 21 | color: textColor, 22 | )), 23 | ], 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/features/home/screens/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/helper/location_helper.dart'; 2 | import 'package:clima/core/utils/utils.dart'; 3 | import 'package:clima/features/home/data/model/weather_model.dart'; 4 | import 'package:clima/features/map/screens/map_screen.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:iconsax/iconsax.dart'; 7 | 8 | import '../../../../core/common/common.dart'; 9 | 10 | part 'home_widget.dart'; 11 | part 'time_and_location.dart'; 12 | part 'weather_status.dart'; 13 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/cubit/hourly_forecast_state.dart: -------------------------------------------------------------------------------- 1 | part of 'hourly_forecast_cubit.dart'; 2 | 3 | abstract class DetailedForecastState extends Equatable { 4 | const DetailedForecastState(); 5 | 6 | @override 7 | List<Object> get props => []; 8 | } 9 | 10 | class DetailedForecastInitial extends DetailedForecastState {} 11 | 12 | class DetailsForecastSuccess extends DetailedForecastState { 13 | final Daily dailyForecast; 14 | final WeatherHourly hourlyForecast; 15 | final WeatherNewsModel article; 16 | const DetailsForecastSuccess( 17 | {required this.dailyForecast, 18 | required this.hourlyForecast, 19 | required this.article}); 20 | } 21 | 22 | class HourlyForecastState extends DetailedForecastState { 23 | const HourlyForecastState(); 24 | } 25 | 26 | class DetailedForecastError extends DetailedForecastState { 27 | final String errorMessage; 28 | 29 | const DetailedForecastError(this.errorMessage); 30 | } 31 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/data/models/forecast_info_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/utils/utils.dart'; 2 | 3 | class ForecastInfoModel { 4 | final String title; 5 | final String image; 6 | final String content; 7 | final String prefix; 8 | 9 | ForecastInfoModel(this.title, this.image, this.content, this.prefix); 10 | } 11 | 12 | List<ForecastInfoModel> getForecastInfo({ 13 | required String uvIndex, 14 | required String humidity, 15 | required String wind, 16 | }) { 17 | List<ForecastInfoModel> forecastInfoList = [ 18 | ForecastInfoModel("UV index", AppImages.sunSvg, uvIndex, "°"), 19 | ForecastInfoModel("Humidity", AppImages.humiditySvg, humidity, "%"), 20 | ForecastInfoModel("Wind", AppImages.windSvg, wind, "km/h"), 21 | ]; 22 | return forecastInfoList; 23 | } 24 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/data/models/open_meteo_hourly_response_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/features/hourly_forecast/data/models/weather_daily_model.dart'; 2 | import 'package:clima/features/hourly_forecast/data/models/weather_hourly_model.dart'; 3 | 4 | class OpenMeteoHourlyResponse { 5 | WeatherHourly hourly; 6 | WeatherDaily daily; 7 | 8 | OpenMeteoHourlyResponse({ 9 | required this.hourly, 10 | required this.daily, 11 | }); 12 | 13 | factory OpenMeteoHourlyResponse.fromJson(Map<String, dynamic> json) => 14 | OpenMeteoHourlyResponse( 15 | hourly: WeatherHourly.fromJson(json["hourly"]), 16 | daily: WeatherDaily.fromJson(json["daily"]), 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/data/models/weather_daily_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/features/home/data/model/weather_theme.dart'; 2 | 3 | class WeatherDaily { 4 | final List<String> time; 5 | final List<num> temperature2mMax; 6 | final List<num> temperature2mMin; 7 | final List<num> apparentTemperatureMax; 8 | final List<num> apparentTemperatureMin; 9 | final List<String> sunrise; 10 | final List<String> sunset; 11 | final List<num> daylightDuration; 12 | final List<num> sunshineDuration; 13 | final List<num> uvIndexMax; 14 | final List<num?> rainSum; 15 | final List<num?> windSpeed; 16 | 17 | WeatherDaily( 18 | {required this.time, 19 | required this.temperature2mMax, 20 | required this.temperature2mMin, 21 | required this.apparentTemperatureMax, 22 | required this.apparentTemperatureMin, 23 | required this.sunrise, 24 | required this.sunset, 25 | required this.daylightDuration, 26 | required this.sunshineDuration, 27 | required this.uvIndexMax, 28 | required this.rainSum, 29 | required this.windSpeed}); 30 | 31 | factory WeatherDaily.fromJson(Map<String, dynamic> json) { 32 | return WeatherDaily( 33 | time: List<String>.from(json['time']), 34 | temperature2mMax: List<num>.from(json['temperature_2m_max']), 35 | temperature2mMin: List<num>.from(json['temperature_2m_min']), 36 | apparentTemperatureMax: List<num>.from(json['apparent_temperature_max']), 37 | apparentTemperatureMin: List<num>.from(json['apparent_temperature_min']), 38 | sunrise: List<String>.from(json['sunrise']), 39 | sunset: List<String>.from(json['sunset']), 40 | daylightDuration: List<num>.from(json['daylight_duration']), 41 | sunshineDuration: List<num>.from(json['sunshine_duration']), 42 | uvIndexMax: List<num>.from(json['uv_index_max']), 43 | rainSum: List<num>.from(json['rain_sum']), 44 | windSpeed: List<num>.from(json['wind_speed_10m_max']), 45 | ); 46 | } 47 | } 48 | 49 | class Daily { 50 | final String date; 51 | final num temperatureMax; 52 | final num temperatureMin; 53 | final num apparentTemperature; 54 | final String sunrise; 55 | final String sunset; 56 | final num daylightDuration; 57 | final num sunshineDuration; 58 | final num uvIndexMax; 59 | final num? humidity; 60 | final num? windSpeed; 61 | final WeatherTheme theme; 62 | 63 | Daily( 64 | {required this.date, 65 | required this.temperatureMax, 66 | required this.temperatureMin, 67 | required this.apparentTemperature, 68 | required this.sunrise, 69 | required this.sunset, 70 | required this.daylightDuration, 71 | required this.sunshineDuration, 72 | required this.uvIndexMax, 73 | required this.theme, 74 | required this.humidity, 75 | required this.windSpeed}); 76 | } 77 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/data/models/weather_hourly_model.dart: -------------------------------------------------------------------------------- 1 | class WeatherHourly { 2 | final List<String> time; 3 | final List<num> temperature; 4 | final List<int> humidity; 5 | final List<int> isDay; 6 | final List<int> weatherCode; 7 | 8 | List<String> image; 9 | 10 | WeatherHourly({ 11 | required this.time, 12 | required this.temperature, 13 | required this.humidity, 14 | required this.isDay, 15 | required this.weatherCode, 16 | required this.image, 17 | }); 18 | 19 | factory WeatherHourly.fromJson(Map<String, dynamic> json) { 20 | return WeatherHourly( 21 | time: List<String>.from(json['time']), 22 | temperature: List<double>.from(json['temperature_2m']), 23 | humidity: List<int>.from(json['relative_humidity_2m']), 24 | isDay: List<int>.from(json['is_day']), 25 | weatherCode: List<int>.from(json['weather_code']), 26 | image: [], 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/data/models/weather_news_model.dart: -------------------------------------------------------------------------------- 1 | class WeatherNewsModel { 2 | String? title; 3 | String? url; 4 | 5 | WeatherNewsModel({this.title, this.url}); 6 | 7 | factory WeatherNewsModel.fromJson(Map<String, dynamic> json) { 8 | return WeatherNewsModel( 9 | title: json['articles'][0]['title'], url: json['articles'][0]['url']); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/data/repo/hourly_forecast_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:fpdart/fpdart.dart'; 2 | 3 | import '../../../../core/network/network.dart'; 4 | import '../models/open_meteo_hourly_response_model.dart'; 5 | import '../models/weather_news_model.dart'; 6 | 7 | abstract class HourlyForecastRepository { 8 | Future<Either<Failure, OpenMeteoHourlyResponse>> fetchWeatherData( 9 | double? latitude, double? longitude); 10 | Future<Either<Failure, WeatherNewsModel>> fetchWeatherNews(); 11 | } 12 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/data/repo/hourly_forecast_repo_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/helper/env_helper.dart'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:fpdart/fpdart.dart'; 4 | 5 | import '../../../../core/network/network.dart'; 6 | import '../models/open_meteo_hourly_response_model.dart'; 7 | import '../models/weather_news_model.dart'; 8 | import 'hourly_forecast_repo.dart'; 9 | 10 | class HourlyForecastRepoImpl extends HourlyForecastRepository { 11 | @override 12 | Future<Either<Failure, OpenMeteoHourlyResponse>> fetchWeatherData( 13 | double? latitude, double? longitude) async { 14 | try { 15 | var result = await NetworkHelper.instance.get( 16 | endPoint: EndPoints.forecast, 17 | params: { 18 | 'hourly': 'temperature_2m,relative_humidity_2m,weather_code,is_day', 19 | 'daily': 20 | 'weather_code,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,sunrise,sunset,daylight_duration,sunshine_duration,uv_index_max,rain_sum,wind_speed_10m_max', 21 | 'timezone': 'auto', 22 | 'forecast_days': 1, 23 | 'forecast_hours': 24, 24 | 'latitude': latitude, 25 | 'longitude': longitude, 26 | }, 27 | ); 28 | final data = OpenMeteoHourlyResponse.fromJson(result.data); 29 | return right(data); 30 | } catch (e) { 31 | return left(ErrorHandler.handle(e).failure!); 32 | } 33 | } 34 | 35 | @override 36 | Future<Either<Failure, WeatherNewsModel>> fetchWeatherNews() async { 37 | Dio dio = Dio( 38 | BaseOptions( 39 | baseUrl: NEWS_BASE_URL, 40 | receiveDataWhenStatusError: true, 41 | ), 42 | ); 43 | try { 44 | var result = await dio.get( 45 | EndPoints.weatherNews, 46 | queryParameters: {'q': 'weather', 'apiKey': EnvHelper.NEWS_API_KEY}, 47 | ); 48 | final data = WeatherNewsModel.fromJson(result.data); 49 | print(data.title); 50 | return right(data); 51 | } catch (e) { 52 | return left(ErrorHandler.handle(e).failure!); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/screens/hourly_forecast_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/common/common.dart'; 2 | import 'package:clima/features/hourly_forecast/screens/widgets/widgets.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | import '../cubit/hourly_forecast_cubit.dart'; 7 | 8 | class HourlyForecastScreen extends StatelessWidget { 9 | const HourlyForecastScreen({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return BlocBuilder<HourlyForecastCubit, DetailedForecastState>( 14 | builder: (context, state) { 15 | if (state is DetailedForecastInitial) { 16 | return const LoadingWidget(); 17 | } else if (state is DetailsForecastSuccess) { 18 | return HourlyForecastWidget( 19 | hourlyForecast: state.hourlyForecast, 20 | dailyForecast: state.dailyForecast, 21 | article: state.article, 22 | ); 23 | } else if (state is DetailedForecastError) { 24 | return FailureWidget(text: state.errorMessage); 25 | } else { 26 | return const FailureWidget(); 27 | } 28 | }, 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/screens/widgets/custom_app_bar.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class CustomAppBar extends StatelessWidget { 4 | const CustomAppBar( 5 | {super.key, required this.daily, required this.temperature}); 6 | 7 | final Daily daily; 8 | final num temperature; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: 16.allInsets, 14 | child: Row( 15 | crossAxisAlignment: CrossAxisAlignment.start, 16 | children: [ 17 | Expanded( 18 | child: Column( 19 | crossAxisAlignment: CrossAxisAlignment.center, 20 | children: [ 21 | Expanded( 22 | child: FittedBox( 23 | fit: BoxFit.scaleDown, 24 | child: TemperatureText(temperature: temperature.toString()), 25 | ), 26 | ), 27 | Text( 28 | Location.instance.city, 29 | style: AppTypography.bold24(), 30 | textAlign: TextAlign.center, 31 | ), 32 | Text( 33 | Location.instance.country, 34 | style: AppTypography.thin14(), 35 | textAlign: TextAlign.center, 36 | ), 37 | ], 38 | ), 39 | ), 40 | Expanded( 41 | child: Column( 42 | crossAxisAlignment: CrossAxisAlignment.center, 43 | children: [ 44 | Expanded( 45 | child: WeatherImage( 46 | image: context.read<HomeCubit>().theme?.image, 47 | begin: -10, 48 | end: 10, 49 | isCenter: false, 50 | ), 51 | ), 52 | SizedBox(height: 16.h), 53 | Text( 54 | "${daily.temperatureMin}° ~ ${daily.temperatureMax}°", 55 | ), 56 | Text( 57 | "feels like ${daily.apparentTemperature}°", 58 | ), 59 | ], 60 | ), 61 | ), 62 | ], 63 | ), 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/screens/widgets/day_and_night_widget.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class DayAndNight extends StatelessWidget { 4 | const DayAndNight({ 5 | super.key, 6 | required this.sunset, 7 | required this.sunrise, 8 | }); 9 | final String sunset; 10 | final String sunrise; 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container( 14 | margin: 16.horizontalInsets, 15 | padding: 16.allInsets, 16 | decoration: AppDecoration.container(context), 17 | width: AppDimensions.width, 18 | height: 200.h, 19 | child: Row( 20 | crossAxisAlignment: CrossAxisAlignment.center, 21 | children: [ 22 | Expanded( 23 | child: Column( 24 | mainAxisAlignment: MainAxisAlignment.center, 25 | children: [ 26 | Text("Sunrise", style: AppTypography.medium18()), 27 | const SizedBox(height: 5), 28 | Text(sunrise), 29 | const SizedBox(height: 5), 30 | Expanded( 31 | child: Image.asset(AppImages.daySun, fit: BoxFit.cover), 32 | ), 33 | ], 34 | ), 35 | ), 36 | Expanded( 37 | child: Column( 38 | mainAxisAlignment: MainAxisAlignment.center, 39 | children: [ 40 | Text("Sunset", style: AppTypography.medium18()), 41 | const SizedBox(height: 5), 42 | Text(sunset), 43 | const SizedBox(height: 5), 44 | Expanded( 45 | child: Image.asset(AppImages.nightMoon, fit: BoxFit.cover), 46 | ), 47 | ], 48 | ), 49 | ), 50 | ], 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/screens/widgets/forecast_info_widget.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class ForecastInfoWidget extends StatelessWidget { 4 | const ForecastInfoWidget({ 5 | super.key, 6 | required this.forecast, 7 | }); 8 | final Daily forecast; 9 | @override 10 | Widget build(BuildContext context) { 11 | return Container( 12 | height: 150.h, 13 | width: double.infinity, 14 | margin: 16.horizontalInsets, 15 | padding: 16.allInsets, 16 | decoration: AppDecoration.container(context), 17 | child: Row( 18 | children: List.generate(3, (index) { 19 | var list = getForecastInfo( 20 | uvIndex: forecast.uvIndexMax.toString(), 21 | wind: forecast.windSpeed.toString(), 22 | humidity: forecast.humidity.toString()); 23 | return Expanded( 24 | child: Column( 25 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 26 | children: [ 27 | Expanded( 28 | child: SvgPicture.asset(list[index].image, 29 | fit: BoxFit.contain, width: double.infinity), 30 | ), 31 | SizedBox(height: 15.h), 32 | Text(list[index].title, style: AppTypography.bold14()), 33 | SizedBox(height: 5.h), 34 | Text("${list[index].content}${list[index].prefix}"), 35 | ], 36 | ), 37 | ); 38 | }), 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class HourlyForecastDetails extends StatelessWidget { 4 | const HourlyForecastDetails({ 5 | super.key, 6 | required this.hourlyForecast, 7 | }); 8 | 9 | final WeatherHourly hourlyForecast; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return SizedBox( 14 | height: 150.h, 15 | child: ListView.separated( 16 | itemCount: hourlyForecast.time.length, 17 | padding: 16.horizontalInsets, 18 | scrollDirection: Axis.horizontal, 19 | physics: const BouncingScrollPhysics(), 20 | itemBuilder: (context, index) => Container( 21 | decoration: AppDecoration.container(context), 22 | width: 90.w, 23 | padding: 8.allInsets, 24 | child: Column( 25 | children: [ 26 | Expanded( 27 | child: Text(hourlyForecast.time[index]), 28 | ), 29 | Expanded( 30 | flex: 3, 31 | child: RepaintBoundary( 32 | child: Lottie.asset(hourlyForecast.image[index]), 33 | ), 34 | ), 35 | SizedBox(height: 8.h), 36 | Expanded( 37 | child: Text( 38 | hourlyForecast.temperature[index].toString(), 39 | style: AppTypography.medium14(), 40 | ), 41 | ), 42 | Expanded( 43 | child: Row( 44 | mainAxisAlignment: MainAxisAlignment.center, 45 | children: [ 46 | const Icon(Icons.water_drop_outlined, size: 16), 47 | Text(hourlyForecast.humidity[index].toString()), 48 | ], 49 | ), 50 | ), 51 | ], 52 | ), 53 | ), 54 | separatorBuilder: (context, index) => SizedBox(width: 8.w), 55 | ), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class HourlyForecastWidget extends StatelessWidget { 4 | const HourlyForecastWidget( 5 | {super.key, 6 | required this.hourlyForecast, 7 | required this.dailyForecast, 8 | required this.article}); 9 | final WeatherHourly hourlyForecast; 10 | final Daily dailyForecast; 11 | final WeatherNewsModel article; 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | body: CustomScrollView( 16 | slivers: [ 17 | SliverAppBar( 18 | expandedHeight: 250.h, 19 | flexibleSpace: FlexibleSpaceBar( 20 | collapseMode: CollapseMode.pin, 21 | background: CustomAppBar( 22 | daily: dailyForecast, 23 | temperature: hourlyForecast.temperature[0], 24 | ), 25 | ), 26 | ), 27 | SliverToBoxAdapter(child: SizedBox(height: 16.h)), 28 | SliverToBoxAdapter( 29 | child: HourlyForecastDetails(hourlyForecast: hourlyForecast)), 30 | SliverToBoxAdapter( 31 | child: NewsWidget( 32 | article: article, 33 | dailyForecast: dailyForecast, 34 | ), 35 | ), 36 | SliverToBoxAdapter( 37 | child: ForecastInfoWidget(forecast: dailyForecast)), 38 | SliverToBoxAdapter(child: SizedBox(height: 16.h)), 39 | SliverToBoxAdapter( 40 | child: DayAndNight( 41 | sunset: dailyForecast.sunset, 42 | sunrise: dailyForecast.sunrise, 43 | ), 44 | ), 45 | SliverToBoxAdapter(child: SizedBox(height: 16.h)), 46 | ], 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/screens/widgets/news.dart: -------------------------------------------------------------------------------- 1 | part of 'widgets.dart'; 2 | 3 | class News extends StatelessWidget { 4 | const News({ 5 | super.key, 6 | required this.title, 7 | required this.content, 8 | }); 9 | final String title; 10 | final String content; 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 15 | crossAxisAlignment: CrossAxisAlignment.center, 16 | children: [ 17 | Text(title, style: AppTypography.medium18()), 18 | Text( 19 | content, 20 | style: AppTypography.medium14(), 21 | overflow: TextOverflow.ellipsis, 22 | ), 23 | ], 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/features/hourly_forecast/screens/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:clima/features/hourly_forecast/data/models/weather_daily_model.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:flutter_svg/flutter_svg.dart'; 7 | import 'package:intl/intl.dart'; 8 | import 'package:lottie/lottie.dart'; 9 | import 'package:url_launcher/url_launcher.dart'; 10 | 11 | import '../../../../core/common/common.dart'; 12 | import '../../../../core/helper/location_helper.dart'; 13 | import '../../../../core/utils/utils.dart'; 14 | import '../../../home/cubit/home_cubit.dart'; 15 | import '../../data/models/forecast_info_model.dart'; 16 | import '../../data/models/weather_hourly_model.dart'; 17 | import '../../data/models/weather_news_model.dart'; 18 | 19 | part 'custom_app_bar.dart'; 20 | part 'day_and_night_widget.dart'; 21 | part 'forecast_info_widget.dart'; 22 | part 'hourly_forecast_details.dart'; 23 | part 'hourly_forecast_widget.dart'; 24 | part 'news.dart'; 25 | part 'news_widget.dart'; 26 | -------------------------------------------------------------------------------- /lib/features/landing_page/bloc/nav_bar/nav_bar_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | part 'nav_bar_event.dart'; 5 | part 'nav_bar_state.dart'; 6 | 7 | class NavBarBloc extends Bloc<NavBarEvent, NavBarState> { 8 | NavBarBloc() : super(const NavBarInitial(tabIndex: 0)) { 9 | on<NavBarEvent>((event, emit) { 10 | if (event is TabChange) { 11 | emit(NavBarInitial(tabIndex: event.tabIndex)); 12 | } 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/features/landing_page/bloc/nav_bar/nav_bar_event.dart: -------------------------------------------------------------------------------- 1 | part of 'nav_bar_bloc.dart'; 2 | 3 | @immutable 4 | abstract class NavBarEvent {} 5 | 6 | class TabChange extends NavBarEvent { 7 | final int tabIndex; 8 | 9 | TabChange({required this.tabIndex}); 10 | } 11 | -------------------------------------------------------------------------------- /lib/features/landing_page/bloc/nav_bar/nav_bar_state.dart: -------------------------------------------------------------------------------- 1 | part of 'nav_bar_bloc.dart'; 2 | 3 | @immutable 4 | abstract class NavBarState { 5 | final int tabIndex; 6 | 7 | const NavBarState({required this.tabIndex}); 8 | } 9 | 10 | class NavBarInitial extends NavBarState { 11 | const NavBarInitial({required super.tabIndex}); 12 | } 13 | -------------------------------------------------------------------------------- /lib/features/landing_page/landing_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/features/daily_forecast/cubit/daily_forecast_cubit.dart'; 2 | import 'package:clima/features/daily_forecast/data/repo/daily_forecast_repo.dart'; 3 | import 'package:clima/features/home/cubit/home_cubit.dart'; 4 | import 'package:clima/features/home/data/repo/home_repo.dart'; 5 | import 'package:clima/features/hourly_forecast/data/repo/hourly_forecast_repo.dart'; 6 | import 'package:clima/features/landing_page/widgets/bottom_nav_bar_list.dart'; 7 | import 'package:clima/features/landing_page/widgets/screens.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_bloc/flutter_bloc.dart'; 10 | 11 | import '../../core/managers/managers.dart'; 12 | import '../hourly_forecast/cubit/hourly_forecast_cubit.dart'; 13 | import 'bloc/nav_bar/nav_bar_bloc.dart'; 14 | 15 | class LandingScreen extends StatelessWidget { 16 | const LandingScreen({super.key}); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return MultiBlocProvider( 21 | providers: [ 22 | BlocProvider( 23 | create: (context) => NavBarBloc(), 24 | ), 25 | BlocProvider( 26 | create: (context) => 27 | HomeCubit(DependencyManager.get<HomeRepository>()) 28 | ..fetchWeatherData(), 29 | ), 30 | BlocProvider( 31 | create: (context) => HourlyForecastCubit( 32 | DependencyManager.get<HourlyForecastRepository>()) 33 | ..fetchWeatherData(), 34 | ), 35 | BlocProvider( 36 | create: (context) => DailyForecastCubit( 37 | DependencyManager.get<DailyForecastRepository>()) 38 | ..fetchDailyData(), 39 | ), 40 | ], 41 | child: BlocBuilder<NavBarBloc, NavBarState>( 42 | builder: (context, state) { 43 | return Scaffold( 44 | body: RefreshIndicator( 45 | onRefresh: () async => fetchData(context), 46 | child: SafeArea( 47 | child: CustomScrollView( 48 | slivers: [ 49 | SliverFillRemaining( 50 | child: screens.elementAt(state.tabIndex), 51 | ) 52 | ], 53 | ), 54 | ), 55 | ), 56 | bottomNavigationBar: BottomNavigationBar( 57 | onTap: (index) { 58 | BlocProvider.of<NavBarBloc>(context) 59 | .add(TabChange(tabIndex: index)); 60 | }, 61 | currentIndex: state.tabIndex, 62 | items: bottomNavItems, 63 | ), 64 | ); 65 | }, 66 | ), 67 | ); 68 | } 69 | 70 | void fetchData(BuildContext context) async { 71 | BlocProvider.of<HourlyForecastCubit>(context).fetchWeatherData(); 72 | BlocProvider.of<HomeCubit>(context).fetchWeatherData(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/features/landing_page/widgets/bottom_nav_bar_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:iconsax/iconsax.dart'; 3 | 4 | List<BottomNavigationBarItem> bottomNavItems = const <BottomNavigationBarItem>[ 5 | BottomNavigationBarItem(icon: Icon(Iconsax.home), label: ''), 6 | BottomNavigationBarItem(icon: Icon(Iconsax.activity), label: ''), 7 | BottomNavigationBarItem(icon: Icon(Iconsax.calendar), label: ''), 8 | ]; 9 | -------------------------------------------------------------------------------- /lib/features/landing_page/widgets/screens.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/features/home/screens/home_screen.dart'; 2 | import 'package:clima/features/hourly_forecast/screens/hourly_forecast_screen.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | import '../../daily_forecast/screens/daily_forecast_screen.dart'; 6 | 7 | const List<Widget> screens = <Widget>[ 8 | HomeScreen(), 9 | HourlyForecastScreen(), 10 | DailyForecastScreen(), 11 | ]; 12 | -------------------------------------------------------------------------------- /lib/features/map/screens/map_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:clima/core/helper/location_helper.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:google_maps_flutter/google_maps_flutter.dart'; 6 | 7 | class Map extends StatefulWidget { 8 | const Map({super.key}); 9 | 10 | @override 11 | _MapState createState() => _MapState(); 12 | } 13 | 14 | class _MapState extends State<Map> { 15 | final Completer<GoogleMapController> _controller = Completer(); 16 | final Set<Marker> _markers = Set<Marker>(); 17 | String mapTheme = ''; 18 | static final LatLng _center = LatLng(Location.instance.position!.latitude, 19 | Location.instance.position!.longitude); 20 | 21 | void _onMapCreated(GoogleMapController controller) { 22 | controller.setMapStyle(mapTheme); 23 | _controller.complete(controller); 24 | } 25 | 26 | void _setMarker(LatLng point) { 27 | setState(() { 28 | _markers.add( 29 | Marker( 30 | markerId: const MarkerId('you are here'), 31 | position: point, 32 | infoWindow: const InfoWindow(title: "you are here")), 33 | ); 34 | }); 35 | } 36 | 37 | @override 38 | void initState() { 39 | super.initState(); 40 | DefaultAssetBundle.of(context) 41 | .loadString('assets/map/map_dark_theme.json') 42 | .then((value) { 43 | mapTheme = value; 44 | }); 45 | _setMarker(_center); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return Stack( 51 | children: [ 52 | GoogleMap( 53 | onMapCreated: _onMapCreated, 54 | markers: _markers, 55 | circles: { 56 | Circle( 57 | circleId: const CircleId("1"), 58 | center: _center, 59 | fillColor: Colors.blueAccent.withOpacity(0.1), 60 | strokeWidth: 2, 61 | radius: 600), 62 | }, 63 | initialCameraPosition: CameraPosition( 64 | target: _center, 65 | zoom: 16.0, 66 | ), 67 | ), 68 | ], 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:clima/core/helper/functions.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'app/app.dart'; 5 | 6 | void main() { 7 | /// [initialization] contains initialization for 8 | /// 1 - [WidgetsFlutterBinding] 9 | /// 2 - [CacheHelper] - Shared Preferences. 10 | /// 3 - [LottieCache] - Preloading Lottie animations. 11 | /// 4 - [Notifications] - initialize notification manager. 12 | initialization(); 13 | runApp(const MyApp()); 14 | } 15 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: clima 2 | 3 | version: 2.1.0 4 | 5 | environment: 6 | sdk: '>=3.1.3 <4.0.0' 7 | 8 | # Dependencies specify other packages that your package needs in order to work. 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | cupertino_icons: ^1.0.6 14 | dio: ^5.4.1 15 | geolocator: ^11.0.0 16 | fpdart: ^1.1.0 17 | flutter_bloc: ^8.1.4 18 | url_launcher: ^6.2.5 19 | get_it: ^7.6.7 20 | lottie: ^3.1.0 21 | iconsax: ^0.0.8 22 | flutter_local_notifications: ^17.0.0 23 | path_provider: ^2.1.2 24 | equatable: ^2.0.5 25 | flutter_svg: ^2.0.10+1 26 | geocoding: ^3.0.0 27 | google_maps_flutter: ^2.6.0 28 | intl: ^0.18.1 29 | wakelock_plus: ^1.2.2 30 | 31 | dev_dependencies: 32 | flutter_test: 33 | sdk: flutter 34 | pretty_dio_logger: ^1.3.1 35 | flutter_lints: ^3.0.1 36 | device_preview: ^1.1.0 37 | 38 | 39 | # The following section is specific to Flutter packages. 40 | flutter: 41 | 42 | uses-material-design: true 43 | 44 | # To add assets to your application, add an assets section, like this: 45 | assets: 46 | - assets/env/api.env 47 | - assets/images/weather/Day Clouds.webp 48 | - assets/images/weather/Day Rain.webp 49 | - assets/images/weather/Day Snow.webp 50 | - assets/images/weather/Day Storm.webp 51 | - assets/images/weather/Day Sun.webp 52 | - assets/images/weather/Day Wind.webp 53 | - assets/images/weather/Night Clouds.webp 54 | - assets/images/weather/Night Rain.webp 55 | - assets/images/weather/Night Snow.webp 56 | - assets/images/weather/Night Storm.webp 57 | - assets/images/weather/Night Moon.webp 58 | - assets/images/weather/Night Wind.webp 59 | - assets/images/weather/humidity.svg 60 | - assets/images/weather/sun.svg 61 | - assets/images/weather/wind.svg 62 | - assets/images/weather/snow.svg 63 | - assets/images/weather/rain.svg 64 | - assets/images/weather/sunny.svg 65 | - assets/lottie/ 66 | - assets/map/map_dark_theme.json 67 | 68 | # To add custom fonts to your application, add a fonts section here, 69 | # example: 70 | fonts: 71 | - family: manrope 72 | fonts: 73 | - asset: assets/fonts/manrope/Manrope-Light.ttf 74 | - asset: assets/fonts/manrope/Manrope-Regular.ttf 75 | - asset: assets/fonts/manrope/Manrope-Medium.ttf 76 | - asset: assets/fonts/manrope/Manrope-Bold.ttf 77 | 78 | # - asset: fonts/TrajanPro_Bold.ttf 79 | # weight: 700 80 | # 81 | # For details regarding fonts from package dependencies, 82 | # see https://flutter.dev/custom-fonts/#from-packages 83 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:clima/app/app.dart'; 9 | import 'package:flutter/material.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(const MyApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <!-- 5 | If you are serving your web app in a path other than the root, change the 6 | href value below to reflect the base path you are serving from. 7 | 8 | The path provided below has to start and end with a slash "/" in order for 9 | it to work correctly. 10 | 11 | For more details: 12 | * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base 13 | 14 | This is a placeholder for base href that will be replaced by the value of 15 | the `--base-href` argument provided to `flutter build`. 16 | --> 17 | <base href="$FLUTTER_BASE_HREF"> 18 | 19 | <meta charset="UTF-8"> 20 | <meta content="IE=Edge" http-equiv="X-UA-Compatible"> 21 | <meta name="description" content="A new Flutter project."> 22 | 23 | <!-- iOS meta tags & icons --> 24 | <meta name="apple-mobile-web-app-capable" content="yes"> 25 | <meta name="apple-mobile-web-app-status-bar-style" content="black"> 26 | <meta name="apple-mobile-web-app-title" content="clima"> 27 | <link rel="apple-touch-icon" href="icons/Icon-192.png"> 28 | 29 | <!-- Favicon --> 30 | <link rel="icon" type="image/png" href="favicon.png"/> 31 | 32 | <title>clima 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clima", 3 | "short_name": "clima", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void RegisterPlugins(flutter::PluginRegistry* registry) { 13 | GeolocatorWindowsRegisterWithRegistrar( 14 | registry->GetRegistrarForPlugin("GeolocatorWindows")); 15 | UrlLauncherWindowsRegisterWithRegistrar( 16 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 17 | } 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | geolocator_windows 7 | url_launcher_windows 8 | ) 9 | 10 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 11 | ) 12 | 13 | set(PLUGIN_BUNDLED_LIBRARIES) 14 | 15 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 16 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 17 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 20 | endforeach(plugin) 21 | 22 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 23 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 25 | endforeach(ffi_plugin) 26 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "clima" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "clima" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "clima.exe" "\0" 98 | VALUE "ProductName", "clima" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"clima", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moha-b/Clima/1777f178b13f3ecf4f7f8ef9212b4823759ba9d8/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------