├── .github └── workflows │ └── static.yml ├── .gitignore ├── .metadata ├── LICENSE ├── Makefile ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle.kts │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── io │ │ │ │ └── adaptiv │ │ │ │ └── flutter_kit │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle.kts ├── assets ├── fonts │ ├── GeneralSans-Bold.otf │ ├── GeneralSans-BoldItalic.otf │ ├── GeneralSans-Extralight.otf │ ├── GeneralSans-ExtralightItalic.otf │ ├── GeneralSans-Italic.otf │ ├── GeneralSans-Light.otf │ ├── GeneralSans-LightItalic.otf │ ├── GeneralSans-Medium.otf │ ├── GeneralSans-MediumItalic.otf │ ├── GeneralSans-Regular.otf │ ├── GeneralSans-Semibold.otf │ └── GeneralSans-SemiboldItalic.otf └── images │ ├── apple-logo.svg │ └── google-logo.svg ├── build.yaml ├── flutter-kit-logo.png ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── 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 │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h └── RunnerTests │ └── RunnerTests.swift ├── lib ├── gen │ └── assets.gen.dart ├── main.dart └── src │ ├── core │ ├── app_initializer.dart │ ├── application.dart │ ├── environment.dart │ ├── i18n │ │ ├── intl │ │ │ ├── messages_all.dart │ │ │ └── messages_en.dart │ │ ├── l10n.dart │ │ └── l10n │ │ │ └── intl_en.arb │ ├── routing │ │ ├── app_router.dart │ │ └── app_router.gr.dart │ └── theme │ │ ├── app_theme.dart │ │ ├── color_scheme_extension.dart │ │ ├── color_schemes.dart │ │ ├── custom_colors.dart │ │ └── dimens.dart │ ├── datasource │ ├── http │ │ ├── dio_config.dart │ │ ├── example_api.dart │ │ └── http_util.dart │ ├── models │ │ └── api_response │ │ │ └── api_response.dart │ └── repositories │ │ ├── base_repository.dart │ │ └── example_repository.dart │ ├── features │ └── login │ │ ├── logic │ │ ├── login_controller.dart │ │ └── login_state.dart │ │ └── ui │ │ └── login_screen.dart │ └── shared │ ├── components │ ├── app_snackbar.dart │ ├── atoms │ │ ├── basic_card.dart │ │ └── dividers │ │ │ └── labeled_divider.dart │ ├── business │ │ └── listenable_consumer.dart │ ├── buttons │ │ └── button.dart │ ├── dialogs │ │ ├── confirmation_dialog.dart │ │ ├── informative_dialog.dart │ │ └── loading_dialog.dart │ └── forms │ │ └── input.dart │ ├── extensions │ ├── context_extensions.dart │ └── iterable_extensions.dart │ ├── locator.dart │ └── services │ ├── app_logger.dart │ └── storage │ ├── in_memory_storage.dart │ ├── local_storage.dart │ └── storage.dart ├── pubspec.lock ├── pubspec.yaml ├── setup.bat ├── setup.sh └── test └── widget_test.dart /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["site"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | - name: Setup Pages 35 | uses: actions/configure-pages@v5 36 | - name: Upload artifact 37 | uses: actions/upload-pages-artifact@v3 38 | with: 39 | # Upload entire repository 40 | path: './docs' 41 | - name: Deploy to GitHub Pages 42 | id: deployment 43 | uses: actions/deploy-pages@v4 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .packages 33 | .pub-cache/ 34 | .pub/ 35 | /build/ 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | .fvm/ 49 | .vscode/ 50 | 51 | .fvmrc -------------------------------------------------------------------------------- /.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: "09de023485e95e6d1225c2baa44b8feb85e0d45f" 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: 09de023485e95e6d1225c2baa44b8feb85e0d45f 17 | base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f 18 | - platform: ios 19 | create_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f 20 | base_revision: 09de023485e95e6d1225c2baa44b8feb85e0d45f 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | **Copyright (c) 2024 Steve Nosse** 2 | 3 | **Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:** 4 | 5 | **The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.** 6 | 7 | **THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.** -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | clean-ios: 2 | @echo "Cleaning iOS build files..." 3 | flutter clean 4 | rm -rf ios/Pods ios/Podfile.lock 5 | flutter pub get 6 | cd ios && pod install && cd .. 7 | 8 | codegen: 9 | @echo "Generating code..." 10 | fvm dart run build_runner build --delete-conflicting-outputs 11 | 12 | i18n: 13 | @echo "Generating i18n..." 14 | fvm dart run intl_utils:generate 15 | 16 | build-android: 17 | @echo "Building Android..." 18 | fvm flutter build appbundle --release 19 | 20 | build-ios: 21 | @echo "Building iOS..." 22 | fvm flutter build ios 23 | 24 | get: 25 | @echo "Getting dependencies..." 26 | fvm flutter pub get -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Flutter Kit

2 | 3 |

Opinionated flutter starter project using feature-first folder structure.

4 | 5 |

6 | GitHub stars 7 |      8 | GitHub forks 9 | 10 |

11 | 12 |
13 | 14 |
15 | 16 | ## Motivation 17 | 18 | Flutter is geat, really. But after few years of development, it's not as easy as it seems; starting a new project can be quite a challenge. The fact that Flutter is as less opinionated has some benefits but also comes with some drawbacks among those the pain of starting a new project. You can easily waste a lot how hours to get productive, while you only wanted to POC a new idea. 19 | 20 | This kit provides a set of preconfigured features and utilities to help you get started with your project. It is inspired by my own experience and knowledge of Flutter. I hope you find it useful. 21 | 22 | If you are not confortable with some choices i made in the structure, we can either discuss it (yeah i may find it helpful) or fork this repo to create your own. 23 | 24 | ## What's in this kit? 25 | This kit includes: 26 | 27 | - An example login feature 28 | - I18n 29 | - Navigation 30 | - State management 31 | - Extensions 32 | - Basic config for API calls 33 | - Theming (Material 3) 34 | 35 | ## Get started 36 | 37 | ### 1. Clone the repository 38 | ```bash 39 | git clone git@github.com:stevenosse/flutter_kit.git 40 | ``` 41 | 42 | ### 2. Configure your project 43 | 44 | Add your configuration to the `pubspec.yaml` file under the `flutter_kit` key: 45 | 46 | ```yaml 47 | flutter_kit: 48 | app_name: Your App Name 49 | ios_bundle_id: com.yourcompany.yourapp 50 | android_package_name: com.yourcompany.yourapp 51 | ``` 52 | 53 | ### 3. Run the setup script 54 | 55 | #### On macOS/Linux: 56 | ```bash 57 | chmod +x setup.sh 58 | ./setup.sh 59 | ``` 60 | 61 | #### On Windows: 62 | ```bash 63 | setup.bat 64 | ``` 65 | 66 | The setup script will: 67 | - Update your app name for both iOS and Android 68 | - Change the iOS bundle identifier 69 | - Change the Android package name 70 | - Move the Android files to the correct package structure 71 | 72 | ### 4. Run flutter pub get 73 | ```bash 74 | flutter pub get 75 | ``` 76 | 77 | ## Features 78 | This kit comes with a set of preconfigured features and utilities: 79 | 80 | - I18n 81 | - Navigation (using auto_route) 82 | - State management (using plain change notifier) 83 | - Extensions (on context, iterable) 84 | - Utility widgets 85 | - Default Theming using Material 3 86 | 87 | ## 🗺️ I18n 88 | This kit uses [intl_utils](https://pub.dev/packages/intl_utils) for internationalization. To add a new language, add a new file to the `src/core/i18n/l10n` folder. The file name should be the language code prefixed with intl_ (e.g. `intl_fr.arb`). 89 | 90 | To generate the code for the new language, run the following command: 91 | 92 | ```bash 93 | flutter pub run intl_utils:generate 94 | ``` 95 | 96 | Alternatively, you can install the [flutter_intl](https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl) package in VSCode to automatically generate the code when you save the file. 97 | 98 | ## 🛣️ Navigation 99 | This kit uses [auto_route](https://pub.dev/packages/auto_route) for navigation. To add a new route, add a new file to the `src/core/routing/app_router.dart` file. 100 | 101 | See the [auto_route documentation](https://pub.dev/packages/auto_route) for more information. 102 | 103 | ## 🧱 State Management 104 | This kit uses plain change notifiers for state management. 105 | 106 | An example of a change notifier can be found in the `src/features/login/logic` folder. 107 | 108 | ## 🗼 Extensions 109 | This kit comes with a few extensions on the `BuildContext` and `Iterable` classes. See the `src/shared/extensions` folder for more information. 110 | 111 | ## 📌 Utils 112 | ### Widgets 113 | This kit comes with a few utility widgets. See the `src/shared/components` folder for more information. 114 | 115 | ### Assets 116 | Assets paths are automatically generated when using build_runnner thanks to the [flutter_gen](https://pub.dev/packages/https://pub.dev/packages/flutter_gen) package. 117 | 118 | Each time you add an asset, run the following command: 119 | 120 | ```bash 121 | $ fluttergen 122 | ``` 123 | 124 | ### CLI 125 | This kit comes with a few CLI commands to make your life easier. 126 | It uses make: 127 | 128 | - Windows: `choco install make` 129 | - Linux: `sudo apt install make` 130 | - Mac: `brew install make` 131 | 132 | See the `Makefile` for more information. 133 | 134 | ## 🖌️ Theming 135 | This kit uses the new Material 3 theming system. See the `src/core/theme` folder for more information. 136 | 137 | The default color scheme is generated using https://m3.material.io/theme-builder#/custom. You can use this tool to generate your own color scheme. 138 | 139 | ## Showcase 140 | - [LineAI](https://github.com/stevenosse/lineai) 141 | 142 | ## 💼 Need a new feature? 143 | If you need a new feature, please open an issue on the [GitHub repository](https://github.com/stevenosse/flutter_boilerplate/issues/new) 144 | 145 | ## 📇 Get in touch 146 | If you have any questions, feel free to contact me on [Twitter](https://twitter.com/nossesteve) 147 | 148 | ## TODO 149 | - [ ] Setup a CI pipeline 150 | - [ ] Add setup for unit tests 151 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | linter: 4 | rules: 5 | avoid_print: true 6 | prefer_single_quotes: true 7 | use_super_parameters: true 8 | always_declare_return_types: true 9 | avoid_annotating_with_dynamic: true 10 | strict_top_level_inference: false 11 | 12 | analyzer: 13 | exclude: ["**.freezed.dart", "**.g.dart"] 14 | 15 | errors: 16 | invalid_annotation_target: ignore -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | .cxx/ 9 | 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/to/reference-keystore 12 | key.properties 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("kotlin-android") 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id("dev.flutter.flutter-gradle-plugin") 6 | } 7 | 8 | android { 9 | namespace = "io.adaptiv.flutter_kit" 10 | compileSdk = flutter.compileSdkVersion 11 | ndkVersion = flutter.ndkVersion 12 | 13 | compileOptions { 14 | sourceCompatibility = JavaVersion.VERSION_11 15 | targetCompatibility = JavaVersion.VERSION_11 16 | } 17 | 18 | kotlinOptions { 19 | jvmTarget = JavaVersion.VERSION_11.toString() 20 | } 21 | 22 | defaultConfig { 23 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 24 | applicationId = "io.adaptiv.flutter_kit" 25 | // You can update the following values to match your application needs. 26 | // For more information, see: https://flutter.dev/to/review-gradle-config. 27 | minSdk = flutter.minSdkVersion 28 | targetSdk = flutter.targetSdkVersion 29 | versionCode = flutter.versionCode 30 | versionName = flutter.versionName 31 | } 32 | 33 | buildTypes { 34 | release { 35 | // TODO: Add your own signing config for the release build. 36 | // Signing with the debug keys for now, so `flutter run --release` works. 37 | signingConfig = signingConfigs.getByName("debug") 38 | } 39 | } 40 | } 41 | 42 | flutter { 43 | source = "../.." 44 | } 45 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/io/adaptiv/flutter_kit/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.adaptiv.flutter_kit 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() 9 | rootProject.layout.buildDirectory.value(newBuildDir) 10 | 11 | subprojects { 12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 13 | project.layout.buildDirectory.value(newSubprojectBuildDir) 14 | } 15 | subprojects { 16 | project.evaluationDependsOn(":app") 17 | } 18 | 19 | tasks.register("clean") { 20 | delete(rootProject.layout.buildDirectory) 21 | } 22 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError 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-8.10.2-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | val flutterSdkPath = run { 3 | val properties = java.util.Properties() 4 | file("local.properties").inputStream().use { properties.load(it) } 5 | val flutterSdkPath = properties.getProperty("flutter.sdk") 6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 7 | 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 "8.7.0" apply false 22 | id("org.jetbrains.kotlin.android") version "1.8.22" apply false 23 | } 24 | 25 | include(":app") 26 | -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-Bold.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-BoldItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-BoldItalic.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-Extralight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-Extralight.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-ExtralightItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-ExtralightItalic.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-Italic.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-Light.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-LightItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-LightItalic.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-Medium.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-MediumItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-MediumItalic.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-Regular.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-Semibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-Semibold.otf -------------------------------------------------------------------------------- /assets/fonts/GeneralSans-SemiboldItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/assets/fonts/GeneralSans-SemiboldItalic.otf -------------------------------------------------------------------------------- /assets/images/apple-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /assets/images/google-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | json_serializable: 5 | options: 6 | explicit_to_json: true -------------------------------------------------------------------------------- /flutter-kit-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/flutter-kit-logo.png -------------------------------------------------------------------------------- /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 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | 33 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_ios_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = 97C146E61CF9000F007C117D /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = 97C146ED1CF9000F007C117D; 25 | remoteInfo = Runner; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXCopyFilesBuildPhase section */ 30 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 31 | isa = PBXCopyFilesBuildPhase; 32 | buildActionMask = 2147483647; 33 | dstPath = ""; 34 | dstSubfolderSpec = 10; 35 | files = ( 36 | ); 37 | name = "Embed Frameworks"; 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXCopyFilesBuildPhase section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 44 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 45 | 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 46 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 48 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 49 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 50 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 51 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 52 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 53 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 331C8082294A63A400263BE5 /* RunnerTests */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 331C807B294A618700263BE5 /* RunnerTests.swift */, 75 | ); 76 | path = RunnerTests; 77 | sourceTree = ""; 78 | }; 79 | 9740EEB11CF90186004384FC /* Flutter */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 83 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 84 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 85 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 86 | ); 87 | name = Flutter; 88 | sourceTree = ""; 89 | }; 90 | 97C146E51CF9000F007C117D = { 91 | isa = PBXGroup; 92 | children = ( 93 | 9740EEB11CF90186004384FC /* Flutter */, 94 | 97C146F01CF9000F007C117D /* Runner */, 95 | 97C146EF1CF9000F007C117D /* Products */, 96 | 331C8082294A63A400263BE5 /* RunnerTests */, 97 | ); 98 | sourceTree = ""; 99 | }; 100 | 97C146EF1CF9000F007C117D /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 97C146EE1CF9000F007C117D /* Runner.app */, 104 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | 97C146F01CF9000F007C117D /* Runner */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 113 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 114 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 115 | 97C147021CF9000F007C117D /* Info.plist */, 116 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 117 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 118 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 119 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 120 | ); 121 | path = Runner; 122 | sourceTree = ""; 123 | }; 124 | /* End PBXGroup section */ 125 | 126 | /* Begin PBXNativeTarget section */ 127 | 331C8080294A63A400263BE5 /* RunnerTests */ = { 128 | isa = PBXNativeTarget; 129 | buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; 130 | buildPhases = ( 131 | 331C807D294A63A400263BE5 /* Sources */, 132 | 331C807F294A63A400263BE5 /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | 331C8086294A63A400263BE5 /* PBXTargetDependency */, 138 | ); 139 | name = RunnerTests; 140 | productName = RunnerTests; 141 | productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; 142 | productType = "com.apple.product-type.bundle.unit-test"; 143 | }; 144 | 97C146ED1CF9000F007C117D /* Runner */ = { 145 | isa = PBXNativeTarget; 146 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 147 | buildPhases = ( 148 | 9740EEB61CF901F6004384FC /* Run Script */, 149 | 97C146EA1CF9000F007C117D /* Sources */, 150 | 97C146EB1CF9000F007C117D /* Frameworks */, 151 | 97C146EC1CF9000F007C117D /* Resources */, 152 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 153 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | ); 159 | name = Runner; 160 | productName = Runner; 161 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 162 | productType = "com.apple.product-type.application"; 163 | }; 164 | /* End PBXNativeTarget section */ 165 | 166 | /* Begin PBXProject section */ 167 | 97C146E61CF9000F007C117D /* Project object */ = { 168 | isa = PBXProject; 169 | attributes = { 170 | BuildIndependentTargetsInParallel = YES; 171 | LastUpgradeCheck = 1510; 172 | ORGANIZATIONNAME = ""; 173 | TargetAttributes = { 174 | 331C8080294A63A400263BE5 = { 175 | CreatedOnToolsVersion = 14.0; 176 | TestTargetID = 97C146ED1CF9000F007C117D; 177 | }; 178 | 97C146ED1CF9000F007C117D = { 179 | CreatedOnToolsVersion = 7.3.1; 180 | LastSwiftMigration = 1100; 181 | }; 182 | }; 183 | }; 184 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 185 | compatibilityVersion = "Xcode 9.3"; 186 | developmentRegion = en; 187 | hasScannedForEncodings = 0; 188 | knownRegions = ( 189 | en, 190 | Base, 191 | ); 192 | mainGroup = 97C146E51CF9000F007C117D; 193 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 194 | projectDirPath = ""; 195 | projectRoot = ""; 196 | targets = ( 197 | 97C146ED1CF9000F007C117D /* Runner */, 198 | 331C8080294A63A400263BE5 /* RunnerTests */, 199 | ); 200 | }; 201 | /* End PBXProject section */ 202 | 203 | /* Begin PBXResourcesBuildPhase section */ 204 | 331C807F294A63A400263BE5 /* Resources */ = { 205 | isa = PBXResourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | 97C146EC1CF9000F007C117D /* Resources */ = { 212 | isa = PBXResourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 216 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 217 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 218 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | /* End PBXResourcesBuildPhase section */ 223 | 224 | /* Begin PBXShellScriptBuildPhase section */ 225 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 226 | isa = PBXShellScriptBuildPhase; 227 | alwaysOutOfDate = 1; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | ); 231 | inputPaths = ( 232 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 9740EEB61CF901F6004384FC /* Run Script */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | alwaysOutOfDate = 1; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | ); 247 | inputPaths = ( 248 | ); 249 | name = "Run Script"; 250 | outputPaths = ( 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | shellPath = /bin/sh; 254 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 255 | }; 256 | /* End PBXShellScriptBuildPhase section */ 257 | 258 | /* Begin PBXSourcesBuildPhase section */ 259 | 331C807D294A63A400263BE5 /* Sources */ = { 260 | isa = PBXSourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | 97C146EA1CF9000F007C117D /* Sources */ = { 268 | isa = PBXSourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 272 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXSourcesBuildPhase section */ 277 | 278 | /* Begin PBXTargetDependency section */ 279 | 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { 280 | isa = PBXTargetDependency; 281 | target = 97C146ED1CF9000F007C117D /* Runner */; 282 | targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; 283 | }; 284 | /* End PBXTargetDependency section */ 285 | 286 | /* Begin PBXVariantGroup section */ 287 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 288 | isa = PBXVariantGroup; 289 | children = ( 290 | 97C146FB1CF9000F007C117D /* Base */, 291 | ); 292 | name = Main.storyboard; 293 | sourceTree = ""; 294 | }; 295 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 296 | isa = PBXVariantGroup; 297 | children = ( 298 | 97C147001CF9000F007C117D /* Base */, 299 | ); 300 | name = LaunchScreen.storyboard; 301 | sourceTree = ""; 302 | }; 303 | /* End PBXVariantGroup section */ 304 | 305 | /* Begin XCBuildConfiguration section */ 306 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 311 | CLANG_ANALYZER_NONNULL = YES; 312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 313 | CLANG_CXX_LIBRARY = "libc++"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 317 | CLANG_WARN_BOOL_CONVERSION = YES; 318 | CLANG_WARN_COMMA = YES; 319 | CLANG_WARN_CONSTANT_CONVERSION = YES; 320 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 321 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 322 | CLANG_WARN_EMPTY_BODY = YES; 323 | CLANG_WARN_ENUM_CONVERSION = YES; 324 | CLANG_WARN_INFINITE_RECURSION = YES; 325 | CLANG_WARN_INT_CONVERSION = YES; 326 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 328 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 330 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 331 | CLANG_WARN_STRICT_PROTOTYPES = YES; 332 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 333 | CLANG_WARN_UNREACHABLE_CODE = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 336 | COPY_PHASE_STRIP = NO; 337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 338 | ENABLE_NS_ASSERTIONS = NO; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 341 | GCC_C_LANGUAGE_STANDARD = gnu99; 342 | GCC_NO_COMMON_BLOCKS = YES; 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 350 | MTL_ENABLE_DEBUG_INFO = NO; 351 | SDKROOT = iphoneos; 352 | SUPPORTED_PLATFORMS = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | VALIDATE_PRODUCT = YES; 355 | }; 356 | name = Profile; 357 | }; 358 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 359 | isa = XCBuildConfiguration; 360 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | CLANG_ENABLE_MODULES = YES; 364 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 365 | DEVELOPMENT_TEAM = G2KAKX49M6; 366 | ENABLE_BITCODE = NO; 367 | INFOPLIST_FILE = Runner/Info.plist; 368 | LD_RUNPATH_SEARCH_PATHS = ( 369 | "$(inherited)", 370 | "@executable_path/Frameworks", 371 | ); 372 | PRODUCT_BUNDLE_IDENTIFIER = io.adaptiv.flutterKit; 373 | PRODUCT_NAME = "$(TARGET_NAME)"; 374 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 375 | SWIFT_VERSION = 5.0; 376 | VERSIONING_SYSTEM = "apple-generic"; 377 | }; 378 | name = Profile; 379 | }; 380 | 331C8088294A63A400263BE5 /* Debug */ = { 381 | isa = XCBuildConfiguration; 382 | buildSettings = { 383 | BUNDLE_LOADER = "$(TEST_HOST)"; 384 | CODE_SIGN_STYLE = Automatic; 385 | CURRENT_PROJECT_VERSION = 1; 386 | GENERATE_INFOPLIST_FILE = YES; 387 | MARKETING_VERSION = 1.0; 388 | PRODUCT_BUNDLE_IDENTIFIER = io.adaptiv.flutterKit.RunnerTests; 389 | PRODUCT_NAME = "$(TARGET_NAME)"; 390 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 391 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 392 | SWIFT_VERSION = 5.0; 393 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 394 | }; 395 | name = Debug; 396 | }; 397 | 331C8089294A63A400263BE5 /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | BUNDLE_LOADER = "$(TEST_HOST)"; 401 | CODE_SIGN_STYLE = Automatic; 402 | CURRENT_PROJECT_VERSION = 1; 403 | GENERATE_INFOPLIST_FILE = YES; 404 | MARKETING_VERSION = 1.0; 405 | PRODUCT_BUNDLE_IDENTIFIER = io.adaptiv.flutterKit.RunnerTests; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | SWIFT_VERSION = 5.0; 408 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 409 | }; 410 | name = Release; 411 | }; 412 | 331C808A294A63A400263BE5 /* Profile */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | BUNDLE_LOADER = "$(TEST_HOST)"; 416 | CODE_SIGN_STYLE = Automatic; 417 | CURRENT_PROJECT_VERSION = 1; 418 | GENERATE_INFOPLIST_FILE = YES; 419 | MARKETING_VERSION = 1.0; 420 | PRODUCT_BUNDLE_IDENTIFIER = io.adaptiv.flutterKit.RunnerTests; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | SWIFT_VERSION = 5.0; 423 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 424 | }; 425 | name = Profile; 426 | }; 427 | 97C147031CF9000F007C117D /* Debug */ = { 428 | isa = XCBuildConfiguration; 429 | buildSettings = { 430 | ALWAYS_SEARCH_USER_PATHS = NO; 431 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 432 | CLANG_ANALYZER_NONNULL = YES; 433 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 434 | CLANG_CXX_LIBRARY = "libc++"; 435 | CLANG_ENABLE_MODULES = YES; 436 | CLANG_ENABLE_OBJC_ARC = YES; 437 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 438 | CLANG_WARN_BOOL_CONVERSION = YES; 439 | CLANG_WARN_COMMA = YES; 440 | CLANG_WARN_CONSTANT_CONVERSION = YES; 441 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 442 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 443 | CLANG_WARN_EMPTY_BODY = YES; 444 | CLANG_WARN_ENUM_CONVERSION = YES; 445 | CLANG_WARN_INFINITE_RECURSION = YES; 446 | CLANG_WARN_INT_CONVERSION = YES; 447 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 448 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 449 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 451 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 452 | CLANG_WARN_STRICT_PROTOTYPES = YES; 453 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 454 | CLANG_WARN_UNREACHABLE_CODE = YES; 455 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 456 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 457 | COPY_PHASE_STRIP = NO; 458 | DEBUG_INFORMATION_FORMAT = dwarf; 459 | ENABLE_STRICT_OBJC_MSGSEND = YES; 460 | ENABLE_TESTABILITY = YES; 461 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 462 | GCC_C_LANGUAGE_STANDARD = gnu99; 463 | GCC_DYNAMIC_NO_PIC = NO; 464 | GCC_NO_COMMON_BLOCKS = YES; 465 | GCC_OPTIMIZATION_LEVEL = 0; 466 | GCC_PREPROCESSOR_DEFINITIONS = ( 467 | "DEBUG=1", 468 | "$(inherited)", 469 | ); 470 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 471 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 472 | GCC_WARN_UNDECLARED_SELECTOR = YES; 473 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 474 | GCC_WARN_UNUSED_FUNCTION = YES; 475 | GCC_WARN_UNUSED_VARIABLE = YES; 476 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 477 | MTL_ENABLE_DEBUG_INFO = YES; 478 | ONLY_ACTIVE_ARCH = YES; 479 | SDKROOT = iphoneos; 480 | TARGETED_DEVICE_FAMILY = "1,2"; 481 | }; 482 | name = Debug; 483 | }; 484 | 97C147041CF9000F007C117D /* Release */ = { 485 | isa = XCBuildConfiguration; 486 | buildSettings = { 487 | ALWAYS_SEARCH_USER_PATHS = NO; 488 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 489 | CLANG_ANALYZER_NONNULL = YES; 490 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 491 | CLANG_CXX_LIBRARY = "libc++"; 492 | CLANG_ENABLE_MODULES = YES; 493 | CLANG_ENABLE_OBJC_ARC = YES; 494 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 495 | CLANG_WARN_BOOL_CONVERSION = YES; 496 | CLANG_WARN_COMMA = YES; 497 | CLANG_WARN_CONSTANT_CONVERSION = YES; 498 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 499 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 500 | CLANG_WARN_EMPTY_BODY = YES; 501 | CLANG_WARN_ENUM_CONVERSION = YES; 502 | CLANG_WARN_INFINITE_RECURSION = YES; 503 | CLANG_WARN_INT_CONVERSION = YES; 504 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 505 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 506 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 507 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 508 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 509 | CLANG_WARN_STRICT_PROTOTYPES = YES; 510 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 511 | CLANG_WARN_UNREACHABLE_CODE = YES; 512 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 513 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 514 | COPY_PHASE_STRIP = NO; 515 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 516 | ENABLE_NS_ASSERTIONS = NO; 517 | ENABLE_STRICT_OBJC_MSGSEND = YES; 518 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 519 | GCC_C_LANGUAGE_STANDARD = gnu99; 520 | GCC_NO_COMMON_BLOCKS = YES; 521 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 522 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 523 | GCC_WARN_UNDECLARED_SELECTOR = YES; 524 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 525 | GCC_WARN_UNUSED_FUNCTION = YES; 526 | GCC_WARN_UNUSED_VARIABLE = YES; 527 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 528 | MTL_ENABLE_DEBUG_INFO = NO; 529 | SDKROOT = iphoneos; 530 | SUPPORTED_PLATFORMS = iphoneos; 531 | SWIFT_COMPILATION_MODE = wholemodule; 532 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 533 | TARGETED_DEVICE_FAMILY = "1,2"; 534 | VALIDATE_PRODUCT = YES; 535 | }; 536 | name = Release; 537 | }; 538 | 97C147061CF9000F007C117D /* Debug */ = { 539 | isa = XCBuildConfiguration; 540 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 541 | buildSettings = { 542 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 543 | CLANG_ENABLE_MODULES = YES; 544 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 545 | DEVELOPMENT_TEAM = G2KAKX49M6; 546 | ENABLE_BITCODE = NO; 547 | INFOPLIST_FILE = Runner/Info.plist; 548 | LD_RUNPATH_SEARCH_PATHS = ( 549 | "$(inherited)", 550 | "@executable_path/Frameworks", 551 | ); 552 | PRODUCT_BUNDLE_IDENTIFIER = io.adaptiv.flutterKit; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 555 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 556 | SWIFT_VERSION = 5.0; 557 | VERSIONING_SYSTEM = "apple-generic"; 558 | }; 559 | name = Debug; 560 | }; 561 | 97C147071CF9000F007C117D /* Release */ = { 562 | isa = XCBuildConfiguration; 563 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 564 | buildSettings = { 565 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 566 | CLANG_ENABLE_MODULES = YES; 567 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 568 | DEVELOPMENT_TEAM = G2KAKX49M6; 569 | ENABLE_BITCODE = NO; 570 | INFOPLIST_FILE = Runner/Info.plist; 571 | LD_RUNPATH_SEARCH_PATHS = ( 572 | "$(inherited)", 573 | "@executable_path/Frameworks", 574 | ); 575 | PRODUCT_BUNDLE_IDENTIFIER = io.adaptiv.flutterKit; 576 | PRODUCT_NAME = "$(TARGET_NAME)"; 577 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 578 | SWIFT_VERSION = 5.0; 579 | VERSIONING_SYSTEM = "apple-generic"; 580 | }; 581 | name = Release; 582 | }; 583 | /* End XCBuildConfiguration section */ 584 | 585 | /* Begin XCConfigurationList section */ 586 | 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { 587 | isa = XCConfigurationList; 588 | buildConfigurations = ( 589 | 331C8088294A63A400263BE5 /* Debug */, 590 | 331C8089294A63A400263BE5 /* Release */, 591 | 331C808A294A63A400263BE5 /* Profile */, 592 | ); 593 | defaultConfigurationIsVisible = 0; 594 | defaultConfigurationName = Release; 595 | }; 596 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 597 | isa = XCConfigurationList; 598 | buildConfigurations = ( 599 | 97C147031CF9000F007C117D /* Debug */, 600 | 97C147041CF9000F007C117D /* Release */, 601 | 249021D3217E4FDB00AE95B9 /* Profile */, 602 | ); 603 | defaultConfigurationIsVisible = 0; 604 | defaultConfigurationName = Release; 605 | }; 606 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 607 | isa = XCConfigurationList; 608 | buildConfigurations = ( 609 | 97C147061CF9000F007C117D /* Debug */, 610 | 97C147071CF9000F007C117D /* Release */, 611 | 249021D4217E4FDB00AE95B9 /* Profile */, 612 | ); 613 | defaultConfigurationIsVisible = 0; 614 | defaultConfigurationName = Release; 615 | }; 616 | /* End XCConfigurationList section */ 617 | }; 618 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 619 | } 620 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 64 | 66 | 72 | 73 | 74 | 75 | 81 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevenosse/flutter_kit/e6420550fb124650272cc33f54fb4a23244800f4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Flutter Kit 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_kit 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 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/gen/assets.gen.dart: -------------------------------------------------------------------------------- 1 | /// GENERATED CODE - DO NOT MODIFY BY HAND 2 | /// ***************************************************** 3 | /// FlutterGen 4 | /// ***************************************************** 5 | 6 | // coverage:ignore-file 7 | // ignore_for_file: type=lint 8 | // ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use 9 | 10 | import 'package:flutter/widgets.dart'; 11 | 12 | class $AssetsImagesGen { 13 | const $AssetsImagesGen(); 14 | 15 | /// File path: assets/images/apple-logo.svg 16 | String get appleLogo => 'assets/images/apple-logo.svg'; 17 | 18 | /// File path: assets/images/google-logo.svg 19 | String get googleLogo => 'assets/images/google-logo.svg'; 20 | 21 | /// List of all assets 22 | List get values => [appleLogo, googleLogo]; 23 | } 24 | 25 | class Assets { 26 | Assets._(); 27 | 28 | static const $AssetsImagesGen images = $AssetsImagesGen(); 29 | } 30 | 31 | class AssetGenImage { 32 | const AssetGenImage(this._assetName); 33 | 34 | final String _assetName; 35 | 36 | Image image({ 37 | Key? key, 38 | AssetBundle? bundle, 39 | ImageFrameBuilder? frameBuilder, 40 | ImageErrorWidgetBuilder? errorBuilder, 41 | String? semanticLabel, 42 | bool excludeFromSemantics = false, 43 | double? scale, 44 | double? width, 45 | double? height, 46 | Color? color, 47 | Animation? opacity, 48 | BlendMode? colorBlendMode, 49 | BoxFit? fit, 50 | AlignmentGeometry alignment = Alignment.center, 51 | ImageRepeat repeat = ImageRepeat.noRepeat, 52 | Rect? centerSlice, 53 | bool matchTextDirection = false, 54 | bool gaplessPlayback = false, 55 | bool isAntiAlias = false, 56 | String? package, 57 | FilterQuality filterQuality = FilterQuality.low, 58 | int? cacheWidth, 59 | int? cacheHeight, 60 | }) { 61 | return Image.asset( 62 | _assetName, 63 | key: key, 64 | bundle: bundle, 65 | frameBuilder: frameBuilder, 66 | errorBuilder: errorBuilder, 67 | semanticLabel: semanticLabel, 68 | excludeFromSemantics: excludeFromSemantics, 69 | scale: scale, 70 | width: width, 71 | height: height, 72 | color: color, 73 | opacity: opacity, 74 | colorBlendMode: colorBlendMode, 75 | fit: fit, 76 | alignment: alignment, 77 | repeat: repeat, 78 | centerSlice: centerSlice, 79 | matchTextDirection: matchTextDirection, 80 | gaplessPlayback: gaplessPlayback, 81 | isAntiAlias: isAntiAlias, 82 | package: package, 83 | filterQuality: filterQuality, 84 | cacheWidth: cacheWidth, 85 | cacheHeight: cacheHeight, 86 | ); 87 | } 88 | 89 | ImageProvider provider({ 90 | AssetBundle? bundle, 91 | String? package, 92 | }) { 93 | return AssetImage( 94 | _assetName, 95 | bundle: bundle, 96 | package: package, 97 | ); 98 | } 99 | 100 | String get path => _assetName; 101 | 102 | String get keyName => _assetName; 103 | } 104 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_kit/src/core/app_initializer.dart'; 5 | import 'package:flutter_kit/src/core/application.dart'; 6 | 7 | void main() { 8 | final AppInitializer appInitializer = AppInitializer(); 9 | 10 | runZonedGuarded( 11 | () async { 12 | WidgetsFlutterBinding.ensureInitialized(); 13 | 14 | await appInitializer.preAppRun(); 15 | 16 | runApp(Application()); 17 | 18 | appInitializer.postAppRun(); 19 | }, 20 | (error, stack) {}, 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/core/app_initializer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_kit/src/shared/locator.dart'; 5 | import 'package:flutter_kit/src/shared/services/storage/storage.dart'; 6 | 7 | class AppInitializer { 8 | /// Initialize services, plugins, etc. before the app runs. 9 | Future preAppRun() async { 10 | await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); 11 | 12 | await locator().init(); 13 | } 14 | 15 | /// Initialize services, plugins, etc. after the app runs. 16 | Future postAppRun() async { 17 | // Hide RSOD in release mode. 18 | if (kReleaseMode) { 19 | ErrorWidget.builder = (FlutterErrorDetails details) => const SizedBox(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/core/application.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_kit/src/core/environment.dart'; 4 | import 'package:flutter_kit/src/core/i18n/l10n.dart'; 5 | import 'package:flutter_kit/src/core/routing/app_router.dart'; 6 | import 'package:flutter_kit/src/core/theme/app_theme.dart'; 7 | import 'package:flutter_kit/src/shared/locator.dart'; 8 | import 'package:flutter_localizations/flutter_localizations.dart'; 9 | 10 | class Application extends StatelessWidget { 11 | final AppRouter _appRouter; 12 | 13 | Application({ 14 | super.key, 15 | AppRouter? appRouter, 16 | }) : _appRouter = appRouter ?? locator(); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return MaterialApp.router( 21 | title: Environment.appName, 22 | routerConfig: _appRouter.config( 23 | navigatorObservers: () => [ 24 | AutoRouteObserver(), 25 | ], 26 | ), 27 | theme: AppTheme.lightTheme, 28 | darkTheme: AppTheme.darkTheme, 29 | debugShowCheckedModeBanner: false, 30 | localizationsDelegates: const [ 31 | I18n.delegate, 32 | GlobalMaterialLocalizations.delegate, 33 | GlobalWidgetsLocalizations.delegate, 34 | GlobalCupertinoLocalizations.delegate, 35 | ], 36 | supportedLocales: I18n.delegate.supportedLocales, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/core/environment.dart: -------------------------------------------------------------------------------- 1 | class Environment { 2 | static const String environment = String.fromEnvironment('env', defaultValue: 'dev'); 3 | static const String baseUrl = 4 | String.fromEnvironment('base_url', defaultValue: 'https://jsonplaceholder.typicode.com'); 5 | static const String appName = String.fromEnvironment('app_name', defaultValue: 'Flutter Kit'); 6 | } 7 | -------------------------------------------------------------------------------- /lib/src/core/i18n/intl/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | 5 | // Ignore issues from commonly used lints in this file. 6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new 7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering 8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment 9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases 10 | // ignore_for_file:comment_references 11 | 12 | import 'dart:async'; 13 | 14 | import 'package:flutter/foundation.dart'; 15 | import 'package:intl/intl.dart'; 16 | import 'package:intl/message_lookup_by_library.dart'; 17 | import 'package:intl/src/intl_helpers.dart'; 18 | 19 | import 'messages_en.dart' as messages_en; 20 | 21 | typedef Future LibraryLoader(); 22 | Map _deferredLibraries = { 23 | 'en': () => new SynchronousFuture(null), 24 | }; 25 | 26 | MessageLookupByLibrary? _findExact(String localeName) { 27 | switch (localeName) { 28 | case 'en': 29 | return messages_en.messages; 30 | default: 31 | return null; 32 | } 33 | } 34 | 35 | /// User programs should call this before using [localeName] for messages. 36 | Future initializeMessages(String localeName) { 37 | var availableLocale = Intl.verifiedLocale( 38 | localeName, 39 | (locale) => _deferredLibraries[locale] != null, 40 | onFailure: (_) => null, 41 | ); 42 | if (availableLocale == null) { 43 | return new SynchronousFuture(false); 44 | } 45 | var lib = _deferredLibraries[availableLocale]; 46 | lib == null ? new SynchronousFuture(false) : lib(); 47 | initializeInternalMessageLookup(() => new CompositeMessageLookup()); 48 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); 49 | return new SynchronousFuture(true); 50 | } 51 | 52 | bool _messagesExistFor(String locale) { 53 | try { 54 | return _findExact(locale) != null; 55 | } catch (e) { 56 | return false; 57 | } 58 | } 59 | 60 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { 61 | var actualLocale = Intl.verifiedLocale( 62 | locale, 63 | _messagesExistFor, 64 | onFailure: (_) => null, 65 | ); 66 | if (actualLocale == null) return null; 67 | return _findExact(actualLocale); 68 | } 69 | -------------------------------------------------------------------------------- /lib/src/core/i18n/intl/messages_en.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a en locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'en'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "dialog_cancel": MessageLookupByLibrary.simpleMessage("Cancel"), 26 | "dialog_confirm": MessageLookupByLibrary.simpleMessage("Confirm"), 27 | "loadingDialog_content": MessageLookupByLibrary.simpleMessage( 28 | "Please wait...", 29 | ), 30 | "login_appleBtnLabel": MessageLookupByLibrary.simpleMessage( 31 | "Sign in with Apple", 32 | ), 33 | "login_emailHint": MessageLookupByLibrary.simpleMessage( 34 | "Ex: KpNqg@example.com", 35 | ), 36 | "login_emailLabel": MessageLookupByLibrary.simpleMessage("Email"), 37 | "login_forgotPasswordLabel": MessageLookupByLibrary.simpleMessage( 38 | "Forgot password?", 39 | ), 40 | "login_googleBtnLabel": MessageLookupByLibrary.simpleMessage( 41 | "Sign in with Google", 42 | ), 43 | "login_passwordHint": MessageLookupByLibrary.simpleMessage("************"), 44 | "login_passwordLabel": MessageLookupByLibrary.simpleMessage("Password"), 45 | "login_submitBtnLabel": MessageLookupByLibrary.simpleMessage("Login"), 46 | "login_subtitle": MessageLookupByLibrary.simpleMessage( 47 | "Let\'s continue where we left off.", 48 | ), 49 | "login_title": MessageLookupByLibrary.simpleMessage("Login"), 50 | "or": MessageLookupByLibrary.simpleMessage("OR"), 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/core/i18n/l10n.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'intl/messages_all.dart'; 5 | 6 | // ************************************************************************** 7 | // Generator: Flutter Intl IDE plugin 8 | // Made by Localizely 9 | // ************************************************************************** 10 | 11 | // ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars 12 | // ignore_for_file: join_return_with_assignment, prefer_final_in_for_each 13 | // ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes 14 | 15 | class I18n { 16 | I18n(); 17 | 18 | static I18n? _current; 19 | 20 | static I18n get current { 21 | assert( 22 | _current != null, 23 | 'No instance of I18n was loaded. Try to initialize the I18n delegate before accessing I18n.current.', 24 | ); 25 | return _current!; 26 | } 27 | 28 | static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); 29 | 30 | static Future load(Locale locale) { 31 | final name = (locale.countryCode?.isEmpty ?? false) 32 | ? locale.languageCode 33 | : locale.toString(); 34 | final localeName = Intl.canonicalizedLocale(name); 35 | return initializeMessages(localeName).then((_) { 36 | Intl.defaultLocale = localeName; 37 | final instance = I18n(); 38 | I18n._current = instance; 39 | 40 | return instance; 41 | }); 42 | } 43 | 44 | static I18n of(BuildContext context) { 45 | final instance = I18n.maybeOf(context); 46 | assert( 47 | instance != null, 48 | 'No instance of I18n present in the widget tree. Did you add I18n.delegate in localizationsDelegates?', 49 | ); 50 | return instance!; 51 | } 52 | 53 | static I18n? maybeOf(BuildContext context) { 54 | return Localizations.of(context, I18n); 55 | } 56 | 57 | /// `Login` 58 | String get login_title { 59 | return Intl.message('Login', name: 'login_title', desc: '', args: []); 60 | } 61 | 62 | /// `Let's continue where we left off.` 63 | String get login_subtitle { 64 | return Intl.message( 65 | 'Let\'s continue where we left off.', 66 | name: 'login_subtitle', 67 | desc: '', 68 | args: [], 69 | ); 70 | } 71 | 72 | /// `Email` 73 | String get login_emailLabel { 74 | return Intl.message('Email', name: 'login_emailLabel', desc: '', args: []); 75 | } 76 | 77 | /// `Ex: KpNqg@example.com` 78 | String get login_emailHint { 79 | return Intl.message( 80 | 'Ex: KpNqg@example.com', 81 | name: 'login_emailHint', 82 | desc: '', 83 | args: [], 84 | ); 85 | } 86 | 87 | /// `Password` 88 | String get login_passwordLabel { 89 | return Intl.message( 90 | 'Password', 91 | name: 'login_passwordLabel', 92 | desc: '', 93 | args: [], 94 | ); 95 | } 96 | 97 | /// `************` 98 | String get login_passwordHint { 99 | return Intl.message( 100 | '************', 101 | name: 'login_passwordHint', 102 | desc: '', 103 | args: [], 104 | ); 105 | } 106 | 107 | /// `Forgot password?` 108 | String get login_forgotPasswordLabel { 109 | return Intl.message( 110 | 'Forgot password?', 111 | name: 'login_forgotPasswordLabel', 112 | desc: '', 113 | args: [], 114 | ); 115 | } 116 | 117 | /// `Sign in with Google` 118 | String get login_googleBtnLabel { 119 | return Intl.message( 120 | 'Sign in with Google', 121 | name: 'login_googleBtnLabel', 122 | desc: '', 123 | args: [], 124 | ); 125 | } 126 | 127 | /// `Sign in with Apple` 128 | String get login_appleBtnLabel { 129 | return Intl.message( 130 | 'Sign in with Apple', 131 | name: 'login_appleBtnLabel', 132 | desc: '', 133 | args: [], 134 | ); 135 | } 136 | 137 | /// `Login` 138 | String get login_submitBtnLabel { 139 | return Intl.message( 140 | 'Login', 141 | name: 'login_submitBtnLabel', 142 | desc: '', 143 | args: [], 144 | ); 145 | } 146 | 147 | /// `Please wait...` 148 | String get loadingDialog_content { 149 | return Intl.message( 150 | 'Please wait...', 151 | name: 'loadingDialog_content', 152 | desc: '', 153 | args: [], 154 | ); 155 | } 156 | 157 | /// `Cancel` 158 | String get dialog_cancel { 159 | return Intl.message('Cancel', name: 'dialog_cancel', desc: '', args: []); 160 | } 161 | 162 | /// `Confirm` 163 | String get dialog_confirm { 164 | return Intl.message('Confirm', name: 'dialog_confirm', desc: '', args: []); 165 | } 166 | 167 | /// `OR` 168 | String get or { 169 | return Intl.message('OR', name: 'or', desc: '', args: []); 170 | } 171 | } 172 | 173 | class AppLocalizationDelegate extends LocalizationsDelegate { 174 | const AppLocalizationDelegate(); 175 | 176 | List get supportedLocales { 177 | return const [Locale.fromSubtags(languageCode: 'en')]; 178 | } 179 | 180 | @override 181 | bool isSupported(Locale locale) => _isSupported(locale); 182 | @override 183 | Future load(Locale locale) => I18n.load(locale); 184 | @override 185 | bool shouldReload(AppLocalizationDelegate old) => false; 186 | 187 | bool _isSupported(Locale locale) { 188 | for (var supportedLocale in supportedLocales) { 189 | if (supportedLocale.languageCode == locale.languageCode) { 190 | return true; 191 | } 192 | } 193 | return false; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /lib/src/core/i18n/l10n/intl_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "@@locale": "en", 3 | 4 | "login_title": "Login", 5 | "login_subtitle": "Let's continue where we left off.", 6 | "login_emailLabel": "Email", 7 | "login_emailHint": "Ex: KpNqg@example.com", 8 | "login_passwordLabel": "Password", 9 | "login_passwordHint": "************", 10 | "login_forgotPasswordLabel": "Forgot password?", 11 | "login_googleBtnLabel": "Sign in with Google", 12 | "login_appleBtnLabel": "Sign in with Apple", 13 | "login_submitBtnLabel": "Login", 14 | 15 | "loadingDialog_content": "Please wait...", 16 | 17 | "dialog_cancel": "Cancel", 18 | "dialog_confirm": "Confirm", 19 | 20 | "or": "OR" 21 | } -------------------------------------------------------------------------------- /lib/src/core/routing/app_router.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter_kit/src/features/login/ui/login_screen.dart'; 3 | 4 | part 'app_router.gr.dart'; 5 | 6 | @AutoRouterConfig(replaceInRouteName: 'Screen,Route') 7 | class AppRouter extends RootStackRouter { 8 | @override 9 | List routes = [ 10 | AutoRoute(page: LoginRoute.page, initial: true), 11 | ]; 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/core/routing/app_router.gr.dart: -------------------------------------------------------------------------------- 1 | // dart format width=80 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | 4 | // ************************************************************************** 5 | // AutoRouterGenerator 6 | // ************************************************************************** 7 | 8 | // ignore_for_file: type=lint 9 | // coverage:ignore-file 10 | 11 | part of 'app_router.dart'; 12 | 13 | /// generated route for 14 | /// [LoginScreen] 15 | class LoginRoute extends PageRouteInfo { 16 | const LoginRoute({List? children}) 17 | : super(LoginRoute.name, initialChildren: children); 18 | 19 | static const String name = 'LoginRoute'; 20 | 21 | static PageInfo page = PageInfo( 22 | name, 23 | builder: (data) { 24 | return WrappedRoute(child: const LoginScreen()); 25 | }, 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/core/theme/app_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_kit/src/core/theme/color_schemes.dart'; 3 | import 'package:flutter_kit/src/core/theme/custom_colors.dart'; 4 | 5 | class AppTheme { 6 | static const _fontFamily = 'GeneralSans'; 7 | 8 | static ThemeData _buildTheme({required Brightness brightness}) { 9 | final ColorScheme colors = brightness == Brightness.light ? lightColorScheme : darkColorScheme; 10 | 11 | return ThemeData( 12 | useMaterial3: true, 13 | textTheme: _buildTextTheme(colors: colors), 14 | appBarTheme: AppBarTheme( 15 | surfaceTintColor: Colors.transparent, 16 | backgroundColor: colors.surface, 17 | ), 18 | cardTheme: CardThemeData( 19 | color: colors.surface, 20 | shadowColor: colors.shadow.withValues(alpha: .5), 21 | ), 22 | colorScheme: colors, 23 | extensions: [brightness == Brightness.light ? CustomColors.light : CustomColors.dark], 24 | ); 25 | } 26 | 27 | static TextTheme _buildTextTheme({required ColorScheme colors}) { 28 | return TextTheme( 29 | displayLarge: TextStyle( 30 | fontFamily: _fontFamily, 31 | fontWeight: FontWeight.w700, 32 | fontSize: 32, 33 | color: colors.onSurface, 34 | ), 35 | headlineLarge: TextStyle( 36 | fontFamily: _fontFamily, 37 | fontWeight: FontWeight.w700, 38 | fontSize: 28, 39 | color: colors.onSurface, 40 | ), 41 | titleLarge: TextStyle( 42 | fontFamily: _fontFamily, 43 | fontWeight: FontWeight.bold, 44 | fontSize: 24, 45 | color: colors.onSurface, 46 | ), 47 | titleMedium: TextStyle( 48 | fontFamily: _fontFamily, 49 | fontWeight: FontWeight.w700, 50 | fontSize: 20, 51 | color: colors.onSurface, 52 | ), 53 | titleSmall: TextStyle( 54 | fontFamily: _fontFamily, 55 | fontWeight: FontWeight.w700, 56 | fontSize: 18, 57 | color: colors.onSurface, 58 | ), 59 | bodyLarge: TextStyle( 60 | fontFamily: _fontFamily, 61 | fontWeight: FontWeight.w600, 62 | fontSize: 16, 63 | color: colors.onSurface, 64 | ), 65 | bodyMedium: TextStyle( 66 | fontFamily: _fontFamily, 67 | fontWeight: FontWeight.w500, 68 | fontSize: 16, 69 | color: colors.onSurface, 70 | ), 71 | bodySmall: TextStyle( 72 | fontFamily: _fontFamily, 73 | fontWeight: FontWeight.w500, 74 | fontSize: 14, 75 | color: colors.onSurface, 76 | ), 77 | labelLarge: TextStyle( 78 | fontFamily: _fontFamily, 79 | fontWeight: FontWeight.w500, 80 | fontSize: 14, 81 | color: colors.onSurface, 82 | ), 83 | labelMedium: TextStyle( 84 | fontFamily: _fontFamily, 85 | fontWeight: FontWeight.w500, 86 | fontSize: 14, 87 | color: colors.onSurface, 88 | ), 89 | labelSmall: TextStyle( 90 | fontFamily: _fontFamily, 91 | fontWeight: FontWeight.w500, 92 | fontSize: 10, 93 | color: colors.onSurface, 94 | ), 95 | ); 96 | } 97 | 98 | static final ThemeData lightTheme = _buildTheme(brightness: Brightness.light); 99 | 100 | static final ThemeData darkTheme = _buildTheme(brightness: Brightness.dark); 101 | } 102 | -------------------------------------------------------------------------------- /lib/src/core/theme/color_scheme_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'custom_colors.dart'; 3 | 4 | extension ColorSchemeExtension on ColorScheme { 5 | CustomColors get custom => brightness == Brightness.light ? CustomColors.light : CustomColors.dark; 6 | 7 | // For ease of use, creating extensions for custom colors 8 | Color get success => custom.success; 9 | Color get onSuccess => custom.onSuccess; 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/core/theme/color_schemes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const lightColorScheme = ColorScheme( 4 | brightness: Brightness.light, 5 | primary: Color(0xFF6750A4), 6 | onPrimary: Color(0xFFFFFFFF), 7 | primaryContainer: Color(0xFFEADDFF), 8 | onPrimaryContainer: Color(0xFF21005D), 9 | secondary: Color(0xFF625B71), 10 | onSecondary: Color(0xFFFFFFFF), 11 | secondaryContainer: Color(0xFFE8DEF8), 12 | onSecondaryContainer: Color(0xFF1D192B), 13 | tertiary: Color(0xFF7D5260), 14 | onTertiary: Color(0xFFFFFFFF), 15 | tertiaryContainer: Color(0xFFFFD8E4), 16 | onTertiaryContainer: Color(0xFF31111D), 17 | error: Color(0xFFB3261E), 18 | onError: Color(0xFFFFFFFF), 19 | errorContainer: Color(0xFFF9DEDC), 20 | onErrorContainer: Color(0xFF410E0B), 21 | outline: Color(0xFF79747E), 22 | surface: Color(0XFFF4F4F5), 23 | onSurface: Color(0XFF09090B), 24 | onSurfaceVariant: Color(0xFF49454F), 25 | inverseSurface: Color(0xFF313033), 26 | onInverseSurface: Color(0xFFF4EFF4), 27 | inversePrimary: Color(0xFFD0BCFF), 28 | shadow: Color(0xFF000000), 29 | surfaceTint: Color(0xFF6750A4), 30 | outlineVariant: Color(0xFFCAC4D0), 31 | scrim: Color(0xFF000000), 32 | ); 33 | 34 | const darkColorScheme = ColorScheme( 35 | brightness: Brightness.dark, 36 | primary: Color(0xFFD0BCFF), 37 | onPrimary: Color(0xFF381E72), 38 | primaryContainer: Color(0xFF4F378B), 39 | onPrimaryContainer: Color(0xFFEADDFF), 40 | secondary: Color(0xFFCCC2DC), 41 | onSecondary: Color(0xFF332D41), 42 | secondaryContainer: Color(0xFF4A4458), 43 | onSecondaryContainer: Color(0xFFE8DEF8), 44 | tertiary: Color(0xFFEFB8C8), 45 | onTertiary: Color(0xFF492532), 46 | tertiaryContainer: Color(0xFF633B48), 47 | onTertiaryContainer: Color(0xFFFFD8E4), 48 | error: Color(0xFFF2B8B5), 49 | onError: Color(0xFF601410), 50 | errorContainer: Color(0xFF8C1D18), 51 | onErrorContainer: Color(0xFFF9DEDC), 52 | outline: Color(0xFF938F99), 53 | surface: Color.fromARGB(255, 37, 37, 41), 54 | onSurface: Color.fromARGB(255, 226, 227, 216), 55 | onSurfaceVariant: Color(0xFFCAC4D0), 56 | inverseSurface: Color(0xFFE6E1E5), 57 | onInverseSurface: Color(0xFF313033), 58 | inversePrimary: Color(0xFF6750A4), 59 | shadow: Color(0xFF000000), 60 | surfaceTint: Color(0xFFD0BCFF), 61 | outlineVariant: Color(0xFF49454F), 62 | scrim: Color(0xFF000000), 63 | ); 64 | -------------------------------------------------------------------------------- /lib/src/core/theme/custom_colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | @immutable 4 | class CustomColors extends ThemeExtension { 5 | final Color success; 6 | final Color onSuccess; 7 | 8 | const CustomColors({ 9 | required this.success, 10 | required this.onSuccess, 11 | }); 12 | 13 | @override 14 | CustomColors copyWith({ 15 | Color? success, 16 | Color? onSuccess, 17 | }) { 18 | return CustomColors( 19 | success: success ?? this.success, 20 | onSuccess: onSuccess ?? this.onSuccess, 21 | ); 22 | } 23 | 24 | @override 25 | CustomColors lerp(ThemeExtension? other, double t) { 26 | if (other is! CustomColors) { 27 | return this; 28 | } 29 | return CustomColors( 30 | success: Color.lerp(success, other.success, t)!, 31 | onSuccess: Color.lerp(onSuccess, other.onSuccess, t)!, 32 | ); 33 | } 34 | 35 | static const light = CustomColors( 36 | success: Color(0xFF4CAF50), 37 | onSuccess: Color(0xFFFFFFFF), 38 | ); 39 | 40 | static const dark = CustomColors( 41 | success: Color(0xFF81C784), 42 | onSuccess: Color(0xFF1B5E20), 43 | ); 44 | } -------------------------------------------------------------------------------- /lib/src/core/theme/dimens.dart: -------------------------------------------------------------------------------- 1 | class Dimens { 2 | static const double minSpacing = 4.0; 3 | static const double halfSpacing = 8.0; 4 | static const double spacing = 16.0; 5 | static const double doubleSpacing = 32.0; 6 | static const double tripleSpacing = 48.0; 7 | 8 | static const double radius = 12.0; 9 | static const double doubleRadius = 24.0; 10 | static const double fullRadius = 999.0; 11 | 12 | static const double iconSize = 24.0; 13 | static const double iconSizeM = 20.0; 14 | static const double iconSizeS = 18.0; 15 | 16 | static const double loaderSize = 25.0; 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/datasource/http/dio_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter_kit/src/core/environment.dart'; 5 | 6 | final _logInterceptor = LogInterceptor( 7 | logPrint: (object) => log(object.toString()), 8 | request: true, 9 | requestHeader: true, 10 | requestBody: true, 11 | responseBody: true, 12 | ); 13 | 14 | class DioConfig { 15 | final Dio dio; 16 | 17 | DioConfig({Dio? dio}) 18 | : dio = dio ?? 19 | Dio( 20 | BaseOptions( 21 | baseUrl: Environment.baseUrl, 22 | headers: { 23 | 'Accept': 'application/json', 24 | }, 25 | contentType: 'application/json', 26 | ), 27 | ) { 28 | this.dio.interceptors.add(_logInterceptor); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/datasource/http/example_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class ExampleApi { 4 | final Dio dio; 5 | 6 | ExampleApi({required this.dio}); 7 | 8 | Future getExample() async { 9 | await Future.delayed(const Duration(seconds: 2)); 10 | return 'Example'; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/datasource/http/http_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter_kit/src/datasource/models/api_response/api_response.dart'; 3 | 4 | ApiError createApiErrorFromDioError(DioException error) { 5 | return ApiError( 6 | type: _getTypeFromDioError(error), 7 | statusCode: error.response?.statusCode ?? 0, 8 | error: error, 9 | ); 10 | } 11 | 12 | ApiError unknownError(Object? e) { 13 | return ApiError( 14 | error: e, 15 | statusCode: 0, 16 | type: ApiErrorType.unknown, 17 | ); 18 | } 19 | 20 | ApiErrorType _getTypeFromDioError(DioException error) { 21 | if (error.response?.statusCode.toString().startsWith('5') ?? false) { 22 | return ApiErrorType.server; 23 | } else if (error.response?.statusCode.toString().startsWith('4') ?? false) { 24 | return ApiErrorType.user; 25 | } else if (error.response == null) { 26 | return ApiErrorType.network; 27 | } 28 | return ApiErrorType.unknown; 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/datasource/models/api_response/api_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | sealed class ApiResponse { 4 | const ApiResponse(); 5 | 6 | factory ApiResponse.success(R response) = ApiResponseSuccess; 7 | factory ApiResponse.error(E response) = ApiResponseError; 8 | 9 | void when({ 10 | required void Function(R data) success, 11 | required void Function(E error) error, 12 | }) { 13 | switch (this) { 14 | case ApiResponseSuccess success_: 15 | success(success_.response); 16 | case ApiResponseError error_: 17 | error(error_.response); 18 | } 19 | } 20 | } 21 | 22 | final class ApiResponseSuccess extends ApiResponse { 23 | final R response; 24 | 25 | const ApiResponseSuccess(this.response); 26 | } 27 | 28 | final class ApiResponseError extends ApiResponse { 29 | final E response; 30 | 31 | const ApiResponseError(this.response); 32 | } 33 | 34 | enum ApiErrorType { 35 | /// Something went wrong on the server side 36 | server, 37 | 38 | /// Something's wrong with the network request 39 | network, 40 | 41 | /// User did something wrong 42 | user, 43 | 44 | /// The error is unidentified 45 | unknown, 46 | } 47 | 48 | class ApiError extends Equatable { 49 | final ApiErrorType type; 50 | final int statusCode; 51 | final T error; 52 | 53 | const ApiError({ 54 | required this.type, 55 | required this.error, 56 | required this.statusCode, 57 | }); 58 | 59 | @override 60 | List get props => [type, error]; 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/datasource/repositories/base_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter_kit/src/datasource/http/http_util.dart'; 3 | import 'package:flutter_kit/src/datasource/models/api_response/api_response.dart'; 4 | 5 | typedef ApiCall = Future Function(); 6 | 7 | abstract class BaseRepository { 8 | Future> runApiCall({required ApiCall> call}) async { 9 | try { 10 | final response = await call(); 11 | return response; 12 | } on DioException catch (e) { 13 | return ApiResponse.error(createApiErrorFromDioError(e)); 14 | } catch (e) { 15 | return ApiResponse.error(unknownError(e)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/datasource/repositories/example_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_kit/src/datasource/http/dio_config.dart'; 2 | import 'package:flutter_kit/src/datasource/http/example_api.dart'; 3 | import 'package:flutter_kit/src/datasource/models/api_response/api_response.dart'; 4 | import 'package:flutter_kit/src/datasource/repositories/base_repository.dart'; 5 | import 'package:flutter_kit/src/shared/locator.dart'; 6 | 7 | class ExampleRepository extends BaseRepository { 8 | final ExampleApi exampleApi; 9 | 10 | ExampleRepository({ 11 | ExampleApi? exampleApi, 12 | }) : exampleApi = exampleApi ?? ExampleApi(dio: locator().dio); 13 | 14 | Future> getExample() async { 15 | return runApiCall( 16 | call: () async { 17 | final response = await exampleApi.getExample(); 18 | return ApiResponse.success(response); 19 | }, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/features/login/logic/login_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:flutter_kit/src/datasource/models/api_response/api_response.dart'; 4 | import 'package:flutter_kit/src/datasource/repositories/example_repository.dart'; 5 | import 'package:flutter_kit/src/shared/locator.dart'; 6 | 7 | part 'login_state.dart'; 8 | 9 | class LoginController extends ValueNotifier { 10 | final ExampleRepository _exampleRepository; 11 | 12 | LoginController({ 13 | ExampleRepository? exampleRepository, 14 | }) : _exampleRepository = exampleRepository ?? locator(), 15 | super(LoginInitial(email: '', password: '')); 16 | 17 | void onEmailChanged(String email) { 18 | value = LoginInitial(email: email, password: value.password); 19 | } 20 | 21 | void onPasswordChanged(String password) { 22 | value = LoginInitial(password: password, email: value.email); 23 | } 24 | 25 | Future login() async { 26 | value = LoginLoading(email: value.email, password: value.password); 27 | 28 | final response = await _exampleRepository.getExample(); 29 | 30 | response.when( 31 | success: (data) => value = LoginSuccess(email: value.email, password: value.password, response: data), 32 | error: (error) => value = LoginError(email: value.email, password: value.password, error: error), 33 | ); 34 | } 35 | 36 | void reset() => value = LoginInitial(email: '', password: ''); 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/features/login/logic/login_state.dart: -------------------------------------------------------------------------------- 1 | part of 'login_controller.dart'; 2 | 3 | sealed class LoginState extends Equatable { 4 | final String email; 5 | final String password; 6 | 7 | const LoginState({ 8 | required this.email, 9 | required this.password, 10 | }); 11 | } 12 | 13 | final class LoginInitial extends LoginState { 14 | const LoginInitial({required super.email, required super.password}); 15 | 16 | @override 17 | List get props => [email, password]; 18 | } 19 | 20 | final class LoginLoading extends LoginState { 21 | const LoginLoading({required super.email, required super.password}); 22 | 23 | @override 24 | List get props => [email, password]; 25 | } 26 | 27 | final class LoginError extends LoginState { 28 | final ApiError error; 29 | 30 | const LoginError({ 31 | required super.email, 32 | required super.password, 33 | required this.error, 34 | }); 35 | 36 | @override 37 | List get props => [email, password, error]; 38 | } 39 | 40 | final class LoginSuccess extends LoginState { 41 | final Object response; 42 | 43 | const LoginSuccess({ 44 | required super.email, 45 | required super.password, 46 | required this.response, 47 | }); 48 | 49 | @override 50 | List get props => [email, password, response]; 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/features/login/ui/login_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:flutter_kit/gen/assets.gen.dart'; 5 | import 'package:flutter_kit/src/core/i18n/l10n.dart'; 6 | import 'package:flutter_kit/src/core/theme/dimens.dart'; 7 | import 'package:flutter_kit/src/features/login/logic/login_controller.dart'; 8 | import 'package:flutter_kit/src/shared/components/atoms/dividers/labeled_divider.dart'; 9 | import 'package:flutter_kit/src/shared/components/business/listenable_consumer.dart'; 10 | import 'package:flutter_kit/src/shared/components/buttons/button.dart'; 11 | import 'package:flutter_kit/src/shared/components/dialogs/loading_dialog.dart'; 12 | import 'package:flutter_kit/src/shared/components/forms/input.dart'; 13 | import 'package:flutter_kit/src/shared/extensions/context_extensions.dart'; 14 | import 'package:flutter_svg/flutter_svg.dart'; 15 | import 'package:iconsax_plus/iconsax_plus.dart'; 16 | 17 | @RoutePage() 18 | class LoginScreen extends StatefulWidget implements AutoRouteWrapper { 19 | const LoginScreen({super.key}); 20 | 21 | @override 22 | State createState() => _LoginScreenState(); 23 | 24 | @override 25 | Widget wrappedRoute(BuildContext context) { 26 | return ChangeNotifierProvider( 27 | create: (_) => LoginController(), 28 | child: this, 29 | ); 30 | } 31 | } 32 | 33 | class _LoginScreenState extends State { 34 | final GlobalKey _formKey = GlobalKey(); 35 | final TextEditingController _emailController = TextEditingController(); 36 | final TextEditingController _passwordController = TextEditingController(); 37 | 38 | bool _isPasswordVisible = false; 39 | 40 | @override 41 | void dispose() { 42 | _emailController.dispose(); 43 | _passwordController.dispose(); 44 | super.dispose(); 45 | } 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | final controller = Provider.of(context, listen: false); 50 | return ListenableConsumer( 51 | listenable: controller, 52 | listener: (context, notifier) { 53 | switch (notifier.value) { 54 | case LoginLoading(): 55 | LoadingDialog.show(context: context); 56 | case LoginSuccess(): 57 | LoadingDialog.hide(context: context); 58 | case LoginError(): 59 | LoadingDialog.hide(context: context); 60 | default: 61 | break; 62 | } 63 | }, 64 | builder: (context, notifier) { 65 | return Scaffold( 66 | appBar: AppBar(toolbarHeight: 0), 67 | body: SafeArea( 68 | child: ListView( 69 | padding: const EdgeInsets.all(Dimens.spacing), 70 | children: [ 71 | const SizedBox(height: Dimens.tripleSpacing), 72 | Text( 73 | I18n.of(context).login_title, 74 | style: context.textTheme.titleLarge, 75 | ), 76 | const SizedBox(height: Dimens.minSpacing), 77 | Text(I18n.of(context).login_subtitle, style: context.textTheme.bodyMedium), 78 | const SizedBox(height: Dimens.doubleSpacing), 79 | AutofillGroup( 80 | child: Form( 81 | key: _formKey, 82 | child: Column( 83 | crossAxisAlignment: CrossAxisAlignment.start, 84 | children: [ 85 | Input( 86 | autofillHints: const [AutofillHints.email], 87 | controller: _emailController, 88 | labelText: I18n.of(context).login_emailLabel, 89 | hintText: I18n.of(context).login_emailHint, 90 | onChanged: context.read().onEmailChanged, 91 | textInputAction: TextInputAction.next, 92 | ), 93 | const SizedBox(height: Dimens.spacing), 94 | Input( 95 | autofillHints: const [AutofillHints.password], 96 | controller: _passwordController, 97 | isPassword: !_isPasswordVisible, 98 | labelText: I18n.of(context).login_passwordLabel, 99 | hintText: I18n.of(context).login_passwordHint, 100 | onChanged: context.read().onPasswordChanged, 101 | textInputAction: TextInputAction.done, 102 | suffixIcon: IconButton( 103 | onPressed: () => setState(() => _isPasswordVisible = !_isPasswordVisible), 104 | icon: Icon( 105 | _isPasswordVisible ? IconsaxPlusBroken.eye : IconsaxPlusBroken.eye_slash, 106 | color: context.colorScheme.onSurface, 107 | ), 108 | ), 109 | onSubmitted: (_) => _onLogin(), 110 | ), 111 | ], 112 | ), 113 | ), 114 | ), 115 | const SizedBox(height: Dimens.spacing), 116 | Align( 117 | alignment: Alignment.centerRight, 118 | child: TextButton( 119 | onPressed: () {}, 120 | child: Text(I18n.of(context).login_forgotPasswordLabel), 121 | ), 122 | ), 123 | const SizedBox(height: Dimens.spacing), 124 | Button.primary( 125 | title: I18n.of(context).login_submitBtnLabel, 126 | onPressed: _onLogin, 127 | ), 128 | const SizedBox(height: Dimens.doubleSpacing), 129 | LabeledDivider( 130 | label: I18n.of(context).or, 131 | ), 132 | const SizedBox(height: Dimens.doubleSpacing), 133 | Button.outline( 134 | icon: SvgPicture.asset( 135 | Assets.images.googleLogo, 136 | width: Dimens.iconSize, 137 | height: Dimens.iconSize, 138 | ), 139 | title: I18n.of(context).login_googleBtnLabel, 140 | onPressed: () {}, 141 | ), 142 | const SizedBox(height: Dimens.spacing), 143 | Button.outline( 144 | icon: SvgPicture.asset( 145 | Assets.images.appleLogo, 146 | colorFilter: ColorFilter.mode(context.colorScheme.onSurface, BlendMode.srcIn), 147 | width: Dimens.iconSize, 148 | height: Dimens.iconSize, 149 | ), 150 | title: I18n.of(context).login_appleBtnLabel, 151 | onPressed: () {}, 152 | ), 153 | ], 154 | ), 155 | ), 156 | ); 157 | }, 158 | ); 159 | } 160 | 161 | void _onLogin() => context.read().login(); 162 | } 163 | -------------------------------------------------------------------------------- /lib/src/shared/components/app_snackbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_kit/src/core/theme/color_scheme_extension.dart'; 3 | import 'package:flutter_kit/src/shared/extensions/context_extensions.dart'; 4 | 5 | enum AppSnackbarType { 6 | info, 7 | success, 8 | danger, 9 | } 10 | 11 | final class AppSnackbar { 12 | static void show({ 13 | required BuildContext context, 14 | required String title, 15 | AppSnackbarType type = AppSnackbarType.info, 16 | }) { 17 | final messenger = ScaffoldMessenger.maybeOf(context); 18 | if (messenger == null) { 19 | return; 20 | } 21 | 22 | messenger.showSnackBar(SnackBar( 23 | content: Text( 24 | title, 25 | style: context.textTheme.bodyMedium?.copyWith( 26 | color: switch (type) { 27 | AppSnackbarType.danger => context.colorScheme.onError, 28 | AppSnackbarType.success => context.colorScheme.onSuccess, 29 | AppSnackbarType.info => context.colorScheme.onPrimary, 30 | }, 31 | ), 32 | ), 33 | backgroundColor: switch (type) { 34 | AppSnackbarType.danger => context.colorScheme.error, 35 | AppSnackbarType.success => context.colorScheme.success, 36 | AppSnackbarType.info => context.colorScheme.primary, 37 | }, 38 | )); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/shared/components/atoms/basic_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_kit/src/core/theme/dimens.dart'; 3 | 4 | class BasicCard extends StatelessWidget { 5 | const BasicCard({ 6 | super.key, 7 | required this.child, 8 | this.childPadding, 9 | this.borderRadius, 10 | }); 11 | 12 | final Widget child; 13 | final BorderRadius? borderRadius; 14 | final EdgeInsetsGeometry? childPadding; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | final contentChild = Padding( 19 | padding: childPadding ?? const EdgeInsets.all(Dimens.spacing), 20 | child: child, 21 | ); 22 | 23 | return Card( 24 | surfaceTintColor: Colors.transparent, 25 | margin: EdgeInsets.zero, 26 | shape: RoundedRectangleBorder( 27 | borderRadius: borderRadius ?? BorderRadius.circular(Dimens.radius), 28 | ), 29 | child: contentChild, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/shared/components/atoms/dividers/labeled_divider.dart: -------------------------------------------------------------------------------- 1 | // Drawing a divider with some text in the center 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_kit/src/core/theme/dimens.dart'; 4 | import 'package:flutter_kit/src/shared/extensions/context_extensions.dart'; 5 | 6 | class LabeledDivider extends StatelessWidget { 7 | final String label; 8 | final EdgeInsetsGeometry labelPadding; 9 | final Color? labelBackgroundColor; 10 | 11 | const LabeledDivider({ 12 | required this.label, 13 | this.labelPadding = const EdgeInsets.symmetric(horizontal: Dimens.spacing), 14 | super.key, 15 | this.labelBackgroundColor, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Row( 21 | children: [ 22 | const Expanded(child: Divider()), 23 | Padding( 24 | padding: labelPadding, 25 | child: Text( 26 | label, 27 | style: context.textTheme.bodySmall?.copyWith( 28 | color: context.colorScheme.onSurface.withValues(alpha: .8), 29 | ), 30 | ), 31 | ), 32 | const Expanded(child: Divider()), 33 | ], 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/shared/components/business/listenable_consumer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// A widget that combines the functionality of a listener and builder for a [Listenable] object. 4 | /// 5 | /// This widget is optimized to both listen for changes in a [Listenable] and rebuild 6 | /// the UI when those changes occur. It's similar to combining [ValueListenableBuilder] 7 | /// with a listener pattern, but in a single widget. 8 | /// 9 | /// The [listener] callback is invoked whenever the [listenable] notifies its listeners, 10 | /// but outside of the build phase to avoid unnecessary rebuilds. 11 | /// 12 | /// The [builder] callback is used to build the widget tree and is called when the 13 | /// [listenable] notifies its listeners. 14 | class ListenableConsumer extends StatefulWidget { 15 | /// The listenable object to listen to. 16 | final T listenable; 17 | 18 | /// Builder function that constructs the widget tree. 19 | final Widget Function(BuildContext context, T listenable) builder; 20 | 21 | /// Listener function that is called when the listenable notifies its listeners. 22 | /// This is called before the widget rebuilds. 23 | final void Function(BuildContext context, T listenable) listener; 24 | 25 | const ListenableConsumer({ 26 | super.key, 27 | required this.listenable, 28 | required this.builder, 29 | required this.listener, 30 | }); 31 | 32 | @override 33 | State> createState() => _ListenableConsumerState(); 34 | } 35 | 36 | class _ListenableConsumerState extends State> { 37 | @override 38 | void initState() { 39 | super.initState(); 40 | widget.listenable.addListener(_handleChange); 41 | _callListener(); 42 | } 43 | 44 | @override 45 | void didUpdateWidget(ListenableConsumer oldWidget) { 46 | super.didUpdateWidget(oldWidget); 47 | if (oldWidget.listenable != widget.listenable) { 48 | oldWidget.listenable.removeListener(_handleChange); 49 | widget.listenable.addListener(_handleChange); 50 | _callListener(); 51 | } 52 | } 53 | 54 | @override 55 | void dispose() { 56 | widget.listenable.removeListener(_handleChange); 57 | super.dispose(); 58 | } 59 | 60 | void _callListener() { 61 | if (mounted) { 62 | widget.listener(context, widget.listenable); 63 | } 64 | } 65 | 66 | void _handleChange() { 67 | _callListener(); 68 | if (mounted) { 69 | Future.microtask(() { 70 | if (mounted) { 71 | setState(() {}); 72 | } 73 | }); 74 | } 75 | } 76 | 77 | @override 78 | Widget build(BuildContext context) { 79 | return widget.builder(context, widget.listenable); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/src/shared/components/buttons/button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_kit/src/core/theme/dimens.dart'; 3 | import 'package:flutter_kit/src/shared/extensions/context_extensions.dart'; 4 | 5 | enum ButtonType { 6 | primary, 7 | neutral, 8 | outline, 9 | } 10 | 11 | class Button extends StatelessWidget { 12 | const Button._({ 13 | super.key, 14 | required this.title, 15 | this.onPressed, 16 | required this.type, 17 | this.icon, 18 | }); 19 | 20 | factory Button.neutral({ 21 | Key? key, 22 | required String title, 23 | Widget? icon, 24 | VoidCallback? onPressed, 25 | }) { 26 | return Button._( 27 | key: key, 28 | title: title, 29 | icon: icon, 30 | onPressed: onPressed, 31 | type: ButtonType.neutral, 32 | ); 33 | } 34 | 35 | factory Button.outline({ 36 | Key? key, 37 | required String title, 38 | Widget? icon, 39 | VoidCallback? onPressed, 40 | }) { 41 | return Button._( 42 | key: key, 43 | title: title, 44 | icon: icon, 45 | onPressed: onPressed, 46 | type: ButtonType.outline, 47 | ); 48 | } 49 | 50 | factory Button.primary({ 51 | Key? key, 52 | required String title, 53 | Widget? icon, 54 | VoidCallback? onPressed, 55 | }) { 56 | return Button._( 57 | key: key, 58 | title: title, 59 | icon: icon, 60 | onPressed: onPressed, 61 | type: ButtonType.primary, 62 | ); 63 | } 64 | 65 | final String title; 66 | final Widget? icon; 67 | final VoidCallback? onPressed; 68 | final ButtonType type; 69 | 70 | @override 71 | Widget build(BuildContext context) { 72 | final label = Text( 73 | title, 74 | style: context.textTheme.bodyMedium?.copyWith( 75 | color: (type == ButtonType.primary || type == ButtonType.neutral) 76 | ? context.colorScheme.surface 77 | : context.colorScheme.onSurface, 78 | fontWeight: type == ButtonType.primary ? FontWeight.w600 : FontWeight.w500, 79 | ), 80 | ); 81 | return SizedBox( 82 | width: double.infinity, 83 | height: kMinInteractiveDimension, 84 | child: switch (type) { 85 | ButtonType.primary || ButtonType.neutral => ElevatedButton.icon( 86 | onPressed: onPressed, 87 | style: ElevatedButton.styleFrom( 88 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Dimens.radius)), 89 | backgroundColor: 90 | type == ButtonType.primary ? context.colorScheme.primary : context.colorScheme.onSurface, 91 | ), 92 | icon: icon ?? const SizedBox(), 93 | label: label, 94 | ), 95 | ButtonType.outline => OutlinedButton.icon( 96 | onPressed: onPressed, 97 | style: OutlinedButton.styleFrom( 98 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Dimens.radius)), 99 | side: BorderSide(color: context.colorScheme.onSurface.withValues(alpha: .3), width: 1.2), 100 | ), 101 | icon: icon ?? const SizedBox(), 102 | label: label, 103 | ) 104 | }, 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/src/shared/components/dialogs/confirmation_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_kit/src/core/i18n/l10n.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_kit/src/core/theme/dimens.dart'; 4 | import 'package:flutter_kit/src/shared/extensions/context_extensions.dart'; 5 | 6 | class ConfirmationDialog { 7 | static Future show({ 8 | required BuildContext context, 9 | required String title, 10 | required String message, 11 | String? confirmText, 12 | String? cancelText, 13 | Color? confirmButtonColor, 14 | }) async { 15 | final result = await showDialog( 16 | context: context, 17 | barrierDismissible: true, 18 | useRootNavigator: false, 19 | builder: (context) { 20 | return AlertDialog.adaptive( 21 | surfaceTintColor: Colors.transparent, 22 | backgroundColor: context.colorScheme.surface, 23 | shape: RoundedRectangleBorder( 24 | borderRadius: BorderRadius.circular(Dimens.radius), 25 | ), 26 | title: Text(title), 27 | content: Text(message), 28 | actions: [ 29 | TextButton( 30 | onPressed: () => Navigator.of(context).pop(false), 31 | child: Text(cancelText ?? I18n.of(context).dialog_cancel), 32 | ), 33 | TextButton( 34 | onPressed: () => Navigator.of(context).pop(true), 35 | child: Text( 36 | confirmText ?? I18n.of(context).dialog_confirm, 37 | style: TextStyle(color: confirmButtonColor), 38 | ), 39 | ), 40 | ], 41 | ); 42 | }, 43 | ); 44 | return result ?? false; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/shared/components/dialogs/informative_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_kit/src/core/theme/dimens.dart'; 3 | import 'package:flutter_kit/src/shared/extensions/context_extensions.dart'; 4 | 5 | class InformativeDialog { 6 | static Future show({ 7 | required BuildContext context, 8 | required String title, 9 | required String message, 10 | String? buttonText, 11 | VoidCallback? onConfirm, 12 | }) { 13 | return showDialog( 14 | context: context, 15 | barrierDismissible: true, 16 | useRootNavigator: false, 17 | builder: (context) { 18 | return AlertDialog.adaptive( 19 | surfaceTintColor: Colors.transparent, 20 | backgroundColor: context.colorScheme.surface, 21 | shape: RoundedRectangleBorder( 22 | borderRadius: BorderRadius.circular(Dimens.radius), 23 | ), 24 | title: Text(title), 25 | content: Text(message), 26 | actions: [ 27 | TextButton( 28 | onPressed: () { 29 | Navigator.of(context).pop(); 30 | onConfirm?.call(); 31 | }, 32 | child: Text(buttonText ?? 'OK'), 33 | ), 34 | ], 35 | ); 36 | }, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/shared/components/dialogs/loading_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_kit/src/core/i18n/l10n.dart'; 2 | import 'package:flutter_kit/src/core/theme/dimens.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_kit/src/shared/extensions/context_extensions.dart'; 5 | 6 | class LoadingDialog { 7 | static bool _isVisible = false; 8 | 9 | static Future show({required BuildContext context, List actions = const []}) { 10 | if (_isVisible == false) { 11 | _isVisible = true; 12 | 13 | return showDialog( 14 | context: context, 15 | barrierDismissible: false, 16 | useRootNavigator: false, 17 | builder: (context) { 18 | return PopScope( 19 | canPop: false, 20 | child: AlertDialog.adaptive( 21 | surfaceTintColor: Colors.transparent, 22 | backgroundColor: context.colorScheme.surface, 23 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Dimens.radius)), 24 | content: Padding( 25 | padding: const EdgeInsets.all(Dimens.halfSpacing), 26 | child: Row( 27 | children: [ 28 | const SizedBox( 29 | width: Dimens.loaderSize, 30 | height: Dimens.loaderSize, 31 | child: CircularProgressIndicator.adaptive(), 32 | ), 33 | const SizedBox(width: Dimens.spacing), 34 | Expanded(child: Text(I18n.of(context).loadingDialog_content)), 35 | ], 36 | ), 37 | ), 38 | ), 39 | ); 40 | }, 41 | ); 42 | } 43 | return Future.value(); 44 | } 45 | 46 | static void hide({required BuildContext context}) { 47 | if (_isVisible) { 48 | _isVisible = false; 49 | Navigator.of(context).pop(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/shared/components/forms/input.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_kit/src/core/theme/dimens.dart'; 3 | import 'package:flutter_kit/src/shared/extensions/context_extensions.dart'; 4 | 5 | class Input extends StatelessWidget { 6 | final FocusNode? focusNode; 7 | final TextEditingController? controller; 8 | final bool readOnly; 9 | final String? hintText; 10 | final TextStyle? hintStyle; 11 | final TextStyle? style; 12 | final Widget? prefixIcon; 13 | final Widget? suffixIcon; 14 | final Color? cursorColor; 15 | final bool? filled; 16 | final bool isPassword; 17 | final int? minLines; 18 | final bool isBorderless; 19 | final TextInputType? keyboardType; 20 | final Color? fillColor; 21 | final FormFieldValidator? validator; 22 | final ValueChanged? onChanged; 23 | final ValueChanged? onSubmitted; 24 | final bool enabled; 25 | final int? maxLines; 26 | final TextInputAction? textInputAction; 27 | final List? autofillHints; 28 | final String? labelText; 29 | final TextCapitalization? textCapitalization; 30 | final bool expands; 31 | 32 | const Input({ 33 | super.key, 34 | this.focusNode, 35 | required this.controller, 36 | this.hintText, 37 | this.hintStyle, 38 | this.style, 39 | this.prefixIcon, 40 | this.suffixIcon, 41 | this.cursorColor, 42 | this.filled, 43 | this.isPassword = false, 44 | this.minLines, 45 | this.isBorderless = false, 46 | this.keyboardType, 47 | this.fillColor, 48 | this.validator, 49 | this.onChanged, 50 | this.readOnly = false, 51 | this.enabled = true, 52 | this.maxLines, 53 | this.textInputAction, 54 | this.autofillHints, 55 | this.onSubmitted, 56 | this.labelText, 57 | this.textCapitalization, 58 | this.expands = false, 59 | }); 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | final Color borderColor = context.colorScheme.outline; 64 | 65 | return Column( 66 | crossAxisAlignment: CrossAxisAlignment.start, 67 | children: [ 68 | if (labelText != null) ...[ 69 | Text( 70 | labelText!, 71 | style: Theme.of(context).textTheme.bodySmall?.copyWith(fontWeight: FontWeight.w500), 72 | ), 73 | const SizedBox(height: Dimens.minSpacing), 74 | ], 75 | ClipRRect( 76 | borderRadius: BorderRadius.circular(Dimens.radius), 77 | child: TextFormField( 78 | expands: expands, 79 | autofillHints: autofillHints, 80 | controller: controller, 81 | obscureText: isPassword, 82 | cursorColor: cursorColor, 83 | readOnly: readOnly, 84 | focusNode: focusNode, 85 | style: style ?? Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500), 86 | minLines: isPassword ? 1 : minLines, 87 | maxLines: isPassword ? 1 : maxLines, 88 | keyboardType: keyboardType, 89 | textInputAction: textInputAction, 90 | onFieldSubmitted: onSubmitted, 91 | textCapitalization: textCapitalization ?? TextCapitalization.sentences, 92 | decoration: InputDecoration( 93 | contentPadding: const EdgeInsets.symmetric(vertical: Dimens.halfSpacing, horizontal: Dimens.spacing), 94 | hintText: hintText, 95 | hintStyle: hintStyle, 96 | filled: filled, 97 | fillColor: fillColor ?? context.colorScheme.surface, 98 | prefixIcon: prefixIcon, 99 | suffixIcon: suffixIcon, 100 | enabled: enabled, 101 | errorBorder: _getInputBorder(color: context.colorScheme.error), 102 | enabledBorder: _getInputBorder(color: borderColor), 103 | border: _getInputBorder(color: borderColor), 104 | focusedErrorBorder: _getInputBorder(color: context.colorScheme.error), 105 | focusedBorder: _getInputBorder(color: context.colorScheme.primary), 106 | ), 107 | onChanged: onChanged, 108 | validator: validator, 109 | ), 110 | ), 111 | ], 112 | ); 113 | } 114 | 115 | InputBorder _getInputBorder({required Color color}) => isBorderless 116 | ? InputBorder.none 117 | : OutlineInputBorder( 118 | borderRadius: BorderRadius.circular(Dimens.radius), 119 | borderSide: BorderSide(color: color, width: 1.5) 120 | ); 121 | } 122 | -------------------------------------------------------------------------------- /lib/src/shared/extensions/context_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension ThemeDataX on BuildContext { 4 | Brightness get brightness => Theme.of(this).brightness; 5 | 6 | ColorScheme get colorScheme => Theme.of(this).colorScheme; 7 | 8 | TextTheme get textTheme => Theme.of(this).textTheme; 9 | 10 | ThemeData get theme => Theme.of(this); 11 | 12 | IconThemeData get iconTheme => Theme.of(this).iconTheme; 13 | 14 | AppBarTheme get appBarTheme => Theme.of(this).appBarTheme; 15 | 16 | BottomAppBarTheme get bottomAppBarTheme => Theme.of(this).bottomAppBarTheme; 17 | 18 | BottomNavigationBarThemeData get bottomNavigationBarTheme => Theme.of(this).bottomNavigationBarTheme; 19 | } 20 | 21 | extension MediaQueryX on BuildContext { 22 | Size get screenSize => MediaQuery.sizeOf(this); 23 | 24 | Brightness get platformBrightness => MediaQuery.platformBrightnessOf(this); 25 | 26 | EdgeInsets get padding => MediaQuery.paddingOf(this); 27 | 28 | EdgeInsets get viewInsets => MediaQuery.viewInsetsOf(this); 29 | 30 | TextScaler get textScaler => MediaQuery.textScalerOf(this); 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/shared/extensions/iterable_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension ListUtil on Iterable { 4 | Iterable divide({required Widget separator}) sync* { 5 | for (int i = 0; i < length; i++) { 6 | yield elementAt(i); 7 | if (last != elementAt(i)) { 8 | yield separator; 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/shared/locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_kit/src/core/routing/app_router.dart'; 2 | import 'package:flutter_kit/src/datasource/http/dio_config.dart'; 3 | import 'package:flutter_kit/src/datasource/http/example_api.dart'; 4 | import 'package:flutter_kit/src/datasource/repositories/example_repository.dart'; 5 | import 'package:flutter_kit/src/shared/services/app_logger.dart'; 6 | import 'package:flutter_kit/src/shared/services/storage/local_storage.dart'; 7 | import 'package:flutter_kit/src/shared/services/storage/storage.dart'; 8 | import 'package:get_it/get_it.dart'; 9 | 10 | final GetIt locator = GetIt.instance 11 | ..registerLazySingleton(() => DioConfig()) 12 | ..registerLazySingleton(() => AppRouter()) 13 | ..registerLazySingleton(() => AppLogger()) 14 | ..registerLazySingleton(() => LocalStorage()) 15 | ..registerLazySingleton(() => ExampleApi(dio: locator().dio)) 16 | ..registerLazySingleton(() => ExampleRepository()); 17 | -------------------------------------------------------------------------------- /lib/src/shared/services/app_logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | class AppLogger { 4 | static final AppLogger _instance = AppLogger._internal(); 5 | factory AppLogger() => _instance; 6 | AppLogger._internal(); 7 | 8 | final _logger = Logger( 9 | printer: PrettyPrinter( 10 | methodCount: 2, 11 | errorMethodCount: 8, 12 | lineLength: 120, 13 | colors: true, 14 | printEmojis: true, 15 | ), 16 | ); 17 | 18 | void debug(String message, [Object? error, StackTrace? stackTrace]) { 19 | _logger.d(message, error: error, stackTrace: stackTrace); 20 | } 21 | 22 | void info(String message, [Object? error, StackTrace? stackTrace]) { 23 | _logger.i(message, error: error, stackTrace: stackTrace); 24 | } 25 | 26 | void warning(String message, [Object? error, StackTrace? stackTrace]) { 27 | _logger.w(message, error: error, stackTrace: stackTrace); 28 | } 29 | 30 | void error(String message, [Object? error, StackTrace? stackTrace]) { 31 | _logger.e(message, error: error, stackTrace: stackTrace); 32 | } 33 | 34 | void verbose(String message, [Object? error, StackTrace? stackTrace]) { 35 | _logger.t(message, error: error, stackTrace: stackTrace); 36 | } 37 | 38 | void wtf(String message, [Object? error, StackTrace? stackTrace]) { 39 | _logger.f(message, error: error, stackTrace: stackTrace); 40 | } 41 | } 42 | 43 | // Global instance for easy access 44 | final logger = AppLogger(); 45 | -------------------------------------------------------------------------------- /lib/src/shared/services/storage/in_memory_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter_kit/src/shared/services/storage/storage.dart'; 4 | 5 | class InMemoryStorage extends Storage { 6 | final Map _storage = {}; 7 | 8 | @override 9 | Future init() async {} 10 | 11 | @override 12 | Future clear() async => _storage.clear(); 13 | 14 | @override 15 | Future containsKey({required String key}) async => _storage.containsKey(key); 16 | 17 | @override 18 | Future> getKeys() async => _storage.keys.toSet(); 19 | 20 | @override 21 | Future read({required String key}) async { 22 | final value = _storage[key]; 23 | if (value == null) return null; 24 | 25 | if (value is T) { 26 | return value as T?; 27 | } else if (T == String && value is! String) { 28 | return value.toString() as T?; 29 | } else if (value is String) { 30 | // Try to decode JSON for complex types 31 | try { 32 | final decoded = jsonDecode(value); 33 | return decoded as T?; 34 | } catch (_) { 35 | // If not valid JSON, return as is if possible 36 | return value as T?; 37 | } 38 | } else { 39 | return value as T?; 40 | } 41 | } 42 | 43 | @override 44 | Future remove({required String key}) async => _storage.remove(key); 45 | 46 | @override 47 | Future write({required String key, required value}) async { 48 | _storage[key] = value; 49 | return true; 50 | } 51 | 52 | @override 53 | Future writeBool({required String key, required bool value}) async { 54 | _storage[key] = value; 55 | return true; 56 | } 57 | 58 | @override 59 | Future writeDouble({required String key, required double value}) async { 60 | _storage[key] = value; 61 | return true; 62 | } 63 | 64 | @override 65 | Future writeInt({required String key, required int value}) async { 66 | _storage[key] = value; 67 | return true; 68 | } 69 | 70 | @override 71 | Future writeString({required String key, required String value}) async { 72 | _storage[key] = value; 73 | return true; 74 | } 75 | 76 | @override 77 | Future writeStringList({required String key, required List value}) async { 78 | _storage[key] = value; 79 | return true; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/src/shared/services/storage/local_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter_kit/src/shared/services/storage/storage.dart'; 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | 6 | class LocalStorage extends Storage { 7 | late SharedPreferences _sharedPreferences; 8 | 9 | @override 10 | Future init() async => _sharedPreferences = await SharedPreferences.getInstance(); 11 | 12 | @override 13 | Future clear() async => await _sharedPreferences.clear(); 14 | 15 | @override 16 | Future containsKey({required String key}) async => _sharedPreferences.containsKey(key); 17 | 18 | @override 19 | Future> getKeys() async => _sharedPreferences.getKeys(); 20 | 21 | @override 22 | Future read({required String key}) async { 23 | // If key doesn't exist, return null 24 | if (!_sharedPreferences.containsKey(key)) return null; 25 | 26 | // Handle different types based on T 27 | if (T == String) { 28 | return _sharedPreferences.getString(key) as T?; 29 | } else if (T == int) { 30 | return _sharedPreferences.getInt(key) as T?; 31 | } else if (T == double) { 32 | return _sharedPreferences.getDouble(key) as T?; 33 | } else if (T == bool) { 34 | return _sharedPreferences.getBool(key) as T?; 35 | } else if (T == List) { 36 | return _sharedPreferences.getStringList(key) as T?; 37 | } else { 38 | // For complex types, try to parse from JSON string 39 | final value = _sharedPreferences.getString(key); 40 | if (value == null) return null; 41 | try { 42 | return jsonDecode(value) as T?; 43 | } catch (e) { 44 | return value as T?; 45 | } 46 | } 47 | } 48 | 49 | @override 50 | Future remove({required String key}) async => await _sharedPreferences.remove(key); 51 | 52 | @override 53 | Future write({required String key, required value}) async { 54 | if (value == null) { 55 | return await _sharedPreferences.remove(key); 56 | } else if (value is String) { 57 | return await writeString(key: key, value: value); 58 | } else if (value is int) { 59 | return await writeInt(key: key, value: value); 60 | } else if (value is double) { 61 | return await writeDouble(key: key, value: value); 62 | } else if (value is bool) { 63 | return await writeBool(key: key, value: value); 64 | } else if (value is List) { 65 | return await writeStringList(key: key, value: value); 66 | } else { 67 | // For complex objects, serialize to JSON 68 | try { 69 | final jsonString = jsonEncode(value); 70 | return await writeString(key: key, value: jsonString); 71 | } catch (e) { 72 | // Fallback to toString if JSON serialization fails 73 | return await writeString(key: key, value: value.toString()); 74 | } 75 | } 76 | } 77 | 78 | @override 79 | Future writeBool({required String key, required bool value}) async => 80 | await _sharedPreferences.setBool(key, value); 81 | 82 | @override 83 | Future writeDouble({required String key, required double value}) async => 84 | await _sharedPreferences.setDouble(key, value); 85 | 86 | @override 87 | Future writeInt({required String key, required int value}) async => await _sharedPreferences.setInt(key, value); 88 | 89 | @override 90 | Future writeString({required String key, required String value}) async => 91 | await _sharedPreferences.setString(key, value); 92 | 93 | @override 94 | Future writeStringList({required String key, required List value}) async => 95 | await _sharedPreferences.setStringList(key, value); 96 | } 97 | -------------------------------------------------------------------------------- /lib/src/shared/services/storage/storage.dart: -------------------------------------------------------------------------------- 1 | abstract class Storage { 2 | /// Initialize the storage service 3 | Future init(); 4 | 5 | /// Clear all data from storage 6 | Future clear(); 7 | 8 | /// Remove a specific key from storage 9 | Future remove({required String key}); 10 | 11 | /// Check if a key exists in storage 12 | Future containsKey({required String key}); 13 | 14 | /// Get all keys in storage 15 | Future> getKeys(); 16 | 17 | /// Read a value from storage 18 | Future read({required String key}); 19 | 20 | /// Write a string value to storage 21 | Future writeString({required String key, required String value}); 22 | 23 | /// Write an integer value to storage 24 | Future writeInt({required String key, required int value}); 25 | 26 | /// Write a double value to storage 27 | Future writeDouble({required String key, required double value}); 28 | 29 | /// Write a boolean value to storage 30 | Future writeBool({required String key, required bool value}); 31 | 32 | /// Write a list of strings to storage 33 | Future writeStringList({required String key, required List value}); 34 | 35 | /// Write any value to storage (convenience method) 36 | /// Note: This will attempt to determine the type and use the appropriate method 37 | /// For complex objects, consider using JSON serialization before storing 38 | Future write({required String key, required value}); 39 | } 40 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "82.0.0" 12 | analyzer: 13 | dependency: transitive 14 | description: 15 | name: analyzer 16 | sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "7.4.5" 20 | ansicolor: 21 | dependency: transitive 22 | description: 23 | name: ansicolor 24 | sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.0.3" 28 | archive: 29 | dependency: transitive 30 | description: 31 | name: archive 32 | sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "4.0.7" 36 | args: 37 | dependency: transitive 38 | description: 39 | name: args 40 | sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "2.7.0" 44 | async: 45 | dependency: transitive 46 | description: 47 | name: async 48 | sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "2.13.0" 52 | auto_route: 53 | dependency: "direct main" 54 | description: 55 | name: auto_route 56 | sha256: b8c036fa613a98a759cf0fdcba26e62f4985dcbff01a5e760ab411e8554bbaf0 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "10.1.0+1" 60 | auto_route_generator: 61 | dependency: "direct dev" 62 | description: 63 | name: auto_route_generator 64 | sha256: "2a5b5bf9c55d4a2098931037dac90921a4663808aed494bb4f134d82d46cb8ec" 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "10.2.3" 68 | boolean_selector: 69 | dependency: transitive 70 | description: 71 | name: boolean_selector 72 | sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "2.1.2" 76 | build: 77 | dependency: transitive 78 | description: 79 | name: build 80 | sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "2.4.2" 84 | build_config: 85 | dependency: transitive 86 | description: 87 | name: build_config 88 | sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "1.1.2" 92 | build_daemon: 93 | dependency: transitive 94 | description: 95 | name: build_daemon 96 | sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "4.0.4" 100 | build_resolvers: 101 | dependency: transitive 102 | description: 103 | name: build_resolvers 104 | sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "2.4.4" 108 | build_runner: 109 | dependency: "direct dev" 110 | description: 111 | name: build_runner 112 | sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "2.4.15" 116 | build_runner_core: 117 | dependency: transitive 118 | description: 119 | name: build_runner_core 120 | sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "8.0.0" 124 | built_collection: 125 | dependency: transitive 126 | description: 127 | name: built_collection 128 | sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" 129 | url: "https://pub.dev" 130 | source: hosted 131 | version: "5.1.1" 132 | built_value: 133 | dependency: transitive 134 | description: 135 | name: built_value 136 | sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27" 137 | url: "https://pub.dev" 138 | source: hosted 139 | version: "8.10.1" 140 | characters: 141 | dependency: transitive 142 | description: 143 | name: characters 144 | sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 145 | url: "https://pub.dev" 146 | source: hosted 147 | version: "1.4.0" 148 | checked_yaml: 149 | dependency: transitive 150 | description: 151 | name: checked_yaml 152 | sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" 153 | url: "https://pub.dev" 154 | source: hosted 155 | version: "2.0.4" 156 | cli_config: 157 | dependency: transitive 158 | description: 159 | name: cli_config 160 | sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec 161 | url: "https://pub.dev" 162 | source: hosted 163 | version: "0.2.0" 164 | clock: 165 | dependency: transitive 166 | description: 167 | name: clock 168 | sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b 169 | url: "https://pub.dev" 170 | source: hosted 171 | version: "1.1.2" 172 | code_builder: 173 | dependency: transitive 174 | description: 175 | name: code_builder 176 | sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" 177 | url: "https://pub.dev" 178 | source: hosted 179 | version: "4.10.1" 180 | collection: 181 | dependency: transitive 182 | description: 183 | name: collection 184 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 185 | url: "https://pub.dev" 186 | source: hosted 187 | version: "1.19.1" 188 | convert: 189 | dependency: transitive 190 | description: 191 | name: convert 192 | sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 193 | url: "https://pub.dev" 194 | source: hosted 195 | version: "3.1.2" 196 | coverage: 197 | dependency: transitive 198 | description: 199 | name: coverage 200 | sha256: aa07dbe5f2294c827b7edb9a87bba44a9c15a3cc81bc8da2ca19b37322d30080 201 | url: "https://pub.dev" 202 | source: hosted 203 | version: "1.14.1" 204 | crypto: 205 | dependency: transitive 206 | description: 207 | name: crypto 208 | sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" 209 | url: "https://pub.dev" 210 | source: hosted 211 | version: "3.0.6" 212 | cupertino_icons: 213 | dependency: "direct main" 214 | description: 215 | name: cupertino_icons 216 | sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 217 | url: "https://pub.dev" 218 | source: hosted 219 | version: "1.0.8" 220 | dart_style: 221 | dependency: transitive 222 | description: 223 | name: dart_style 224 | sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af" 225 | url: "https://pub.dev" 226 | source: hosted 227 | version: "3.1.0" 228 | dio: 229 | dependency: "direct main" 230 | description: 231 | name: dio 232 | sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" 233 | url: "https://pub.dev" 234 | source: hosted 235 | version: "5.8.0+1" 236 | dio_web_adapter: 237 | dependency: transitive 238 | description: 239 | name: dio_web_adapter 240 | sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" 241 | url: "https://pub.dev" 242 | source: hosted 243 | version: "2.1.1" 244 | equatable: 245 | dependency: "direct main" 246 | description: 247 | name: equatable 248 | sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" 249 | url: "https://pub.dev" 250 | source: hosted 251 | version: "2.0.7" 252 | fake_async: 253 | dependency: transitive 254 | description: 255 | name: fake_async 256 | sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" 257 | url: "https://pub.dev" 258 | source: hosted 259 | version: "1.3.3" 260 | ffi: 261 | dependency: transitive 262 | description: 263 | name: ffi 264 | sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" 265 | url: "https://pub.dev" 266 | source: hosted 267 | version: "2.1.4" 268 | file: 269 | dependency: transitive 270 | description: 271 | name: file 272 | sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 273 | url: "https://pub.dev" 274 | source: hosted 275 | version: "7.0.1" 276 | fixnum: 277 | dependency: transitive 278 | description: 279 | name: fixnum 280 | sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be 281 | url: "https://pub.dev" 282 | source: hosted 283 | version: "1.1.1" 284 | flutter: 285 | dependency: "direct main" 286 | description: flutter 287 | source: sdk 288 | version: "0.0.0" 289 | flutter_lints: 290 | dependency: "direct dev" 291 | description: 292 | name: flutter_lints 293 | sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" 294 | url: "https://pub.dev" 295 | source: hosted 296 | version: "6.0.0" 297 | flutter_localizations: 298 | dependency: "direct main" 299 | description: flutter 300 | source: sdk 301 | version: "0.0.0" 302 | flutter_svg: 303 | dependency: "direct main" 304 | description: 305 | name: flutter_svg 306 | sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 307 | url: "https://pub.dev" 308 | source: hosted 309 | version: "2.1.0" 310 | flutter_test: 311 | dependency: "direct dev" 312 | description: flutter 313 | source: sdk 314 | version: "0.0.0" 315 | flutter_web_plugins: 316 | dependency: transitive 317 | description: flutter 318 | source: sdk 319 | version: "0.0.0" 320 | frontend_server_client: 321 | dependency: transitive 322 | description: 323 | name: frontend_server_client 324 | sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 325 | url: "https://pub.dev" 326 | source: hosted 327 | version: "4.0.0" 328 | get_it: 329 | dependency: "direct main" 330 | description: 331 | name: get_it 332 | sha256: f126a3e286b7f5b578bf436d5592968706c4c1de28a228b870ce375d9f743103 333 | url: "https://pub.dev" 334 | source: hosted 335 | version: "8.0.3" 336 | glob: 337 | dependency: transitive 338 | description: 339 | name: glob 340 | sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de 341 | url: "https://pub.dev" 342 | source: hosted 343 | version: "2.1.3" 344 | graphs: 345 | dependency: transitive 346 | description: 347 | name: graphs 348 | sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" 349 | url: "https://pub.dev" 350 | source: hosted 351 | version: "2.3.2" 352 | hotreloader: 353 | dependency: transitive 354 | description: 355 | name: hotreloader 356 | sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b 357 | url: "https://pub.dev" 358 | source: hosted 359 | version: "4.3.0" 360 | http: 361 | dependency: transitive 362 | description: 363 | name: http 364 | sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" 365 | url: "https://pub.dev" 366 | source: hosted 367 | version: "1.4.0" 368 | http_multi_server: 369 | dependency: transitive 370 | description: 371 | name: http_multi_server 372 | sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 373 | url: "https://pub.dev" 374 | source: hosted 375 | version: "3.2.2" 376 | http_parser: 377 | dependency: transitive 378 | description: 379 | name: http_parser 380 | sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" 381 | url: "https://pub.dev" 382 | source: hosted 383 | version: "4.1.2" 384 | iconsax_plus: 385 | dependency: "direct main" 386 | description: 387 | name: iconsax_plus 388 | sha256: e9e51b0652a1d3ceea5fedbfc2c1bb4ad432c2ceb7be7de2e30caf085678933c 389 | url: "https://pub.dev" 390 | source: hosted 391 | version: "1.0.0" 392 | intl: 393 | dependency: "direct main" 394 | description: 395 | name: intl 396 | sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" 397 | url: "https://pub.dev" 398 | source: hosted 399 | version: "0.20.2" 400 | intl_utils: 401 | dependency: "direct main" 402 | description: 403 | name: intl_utils 404 | sha256: "3b2655259dbebd26e3b1466d0839f389dc59a275337e1b760ac645bc7814d2d3" 405 | url: "https://pub.dev" 406 | source: hosted 407 | version: "2.8.10" 408 | io: 409 | dependency: transitive 410 | description: 411 | name: io 412 | sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b 413 | url: "https://pub.dev" 414 | source: hosted 415 | version: "1.0.5" 416 | js: 417 | dependency: transitive 418 | description: 419 | name: js 420 | sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" 421 | url: "https://pub.dev" 422 | source: hosted 423 | version: "0.7.2" 424 | json_annotation: 425 | dependency: "direct main" 426 | description: 427 | name: json_annotation 428 | sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" 429 | url: "https://pub.dev" 430 | source: hosted 431 | version: "4.9.0" 432 | json_serializable: 433 | dependency: "direct dev" 434 | description: 435 | name: json_serializable 436 | sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c 437 | url: "https://pub.dev" 438 | source: hosted 439 | version: "6.9.5" 440 | leak_tracker: 441 | dependency: transitive 442 | description: 443 | name: leak_tracker 444 | sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" 445 | url: "https://pub.dev" 446 | source: hosted 447 | version: "10.0.9" 448 | leak_tracker_flutter_testing: 449 | dependency: transitive 450 | description: 451 | name: leak_tracker_flutter_testing 452 | sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 453 | url: "https://pub.dev" 454 | source: hosted 455 | version: "3.0.9" 456 | leak_tracker_testing: 457 | dependency: transitive 458 | description: 459 | name: leak_tracker_testing 460 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 461 | url: "https://pub.dev" 462 | source: hosted 463 | version: "3.0.1" 464 | lean_builder: 465 | dependency: transitive 466 | description: 467 | name: lean_builder 468 | sha256: ac129cd2173aa4e53e1327bcee2233d738d68ee446f3c797135633deafe6ca8a 469 | url: "https://pub.dev" 470 | source: hosted 471 | version: "0.1.0-alpha.12" 472 | lints: 473 | dependency: transitive 474 | description: 475 | name: lints 476 | sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 477 | url: "https://pub.dev" 478 | source: hosted 479 | version: "6.0.0" 480 | logger: 481 | dependency: "direct main" 482 | description: 483 | name: logger 484 | sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1 485 | url: "https://pub.dev" 486 | source: hosted 487 | version: "2.5.0" 488 | logging: 489 | dependency: transitive 490 | description: 491 | name: logging 492 | sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 493 | url: "https://pub.dev" 494 | source: hosted 495 | version: "1.3.0" 496 | matcher: 497 | dependency: transitive 498 | description: 499 | name: matcher 500 | sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 501 | url: "https://pub.dev" 502 | source: hosted 503 | version: "0.12.17" 504 | material_color_utilities: 505 | dependency: transitive 506 | description: 507 | name: material_color_utilities 508 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 509 | url: "https://pub.dev" 510 | source: hosted 511 | version: "0.11.1" 512 | meta: 513 | dependency: transitive 514 | description: 515 | name: meta 516 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 517 | url: "https://pub.dev" 518 | source: hosted 519 | version: "1.16.0" 520 | mime: 521 | dependency: transitive 522 | description: 523 | name: mime 524 | sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" 525 | url: "https://pub.dev" 526 | source: hosted 527 | version: "2.0.0" 528 | nested: 529 | dependency: transitive 530 | description: 531 | name: nested 532 | sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" 533 | url: "https://pub.dev" 534 | source: hosted 535 | version: "1.0.0" 536 | node_preamble: 537 | dependency: transitive 538 | description: 539 | name: node_preamble 540 | sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" 541 | url: "https://pub.dev" 542 | source: hosted 543 | version: "2.0.2" 544 | package_config: 545 | dependency: transitive 546 | description: 547 | name: package_config 548 | sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc 549 | url: "https://pub.dev" 550 | source: hosted 551 | version: "2.2.0" 552 | path: 553 | dependency: transitive 554 | description: 555 | name: path 556 | sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 557 | url: "https://pub.dev" 558 | source: hosted 559 | version: "1.9.1" 560 | path_parsing: 561 | dependency: transitive 562 | description: 563 | name: path_parsing 564 | sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" 565 | url: "https://pub.dev" 566 | source: hosted 567 | version: "1.1.0" 568 | path_provider_linux: 569 | dependency: transitive 570 | description: 571 | name: path_provider_linux 572 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 573 | url: "https://pub.dev" 574 | source: hosted 575 | version: "2.2.1" 576 | path_provider_platform_interface: 577 | dependency: transitive 578 | description: 579 | name: path_provider_platform_interface 580 | sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" 581 | url: "https://pub.dev" 582 | source: hosted 583 | version: "2.1.2" 584 | path_provider_windows: 585 | dependency: transitive 586 | description: 587 | name: path_provider_windows 588 | sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 589 | url: "https://pub.dev" 590 | source: hosted 591 | version: "2.3.0" 592 | petitparser: 593 | dependency: transitive 594 | description: 595 | name: petitparser 596 | sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" 597 | url: "https://pub.dev" 598 | source: hosted 599 | version: "6.1.0" 600 | platform: 601 | dependency: transitive 602 | description: 603 | name: platform 604 | sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" 605 | url: "https://pub.dev" 606 | source: hosted 607 | version: "3.1.6" 608 | plugin_platform_interface: 609 | dependency: transitive 610 | description: 611 | name: plugin_platform_interface 612 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 613 | url: "https://pub.dev" 614 | source: hosted 615 | version: "2.1.8" 616 | pool: 617 | dependency: transitive 618 | description: 619 | name: pool 620 | sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" 621 | url: "https://pub.dev" 622 | source: hosted 623 | version: "1.5.1" 624 | posix: 625 | dependency: transitive 626 | description: 627 | name: posix 628 | sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62 629 | url: "https://pub.dev" 630 | source: hosted 631 | version: "6.0.2" 632 | provider: 633 | dependency: "direct main" 634 | description: 635 | name: provider 636 | sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" 637 | url: "https://pub.dev" 638 | source: hosted 639 | version: "6.1.5" 640 | pub_semver: 641 | dependency: transitive 642 | description: 643 | name: pub_semver 644 | sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" 645 | url: "https://pub.dev" 646 | source: hosted 647 | version: "2.2.0" 648 | pubspec_parse: 649 | dependency: transitive 650 | description: 651 | name: pubspec_parse 652 | sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" 653 | url: "https://pub.dev" 654 | source: hosted 655 | version: "1.5.0" 656 | shared_preferences: 657 | dependency: "direct main" 658 | description: 659 | name: shared_preferences 660 | sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" 661 | url: "https://pub.dev" 662 | source: hosted 663 | version: "2.5.3" 664 | shared_preferences_android: 665 | dependency: transitive 666 | description: 667 | name: shared_preferences_android 668 | sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" 669 | url: "https://pub.dev" 670 | source: hosted 671 | version: "2.4.10" 672 | shared_preferences_foundation: 673 | dependency: transitive 674 | description: 675 | name: shared_preferences_foundation 676 | sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" 677 | url: "https://pub.dev" 678 | source: hosted 679 | version: "2.5.4" 680 | shared_preferences_linux: 681 | dependency: transitive 682 | description: 683 | name: shared_preferences_linux 684 | sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" 685 | url: "https://pub.dev" 686 | source: hosted 687 | version: "2.4.1" 688 | shared_preferences_platform_interface: 689 | dependency: transitive 690 | description: 691 | name: shared_preferences_platform_interface 692 | sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" 693 | url: "https://pub.dev" 694 | source: hosted 695 | version: "2.4.1" 696 | shared_preferences_web: 697 | dependency: transitive 698 | description: 699 | name: shared_preferences_web 700 | sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 701 | url: "https://pub.dev" 702 | source: hosted 703 | version: "2.4.3" 704 | shared_preferences_windows: 705 | dependency: transitive 706 | description: 707 | name: shared_preferences_windows 708 | sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" 709 | url: "https://pub.dev" 710 | source: hosted 711 | version: "2.4.1" 712 | shelf: 713 | dependency: transitive 714 | description: 715 | name: shelf 716 | sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 717 | url: "https://pub.dev" 718 | source: hosted 719 | version: "1.4.2" 720 | shelf_packages_handler: 721 | dependency: transitive 722 | description: 723 | name: shelf_packages_handler 724 | sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" 725 | url: "https://pub.dev" 726 | source: hosted 727 | version: "3.0.2" 728 | shelf_static: 729 | dependency: transitive 730 | description: 731 | name: shelf_static 732 | sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 733 | url: "https://pub.dev" 734 | source: hosted 735 | version: "1.1.3" 736 | shelf_web_socket: 737 | dependency: transitive 738 | description: 739 | name: shelf_web_socket 740 | sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" 741 | url: "https://pub.dev" 742 | source: hosted 743 | version: "3.0.0" 744 | sky_engine: 745 | dependency: transitive 746 | description: flutter 747 | source: sdk 748 | version: "0.0.0" 749 | source_gen: 750 | dependency: transitive 751 | description: 752 | name: source_gen 753 | sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" 754 | url: "https://pub.dev" 755 | source: hosted 756 | version: "2.0.0" 757 | source_helper: 758 | dependency: transitive 759 | description: 760 | name: source_helper 761 | sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" 762 | url: "https://pub.dev" 763 | source: hosted 764 | version: "1.3.5" 765 | source_map_stack_trace: 766 | dependency: transitive 767 | description: 768 | name: source_map_stack_trace 769 | sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b 770 | url: "https://pub.dev" 771 | source: hosted 772 | version: "2.1.2" 773 | source_maps: 774 | dependency: transitive 775 | description: 776 | name: source_maps 777 | sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" 778 | url: "https://pub.dev" 779 | source: hosted 780 | version: "0.10.13" 781 | source_span: 782 | dependency: transitive 783 | description: 784 | name: source_span 785 | sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" 786 | url: "https://pub.dev" 787 | source: hosted 788 | version: "1.10.1" 789 | stack_trace: 790 | dependency: transitive 791 | description: 792 | name: stack_trace 793 | sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 794 | url: "https://pub.dev" 795 | source: hosted 796 | version: "1.12.1" 797 | stream_channel: 798 | dependency: transitive 799 | description: 800 | name: stream_channel 801 | sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" 802 | url: "https://pub.dev" 803 | source: hosted 804 | version: "2.1.4" 805 | stream_transform: 806 | dependency: transitive 807 | description: 808 | name: stream_transform 809 | sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 810 | url: "https://pub.dev" 811 | source: hosted 812 | version: "2.1.1" 813 | string_scanner: 814 | dependency: transitive 815 | description: 816 | name: string_scanner 817 | sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" 818 | url: "https://pub.dev" 819 | source: hosted 820 | version: "1.4.1" 821 | term_glyph: 822 | dependency: transitive 823 | description: 824 | name: term_glyph 825 | sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" 826 | url: "https://pub.dev" 827 | source: hosted 828 | version: "1.2.2" 829 | test: 830 | dependency: transitive 831 | description: 832 | name: test 833 | sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" 834 | url: "https://pub.dev" 835 | source: hosted 836 | version: "1.25.15" 837 | test_api: 838 | dependency: transitive 839 | description: 840 | name: test_api 841 | sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd 842 | url: "https://pub.dev" 843 | source: hosted 844 | version: "0.7.4" 845 | test_core: 846 | dependency: transitive 847 | description: 848 | name: test_core 849 | sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" 850 | url: "https://pub.dev" 851 | source: hosted 852 | version: "0.6.8" 853 | timing: 854 | dependency: transitive 855 | description: 856 | name: timing 857 | sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" 858 | url: "https://pub.dev" 859 | source: hosted 860 | version: "1.0.2" 861 | typed_data: 862 | dependency: transitive 863 | description: 864 | name: typed_data 865 | sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 866 | url: "https://pub.dev" 867 | source: hosted 868 | version: "1.4.0" 869 | vector_graphics: 870 | dependency: transitive 871 | description: 872 | name: vector_graphics 873 | sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" 874 | url: "https://pub.dev" 875 | source: hosted 876 | version: "1.1.18" 877 | vector_graphics_codec: 878 | dependency: transitive 879 | description: 880 | name: vector_graphics_codec 881 | sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" 882 | url: "https://pub.dev" 883 | source: hosted 884 | version: "1.1.13" 885 | vector_graphics_compiler: 886 | dependency: transitive 887 | description: 888 | name: vector_graphics_compiler 889 | sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" 890 | url: "https://pub.dev" 891 | source: hosted 892 | version: "1.1.17" 893 | vector_math: 894 | dependency: transitive 895 | description: 896 | name: vector_math 897 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 898 | url: "https://pub.dev" 899 | source: hosted 900 | version: "2.1.4" 901 | vm_service: 902 | dependency: transitive 903 | description: 904 | name: vm_service 905 | sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 906 | url: "https://pub.dev" 907 | source: hosted 908 | version: "15.0.0" 909 | watcher: 910 | dependency: transitive 911 | description: 912 | name: watcher 913 | sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" 914 | url: "https://pub.dev" 915 | source: hosted 916 | version: "1.1.1" 917 | web: 918 | dependency: transitive 919 | description: 920 | name: web 921 | sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" 922 | url: "https://pub.dev" 923 | source: hosted 924 | version: "1.1.1" 925 | web_socket: 926 | dependency: transitive 927 | description: 928 | name: web_socket 929 | sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" 930 | url: "https://pub.dev" 931 | source: hosted 932 | version: "1.0.1" 933 | web_socket_channel: 934 | dependency: transitive 935 | description: 936 | name: web_socket_channel 937 | sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 938 | url: "https://pub.dev" 939 | source: hosted 940 | version: "3.0.3" 941 | webkit_inspection_protocol: 942 | dependency: transitive 943 | description: 944 | name: webkit_inspection_protocol 945 | sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" 946 | url: "https://pub.dev" 947 | source: hosted 948 | version: "1.2.1" 949 | xdg_directories: 950 | dependency: transitive 951 | description: 952 | name: xdg_directories 953 | sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" 954 | url: "https://pub.dev" 955 | source: hosted 956 | version: "1.1.0" 957 | xml: 958 | dependency: transitive 959 | description: 960 | name: xml 961 | sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 962 | url: "https://pub.dev" 963 | source: hosted 964 | version: "6.5.0" 965 | xxh3: 966 | dependency: transitive 967 | description: 968 | name: xxh3 969 | sha256: "399a0438f5d426785723c99da6b16e136f4953fb1e9db0bf270bd41dd4619916" 970 | url: "https://pub.dev" 971 | source: hosted 972 | version: "1.2.0" 973 | yaml: 974 | dependency: transitive 975 | description: 976 | name: yaml 977 | sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce 978 | url: "https://pub.dev" 979 | source: hosted 980 | version: "3.1.3" 981 | sdks: 982 | dart: ">=3.8.0 <4.0.0" 983 | flutter: ">=3.27.0" 984 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_kit 2 | description: A boilerplate for Flutter Projects using BLoC Architecture 3 | 4 | publish_to: 'none' 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: '>=3.0.0' 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | flutter_localizations: 15 | sdk: flutter 16 | 17 | auto_route: ^10.1.0 18 | cupertino_icons: ^1.0.8 19 | dio: ^5.8.0+1 20 | equatable: ^2.0.7 21 | flutter_svg: ^2.1.0 22 | get_it: ^8.0.3 23 | provider: ^6.1.5 24 | iconsax_plus: ^1.0.0 25 | intl: ^0.20.2 26 | intl_utils: ^2.8.10 27 | json_annotation: ^4.9.0 28 | logger: ^2.5.0 29 | shared_preferences: ^2.5.3 30 | 31 | dev_dependencies: 32 | flutter_test: 33 | sdk: flutter 34 | 35 | auto_route_generator: ^10.2.3 36 | json_serializable: ^6.9.5 37 | build_runner: ^2.4.15 38 | flutter_lints: ^6.0.0 39 | 40 | flutter: 41 | uses-material-design: true 42 | 43 | assets: 44 | - assets/images/ 45 | 46 | fonts: 47 | - family: GeneralSans 48 | fonts: 49 | - asset: assets/fonts/GeneralSans-Extralight.otf 50 | weight: 200 51 | - asset: assets/fonts/GeneralSans-ExtralightItalic.otf 52 | weight: 200 53 | style: italic 54 | - asset: assets/fonts/GeneralSans-Light.otf 55 | weight: 300 56 | - asset: assets/fonts/GeneralSans-LightItalic.otf 57 | weight: 300 58 | - asset: assets/fonts/GeneralSans-Regular.otf 59 | style: normal 60 | - asset: assets/fonts/GeneralSans-Italic.otf 61 | style: italic 62 | - asset: assets/fonts/GeneralSans-Medium.otf 63 | weight: 500 64 | - asset: assets/fonts/GeneralSans-MediumItalic.otf 65 | weight: 500 66 | style: italic 67 | - asset: assets/fonts/GeneralSans-Semibold.otf 68 | weight: 600 69 | - asset: assets/fonts/GeneralSans-SemiboldItalic.otf 70 | weight: 600 71 | style: italic 72 | - asset: assets/fonts/GeneralSans-Bold.otf 73 | weight: 700 74 | - asset: assets/fonts/GeneralSans-BoldItalic.otf 75 | weight: 700 76 | style: italic 77 | 78 | fluttergen: 79 | output: lib/src/generated 80 | line_length: 120 81 | 82 | integrations: 83 | flutter_svg: true 84 | 85 | flutter_intl: 86 | enabled: true 87 | class_name: I18n 88 | main_locale: en 89 | arb_dir: lib/src/core/i18n/l10n 90 | output_dir: lib/src/core/i18n 91 | 92 | flutter_kit: 93 | app_name: "Flutter Kit" 94 | ios_bundle_id: com.example.flutter_kit 95 | android_package_name: com.example.flutter_kit 96 | dart_package: flutter_kit -------------------------------------------------------------------------------- /setup.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal EnableDelayedExpansion 3 | 4 | echo Flutter Kit Installation Script 5 | echo ---------------------------------------- 6 | 7 | :: Function to read value from pubspec.yaml using findstr and remove quotes 8 | for /f "tokens=2 delims=:" %%a in ('findstr /r "^ app_name:" pubspec.yaml') do ( 9 | set "APP_NAME=%%a" 10 | set "APP_NAME=!APP_NAME: =!" 11 | set "APP_NAME=!APP_NAME:"=!" 12 | ) 13 | for /f "tokens=2 delims=:" %%a in ('findstr /r "^ ios_bundle_id:" pubspec.yaml') do ( 14 | set "IOS_BUNDLE_ID=%%a" 15 | set "IOS_BUNDLE_ID=!IOS_BUNDLE_ID: =!" 16 | ) 17 | for /f "tokens=2 delims=:" %%a in ('findstr /r "^ android_package_name:" pubspec.yaml') do ( 18 | set "ANDROID_PACKAGE_NAME=%%a" 19 | set "ANDROID_PACKAGE_NAME=!ANDROID_PACKAGE_NAME: =!" 20 | ) 21 | for /f "tokens=2 delims=:" %%a in ('findstr /r "^ dart_package:" pubspec.yaml') do ( 22 | set "DART_PACKAGE=%%a" 23 | set "DART_PACKAGE=!DART_PACKAGE: =!" 24 | ) 25 | 26 | echo Configuration from pubspec.yaml: 27 | echo App Name: !APP_NAME! 28 | echo iOS Bundle ID: !IOS_BUNDLE_ID! 29 | echo Android Package Name: !ANDROID_PACKAGE_NAME! 30 | echo Dart Package Name: !DART_PACKAGE! 31 | echo. 32 | 33 | :: Get current applicationId and namespace from build.gradle.kts 34 | set "BUILD_GRADLE_PATH=android\app\build.gradle.kts" 35 | for /f "tokens=2 delims==" %%a in ('findstr /r "applicationId" "%BUILD_GRADLE_PATH%"') do ( 36 | set "OLD_APPLICATION_ID=%%a" 37 | set "OLD_APPLICATION_ID=!OLD_APPLICATION_ID: =!" 38 | set "OLD_APPLICATION_ID=!OLD_APPLICATION_ID:\"=!" 39 | ) 40 | for /f "tokens=2 delims==" %%a in ('findstr /r "namespace" "%BUILD_GRADLE_PATH%"') do ( 41 | set "OLD_NAMESPACE=%%a" 42 | set "OLD_NAMESPACE=!OLD_NAMESPACE: =!" 43 | set "OLD_NAMESPACE=!OLD_NAMESPACE:\"=!" 44 | ) 45 | 46 | echo Current applicationId: !OLD_APPLICATION_ID! 47 | echo Current namespace: !OLD_NAMESPACE! 48 | echo New package name: !ANDROID_PACKAGE_NAME! 49 | 50 | :: Update build.gradle.kts 51 | echo Updating build.gradle.kts... 52 | powershell -Command "(Get-Content '%BUILD_GRADLE_PATH%') -replace 'applicationId = \"!OLD_APPLICATION_ID!\"', 'applicationId = \"!ANDROID_PACKAGE_NAME!\"' | Set-Content '%BUILD_GRADLE_PATH%'" 53 | powershell -Command "(Get-Content '%BUILD_GRADLE_PATH%') -replace 'namespace = \"!OLD_NAMESPACE!\"', 'namespace = \"!ANDROID_PACKAGE_NAME!\"' | Set-Content '%BUILD_GRADLE_PATH%'" 54 | 55 | :: Update AndroidManifest.xml 56 | echo Updating AndroidManifest.xml... 57 | for /r "android" %%F in (AndroidManifest.xml) do ( 58 | powershell -Command "(Get-Content '%%F') -replace 'package=\"!OLD_NAMESPACE!\"', 'package=\"!ANDROID_PACKAGE_NAME!\"' -replace 'android:label=\"[^\"]*\"', 'android:label=\"!APP_NAME!\"' | Set-Content '%%F'" 59 | ) 60 | 61 | :: Update Dart package name in lib files 62 | echo Updating Dart package name in files... 63 | for /r "lib" %%F in (*.dart) do ( 64 | powershell -Command "(Get-Content '%%F') -replace 'package:flutter_kit/', 'package:!DART_PACKAGE!/' | Set-Content '%%F'" 65 | ) 66 | 67 | :: Update package name in pubspec.yaml 68 | echo Updating package name in pubspec.yaml... 69 | powershell -Command "(Get-Content 'pubspec.yaml') -replace '^name: .*', 'name: !DART_PACKAGE!' | Set-Content 'pubspec.yaml'" 70 | 71 | :: Move Java/Kotlin files to new package structure 72 | echo Moving Android files to new package structure... 73 | set "OLD_PATH=!OLD_NAMESPACE:.=\!" 74 | set "NEW_PATH=!ANDROID_PACKAGE_NAME:.=\!" 75 | set "KOTLIN_PATH=android\app\src\main\kotlin" 76 | 77 | :: Create new package directory 78 | if not exist "!KOTLIN_PATH!\!NEW_PATH!" mkdir "!KOTLIN_PATH!\!NEW_PATH!" 79 | 80 | :: Handle MainActivity.kt 81 | if exist "!KOTLIN_PATH!\!OLD_PATH!\MainActivity.kt" ( 82 | echo Copying and updating existing MainActivity.kt... 83 | powershell -Command "(Get-Content '!KOTLIN_PATH!\!OLD_PATH!\MainActivity.kt') -replace 'package !OLD_NAMESPACE!', 'package !ANDROID_PACKAGE_NAME!' | Set-Content '!KOTLIN_PATH!\!NEW_PATH!\MainActivity.kt'" 84 | :: Delete old MainActivity.kt 85 | del "!KOTLIN_PATH!\!OLD_PATH!\MainActivity.kt" 86 | ) else ( 87 | echo Creating new MainActivity.kt... 88 | ( 89 | echo package !ANDROID_PACKAGE_NAME! 90 | echo. 91 | echo import io.flutter.embedding.android.FlutterActivity 92 | echo. 93 | echo class MainActivity: FlutterActivity { 94 | echo } 95 | ) > "!KOTLIN_PATH!\!NEW_PATH!\MainActivity.kt" 96 | ) 97 | 98 | :: Clean up old package structure 99 | if exist "!KOTLIN_PATH!\!OLD_PATH!" ( 100 | echo Cleaning up old package directory... 101 | :: Move any remaining files 102 | for %%F in ("!KOTLIN_PATH!\!OLD_PATH!\*") do ( 103 | if not "%%~nxF"=="MainActivity.kt" ( 104 | move "%%F" "!KOTLIN_PATH!\!NEW_PATH!" > nul 105 | ) 106 | ) 107 | :: Remove old directory 108 | rmdir /S /Q "!KOTLIN_PATH!\!OLD_PATH!" 109 | ) 110 | 111 | echo Installation completed successfully! 112 | echo Please rebuild your project: 113 | echo flutter clean ^&^& flutter pub get 114 | 115 | endlocal -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Colors for terminal output 4 | RED='\033[0;31m' 5 | GREEN='\033[0;32m' 6 | BLUE='\033[0;34m' 7 | NC='\033[0m' # No Color 8 | 9 | echo -e "${BLUE}Flutter Kit Installation Script${NC}" 10 | echo "----------------------------------------" 11 | 12 | # Function to read value from pubspec.yaml 13 | read_pubspec_value() { 14 | local key=$1 15 | value=$(grep "^ $key:" pubspec.yaml | cut -d ':' -f 2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/^"\(.*\)"$/\1/') 16 | echo "$value" 17 | } 18 | 19 | # Read values from pubspec.yaml 20 | APP_NAME=$(read_pubspec_value "app_name") 21 | IOS_BUNDLE_ID=$(read_pubspec_value "ios_bundle_id") 22 | ANDROID_PACKAGE_NAME=$(read_pubspec_value "android_package_name") 23 | DART_PACKAGE=$(read_pubspec_value "dart_package") 24 | 25 | echo -e "${GREEN}Configuration from pubspec.yaml:${NC}" 26 | echo "App Name: $APP_NAME" 27 | echo "iOS Bundle ID: $IOS_BUNDLE_ID" 28 | echo "Android Package Name: $ANDROID_PACKAGE_NAME" 29 | echo "Dart Package Name: $DART_PACKAGE" 30 | echo "" 31 | 32 | # Check if running on macOS for iOS updates 33 | if [[ "$OSTYPE" == "darwin"* ]]; then 34 | # Update iOS bundle identifier 35 | echo -e "${BLUE}Updating iOS bundle identifier...${NC}" 36 | find ios -name "project.pbxproj" -exec sed -i '' "s/PRODUCT_BUNDLE_IDENTIFIER = .*;/PRODUCT_BUNDLE_IDENTIFIER = $IOS_BUNDLE_ID;/g" {} \; 37 | 38 | # Update iOS app name 39 | echo -e "${BLUE}Updating iOS app name...${NC}" 40 | PLIST_PATH="ios/Runner/Info.plist" 41 | plutil -replace CFBundleName -string "$APP_NAME" "$PLIST_PATH" 42 | plutil -replace CFBundleDisplayName -string "$APP_NAME" "$PLIST_PATH" 43 | fi 44 | 45 | # Android package updates 46 | echo -e "${BLUE}Updating Android configuration...${NC}" 47 | BUILD_GRADLE_PATH="android/app/build.gradle.kts" 48 | 49 | # Extract current applicationId and namespace 50 | OLD_APPLICATION_ID=$(grep "applicationId" "$BUILD_GRADLE_PATH" | head -1 | sed "s/.*applicationId = \"\(.*\)\".*/\1/") 51 | OLD_NAMESPACE=$(grep "namespace" "$BUILD_GRADLE_PATH" | head -1 | sed "s/.*namespace = \"\(.*\)\".*/\1/") 52 | 53 | echo "Current applicationId: $OLD_APPLICATION_ID" 54 | echo "Current namespace: $OLD_NAMESPACE" 55 | echo "New package name: $ANDROID_PACKAGE_NAME" 56 | 57 | # Update build.gradle.kts 58 | echo -e "${BLUE}Updating build.gradle.kts...${NC}" 59 | sed -i.bak "s/applicationId = \"$OLD_APPLICATION_ID\"/applicationId = \"$ANDROID_PACKAGE_NAME\"/g" "$BUILD_GRADLE_PATH" 60 | sed -i.bak "s/namespace = \"$OLD_NAMESPACE\"/namespace = \"$ANDROID_PACKAGE_NAME\"/g" "$BUILD_GRADLE_PATH" 61 | rm "$BUILD_GRADLE_PATH.bak" 62 | 63 | # Update AndroidManifest.xml 64 | echo -e "${BLUE}Updating AndroidManifest.xml...${NC}" 65 | find android -name "AndroidManifest.xml" -exec sed -i.bak "s/package=\"$OLD_NAMESPACE\"/package=\"$ANDROID_PACKAGE_NAME\"/g" {} \; 66 | find android -name "AndroidManifest.xml" -exec sed -i.bak "s/android:label=\"[^\"]*\"/android:label=\"$APP_NAME\"/g" {} \; 67 | find android -name "*.bak" -type f -delete 68 | 69 | # Move Java/Kotlin files to new package structure 70 | echo -e "${BLUE}Reorganizing source files...${NC}" 71 | OLD_PATH=$(echo "$OLD_NAMESPACE" | sed 's/\./\//g') 72 | NEW_PATH=$(echo "$ANDROID_PACKAGE_NAME" | sed 's/\./\//g') 73 | KOTLIN_PATH="android/app/src/main/kotlin" 74 | 75 | # Create new directory structure 76 | mkdir -p "$KOTLIN_PATH/$NEW_PATH" 77 | 78 | # Handle MainActivity.kt 79 | if [ -f "$KOTLIN_PATH/$OLD_PATH/MainActivity.kt" ]; then 80 | echo -e "${BLUE}Updating MainActivity.kt...${NC}" 81 | sed "s/package $OLD_NAMESPACE/package $ANDROID_PACKAGE_NAME/g" "$KOTLIN_PATH/$OLD_PATH/MainActivity.kt" > "$KOTLIN_PATH/$NEW_PATH/MainActivity.kt" 82 | rm -f "$KOTLIN_PATH/$OLD_PATH/MainActivity.kt" 83 | else 84 | echo -e "${BLUE}Creating new MainActivity.kt...${NC}" 85 | cat > "$KOTLIN_PATH/$NEW_PATH/MainActivity.kt" << EOL 86 | package $ANDROID_PACKAGE_NAME 87 | 88 | import io.flutter.embedding.android.FlutterActivity 89 | 90 | class MainActivity: FlutterActivity() { 91 | } 92 | EOL 93 | fi 94 | 95 | # Cleanup old package structure 96 | echo -e "${BLUE}Cleaning up old files...${NC}" 97 | if [ -d "$KOTLIN_PATH/$OLD_PATH" ]; then 98 | find "$KOTLIN_PATH/$OLD_PATH" -type f -delete 99 | find "$KOTLIN_PATH" -type d -empty -delete 100 | fi 101 | 102 | # Update Dart package name in files 103 | echo -e "${BLUE}Updating Dart package name in files...${NC}" 104 | find lib -type f -name "*.dart" -exec sed -i.bak "s/package:flutter_kit\//package:$DART_PACKAGE\//g" {} \; 105 | find lib -name "*.bak" -type f -delete 106 | 107 | # Update package name in pubspec.yaml 108 | echo -e "${BLUE}Updating package name in pubspec.yaml...${NC}" 109 | sed -i.bak "s/^name: .*/name: $DART_PACKAGE/" pubspec.yaml 110 | rm -f pubspec.yaml.bak 111 | 112 | echo -e "${GREEN}Android configuration updated successfully!${NC}" 113 | echo "Please rebuild your project:" 114 | echo "flutter clean && flutter pub get" -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | 2 | void main() { 3 | 4 | } 5 | --------------------------------------------------------------------------------