├── .github └── workflows │ ├── build_and_test.yml │ └── sonar_scanner.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── cevheri │ │ │ │ └── flutter │ │ │ │ └── flutter_bloc_advance │ │ │ │ └── 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 ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── README_header.png ├── images │ ├── img.png │ └── logoLight.png └── mock │ ├── GET_account.json │ ├── GET_admin_users.json │ ├── GET_admin_users_authorities_pathParams.json │ ├── GET_admin_users_filter_queryParams.json │ ├── GET_admin_users_list.json │ ├── GET_admin_users_list_queryParams.json │ ├── GET_admin_users_pathParams.json │ ├── GET_admin_users_queryParams.json │ ├── GET_authorities.json │ ├── GET_authorities_pathParams.json │ ├── GET_authorities_queryParams.json │ ├── GET_cities.json │ ├── GET_cities_pathParams.json │ ├── GET_cities_queryParams.json │ ├── GET_customer.json │ ├── GET_districs.json │ ├── GET_districts_cities_pathParams.json │ ├── GET_districts_pathParams.json │ ├── GET_districts_queryParams.json │ ├── POST_account.json │ ├── POST_account_change-password.json │ ├── POST_account_reset_password_init.json │ ├── POST_admin_users.json │ ├── POST_authenticate.json │ ├── POST_authenticate_send_otp.json │ ├── POST_authenticate_verify_otp.json │ ├── POST_authorities.json │ ├── POST_cities.json │ ├── POST_districts.json │ ├── POST_register.json │ ├── PUT_account.json │ ├── PUT_admin_users.json │ └── menus.json ├── build.yaml ├── docs ├── bloc-use-case.excalidraw ├── dev-prod-usecase.excalidraw ├── dev-prod-usecase.svg ├── flutter_stream_guide.md └── unittest.excalidraw ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── 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 ├── configuration │ ├── allowed_paths.dart │ ├── app_key_constants.dart │ ├── app_logger.dart │ ├── constants.dart │ ├── environment.dart │ ├── local_storage.dart │ └── padding_spacing.dart ├── data │ ├── app_api_exception.dart │ ├── http_utils.dart │ ├── models │ │ ├── authority.dart │ │ ├── change_password.dart │ │ ├── city.dart │ │ ├── customer.dart │ │ ├── district.dart │ │ ├── jwt_token.dart │ │ ├── menu.dart │ │ ├── send_otp_request.dart │ │ ├── user.dart │ │ ├── user_jwt.dart │ │ └── verify_otp_request.dart │ └── repository │ │ ├── account_repository.dart │ │ ├── authority_repository.dart │ │ ├── city_repository.dart │ │ ├── customer_repository.dart │ │ ├── district_repository.dart │ │ ├── login_repository.dart │ │ ├── menu_repository.dart │ │ └── user_repository.dart ├── generated │ ├── intl │ │ ├── messages_all.dart │ │ ├── messages_en.dart │ │ └── messages_tr.dart │ └── l10n.dart ├── l10n │ ├── intl_en.arb │ └── intl_tr.arb ├── main │ ├── app.dart │ ├── app_stateless_widget.dart │ ├── initialize_reflectable.dart │ ├── main_local.dart │ ├── main_local.mapper.g.dart │ ├── main_prod.dart │ └── main_prod.mapper.g.dart ├── presentation │ ├── common_blocs │ │ ├── account │ │ │ ├── account.dart │ │ │ ├── account_bloc.dart │ │ │ ├── account_event.dart │ │ │ └── account_state.dart │ │ ├── authority │ │ │ ├── authority.dart │ │ │ ├── authority_bloc.dart │ │ │ ├── authority_event.dart │ │ │ └── authority_state.dart │ │ ├── city │ │ │ ├── city.dart │ │ │ ├── city_bloc.dart │ │ │ ├── city_event.dart │ │ │ └── city_state.dart │ │ └── district │ │ │ ├── district.dart │ │ │ ├── district_bloc.dart │ │ │ ├── district_event.dart │ │ │ └── district_state.dart │ ├── common_widgets │ │ └── drawer │ │ │ ├── drawer_bloc │ │ │ ├── drawer.dart │ │ │ ├── drawer_bloc.dart │ │ │ ├── drawer_event.dart │ │ │ └── drawer_state.dart │ │ │ └── drawer_widget.dart │ └── screen │ │ ├── account │ │ └── account_screen.dart │ │ ├── change_password │ │ ├── bloc │ │ │ ├── change_password.dart │ │ │ ├── change_password_bloc.dart │ │ │ ├── change_password_event.dart │ │ │ └── change_password_state.dart │ │ └── change_password_screen.dart │ │ ├── components │ │ ├── authorities_lov_widget.dart │ │ ├── confirmation_dialog_widget.dart │ │ ├── editor_form_mode.dart │ │ ├── language_selection_dialog.dart │ │ ├── responsive_form_widget.dart │ │ ├── submit_button_widget.dart │ │ └── user_form_fields.dart │ │ ├── customer │ │ ├── bloc │ │ │ ├── customer.dart │ │ │ ├── customer_bloc.dart │ │ │ ├── customer_event.dart │ │ │ └── customer_state.dart │ │ ├── create │ │ │ └── create_customer_screen.dart │ │ ├── edit │ │ │ ├── edit_customer_screen.dart │ │ │ └── edit_form_customer_widget.dart │ │ └── list │ │ │ └── list_customer_screen.dart │ │ ├── forgot_password │ │ ├── bloc │ │ │ ├── forgot_password.dart │ │ │ ├── forgot_password_bloc.dart │ │ │ ├── forgot_password_event.dart │ │ │ └── forgot_password_state.dart │ │ └── forgot_password_screen.dart │ │ ├── home │ │ └── home_screen.dart │ │ ├── login │ │ ├── bloc │ │ │ ├── login.dart │ │ │ ├── login_bloc.dart │ │ │ ├── login_event.dart │ │ │ └── login_state.dart │ │ └── login_screen.dart │ │ ├── register │ │ ├── bloc │ │ │ ├── register.dart │ │ │ ├── register_bloc.dart │ │ │ ├── register_event.dart │ │ │ └── register_state.dart │ │ └── register_screen.dart │ │ ├── settings │ │ ├── bloc │ │ │ ├── settings.dart │ │ │ ├── settings_bloc.dart │ │ │ ├── settings_event.dart │ │ │ └── settings_state.dart │ │ └── settings_screen.dart │ │ └── user │ │ ├── bloc │ │ ├── user.dart │ │ ├── user_bloc.dart │ │ ├── user_event.dart │ │ └── user_state.dart │ │ ├── editor │ │ └── user_editor_screen.dart │ │ └── list │ │ └── list_user_screen.dart ├── routes │ ├── app_navigator_routes │ │ └── app_navigator_routes_config.dart │ ├── app_router.dart │ ├── app_routes_constants.dart │ ├── get_routes │ │ └── app_get_router_config.dart │ ├── go_router_routes │ │ ├── app_go_router_config.dart │ │ └── routes │ │ │ ├── account_routes.dart │ │ │ ├── auth_routes.dart │ │ │ ├── home_routes.dart │ │ │ ├── settings_routes.dart │ │ │ └── user_routes.dart │ └── navigator_static_methods └── utils │ ├── app_constants.dart │ ├── menu_list_cache.dart │ ├── message.dart │ ├── security_utils.dart │ └── storage.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Podfile ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements └── RunnerTests │ └── RunnerTests.swift ├── pubspec.lock ├── pubspec.yaml ├── scripts ├── run_build.sh ├── run_fvm_build.sh ├── run_sonar_scanner.sh └── run_unittests.sh ├── sonar-project.properties ├── test ├── conf │ ├── environment_test.dart │ ├── local_storage_getx_test.dart │ ├── local_storage_getx_test.mocks.dart │ ├── local_storage_test.dart │ ├── local_storage_test.mocks.dart │ ├── localization_test.dart │ ├── logger_test.dart │ ├── router_test.dart │ └── security_utils_test.dart ├── configuration │ └── padding_spacing_test.dart ├── data │ ├── http_utils_test.dart │ ├── http_utils_test.mocks.dart │ ├── model │ │ ├── authority_test.dart │ │ ├── change_password_test.dart │ │ ├── city_test.dart │ │ ├── customer_test.dart │ │ ├── district_test.dart │ │ ├── jwt_token_test.dart │ │ ├── menu_test.dart │ │ ├── otp_send_model_test.dart │ │ ├── otp_verify_model_test.dart │ │ ├── user_jwt_test.dart │ │ └── user_test.dart │ └── repository │ │ ├── account_repository_test.dart │ │ ├── authority_reporitory_test.dart │ │ ├── city_repository_test.dart │ │ ├── district_repostiroy_test.dart │ │ ├── login_repository_test.dart │ │ ├── package_info.dart │ │ └── user_repository_test.dart ├── fake │ ├── city_data.dart │ ├── customer_data.dart │ └── user_data.dart ├── main │ └── app_test.dart ├── presentation │ ├── blocs │ │ ├── account_bloc_test.dart │ │ ├── account_bloc_test.mocks.dart │ │ ├── authority_bloc_test.dart │ │ ├── authority_bloc_test.mocks.dart │ │ ├── change_password_bloc_test.dart │ │ ├── change_password_bloc_test.mocks.dart │ │ ├── city_bloc_test.dart │ │ ├── city_bloc_test.mocks.dart │ │ ├── district_bloc_test.dart │ │ ├── district_bloc_test.mocks.dart │ │ ├── drawer_bloc_test.dart │ │ ├── drawer_bloc_test.mocks.dart │ │ ├── forgot_password_bloc_test.dart │ │ ├── forgot_password_bloc_test.mocks.dart │ │ ├── login_bloc_test.dart │ │ ├── login_bloc_test.mocks.dart │ │ ├── register_bloc_test.dart │ │ ├── register_bloc_test.mocks.dart │ │ ├── settings_bloc_test.dart │ │ ├── user_bloc_test.dart │ │ └── user_bloc_test.mocks.dart │ ├── screen │ │ ├── account │ │ │ ├── account_screen_test.dart │ │ │ ├── account_screen_test.mocks.dart │ │ │ ├── change_password_screen_test.dart │ │ │ └── change_password_screen_test.mocks.dart │ │ ├── forgot_password │ │ │ ├── forgot_password_screen_test.dart │ │ │ └── forgot_password_screen_test.mocks.dart │ │ ├── home │ │ │ └── home_screen_test.dart │ │ ├── login │ │ │ ├── login_screen_test.dart │ │ │ └── login_screen_test.mocks.dart │ │ ├── register │ │ │ ├── register_screen.test.dart │ │ │ ├── register_screen.test.mocks.dart │ │ │ ├── register_screen_go_router_test.dart │ │ │ └── register_screen_go_router_test.mocks.dart │ │ ├── settings │ │ │ ├── settings_screen_test.dart │ │ │ └── settings_screen_test.mocks.dart │ │ └── user │ │ │ ├── list_user_screen_test.dart │ │ │ ├── list_user_screen_test.mocks.dart │ │ │ ├── package_info.dart │ │ │ ├── user_editor_screen_test.dart │ │ │ └── user_editor_screen_test.mocks.dart │ └── widgets │ │ ├── login_otp_email_widget_test.dart │ │ ├── login_otp_email_widget_test.mocks.dart │ │ ├── login_otp_verify_widget_test.dart │ │ ├── login_otp_verify_widget_test.mocks.dart │ │ ├── package_info.dart │ │ ├── responsive_form_widget_test.dart │ │ └── submit_button_widget_test.dart ├── test_utils.dart ├── utils │ └── app_constants_test.dart └── widget_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.github/workflows/build_and_test.yml: -------------------------------------------------------------------------------- 1 | name: Flutter Build And Test 2 | on: 3 | push: 4 | ## all branches 5 | branches: 6 | - main 7 | - dev 8 | - feature/* 9 | - bugfix/* 10 | - hotfix/* 11 | - release/v* 12 | 13 | pull_request: 14 | branches: 15 | - main 16 | jobs: 17 | build: 18 | name: Analyze and Test 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v4 23 | 24 | - name: Setup Flutter 25 | uses: subosito/flutter-action@v2.18.0 26 | with: 27 | flutter-version: 3.27.1 28 | channel: stable 29 | - name: Setup Dart 30 | uses: dart-lang/setup-dart@v1.7.0 31 | with: 32 | dart-version: 3.6.0 33 | 34 | - name: Setup Java JDK 17 35 | uses: actions/setup-java@v4 36 | with: 37 | distribution: 'temurin' 38 | java-version: '17' 39 | - name: set JAVA_HOME 40 | run: echo "JAVA_HOME=/usr/lib/jvm/temurin-17-jdk-amd64" >> $GITHUB_ENV 41 | - name: flutter jdk configuration 42 | run: flutter config --jdk-dir /usr/lib/jvm/temurin-17-jdk-amd64 43 | 44 | - name: Install dependencies 45 | run: flutter pub get 46 | - name: Analyze 47 | run: flutter analyze 48 | # - name: Run tests 49 | # run: flutter test --concurrency=1 --test-randomize-ordering-seed=random 50 | # - name: Run tests 51 | # run: flutter test 52 | - name: Build APK 53 | run: flutter build apk --release --target lib/main/main_prod.dart 54 | - name: Build Android App Bundle 55 | run: flutter build appbundle --release --target lib/main/main_prod.dart 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | .vscode/ 20 | 21 | # unittest temp storage 22 | GetStorage.bak 23 | GetStorage.gs 24 | coverage/ 25 | tmp/ 26 | 27 | # sonar 28 | .scannerwork/ 29 | 30 | # The .vscode folder contains launch configuration and tasks you configure in 31 | # VS Code which you may wish to be included in version control, so this line 32 | # is commented out by default. 33 | #.vscode/ 34 | 35 | # Flutter/Dart/Pub related 36 | **/doc/api/ 37 | **/ios/Flutter/.last_build_id 38 | .dart_tool/ 39 | .flutter-plugins 40 | .flutter-plugins-dependencies 41 | .packages 42 | .pub-cache/ 43 | .pub/ 44 | /build/ 45 | 46 | # Symbolication related 47 | app.*.symbols 48 | 49 | # Obfuscation related 50 | app.*.map.json 51 | 52 | # Android Studio will place build artifacts here 53 | /android/app/debug 54 | /android/app/profile 55 | /android/app/release 56 | 57 | # FVM Version Cache 58 | .fvm/ 59 | .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: "603104015dd692ea3403755b55d07813d5cf8965" 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: 603104015dd692ea3403755b55d07813d5cf8965 17 | base_revision: 603104015dd692ea3403755b55d07813d5cf8965 18 | - platform: android 19 | create_revision: 603104015dd692ea3403755b55d07813d5cf8965 20 | base_revision: 603104015dd692ea3403755b55d07813d5cf8965 21 | - platform: ios 22 | create_revision: 603104015dd692ea3403755b55d07813d5cf8965 23 | base_revision: 603104015dd692ea3403755b55d07813d5cf8965 24 | - platform: linux 25 | create_revision: 603104015dd692ea3403755b55d07813d5cf8965 26 | base_revision: 603104015dd692ea3403755b55d07813d5cf8965 27 | - platform: macos 28 | create_revision: 603104015dd692ea3403755b55d07813d5cf8965 29 | base_revision: 603104015dd692ea3403755b55d07813d5cf8965 30 | - platform: web 31 | create_revision: 603104015dd692ea3403755b55d07813d5cf8965 32 | base_revision: 603104015dd692ea3403755b55d07813d5cf8965 33 | - platform: windows 34 | create_revision: 603104015dd692ea3403755b55d07813d5cf8965 35 | base_revision: 603104015dd692ea3403755b55d07813d5cf8965 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Only the latest version of advanced-flutter-template-with-bloc is supported with security updates. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | Use this section to tell people how to report a vulnerability. 10 | 11 | Please do not report security issues to the public issue tracker. If you have found a security vulnerability, please send security issues to cevheribozoglan@gmail.com. We will do our best to get back to you soon. 12 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # prefer_const_declarations: false 25 | # constant_identifier_names: false 26 | # Unnecessary 'new' keyword. (Documentation) 27 | # unnecessary_new: false 28 | # Use 'const' with the constructor to improve performance 29 | # prefer_const_constructors: false 30 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 31 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 32 | 33 | # Additional information about this file can be found at 34 | # https://dart.dev/guides/language/analysis-options 35 | 36 | # exclude generated files 37 | analyzer: 38 | exclude: 39 | - "**/*.g.dart" 40 | - "**/*.p.dart" 41 | - "**/*.r.dart" 42 | - "**/*.reflectable.dart" 43 | - "**/*.router.dart" 44 | - "**/*.theme.dart" 45 | - "**/*.theme.g.dart" 46 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id "dev.flutter.flutter-gradle-plugin" 6 | } 7 | 8 | android { 9 | namespace = "com.cevheri.flutter.flutter_bloc_advance" 10 | compileSdk = flutter.compileSdkVersion 11 | ndkVersion = flutter.ndkVersion 12 | 13 | compileOptions { 14 | sourceCompatibility = JavaVersion.VERSION_1_8 15 | targetCompatibility = JavaVersion.VERSION_1_8 16 | } 17 | 18 | kotlinOptions { 19 | jvmTarget = JavaVersion.VERSION_1_8 20 | } 21 | 22 | defaultConfig { 23 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 24 | applicationId = "com.cevheri.flutter.flutter_bloc_advance" 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.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/com/cevheri/flutter/flutter_bloc_advance/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.cevheri.flutter.flutter_bloc_advance 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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(":app") 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -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.3-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.1.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.8.22" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /assets/README_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/assets/README_header.png -------------------------------------------------------------------------------- /assets/images/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/assets/images/img.png -------------------------------------------------------------------------------- /assets/images/logoLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/assets/images/logoLight.png -------------------------------------------------------------------------------- /assets/mock/GET_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "user-1", 3 | "login": "admin", 4 | "email": "admin@sample.tech", 5 | "firstName": "Admin", 6 | "lastName": "User", 7 | "activated": true, 8 | "langKey": "en", 9 | "createdBy": "system", 10 | "createdDate": "2024-01-04T06:02:47.757Z", 11 | "lastModifiedBy": "admin", 12 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 13 | "authorities": [ 14 | "ROLE_ADMIN", "ROLE_USER" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /assets/mock/GET_admin_users.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "user-1", 3 | "login": "admin", 4 | "email": "admin@sample.tech", 5 | "firstName": "Admin", 6 | "lastName": "User", 7 | "activated": true, 8 | "langKey": "en", 9 | "createdBy": "system", 10 | "createdDate": "2024-01-04T06:02:47.757Z", 11 | "lastModifiedBy": "admin", 12 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 13 | "authorities": [ 14 | "ROLE_ADMIN", "ROLE_USER" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /assets/mock/GET_admin_users_authorities_pathParams.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "user-1", 4 | "login": "admin", 5 | "email": "admin@sample.tech", 6 | "firstName": "Admin", 7 | "lastName": "User", 8 | "activated": true, 9 | "langKey": "en", 10 | "createdBy": "system", 11 | "createdDate": "2024-01-04T06:02:47.757Z", 12 | "lastModifiedBy": "admin", 13 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 14 | "authorities": [ 15 | "ROLE_ADMIN", "ROLE_USER" 16 | ] 17 | }, 18 | { 19 | "id": "user-2", 20 | "login": "new_user-2", 21 | "firstName": "Mock new user First Name", 22 | "lastName": "Mock new user Last Name", 23 | "email": "mock_account-2@test.com", 24 | "activated": true, 25 | "langKey": null, 26 | "createdBy": "system", 27 | "createdDate": null, 28 | "lastModifiedBy": "admin", 29 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 30 | "authorities": [ 31 | "ROLE_ADMIN" 32 | ] 33 | }, 34 | { 35 | "id": "user-3", 36 | "login": "new_user-3", 37 | "firstName": "Mock new user First Name", 38 | "lastName": "Mock new user Last Name", 39 | "email": "mock_account-3@test.com", 40 | "activated": true, 41 | "langKey": null, 42 | "createdBy": "system", 43 | "createdDate": null, 44 | "lastModifiedBy": "admin", 45 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 46 | "authorities": [ 47 | "ROLE_ADMIN" 48 | ] 49 | }, 50 | { 51 | "id": "user-4", 52 | "login": "new_user-4", 53 | "firstName": "Mock new user First Name", 54 | "lastName": "Mock new user Last Name", 55 | "email": "mock_account-4@test.com", 56 | "activated": true, 57 | "langKey": null, 58 | "createdBy": "system", 59 | "createdDate": null, 60 | "lastModifiedBy": "admin", 61 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 62 | "authorities": [ 63 | "ROLE_ADMIN" 64 | ] 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /assets/mock/GET_admin_users_filter_queryParams.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "user-1", 4 | "login": "admin", 5 | "email": "admin@sample.tech", 6 | "firstName": "Admin", 7 | "lastName": "User", 8 | "activated": true, 9 | "langKey": "en", 10 | "createdBy": "system", 11 | "createdDate": "2024-01-04T06:02:47.757Z", 12 | "lastModifiedBy": "admin", 13 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 14 | "authorities": [ 15 | "ROLE_ADMIN", "ROLE_USER" 16 | ] 17 | }, 18 | { 19 | "id": "user-2", 20 | "login": "new_user-2", 21 | "firstName": "Mock new user First Name", 22 | "lastName": "Mock new user Last Name", 23 | "email": "mock_account-2@test.com", 24 | "activated": true, 25 | "langKey": null, 26 | "createdBy": "system", 27 | "createdDate": null, 28 | "lastModifiedBy": "admin", 29 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 30 | "authorities": [ 31 | "ROLE_ADMIN" 32 | ] 33 | }, 34 | { 35 | "id": "user-3", 36 | "login": "new_user-3", 37 | "firstName": "Mock new user First Name", 38 | "lastName": "Mock new user Last Name", 39 | "email": "mock_account-3@test.com", 40 | "activated": true, 41 | "langKey": null, 42 | "createdBy": "system", 43 | "createdDate": null, 44 | "lastModifiedBy": "admin", 45 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 46 | "authorities": [ 47 | "ROLE_ADMIN" 48 | ] 49 | }, 50 | { 51 | "id": "user-4", 52 | "login": "new_user-4", 53 | "firstName": "Mock new user First Name", 54 | "lastName": "Mock new user Last Name", 55 | "email": "mock_account-4@test.com", 56 | "activated": true, 57 | "langKey": null, 58 | "createdBy": "system", 59 | "createdDate": null, 60 | "lastModifiedBy": "admin", 61 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 62 | "authorities": [ 63 | "ROLE_ADMIN" 64 | ] 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /assets/mock/GET_admin_users_list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "user-1", 4 | "login": "admin", 5 | "email": "admin@sample.tech", 6 | "firstName": "Admin", 7 | "lastName": "User", 8 | "activated": true, 9 | "langKey": "en", 10 | "createdBy": "system", 11 | "createdDate": "2024-01-04T06:02:47.757Z", 12 | "lastModifiedBy": "admin", 13 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 14 | "authorities": [ 15 | "ROLE_ADMIN", "ROLE_USER" 16 | ] 17 | }, 18 | { 19 | "id": "user-2", 20 | "login": "new_user-2", 21 | "firstName": "Mock new user First Name", 22 | "lastName": "Mock new user Last Name", 23 | "email": "mock_account-2@test.com", 24 | "activated": true, 25 | "langKey": null, 26 | "createdBy": "system", 27 | "createdDate": null, 28 | "lastModifiedBy": "admin", 29 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 30 | "authorities": [ 31 | "ROLE_ADMIN" 32 | ] 33 | }, 34 | { 35 | "id": "user-3", 36 | "login": "new_user-3", 37 | "firstName": "Mock new user First Name", 38 | "lastName": "Mock new user Last Name", 39 | "email": "mock_account-3@test.com", 40 | "activated": true, 41 | "langKey": null, 42 | "createdBy": "system", 43 | "createdDate": null, 44 | "lastModifiedBy": "admin", 45 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 46 | "authorities": [ 47 | "ROLE_ADMIN" 48 | ] 49 | }, 50 | { 51 | "id": "user-4", 52 | "login": "new_user-4", 53 | "firstName": "Mock new user First Name", 54 | "lastName": "Mock new user Last Name", 55 | "email": "mock_account-4@test.com", 56 | "activated": true, 57 | "langKey": null, 58 | "createdBy": "system", 59 | "createdDate": null, 60 | "lastModifiedBy": "admin", 61 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 62 | "authorities": [ 63 | "ROLE_ADMIN" 64 | ] 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /assets/mock/GET_admin_users_list_queryParams.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "user-1", 4 | "login": "admin", 5 | "email": "admin@sample.tech", 6 | "firstName": "Admin", 7 | "lastName": "User", 8 | "activated": true, 9 | "langKey": "en", 10 | "createdBy": "system", 11 | "createdDate": "2024-01-04T06:02:47.757Z", 12 | "lastModifiedBy": "admin", 13 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 14 | "authorities": [ 15 | "ROLE_ADMIN", "ROLE_USER" 16 | ] 17 | }, 18 | { 19 | "id": "user-2", 20 | "login": "new_user-2", 21 | "firstName": "Mock new user First Name", 22 | "lastName": "Mock new user Last Name", 23 | "email": "mock_account-2@test.com", 24 | "activated": true, 25 | "langKey": null, 26 | "createdBy": "system", 27 | "createdDate": null, 28 | "lastModifiedBy": "admin", 29 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 30 | "authorities": [ 31 | "ROLE_ADMIN" 32 | ] 33 | }, 34 | { 35 | "id": "user-3", 36 | "login": "new_user-3", 37 | "firstName": "Mock new user First Name", 38 | "lastName": "Mock new user Last Name", 39 | "email": "mock_account-3@test.com", 40 | "activated": true, 41 | "langKey": null, 42 | "createdBy": "system", 43 | "createdDate": null, 44 | "lastModifiedBy": "admin", 45 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 46 | "authorities": [ 47 | "ROLE_ADMIN" 48 | ] 49 | }, 50 | { 51 | "id": "user-4", 52 | "login": "new_user-4", 53 | "firstName": "Mock new user First Name", 54 | "lastName": "Mock new user Last Name", 55 | "email": "mock_account-4@test.com", 56 | "activated": true, 57 | "langKey": null, 58 | "createdBy": "system", 59 | "createdDate": null, 60 | "lastModifiedBy": "admin", 61 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 62 | "authorities": [ 63 | "ROLE_ADMIN" 64 | ] 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /assets/mock/GET_admin_users_pathParams.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "id": "user-1", 4 | "login": "admin", 5 | "email": "admin@sample.tech", 6 | "firstName": "Admin", 7 | "lastName": "User", 8 | "activated": true, 9 | "langKey": "en", 10 | "createdBy": "system", 11 | "createdDate": "2024-01-04T06:02:47.757Z", 12 | "lastModifiedBy": "admin", 13 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 14 | "authorities": ["ROLE_ADMIN", "ROLE_USER"] 15 | 16 | } 17 | -------------------------------------------------------------------------------- /assets/mock/GET_admin_users_queryParams.json: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | { 4 | "id": "user-1", 5 | "login": "admin", 6 | "email": "admin@sample.tech", 7 | "firstName": "Admin", 8 | "lastName": "User", 9 | "activated": true, 10 | "langKey": "en", 11 | "createdBy": "system", 12 | "createdDate": "2024-01-04T06:02:47.757Z", 13 | "lastModifiedBy": "admin", 14 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 15 | "authorities": ["ROLE_ADMIN", "ROLE_USER"] 16 | 17 | }, 18 | { 19 | 20 | "id": "user-2", 21 | "login": "new_user-2", 22 | "firstName": "Mock new user First Name", 23 | "lastName": "Mock new user Last Name", 24 | "email": "mock_account-2@test.com", 25 | "activated": true, 26 | "langKey": null, 27 | "createdBy": "system", 28 | "createdDate": null, 29 | "lastModifiedBy": "admin", 30 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 31 | "authorities": ["ROLE_ADMIN"] 32 | 33 | }, 34 | { 35 | 36 | "id": "user-3", 37 | "login": "new_user-3", 38 | "firstName": "Mock new user First Name", 39 | "lastName": "Mock new user Last Name", 40 | "email": "mock_account-3@test.com", 41 | "activated": true, 42 | "langKey": null, 43 | "createdBy": "system", 44 | "createdDate": null, 45 | "lastModifiedBy": "admin", 46 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 47 | "authorities": ["ROLE_ADMIN"] 48 | 49 | }, 50 | { 51 | 52 | "id": "user-4", 53 | "login": "new_user-4", 54 | "firstName": "Mock new user First Name", 55 | "lastName": "Mock new user Last Name", 56 | "email": "mock_account-4@test.com", 57 | "activated": true, 58 | "langKey": null, 59 | "createdBy": "system", 60 | "createdDate": null, 61 | "lastModifiedBy": "admin", 62 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 63 | "authorities": ["ROLE_ADMIN"] 64 | 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /assets/mock/GET_authorities.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "ROLE_USER" 4 | }, 5 | { 6 | "name": "ROLE_ADMIN" 7 | } 8 | ] -------------------------------------------------------------------------------- /assets/mock/GET_authorities_pathParams.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ROLE_USER" 3 | } 4 | -------------------------------------------------------------------------------- /assets/mock/GET_authorities_queryParams.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "ROLE_USER" 4 | }, 5 | { 6 | "name": "ROLE_ADMIN" 7 | } 8 | ] -------------------------------------------------------------------------------- /assets/mock/GET_cities.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "name": "name", 5 | "plateCode": "plateCode" 6 | }, 7 | { 8 | "id": "2", 9 | "name": "name2", 10 | "plateCode": "plateCode2" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /assets/mock/GET_cities_pathParams.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1", 3 | "name": "name", 4 | "plateCode": "plateCode" 5 | } 6 | -------------------------------------------------------------------------------- /assets/mock/GET_cities_queryParams.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "name": "name", 5 | "plateCode": "plateCode" 6 | }, 7 | { 8 | "id": "2", 9 | "name": "name2", 10 | "plateCode": "plateCode2" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /assets/mock/GET_customer.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/assets/mock/GET_customer.json -------------------------------------------------------------------------------- /assets/mock/GET_districs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "name": "name", 5 | "code": "code" 6 | }, 7 | { 8 | "id": "2", 9 | "name": "name2", 10 | "code": "code2" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /assets/mock/GET_districts_cities_pathParams.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "name": "name", 5 | "code": "code" 6 | }, 7 | { 8 | "id": "2", 9 | "name": "name2", 10 | "code": "code2" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /assets/mock/GET_districts_pathParams.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1", 3 | "name": "name", 4 | "code": "code" 5 | } 6 | -------------------------------------------------------------------------------- /assets/mock/GET_districts_queryParams.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "name": "name", 5 | "code": "code" 6 | }, 7 | { 8 | "id": "2", 9 | "name": "name2", 10 | "code": "code2" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /assets/mock/POST_account.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "id": "user-1", 4 | "login": "admin", 5 | "email": "admin@sample.tech", 6 | "firstName": "Admin", 7 | "lastName": "User", 8 | "activated": true, 9 | "langKey": "en", 10 | "createdBy": "system", 11 | "createdDate": "2024-01-04T06:02:47.757Z", 12 | "lastModifiedBy": "admin", 13 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 14 | "authorities": ["ROLE_ADMIN", "ROLE_USER"] 15 | 16 | } 17 | -------------------------------------------------------------------------------- /assets/mock/POST_account_change-password.json: -------------------------------------------------------------------------------- 1 | { 2 | "currentPassword": "123456", 3 | "newPassword": "123456" 4 | } -------------------------------------------------------------------------------- /assets/mock/POST_account_reset_password_init.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "test@test.com" 3 | } -------------------------------------------------------------------------------- /assets/mock/POST_admin_users.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "id": "user-1", 4 | "login": "admin", 5 | "email": "admin@sample.tech", 6 | "firstName": "Admin", 7 | "lastName": "User", 8 | "activated": true, 9 | "langKey": "en", 10 | "createdBy": "system", 11 | "createdDate": "2024-01-04T06:02:47.757Z", 12 | "lastModifiedBy": "admin", 13 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 14 | "authorities": ["ROLE_ADMIN", "ROLE_USER"] 15 | 16 | } 17 | -------------------------------------------------------------------------------- /assets/mock/POST_authenticate.json: -------------------------------------------------------------------------------- 1 | { 2 | "id_token": "MOCK_TOKEN" 3 | } 4 | -------------------------------------------------------------------------------- /assets/mock/POST_authenticate_send_otp.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /assets/mock/POST_authenticate_verify_otp.json: -------------------------------------------------------------------------------- 1 | { 2 | "id_token": "MOCK_TOKEN" 3 | } 4 | -------------------------------------------------------------------------------- /assets/mock/POST_authorities.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ROLE_USER" 3 | } -------------------------------------------------------------------------------- /assets/mock/POST_cities.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1", 3 | "name": "name", 4 | "plateCode": "plateCode" 5 | } 6 | -------------------------------------------------------------------------------- /assets/mock/POST_districts.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1", 3 | "name": "name", 4 | "code": "code" 5 | } 6 | -------------------------------------------------------------------------------- /assets/mock/POST_register.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "id": "user-2", 4 | "login": "user", 5 | "email": "user@sample.tech", 6 | "firstName": "User", 7 | "lastName": "User", 8 | "langKey": "en", 9 | "createdBy": "system", 10 | "createdDate": "2024-01-04T06:02:47.757Z", 11 | "lastModifiedBy": "admin", 12 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 13 | "authorities": ["ROLE_USER"] 14 | 15 | } 16 | -------------------------------------------------------------------------------- /assets/mock/PUT_account.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "id": "user-1", 4 | "login": "admin", 5 | "email": "admin@sample.tech", 6 | "firstName": "Admin", 7 | "lastName": "User", 8 | "activated": true, 9 | "langKey": "en", 10 | "createdBy": "system", 11 | "createdDate": "2024-01-04T06:02:47.757Z", 12 | "lastModifiedBy": "admin", 13 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 14 | "authorities": [ "ROLE_ADMIN", "ROLE_USER"] 15 | 16 | } 17 | -------------------------------------------------------------------------------- /assets/mock/PUT_admin_users.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "id": "user-1", 4 | "login": "admin", 5 | "email": "admin@sample.tech", 6 | "firstName": "Admin", 7 | "lastName": "User", 8 | "activated": true, 9 | "langKey": "en", 10 | "createdBy": "system", 11 | "createdDate": "2024-01-04T06:02:47.757Z", 12 | "lastModifiedBy": "admin", 13 | "lastModifiedDate": "2024-01-04T06:02:47.757Z", 14 | "authorities": ["ROLE_ADMIN", "ROLE_USER"] 15 | 16 | } 17 | -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | dart_json_mapper: 5 | generate_for: 6 | - lib/main/main_local.dart 7 | - lib/main/main_prod.dart 8 | reflectable: 9 | generate_for: 10 | - no/files 11 | flutter_localizations: 12 | generate_for: 13 | - lib/l10n/*.arb 14 | -------------------------------------------------------------------------------- /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 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.9.1) 3 | - CryptoSwift (1.8.3) 4 | - Flutter (1.0.0) 5 | - flutter_inappwebview (0.0.1): 6 | - Flutter 7 | - flutter_inappwebview/Core (= 0.0.1) 8 | - OrderedSet (~> 5.0) 9 | - flutter_inappwebview/Core (0.0.1): 10 | - Flutter 11 | - OrderedSet (~> 5.0) 12 | - http_certificate_pinning (1.0.3): 13 | - Alamofire (~> 4.9.1) 14 | - CryptoSwift 15 | - Flutter 16 | - OrderedSet (5.0.0) 17 | - printing (1.0.0): 18 | - Flutter 19 | - shared_preferences_foundation (0.0.1): 20 | - Flutter 21 | - FlutterMacOS 22 | 23 | DEPENDENCIES: 24 | - Flutter (from `Flutter`) 25 | - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) 26 | - http_certificate_pinning (from `.symlinks/plugins/http_certificate_pinning/ios`) 27 | - printing (from `.symlinks/plugins/printing/ios`) 28 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) 29 | 30 | SPEC REPOS: 31 | trunk: 32 | - Alamofire 33 | - CryptoSwift 34 | - OrderedSet 35 | 36 | EXTERNAL SOURCES: 37 | Flutter: 38 | :path: Flutter 39 | flutter_inappwebview: 40 | :path: ".symlinks/plugins/flutter_inappwebview/ios" 41 | http_certificate_pinning: 42 | :path: ".symlinks/plugins/http_certificate_pinning/ios" 43 | printing: 44 | :path: ".symlinks/plugins/printing/ios" 45 | shared_preferences_foundation: 46 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin" 47 | 48 | SPEC CHECKSUMS: 49 | Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18 50 | CryptoSwift: 967f37cea5a3294d9cce358f78861652155be483 51 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 52 | flutter_inappwebview: 3d32228f1304635e7c028b0d4252937730bbc6cf 53 | http_certificate_pinning: 2a9097b304c7ea3b51804af46d381aba8108fa29 54 | OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c 55 | printing: 233e1b73bd1f4a05615548e9b5a324c98588640b 56 | shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 57 | 58 | PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 59 | 60 | COCOAPODS: 1.15.2 61 | -------------------------------------------------------------------------------- /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.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/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/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 Bloc Advance 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_bloc_advance 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/configuration/allowed_paths.dart: -------------------------------------------------------------------------------- 1 | List allowedPaths = [ 2 | '/authenticate', 3 | '/register', 4 | '/logout', 5 | '/account/reset-password/init', 6 | '/forgot-password', 7 | '/login-otp', 8 | '/login-otp-verify', 9 | '/authenticate/send-otp', 10 | '/authenticate/verify-otp' 11 | ]; 12 | 13 | List authPaths = [ 14 | '/authenticate', 15 | '/login-otp', 16 | '/login-otp-verify', 17 | ]; 18 | -------------------------------------------------------------------------------- /lib/configuration/app_key_constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const Key drawerButtonLogoutKey = Key("drawerButtonLogoutKey"); 4 | const Key drawerButtonLogoutYesKey = Key("drawerButtonLogoutYesKey"); 5 | const Key drawerButtonLogoutNoKey = Key("drawerButtonLogoutNoKey"); 6 | const Key loginTextFieldUsernameKey = Key("loginTextFieldUsernameKey"); 7 | const Key loginTextFieldPasswordKey = Key("loginTextFieldPasswordKey"); 8 | const Key loginButtonSubmitKey = Key("loginButtonSubmitKey"); 9 | const Key loginButtonForgotPasswordKey = Key("loginButtonForgotPasswordKey"); 10 | const Key loginButtonRegisterKey = Key("loginButtonRegisterKey"); 11 | const Key loginButtonPasswordVisibilityKey = Key("loginButtonPasswordVisibilityKey"); 12 | const Key forgotPasswordButtonSubmitKey = Key("forgotPasswordButtonSubmit"); 13 | const Key forgotPasswordTextFieldEmailKey = Key("forgotPasswordTextFieldEmail"); 14 | 15 | // register screen 16 | const Key registerSubmitButtonKey = Key("registerSubmitButtonKey"); 17 | const Key registerFirstNameTextFieldKey = Key("registerFirstNameTextFieldKey"); 18 | const Key registerLastNameTextFieldKey = Key("registerLastNameTextFieldKey"); 19 | const Key registerEmailTextFieldKey = Key("registerEmailTextFieldKey"); 20 | 21 | // settings screen 22 | const Key settingsChangePasswordButtonKey = Key("settingsChangePasswordButtonKey"); 23 | const Key settingsChangeLanguageButtonKey = Key("settingsChangeLanguageButtonKey"); 24 | const Key settingsLogoutButtonKey = Key("settingsLogoutButtonKey"); 25 | 26 | // change password screen 27 | const Key changePasswordButtonSubmitKey = Key("changePasswordButtonSubmitKey"); 28 | const Key changePasswordTextFieldCurrentPasswordKey = Key("changePasswordTextFieldCurrentPasswordKey"); 29 | const Key changePasswordTextFieldNewPasswordKey = Key("changePasswordTextFieldNewPasswordKey"); 30 | -------------------------------------------------------------------------------- /lib/configuration/app_logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | enum LogFormat { simple, extended } 4 | 5 | /// A simple logger class that wraps the [Logger] class. 6 | /// 7 | /// This class provides a simple way to log messages with different log levels. 8 | /// It also provides a way to configure the logger with different options. 9 | /// 10 | class AppLogger { 11 | static late Logger _logger; 12 | static late Level _level; 13 | static late LogFormat _logFormat; 14 | late String _name; 15 | 16 | AppLogger() { 17 | _name = "Logger"; 18 | } 19 | 20 | AppLogger.getLogger(String name) { 21 | _name = name; 22 | } 23 | 24 | static void configure({ 25 | required bool isProduction, 26 | bool logToFile = false, 27 | LogFormat logFormat = LogFormat.simple, 28 | }) { 29 | _level = isProduction ? Level.info : Level.debug; 30 | _logFormat = logFormat; 31 | final simple = SimplePrinter(printTime: true); 32 | final pretty = PrettyPrinter(dateTimeFormat: DateTimeFormat.dateAndTime, methodCount: 2); 33 | LogPrinter printer = _logFormat == LogFormat.simple ? simple : pretty; 34 | LogOutput output = 35 | ConsoleOutput(); // logToFile ? FileOutput(file: File('log.txt'), overrideExisting: true) : ConsoleOutput(); Unsupported operation: Not supported on this platform. 36 | LogFilter filter = isProduction ? ProductionFilter() : DevelopmentFilter(); 37 | _logger = Logger(filter: filter, printer: printer, output: output, level: _level); 38 | } 39 | 40 | // generate log methods with message and arguments 41 | String _msg(dynamic message, [List? args]) { 42 | String msg = "$_name : $message"; 43 | if (args != null) { 44 | for (int i = 0; i < args.length; i++) { 45 | msg = msg.replaceFirst("{}", args[i].toString()); 46 | } 47 | } 48 | return msg; 49 | } 50 | 51 | void trace(dynamic message, [List? args]) => _logger.t(_msg(message, args)); 52 | 53 | void debug(dynamic message, [List? args]) => _logger.d(_msg(message, args)); 54 | 55 | void info(dynamic message, [List? args]) => _logger.i(_msg(message, args)); 56 | 57 | void warn(dynamic message, [List? args]) => _logger.w(_msg(message, args)); 58 | 59 | void error(dynamic message, [List? args]) => _logger.e(_msg(message, args)); 60 | 61 | @override 62 | String toString() { 63 | return "AppLogger($_name, ${_level.name}, ${_logFormat.name})"; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/configuration/constants.dart: -------------------------------------------------------------------------------- 1 | class LocaleConstants { 2 | static const Map languages = {'en': 'English'}; 3 | static const String langStorageKey = 'locale'; 4 | static const String logoLightUrl = 'assets/images/logoLight.png'; 5 | static const String defaultImgUrl = 'assets/images/img.png'; 6 | } 7 | -------------------------------------------------------------------------------- /lib/configuration/environment.dart: -------------------------------------------------------------------------------- 1 | /// This file is used to set the environment 2 | enum Environment { dev, prod, test } 3 | 4 | /// This class is used to store all environment variables 5 | /// 6 | /// It is used in the main_local.dart file to set the environment 7 | class ProfileConstants { 8 | static Map? _config; 9 | 10 | static void setEnvironment(Environment env) { 11 | switch (env) { 12 | case Environment.dev: 13 | _config = _Config.devConstants; 14 | break; 15 | case Environment.test: 16 | _config = _Config.testConstants; 17 | break; 18 | case Environment.prod: 19 | _config = _Config.prodConstants; 20 | break; 21 | } 22 | } 23 | 24 | static bool get isProduction { 25 | return _config == _Config.prodConstants; 26 | } 27 | 28 | static bool get isDevelopment { 29 | return _config == _Config.devConstants; 30 | } 31 | 32 | static bool get isTest { 33 | return _config == _Config.testConstants; 34 | } 35 | 36 | static get api { 37 | return _config![_Config.api]; 38 | } 39 | } 40 | 41 | class _Config { 42 | static const api = "API"; 43 | 44 | static Map devConstants = { 45 | api: "mock", 46 | }; 47 | 48 | static Map testConstants = { 49 | api: "mock", 50 | }; 51 | 52 | static Map prodConstants = { 53 | api: "https://dhw-api.onrender.com/api", 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /lib/data/app_api_exception.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 2 | 3 | final _log = AppLogger.getLogger("BusinessException"); 4 | 5 | abstract class AppException implements Exception { 6 | final String? _message; 7 | final String? _prefix; 8 | 9 | AppException(String? message, String? prefix) 10 | : _message = message, 11 | _prefix = prefix { 12 | _log.error("$_prefix$_message"); 13 | } 14 | 15 | @override 16 | String toString() { 17 | return "$_prefix$_message"; 18 | } 19 | } 20 | 21 | class FetchDataException extends AppException { 22 | FetchDataException(String message) : super(message, "Error During Communication: "); 23 | } 24 | 25 | final class BadRequestException extends AppException { 26 | BadRequestException([message]) : super(message, "Invalid Request: "); 27 | } 28 | 29 | final class UnauthorizedException extends AppException { 30 | UnauthorizedException([message]) : super(message, "Unauthorized: "); 31 | } 32 | 33 | class InvalidInputException extends AppException { 34 | InvalidInputException(String message) : super(message, "Invalid Input: "); 35 | } 36 | 37 | class ApiBusinessException extends AppException { 38 | ApiBusinessException(String message) : super(message, "Api Business Exception: "); 39 | } 40 | -------------------------------------------------------------------------------- /lib/data/models/authority.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dart_json_mapper/dart_json_mapper.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | 6 | @jsonSerializable 7 | class Authority extends Equatable { 8 | @JsonProperty(name: 'name') 9 | final String? name; 10 | 11 | const Authority({ 12 | this.name, 13 | }); 14 | 15 | Authority copyWith({ 16 | String? name, 17 | }) { 18 | return Authority( 19 | name: name ?? this.name, 20 | ); 21 | } 22 | 23 | static Authority? fromJson(Map json) { 24 | if (json['name'] != null) { 25 | return Authority(name: json['name']); 26 | } 27 | return null; 28 | } 29 | 30 | static Authority? fromJsonString(String json) => fromJson(jsonDecode(json)); 31 | 32 | static List fromJsonList(List json) => json.map((value) => Authority.fromJson(value)?.name).toList(); 33 | 34 | static List fromJsonStringList(String json) => fromJsonList(jsonDecode(json)); 35 | 36 | Map? toJson() { 37 | return { 38 | 'name': name, 39 | }; 40 | } 41 | 42 | @override 43 | List get props => [name]; 44 | 45 | @override 46 | String toString() { 47 | return 'Authority($name)'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/data/models/change_password.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dart_json_mapper/dart_json_mapper.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | 6 | @jsonSerializable 7 | class PasswordChangeDTO extends Equatable { 8 | @JsonProperty(name: 'currentPassword') 9 | final String? currentPassword; 10 | 11 | @JsonProperty(name: 'newPassword') 12 | final String? newPassword; 13 | 14 | const PasswordChangeDTO({ 15 | this.currentPassword, 16 | this.newPassword, 17 | }); 18 | 19 | PasswordChangeDTO copyWith({ 20 | String? currentPassword, 21 | String? newPassword, 22 | }) { 23 | return PasswordChangeDTO( 24 | currentPassword: currentPassword ?? this.currentPassword, 25 | newPassword: newPassword ?? this.newPassword, 26 | ); 27 | } 28 | 29 | static PasswordChangeDTO? fromJson(Map json) { 30 | var result = JsonMapper.fromMap(json); 31 | if (result == null) { 32 | return null; 33 | } 34 | return result; 35 | } 36 | 37 | static PasswordChangeDTO? fromJsonString(String json) { 38 | var result = JsonMapper.deserialize(jsonDecode(json)); 39 | if (result == null) { 40 | return null; 41 | } 42 | return result; 43 | } 44 | 45 | Map? toJson() => JsonMapper.toMap(this); 46 | 47 | @override 48 | List get props => [currentPassword, newPassword]; 49 | 50 | @override 51 | bool get stringify => true; 52 | } 53 | -------------------------------------------------------------------------------- /lib/data/models/city.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dart_json_mapper/dart_json_mapper.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | 6 | @jsonSerializable 7 | class City extends Equatable { 8 | @JsonProperty(name: 'id') 9 | final String? id; 10 | 11 | @JsonProperty(name: 'name') 12 | final String? name; 13 | 14 | @JsonProperty(name: 'plateCode') 15 | final String? plateCode; 16 | 17 | const City({ 18 | this.id, 19 | this.name, 20 | this.plateCode, 21 | }); 22 | 23 | City copyWith({ 24 | String? id, 25 | String? name, 26 | String? plateCode, 27 | }) { 28 | return City( 29 | id: id ?? this.id, 30 | name: name ?? this.name, 31 | plateCode: plateCode ?? this.plateCode, 32 | ); 33 | } 34 | 35 | static City? fromJson(Map json) { 36 | return const City().copyWith( 37 | id: json['id'], 38 | name: json['name'], 39 | plateCode: json['plateCode'], 40 | ); 41 | } 42 | 43 | static City? fromJsonString(String json) { 44 | return fromJson(jsonDecode(json)); 45 | } 46 | 47 | static List fromJsonList(List json) { 48 | return json.map((value) => City.fromJson(value)).toList(); 49 | } 50 | 51 | static List fromJsonStringList(String json) { 52 | return fromJsonList(jsonDecode(json)); 53 | } 54 | 55 | Map? toJson() { 56 | final Map json = {}; 57 | if (id != null) { 58 | json['id'] = id; 59 | } 60 | if (name != null) { 61 | json['name'] = name; 62 | } 63 | if (plateCode != null) { 64 | json['plateCode'] = plateCode; 65 | } 66 | return json; 67 | } 68 | 69 | @override 70 | List get props => [ 71 | id, 72 | name, 73 | plateCode, 74 | ]; 75 | 76 | @override 77 | bool get stringify => true; 78 | } 79 | -------------------------------------------------------------------------------- /lib/data/models/district.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dart_json_mapper/dart_json_mapper.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | 6 | @jsonSerializable 7 | class District extends Equatable { 8 | @JsonProperty(name: 'id') 9 | final String? id; 10 | 11 | @JsonProperty(name: 'name') 12 | final String? name; 13 | 14 | @JsonProperty(name: 'code') 15 | final String? code; 16 | 17 | const District({ 18 | this.id, 19 | this.name, 20 | this.code, 21 | }); 22 | 23 | District copyWith({ 24 | String? id, 25 | String? name, 26 | String? code, 27 | }) { 28 | return District( 29 | id: id ?? this.id, 30 | name: name ?? this.name, 31 | code: code ?? this.code, 32 | ); 33 | } 34 | 35 | static District? fromJson(Map json) { 36 | return const District().copyWith( 37 | id: json['id'], 38 | name: json['name'], 39 | code: json['code'], 40 | ); 41 | } 42 | 43 | static District? fromJsonString(String json) => fromJson(jsonDecode(json)); 44 | 45 | static List fromJsonList(List json) => json.map((value) => District.fromJson(value)).toList(); 46 | 47 | static List fromJsonStringList(String json) => fromJsonList(jsonDecode(json)); 48 | 49 | Map? toJson() { 50 | Map json = {}; 51 | if (id != null) { 52 | json['id'] = id; 53 | } 54 | if (name != null) { 55 | json['name'] = name; 56 | } 57 | if (code != null) { 58 | json['code'] = code; 59 | } 60 | return json; 61 | } 62 | 63 | @override 64 | List get props => [ 65 | id, 66 | name, 67 | code, 68 | ]; 69 | 70 | @override 71 | bool get stringify => true; 72 | } 73 | -------------------------------------------------------------------------------- /lib/data/models/jwt_token.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dart_json_mapper/dart_json_mapper.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | 6 | @jsonSerializable 7 | class JWTToken extends Equatable { 8 | @JsonProperty(name: 'id_token') 9 | final String? idToken; 10 | 11 | const JWTToken({ 12 | this.idToken, 13 | }); 14 | 15 | JWTToken copyWith({ 16 | String? idToken, 17 | }) { 18 | return JWTToken( 19 | idToken: idToken ?? this.idToken, 20 | ); 21 | } 22 | 23 | static JWTToken? fromJson(Map json) { 24 | var result = JsonMapper.fromMap(json); 25 | if (result == null) { 26 | return null; 27 | } 28 | return result; 29 | } 30 | 31 | static JWTToken? fromJsonString(String json) { 32 | var result = JsonMapper.deserialize(jsonDecode(json)); 33 | if (result == null) { 34 | return null; 35 | } 36 | return result; 37 | } 38 | 39 | Map? toJson() => JsonMapper.toMap(this); 40 | 41 | @override 42 | bool operator ==(Object other) => identical(this, other) || other is JWTToken && runtimeType == other.runtimeType && idToken == other.idToken; 43 | 44 | @override 45 | int get hashCode => idToken.hashCode; 46 | 47 | @override 48 | List get props => [ 49 | idToken, 50 | ]; 51 | 52 | @override 53 | bool get stringify => true; 54 | } 55 | -------------------------------------------------------------------------------- /lib/data/models/send_otp_request.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_json_mapper/dart_json_mapper.dart'; 2 | 3 | @JsonSerializable() 4 | class SendOtpRequest { 5 | final String email; 6 | 7 | SendOtpRequest({required this.email}); 8 | 9 | Map toJson() => {"email": email}; 10 | 11 | static SendOtpRequest? fromJson(Map json) { 12 | try { 13 | if (!json.containsKey('email')) return null; 14 | return JsonMapper.fromMap(json); 15 | } catch (e) { 16 | return null; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/data/models/user_jwt.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dart_json_mapper/dart_json_mapper.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | 6 | @jsonSerializable 7 | class UserJWT extends Equatable { 8 | @JsonProperty(name: 'username') 9 | final String? username; 10 | 11 | @JsonProperty(name: 'password') 12 | final String? password; 13 | 14 | const UserJWT(this.username, this.password); 15 | 16 | UserJWT copyWith({ 17 | String? username, 18 | String? password, 19 | }) { 20 | return UserJWT( 21 | username ?? this.username, 22 | password ?? this.password, 23 | ); 24 | } 25 | 26 | static UserJWT? fromJson(Map json) { 27 | return JsonMapper.fromMap(json); 28 | } 29 | 30 | static UserJWT? fromJsonString(String json) { 31 | return JsonMapper.deserialize(jsonDecode(json)); 32 | } 33 | 34 | Map? toJson() => JsonMapper.toMap(this); 35 | 36 | @override 37 | List get props => [username, password]; 38 | 39 | @override 40 | bool get stringify => true; 41 | } 42 | -------------------------------------------------------------------------------- /lib/data/models/verify_otp_request.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_json_mapper/dart_json_mapper.dart'; 2 | 3 | @JsonSerializable() 4 | class VerifyOtpRequest { 5 | final String email; 6 | final String otp; 7 | 8 | VerifyOtpRequest({required this.email, required this.otp}); 9 | 10 | Map toJson() => {"email": email, "otp": otp}; 11 | 12 | static VerifyOtpRequest? fromJson(Map json) { 13 | try { 14 | if (!json.containsKey('email') || !json.containsKey('otp')) return null; 15 | return JsonMapper.fromMap(json); 16 | } catch (e) { 17 | return null; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/data/repository/authority_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 2 | import 'package:flutter_bloc_advance/data/app_api_exception.dart'; 3 | import 'package:flutter_bloc_advance/data/http_utils.dart'; 4 | import 'package:flutter_bloc_advance/data/models/authority.dart'; 5 | 6 | class AuthorityRepository { 7 | static final _log = AppLogger.getLogger("AuthorityRepository"); 8 | 9 | AuthorityRepository(); 10 | 11 | final String _resource = "authorities"; 12 | 13 | Future create(Authority authority) async { 14 | _log.debug("BEGIN:createAuthority repository start : {}", [authority.toString()]); 15 | if (authority.name == null || authority.name!.isEmpty) { 16 | throw BadRequestException("Authority name null"); 17 | } 18 | final httpResponse = await HttpUtils.postRequest("/$_resource", authority); 19 | final response = Authority.fromJsonString(httpResponse.body); 20 | _log.debug("END:createAuthority successful"); 21 | return response; 22 | } 23 | 24 | Future> list() async { 25 | _log.debug("BEGIN:getAuthorities repository start"); 26 | final queryParams = {"sort": "&sort=name"}; 27 | final httpResponse = await HttpUtils.getRequest("/$_resource", queryParams: queryParams); 28 | final response = Authority.fromJsonStringList(httpResponse.body); 29 | _log.debug("END:getAuthorities successful - response list size: {}", [response.length]); 30 | return response; 31 | } 32 | 33 | Future retrieve(String id) async { 34 | _log.debug("BEGIN:getAuthority repository start - id: {}", [id]); 35 | if (id.isEmpty) { 36 | throw BadRequestException("Authority id null"); 37 | } 38 | final pathParams = id; 39 | final httpResponse = await HttpUtils.getRequest("/$_resource", pathParams: pathParams); 40 | final response = Authority.fromJsonString(httpResponse.body); 41 | _log.debug("END:getAuthority successful - response.body: {}", [response.toString()]); 42 | return response; 43 | } 44 | 45 | Future delete(String id) async { 46 | _log.debug("BEGIN:deleteAuthority repository start - id: {}", [id]); 47 | if (id.isEmpty) { 48 | throw BadRequestException("Authority id null"); 49 | } 50 | final pathParams = id; 51 | final httpResponse = await HttpUtils.deleteRequest("/$_resource", pathParams: pathParams); 52 | _log.debug("END:deleteAuthority successful - response status code: {}", [httpResponse.statusCode]); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/data/repository/city_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 2 | import 'package:flutter_bloc_advance/data/app_api_exception.dart'; 3 | 4 | import '../http_utils.dart'; 5 | import '../models/city.dart'; 6 | 7 | class CityRepository { 8 | static final _log = AppLogger.getLogger("CityRepository"); 9 | 10 | CityRepository(); 11 | 12 | final String _resource = "cities"; 13 | 14 | Future create(City city) async { 15 | _log.debug("BEGIN:createCity repository start : {}", [city.toString()]); 16 | if (city.name == null || city.name!.isEmpty) { 17 | throw BadRequestException("City name null"); 18 | } 19 | final httpResponse = await HttpUtils.postRequest("/$_resource", city); 20 | var response = City.fromJsonString(httpResponse.body); 21 | _log.debug("END:createCity successful"); 22 | return response; 23 | } 24 | 25 | Future> list({int page = 0, int size = 10, List sort = const ["id,desc"]}) async { 26 | _log.debug("BEGIN:getCities repository start - page: {}, size: {}, sort: {}", [page, size, sort]); 27 | final queryParams = {"page": page.toString(), "size": size.toString(), "sort": sort.join("&sort=")}; 28 | final httpResponse = await HttpUtils.getRequest("/$_resource", queryParams: queryParams); 29 | var response = City.fromJsonStringList(httpResponse.body); 30 | _log.debug("END:getCities successful - response list size: {}", [response.length]); 31 | return response; 32 | } 33 | 34 | Future retrieve(String id) async { 35 | _log.debug("BEGIN:getCity repository start - id: {}", [id]); 36 | if (id.isEmpty) { 37 | throw BadRequestException("City id null"); 38 | } 39 | final pathParams = id; 40 | final httpResponse = await HttpUtils.getRequest("/$_resource", pathParams: pathParams); 41 | var response = City.fromJsonString(httpResponse.body); 42 | _log.debug("END:getCity successful - response.body: {}", [response.toString()]); 43 | return response; 44 | } 45 | 46 | Future delete(String id) async { 47 | _log.debug("BEGIN:deleteCity repository start - id: {}", [id]); 48 | if (id.isEmpty) { 49 | throw BadRequestException("City id null"); 50 | } 51 | final pathParams = id; 52 | final httpResponse = await HttpUtils.deleteRequest("/$_resource", pathParams: pathParams); 53 | _log.debug("END:deleteCity successful - response status code: {}", [httpResponse.statusCode]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/data/repository/customer_repository.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/data/repository/customer_repository.dart -------------------------------------------------------------------------------- /lib/data/repository/menu_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 3 | 4 | import '../models/menu.dart'; 5 | 6 | class MenuRepository { 7 | static final _log = AppLogger.getLogger("MenuRepository"); 8 | 9 | MenuRepository(); 10 | 11 | Future> list() async { 12 | _log.debug("BEGIN:getMenus repository start"); 13 | final json = await rootBundle.loadString('assets/mock/menus.json'); 14 | final result = Menu.fromJsonStringList(json); 15 | _log.debug("END:getMenus successful - response.body: {}", [result.toString()]); 16 | return result; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/main/app_stateless_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/foundation.dart' show kIsWeb; 5 | import 'package:flutter/material.dart'; 6 | 7 | /// App stateless widget 8 | /// This class is used to create a stateless widget that can be used in the app. 9 | /// This widget is used to create a multi-platform widget that can be used in Android, iOS, and Web. 10 | /// 11 | /// Example: 12 | /// ```dart 13 | /// class PlatformButton extends PlatformWidget { 14 | /// final String text; 15 | /// final VoidCallback onPressed; 16 | /// final bool isLoading; 17 | /// 18 | /// const PlatformButton({ 19 | /// super.key, 20 | /// required this.text, 21 | /// required this.onPressed, 22 | /// this.isLoading = false, 23 | /// }); 24 | /// 25 | /// @override 26 | /// Widget buildCupertinoWidget(BuildContext context) { 27 | /// return CupertinoButton( 28 | /// onPressed: isLoading ? null : onPressed, 29 | /// child: isLoading 30 | /// ? const CupertinoActivityIndicator() 31 | /// : Text(text), 32 | /// ); 33 | /// } 34 | /// 35 | /// @override 36 | /// Widget buildMaterialWidget(BuildContext context) { 37 | /// return ElevatedButton( 38 | /// onPressed: isLoading ? null : onPressed, 39 | /// child: isLoading 40 | /// ? const CircularProgressIndicator() 41 | /// : Text(text), 42 | /// ); 43 | /// } 44 | /// 45 | /// @override 46 | /// Widget buildWebWidget(BuildContext context) { 47 | /// // Custom web widgets 48 | /// return buildMaterialWidget(context); 49 | /// } 50 | /// } 51 | abstract class AppStatelessWidget extends StatelessWidget { 52 | const AppStatelessWidget({super.key}); 53 | 54 | Widget buildCupertinoWidget(BuildContext context); 55 | 56 | Widget buildMaterialWidget(BuildContext context); 57 | 58 | Widget buildWebWidget(BuildContext context); 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | if (kIsWeb) { 63 | return buildWebWidget(context); 64 | } 65 | if (Platform.isIOS) { 66 | return buildCupertinoWidget(context); 67 | } 68 | return buildMaterialWidget(context); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/main/initialize_reflectable.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/main/initialize_reflectable.dart -------------------------------------------------------------------------------- /lib/main/main_local.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_theme/adaptive_theme.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 5 | import 'package:flutter_bloc_advance/configuration/environment.dart'; 6 | import 'package:flutter_bloc_advance/configuration/local_storage.dart'; 7 | import 'package:flutter_bloc_advance/routes/app_router.dart'; 8 | 9 | import 'app.dart'; 10 | import 'main_local.mapper.g.dart' show initializeJsonMapper; 11 | 12 | /// IMPORTANT!! run this command to generate main_prod.mapper.g.dart 13 | // dart run build_runner build --delete-conflicting-outputs 14 | // flutter pub run intl_utils:generate 15 | /// main entry point of local computer development 16 | void main() async { 17 | // first configure the logger 18 | AppLogger.configure(isProduction: false); 19 | final log = AppLogger.getLogger("main_local.dart"); 20 | 21 | ProfileConstants.setEnvironment(Environment.dev); 22 | 23 | log.info("Starting App with env: {}", [Environment.dev.name]); 24 | 25 | initializeJsonMapper(); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | 28 | //TODO change to the system language(browser language) 29 | const defaultLanguage = "en"; 30 | AppLocalStorage().setStorage(StorageType.sharedPreferences); 31 | await AppLocalStorage().save(StorageKeys.language.name, defaultLanguage); 32 | 33 | AppRouter().setRouter(RouterType.goRouter); 34 | 35 | WidgetsFlutterBinding.ensureInitialized(); 36 | const initialTheme = AdaptiveThemeMode.dark; 37 | SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then((_) { 38 | runApp(const App(language: defaultLanguage, initialTheme: initialTheme)); 39 | }); 40 | 41 | //TODO change to the system theme(browser theme) 42 | final defaultThemeName = initialTheme.name; 43 | await AppLocalStorage().save(StorageKeys.theme.name, defaultThemeName); 44 | 45 | log.info("Started App with local environment language: {} and theme: {}", [defaultLanguage, defaultThemeName]); 46 | } 47 | -------------------------------------------------------------------------------- /lib/main/main_prod.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_theme/adaptive_theme.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 5 | import 'package:flutter_bloc_advance/configuration/environment.dart'; 6 | import 'package:flutter_bloc_advance/configuration/local_storage.dart'; 7 | import 'package:flutter_bloc_advance/routes/app_router.dart'; 8 | 9 | import 'app.dart'; 10 | import 'main_prod.mapper.g.dart' show initializeJsonMapper; 11 | 12 | /// IMPORTANT!! run this command to generate main_prod.mapper.g.dart 13 | // dart run build_runner build --delete-conflicting-outputs 14 | // flutter pub run intl_utils:generate 15 | /// main entry point of PRODUCTION 16 | void main() async { 17 | // first configure the logger 18 | AppLogger.configure(isProduction: false); 19 | final log = AppLogger.getLogger("main_prod.dart"); 20 | 21 | ProfileConstants.setEnvironment(Environment.prod); 22 | 23 | log.info("Starting App with env: {}", [Environment.prod.name]); 24 | 25 | initializeJsonMapper(); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | 28 | const defaultLanguage = "en"; 29 | AppLocalStorage().setStorage(StorageType.sharedPreferences); 30 | await AppLocalStorage().save(StorageKeys.language.name, defaultLanguage); 31 | 32 | AppRouter().setRouter(RouterType.goRouter); 33 | 34 | WidgetsFlutterBinding.ensureInitialized(); 35 | const initialTheme = AdaptiveThemeMode.dark; 36 | SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then((_) { 37 | runApp(const App(language: defaultLanguage, initialTheme: initialTheme)); 38 | }); 39 | 40 | //TODO change to the system theme(browser theme) 41 | final defaultThemeName = initialTheme.name; 42 | await AppLocalStorage().save(StorageKeys.theme.name, defaultThemeName); 43 | 44 | log.info("Started App with local environment language: {} and theme: {}", [defaultLanguage, defaultThemeName]); 45 | } 46 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/account/account.dart: -------------------------------------------------------------------------------- 1 | // Account bloc barrel file 2 | export 'account_bloc.dart'; 3 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/account/account_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 6 | 7 | import '../../../data/models/user.dart'; 8 | import '../../../data/repository/account_repository.dart'; 9 | 10 | part 'account_event.dart'; 11 | part 'account_state.dart'; 12 | 13 | /// Bloc responsible for managing the account. 14 | /// It is used to load, update and delete the account. 15 | class AccountBloc extends Bloc { 16 | static final _log = AppLogger.getLogger("AccountBloc"); 17 | final AccountRepository _repository; 18 | 19 | AccountBloc({required AccountRepository repository}) 20 | : _repository = repository, 21 | super(const AccountState()) { 22 | on((event, emit) {}); 23 | on(_onFetchAccount); 24 | on(_onSubmit); 25 | } 26 | 27 | /// Load the current account. 28 | FutureOr _onFetchAccount(AccountFetchEvent event, Emitter emit) async { 29 | _log.debug("BEGIN: getAccount bloc: _onLoad"); 30 | emit(state.copyWith(status: AccountStatus.loading)); 31 | 32 | try { 33 | User user = await _repository.getAccount(); 34 | emit(state.copyWith(data: user, status: AccountStatus.success)); 35 | _log.debug("END: getAccount bloc: _onLoad success: {}", [user.toString()]); 36 | } catch (e) { 37 | emit(state.copyWith(status: AccountStatus.failure)); 38 | _log.error("END: getAccount bloc: _onLoad error: {}", [e.toString()]); 39 | } 40 | } 41 | 42 | FutureOr _onSubmit(AccountSubmitEvent event, Emitter emit) async { 43 | _log.debug("BEGIN: onSubmit AccountSubmitEvent event: {}", [event.data.toString()]); 44 | emit(state.copyWith(status: AccountStatus.loading)); 45 | try { 46 | final user = await _repository.update(event.data); 47 | emit(state.copyWith(status: AccountStatus.success, data: user)); 48 | _log.debug("END:onSubmitAccountSubmitEvent event success: {}", [user.toString()]); 49 | } catch (e) { 50 | emit(state.copyWith(status: AccountStatus.failure)); 51 | _log.error("END:onSubmit AccountSubmitEvent event error: {}", [e.toString()]); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/account/account_event.dart: -------------------------------------------------------------------------------- 1 | part of 'account_bloc.dart'; 2 | 3 | class AccountEvent extends Equatable { 4 | const AccountEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class AccountFetchEvent extends AccountEvent { 11 | const AccountFetchEvent(); 12 | 13 | @override 14 | List get props => []; 15 | } 16 | 17 | class AccountSubmitEvent extends AccountEvent { 18 | final User data; 19 | 20 | const AccountSubmitEvent(this.data); 21 | 22 | @override 23 | List get props => [data]; 24 | } 25 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/account/account_state.dart: -------------------------------------------------------------------------------- 1 | part of 'account_bloc.dart'; 2 | 3 | /// Account status used the success or failure of the account loading. 4 | enum AccountStatus { initial, loading, success, failure } 5 | 6 | /// Account state that contains the current account and the status of the account. 7 | /// The status is used to display the loading indicator. 8 | /// 9 | /// The state is immutable and copyWith is used to update the state. 10 | class AccountState extends Equatable { 11 | final User? data; 12 | final AccountStatus status; 13 | 14 | const AccountState({ 15 | this.data, 16 | this.status = AccountStatus.initial, 17 | }); 18 | 19 | AccountState copyWith({ 20 | User? data, 21 | AccountStatus? status, 22 | }) { 23 | return AccountState(status: status ?? this.status, data: data ?? this.data); 24 | } 25 | 26 | @override 27 | List get props => [status, data ?? '']; 28 | 29 | @override 30 | bool get stringify => true; 31 | } 32 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/authority/authority.dart: -------------------------------------------------------------------------------- 1 | // Account bloc barrel file 2 | export 'authority_bloc.dart'; 3 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/authority/authority_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 6 | 7 | import '../../../data/repository/authority_repository.dart'; 8 | 9 | part 'authority_event.dart'; 10 | part 'authority_state.dart'; 11 | 12 | /// Bloc responsible for managing the authority. 13 | /// It is used to load, update and delete the authority. 14 | class AuthorityBloc extends Bloc { 15 | static final _log = AppLogger.getLogger("AuthorityBloc"); 16 | final AuthorityRepository _repository; 17 | 18 | AuthorityBloc({required AuthorityRepository repository}) 19 | : _repository = repository, 20 | super(const AuthorityInitialState()) { 21 | on((event, emit) {}); 22 | on(_onLoad); 23 | } 24 | 25 | /// Load the current authority. 26 | FutureOr _onLoad(AuthorityLoad event, Emitter emit) async { 27 | _log.debug("BEGIN: getAuthorities bloc: _onLoad"); 28 | emit(const AuthorityLoadingState()); 29 | try { 30 | final authorities = await _repository.list(); 31 | if (authorities.isEmpty) { 32 | emit(const AuthorityLoadFailureState(message: "No authorities found")); 33 | _log.error("END: getAuthorities bloc: _onLoad error: {}", ["No authorities found"]); 34 | return; 35 | } 36 | emit(AuthorityLoadSuccessState(authorities: authorities)); 37 | _log.debug("END: getAuthorities bloc: _onLoad success: {}", [authorities.toString()]); 38 | } catch (e) { 39 | emit(AuthorityLoadFailureState(message: e.toString())); 40 | _log.error("END: getAuthorities bloc: _onLoad error: {}", [e.toString()]); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/authority/authority_event.dart: -------------------------------------------------------------------------------- 1 | part of 'authority_bloc.dart'; 2 | 3 | class AuthorityEvent extends Equatable { 4 | const AuthorityEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class AuthorityLoad extends AuthorityEvent { 11 | const AuthorityLoad(); 12 | 13 | @override 14 | List get props => []; 15 | } 16 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/authority/authority_state.dart: -------------------------------------------------------------------------------- 1 | part of 'authority_bloc.dart'; 2 | 3 | /// Authority status used the success or failure of the authority loading. 4 | enum AuthorityStatus { initial, loading, success, failure } 5 | 6 | /// Authority state that contains the current authority and the status of the authority. 7 | /// The status is used to display the loading indicator. 8 | /// 9 | /// The state is immutable and copyWith is used to update the state. 10 | class AuthorityState extends Equatable { 11 | final List authorities; 12 | final AuthorityStatus status; 13 | 14 | const AuthorityState({ 15 | this.authorities = const [], 16 | this.status = AuthorityStatus.initial, 17 | }); 18 | 19 | AuthorityState copyWith({ 20 | List? authorities, 21 | AuthorityStatus? status, 22 | }) { 23 | return AuthorityState( 24 | status: status ?? this.status, 25 | authorities: authorities ?? this.authorities, 26 | ); 27 | } 28 | 29 | @override 30 | List get props => [status, authorities]; 31 | 32 | @override 33 | bool get stringify => true; 34 | } 35 | 36 | class AuthorityInitialState extends AuthorityState { 37 | const AuthorityInitialState() : super(status: AuthorityStatus.initial); 38 | } 39 | 40 | class AuthorityLoadingState extends AuthorityState { 41 | const AuthorityLoadingState() : super(status: AuthorityStatus.loading); 42 | } 43 | 44 | class AuthorityLoadSuccessState extends AuthorityState { 45 | const AuthorityLoadSuccessState({required super.authorities}) : super(status: AuthorityStatus.success); 46 | } 47 | 48 | class AuthorityLoadFailureState extends AuthorityState { 49 | final String message; 50 | 51 | const AuthorityLoadFailureState({required this.message}) : super(status: AuthorityStatus.failure); 52 | 53 | @override 54 | List get props => [status, message]; 55 | } 56 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/city/city.dart: -------------------------------------------------------------------------------- 1 | // Account bloc barrel file 2 | export 'city_bloc.dart'; 3 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/city/city_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 6 | 7 | import '../../../data/models/city.dart'; 8 | import '../../../data/repository/city_repository.dart'; 9 | 10 | part 'city_event.dart'; 11 | part 'city_state.dart'; 12 | 13 | /// Bloc responsible for managing the city. 14 | class CityBloc extends Bloc { 15 | static final _log = AppLogger.getLogger("CityBloc"); 16 | final CityRepository _repository; 17 | 18 | CityBloc({required CityRepository repository}) 19 | : _repository = repository, 20 | super(const CityInitialState()) { 21 | on((event, emit) {}); 22 | on(_onLoad); 23 | } 24 | 25 | FutureOr _onLoad(CityLoad event, Emitter emit) async { 26 | _log.debug("BEGIN: getCity bloc: _onLoad"); 27 | emit(const CityLoadingState()); 28 | try { 29 | List cities = await _repository.list(); 30 | emit(CityLoadSuccessState(cities: cities)); 31 | _log.debug("END: getCity bloc: _onLoad success: {}", [cities.toString()]); 32 | } catch (e) { 33 | emit(CityLoadFailureState(message: e.toString())); 34 | _log.error("END: getCity bloc: _onLoad error: {}", [e.toString()]); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/city/city_event.dart: -------------------------------------------------------------------------------- 1 | part of 'city_bloc.dart'; 2 | 3 | class CityEvent extends Equatable { 4 | const CityEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class CityLoad extends CityEvent { 11 | const CityLoad(); 12 | 13 | @override 14 | List get props => []; 15 | } 16 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/city/city_state.dart: -------------------------------------------------------------------------------- 1 | part of 'city_bloc.dart'; 2 | 3 | /// City status used the success or failure of the authority loading. 4 | enum CityStatus { initial, loading, success, failure } 5 | 6 | /// City state that contains the current authority and the status of the authority. 7 | /// The status is used to display the loading indicator. 8 | /// 9 | /// The state is immutable and copyWith is used to update the state. 10 | class CityState extends Equatable { 11 | final List? cities; 12 | final CityStatus status; 13 | 14 | const CityState({ 15 | this.cities, 16 | this.status = CityStatus.initial, 17 | }); 18 | 19 | CityState copyWith({ 20 | List? cities, 21 | CityStatus? status, 22 | }) { 23 | return CityState(status: status ?? this.status, cities: cities ?? this.cities); 24 | } 25 | 26 | @override 27 | List get props => [status, cities ?? []]; 28 | 29 | @override 30 | bool get stringify => true; 31 | } 32 | 33 | class CityInitialState extends CityState { 34 | const CityInitialState() : super(status: CityStatus.initial); 35 | } 36 | 37 | class CityLoadingState extends CityState { 38 | const CityLoadingState() : super(status: CityStatus.loading); 39 | } 40 | 41 | class CityLoadSuccessState extends CityState { 42 | const CityLoadSuccessState({required List cities}) : super(cities: cities, status: CityStatus.success); 43 | } 44 | 45 | class CityLoadFailureState extends CityState { 46 | final String message; 47 | 48 | const CityLoadFailureState({required this.message}) : super(status: CityStatus.failure); 49 | 50 | @override 51 | List get props => [status, message]; 52 | } 53 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/district/district.dart: -------------------------------------------------------------------------------- 1 | // Account bloc barrel file 2 | export 'district_bloc.dart'; 3 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/district/district_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 6 | 7 | import '../../../data/models/district.dart'; 8 | import '../../../data/repository/district_repository.dart'; 9 | 10 | part 'district_event.dart'; 11 | part 'district_state.dart'; 12 | 13 | /// Bloc responsible for managing the district. 14 | class DistrictBloc extends Bloc { 15 | static final _log = AppLogger.getLogger("DistrictBloc"); 16 | final DistrictRepository _repository; 17 | 18 | DistrictBloc({required DistrictRepository repository}) 19 | : _repository = repository, 20 | super(const DistrictInitialState()) { 21 | on((event, emit) {}); 22 | on(_onLoad); 23 | on(_onLoadByCity); 24 | } 25 | 26 | FutureOr _onLoad(DistrictLoad event, Emitter emit) async { 27 | _log.debug("BEGIN: getDistrict bloc: _onLoad"); 28 | emit(const DistrictLoadingState()); 29 | try { 30 | List? district = await _repository.list(); 31 | emit(DistrictLoadSuccessState(districts: district)); 32 | _log.debug("END: getDistrict bloc: _onLoad success: {}", [district.toString()]); 33 | } catch (e) { 34 | emit(DistrictLoadFailureState(message: e.toString())); 35 | _log.error("END: getDistrict bloc: _onLoad error: {}", [e.toString()]); 36 | } 37 | } 38 | 39 | FutureOr _onLoadByCity(DistrictLoadByCity event, Emitter emit) async { 40 | _log.debug("BEGIN: getDistrict bloc: _onLoadByCity"); 41 | emit(const DistrictLoadingState()); 42 | try { 43 | List? district = await _repository.listByCity(event.cityId); 44 | emit(DistrictLoadSuccessState(districts: district)); 45 | _log.debug("END: getDistrict bloc: _onLoadByCity success: {}", [district.toString()]); 46 | } catch (e) { 47 | emit(DistrictLoadFailureState(message: e.toString())); 48 | _log.error("END: getDistrict bloc: _onLoadByCity error: {}", [e.toString()]); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/district/district_event.dart: -------------------------------------------------------------------------------- 1 | part of 'district_bloc.dart'; 2 | 3 | class DistrictEvent extends Equatable { 4 | const DistrictEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class DistrictLoadByCity extends DistrictEvent { 11 | final String cityId; 12 | 13 | const DistrictLoadByCity({required this.cityId}); 14 | 15 | @override 16 | List get props => [cityId]; 17 | } 18 | 19 | class DistrictLoad extends DistrictEvent { 20 | const DistrictLoad(); 21 | 22 | @override 23 | List get props => []; 24 | } 25 | -------------------------------------------------------------------------------- /lib/presentation/common_blocs/district/district_state.dart: -------------------------------------------------------------------------------- 1 | part of 'district_bloc.dart'; 2 | 3 | /// District status used the success or failure of the authority loading. 4 | enum DistrictStatus { initial, loading, success, failure } 5 | 6 | /// District state that contains the current authority and the status of the authority. 7 | /// The status is used to display the loading indicator. 8 | /// 9 | /// The state is immutable and copyWith is used to update the state. 10 | class DistrictState extends Equatable { 11 | final List? districts; 12 | final DistrictStatus status; 13 | 14 | const DistrictState({ 15 | this.districts, 16 | this.status = DistrictStatus.initial, 17 | }); 18 | 19 | DistrictState copyWith({ 20 | List? districts, 21 | DistrictStatus? status, 22 | }) { 23 | return DistrictState(status: status ?? this.status, districts: districts ?? districts); 24 | } 25 | 26 | @override 27 | List get props => [status, districts ?? []]; 28 | 29 | @override 30 | bool get stringify => true; 31 | } 32 | 33 | class DistrictInitialState extends DistrictState { 34 | const DistrictInitialState() : super(status: DistrictStatus.initial); 35 | } 36 | 37 | class DistrictLoadingState extends DistrictState { 38 | const DistrictLoadingState() : super(status: DistrictStatus.loading); 39 | } 40 | 41 | class DistrictLoadSuccessState extends DistrictState { 42 | const DistrictLoadSuccessState({required super.districts}) : super(status: DistrictStatus.success); 43 | } 44 | 45 | class DistrictLoadFailureState extends DistrictState { 46 | final String message; 47 | 48 | const DistrictLoadFailureState({required this.message}) : super(status: DistrictStatus.failure); 49 | 50 | @override 51 | List get props => [status, message]; 52 | } 53 | -------------------------------------------------------------------------------- /lib/presentation/common_widgets/drawer/drawer_bloc/drawer.dart: -------------------------------------------------------------------------------- 1 | export 'drawer_bloc.dart'; 2 | -------------------------------------------------------------------------------- /lib/presentation/common_widgets/drawer/drawer_bloc/drawer_event.dart: -------------------------------------------------------------------------------- 1 | part of 'drawer_bloc.dart'; 2 | 3 | abstract class DrawerEvent extends Equatable { 4 | const DrawerEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class Logout extends DrawerEvent {} 11 | 12 | class LoadMenus extends DrawerEvent { 13 | final String language; 14 | final AdaptiveThemeMode theme; 15 | 16 | const LoadMenus({required this.language, required this.theme}); 17 | 18 | @override 19 | List get props => [language, theme]; 20 | } 21 | 22 | class RefreshMenus extends DrawerEvent {} 23 | 24 | class ChangeLanguageEvent extends DrawerEvent { 25 | final String language; 26 | 27 | const ChangeLanguageEvent({required this.language}); 28 | 29 | @override 30 | List get props => [language]; 31 | } 32 | 33 | class ChangeThemeEvent extends DrawerEvent { 34 | final AdaptiveThemeMode theme; 35 | 36 | const ChangeThemeEvent({required this.theme}); 37 | 38 | @override 39 | List get props => [theme]; 40 | } 41 | -------------------------------------------------------------------------------- /lib/presentation/common_widgets/drawer/drawer_bloc/drawer_state.dart: -------------------------------------------------------------------------------- 1 | part of 'drawer_bloc.dart'; 2 | 3 | enum DrawerStateStatus { initial, loading, success, error } 4 | 5 | class DrawerState extends Equatable { 6 | final List menus; 7 | final bool isLogout; 8 | final DrawerStateStatus status; 9 | final String? language; 10 | final AdaptiveThemeMode? theme; 11 | 12 | const DrawerState({ 13 | this.menus = const [], 14 | this.isLogout = false, 15 | this.status = DrawerStateStatus.initial, 16 | this.language, 17 | this.theme, 18 | }); 19 | 20 | DrawerState copyWith({ 21 | List? menus, 22 | bool? isLogout, 23 | DrawerStateStatus? status, 24 | String? language, 25 | AdaptiveThemeMode? theme, 26 | }) { 27 | return DrawerState( 28 | menus: menus ?? this.menus, 29 | isLogout: isLogout ?? this.isLogout, 30 | status: status ?? this.status, 31 | language: language ?? this.language, 32 | theme: theme ?? this.theme, 33 | ); 34 | } 35 | 36 | @override 37 | List get props => [status, menus, isLogout]; 38 | } 39 | 40 | //TODO add default language and theme 41 | class DrawerStateInitial extends DrawerState { 42 | const DrawerStateInitial() : super(status: DrawerStateStatus.initial); 43 | } 44 | 45 | class DrawerStateLoading extends DrawerState { 46 | const DrawerStateLoading() : super(status: DrawerStateStatus.loading); 47 | } 48 | 49 | class DrawerStateLoaded extends DrawerState { 50 | const DrawerStateLoaded({required super.menus}) : super(status: DrawerStateStatus.success); 51 | } 52 | 53 | class DrawerStateError extends DrawerState { 54 | final String message; 55 | 56 | const DrawerStateError({required this.message}) : super(status: DrawerStateStatus.error); 57 | } 58 | 59 | class DrawerLanguageChanged extends DrawerState { 60 | const DrawerLanguageChanged({required super.language}) : super(status: DrawerStateStatus.success); 61 | } 62 | 63 | class DrawerThemeChanged extends DrawerState { 64 | const DrawerThemeChanged({required super.theme}) : super(status: DrawerStateStatus.success); 65 | } 66 | -------------------------------------------------------------------------------- /lib/presentation/screen/change_password/bloc/change_password.dart: -------------------------------------------------------------------------------- 1 | export 'change_password_bloc.dart'; 2 | -------------------------------------------------------------------------------- /lib/presentation/screen/change_password/bloc/change_password_event.dart: -------------------------------------------------------------------------------- 1 | part of 'change_password_bloc.dart'; 2 | 3 | abstract class ChangePasswordEvent extends Equatable { 4 | const ChangePasswordEvent(); 5 | 6 | @override 7 | List get props => []; 8 | 9 | @override 10 | bool get stringify => true; 11 | } 12 | 13 | class TogglePasswordVisibility extends ChangePasswordEvent { 14 | const TogglePasswordVisibility(); 15 | } 16 | 17 | class ChangePasswordChanged extends ChangePasswordEvent { 18 | final String currentPassword; 19 | final String newPassword; 20 | 21 | const ChangePasswordChanged({required this.currentPassword, required this.newPassword}); 22 | } 23 | -------------------------------------------------------------------------------- /lib/presentation/screen/change_password/bloc/change_password_state.dart: -------------------------------------------------------------------------------- 1 | part of 'change_password_bloc.dart'; 2 | 3 | enum ChangePasswordStatus { initial, loading, success, failure } 4 | 5 | const String authenticationFailKey = 'error.authenticate'; 6 | 7 | class ChangePasswordState extends Equatable { 8 | final ChangePasswordStatus status; 9 | 10 | const ChangePasswordState({this.status = ChangePasswordStatus.initial}); 11 | 12 | ChangePasswordState copyWith({ChangePasswordStatus? status}) { 13 | return ChangePasswordState( 14 | status: status ?? this.status, 15 | ); 16 | } 17 | 18 | @override 19 | List get props => [status]; 20 | 21 | @override 22 | bool get stringify => true; 23 | } 24 | 25 | class ChangePasswordInitialState extends ChangePasswordState { 26 | const ChangePasswordInitialState() : super(status: ChangePasswordStatus.initial); 27 | } 28 | 29 | class ChangePasswordLoadingState extends ChangePasswordState { 30 | const ChangePasswordLoadingState() : super(status: ChangePasswordStatus.loading); 31 | } 32 | 33 | class ChangePasswordCompletedState extends ChangePasswordState { 34 | const ChangePasswordCompletedState() : super(status: ChangePasswordStatus.success); 35 | } 36 | 37 | class ChangePasswordErrorState extends ChangePasswordState { 38 | final String message; 39 | 40 | const ChangePasswordErrorState({required this.message}) : super(status: ChangePasswordStatus.failure); 41 | 42 | @override 43 | List get props => [status, message]; 44 | } 45 | -------------------------------------------------------------------------------- /lib/presentation/screen/components/authorities_lov_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:flutter_bloc_advance/generated/l10n.dart'; 4 | import 'package:flutter_bloc_advance/presentation/common_blocs/authority/authority.dart'; 5 | import 'package:flutter_form_builder/flutter_form_builder.dart'; 6 | 7 | /// Dropdown widget for selecting user authorities/roles. 8 | /// Displays available authority options from AuthorityBloc. 9 | /// Updates when authorities state changes. 10 | class AuthoritiesDropdown extends StatelessWidget { 11 | final bool enabled; 12 | final String? initialValue; 13 | 14 | const AuthoritiesDropdown({super.key, this.enabled = true, this.initialValue}); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | BlocProvider.of(context).add(const AuthorityLoad()); 19 | return Padding( 20 | padding: const EdgeInsets.only(right: 10), 21 | child: BlocBuilder( 22 | builder: (context, state) { 23 | if (state is AuthorityLoadSuccessState) { 24 | final authorities = ["", ...state.authorities]; 25 | return FormBuilderDropdown( 26 | enabled: enabled, 27 | name: 'authorities', 28 | decoration: InputDecoration(hintText: S.of(context).authorities), 29 | items: authorities.map((e) => DropdownMenuItem(value: e, child: Text(e ?? ""))).toList(), 30 | initialValue: initialValue ?? authorities.first, 31 | ); 32 | } 33 | return const SizedBox.shrink(); 34 | }, 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/presentation/screen/components/editor_form_mode.dart: -------------------------------------------------------------------------------- 1 | /// Enum for the mode of the editor form 2 | /// 3 | /// The editor form can be in one of three modes: 4 | /// - create: the form is used to create a new entity 5 | /// - edit: the form is used to edit an existing entity 6 | /// - view: the form is used to view an existing entity 7 | enum EditorFormMode { create, edit, view } 8 | -------------------------------------------------------------------------------- /lib/presentation/screen/components/language_selection_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc_advance/configuration/local_storage.dart'; 3 | import 'package:flutter_bloc_advance/generated/l10n.dart'; 4 | import 'package:go_router/go_router.dart'; 5 | 6 | class LanguageSelectionDialog extends StatelessWidget { 7 | const LanguageSelectionDialog({super.key}); 8 | 9 | static Future show(BuildContext context) { 10 | return showDialog(context: context, builder: (_) => const LanguageSelectionDialog()); 11 | } 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final theme = Theme.of(context); 16 | final l10n = S.of(context); 17 | 18 | return AlertDialog( 19 | title: Text( 20 | l10n.language_select, 21 | style: theme.textTheme.titleLarge, 22 | textAlign: TextAlign.center, 23 | ), 24 | actionsAlignment: MainAxisAlignment.center, 25 | actions: [ 26 | TextButton( 27 | style: TextButton.styleFrom(backgroundColor: theme.colorScheme.primary), 28 | onPressed: () => _setLanguage(context, 'tr'), 29 | child: Text( 30 | l10n.turkish, 31 | style: theme.textTheme.labelLarge?.copyWith(color: theme.colorScheme.onPrimary), 32 | ), 33 | ), 34 | TextButton( 35 | style: TextButton.styleFrom(backgroundColor: theme.colorScheme.primary), 36 | onPressed: () => _setLanguage(context, 'en'), 37 | child: Text( 38 | l10n.english, 39 | style: theme.textTheme.labelLarge?.copyWith(color: theme.colorScheme.onPrimary), 40 | ), 41 | ), 42 | ], 43 | ); 44 | } 45 | 46 | Future _setLanguage(BuildContext context, String langCode) async { 47 | await AppLocalStorage().save(StorageKeys.language.name, langCode); 48 | await S.load(Locale(langCode)); 49 | if (context.mounted) { 50 | context.pop(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/presentation/screen/components/responsive_form_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc_advance/configuration/padding_spacing.dart'; 3 | import 'package:flutter_form_builder/flutter_form_builder.dart'; 4 | 5 | class ResponsiveFormBuilder extends StatelessWidget { 6 | final GlobalKey formKey; 7 | final List children; 8 | final MainAxisAlignment mainAxisAlignment; 9 | final CrossAxisAlignment crossAxisAlignment; 10 | final bool autoValidateMode; 11 | final VoidCallback? onChanged; 12 | final bool shrinkWrap; 13 | final Map initialValue; 14 | 15 | const ResponsiveFormBuilder({ 16 | super.key, 17 | required this.formKey, 18 | required this.children, 19 | this.mainAxisAlignment = MainAxisAlignment.start, 20 | this.crossAxisAlignment = CrossAxisAlignment.start, 21 | this.autoValidateMode = false, 22 | this.onChanged, 23 | this.shrinkWrap = false, 24 | this.initialValue = const {}, 25 | }); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return FormBuilder( 30 | key: formKey, 31 | initialValue: initialValue, 32 | autovalidateMode: autoValidateMode ? AutovalidateMode.onUserInteraction : AutovalidateMode.disabled, 33 | onChanged: onChanged, 34 | child: SingleChildScrollView( 35 | child: Center( 36 | child: ConstrainedBox( 37 | constraints: const BoxConstraints(maxWidth: Spacing.formMaxWidthLarge), 38 | child: Padding( 39 | padding: const EdgeInsets.all(Spacing.medium), 40 | child: Column( 41 | spacing: Spacing.medium, 42 | mainAxisAlignment: mainAxisAlignment, 43 | crossAxisAlignment: crossAxisAlignment, 44 | children: children, 45 | ), 46 | ), 47 | ), 48 | ), 49 | ), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/presentation/screen/components/submit_button_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc_advance/configuration/padding_spacing.dart'; 4 | 5 | class ResponsiveSubmitButton extends StatelessWidget { 6 | final String buttonText; 7 | final VoidCallback? onPressed; 8 | final bool isLoading; 9 | @visibleForTesting 10 | final bool? isWebPlatform; 11 | const ResponsiveSubmitButton({super.key, this.buttonText = 'Save', this.onPressed, this.isLoading = false, this.isWebPlatform}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final submitButton = FilledButton( 16 | onPressed: isLoading ? null : onPressed, 17 | child: ButtonContent(text: buttonText, isLoading: isLoading), 18 | ); 19 | debugPrint("isWebPlatform: $isWebPlatform kIsWeb: $kIsWeb"); 20 | if (isWebPlatform ?? kIsWeb) { 21 | return Align(alignment: Alignment.centerRight, child: submitButton); 22 | } 23 | return submitButton; 24 | } 25 | } 26 | 27 | class ButtonContent extends StatelessWidget { 28 | final String text; 29 | final bool isLoading; 30 | 31 | const ButtonContent({ 32 | super.key, 33 | required this.text, 34 | required this.isLoading, 35 | }); 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | final buttonForegroundColor = Theme.of(context).colorScheme.onPrimary; 40 | return Row( 41 | spacing: Spacing.small, 42 | mainAxisSize: MainAxisSize.min, 43 | mainAxisAlignment: MainAxisAlignment.center, 44 | children: [ 45 | if (isLoading) ...[ 46 | SizedBox( 47 | width: 16, 48 | height: 16, 49 | child: CircularProgressIndicator( 50 | strokeWidth: 2, 51 | valueColor: AlwaysStoppedAnimation(buttonForegroundColor), 52 | ), 53 | ), 54 | ], 55 | Text(text), 56 | ], 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/presentation/screen/customer/bloc/customer.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/presentation/screen/customer/bloc/customer.dart -------------------------------------------------------------------------------- /lib/presentation/screen/customer/bloc/customer_bloc.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/presentation/screen/customer/bloc/customer_bloc.dart -------------------------------------------------------------------------------- /lib/presentation/screen/customer/bloc/customer_event.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/presentation/screen/customer/bloc/customer_event.dart -------------------------------------------------------------------------------- /lib/presentation/screen/customer/bloc/customer_state.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/presentation/screen/customer/bloc/customer_state.dart -------------------------------------------------------------------------------- /lib/presentation/screen/customer/create/create_customer_screen.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/presentation/screen/customer/create/create_customer_screen.dart -------------------------------------------------------------------------------- /lib/presentation/screen/customer/edit/edit_customer_screen.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/presentation/screen/customer/edit/edit_customer_screen.dart -------------------------------------------------------------------------------- /lib/presentation/screen/customer/edit/edit_form_customer_widget.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/presentation/screen/customer/edit/edit_form_customer_widget.dart -------------------------------------------------------------------------------- /lib/presentation/screen/customer/list/list_customer_screen.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/presentation/screen/customer/list/list_customer_screen.dart -------------------------------------------------------------------------------- /lib/presentation/screen/forgot_password/bloc/forgot_password.dart: -------------------------------------------------------------------------------- 1 | export 'forgot_password_bloc.dart'; 2 | -------------------------------------------------------------------------------- /lib/presentation/screen/forgot_password/bloc/forgot_password_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:equatable/equatable.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 7 | import 'package:flutter_bloc_advance/data/app_api_exception.dart'; 8 | 9 | import '../../../../data/repository/account_repository.dart'; 10 | 11 | part 'forgot_password_event.dart'; 12 | part 'forgot_password_state.dart'; 13 | 14 | class ForgotPasswordBloc extends Bloc { 15 | static final _log = AppLogger.getLogger("ForgotPasswordBloc"); 16 | final AccountRepository _repository; 17 | 18 | ForgotPasswordBloc({required AccountRepository repository}) 19 | : _repository = repository, 20 | super(const ForgotPasswordState(status: ForgotPasswordStatus.initial)) { 21 | on(_onSubmit); 22 | } 23 | 24 | @override 25 | void onTransition(Transition transition) { 26 | super.onTransition(transition); 27 | _log.trace("current state: ${transition.currentState.toString()}"); 28 | _log.trace("event: ${transition.event.toString()}"); 29 | _log.trace("next state: ${transition.nextState.toString()}"); 30 | } 31 | 32 | FutureOr _onSubmit(ForgotPasswordEmailChanged event, Emitter emit) async { 33 | _log.debug("BEGIN: forgotPassword bloc: _onSubmit"); 34 | emit(state.copyWith(status: ForgotPasswordStatus.loading)); 35 | try { 36 | String result = event.email.replaceAll('"', ''); 37 | var resultStatusCode = await _repository.resetPassword(result); 38 | if (resultStatusCode < HttpStatus.badRequest) { 39 | emit(state.copyWith(status: ForgotPasswordStatus.success, email: result)); 40 | } else { 41 | throw BadRequestException("API Error"); 42 | } 43 | _log.debug("END: forgotPassword bloc: _onSubmit success: {}", [resultStatusCode.toString()]); 44 | } catch (e) { 45 | emit(state.copyWith(status: ForgotPasswordStatus.failure)); 46 | _log.error("END: forgotPassword bloc: _onSubmit error: {}", [e.toString()]); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/presentation/screen/forgot_password/bloc/forgot_password_event.dart: -------------------------------------------------------------------------------- 1 | part of 'forgot_password_bloc.dart'; 2 | 3 | abstract class ForgotPasswordEvent extends Equatable { 4 | const ForgotPasswordEvent(); 5 | 6 | @override 7 | bool? get stringify => true; 8 | } 9 | 10 | class ForgotPasswordEmailChanged extends ForgotPasswordEvent { 11 | final String email; 12 | 13 | const ForgotPasswordEmailChanged({ 14 | required this.email, 15 | }); 16 | 17 | @override 18 | List get props => [email]; 19 | } 20 | -------------------------------------------------------------------------------- /lib/presentation/screen/forgot_password/bloc/forgot_password_state.dart: -------------------------------------------------------------------------------- 1 | part of 'forgot_password_bloc.dart'; 2 | 3 | enum ForgotPasswordStatus { initial, loading, success, failure } 4 | 5 | const String authenticationFailKey = 'error.authenticate'; 6 | 7 | class ForgotPasswordState extends Equatable { 8 | final String? email; 9 | final ForgotPasswordStatus status; 10 | 11 | const ForgotPasswordState({ 12 | this.email, 13 | this.status = ForgotPasswordStatus.initial, 14 | }); 15 | 16 | ForgotPasswordState copyWith({ 17 | String? email, 18 | ForgotPasswordStatus? status, 19 | }) { 20 | return ForgotPasswordState( 21 | email: email ?? this.email, 22 | status: status ?? this.status, 23 | ); 24 | } 25 | 26 | @override 27 | List get props => [status, email ?? ""]; 28 | 29 | @override 30 | bool get stringify => true; 31 | } 32 | 33 | class ForgotPasswordInitialState extends ForgotPasswordState { 34 | const ForgotPasswordInitialState() : super(status: ForgotPasswordStatus.initial); 35 | } 36 | 37 | class ForgotPasswordLoadingState extends ForgotPasswordState { 38 | const ForgotPasswordLoadingState() : super(status: ForgotPasswordStatus.loading); 39 | } 40 | 41 | class ForgotPasswordCompletedState extends ForgotPasswordState { 42 | const ForgotPasswordCompletedState() : super(status: ForgotPasswordStatus.success); 43 | } 44 | 45 | class ForgotPasswordErrorState extends ForgotPasswordState { 46 | final String message; 47 | 48 | const ForgotPasswordErrorState({required this.message}) : super(status: ForgotPasswordStatus.failure); 49 | 50 | @override 51 | List get props => [status, message]; 52 | } 53 | -------------------------------------------------------------------------------- /lib/presentation/screen/login/bloc/login.dart: -------------------------------------------------------------------------------- 1 | export 'login_bloc.dart'; 2 | -------------------------------------------------------------------------------- /lib/presentation/screen/login/bloc/login_event.dart: -------------------------------------------------------------------------------- 1 | part of 'login_bloc.dart'; 2 | 3 | abstract class LoginEvent extends Equatable { 4 | const LoginEvent(); 5 | } 6 | 7 | class TogglePasswordVisibility extends LoginEvent { 8 | const TogglePasswordVisibility(); 9 | 10 | @override 11 | List get props => []; 12 | } 13 | 14 | class LoginFormSubmitted extends LoginEvent { 15 | final String username; 16 | final String password; 17 | 18 | const LoginFormSubmitted({required this.username, required this.password}); 19 | 20 | @override 21 | List get props => [username, password]; 22 | } 23 | 24 | enum LoginMethod {otp, password} 25 | 26 | class ChangeLoginMethod extends LoginEvent { 27 | final LoginMethod method; 28 | 29 | const ChangeLoginMethod({required this.method}); 30 | 31 | @override 32 | List get props => [method]; 33 | } 34 | 35 | class SendOtpRequested extends LoginEvent { 36 | final String email; 37 | 38 | const SendOtpRequested({required this.email}); 39 | 40 | @override 41 | List get props => [email]; 42 | } 43 | 44 | class VerifyOtpSubmitted extends LoginEvent { 45 | final String email; 46 | final String otpCode; 47 | 48 | const VerifyOtpSubmitted({required this.email, required this.otpCode}); 49 | 50 | @override 51 | List get props => [email, otpCode]; 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /lib/presentation/screen/register/bloc/register.dart: -------------------------------------------------------------------------------- 1 | export 'register_bloc.dart'; 2 | -------------------------------------------------------------------------------- /lib/presentation/screen/register/bloc/register_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 6 | import 'package:flutter_bloc_advance/data/app_api_exception.dart'; 7 | 8 | import '../../../../data/models/user.dart'; 9 | import '../../../../data/repository/account_repository.dart'; 10 | 11 | part 'register_event.dart'; 12 | part 'register_state.dart'; 13 | 14 | class RegisterBloc extends Bloc { 15 | static final _log = AppLogger.getLogger("RegisterBloc"); 16 | final AccountRepository _repository; 17 | 18 | RegisterBloc({required AccountRepository repository}) 19 | : _repository = repository, 20 | super(const RegisterInitialState()) { 21 | on(_onSubmit); 22 | } 23 | 24 | @override 25 | void onTransition(Transition transition) { 26 | super.onTransition(transition); 27 | _log.trace("current state: ${transition.currentState.toString()}"); 28 | _log.trace("event: ${transition.event.toString()}"); 29 | _log.trace("next state: ${transition.nextState.toString()}"); 30 | } 31 | 32 | FutureOr _onSubmit(RegisterFormSubmitted event, Emitter emit) async { 33 | _log.debug("BEGIN: onSubmit RegisterFormSubmitted event: {}", [event.data.toString()]); 34 | emit(const RegisterLoadingState()); 35 | try { 36 | var user = await _repository.register(event.data); 37 | if (user != null) { 38 | emit(RegisterCompletedState(user: user)); 39 | _log.debug("END:onSubmit RegisterFormSubmitted event success: {}", [user.toString()]); 40 | } else { 41 | throw BadRequestException("Register Error: response is null"); 42 | } 43 | } catch (e) { 44 | emit(RegisterErrorState(message: e.toString())); 45 | _log.error("END:onSubmit RegisterFormSubmitted event error: {}", [e.toString()]); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/presentation/screen/register/bloc/register_event.dart: -------------------------------------------------------------------------------- 1 | part of 'register_bloc.dart'; 2 | 3 | abstract class RegisterEvent extends Equatable { 4 | const RegisterEvent(); 5 | } 6 | 7 | class RegisterFormSubmitted extends RegisterEvent { 8 | final User data; 9 | 10 | const RegisterFormSubmitted({ 11 | required this.data, 12 | }); 13 | 14 | @override 15 | List get props => [data]; 16 | } 17 | -------------------------------------------------------------------------------- /lib/presentation/screen/register/bloc/register_state.dart: -------------------------------------------------------------------------------- 1 | part of 'register_bloc.dart'; 2 | 3 | enum RegisterStatus { initial, loading, success, error } 4 | 5 | class RegisterState extends Equatable { 6 | final User? data; 7 | final RegisterStatus status; 8 | 9 | const RegisterState({ 10 | this.data, 11 | this.status = RegisterStatus.initial, 12 | }); 13 | 14 | RegisterState copyWith({ 15 | User? data, 16 | RegisterStatus? status, 17 | }) { 18 | return RegisterState( 19 | data: data ?? this.data, 20 | status: status ?? this.status, 21 | ); 22 | } 23 | 24 | @override 25 | List get props => [status, data ?? '']; 26 | 27 | @override 28 | bool get stringify => true; 29 | } 30 | 31 | class RegisterInitialState extends RegisterState { 32 | const RegisterInitialState() : super(status: RegisterStatus.initial); 33 | } 34 | 35 | class RegisterLoadingState extends RegisterState { 36 | const RegisterLoadingState() : super(status: RegisterStatus.loading); 37 | } 38 | 39 | class RegisterCompletedState extends RegisterState { 40 | const RegisterCompletedState({required User user}) : super(data: user, status: RegisterStatus.success); 41 | } 42 | 43 | class RegisterErrorState extends RegisterState { 44 | final String message; 45 | 46 | const RegisterErrorState({required this.message}) : super(status: RegisterStatus.error); 47 | } 48 | -------------------------------------------------------------------------------- /lib/presentation/screen/settings/bloc/settings.dart: -------------------------------------------------------------------------------- 1 | // Settings bloc barrel file 2 | export 'settings_bloc.dart'; 3 | -------------------------------------------------------------------------------- /lib/presentation/screen/settings/bloc/settings_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:adaptive_theme/adaptive_theme.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 7 | 8 | part 'settings_event.dart'; 9 | part 'settings_state.dart'; 10 | 11 | /// Bloc responsible for managing the Settings. 12 | class SettingsBloc extends Bloc { 13 | static final _log = AppLogger.getLogger("SettingsBloc"); 14 | 15 | SettingsBloc() : super(const SettingsInitial()) { 16 | on(_onChangeLanguage); 17 | on(_onChangeTheme); 18 | on(_onLogout); 19 | } 20 | 21 | FutureOr _onChangeLanguage(ChangeLanguage event, Emitter emit) async { 22 | _log.debug("BEGIN: onChangeLanguage ChangeLanguage event: {}", [event.language]); 23 | emit(const SettingsLoading()); 24 | try { 25 | if (event.language == null || event.language!.isEmpty) { 26 | throw Exception("Language is null"); 27 | } else { 28 | // Change the language 29 | emit(SettingsLanguageChanged(language: event.language)); 30 | _log.debug("END:onChangeLanguage ChangeLanguage event success"); 31 | } 32 | } catch (e) { 33 | emit(const SettingsFailure(message: "Change Language Error")); 34 | _log.error("END:onChangeLanguage ChangeLanguage event failure: {}", ["Change Language Error"]); 35 | } 36 | } 37 | 38 | FutureOr _onChangeTheme(ChangeTheme event, Emitter emit) async { 39 | _log.debug("BEGIN: onChangeTheme ChangeTheme event: {}", [event.theme.name]); 40 | emit(const SettingsLoading()); 41 | 42 | // Change the theme 43 | emit(SettingsThemeChanged(theme: event.theme)); 44 | _log.debug("END:onChangeTheme ChangeTheme event success"); 45 | } 46 | 47 | FutureOr _onLogout(Logout event, Emitter emit) async { 48 | _log.debug("BEGIN: onLogout Logout event: {}", []); 49 | emit(const SettingsLoading()); 50 | 51 | emit(const SettingsLogoutSuccess()); 52 | _log.debug("END:onLogout Logout event success: {}", []); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/presentation/screen/settings/bloc/settings_event.dart: -------------------------------------------------------------------------------- 1 | part of 'settings_bloc.dart'; 2 | 3 | class SettingsEvent extends Equatable { 4 | const SettingsEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class Logout extends SettingsEvent {} 11 | 12 | class ChangeLanguage extends SettingsEvent { 13 | final String? language; 14 | 15 | const ChangeLanguage({required this.language}); 16 | 17 | @override 18 | List get props => [language ?? ""]; 19 | } 20 | 21 | class ChangeTheme extends SettingsEvent { 22 | final AdaptiveThemeMode theme; 23 | 24 | const ChangeTheme({required this.theme}); 25 | 26 | @override 27 | List get props => [theme]; 28 | } 29 | -------------------------------------------------------------------------------- /lib/presentation/screen/settings/bloc/settings_state.dart: -------------------------------------------------------------------------------- 1 | part of 'settings_bloc.dart'; 2 | 3 | enum SettingsStatus { initial, loading, success, failure } 4 | 5 | abstract class SettingsState extends Equatable { 6 | final SettingsStatus status; 7 | 8 | const SettingsState({required this.status}); 9 | } 10 | 11 | class SettingsInitial extends SettingsState { 12 | const SettingsInitial() : super(status: SettingsStatus.initial); 13 | 14 | @override 15 | List get props => [status]; 16 | } 17 | 18 | class SettingsLoading extends SettingsState { 19 | const SettingsLoading() : super(status: SettingsStatus.loading); 20 | 21 | @override 22 | List get props => [status]; 23 | } 24 | 25 | class SettingsLogoutSuccess extends SettingsState { 26 | const SettingsLogoutSuccess() : super(status: SettingsStatus.success); 27 | 28 | @override 29 | List get props => [status]; 30 | } 31 | 32 | class SettingsLanguageChanged extends SettingsState { 33 | final String? language; 34 | 35 | const SettingsLanguageChanged({required this.language}) : super(status: SettingsStatus.success); 36 | 37 | @override 38 | List get props => [status, language ?? ""]; 39 | } 40 | 41 | class SettingsThemeChanged extends SettingsState { 42 | final AdaptiveThemeMode theme; 43 | 44 | const SettingsThemeChanged({required this.theme}) : super(status: SettingsStatus.success); 45 | 46 | @override 47 | List get props => [theme, status]; 48 | } 49 | 50 | class SettingsFailure extends SettingsState { 51 | final String message; 52 | 53 | const SettingsFailure({required this.message}) : super(status: SettingsStatus.failure); 54 | 55 | @override 56 | List get props => [message, status]; 57 | } 58 | -------------------------------------------------------------------------------- /lib/presentation/screen/user/bloc/user.dart: -------------------------------------------------------------------------------- 1 | // User bloc barrel file 2 | export 'user_bloc.dart'; 3 | -------------------------------------------------------------------------------- /lib/presentation/screen/user/bloc/user_event.dart: -------------------------------------------------------------------------------- 1 | part of 'user_bloc.dart'; 2 | 3 | class UserEvent extends Equatable { 4 | const UserEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class UserSearchEvent extends UserEvent { 11 | final int page; 12 | final int size; 13 | final String? authorities; 14 | final String? name; 15 | 16 | const UserSearchEvent({ 17 | this.page = 0, 18 | this.size = 10, 19 | this.authorities, 20 | this.name, 21 | }); 22 | } 23 | 24 | class UserEditorInit extends UserEvent { 25 | const UserEditorInit(); 26 | 27 | @override 28 | List get props => []; 29 | } 30 | 31 | class UserSubmitEvent extends UserEvent { 32 | final User user; 33 | 34 | const UserSubmitEvent(this.user); 35 | 36 | @override 37 | List get props => [user]; 38 | } 39 | 40 | class UserFetchEvent extends UserEvent { 41 | final String id; 42 | 43 | const UserFetchEvent(this.id); 44 | 45 | @override 46 | List get props => [id]; 47 | } 48 | 49 | class UserDeleteEvent extends UserEvent { 50 | final String id; 51 | 52 | const UserDeleteEvent(this.id); 53 | 54 | @override 55 | List get props => [id]; 56 | } 57 | 58 | class UserSaveCompleteEvent extends UserEvent { 59 | const UserSaveCompleteEvent(); 60 | 61 | @override 62 | List get props => []; 63 | } 64 | 65 | class UserViewCompleteEvent extends UserEvent { 66 | const UserViewCompleteEvent(); 67 | 68 | @override 69 | List get props => []; 70 | } 71 | -------------------------------------------------------------------------------- /lib/presentation/screen/user/bloc/user_state.dart: -------------------------------------------------------------------------------- 1 | part of 'user_bloc.dart'; 2 | 3 | enum UserStatus { 4 | initial, 5 | loading, 6 | success, 7 | failure, 8 | searchSuccess, 9 | fetchSuccess, 10 | deleteSuccess, 11 | saveSuccess, 12 | viewSuccess, 13 | } 14 | 15 | class UserState extends Equatable { 16 | final User? data; 17 | final UserStatus status; 18 | final List? userList; 19 | final String? err; 20 | 21 | const UserState({ 22 | this.status = UserStatus.initial, 23 | this.data, 24 | this.userList, 25 | this.err, 26 | }); 27 | 28 | UserState copyWith({ 29 | UserStatus? status, 30 | User? data, 31 | List? userList, 32 | String? err, 33 | }) { 34 | return UserState(status: status ?? this.status, data: data ?? this.data, userList: userList ?? this.userList, err: err ?? this.err); 35 | } 36 | 37 | @override 38 | List get props => [status, data, userList, err]; 39 | } 40 | -------------------------------------------------------------------------------- /lib/routes/app_navigator_routes/app_navigator_routes_config.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/lib/routes/app_navigator_routes/app_navigator_routes_config.dart -------------------------------------------------------------------------------- /lib/routes/app_routes_constants.dart: -------------------------------------------------------------------------------- 1 | /// Routes for the application 2 | /// 3 | /// This class contains all the routes used in the application. 4 | class ApplicationRoutesConstants { 5 | static const home = '/'; 6 | 7 | // Auth routes 8 | static const login = '/login'; 9 | static const register = '/register'; 10 | static const forgotPassword = '/forgot-password'; 11 | static const changePassword = '/change-password'; 12 | static const loginOtp = '/login-otp'; 13 | static const loginOtpVerify = '/login-otp-verify'; 14 | 15 | // Account routes 16 | static const account = '/account'; 17 | 18 | // User routes 19 | static const userList = '/user'; 20 | static const userView = '/user/:id/view'; 21 | static const userEdit = '/user/:id/edit'; 22 | static const userNew = '/user/new'; 23 | 24 | // Settings routes 25 | static const settings = '/settings'; 26 | 27 | // Error routes 28 | static const notFound = '/not-found'; 29 | static const error = '/error'; 30 | static const error500 = '/error/500'; 31 | } 32 | -------------------------------------------------------------------------------- /lib/routes/get_routes/app_get_router_config.dart: -------------------------------------------------------------------------------- 1 | 2 | /// GetX Router Configuration 3 | /// WARN: Not Tested 4 | class AppGetRouterConfig { 5 | // static final List routes = [ 6 | // GetPage(name: 'login', page: () => LoginScreen()), 7 | // GetPage(name: 'forgot-password', page: () => ForgotPasswordScreen()), 8 | // GetPage(name: 'change-password', page: () => ChangePasswordScreen()), 9 | // GetPage(name: 'home', page: () => HomeScreen()), 10 | // GetPage(name: 'settings', page: () => SettingsScreen()), 11 | // GetPage(name: 'account', page: () => AccountScreen()), 12 | // GetPage(name: 'user', page: () => ListUserScreen()), 13 | // //TODO user create, edit, view in GetX 14 | // // GetPage(name:'user/:id', page:()=>ViewUserScreen()), 15 | // // GetPage(name: 'user/new', page: () => CreateUserScreen()), 16 | // // GetPage(name: 'user/:id/edit', page: () => EditUserScreen(id: "")), 17 | // 18 | // // last item is the 404 page 19 | // GetPage(name: 'not-found', page: () => const Scaffold(body: Center(child: Text('Not Found')))), 20 | // ]; 21 | // 22 | // static GetMaterialApp routeBuilder(ThemeData light, ThemeData dark, String language) { 23 | // return GetMaterialApp( 24 | // title: 'Flutter Bloc Advance', 25 | // theme: light, 26 | // darkTheme: dark, 27 | // themeMode: ThemeMode.system, 28 | // getPages: routes, 29 | // initialRoute: 'login', 30 | // unknownRoute: routes.last, 31 | // ); 32 | // } 33 | } 34 | -------------------------------------------------------------------------------- /lib/routes/go_router_routes/routes/account_routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/presentation/screen/account/account_screen.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | class AccountRoutes { 5 | static final List routes = [ 6 | GoRoute(path: '/account', builder: (context, state) => AccountScreen()), 7 | ]; 8 | } 9 | -------------------------------------------------------------------------------- /lib/routes/go_router_routes/routes/auth_routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'package:flutter_bloc_advance/data/repository/account_repository.dart'; 3 | import 'package:flutter_bloc_advance/presentation/screen/change_password/bloc/change_password.dart'; 4 | import 'package:flutter_bloc_advance/presentation/screen/change_password/change_password_screen.dart'; 5 | import 'package:flutter_bloc_advance/presentation/screen/forgot_password/bloc/forgot_password.dart'; 6 | import 'package:flutter_bloc_advance/presentation/screen/forgot_password/forgot_password_screen.dart'; 7 | import 'package:flutter_bloc_advance/presentation/screen/login/login_screen.dart'; 8 | import 'package:flutter_bloc_advance/presentation/screen/register/bloc/register.dart'; 9 | import 'package:flutter_bloc_advance/presentation/screen/register/register_screen.dart'; 10 | import 'package:flutter_bloc_advance/routes/app_routes_constants.dart'; 11 | import 'package:go_router/go_router.dart'; 12 | 13 | class AuthRoutes { 14 | static final List routes = [ 15 | GoRoute(name: 'login', path: ApplicationRoutesConstants.login, builder: (context, state) => LoginScreen()), 16 | GoRoute(name: 'login-otp', path: ApplicationRoutesConstants.loginOtp, builder: (context, state) => OtpEmailScreen()), 17 | GoRoute(name: 'login-otp-verify', path: '/login-otp-verify/:email', builder: (context, state) => OtpVerifyScreen(email: state.pathParameters['email']!)), 18 | GoRoute( 19 | name: 'forgot-password', 20 | path: ApplicationRoutesConstants.forgotPassword, 21 | builder: (context, state) => 22 | BlocProvider(create: (_) => ForgotPasswordBloc(repository: AccountRepository()), child: ForgotPasswordScreen()), 23 | ), 24 | GoRoute( 25 | name: 'change-password', 26 | path: ApplicationRoutesConstants.changePassword, 27 | builder: (context, state) => 28 | BlocProvider(create: (_) => ChangePasswordBloc(repository: AccountRepository()), child: ChangePasswordScreen()), 29 | ), 30 | GoRoute( 31 | name: 'register', 32 | path: ApplicationRoutesConstants.register, 33 | builder: (context, state) => BlocProvider(create: (_) => RegisterBloc(repository: AccountRepository()), child: RegisterScreen())), 34 | 35 | ]; 36 | } 37 | -------------------------------------------------------------------------------- /lib/routes/go_router_routes/routes/home_routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/presentation/screen/home/home_screen.dart'; 2 | import 'package:flutter_bloc_advance/routes/app_routes_constants.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | class HomeRoutes { 6 | static final List routes = [ 7 | GoRoute(name: 'home', path: ApplicationRoutesConstants.home, builder: (context, state) => HomeScreen()), 8 | ]; 9 | } 10 | -------------------------------------------------------------------------------- /lib/routes/go_router_routes/routes/settings_routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/presentation/screen/settings/settings_screen.dart'; 2 | import 'package:flutter_bloc_advance/routes/app_routes_constants.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | class SettingsRoutes { 6 | static final List routes = [ 7 | GoRoute(name: 'settings', path: ApplicationRoutesConstants.settings, builder: (context, state) => SettingsScreen()), 8 | ]; 9 | } 10 | -------------------------------------------------------------------------------- /lib/utils/app_constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | class AppConstants { 4 | static const String appKey = "flutter_bloc_advanced"; 5 | static const String appName = "FlutterTemplate"; 6 | static const String appVersion = "1.0.0"; 7 | static const String appDescription = "Flutter Template with BLOC and Clean Architecture"; 8 | static const String appAuthor = "sample.tech"; 9 | static const String appAuthorEmail = "info@sample.tech"; 10 | } 11 | 12 | class UpperCaseTextFormatter extends TextInputFormatter { 13 | @override 14 | TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { 15 | return TextEditingValue( 16 | text: newValue.text.toUpperCase(), 17 | selection: newValue.selection, 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/utils/menu_list_cache.dart: -------------------------------------------------------------------------------- 1 | import '../data/models/menu.dart'; 2 | 3 | class MenuListCache { 4 | static List menus = []; 5 | } 6 | -------------------------------------------------------------------------------- /lib/utils/message.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | class Message { 5 | static const _duration = Duration(seconds: 3); 6 | 7 | static Future getMessage( 8 | {required BuildContext context, required String title, required String content, Duration duration = Message._duration}) async { 9 | Get.snackbar( 10 | title, 11 | content, 12 | snackPosition: SnackPosition.BOTTOM, 13 | margin: const EdgeInsets.fromLTRB(100, 20, 100, 20), 14 | isDismissible: true, 15 | duration: duration, 16 | ); 17 | } 18 | 19 | static Future errorMessage( 20 | {required BuildContext context, required String title, required String content, Duration duration = Message._duration}) async { 21 | Get.snackbar( 22 | title, 23 | content, 24 | snackPosition: SnackPosition.BOTTOM, 25 | margin: const EdgeInsets.fromLTRB(100, 20, 100, 20), 26 | isDismissible: true, 27 | colorText: Colors.red, 28 | duration: duration, 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | 11 | void fl_register_plugins(FlPluginRegistry* registry) { 12 | g_autoptr(FlPluginRegistrar) printing_registrar = 13 | fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin"); 14 | printing_plugin_register_with_registrar(printing_registrar); 15 | } 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | printing 7 | ) 8 | 9 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 10 | ) 11 | 12 | set(PLUGIN_BUNDLED_LIBRARIES) 13 | 14 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 15 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 16 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 19 | endforeach(plugin) 20 | 21 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 22 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 24 | endforeach(ffi_plugin) 25 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import flutter_inappwebview_macos 9 | import path_provider_foundation 10 | import printing 11 | import shared_preferences_foundation 12 | 13 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 14 | InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) 15 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 16 | PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin")) 17 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 18 | } 19 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_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_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = flutter_bloc_advance 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.cevheri.flutter.flutterBlocAdvance 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024 com.cevheri.flutter. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 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 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_bloc_advance 2 | description: "Flutter Bloc Advance Template" 3 | publish_to: 'none' 4 | version: 0.13.1+1 5 | 6 | environment: 7 | sdk: ^3.6.0 8 | flutter: '3.27.1' 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | cupertino_icons: ^1.0.8 14 | flutter_localizations: 15 | sdk: flutter 16 | flutter_bloc: 17 | http: 18 | go_router: 19 | adaptive_theme: 20 | equatable: 21 | auto_route: 22 | get: 23 | intl: 24 | pdf: 25 | printing: 26 | dart_json_mapper: 27 | expansion_tile_card: 28 | device_preview: 29 | getwidget: 30 | flutter_form_builder: ^9.5.0 31 | form_builder_validators: ^11.0.0 32 | string_2_icon: 33 | intl_utils: 34 | pattern_formatter: 35 | http_certificate_pinning: 36 | bloc_concurrency: 37 | modbus_client_tcp: 38 | custom_navigation_bar: 39 | flutter_inappwebview: 40 | animated_toggle_switch: 41 | reflectable: 42 | glob: 43 | shared_preferences: 44 | logger: ^2.5.0 45 | get_storage: 46 | 47 | dev_dependencies: 48 | flutter_test: 49 | sdk: flutter 50 | flutter_lints: ^4.0.0 51 | integration_test: 52 | sdk: flutter 53 | build_runner: 54 | test: ^1.25.7 55 | bloc_test: ^9.1.7 56 | mockito: ^5.4.4 57 | 58 | flutter_secure_storage: 59 | enabled: true 60 | class_name: SecureStorage 61 | main_locale: en 62 | 63 | flutter_intl: 64 | enabled: true 65 | 66 | flutter: 67 | uses-material-design: true 68 | generate: true 69 | assets: 70 | - assets/mock/ 71 | - assets/images/ -------------------------------------------------------------------------------- /scripts/run_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Running flutter clean" 4 | flutter clean 5 | 6 | echo "Running pub get" 7 | flutter pub get 8 | 9 | echo "Running build_runner" 10 | dart run build_runner build --delete-conflicting-outputs 11 | dart run intl_utils:generate 12 | 13 | echo "Running flutter analyze" 14 | flutter analyze 15 | if [ $? -ne 0 ]; then 16 | echo "Flutter analyze found issues. Exiting." 17 | exit 1 18 | fi 19 | 20 | echo "Running flutter test" 21 | flutter test --coverage 22 | if [ $? -ne 0 ]; then 23 | echo "Flutter test found issues. Exiting." 24 | exit 1 25 | fi 26 | 27 | echo "Running flutter build apk" 28 | flutter build apk --release --target lib/main/main_prod.dart 29 | 30 | echo "Running flutter build appbundle" 31 | flutter build appbundle --release --target lib/main/main_prod.dart 32 | -------------------------------------------------------------------------------- /scripts/run_fvm_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | fvm use 3.27.1 4 | 5 | echo "Running flutter clean" 6 | fvm flutter clean 7 | 8 | echo "Running pub get" 9 | fvm flutter pub get 10 | 11 | echo "Running build_runner" 12 | fvm dart run build_runner build --delete-conflicting-outputs 13 | fvm dart run intl_utils:generate 14 | 15 | echo "Running flutter analyze" 16 | fvm flutter analyze 17 | if [ $? -ne 0 ]; then 18 | echo "Flutter analyze found issues. Exiting." 19 | exit 1 20 | fi 21 | 22 | echo "Running flutter test" 23 | fvm flutter test --coverage 24 | if [ $? -ne 0 ]; then 25 | echo "Flutter test found issues. Exiting." 26 | exit 1 27 | fi 28 | 29 | echo "Running flutter build apk" 30 | fvm flutter build apk --release --target lib/main/main_prod.dart 31 | 32 | echo "Running flutter build appbundle" 33 | fvm flutter build appbundle --release --target lib/main/main_prod.dart 34 | -------------------------------------------------------------------------------- /scripts/run_sonar_scanner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #dart pub global activate fvm 4 | #export PATH="$PATH":"$HOME/.pub-cache/bin" 5 | fvm use 3.27.1 6 | 7 | fvm flutter clean 8 | fvm flutter pub get 9 | 10 | fvm dart run build_runner build --delete-conflicting-outputs 11 | fvm dart run intl_utils:generate 12 | 13 | # when flutter analyze issue found then exit 14 | fvm flutter analyze 15 | if [ $? -ne 0 ]; then 16 | echo "Flutter analyze found issues. Exiting." 17 | exit 1 18 | fi 19 | 20 | fvm flutter test --coverage 21 | if [ $? -ne 0 ]; then 22 | echo "Flutter test found issues. Exiting." 23 | exit 1 24 | fi 25 | 26 | # Download SonarQube scanner 27 | SONAR_SCANNER_VERSION=6.2.1.4610 28 | SONAR_SCANNER_DIR=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux-x64 29 | SONAR_SCANNER_ZIP=$SONAR_SCANNER_DIR.zip 30 | 31 | if [ ! -d "$SONAR_SCANNER_DIR" ]; then 32 | echo "Downloading SonarQube scanner..." 33 | curl --create-dirs -sSLo $SONAR_SCANNER_ZIP https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux-x64.zip 34 | unzip -o $SONAR_SCANNER_ZIP -d $HOME/.sonar/ 35 | fi 36 | 37 | # Set up environment variables 38 | export PATH=$SONAR_SCANNER_DIR/bin:$PATH 39 | 40 | # Run SonarQube scanner 41 | $SONAR_SCANNER_DIR/bin/sonar-scanner \ 42 | -Dsonar.projectKey=cevheri_flutter-bloc-advanced \ 43 | -Dsonar.organization=cevheri-open-source \ 44 | -Dsonar.sources=. \ 45 | -Dsonar.host.url=https://sonarcloud.io \ 46 | -Dsonar.login=$SONAR_TOKEN -------------------------------------------------------------------------------- /scripts/run_unittests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #dart pub global activate fvm 4 | #export PATH="$PATH":"$HOME/.pub-cache/bin" 5 | fvm use 3.27.1 6 | 7 | fvm flutter clean 8 | fvm flutter pub get 9 | 10 | fvm dart run build_runner build --delete-conflicting-outputs 11 | fvm dart run intl_utils:generate 12 | 13 | # when flutter analyze issue found then exit 14 | fvm flutter analyze 15 | if [ $? -ne 0 ]; then 16 | echo "Flutter analyze found issues. Exiting." 17 | exit 1 18 | fi 19 | 20 | fvm flutter test --coverage 21 | if [ $? -ne 0 ]; then 22 | echo "Flutter test found issues. Exiting." 23 | exit 1 24 | fi -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # This is the name and version displayed in the SonarCloud UI. 2 | sonar.projectKey=cevheri_flutter-bloc-advanced 3 | sonar.organization=cevheri-open-source 4 | sonar.host.url=https://sonarcloud.io 5 | 6 | 7 | sonar.projectName=flutter-template 8 | sonar.projectVersion=1.0 9 | 10 | 11 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 12 | sonar.sources=lib/ 13 | #sonar.exclusions=**/test/** 14 | #sonar.test.exclusions=test/** 15 | sonar.tests=test/ 16 | #sonar.verbose=true 17 | # exclude: lib/generated and lib/main dart files 18 | sonar.exclusions=assets/**,lib/main/**,test/**,linux/**,macos/**,windows/**,build/**,ios/**,android/**,web/**,example/**,packages/**,pubspec.lock,functions/**,lib/generated/**,lib/i18n/**,lib/routes/app_router.dart 19 | 20 | sonar.cpd.exclusions=lib/main/** 21 | 22 | # Encoding of the source code. Default is default system encoding 23 | sonar.sourceEncoding=UTF-8 24 | 25 | sonar.c.file.suffixes=- 26 | sonar.cpp.file.suffixes=- 27 | sonar.objc.file.suffixes=- 28 | 29 | 30 | 31 | sonar.dart.lcov.reportPaths=coverage/lcov.info -------------------------------------------------------------------------------- /test/conf/environment_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/configuration/allowed_paths.dart'; 2 | import 'package:flutter_bloc_advance/configuration/environment.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | group('ProfileConstants', () { 7 | test('setEnvironment sets dev environment', () { 8 | ProfileConstants.setEnvironment(Environment.dev); 9 | expect(ProfileConstants.isDevelopment, true); 10 | expect(ProfileConstants.api, "mock"); 11 | }); 12 | 13 | test('setEnvironment sets test environment', () { 14 | ProfileConstants.setEnvironment(Environment.test); 15 | expect(ProfileConstants.isDevelopment, false); 16 | expect(ProfileConstants.isProduction, false); 17 | expect(ProfileConstants.api, "mock"); 18 | }); 19 | 20 | test('setEnvironment sets prod environment', () { 21 | ProfileConstants.setEnvironment(Environment.prod); 22 | expect(ProfileConstants.isProduction, true); 23 | expect(ProfileConstants.api, "https://dhw-api.onrender.com/api"); 24 | }); 25 | }); 26 | test("allowed paths", () { 27 | expect(allowedPaths, [ 28 | '/authenticate', 29 | '/register', 30 | '/logout', 31 | '/account/reset-password/init', 32 | '/forgot-password', 33 | '/login-otp', 34 | '/login-otp-verify', 35 | '/authenticate/send-otp', 36 | '/authenticate/verify-otp' 37 | ]); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /test/conf/logger_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | // AppLogger Test 6 | group("AppLogger Test", () { 7 | test("Logger Test", () { 8 | // production 9 | AppLogger.configure(isProduction: true, logFormat: LogFormat.simple); 10 | final log = AppLogger(); 11 | log.trace("trace"); 12 | log.debug("debug"); 13 | log.info("info"); 14 | log.warn("warn"); 15 | log.error("error"); 16 | expect(log.toString(), "AppLogger(Logger, info, simple)"); 17 | }); 18 | 19 | test("Logger test with name", () { 20 | // production 21 | AppLogger.configure(isProduction: true, logFormat: LogFormat.simple); 22 | final log = AppLogger.getLogger("logger_test.dart"); 23 | log.trace("trace"); 24 | log.debug("debug"); 25 | log.info("info"); 26 | log.warn("warn"); 27 | log.error("error"); 28 | expect(log.toString(), "AppLogger(logger_test.dart, info, simple)"); 29 | }); 30 | 31 | test("Logger Test with parameters", () { 32 | // production 33 | AppLogger.configure(isProduction: true, logFormat: LogFormat.simple); 34 | final log = AppLogger.getLogger("logger_test.dart"); 35 | log.trace("trace p1:{}", ["param1"]); 36 | log.debug("debug"); 37 | log.info("info"); 38 | log.warn("warn"); 39 | log.error("error"); 40 | expect(log.toString(), "AppLogger(logger_test.dart, info, simple)"); 41 | }); 42 | 43 | test("Logger Test with parameters dev env", () { 44 | // production 45 | AppLogger.configure(isProduction: false, logFormat: LogFormat.extended); 46 | final log = AppLogger.getLogger("logger_test.dart"); 47 | log.trace("trace p1:{}", ["param1"]); 48 | log.debug("debug"); 49 | log.info("info"); 50 | log.warn("warn"); 51 | log.error("error"); 52 | expect(log.toString(), "AppLogger(logger_test.dart, debug, extended)"); 53 | }); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /test/data/model/authority_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter_bloc_advance/data/models/authority.dart'; 4 | import 'package:flutter_bloc_advance/main/main_local.mapper.g.dart'; 5 | import 'package:flutter_test/flutter_test.dart'; 6 | 7 | import '../../fake/user_data.dart'; 8 | 9 | /// Test the Authority model 10 | void main() { 11 | setUp(() { 12 | initializeJsonMapper(); 13 | }); 14 | 15 | group("Authority Model", () { 16 | test('should create a Authority instance (Constructor)', () { 17 | expect(mockAuthorityPayload.name, 'ROLE_USER'); 18 | }); 19 | 20 | test('should copy a Authority instance with new values (copyWith)', () { 21 | final entityUpd = mockAuthorityPayload.copyWith(); 22 | 23 | expect(entityUpd == mockAuthorityPayload, true); 24 | }); 25 | 26 | test('should copy a Authority instance with new values (copyWith)', () { 27 | final entityUpd = mockAuthorityPayload.copyWith(name: 'ROLE_ADMIN'); 28 | 29 | expect(entityUpd.name, 'ROLE_ADMIN'); 30 | }); 31 | 32 | test('should compare two Authorities instances', () { 33 | const entity = mockAuthorityPayload; 34 | final entityUpd = entity.copyWith(name: 'ROLE_ADMIN'); 35 | 36 | expect(entity == entityUpd, false); 37 | }); 38 | }); 39 | 40 | group("Authority Model Json Test", () { 41 | test('should convert Authorities from Json', () { 42 | final json = mockAuthorityPayload.toJson(); 43 | final entity = Authority.fromJson(json!); 44 | 45 | expect(entity?.name, 'ROLE_USER'); 46 | }); 47 | 48 | test('should convert Authorities from JsonString', () { 49 | final jsonString = jsonEncode(mockAuthorityPayload.toJson()); 50 | final entity = Authority.fromJsonString(jsonString); 51 | 52 | expect(entity?.name, 'ROLE_USER'); 53 | }); 54 | 55 | test('should convert Authorities to Json', () { 56 | final json = mockAuthorityPayload.toJson()!; 57 | 58 | expect(json['name'], 'ROLE_USER'); 59 | }); 60 | 61 | test("to string method", () { 62 | const entity = mockAuthorityPayload; 63 | 64 | expect(entity.toString(), 'Authority(ROLE_USER)'); 65 | }); 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /test/data/model/otp_send_model_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/data/models/send_otp_request.dart'; 2 | import 'package:flutter_bloc_advance/main/main_local.mapper.g.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | group('SendOtpRequest', () { 7 | test('should create SendOtpRequest instance with email', () { 8 | // given 9 | const testEmail = 'test@example.com'; 10 | 11 | // when 12 | final request = SendOtpRequest(email: testEmail); 13 | 14 | // then 15 | expect(request.email, equals(testEmail)); 16 | }); 17 | 18 | test('should convert SendOtpRequest to JSON correctly', () { 19 | // given 20 | const testEmail = 'test@example.com'; 21 | final request = SendOtpRequest(email: testEmail); 22 | 23 | // when 24 | final json = request.toJson(); 25 | 26 | // then 27 | expect(json, { 28 | 'email': 'test@example.com', 29 | }); 30 | }); 31 | 32 | test('should create SendOtpRequest from JSON correctly', () { 33 | initializeJsonMapper(); 34 | // given 35 | final json = { 36 | 'email': 'test@example.com', 37 | }; 38 | 39 | // when 40 | final request = SendOtpRequest.fromJson(json); 41 | 42 | // then 43 | expect(request, isNotNull); 44 | expect(request?.email, equals('test@example.com')); 45 | }); 46 | 47 | test('should return null when fromJson is called with invalid JSON', () { 48 | initializeJsonMapper(); 49 | // given 50 | final invalidJson = { 51 | 'invalid_key': 'test@example.com', 52 | }; 53 | 54 | // when 55 | final request = SendOtpRequest.fromJson(invalidJson); 56 | 57 | // then 58 | expect(request, isNull); 59 | }); 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /test/data/repository/package_info.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/test/data/repository/package_info.dart -------------------------------------------------------------------------------- /test/fake/city_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/data/models/city.dart'; 2 | import 'package:flutter_bloc_advance/data/models/district.dart'; 3 | 4 | /// City Fake data with full payload 5 | const mockCityPayload = City(id: '1', name: 'istanbul', plateCode: '34'); 6 | 7 | /// District Fake data with full payload 8 | const mockDistrictPayload = District(id: 'id', name: 'kadikoy', code: '34'); 9 | -------------------------------------------------------------------------------- /test/fake/customer_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/data/models/customer.dart'; 2 | 3 | /// Customer fake data with full payload 4 | const mockCustomerFullPayload = Customer( 5 | id: '1', 6 | name: 'Acme', 7 | phone: '5055055050', 8 | cityName: 'Konya', 9 | email: 'john.doe@example.com', 10 | districtName: 'selçuklu', 11 | address: 'yazır mh.', 12 | active: true, 13 | ); 14 | -------------------------------------------------------------------------------- /test/fake/user_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc_advance/data/models/authority.dart'; 2 | import 'package:flutter_bloc_advance/data/models/change_password.dart'; 3 | import 'package:flutter_bloc_advance/data/models/jwt_token.dart'; 4 | import 'package:flutter_bloc_advance/data/models/menu.dart'; 5 | import 'package:flutter_bloc_advance/data/models/user.dart'; 6 | import 'package:flutter_bloc_advance/data/models/user_jwt.dart'; 7 | 8 | final DateTime createdDate = DateTime(2024, 1, 1); 9 | 10 | /// User fake data with full payload 11 | final mockUserFullPayload = User( 12 | id: '1', 13 | login: 'test_login', 14 | firstName: 'John', 15 | lastName: 'Doe', 16 | email: 'john.doe@example.com', 17 | activated: true, 18 | langKey: 'en', 19 | createdBy: 'admin', 20 | createdDate: createdDate, 21 | lastModifiedBy: 'admin', 22 | lastModifiedDate: createdDate, 23 | authorities: const ['ROLE_USER'], 24 | ); 25 | 26 | /// Authority(Role) fake data 27 | const mockAuthorityPayload = Authority(name: 'ROLE_USER'); 28 | 29 | /// PasswordChange fake data with full payload 30 | const mockPasswordChangePayload = PasswordChangeDTO(currentPassword: 'password', newPassword: 'new_password'); 31 | 32 | /// JWTToken fake data 33 | const mockJWTTokenPayload = JWTToken(idToken: 'MOCK_TOKEN'); 34 | 35 | /// Menu fake data with full payload 36 | const mockMenuPayload = Menu( 37 | id: "0", 38 | name: 'test name', 39 | description: '', 40 | url: 'https://dhw-api.onrender.com/', 41 | icon: '', 42 | orderPriority: 01, 43 | active: false, 44 | parent: null, 45 | level: 01, 46 | ); 47 | 48 | /// UserJWT fake data with full payload 49 | const mockUserJWTPayload = UserJWT("username", "password"); 50 | -------------------------------------------------------------------------------- /test/main/app_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_theme/adaptive_theme.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:flutter_bloc_advance/main/app.dart'; 5 | import 'package:flutter_test/flutter_test.dart'; 6 | 7 | import '../test_utils.dart'; 8 | 9 | void main() { 10 | setUpAll(() async { 11 | await TestUtils().setupUnitTest(); 12 | }); 13 | 14 | group('App Widget Tests', () { 15 | late App app; 16 | 17 | setUp(() { 18 | app = const App(language: 'tr', initialTheme: AdaptiveThemeMode.light); 19 | }); 20 | 21 | testWidgets('App should build without errors', (WidgetTester tester) async { 22 | await tester.pumpWidget(app); 23 | await tester.pumpAndSettle(); 24 | expect(find.byType(AdaptiveTheme), findsOneWidget); 25 | }); 26 | 27 | testWidgets('App should handle theme changes', (WidgetTester tester) async { 28 | await tester.pumpWidget(app); 29 | await tester.pumpAndSettle(); 30 | 31 | final AdaptiveTheme adaptiveTheme = tester.widget(find.byType(AdaptiveTheme)); 32 | expect(adaptiveTheme.initial, equals(AdaptiveThemeMode.light)); 33 | 34 | // Test dark theme 35 | app = const App(language: 'tr', initialTheme: AdaptiveThemeMode.dark); 36 | await tester.pumpWidget(app); 37 | await tester.pumpAndSettle(); 38 | 39 | final AdaptiveTheme darkTheme = tester.widget(find.byType(AdaptiveTheme)); 40 | expect(darkTheme.initial, equals(AdaptiveThemeMode.dark)); 41 | }); 42 | 43 | testWidgets('App should handle different languages', (WidgetTester tester) async { 44 | TestUtils().setupAuthentication(); 45 | app = const App(language: 'en', initialTheme: AdaptiveThemeMode.light); 46 | await tester.pumpWidget(app); 47 | await tester.pumpAndSettle(); 48 | 49 | const expectedLocale = Locale('en'); 50 | final materialApp = tester.widget(find.byType(MaterialApp)) as MaterialApp; 51 | expect(materialApp.locale, equals(expectedLocale)); 52 | }); 53 | 54 | testWidgets('MultiBlocProvider should contain all required providers', (WidgetTester tester) async { 55 | await tester.pumpWidget(app); 56 | await tester.pumpAndSettle(); 57 | expect(find.byType(MultiBlocProvider), findsOneWidget); 58 | }); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /test/presentation/screen/user/package_info.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/test/presentation/screen/user/package_info.dart -------------------------------------------------------------------------------- /test/presentation/widgets/package_info.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/test/presentation/widgets/package_info.dart -------------------------------------------------------------------------------- /test/test_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 3 | import 'package:flutter_bloc_advance/configuration/environment.dart'; 4 | import 'package:flutter_bloc_advance/configuration/local_storage.dart'; 5 | import 'package:flutter_bloc_advance/main/main_local.mapper.g.dart'; 6 | import 'package:flutter_bloc_advance/routes/app_router.dart'; 7 | import 'package:flutter_test/flutter_test.dart'; 8 | import 'package:shared_preferences/shared_preferences.dart'; 9 | 10 | /// Utility class for the tests 11 | /// 12 | /// This class contains utility methods that are used in the tests 13 | class TestUtils { 14 | /// Initialize the dependencies for the BLoC tests 15 | /// 16 | /// This method initializes the following dependencies:

17 | /// 1. JsonMapper

18 | /// 2. Flutter Test Binding

19 | /// 3. Shared Preferences

20 | /// 4. Equatable Configuration

21 | /// 5. Mock Method Call Handler for Path Provider

22 | 23 | 24 | Future setupUnitTest() async { 25 | AppLogger.configure(isProduction: false, logFormat: LogFormat.simple); 26 | ProfileConstants.setEnvironment(Environment.test); 27 | initializeJsonMapper(); 28 | TestWidgetsFlutterBinding.ensureInitialized(); 29 | EquatableConfig.stringify = true; 30 | await _clearStorage(); 31 | await AppLocalStorage().save(StorageKeys.language.name, "en"); 32 | AppRouter().setRouter(RouterType.goRouter); 33 | } 34 | Future setupRepositoryUnitTest() async { 35 | AppLogger.configure(isProduction: false, logFormat: LogFormat.simple); 36 | ProfileConstants.setEnvironment(Environment.test); 37 | initializeJsonMapper(); 38 | await _clearStorage(); 39 | await AppLocalStorage().save(StorageKeys.language.name, "en"); 40 | AppRouter().setRouter(RouterType.goRouter); 41 | } 42 | 43 | Future tearDownUnitTest() async { 44 | return await _clearStorage(); 45 | } 46 | 47 | // add mock token to storage 48 | Future setupAuthentication() async { 49 | await AppLocalStorage().save(StorageKeys.jwtToken.name, "MOCK_TOKEN"); 50 | } 51 | 52 | Future _clearStorage() async { 53 | SharedPreferences.setMockInitialValues({}); 54 | await AppLocalStorage().clear(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // import 'package:adaptive_theme/adaptive_theme.dart'; 2 | // import 'package:flutter/material.dart'; 3 | // import 'package:flutter_bloc_advance/configuration/app_logger.dart'; 4 | // import 'package:flutter_bloc_advance/configuration/environment.dart'; 5 | // import 'package:flutter_bloc_advance/configuration/local_storage.dart'; 6 | // import 'package:flutter_bloc_advance/main/app.dart'; 7 | // import 'package:flutter_bloc_advance/main/main_local.mapper.g.dart'; 8 | // import 'package:flutter_bloc_advance/routes/app_router.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | void main() { 12 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 13 | // AppLogger.configure(isProduction: false); 14 | // final log = AppLogger.getLogger("main_local.dart"); 15 | // ProfileConstants.setEnvironment(Environment.dev); 16 | // log.info("Starting App with env: {}", [Environment.dev.name]); 17 | // initializeJsonMapper(); 18 | // //WidgetsFlutterBinding.ensureInitialized(); 19 | // const defaultLanguage = "en"; 20 | // AppLocalStorage().setStorage(StorageType.sharedPreferences); 21 | // await AppLocalStorage().save(StorageKeys.language.name, defaultLanguage); 22 | // AppRouter().setRouter(RouterType.goRouter); 23 | // const initialTheme = AdaptiveThemeMode.dark; 24 | // //await tester.pumpWidget(const App(language: defaultLanguage, initialTheme: initialTheme)); 25 | 26 | // Verify that our counter starts at 0. 27 | // expect(find.text('0'), findsOneWidget); 28 | // expect(find.text('1'), findsNothing); 29 | 30 | // Tap the '+' icon and trigger a frame. 31 | // await tester.tap(find.byIcon(Icons.add)); 32 | // await tester.pump(); 33 | 34 | // Verify that our counter has incremented. 35 | // expect(find.text('0'), findsNothing); 36 | // expect(find.text('1'), findsOneWidget); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | flutter_bloc_advance 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_bloc_advance", 3 | "short_name": "flutter_bloc_advance", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "flutter_bloc_advance", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void RegisterPlugins(flutter::PluginRegistry* registry) { 13 | FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( 14 | registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); 15 | PrintingPluginRegisterWithRegistrar( 16 | registry->GetRegistrarForPlugin("PrintingPlugin")); 17 | } 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | flutter_inappwebview_windows 7 | printing 8 | ) 9 | 10 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 11 | ) 12 | 13 | set(PLUGIN_BUNDLED_LIBRARIES) 14 | 15 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 16 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 17 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 20 | endforeach(plugin) 21 | 22 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 23 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 25 | endforeach(ffi_plugin) 26 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"flutter_bloc_advance", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cevheri/flutter-bloc-advanced/4b9dcdbad897a0f78ccb09b6062e94b008c4d35d/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | unsigned int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length == 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------