├── .gitignore ├── .idea ├── .gitignore ├── caches │ └── deviceStreaming.xml ├── git_toolbox_blame.xml ├── git_toolbox_prj.xml ├── libraries │ └── Dart_SDK.xml ├── mason_bricks.iml ├── material_theme_project_new.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── bricks ├── base │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── __brick__ │ │ ├── assets │ │ │ └── HandCoding-bro.svg │ │ ├── l10n.yaml │ │ ├── lib │ │ │ ├── core │ │ │ │ ├── constants │ │ │ │ │ └── constants.dart │ │ │ │ ├── data │ │ │ │ │ ├── dio_config.dart │ │ │ │ │ ├── interceptors │ │ │ │ │ │ ├── auth_interceptor.dart │ │ │ │ │ │ └── logging_interceptor.dart │ │ │ │ │ ├── network │ │ │ │ │ │ ├── api_provider.dart │ │ │ │ │ │ └── network_info.dart │ │ │ │ │ └── token_manager.dart │ │ │ │ ├── error │ │ │ │ │ ├── error_handler.dart │ │ │ │ │ ├── exceptions.dart │ │ │ │ │ ├── failures.dart │ │ │ │ │ └── response_error.dart │ │ │ │ ├── firebase │ │ │ │ │ ├── firebase_config.dart │ │ │ │ │ └── notification_controller.dart │ │ │ │ ├── global_app_setup │ │ │ │ │ ├── app_config.dart │ │ │ │ │ └── global_app_setup.dart │ │ │ │ ├── language │ │ │ │ │ ├── language_bloc.dart │ │ │ │ │ ├── language_event.dart │ │ │ │ │ └── language_state.dart │ │ │ │ ├── logger │ │ │ │ │ └── app_logger.dart │ │ │ │ ├── navigation │ │ │ │ │ └── app_navigation.dart │ │ │ │ ├── params │ │ │ │ │ └── no_params.dart │ │ │ │ ├── secure_storage │ │ │ │ │ ├── flutter_secure_storage_const.dart │ │ │ │ │ ├── secure_storage.dart │ │ │ │ │ └── secure_storage_interface.dart │ │ │ │ ├── service_locator.dart │ │ │ │ ├── success_response │ │ │ │ │ └── success_response.dart │ │ │ │ ├── theme │ │ │ │ │ ├── app_theme.dart │ │ │ │ │ ├── bloc │ │ │ │ │ │ ├── theme_bloc.dart │ │ │ │ │ │ ├── theme_event.dart │ │ │ │ │ │ └── theme_state.dart │ │ │ │ │ ├── color_themes.dart │ │ │ │ │ ├── text_themes.dart │ │ │ │ │ └── theme_extensions.dart │ │ │ │ ├── use_case │ │ │ │ │ └── base_usecase.dart │ │ │ │ └── utils │ │ │ │ │ └── extensions │ │ │ │ │ ├── context_extensions.dart │ │ │ │ │ ├── date_extentions.dart │ │ │ │ │ ├── file_extension.dart │ │ │ │ │ └── string_extensions.dart │ │ │ ├── features │ │ │ │ └── splash_feature │ │ │ │ │ ├── data │ │ │ │ │ ├── data_sources │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── models │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── repositories │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── domain │ │ │ │ │ ├── entities │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── repositories │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── use_cases │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── presentation │ │ │ │ │ ├── manager │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── pages │ │ │ │ │ └── splash_page.dart │ │ │ │ │ └── widgets │ │ │ │ │ └── .gitkeep │ │ │ ├── l10n │ │ │ │ ├── intl_en.arb │ │ │ │ └── intl_fa.arb │ │ │ ├── library.dart │ │ │ ├── main_dev.dart │ │ │ ├── main_local.dart │ │ │ ├── main_prod.dart │ │ │ └── my_app.dart │ │ └── pubspec.yaml │ └── brick.yaml ├── feature │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── __brick__ │ │ └── lib │ │ │ └── features │ │ │ └── {{name}}_feature │ │ │ ├── data │ │ │ ├── data_sources │ │ │ │ └── {{name}}_api_provider.dart │ │ │ ├── models │ │ │ │ └── .gitkeep │ │ │ └── repositories │ │ │ │ └── {{name}}_repository_impl.dart │ │ │ ├── domain │ │ │ ├── entities │ │ │ │ └── .gitkeep │ │ │ ├── repositories │ │ │ │ └── {{name}}_repository.dart │ │ │ └── use_cases │ │ │ │ └── {{useCase}}_use_case.dart │ │ │ └── presentation │ │ │ ├── manager │ │ │ ├── status │ │ │ │ └── {{useCase}}_status.dart │ │ │ ├── {{name}}_bloc.dart │ │ │ ├── {{name}}_event.dart │ │ │ └── {{name}}_state.dart │ │ │ ├── pages │ │ │ └── {{name}}_page.dart │ │ │ └── widgets │ │ │ └── .gitkeep │ └── brick.yaml └── status │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── __brick__ │ └── lib │ │ └── features │ │ ├── dealership_panel │ │ └── {{blocName}}_bloc │ │ │ ├── status │ │ │ └── {{event}}_status.dart │ │ │ └── {{blocName}}_state.dart │ │ └── {{name}}_feature │ │ └── presentation │ │ └── manager │ │ ├── status │ │ ├── {{event}}_status.dart │ │ └── {{useCase}}_status.dart │ │ ├── {{name}}_bloc.dart │ │ ├── {{name}}_event.dart │ │ └── {{name}}_state.dart │ └── brick.yaml └── example ├── .gitignore ├── .mason └── bricks.json ├── .metadata ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── example │ │ │ │ └── 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 └── HandCoding-bro.svg ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── 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 ├── l10n.yaml ├── lib ├── core │ ├── constants │ │ └── constants.dart │ ├── data │ │ └── network │ │ │ ├── api_intercepror.dart │ │ │ ├── api_interface.dart │ │ │ └── api_provider.dart │ ├── error │ │ ├── error_handling.dart │ │ └── response_error.dart │ ├── extensions │ │ ├── date_extentions.dart │ │ ├── file_extension.dart │ │ └── theme_extension.dart │ ├── firebase │ │ ├── firebase_config.dart │ │ └── notification_controller.dart │ ├── global_app_setup │ │ ├── app_config.dart │ │ └── global_app_setup.dart │ ├── language │ │ ├── language_bloc.dart │ │ ├── language_event.dart │ │ └── language_state.dart │ ├── logger │ │ └── app_logger.dart │ ├── navigation │ │ └── app_navigation.dart │ ├── params │ │ └── no_params.dart │ ├── secure_storage │ │ ├── flutter_secure_storage_const.dart │ │ ├── secure_storage.dart │ │ └── secure_storage_interface.dart │ ├── service_locator.dart │ ├── success_response │ │ └── success_response.dart │ ├── theme │ │ ├── app_theme.dart │ │ ├── bloc │ │ │ ├── theme_bloc.dart │ │ │ ├── theme_event.dart │ │ │ └── theme_state.dart │ │ ├── color_themes.dart │ │ ├── text_themes.dart │ │ └── theme_extensions.dart │ └── use_case │ │ └── base_usecase.dart ├── features │ ├── splash_feature │ │ ├── data │ │ │ ├── data_sources │ │ │ │ └── .gitkeep │ │ │ ├── models │ │ │ │ └── .gitkeep │ │ │ └── repositories │ │ │ │ └── .gitkeep │ │ ├── domain │ │ │ ├── entities │ │ │ │ └── .gitkeep │ │ │ ├── repositories │ │ │ │ └── .gitkeep │ │ │ └── use_cases │ │ │ │ └── .gitkeep │ │ └── presentation │ │ │ ├── manager │ │ │ └── .gitkeep │ │ │ ├── pages │ │ │ └── splash_page.dart │ │ │ └── widgets │ │ │ └── .gitkeep │ └── user_feature │ │ ├── data │ │ ├── data_sources │ │ │ └── user_api_provider.dart │ │ ├── models │ │ │ └── .gitkeep │ │ └── repositories │ │ │ └── user_repository_impl.dart │ │ ├── domain │ │ ├── entities │ │ │ └── .gitkeep │ │ ├── repositories │ │ │ └── user_repository.dart │ │ └── use_cases │ │ │ └── get_users_use_case.dart │ │ └── presentation │ │ ├── manager │ │ ├── status │ │ │ └── get_users_status.dart │ │ ├── user_bloc.dart │ │ ├── user_event.dart │ │ └── user_state.dart │ │ ├── pages │ │ └── user_page.dart │ │ └── widgets │ │ └── .gitkeep ├── l10n │ ├── intl_en.arb │ └── intl_fa.arb ├── library.dart ├── main_dev.dart ├── main_local.dart ├── main_prod.dart └── my_app.dart ├── mason-lock.json ├── mason.yaml ├── pubspec.lock └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore everything in this directory 2 | #* 3 | ## Except this file 4 | #!.gitignore -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/git_toolbox_blame.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /.idea/mason_bricks.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/material_theme_project_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /bricks/base/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.0+1 2 | 3 | - TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /bricks/base/LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /bricks/base/README.md: -------------------------------------------------------------------------------- 1 | # base template 2 | 3 | [![Powered by Mason](https://img.shields.io/endpoint?url=https%3A%2F%2Ftinyurl.com%2Fmason-badge)](https://github.com/felangel/mason) 4 | 5 | A new brick created with the Mason CLI. 6 | 7 | _Generated by [mason][1] 🧱_ 8 | 9 | ## Getting Started 🚀 10 | 11 | This is a starting point for a new brick. 12 | A few resources to get you started if this is your first brick template: 13 | 14 | - [Official Mason Documentation][2] 15 | - [Code generation with Mason Blog][3] 16 | - [Very Good Livestream: Felix Angelov Demos Mason][4] 17 | - [Flutter Package of the Week: Mason][5] 18 | - [Observable Flutter: Building a Mason brick][6] 19 | - [Meet Mason: Flutter Vikings 2022][7] 20 | 21 | [1]: https://github.com/felangel/mason 22 | [2]: https://docs.brickhub.dev 23 | [3]: https://verygood.ventures/blog/code-generation-with-mason 24 | [4]: https://youtu.be/G4PTjA6tpTU 25 | [5]: https://youtu.be/qjA0JFiPMnQ 26 | [6]: https://youtu.be/o8B1EfcUisw 27 | [7]: https://youtu.be/LXhgiF5HiQg 28 | -------------------------------------------------------------------------------- /bricks/base/__brick__/l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/l10n 2 | template-arb-file: intl_en.arb 3 | output-localization-file: app_localizations.dart -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/constants/constants.dart: -------------------------------------------------------------------------------- 1 | class Constants { 2 | static String? accessToken = ""; 3 | static String? firebaseToken = ""; 4 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/data/dio_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import '../global_app_setup/app_config.dart'; 4 | import 'interceptors/auth_interceptor.dart'; 5 | import 'interceptors/logging_interceptor.dart'; 6 | import 'package:pretty_dio_logger/pretty_dio_logger.dart'; 7 | 8 | class DioConfig { 9 | static Dio createDio({ 10 | String? baseUrl, 11 | Duration? connectTimeout, 12 | Duration? receiveTimeout, 13 | Duration? sendTimeout, 14 | }) { 15 | final dio = Dio( 16 | BaseOptions( 17 | baseUrl: baseUrl ?? AppConfig.baseUrl, 18 | connectTimeout: connectTimeout ?? const Duration(seconds: 30), 19 | receiveTimeout: receiveTimeout ?? const Duration(seconds: 30), 20 | sendTimeout: sendTimeout ?? const Duration(seconds: 30), 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | 'Accept': 'application/json', 24 | }, 25 | ), 26 | ); 27 | 28 | dio.interceptors.addAll([ 29 | AuthInterceptor(), 30 | LoggingInterceptor(), 31 | PrettyDioLogger( 32 | requestHeader: true, 33 | requestBody: true, 34 | responseBody: true, 35 | responseHeader: false, 36 | error: true, 37 | compact: true, 38 | maxWidth: 90, 39 | enabled: kDebugMode,) 40 | ]); 41 | 42 | return dio; 43 | } 44 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/data/interceptors/auth_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:foveo/core/data/token_manager.dart'; 3 | 4 | 5 | class AuthInterceptor extends Interceptor { 6 | 7 | @override 8 | Future onRequest( 9 | RequestOptions options, 10 | RequestInterceptorHandler handler, 11 | ) async { 12 | final tokens =await TokenManager.getAccessToken(); 13 | if (tokens != null) { 14 | options.headers['Authorization'] = 'Bearer $tokens'; 15 | } 16 | return handler.next(options); 17 | } 18 | 19 | @override 20 | Future onError( 21 | DioException err, 22 | ErrorInterceptorHandler handler, 23 | ) async { 24 | if (err.response?.statusCode == 401) { 25 | try { 26 | final refreshToken = await TokenManager.getRefreshToken(); 27 | if (refreshToken != null) { 28 | // اینجا باید درخواست refresh token را به سرور ارسال کنید 29 | // و توکن‌های جدید را دریافت کنید 30 | // مثال: 31 | // final response = await dio.post('/refresh-token', data: {'refresh_token': tokens.refreshToken}); 32 | // await ref.read(tokenProvider.notifier).refreshAccessToken(response.data['access_token']); 33 | 34 | // تکرار درخواست اصلی 35 | final newTokens = await TokenManager.getAccessToken(); 36 | err.requestOptions.headers['Authorization'] = 'Bearer $newTokens'; 37 | final response = await Dio().fetch(err.requestOptions); 38 | return handler.resolve(response); 39 | } 40 | } catch (e) { 41 | // در صورت خطا در refresh token، کاربر را logout کنید 42 | await TokenManager.clearTokens(); 43 | // TODO: Navigate to login screen 44 | } 45 | } 46 | return handler.next(err); 47 | } 48 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/data/interceptors/logging_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:logger/logger.dart'; 3 | 4 | class LoggingInterceptor extends Interceptor { 5 | final Logger _logger = Logger( 6 | printer: PrettyPrinter( 7 | methodCount: 2, 8 | errorMethodCount: 8, 9 | lineLength: 120, 10 | colors: true, 11 | printEmojis: true, 12 | printTime: true, 13 | ), 14 | ); 15 | 16 | @override 17 | void onRequest(RequestOptions options, RequestInterceptorHandler handler) { 18 | _logger.i('REQUEST[${options.method}] => PATH: ${options.path}'); 19 | _logger.d('Headers: ${options.headers}'); 20 | _logger.d('Data: ${options.data}'); 21 | handler.next(options); 22 | } 23 | 24 | @override 25 | void onResponse(Response response, ResponseInterceptorHandler handler) { 26 | _logger.i( 27 | 'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}', 28 | ); 29 | _logger.d('Response Data: ${response.data}'); 30 | handler.next(response); 31 | } 32 | 33 | @override 34 | void onError(DioException err, ErrorInterceptorHandler handler) { 35 | _logger.e( 36 | 'ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path}', 37 | ); 38 | _logger.e('Error Data: ${err.response?.data}'); 39 | handler.next(err); 40 | } 41 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/data/network/api_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import '../../error/exceptions.dart'; 3 | import '../../error/failures.dart'; 4 | import '../dio_config.dart'; 5 | import 'network_info.dart'; 6 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 7 | abstract class ApiProviderInterface { 8 | Future get(String path, {Map? queryParameters}); 9 | Future post(String path, {dynamic data}); 10 | Future put(String path, {dynamic data}); 11 | Future delete(String path); 12 | Future patch(String path, {dynamic data}); 13 | } 14 | 15 | class ApiProvider implements ApiProviderInterface { 16 | final Dio _dio; 17 | final NetworkInfo _networkInfo; 18 | 19 | ApiProvider._internal(this._dio, this._networkInfo); 20 | 21 | static ApiProvider? _instance; 22 | static ApiProvider get instance { 23 | _instance ??= ApiProvider._internal( 24 | DioConfig.createDio(), 25 | NetworkInfoImpl(InternetConnectionChecker.createInstance()), 26 | ); 27 | return _instance!; 28 | } 29 | 30 | @override 31 | Future get(String path, {Map? queryParameters}) async { 32 | try { 33 | if (!await _networkInfo.isConnected) { 34 | throw NetworkFailure(); 35 | } 36 | final response = await _dio.get(path, queryParameters: queryParameters); 37 | return _handleResponse(response); 38 | } on DioException catch (e) { 39 | throw _handleDioError(e); 40 | } catch (e) { 41 | throw ServerException(); 42 | } 43 | } 44 | 45 | @override 46 | Future post(String path, {dynamic data}) async { 47 | try { 48 | if (!await _networkInfo.isConnected) { 49 | throw NetworkFailure(); 50 | } 51 | final response = await _dio.post(path, data: data); 52 | return _handleResponse(response); 53 | } on DioException catch (e) { 54 | throw _handleDioError(e); 55 | } catch (e) { 56 | throw ServerException(); 57 | } 58 | } 59 | 60 | @override 61 | Future put(String path, {dynamic data}) async { 62 | try { 63 | if (!await _networkInfo.isConnected) { 64 | throw NetworkFailure(); 65 | } 66 | final response = await _dio.put(path, data: data); 67 | return _handleResponse(response); 68 | } on DioException catch (e) { 69 | throw _handleDioError(e); 70 | } catch (e) { 71 | throw ServerException(); 72 | } 73 | } 74 | 75 | @override 76 | Future delete(String path) async { 77 | try { 78 | if (!await _networkInfo.isConnected) { 79 | throw NetworkFailure(); 80 | } 81 | final response = await _dio.delete(path); 82 | return _handleResponse(response); 83 | } on DioException catch (e) { 84 | throw _handleDioError(e); 85 | } catch (e) { 86 | throw ServerException(); 87 | } 88 | } 89 | 90 | @override 91 | Future patch(String path, {dynamic data}) async { 92 | try { 93 | if (!await _networkInfo.isConnected) { 94 | throw NetworkFailure(); 95 | } 96 | final response = await _dio.patch(path, data: data); 97 | return _handleResponse(response); 98 | } on DioException catch (e) { 99 | throw _handleDioError(e); 100 | } catch (e) { 101 | throw ServerException(); 102 | } 103 | } 104 | 105 | dynamic _handleResponse(Response response) { 106 | if (response.statusCode! >= 200 && response.statusCode! < 300) { 107 | return response.data; 108 | } else { 109 | throw ServerException(); 110 | } 111 | } 112 | 113 | Exception _handleDioError(DioException error) { 114 | switch (error.type) { 115 | case DioExceptionType.connectionTimeout: 116 | case DioExceptionType.sendTimeout: 117 | case DioExceptionType.receiveTimeout: 118 | return TimeoutException(); 119 | case DioExceptionType.badResponse: 120 | switch (error.response?.statusCode) { 121 | case 400: 122 | return BadRequestException(); 123 | case 401: 124 | return UnauthorizedException(); 125 | case 403: 126 | return ForbiddenException(); 127 | case 404: 128 | return NotFoundException(); 129 | case 500: 130 | return ServerException(); 131 | default: 132 | return ServerException(); 133 | } 134 | case DioExceptionType.cancel: 135 | return RequestCancelledException(); 136 | default: 137 | return ServerException(); 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/data/network/network_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 2 | 3 | abstract class NetworkInfo { 4 | Future get isConnected; 5 | } 6 | 7 | class NetworkInfoImpl implements NetworkInfo { 8 | final InternetConnectionChecker connectionChecker; 9 | 10 | NetworkInfoImpl(this.connectionChecker); 11 | 12 | @override 13 | Future get isConnected => connectionChecker.hasConnection; 14 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/data/token_manager.dart: -------------------------------------------------------------------------------- 1 | import '../secure_storage/secure_storage.dart'; 2 | 3 | class TokenManager { 4 | static final _storage = SecureStorage(); 5 | static const _accessTokenKey = 'access_token'; 6 | static const _refreshTokenKey = 'refresh_token'; 7 | 8 | static Future setTokens({ 9 | required String accessToken, 10 | required String refreshToken, 11 | }) async { 12 | await _storage.write(_accessTokenKey, accessToken); 13 | await _storage.write(_refreshTokenKey, refreshToken); 14 | } 15 | 16 | static Future getAccessToken() async { 17 | return await _storage.read(_accessTokenKey); 18 | } 19 | 20 | static Future getRefreshToken() async { 21 | return await _storage.read(_refreshTokenKey); 22 | } 23 | 24 | static Future clearTokens() async { 25 | await _storage.delete(_accessTokenKey); 26 | await _storage.delete(_refreshTokenKey); 27 | } 28 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/error/error_handler.dart: -------------------------------------------------------------------------------- 1 | import 'exceptions.dart'; 2 | import 'failures.dart'; 3 | 4 | class ErrorHandler { 5 | static Failure handleError(dynamic error) { 6 | if (error is ServerException) { 7 | return const ServerFailure(); 8 | } else if (error is CacheException) { 9 | return const CacheFailure(); 10 | } else if (error is NetworkException) { 11 | return const NetworkFailure(); 12 | } else if (error is ValidationException) { 13 | return ValidationFailure(error.message); 14 | } else if (error is AuthenticationException) { 15 | return AuthenticationFailure(error.message); 16 | } else if (error is UnknownException) { 17 | return UnknownFailure(error.message); 18 | } else { 19 | return UnknownFailure(error.toString()); 20 | } 21 | } 22 | 23 | static String getErrorMessage(Failure failure) { 24 | switch (failure.runtimeType) { 25 | case ServerFailure: 26 | return 'خطا در ارتباط با سرور'; 27 | case CacheFailure: 28 | return 'خطا در ذخیره‌سازی اطلاعات'; 29 | case NetworkFailure: 30 | return 'خطا در اتصال به اینترنت'; 31 | case ValidationFailure: 32 | return (failure as ValidationFailure).message; 33 | case AuthenticationFailure: 34 | return (failure as AuthenticationFailure).message; 35 | case UnknownFailure: 36 | return (failure as UnknownFailure).message; 37 | default: 38 | return 'خطای ناشناخته'; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/error/exceptions.dart: -------------------------------------------------------------------------------- 1 | class ServerException implements Exception {} 2 | 3 | class CacheException implements Exception {} 4 | 5 | class NetworkException implements Exception {} 6 | 7 | class TimeoutException implements Exception {} 8 | 9 | class BadRequestException implements Exception {} 10 | 11 | class UnauthorizedException implements Exception {} 12 | 13 | class ForbiddenException implements Exception {} 14 | 15 | class NotFoundException implements Exception {} 16 | 17 | class RequestCancelledException implements Exception {} 18 | 19 | class ValidationException implements Exception { 20 | final String message; 21 | ValidationException(this.message); 22 | } 23 | 24 | class AuthenticationException implements Exception { 25 | final String message; 26 | AuthenticationException(this.message); 27 | } 28 | 29 | class UnknownException implements Exception { 30 | final String message; 31 | UnknownException(this.message); 32 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/error/failures.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:equatable/equatable.dart'; 3 | 4 | abstract class Failure extends Equatable { 5 | final List properties; 6 | 7 | const Failure([this.properties = const []]); 8 | 9 | @override 10 | List get props => properties; 11 | } 12 | 13 | class ServerFailure extends Failure { 14 | const ServerFailure() : super(); 15 | } 16 | 17 | class CacheFailure extends Failure { 18 | const CacheFailure() : super(); 19 | } 20 | 21 | class NetworkFailure extends Failure { 22 | const NetworkFailure() : super(); 23 | } 24 | 25 | class ValidationFailure extends Failure { 26 | final String message; 27 | ValidationFailure(this.message) : super([message]); 28 | } 29 | 30 | class AuthenticationFailure extends Failure { 31 | final String message; 32 | AuthenticationFailure(this.message) : super([message]); 33 | } 34 | 35 | class UnknownFailure extends Failure { 36 | final String message; 37 | UnknownFailure(this.message) : super([message]); 38 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/error/response_error.dart: -------------------------------------------------------------------------------- 1 | class ResponseError { 2 | final int statusCode; 3 | final String message; 4 | ResponseError({required this.message , required this.statusCode}); 5 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/firebase/firebase_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 2 | import 'package:firebase_core/firebase_core.dart'; 3 | import 'package:firebase_messaging/firebase_messaging.dart'; 4 | import 'package:awesome_notifications/awesome_notifications.dart'; 5 | import '../logger/app_logger.dart'; 6 | import '../secure_storage/secure_storage.dart'; 7 | 8 | class FirebaseConfig with AppLogger { 9 | FirebaseConfig(); 10 | 11 | final SecureStorage _secureStorage = SecureStorage(); // استفاده از SecureStorage 12 | 13 | Future fetchToken() async { 14 | logger.w("Fetching Firebase Token"); 15 | 16 | String? token; 17 | 18 | try { 19 | // مقداردهی اولیه Firebase 20 | await Firebase.initializeApp(); 21 | 22 | // اشتراک در موضوع 23 | await FirebaseMessaging.instance.subscribeToTopic('news'); 24 | 25 | // خواندن توکن از SecureStorage 26 | token = await _secureStorage.read('firebaseToken'); 27 | logger.f(token); 28 | 29 | if (token == null || token.isEmpty) { 30 | // حذف توکن قدیمی و گرفتن توکن جدید 31 | await FirebaseMessaging.instance.deleteToken(); 32 | token = await FirebaseMessaging.instance.getToken(); 33 | logger.e(token); 34 | 35 | if (token != null) { 36 | // ذخیره توکن جدید در SecureStorage 37 | await _secureStorage.write('firebaseToken', token); 38 | } 39 | } 40 | // گوش دادن به نوتیفیکیشن‌ها 41 | listenOnNotifications(); 42 | listenOnNotificationsOpenedApp(); 43 | 44 | return token; 45 | } catch (e) { 46 | logger.e(e); 47 | return null; 48 | } 49 | } 50 | 51 | Future deleteFirebaseToken() async { 52 | try { 53 | // حذف توکن از SecureStorage 54 | await _secureStorage.delete('firebaseToken'); 55 | 56 | // حذف توکن از Firebase Messaging 57 | await FirebaseMessaging.instance.deleteToken(); 58 | 59 | String? newToken = await FirebaseMessaging.instance.getToken(); 60 | logger.i(newToken); 61 | 62 | if (newToken != null) { 63 | // ذخیره توکن جدید در SecureStorage 64 | await _secureStorage.write('firebaseToken', newToken); 65 | 66 | } 67 | 68 | logger.i("Firebase token has been removed."); 69 | return true; 70 | } catch (e) { 71 | logger.e(e); 72 | return false; 73 | } 74 | } 75 | 76 | void listenOnNotificationsOpenedApp() { 77 | FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { 78 | _handleBackgroundNotification(message); 79 | }); 80 | } 81 | 82 | void listenOnNotifications() { 83 | FirebaseMessaging.onMessage.listen((RemoteMessage message) async { 84 | _handleBackgroundNotification(message); 85 | }); 86 | } 87 | 88 | static Future _handleBackgroundNotification(RemoteMessage message) async { 89 | return AwesomeNotifications().createNotification( 90 | content: NotificationContent( 91 | actionType: ActionType.KeepOnTop, 92 | wakeUpScreen: true, 93 | id: 123, 94 | criticalAlert: true, 95 | channelKey: 'basic_channel', 96 | title: message.notification?.title ?? "", 97 | body: message.notification?.body ?? "", 98 | fullScreenIntent: true, 99 | payload: {"name": "FlutterCampus"}, 100 | ), 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/firebase/notification_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:isolate'; 2 | import 'dart:ui'; 3 | 4 | import 'package:awesome_notifications/awesome_notifications.dart'; 5 | 6 | import '../../main_prod.dart'; 7 | 8 | class NotificationController { 9 | bool? _initialized; 10 | NotificationController() { 11 | ReceivePort port = ReceivePort(); 12 | IsolateNameServer.registerPortWithName( 13 | port.sendPort, 14 | 'background_notification_action', 15 | ); 16 | 17 | port.listen((var received) async { 18 | logger.w(received); 19 | onSilentActionHandle(received); 20 | }); 21 | _initialized = true; 22 | } 23 | 24 | @pragma("vm:entry-point") 25 | Future onSilentActionHandle(ReceivedAction received) async { 26 | logger.e(_initialized); 27 | if (!_initialized!) { 28 | SendPort? uiSendPort = 29 | IsolateNameServer.lookupPortByName('background_notification_action'); 30 | if (uiSendPort != null) { 31 | uiSendPort.send(received); 32 | return; 33 | } 34 | } 35 | 36 | await _handleBackgroundAction(received); 37 | } 38 | 39 | static Future _handleBackgroundAction(ReceivedAction received) async { 40 | 41 | } 42 | 43 | /// Use this method to detect when a new notification or a schedule is created 44 | @pragma("vm:entry-point") 45 | static Future onNotificationCreatedMethod( 46 | ReceivedNotification receivedNotification) async { 47 | logger.i("onNotificationCreatedMethod"); 48 | } 49 | 50 | /// Use this method to detect every time that a new notification is displayed 51 | @pragma("vm:entry-point") 52 | static Future onNotificationDisplayedMethod( 53 | ReceivedNotification receivedNotification) async { 54 | logger.i("onNotificationDisplayedMethod"); 55 | 56 | // Your code goes here 57 | } 58 | 59 | /// Use this method to detect if the user dismissed a notification 60 | @pragma("vm:entry-point") 61 | static Future onDismissActionReceivedMethod( 62 | ReceivedAction receivedAction) async { 63 | logger.i("onDismissActionReceivedMethod"); 64 | // Your code goes here 65 | } 66 | 67 | /// Use this method to detect when the user taps on a notification or action button 68 | @pragma("vm:entry-point") 69 | static Future onActionReceivedMethod( 70 | ReceivedAction receivedAction, 71 | ) async { 72 | logger.i("onActionReceivedMethod"); 73 | } 74 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/global_app_setup/app_config.dart: -------------------------------------------------------------------------------- 1 | enum Flavor{ 2 | development, 3 | production, 4 | local 5 | } 6 | 7 | 8 | class AppConfig{ 9 | 10 | static Flavor appFlavor = Flavor.production; 11 | static String get baseUrl { 12 | switch(appFlavor){ 13 | case Flavor.development: 14 | return 'http://192.168.1.7:8080'; 15 | case Flavor.production: 16 | return 'http://192.168.1.3:8080'; 17 | case Flavor.local: 18 | return 'http://172.17.240.1:8080'; 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/global_app_setup/global_app_setup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:awesome_notifications/awesome_notifications.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 7 | import '../firebase/notification_controller.dart'; 8 | import '../logger/app_logger.dart'; 9 | import '../service_locator.dart'; 10 | 11 | class GlobalAppSetup with AppLogger { 12 | GlobalAppSetup ({ 13 | required Widget child 14 | }){ 15 | if(kReleaseMode){ 16 | ErrorWidget.builder = (_)=> Container(width: 100, height: 200, color: Colors.red, child: const Center(child: Text("custom error builder"),),); 17 | } 18 | WidgetsFlutterBinding.ensureInitialized(); 19 | 20 | init(child); 21 | 22 | } 23 | 24 | 25 | void init(Widget child) async{ 26 | serviceLocator(); 27 | WidgetsFlutterBinding.ensureInitialized(); 28 | 29 | ///for firebase 30 | // final firebaseConfig = FirebaseConfig(); 31 | // // گرفتن توکن Firebase 32 | // final token = await firebaseConfig.fetchToken(); 33 | // logger.i('Firebase Token: $token'); 34 | // 35 | // // حذف توکن 36 | // final result = await firebaseConfig.deleteFirebaseToken(); 37 | // logger.i('Token Deleted: $result'); 38 | 39 | await ScreenUtil.ensureScreenSize(); 40 | SystemChrome.setPreferredOrientations([ 41 | DeviceOrientation.portraitUp, 42 | DeviceOrientation.portraitDown, 43 | ]); 44 | 45 | runApp(child); 46 | 47 | } 48 | 49 | void _handleFlutterError(FlutterErrorDetails details) { 50 | if(kReleaseMode){ 51 | Zone.current.handleUncaughtError(details.exception, details.stack!); 52 | }else{ 53 | logger.e("error ***************** ${details.exception}"); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/language/language_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import '../secure_storage/secure_storage.dart'; 4 | import 'language_event.dart'; 5 | import 'language_state.dart'; 6 | 7 | class LanguageBloc extends Bloc { 8 | final _storage = SecureStorage(); 9 | static const String _languageKey = 'selected_language'; 10 | 11 | LanguageBloc() : super(LanguageState.initial()) { 12 | on(_onChangeLanguage); 13 | on(_onAddSupportedLocale); 14 | on(_onRemoveSupportedLocale); 15 | 16 | // Load saved language on initialization 17 | _initializeLanguage(); 18 | } 19 | 20 | Future _initializeLanguage() async { 21 | try { 22 | final savedLanguage = await _storage.read(_languageKey); 23 | if (savedLanguage != null) { 24 | add(ChangeLanguage(Locale(savedLanguage))); 25 | } 26 | } catch (e) { 27 | // در صورت خطا در خواندن زبان، از زبان پیش‌فرض استفاده می‌شود 28 | } 29 | } 30 | 31 | Future _onChangeLanguage( 32 | ChangeLanguage event, 33 | Emitter emit, 34 | ) async { 35 | if (!state.supportedLocales.contains(event.locale)) { 36 | return; 37 | } 38 | 39 | emit(state.copyWith(isLoading: true)); 40 | 41 | try { 42 | await _storage.write(_languageKey, event.locale.languageCode); 43 | emit(state.copyWith( 44 | currentLocale: event.locale, 45 | isLoading: false, 46 | )); 47 | } catch (e) { 48 | emit(state.copyWith(isLoading: false)); 49 | } 50 | } 51 | 52 | void _onAddSupportedLocale( 53 | AddSupportedLocale event, 54 | Emitter emit, 55 | ) { 56 | if (state.supportedLocales.contains(event.locale)) { 57 | return; 58 | } 59 | 60 | final updatedLocales = List.from(state.supportedLocales) 61 | ..add(event.locale); 62 | emit(state.copyWith(supportedLocales: updatedLocales)); 63 | } 64 | 65 | void _onRemoveSupportedLocale( 66 | RemoveSupportedLocale event, 67 | Emitter emit, 68 | ) { 69 | if (event.locale == state.currentLocale || 70 | !state.supportedLocales.contains(event.locale)) { 71 | return; 72 | } 73 | 74 | final updatedLocales = List.from(state.supportedLocales) 75 | ..remove(event.locale); 76 | emit(state.copyWith(supportedLocales: updatedLocales)); 77 | } 78 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/language/language_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | abstract class LanguageEvent extends Equatable { 5 | const LanguageEvent(); 6 | 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class ChangeLanguage extends LanguageEvent { 12 | final Locale locale; 13 | 14 | const ChangeLanguage(this.locale); 15 | 16 | @override 17 | List get props => [locale]; 18 | } 19 | 20 | class AddSupportedLocale extends LanguageEvent { 21 | final Locale locale; 22 | 23 | const AddSupportedLocale(this.locale); 24 | 25 | @override 26 | List get props => [locale]; 27 | } 28 | 29 | class RemoveSupportedLocale extends LanguageEvent { 30 | final Locale locale; 31 | 32 | const RemoveSupportedLocale(this.locale); 33 | 34 | @override 35 | List get props => [locale]; 36 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/language/language_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | class LanguageState extends Equatable { 5 | final Locale currentLocale; 6 | final List supportedLocales; 7 | final bool isLoading; 8 | 9 | const LanguageState({ 10 | required this.currentLocale, 11 | required this.supportedLocales, 12 | this.isLoading = false, 13 | }); 14 | 15 | factory LanguageState.initial() { 16 | return LanguageState( 17 | currentLocale: const Locale('fa'), 18 | supportedLocales: const [ 19 | Locale('fa'), 20 | Locale('en'), 21 | ], 22 | isLoading: false, 23 | ); 24 | } 25 | 26 | LanguageState copyWith({ 27 | Locale? currentLocale, 28 | List? supportedLocales, 29 | bool? isLoading, 30 | }) { 31 | return LanguageState( 32 | currentLocale: currentLocale ?? this.currentLocale, 33 | supportedLocales: supportedLocales ?? this.supportedLocales, 34 | isLoading: isLoading ?? this.isLoading, 35 | ); 36 | } 37 | 38 | @override 39 | List get props => [currentLocale, supportedLocales, isLoading]; 40 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/logger/app_logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | mixin AppLogger { 4 | Logger get logger => Logger( 5 | printer: PrettyPrinter( 6 | methodCount: 2, // Number of method calls to be displayed 7 | errorMethodCount: 8, // Number of method calls if stacktrace is provided 8 | lineLength: 120, // Width of the output 9 | colors: true, // Colorful log messages 10 | printEmojis: true, // Print an emoji for each log message 11 | printTime: false // Should each log print contain a timestamp 12 | ), 13 | ); 14 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/navigation/app_navigation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:page_transition/page_transition.dart'; 3 | import '../logger/app_logger.dart'; 4 | import '../../features/splash_feature/presentation/pages/splash_page.dart'; 5 | 6 | class AppNavigator with AppLogger { 7 | static final AppNavigator _instance = AppNavigator._internal(); 8 | factory AppNavigator() => _instance; 9 | AppNavigator._internal(); 10 | 11 | final GlobalKey navigatorKey = GlobalKey(); 12 | final GlobalKey scaffoldMessengerKey = 13 | GlobalKey(); 14 | 15 | NavigatorState get state => navigatorKey.currentState!; 16 | ScaffoldMessengerState get scaffoldState => 17 | scaffoldMessengerKey.currentState!; 18 | 19 | /// TODO : create splash page 20 | final initialRoute = SplashPage.route; 21 | Widget screen = SplashPage(); 22 | 23 | Route? onGenerateRoute(RouteSettings settings) { 24 | switch (settings.name) { 25 | case SplashPage.route: 26 | return PageTransition( 27 | type: PageTransitionType.fade, 28 | duration: const Duration(milliseconds: 200), 29 | child: SplashPage(), 30 | ); 31 | default: 32 | return PageTransition( 33 | type: PageTransitionType.fade, 34 | duration: const Duration(milliseconds: 200), 35 | child: screen, 36 | ); 37 | } 38 | } 39 | 40 | // Basic Navigation Methods 41 | Future to(Widget destination, {String? name}) { 42 | logger.d("Navigate to $destination"); 43 | return state.push( 44 | MaterialPageRoute( 45 | builder: (_) => destination, 46 | settings: RouteSettings(name: name), 47 | ), 48 | ); 49 | } 50 | 51 | Future toNamed(String routeName, {Object? arguments}) { 52 | logger.d("Navigate to named route: $routeName"); 53 | return state.pushNamed(routeName, arguments: arguments); 54 | } 55 | 56 | Future off(Widget destination, {String? name}) { 57 | logger.d("Navigate to $destination and remove previous"); 58 | return state.pushReplacement( 59 | MaterialPageRoute( 60 | builder: (_) => destination, 61 | settings: RouteSettings(name: name), 62 | ), 63 | ); 64 | } 65 | 66 | Future offNamed(String routeName, {Object? arguments}) { 67 | logger.d("Navigate to named route: $routeName and remove previous"); 68 | return state.pushReplacementNamed(routeName, arguments: arguments); 69 | } 70 | 71 | Future offAll(Widget destination, {String? name}) { 72 | logger.d("Navigate to $destination and remove all previous"); 73 | return state.pushAndRemoveUntil( 74 | MaterialPageRoute( 75 | builder: (_) => destination, 76 | settings: RouteSettings(name: name), 77 | ), 78 | (route) => false, 79 | ); 80 | } 81 | 82 | Future offAllNamed(String routeName, {Object? arguments}) { 83 | logger.d("Navigate to named route: $routeName and remove all previous"); 84 | return state.pushNamedAndRemoveUntil( 85 | routeName, 86 | (route) => false, 87 | arguments: arguments, 88 | ); 89 | } 90 | 91 | void back([T? result]) { 92 | logger.d("Navigate back"); 93 | state.pop(result); 94 | } 95 | 96 | bool canPop() { 97 | return state.canPop(); 98 | } 99 | 100 | // Web Navigation Methods 101 | void setUrl(String url) { 102 | logger.d("Set URL to: $url"); 103 | // Implement web URL handling here 104 | } 105 | 106 | String? getCurrentUrl() { 107 | // Implement getting current URL here 108 | return null; 109 | } 110 | 111 | // Snackbar Methods 112 | void showSnackBar(String message, {Duration? duration}) { 113 | scaffoldState.showSnackBar( 114 | SnackBar( 115 | content: Text(message), 116 | duration: duration ?? const Duration(seconds: 2), 117 | ), 118 | ); 119 | } 120 | 121 | // Dialog Methods 122 | Future showDialog({ 123 | required Widget child, 124 | bool barrierDismissible = true, 125 | }) { 126 | return state.push( 127 | PageRouteBuilder( 128 | opaque: false, 129 | barrierDismissible: barrierDismissible, 130 | pageBuilder: (context, animation, secondaryAnimation) => child, 131 | ), 132 | ); 133 | } 134 | 135 | // Bottom Sheet Methods 136 | Future showBottomSheet({ 137 | required Widget child, 138 | bool isDismissible = true, 139 | bool enableDrag = true, 140 | }) { 141 | return state.push( 142 | PageRouteBuilder( 143 | opaque: false, 144 | barrierDismissible: isDismissible, 145 | pageBuilder: (context, animation, secondaryAnimation) => child, 146 | ), 147 | ); 148 | } 149 | } 150 | 151 | class CustomRouteInformation extends RouteInformation { 152 | final String? myProperty; 153 | CustomRouteInformation({ 154 | required this.myProperty, 155 | super.location, 156 | super.state, 157 | }); 158 | } 159 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/params/no_params.dart: -------------------------------------------------------------------------------- 1 | class NoParams{ 2 | NoParams(); 3 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/secure_storage/flutter_secure_storage_const.dart: -------------------------------------------------------------------------------- 1 | const String accessTokeKey = "accessKey"; 2 | const String refreshTokeKey = "refreshKey"; -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/secure_storage/secure_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 2 | import '/core/secure_storage/secure_storage_interface.dart'; 3 | 4 | class SecureStorage implements SecureStorageInterface { 5 | final FlutterSecureStorage _flutterSecureStorage; 6 | 7 | static const _androidOptions = AndroidOptions( 8 | encryptedSharedPreferences: true, 9 | ); 10 | 11 | static const _iosOptions = IOSOptions( 12 | accessibility: KeychainAccessibility.first_unlock, 13 | ); 14 | 15 | static final SecureStorage _instance = SecureStorage._internal(); 16 | 17 | SecureStorage._internal() 18 | : _flutterSecureStorage = const FlutterSecureStorage( 19 | aOptions: _androidOptions, 20 | iOptions: _iosOptions, 21 | ); 22 | 23 | factory SecureStorage() => _instance; 24 | 25 | @override 26 | Future read(String key) async { 27 | try { 28 | return await _flutterSecureStorage.read(key: key); 29 | } catch (e) { 30 | rethrow; 31 | } 32 | } 33 | 34 | @override 35 | Future write(String key, String value) async { 36 | try { 37 | await _flutterSecureStorage.write(key: key, value: value); 38 | } catch (e) { 39 | rethrow; 40 | } 41 | } 42 | 43 | @override 44 | Future delete(String key) async { 45 | try { 46 | await _flutterSecureStorage.delete(key: key); 47 | } catch (e) { 48 | rethrow; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/secure_storage/secure_storage_interface.dart: -------------------------------------------------------------------------------- 1 | abstract interface class SecureStorageInterface { 2 | Future read(String key); 3 | Future write(String key, String value); 4 | Future delete(String key); 5 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/service_locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:get_it/get_it.dart'; 3 | import '../core/navigation/app_navigation.dart'; 4 | GetIt locator = GetIt.instance; 5 | 6 | serviceLocator() async { 7 | locator.registerLazySingleton(() => AppNavigator()); 8 | locator.registerSingleton(Dio()); 9 | 10 | ///############################################## API Provider ############################# 11 | 12 | // locator.registerLazySingleton(() => UserApiProvider()); 13 | 14 | ///############################################## Repository ############################# 15 | 16 | //locator.registerLazySingleton(()=>UserRepositoryImp(userApiProvider: locator())); 17 | 18 | ///############################################## UseCase ############################# 19 | 20 | // locator.registerLazySingleton(()=>GetUserUseCase( userRepository: locator())); 21 | 22 | ///############################################## Bloc ############################# 23 | // locator.registerSingleton(ChatBloc( 24 | // createRoomUseCase: locator(), 25 | // getAllUserUseCase: locator(), 26 | // getOnlineUserUseCase: locator(), 27 | // getConversationUseCase: locator(), 28 | // connectToWebsocketUseCase: locator(), 29 | // getNotReadMessagesUseCase: locator())); 30 | } 31 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/success_response/success_response.dart: -------------------------------------------------------------------------------- 1 | class SuccessResponse { 2 | int? statusCode; 3 | String? message; 4 | SuccessResponse({ 5 | this.message, 6 | this.statusCode 7 | }); 8 | 9 | SuccessResponse.fromJson(dynamic json) { 10 | statusCode = json['statusCode']; 11 | message = json['message']; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/theme/app_theme.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "text_themes.dart"; 3 | import "color_themes.dart"; 4 | 5 | class AppTheme { 6 | static ThemeData lightTheme = ThemeData( 7 | extensions: [ 8 | LightColorThemes.appAdditionalColors, 9 | LightTextThemes.appTextTheme, 10 | ], 11 | splashColor: Colors.transparent, 12 | highlightColor: Colors.transparent, 13 | brightness: Brightness.light, 14 | fontFamily: "IranSans", 15 | primaryColor: Colors.blueGrey, 16 | useMaterial3: true, 17 | iconTheme: IconThemeData(color: Colors.grey[700]), 18 | ); 19 | 20 | static ThemeData darkTheme = ThemeData( 21 | extensions: [ 22 | DarkColorThemes.appAdditionalColors, 23 | DarkTextThemes.appTextTheme, 24 | ], 25 | splashColor: Colors.transparent, 26 | highlightColor: Colors.transparent, 27 | brightness: Brightness.dark, 28 | fontFamily: "IranSans", 29 | primaryColor: Colors.blueGrey, 30 | iconTheme: IconThemeData(color: Colors.grey[700]), 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/theme/bloc/theme_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'theme_event.dart'; 3 | import 'theme_state.dart'; 4 | 5 | class ThemeBloc extends Bloc { 6 | ThemeBloc() : super(ThemeInitial()) { 7 | on((event, emit) { 8 | final currentState = state; 9 | if (currentState is ThemeInitial) { 10 | emit(ThemeChanged(!currentState.isDark)); 11 | } else if (currentState is ThemeChanged) { 12 | emit(ThemeChanged(!currentState.isDark)); 13 | } 14 | }); 15 | 16 | on((event, emit) { 17 | emit(ThemeChanged(event.isDark)); 18 | }); 19 | } 20 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/theme/bloc/theme_event.dart: -------------------------------------------------------------------------------- 1 | abstract class ThemeEvent {} 2 | 3 | class ToggleTheme extends ThemeEvent {} 4 | 5 | class SetTheme extends ThemeEvent { 6 | final bool isDark; 7 | SetTheme(this.isDark); 8 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/theme/bloc/theme_state.dart: -------------------------------------------------------------------------------- 1 | abstract class ThemeState {} 2 | 3 | class ThemeInitial extends ThemeState { 4 | final bool isDark; 5 | ThemeInitial({this.isDark = false}); 6 | } 7 | 8 | class ThemeChanged extends ThemeState { 9 | final bool isDark; 10 | ThemeChanged(this.isDark); 11 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/theme/color_themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | 4 | /// تعریف رنگ‌های اضافی برای حالت روشن 5 | class LightColorThemes { 6 | static final appAdditionalColors = AppColorsTheme( 7 | success50: Color(0xffECFDF3), 8 | success700: Color(0xff027A48), 9 | warningBackground: Color(0xffFFF5DA), 10 | warning: Color(0xffF1A500), 11 | error700: Color(0xffB42318), 12 | error50: Color(0xffFEF3F2), 13 | backgroundtritary: Color(0xffFDFAF3), 14 | lightBackgroundSubded: Color(0xffFBDBDE3), 15 | lightBackgroundSubdedplus: Color(0xffFCF6F8), 16 | secondaryErrorText: Color(0xff9F2626), 17 | ); 18 | } 19 | 20 | /// تعریف رنگ‌های اضافی برای حالت تاریک 21 | class DarkColorThemes { 22 | static final appAdditionalColors = AppColorsTheme( 23 | success50: Color(0xffECFDF3), 24 | success700: Color(0xff027A48), 25 | warningBackground: Color(0xffFFF5DA), 26 | warning: Color(0xffF1A500), 27 | error700: Color(0xffB42318), 28 | error50: Color(0xffFEF3F2), 29 | backgroundtritary: Color(0xffFDFAF3), 30 | lightBackgroundSubded: Color(0xffFBDBDE3), 31 | lightBackgroundSubdedplus: Color(0xffFCF6F8), 32 | secondaryErrorText: Color(0xff9F2626), 33 | ); 34 | } 35 | 36 | 37 | class AppColorsTheme extends ThemeExtension { 38 | const AppColorsTheme({ 39 | required this.success50, 40 | required this.success700, 41 | required this.error50, 42 | required this.error700, 43 | required this.warning, 44 | required this.warningBackground, 45 | required this.backgroundtritary, 46 | required this.lightBackgroundSubdedplus, 47 | required this.lightBackgroundSubded, 48 | required this.secondaryErrorText, 49 | }); 50 | 51 | // رنگ‌های موفقیت 52 | final Color success50; 53 | final Color success700; 54 | 55 | // رنگ‌های خطا 56 | final Color error50; 57 | final Color error700; 58 | final Color secondaryErrorText; 59 | 60 | // رنگ‌های هشدار 61 | final Color warning; 62 | final Color warningBackground; 63 | 64 | // رنگ‌های پس‌زمینه 65 | final Color backgroundtritary; 66 | final Color lightBackgroundSubdedplus; 67 | final Color lightBackgroundSubded; 68 | 69 | @override 70 | ThemeExtension lerp(AppColorsTheme? other, double t) { 71 | if (other is! AppColorsTheme) { 72 | return this; 73 | } 74 | return AppColorsTheme( 75 | success700: Color.lerp(success700, other.success700, t)!, 76 | success50: Color.lerp(success50, other.success50, t)!, 77 | error50: Color.lerp(error50, other.error50, t)!, 78 | error700: Color.lerp(error700, other.error700, t)!, 79 | warning: Color.lerp(warning, other.warning, t)!, 80 | warningBackground: Color.lerp(warningBackground, other.warningBackground, t)!, 81 | backgroundtritary: Color.lerp(backgroundtritary, other.backgroundtritary, t)!, 82 | lightBackgroundSubded: Color.lerp(lightBackgroundSubded, other.lightBackgroundSubded, t)!, 83 | lightBackgroundSubdedplus: Color.lerp(lightBackgroundSubdedplus, other.lightBackgroundSubdedplus, t)!, 84 | secondaryErrorText: Color.lerp(secondaryErrorText, other.secondaryErrorText, t)!, 85 | ); 86 | } 87 | 88 | @override 89 | AppColorsTheme copyWith({ 90 | Color? success50, 91 | Color? success700, 92 | Color? error50, 93 | Color? error700, 94 | Color? warning, 95 | Color? warningBackground, 96 | Color? backgroundtritary, 97 | Color? lightBackgroundSubdedplus, 98 | Color? lightBackgroundSubded, 99 | Color? secondaryErrorText, 100 | }) { 101 | return AppColorsTheme( 102 | success50: success50 ?? this.success50, 103 | success700: success700 ?? this.success700, 104 | error50: error50 ?? this.error50, 105 | error700: error700 ?? this.error700, 106 | warning: warning ?? this.warning, 107 | warningBackground: warningBackground ?? this.warningBackground, 108 | backgroundtritary: backgroundtritary ?? this.backgroundtritary, 109 | lightBackgroundSubdedplus: lightBackgroundSubdedplus ?? this.lightBackgroundSubdedplus, 110 | lightBackgroundSubded: lightBackgroundSubded ?? this.lightBackgroundSubded, 111 | secondaryErrorText: secondaryErrorText ?? this.secondaryErrorText, 112 | ); 113 | } 114 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/theme/text_themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | 4 | import 'color_themes.dart'; 5 | 6 | /// تعریف تم‌های متنی برای حالت روشن 7 | class LightTextThemes { 8 | static final appTextTheme = AppTextTheme( 9 | body1: TextStyle( 10 | fontSize: 14.sp, 11 | color: LightColorThemes.appAdditionalColors.error50, 12 | fontWeight: FontWeight.w400, 13 | ), 14 | h1: TextStyle( 15 | fontSize: 24.sp, 16 | color: LightColorThemes.appAdditionalColors.error50, 17 | fontWeight: FontWeight.bold, 18 | ), 19 | ); 20 | } 21 | 22 | /// تعریف تم‌های متنی برای حالت تاریک 23 | class DarkTextThemes { 24 | static final appTextTheme = AppTextTheme( 25 | body1: TextStyle( 26 | fontSize: 14.sp, 27 | color: DarkColorThemes.appAdditionalColors.error50, 28 | fontWeight: FontWeight.w400, 29 | ), 30 | h1: TextStyle( 31 | fontSize: 24.sp, 32 | color: DarkColorThemes.appAdditionalColors.error50, 33 | fontWeight: FontWeight.bold, 34 | ), 35 | ); 36 | } 37 | 38 | 39 | /// اکستنشن برای مدیریت استایل‌های متنی سفارشی 40 | class AppTextTheme extends ThemeExtension { 41 | const AppTextTheme({ 42 | required this.body1, 43 | required this.h1, 44 | }); 45 | 46 | final TextStyle body1; 47 | final TextStyle h1; 48 | 49 | @override 50 | ThemeExtension copyWith({ 51 | TextStyle? body1, 52 | TextStyle? h1, 53 | }) { 54 | return AppTextTheme( 55 | body1: body1 ?? this.body1, 56 | h1: h1 ?? this.h1, 57 | ); 58 | } 59 | 60 | @override 61 | ThemeExtension lerp( 62 | covariant ThemeExtension? other, 63 | double t, 64 | ) { 65 | if (other is! AppTextTheme) { 66 | return this; 67 | } 68 | 69 | return AppTextTheme( 70 | body1: TextStyle.lerp(body1, other.body1, t)!, 71 | h1: TextStyle.lerp(h1, other.h1, t)!, 72 | ); 73 | } 74 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/theme/theme_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'color_themes.dart'; 3 | import 'text_themes.dart'; 4 | 5 | /// اکستنشن‌های مربوط به ThemeData 6 | extension AppThemeExtension on ThemeData { 7 | /// دسترسی به رنگ‌های سفارشی 8 | /// مثال: Theme.of(context).appColors 9 | AppColorsTheme get appColors => extension() ?? LightColorThemes.appAdditionalColors; 10 | 11 | /// دسترسی به استایل‌های متنی سفارشی 12 | /// مثال: Theme.of(context).appTextTheme 13 | AppTextTheme get appTextTheme => extension() ?? LightTextThemes.appTextTheme; 14 | } 15 | 16 | /// اکستنشن‌های مربوط به BuildContext 17 | extension ThemeGetter on BuildContext { 18 | /// دسترسی به ThemeData 19 | /// مثال: context.theme 20 | ThemeData get theme => Theme.of(this); 21 | 22 | /// دسترسی به رنگ‌های سفارشی 23 | /// مثال: context.appColors 24 | AppColorsTheme get appColors => theme.appColors; 25 | 26 | /// دسترسی به استایل‌های متنی سفارشی 27 | /// مثال: context.appTextTheme 28 | AppTextTheme get appTextTheme => theme.appTextTheme; 29 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/use_case/base_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../error/failures.dart'; 3 | 4 | abstract class BaseUseCase { 5 | Future> call(InputParams params); 6 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/utils/extensions/context_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension ContextExtensions on BuildContext { 4 | // Theme Extensions 5 | ThemeData get theme => Theme.of(this); 6 | TextTheme get textTheme => theme.textTheme; 7 | ColorScheme get colorScheme => theme.colorScheme; 8 | 9 | // Media Query Extensions 10 | MediaQueryData get mediaQuery => MediaQuery.of(this); 11 | Size get size => mediaQuery.size; 12 | double get height => size.height; 13 | double get width => size.width; 14 | double get statusBarHeight => mediaQuery.padding.top; 15 | double get bottomBarHeight => mediaQuery.padding.bottom; 16 | 17 | // Navigation Extensions 18 | NavigatorState get navigator => Navigator.of(this); 19 | 20 | // Responsive Extensions 21 | bool get isMobile => width < 600; 22 | bool get isTablet => width >= 600 && width < 900; 23 | bool get isDesktop => width >= 900; 24 | 25 | // Screen Size Extensions 26 | bool get isSmallScreen => width < 400; 27 | bool get isMediumScreen => width >= 400 && width < 800; 28 | bool get isLargeScreen => width >= 800; 29 | 30 | // Orientation Extensions 31 | bool get isPortrait => height > width; 32 | bool get isLandscape => width > height; 33 | 34 | // Device Type Extensions 35 | bool get isPhone => height < 600; 36 | bool get isTabletDevice => height >= 600 && height < 900; 37 | bool get isDesktopDevice => height >= 900; 38 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/utils/extensions/date_extentions.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | import 'package:persian_datetime_picker/persian_datetime_picker.dart' as datePicker; 3 | 4 | extension CustomDateExtensions on DateTime { 5 | 6 | datePicker.Jalali get toJalaliDateTime{ 7 | return datePicker.Jalali.fromDateTime(this); 8 | } 9 | 10 | int get daysInMonth { 11 | return DateTime(year, month + 1, 0).day; 12 | } 13 | 14 | String? weekdayName() { 15 | const Map weekdayName = { 16 | 1: "Monday", 17 | 2: "Tuesday", 18 | 3: "Wednesday", 19 | 4: "Thursday", 20 | 5: "Friday", 21 | 6: "Saturday", 22 | 7: "Sunday" 23 | }; 24 | return weekdayName[weekday]; 25 | } 26 | 27 | DateTime toNoonUTC() { 28 | // Set time to 12:00 noon UTC 29 | return DateTime.utc(year, month, day, 12, 0, 0, 0, 0); 30 | } 31 | 32 | String getTime() { 33 | // Format time as HH:mm 34 | return "${hour.toString().padLeft(2, "0")}:${minute.toString().padLeft(2, "0")}"; 35 | } 36 | 37 | String toJalaliFullDate() { 38 | // Convert to Jalali and format as full date 39 | return datePicker.Jalali.fromDateTime(this).formatFullDate(); 40 | } 41 | 42 | String toJalaliCompactDate() { 43 | // Convert to Jalali and format as compact date 44 | return datePicker.Jalali.fromDateTime(this).formatCompactDate(); 45 | } 46 | 47 | String toJalaliShortDate() { 48 | // Convert to Jalali and format as short date 49 | return datePicker.Jalali.fromDateTime(this).formatShortDate(); 50 | } 51 | 52 | String toJalaliMediumDate() { 53 | // Convert to Jalali and format as medium date 54 | return datePicker.Jalali.fromDateTime(this).formatMediumDate(); 55 | } 56 | } 57 | 58 | extension DateExtension on String? { 59 | DateTime? get toDateTime { 60 | if (this == null) return null; 61 | return DateTime.tryParse(this!); 62 | } 63 | 64 | String get timeFormat { 65 | if (this == null) return ""; 66 | final dateTime = DateTime.tryParse(this!); 67 | return dateTime != null 68 | ? DateFormat(DateFormat.HOUR_MINUTE_TZ).format(dateTime) 69 | : ""; 70 | } 71 | 72 | String get abbrWeekDay { 73 | if (this == null) return ""; 74 | final dateTime = DateTime.tryParse(this!); 75 | return dateTime != null 76 | ? DateFormat(DateFormat.ABBR_WEEKDAY).format(dateTime) 77 | : ""; 78 | } 79 | 80 | String get YEAR_ABBR_MONTH_DAY { 81 | if (this == null) return ""; 82 | final dateTime = DateTime.tryParse(this!); 83 | return dateTime != null 84 | ? DateFormat(DateFormat.YEAR_ABBR_MONTH_DAY).format(dateTime) 85 | : ""; 86 | } 87 | 88 | String get ABBR_MONTH_WEEKDAY_DAY { 89 | if (this == null) return ""; 90 | final dateTime = DateTime.tryParse(this!); 91 | return dateTime != null 92 | ? DateFormat(DateFormat.ABBR_MONTH_WEEKDAY_DAY).format(dateTime) 93 | : ""; 94 | } 95 | 96 | String formatWithTime() { 97 | if (this == null) return ""; 98 | final dateTime = DateTime.tryParse(this!); 99 | if (dateTime == null) return ""; 100 | return "${DateFormat("yyyy-MM-dd").format(dateTime)} " 101 | "${dateTime.hour.toString().padLeft(2, "0")}:${dateTime.minute.toString().padLeft(2, "0")}"; 102 | } 103 | 104 | String get toJalaliFullDate { 105 | if (this == null) return ""; 106 | final dateTime = DateTime.tryParse(this!); 107 | if (dateTime == null) return ""; 108 | return dateTime.toJalaliFullDate(); 109 | } 110 | 111 | String get toJalaliCompactDate { 112 | if (this == null) return ""; 113 | final dateTime = DateTime.tryParse(this!); 114 | if (dateTime == null) return ""; 115 | return dateTime.toJalaliCompactDate().replaceAll("/", "-"); 116 | } 117 | 118 | String get toJalaliShortDate { 119 | if (this == null) return ""; 120 | final dateTime = DateTime.tryParse(this!); 121 | if (dateTime == null) return ""; 122 | return dateTime.toJalaliShortDate(); 123 | } 124 | 125 | String get toJalaliMediumDate { 126 | if (this == null) return ""; 127 | final dateTime = DateTime.tryParse(this!); 128 | if (dateTime == null) return ""; 129 | return dateTime.toJalaliMediumDate(); 130 | } 131 | 132 | String get toJalaliMediumDateWithTime { 133 | if (this == null) return ""; 134 | final dateTime = DateTime.tryParse(this!); 135 | if (dateTime == null) return ""; 136 | return "${dateTime.toJalaliMediumDate()} ${dateTime.getTime()}"; 137 | } 138 | 139 | String calculateDifference(String endDate) { 140 | if (this == null || endDate.isEmpty) return ""; 141 | 142 | final dateTime1 = DateTime.tryParse(this!); 143 | final dateTime2 = DateTime.tryParse(endDate); 144 | if (dateTime1 == null || dateTime2 == null) return ""; 145 | 146 | final difference = dateTime2.difference(dateTime1); 147 | 148 | final days = difference.inDays; 149 | final hours = difference.inHours % 24; 150 | final minutes = difference.inMinutes % 60; 151 | 152 | final parts = [ 153 | if (days > 0) "${days}d", 154 | if (hours > 0) "${hours}h", 155 | if (minutes > 0) "${minutes}m" 156 | ]; 157 | 158 | return parts.join(" "); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/utils/extensions/file_extension.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:convert'; 3 | import 'package:dio/dio.dart'; 4 | import 'package:path/path.dart' as p; 5 | import 'package:http_parser/http_parser.dart'; 6 | 7 | /// اکستنشن جامع برای کار با فایل 8 | extension FileExtension on File { 9 | /// دریافت سایز فایل به بایت 10 | Future getFileSize() async { 11 | try { 12 | return await length(); 13 | } catch (e) { 14 | rethrow; 15 | } 16 | } 17 | 18 | /// دریافت سایز فایل به مگابایت 19 | Future getFileSizeInMB() async { 20 | final bytes = await getFileSize(); 21 | return bytes / (1024 * 1024); 22 | } 23 | 24 | /// تبدیل فایل به MultipartFile جهت ارسال به سمت سرور با استفاده از dio 25 | /// 26 | /// فقط فایل ورودی لازم است، دیگر نیازی به تعیین نام فایل و نوع MIME نیست. 27 | Future toMultipartFile() async { 28 | try { 29 | final bytes = await readAsBytes(); 30 | final name = p.basename(path); 31 | // MediaType به صورت خودکار بر اساس پسوند فایل انتخاب می‌شود 32 | final contentType = _getMediaTypeFromExtension(name); 33 | 34 | return MultipartFile.fromBytes( 35 | bytes, 36 | filename: name, 37 | contentType: contentType, 38 | ); 39 | } catch (e) { 40 | rethrow; 41 | } 42 | } 43 | 44 | /// دریافت نوع MIME از پسوند فایل 45 | MediaType _getMediaTypeFromExtension(String fileName) { 46 | final extension = p.extension(fileName).toLowerCase(); 47 | 48 | switch (extension) { 49 | case '.jpg': 50 | case '.jpeg': 51 | return MediaType('image', 'jpeg'); 52 | case '.png': 53 | return MediaType('image', 'png'); 54 | case '.gif': 55 | return MediaType('image', 'gif'); 56 | case '.pdf': 57 | return MediaType('application', 'pdf'); 58 | case '.txt': 59 | return MediaType('text', 'plain'); 60 | case '.json': 61 | return MediaType('application', 'json'); 62 | case '.html': 63 | return MediaType('text', 'html'); 64 | // افزودن انواع MIME دیگر طبق نیاز 65 | default: 66 | return MediaType('application', 'octet-stream'); 67 | } 68 | } 69 | 70 | /// خواندن محتویات فایل به صورت رشته 71 | Future readContentAsString({Encoding encoding = utf8}) async { 72 | try { 73 | return await readAsString(encoding: encoding); 74 | } catch (e) { 75 | rethrow; 76 | } 77 | } 78 | 79 | /// خواندن محتویات فایل به صورت بایت 80 | Future> readContentAsBytes() async { 81 | try { 82 | return await readAsBytes(); 83 | } catch (e) { 84 | rethrow; 85 | } 86 | } 87 | 88 | /// دریافت پسوند فایل (به عنوان مثال: .jpg, .png, .txt) 89 | String get fileExtension { 90 | return p.extension(path); 91 | } 92 | 93 | /// حذف فایل در صورت وجود 94 | Future deleteFileIfExists() async { 95 | if (await exists()) { 96 | await delete(); 97 | } 98 | } 99 | 100 | /// نوشتن داده‌ها به فایل به صورت بایت 101 | Future writeContent( 102 | List bytes, { 103 | FileMode mode = FileMode.write, 104 | }) async { 105 | try { 106 | return await writeAsBytes(bytes, mode: mode); 107 | } catch (e) { 108 | rethrow; 109 | } 110 | } 111 | 112 | /// نوشتن رشته به فایل 113 | Future writeStringContent( 114 | String content, { 115 | FileMode mode = FileMode.write, 116 | Encoding encoding = utf8, 117 | }) async { 118 | try { 119 | return await writeAsString(content, mode: mode, encoding: encoding); 120 | } catch (e) { 121 | rethrow; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/core/utils/extensions/string_extensions.dart: -------------------------------------------------------------------------------- 1 | extension StringExtensions on String { 2 | // Validation Extensions 3 | bool get isEmail => RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(this); 4 | bool get isPhoneNumber => RegExp(r'^\+?[\d\s-]{10,}$').hasMatch(this); 5 | bool get isURL => RegExp(r'^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$').hasMatch(this); 6 | 7 | // Formatting Extensions 8 | String get capitalize => '${this[0].toUpperCase()}${substring(1)}'; 9 | String get titleCase => split(' ').map((word) => word.capitalize).join(' '); 10 | String get camelCase => split(' ').map((word) => word.capitalize).join(''); 11 | 12 | // Trimming Extensions 13 | String get trimAll => replaceAll(RegExp(r'\s+'), ''); 14 | String get trimStart => replaceFirst(RegExp(r'^\s+'), ''); 15 | String get trimEnd => replaceFirst(RegExp(r'\s+$'), ''); 16 | 17 | // Length Extensions 18 | bool get isEmptyOrNull => isEmpty || this == 'null'; 19 | bool get isNotEmptyOrNull => !isEmptyOrNull; 20 | 21 | // Number Extensions 22 | int? get toInt => int.tryParse(this); 23 | double? get toDouble => double.tryParse(this); 24 | bool get isNumeric => double.tryParse(this) != null; 25 | 26 | // Date Extensions 27 | DateTime? get toDateTime => DateTime.tryParse(this); 28 | 29 | // HTML Extensions 30 | String get removeHtmlTags => replaceAll(RegExp(r'<[^>]*>'), ''); 31 | 32 | // Password Strength Extensions 33 | bool get hasUpperCase => contains(RegExp(r'[A-Z]')); 34 | bool get hasLowerCase => contains(RegExp(r'[a-z]')); 35 | bool get hasNumbers => contains(RegExp(r'[0-9]')); 36 | bool get hasSpecialCharacters => contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]')); 37 | 38 | // Persian Number Extensions 39 | String get toPersianNumber { 40 | const english = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; 41 | const persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; 42 | String result = this; 43 | for (int i = 0; i < english.length; i++) { 44 | result = result.replaceAll(english[i], persian[i]); 45 | } 46 | return result; 47 | } 48 | 49 | // RTL Extensions 50 | bool get isRTL => RegExp(r'[\u0591-\u07FF\u200F\u202B\u202E\uFB1D-\uFDFD\uFE70-\uFEFC]').hasMatch(this); 51 | 52 | // Currency Extensions 53 | String toCurrency({String symbol = 'ریال'}) { 54 | if (!isNumeric) return this; 55 | final number = double.parse(this); 56 | return '${number.toStringAsFixed(0).replaceAllMapped( 57 | RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), 58 | (Match m) => '${m[1]},', 59 | )} $symbol'; 60 | } 61 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/features/splash_feature/data/data_sources/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/base/__brick__/lib/features/splash_feature/data/data_sources/.gitkeep -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/features/splash_feature/data/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/base/__brick__/lib/features/splash_feature/data/models/.gitkeep -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/features/splash_feature/data/repositories/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/base/__brick__/lib/features/splash_feature/data/repositories/.gitkeep -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/features/splash_feature/domain/entities/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/base/__brick__/lib/features/splash_feature/domain/entities/.gitkeep -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/features/splash_feature/domain/repositories/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/base/__brick__/lib/features/splash_feature/domain/repositories/.gitkeep -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/features/splash_feature/domain/use_cases/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/base/__brick__/lib/features/splash_feature/domain/use_cases/.gitkeep -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/features/splash_feature/presentation/manager/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/base/__brick__/lib/features/splash_feature/presentation/manager/.gitkeep -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/features/splash_feature/presentation/pages/splash_page.dart: -------------------------------------------------------------------------------- 1 | import '../../../../library.dart'; 2 | 3 | class SplashPage extends StatelessWidget { 4 | const SplashPage({super.key}); 5 | static const route = '/'; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | AppLocalizations localizations = AppLocalizations.of(context)!; 10 | return Scaffold( 11 | body: Center( 12 | child: Text(localizations.hello), 13 | ), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/features/splash_feature/presentation/widgets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/base/__brick__/lib/features/splash_feature/presentation/widgets/.gitkeep -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/l10n/intl_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Hello", 3 | "@hello": {} 4 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/l10n/intl_fa.arb: -------------------------------------------------------------------------------- 1 | { 2 | "hello" : "سلام" 3 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/library.dart: -------------------------------------------------------------------------------- 1 | 2 | library tmanagix; 3 | 4 | export 'package:flutter/material.dart'; 5 | export 'package:flutter_bloc/flutter_bloc.dart'; 6 | export 'package:flutter_gen/gen_l10n/app_localizations.dart'; 7 | export 'package:flutter_screenutil/flutter_screenutil.dart'; 8 | export '../core/navigation/app_navigation.dart'; 9 | export '../core/service_locator.dart'; 10 | export '../core/theme/theme_extensions.dart'; 11 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/main_dev.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'core/global_app_setup/app_config.dart'; 3 | import 'core/global_app_setup/global_app_setup.dart'; 4 | import 'my_app.dart'; 5 | 6 | Logger logger = Logger(); 7 | 8 | void main() async { 9 | AppConfig.appFlavor = Flavor.development; 10 | GlobalAppSetup (child: MyApp()); 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/main_local.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'core/global_app_setup/app_config.dart'; 3 | import 'core/global_app_setup/global_app_setup.dart'; 4 | import 'my_app.dart'; 5 | 6 | Logger logger = Logger(); 7 | 8 | void main() async { 9 | AppConfig.appFlavor = Flavor.local; 10 | GlobalAppSetup (child: MyApp()); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/main_prod.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'core/global_app_setup/app_config.dart'; 3 | import 'core/global_app_setup/global_app_setup.dart'; 4 | import 'my_app.dart'; 5 | 6 | Logger logger = Logger(); 7 | 8 | void main() async { 9 | AppConfig.appFlavor = Flavor.production; 10 | GlobalAppSetup (child: MyApp()); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /bricks/base/__brick__/lib/my_app.dart: -------------------------------------------------------------------------------- 1 | import 'core/language/language_state.dart'; 2 | import 'library.dart'; 3 | import '/../core/theme/app_theme.dart'; 4 | import '/../core/theme/bloc/theme_bloc.dart'; 5 | import '/../core/theme/bloc/theme_state.dart'; 6 | import '/../core/language/language_bloc.dart'; 7 | 8 | class MyApp extends StatelessWidget { 9 | MyApp({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return MultiBlocProvider( 14 | providers: [ 15 | BlocProvider(create: (context) => ThemeBloc()), 16 | BlocProvider(create: (context) => LanguageBloc()), 17 | ], 18 | child: BlocBuilder( 19 | builder: (context, themeState) { 20 | final isDark = themeState is ThemeChanged ? themeState.isDark : false; 21 | return BlocBuilder( 22 | builder: (context, languageState) { 23 | return SafeArea( 24 | child: ScreenUtilInit( 25 | designSize: const Size(390, (844 + 33.5)), 26 | minTextAdapt: true, 27 | splitScreenMode: false, 28 | useInheritedMediaQuery: true, 29 | builder: (context, child) { 30 | return MaterialApp( 31 | supportedLocales: languageState.supportedLocales, 32 | localizationsDelegates: AppLocalizations.localizationsDelegates, 33 | locale: languageState.currentLocale, 34 | debugShowCheckedModeBanner: false, 35 | theme: AppTheme.lightTheme, 36 | darkTheme: AppTheme.darkTheme, 37 | themeMode: isDark ? ThemeMode.dark : ThemeMode.light, 38 | navigatorKey: AppNavigator().navigatorKey, 39 | scaffoldMessengerKey: AppNavigator().scaffoldMessengerKey, 40 | onGenerateRoute: AppNavigator().onGenerateRoute, 41 | initialRoute: AppNavigator().initialRoute, 42 | ); 43 | }, 44 | ), 45 | ); 46 | }, 47 | ); 48 | }, 49 | ), 50 | ); 51 | } 52 | } -------------------------------------------------------------------------------- /bricks/base/__brick__/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: {{name}} 2 | description: "A new Flutter project." 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | version: 1.0.0+1 6 | 7 | environment: 8 | sdk: ^3.7.2 9 | 10 | # Dependencies specify other packages that your package needs in order to work. 11 | # To automatically upgrade your package dependencies to the latest versions 12 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 13 | # dependencies can be manually updated by changing the version numbers below to 14 | # the latest version available on pub.dev. To see which dependencies have newer 15 | # versions available, run `flutter pub outdated`. 16 | dependencies: 17 | flutter: 18 | sdk: flutter 19 | flutter_localizations: 20 | sdk: flutter 21 | intl: ^0.19.0 22 | cupertino_icons: ^1.0.8 23 | flutter_bloc: ^9.1.1 24 | dio: ^5.8.0+1 25 | pretty_dio_logger: ^1.4.0 26 | firebase_messaging: ^15.2.5 27 | firebase_core: ^3.13.0 28 | awesome_notifications: ^0.10.1 29 | logger: ^2.5.0 30 | flutter_screenutil: ^5.9.3 31 | dartz: ^0.10.1 32 | page_transition: ^2.2.1 33 | equatable: ^2.0.7 34 | get_it: ^8.0.3 35 | gap: ^3.0.1 36 | persian_datetime_picker: ^3.1.0 37 | shamsi_date: ^1.1.0 38 | package_info_plus: ^8.3.0 39 | flutter_secure_storage: ^9.2.4 40 | internet_connection_checker: ^3.0.1 41 | 42 | dependency_overrides: 43 | intl: ^0.20.0 44 | 45 | dev_dependencies: 46 | flutter_test: 47 | sdk: flutter 48 | build_runner: ^2.4.14 49 | 50 | # The "flutter_lints" package below contains a set of recommended lints to 51 | # encourage good coding practices. The lint set provided by the package is 52 | # activated in the `analysis_options.yaml` file located at the root of your 53 | # package. See that file for information about deactivating specific lint 54 | # rules and activating additional ones. 55 | flutter_lints: ^5.0.0 56 | 57 | # For information on the generic Dart part of this file, see the 58 | # following page: https://dart.dev/tools/pub/pubspec 59 | 60 | # The following section is specific to Flutter packages. 61 | flutter: 62 | 63 | # The following line ensures that the Material Icons font is 64 | # included with your application, so that you can use the icons in 65 | # the material Icons class. 66 | uses-material-design: true 67 | generate: true 68 | # To add assets to your application, add an assets section, like this: 69 | # assets: 70 | # - images/a_dot_burr.jpeg 71 | # - images/a_dot_ham.jpeg 72 | 73 | # An image asset can refer to one or more resolution-specific "variants", see 74 | # https://flutter.dev/to/resolution-aware-images 75 | 76 | # For details regarding adding assets from package dependencies, see 77 | # https://flutter.dev/to/asset-from-package 78 | 79 | # To add custom fonts to your application, add a fonts section here, 80 | # in this "flutter" section. Each entry in this list should have a 81 | # "family" key with the font family name, and a "fonts" key with a 82 | # list giving the asset and other descriptors for the font. For 83 | # example: 84 | # fonts: 85 | # - family: Schyler 86 | # fonts: 87 | # - asset: fonts/Schyler-Regular.ttf 88 | # - asset: fonts/Schyler-Italic.ttf 89 | # style: italic 90 | # - family: Trajan Pro 91 | # fonts: 92 | # - asset: fonts/TrajanPro.ttf 93 | # - asset: fonts/TrajanPro_Bold.ttf 94 | # weight: 700 95 | # 96 | # For details regarding fonts from package dependencies, 97 | # see https://flutter.dev/to/font-from-package 98 | assets: 99 | - lib/l10n/intl_en.arb 100 | - lib/l10n/intl_fa.arb -------------------------------------------------------------------------------- /bricks/base/brick.yaml: -------------------------------------------------------------------------------- 1 | name: base 2 | description: A new brick created with the Mason CLI. 3 | 4 | # The following defines the brick repository url. 5 | # Uncomment and update the following line before publishing the brick. 6 | # repository: https://github.com/my_org/my_repo 7 | 8 | # The following defines the version and build number for your brick. 9 | # A version number is three numbers separated by dots, like 1.2.34 10 | # followed by an optional build number (separated by a +). 11 | version: 0.1.0+1 12 | 13 | # The following defines the environment for the current brick. 14 | # It includes the version of mason that the brick requires. 15 | environment: 16 | mason: ^0.1.0 17 | vars: 18 | name: 19 | type: string 20 | description: | 21 | نام پروژه Flutter شما که در فایل `pubspec.yaml` تعریف شده است. این نام در بسیاری از قسمت‌های پروژه، مانند شناسه پروژه، نام بسته (package) و موارد دیگر استفاده می‌شود. به طور پیش‌فرض، این متغیر به عنوان نام پروژه‌ای با مقدار `my_template` تنظیم شده است. 22 | default: my_template 23 | prompt: What is your project name? 24 | 25 | -------------------------------------------------------------------------------- /bricks/feature/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.0+1 2 | 3 | - TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /bricks/feature/LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /bricks/feature/README.md: -------------------------------------------------------------------------------- 1 | # bloc_classes 2 | 3 | [![Powered by Mason](https://img.shields.io/endpoint?url=https%3A%2F%2Ftinyurl.com%2Fmason-badge)](https://github.com/felangel/mason) 4 | 5 | A new brick created with the Mason CLI. 6 | 7 | _Generated by [mason][1] 🧱_ 8 | 9 | ## Getting Started 🚀 10 | 11 | This is a starting point for a new brick. 12 | A few resources to get you started if this is your first brick template: 13 | 14 | - [Official Mason Documentation][2] 15 | - [Code generation with Mason Blog][3] 16 | - [Very Good Livestream: Felix Angelov Demos Mason][4] 17 | - [Flutter Package of the Week: Mason][5] 18 | - [Observable Flutter: Building a Mason brick][6] 19 | - [Meet Mason: Flutter Vikings 2022][7] 20 | 21 | [1]: https://github.com/felangel/mason 22 | [2]: https://docs.brickhub.dev 23 | [3]: https://verygood.ventures/blog/code-generation-with-mason 24 | [4]: https://youtu.be/G4PTjA6tpTU 25 | [5]: https://youtu.be/qjA0JFiPMnQ 26 | [6]: https://youtu.be/o8B1EfcUisw 27 | [7]: https://youtu.be/LXhgiF5HiQg 28 | -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/data/data_sources/{{name}}_api_provider.dart: -------------------------------------------------------------------------------- 1 | import '../../../../core/data/network/api_provider.dart'; 2 | 3 | class {{name.pascalCase()}}ApiProvider { 4 | 5 | Future call{{useCase.pascalCase()}}Route() async{ 6 | final res = await ApiProvider.instance.get('/api/v1/project/get_suggestion_projects'); 7 | return res ; 8 | } 9 | 10 | } -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/data/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/feature/__brick__/lib/features/{{name}}_feature/data/models/.gitkeep -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/data/repositories/{{name}}_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | import '../../../../core/error/error_handler.dart'; 4 | import '../../../../core/success_response/success_response.dart'; 5 | import '../../domain/repositories/{{name}}_repository.dart'; 6 | import '../data_sources/{{name}}_api_provider.dart'; 7 | import '../../../../core/error/failures.dart'; 8 | class {{name.pascalCase()}}RepositoryImpl extends {{name.pascalCase()}}Repository{ 9 | {{name.pascalCase()}}ApiProvider {{name.camelCase()}}ApiProvider; 10 | {{name.pascalCase()}}RepositoryImpl({required this.{{name.camelCase()}}ApiProvider}); 11 | @override 12 | Future> {{useCase.camelCase()}}() async{ 13 | try { 14 | final response = await {{name.camelCase()}}ApiProvider.call{{useCase.pascalCase()}}Route(); 15 | return Right(SuccessResponse()); 16 | } on DioException catch (error) { 17 | Failure failure = ErrorHandler.handleError(error); 18 | return Left(failure); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/domain/entities/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/feature/__brick__/lib/features/{{name}}_feature/domain/entities/.gitkeep -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/domain/repositories/{{name}}_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../../core/success_response/success_response.dart'; 3 | import '../../../../core/error/failures.dart'; 4 | abstract class {{name.pascalCase()}}Repository { 5 | Future> {{useCase.camelCase()}}(); 6 | } -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/domain/use_cases/{{useCase}}_use_case.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../../core/success_response/success_response.dart'; 3 | import '../../../../core/use_case/base_usecase.dart'; 4 | import '../repositories/{{name}}_repository.dart'; 5 | import '../../../../core/error/failures.dart'; 6 | class {{useCase.pascalCase()}}UseCase extends BaseUseCase{ 7 | 8 | {{name.pascalCase()}}Repository {{name.camelCase()}}Repository; 9 | {{useCase.pascalCase()}}UseCase({required this.{{name.camelCase()}}Repository }); 10 | @override 11 | Future> call(void params)async{ 12 | return await {{name.camelCase()}}Repository.{{useCase.camelCase()}}(); 13 | } 14 | } -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/presentation/manager/status/{{useCase}}_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../../../../../core/error/failures.dart'; 3 | abstract class {{useCase.pascalCase()}}Status extends Equatable{} 4 | 5 | class {{useCase.pascalCase()}}Init extends {{useCase.pascalCase()}}Status{ 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class {{useCase.pascalCase()}}Loading extends {{useCase.pascalCase()}}Status{ 11 | @override 12 | List get props => []; 13 | } 14 | 15 | 16 | class {{useCase.pascalCase()}}Completed extends {{useCase.pascalCase()}}Status{ 17 | @override 18 | List get props => []; 19 | } 20 | 21 | class {{useCase.pascalCase()}}Error extends {{useCase.pascalCase()}}Status{ 22 | final Failure failure; 23 | {{useCase.pascalCase()}}Error({required this.failure}); 24 | @override 25 | List get props => [failure]; 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/presentation/manager/{{name}}_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:dartz/dartz.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | import '../../../../core/error/failures.dart'; 5 | import '../../../../core/success_response/success_response.dart'; 6 | import '../../presentation/manager/status/{{useCase}}_status.dart'; 7 | import '../../domain/use_cases/{{useCase}}_use_case.dart'; 8 | import '/../core/params/no_params.dart'; 9 | 10 | part '{{name}}_event.dart'; 11 | part '{{name}}_state.dart'; 12 | 13 | class {{name.pascalCase()}}Bloc extends Bloc<{{name.pascalCase()}}Event, {{name.pascalCase()}}State> { 14 | {{useCase.pascalCase()}}UseCase {{useCase.camelCase()}}UseCase; 15 | {{name.pascalCase()}}Bloc({required this.{{useCase.camelCase()}}UseCase}) : super({{name.pascalCase()}}State( 16 | {{useCase.camelCase()}}Status: 17 | {{useCase.pascalCase()}}Init(), 18 | )) { 19 | 20 | on<{{useCase.pascalCase()}}>((event, emit) async{ 21 | 22 | emit(state.copyWith(new{{useCase.pascalCase()}}Status: {{useCase.pascalCase()}}Loading())); 23 | 24 | Either result = await {{useCase.camelCase()}}UseCase(NoParams()); 25 | 26 | result.fold((l){ 27 | emit(state.copyWith(new{{useCase.pascalCase()}}Status: {{useCase.pascalCase()}}Error(failure: l))); 28 | },(r){ 29 | emit(state.copyWith(new{{useCase.pascalCase()}}Status: {{useCase.pascalCase()}}Completed())); 30 | }); 31 | 32 | }); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/presentation/manager/{{name}}_event.dart: -------------------------------------------------------------------------------- 1 | part of '{{name}}_bloc.dart'; 2 | 3 | abstract class {{name.pascalCase()}}Event extends Equatable {} 4 | 5 | class {{useCase.pascalCase()}} extends {{name.pascalCase()}}Event{ 6 | @override 7 | List get props => []; 8 | } -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/presentation/manager/{{name}}_state.dart: -------------------------------------------------------------------------------- 1 | part of '{{name}}_bloc.dart'; 2 | 3 | 4 | class {{name.pascalCase()}}State { 5 | 6 | final {{useCase.pascalCase()}}Status {{useCase.camelCase()}}Status; 7 | 8 | const {{name.pascalCase()}}State({required this.{{useCase.camelCase()}}Status}); 9 | 10 | {{name.pascalCase()}}State copyWith({{{useCase.pascalCase()}}Status? new{{useCase.pascalCase()}}Status}){ 11 | return {{name.pascalCase()}}State( 12 | {{useCase.camelCase()}}Status : new{{useCase.pascalCase()}}Status ?? {{useCase.camelCase()}}Status, 13 | ); 14 | } 15 | } -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/presentation/pages/{{name}}_page.dart: -------------------------------------------------------------------------------- 1 | import '../../../../library.dart'; 2 | import '../manager/{{name}}_bloc.dart'; 3 | import '../manager/status/{{useCase}}_status.dart'; 4 | class {{name.pascalCase()}}Page extends StatelessWidget { 5 | const {{name.pascalCase()}}Page({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return BlocProvider( 10 | create: (context) => {{name.pascalCase()}}Bloc({{useCase.camelCase()}}UseCase: locator())..add({{useCase.pascalCase()}}()), 11 | child: Builder( 12 | builder: (context) { 13 | return Scaffold( 14 | body: BlocBuilder<{{name.pascalCase()}}Bloc , {{name.pascalCase()}}State>( 15 | builder: (context , state){ 16 | if(state.{{useCase.camelCase()}}Status is {{useCase.pascalCase()}}Loading){ 17 | return CircularProgressIndicator(); 18 | } 19 | 20 | if(state.{{useCase.camelCase()}}Status is {{useCase.pascalCase()}}Completed){ 21 | {{useCase.pascalCase()}}Completed {{useCase.camelCase()}}Completed = state.{{useCase.camelCase()}}Status as {{useCase.pascalCase()}}Completed; 22 | return SizedBox(); 23 | } 24 | 25 | if(state.{{useCase.camelCase()}}Status is {{useCase.pascalCase()}}Error){ 26 | {{useCase.pascalCase()}}Error {{useCase.camelCase()}}Error = state.{{useCase.camelCase()}}Status as {{useCase.pascalCase()}}Error; 27 | return SizedBox(); 28 | } 29 | return SizedBox(); 30 | 31 | }), 32 | ); 33 | }), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bricks/feature/__brick__/lib/features/{{name}}_feature/presentation/widgets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/bricks/feature/__brick__/lib/features/{{name}}_feature/presentation/widgets/.gitkeep -------------------------------------------------------------------------------- /bricks/feature/brick.yaml: -------------------------------------------------------------------------------- 1 | name: feature 2 | description: A new brick created with the Mason CLI. 3 | 4 | # The following defines the brick repository url. 5 | # Uncomment and update the following line before publishing the brick. 6 | # repository: https://github.com/my_org/my_repo 7 | 8 | # The following defines the version and build number for your brick. 9 | # A version number is three numbers separated by dots, like 1.2.34 10 | # followed by an optional build number (separated by a +). 11 | version: 0.1.0+1 12 | 13 | # The following defines the environment for the current brick. 14 | # It includes the version of mason that the brick requires. 15 | environment: 16 | mason: ^0.1.0 17 | 18 | # Variables specify dynamic values that your brick depends on. 19 | # Zero or more variables can be specified for a given brick. 20 | # Each variable has: 21 | # * a type (string, number, boolean, enum, array, or list) 22 | # * an optional short description 23 | # * an optional default value 24 | # * an optional list of default values (array only) 25 | # * an optional prompt phrase used when asking for the variable 26 | # * a list of values (enums only) 27 | # * an optional separator (list only) 28 | vars: 29 | name: 30 | type: string 31 | description: feature name 32 | default: Dash 33 | prompt: What is your feature Name? 34 | useCase: 35 | type: string 36 | description: useCase name 37 | default: Dash 38 | prompt: What is your useCase Name? 39 | -------------------------------------------------------------------------------- /bricks/status/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.0+1 2 | 3 | - TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /bricks/status/LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /bricks/status/README.md: -------------------------------------------------------------------------------- 1 | # bloc_classes 2 | 3 | [![Powered by Mason](https://img.shields.io/endpoint?url=https%3A%2F%2Ftinyurl.com%2Fmason-badge)](https://github.com/felangel/mason) 4 | 5 | A new brick created with the Mason CLI. 6 | 7 | _Generated by [mason][1] 🧱_ 8 | 9 | ## Getting Started 🚀 10 | 11 | This is a starting point for a new brick. 12 | A few resources to get you started if this is your first brick template: 13 | 14 | - [Official Mason Documentation][2] 15 | - [Code generation with Mason Blog][3] 16 | - [Very Good Livestream: Felix Angelov Demos Mason][4] 17 | - [Flutter Package of the Week: Mason][5] 18 | - [Observable Flutter: Building a Mason brick][6] 19 | - [Meet Mason: Flutter Vikings 2022][7] 20 | 21 | [1]: https://github.com/felangel/mason 22 | [2]: https://docs.brickhub.dev 23 | [3]: https://verygood.ventures/blog/code-generation-with-mason 24 | [4]: https://youtu.be/G4PTjA6tpTU 25 | [5]: https://youtu.be/qjA0JFiPMnQ 26 | [6]: https://youtu.be/o8B1EfcUisw 27 | [7]: https://youtu.be/LXhgiF5HiQg 28 | -------------------------------------------------------------------------------- /bricks/status/__brick__/lib/features/dealership_panel/{{blocName}}_bloc/status/{{event}}_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class {{event.pascalCase()}}Status extends Equatable{} 4 | 5 | class {{event.pascalCase()}}Init extends {{event.pascalCase()}}Status{ 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class {{event.pascalCase()}}Loading extends {{event.pascalCase()}}Status{ 11 | @override 12 | List get props => []; 13 | } 14 | 15 | 16 | class {{event.pascalCase()}}Completed extends {{event.pascalCase()}}Status{ 17 | @override 18 | List get props => []; 19 | } 20 | 21 | class {{event.pascalCase()}}Error extends {{event.pascalCase()}}Status{ 22 | final String message; 23 | {{event.pascalCase()}}Error({required this.message}); 24 | @override 25 | List get props => [message]; 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /bricks/status/__brick__/lib/features/dealership_panel/{{blocName}}_bloc/{{blocName}}_state.dart: -------------------------------------------------------------------------------- 1 | part of '{{blocName}}_bloc.dart'; 2 | 3 | 4 | class {{blocName.pascalCase()}}State { 5 | 6 | final {{event.pascalCase()}}Status {{event.camelCase()}}Status; 7 | 8 | const {{blocName.pascalCase()}}State({required this.{{event.camelCase()}}Status}); 9 | 10 | {{blocName.pascalCase()}}State copyWith({{{event.pascalCase()}}Status? new{{event.pascalCase()}}Status}){ 11 | return {{blocName.pascalCase()}}State( 12 | {{event.camelCase()}}Status : new{{event.pascalCase()}}Status ?? {{event.camelCase()}}Status, 13 | ); 14 | } 15 | } -------------------------------------------------------------------------------- /bricks/status/__brick__/lib/features/{{name}}_feature/presentation/manager/status/{{event}}_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class {{event.pascalCase()}}Status extends Equatable{} 4 | 5 | class {{event.pascalCase()}}Init extends {{event.pascalCase()}}Status{ 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class {{event.pascalCase()}}Loading extends {{event.pascalCase()}}Status{ 11 | @override 12 | List get props => []; 13 | } 14 | 15 | 16 | class {{event.pascalCase()}}Completed extends {{event.pascalCase()}}Status{ 17 | @override 18 | List get props => []; 19 | } 20 | 21 | class {{event.pascalCase()}}Error extends {{event.pascalCase()}}Status{ 22 | final String message; 23 | {{event.pascalCase()}}Error({required this.message}); 24 | @override 25 | List get props => [message]; 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /bricks/status/__brick__/lib/features/{{name}}_feature/presentation/manager/status/{{useCase}}_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../../../../../core/error/response_error.dart'; 3 | abstract class {{useCase.pascalCase()}}Status extends Equatable{} 4 | 5 | class {{useCase.pascalCase()}}Init extends {{useCase.pascalCase()}}Status{ 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class {{useCase.pascalCase()}}Loading extends {{useCase.pascalCase()}}Status{ 11 | @override 12 | List get props => []; 13 | } 14 | 15 | 16 | class {{useCase.pascalCase()}}Completed extends {{useCase.pascalCase()}}Status{ 17 | @override 18 | List get props => []; 19 | } 20 | 21 | class {{useCase.pascalCase()}}Error extends {{useCase.pascalCase()}}Status{ 22 | final ResponseError responseError; 23 | {{useCase.pascalCase()}}Error({required this.responseError}); 24 | @override 25 | List get props => [responseError]; 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /bricks/status/__brick__/lib/features/{{name}}_feature/presentation/manager/{{name}}_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:dartz/dartz.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | import '../../../../core/error/response_error.dart'; 5 | import '../../../../core/success_response/success_response.dart'; 6 | import '../../presentation/manager/status/{{useCase}}_status.dart'; 7 | import '../../domain/use_cases/{{useCase}}_use_case.dart'; 8 | import '/../core/params/no_params.dart'; 9 | 10 | part '{{name}}_event.dart'; 11 | part '{{name}}_state.dart'; 12 | 13 | class {{name.pascalCase()}}Bloc extends Bloc<{{name.pascalCase()}}Event, {{name.pascalCase()}}State> { 14 | {{useCase.pascalCase()}}UseCase {{useCase.camelCase()}}UseCase; 15 | {{name.pascalCase()}}Bloc({required this.{{useCase.camelCase()}}UseCase}) : super({{name.pascalCase()}}State( 16 | {{useCase.camelCase()}}Status: 17 | {{useCase.pascalCase()}}Init(), 18 | )) { 19 | 20 | on<{{useCase.pascalCase()}}>((event, emit) async{ 21 | 22 | emit(state.copyWith(new{{useCase.pascalCase()}}Status: {{useCase.pascalCase()}}Loading())); 23 | 24 | Either result = await getUsersUseCase(NoParams()); 25 | 26 | result.fold((l){ 27 | emit(state.copyWith(new{{useCase.pascalCase()}}Status: {{useCase.pascalCase()}}Error(responseError: l))); 28 | },(r){ 29 | emit(state.copyWith(new{{useCase.pascalCase()}}Status: {{useCase.pascalCase()}}Completed())); 30 | }); 31 | 32 | }); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bricks/status/__brick__/lib/features/{{name}}_feature/presentation/manager/{{name}}_event.dart: -------------------------------------------------------------------------------- 1 | part of '{{name}}_bloc.dart'; 2 | 3 | abstract class {{name.pascalCase()}}Event extends Equatable {} 4 | 5 | class {{useCase.pascalCase()}} extends {{name.pascalCase()}}Event{ 6 | @override 7 | List get props => []; 8 | } -------------------------------------------------------------------------------- /bricks/status/__brick__/lib/features/{{name}}_feature/presentation/manager/{{name}}_state.dart: -------------------------------------------------------------------------------- 1 | part of '{{name}}_bloc.dart'; 2 | 3 | 4 | class {{name.pascalCase()}}State { 5 | 6 | final {{useCase.pascalCase()}}Status {{useCase.camelCase()}}Status; 7 | 8 | const {{name.pascalCase()}}State({required this.{{useCase.camelCase()}}Status}); 9 | 10 | {{name.pascalCase()}}State copyWith({{{useCase.pascalCase()}}Status? new{{useCase.pascalCase()}}Status}){ 11 | return {{name.pascalCase()}}State( 12 | {{useCase.camelCase()}}Status : new{{useCase.pascalCase()}}Status ?? {{useCase.camelCase()}}Status, 13 | ); 14 | } 15 | } -------------------------------------------------------------------------------- /bricks/status/brick.yaml: -------------------------------------------------------------------------------- 1 | name: status 2 | description: A new brick created with the Mason CLI. 3 | 4 | # The following defines the brick repository url. 5 | # Uncomment and update the following line before publishing the brick. 6 | # repository: https://github.com/my_org/my_repo 7 | 8 | # The following defines the version and build number for your brick. 9 | # A version number is three numbers separated by dots, like 1.2.34 10 | # followed by an optional build number (separated by a +). 11 | version: 0.1.0+1 12 | 13 | # The following defines the environment for the current brick. 14 | # It includes the version of mason that the brick requires. 15 | environment: 16 | mason: ">=0.1.0-dev.57 <0.1.0" 17 | 18 | # Variables specify dynamic values that your brick depends on. 19 | # Zero or more variables can be specified for a given brick. 20 | # Each variable has: 21 | # * a type (string, number, boolean, enum, array, or list) 22 | # * an optional short description 23 | # * an optional default value 24 | # * an optional list of default values (array only) 25 | # * an optional prompt phrase used when asking for the variable 26 | # * a list of values (enums only) 27 | # * an optional separator (list only) 28 | vars: 29 | blocName: 30 | type: string 31 | description: bloc name 32 | default: Dash 33 | prompt: What is your bloc Name? 34 | event: 35 | type: string 36 | description: event name 37 | default: Dash 38 | prompt: What is your event Name? 39 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | -------------------------------------------------------------------------------- /example/.mason/bricks.json: -------------------------------------------------------------------------------- 1 | {"base":"C:/Users/farshid/AppData/Local/Mason/Cache/git/mason_bricks_aHR0cHM6Ly9naXRodWIuY29tL2Jhc2ljRmx1dHRlci9tYXNvbl9icmlja3MuZ2l0_8e4eb61d31fc30f237cf8f3381539ad5e104c7f9/bricks/base","feature":"C:/Users/farshid/AppData/Local/Mason/Cache/git/mason_bricks_aHR0cHM6Ly9naXRodWIuY29tL2Jhc2ljRmx1dHRlci9tYXNvbl9icmlja3MuZ2l0_8e4eb61d31fc30f237cf8f3381539ad5e104c7f9/bricks/feature"} -------------------------------------------------------------------------------- /example/.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: "68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3" 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: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3 17 | base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3 18 | - platform: android 19 | create_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3 20 | base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3 21 | - platform: ios 22 | create_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3 23 | base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3 24 | 25 | # User provided section 26 | 27 | # List of Local paths (relative to this file) that should be 28 | # ignored by the migrate tool. 29 | # 30 | # Files that are not part of the templates will be ignored by default. 31 | unmanaged_files: 32 | - 'lib/main.dart' 33 | - 'ios/Runner.xcodeproj/project.pbxproj' 34 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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.example.example" 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.example.example" 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 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/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. -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | example 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 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/l10n 2 | template-arb-file: intl_en.arb 3 | output-localization-file: app_localizations.dart -------------------------------------------------------------------------------- /example/lib/core/constants/constants.dart: -------------------------------------------------------------------------------- 1 | class Constants { 2 | static String? accessToken = ""; 3 | static String? firebaseToken = ""; 4 | } -------------------------------------------------------------------------------- /example/lib/core/data/network/api_intercepror.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import '/../core/secure_storage/flutter_secure_storage_const.dart'; 3 | import '/../core/secure_storage/secure_storage.dart'; 4 | import '/../core/secure_storage/secure_storage_interface.dart'; 5 | class NetworkServiceInterceptor extends Interceptor{ 6 | final SecureStorageInterface _secureStorageInterface; 7 | final Dio _dio ; 8 | NetworkServiceInterceptor( this._secureStorageInterface, this._dio); 9 | @override 10 | void onRequest(RequestOptions options, RequestInterceptorHandler handler)async { 11 | final accessKey = await _secureStorageInterface.read(accessTokeKey); 12 | options.headers['Content-Type'] = 'application/json'; 13 | options.headers['Accept'] = 'application/json'; 14 | options.headers.addAll({"Authorization": "Bearer $accessKey"}); 15 | super.onRequest(options, handler); 16 | } 17 | 18 | @override 19 | void onResponse(Response response, ResponseInterceptorHandler handler) { 20 | super.onResponse(response, handler); 21 | } 22 | 23 | @override 24 | void onError(DioException err, ErrorInterceptorHandler handler) async { 25 | if(err.response?.statusCode == 401){ 26 | final refresh = await _secureStorageInterface.read(refreshTokeKey); 27 | } 28 | super.onError(err, handler); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /example/lib/core/data/network/api_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | abstract class ApiProviderInterface { 4 | Future get(path, {Options? options}); 5 | 6 | Future put(path, {Map? data, Options? options}); 7 | 8 | Future post(path, {Map? data, Options? options}); 9 | 10 | Future delete(path, {Options? options}); 11 | 12 | Future patch(path, {Map? data, Options? options}); 13 | 14 | Future setToken(); 15 | 16 | void initLogger(); 17 | void setErrorHandler(); 18 | } 19 | -------------------------------------------------------------------------------- /example/lib/core/data/network/api_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:pretty_dio_logger/pretty_dio_logger.dart'; 4 | import '/../core/data/network/api_intercepror.dart'; 5 | import '/../core/secure_storage/secure_storage.dart'; 6 | import '/../core/data/network/api_interface.dart'; 7 | import '../../global_app_setup/app_config.dart'; 8 | import '../../constants/constants.dart'; 9 | class ApiProvider extends ApiProviderInterface { 10 | ApiProvider._internal() { 11 | // Initialize the interceptor here after 'dio' is fully initialized. 12 | networkServiceInterceptor = NetworkServiceInterceptor( 13 | SecureStorage(), 14 | dio, 15 | ); 16 | } 17 | 18 | static final ApiProvider _singleton = ApiProvider._internal(); 19 | 20 | factory ApiProvider() { 21 | return _singleton; 22 | } 23 | 24 | static BaseOptions optionsDio = BaseOptions( 25 | baseUrl: AppConfig.baseUrl, 26 | receiveDataWhenStatusError: true, 27 | connectTimeout: const Duration(seconds: 120), 28 | headers: { 29 | "Content-Type": "application/json", 30 | "Accept": "application/json", 31 | }, 32 | ); 33 | 34 | final Dio dio = Dio(optionsDio); 35 | 36 | late final NetworkServiceInterceptor networkServiceInterceptor; 37 | 38 | @override 39 | Future get(path, {dynamic data, Options? options, String? baseUrl}) async { 40 | if (baseUrl != null) { 41 | dio.options.baseUrl = baseUrl; 42 | } 43 | return await dio.get(path, queryParameters: data, options: options); 44 | } 45 | 46 | @override 47 | Future post(path, {dynamic data, Options? options, String? baseUrl}) async { 48 | if (baseUrl != null) { 49 | dio.options.baseUrl = baseUrl; 50 | } 51 | return await dio.post(path, data: data, options: options); 52 | } 53 | 54 | @override 55 | Future put(path, {Map? data, Options? options}) async => 56 | await dio.put(path, data: data, options: options); 57 | 58 | @override 59 | Future delete(path, {Options? options}) async => 60 | await dio.delete(path, options: options); 61 | 62 | @override 63 | Future patch(path, {dynamic data, Options? options}) async => 64 | await dio.patch(path, options: options, data: data); 65 | 66 | @override 67 | void initLogger() async { 68 | dio.interceptors.add(PrettyDioLogger( 69 | requestHeader: false, 70 | requestBody: true, 71 | request: true, 72 | responseBody: true, 73 | responseHeader: false, 74 | compact: false, 75 | enabled: kDebugMode, 76 | )); 77 | setErrorHandler(); 78 | } 79 | 80 | @override 81 | Future setToken() async { 82 | dio.options.headers.addAll({"Authorization": "Bearer ${Constants.accessToken}"}); 83 | } 84 | 85 | @override 86 | void setErrorHandler() { 87 | dio.interceptors.add(networkServiceInterceptor); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /example/lib/core/error/error_handling.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import '/core/error/response_error.dart'; 3 | 4 | class ErrorHandling { 5 | static final ErrorHandling _instance = ErrorHandling._internal(); 6 | 7 | factory ErrorHandling() { 8 | return _instance; 9 | } 10 | 11 | ErrorHandling._internal(); 12 | 13 | ResponseError handleDioError(DioException error, String fromMethod) { 14 | if (error.type == DioExceptionType.connectionTimeout) { 15 | return ResponseError( 16 | message: "اینترنت خود را بررسی کنید.", 17 | statusCode: 408, 18 | ); 19 | } else if (error.type == DioExceptionType.unknown) { 20 | return ResponseError( 21 | message: "سرور در دسترسی نمیباشد. لطفا بعدا امتحان کنید.", 22 | statusCode: 502, 23 | ); 24 | } else if (error.response?.statusCode == 404) { 25 | return ResponseError( 26 | message: error.response?.data["message"] ?? "صفحه مورد نظر یافت نشد.", 27 | statusCode: error.response?.statusCode??404, 28 | ); 29 | } else { 30 | // Extract error message from response data or status message 31 | final message = error.response?.data["message"]?.toString() ?? 32 | error.response?.statusMessage?.toString(); 33 | return ResponseError( 34 | message: message??"لطفا بعدا امتحان کنید.", 35 | statusCode: error.response?.statusCode??500, 36 | ); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /example/lib/core/error/response_error.dart: -------------------------------------------------------------------------------- 1 | class ResponseError { 2 | final int statusCode; 3 | final String message; 4 | ResponseError({required this.message , required this.statusCode}); 5 | } -------------------------------------------------------------------------------- /example/lib/core/extensions/date_extentions.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | import 'package:persian_datetime_picker/persian_datetime_picker.dart' as datePicker; 3 | 4 | extension CustomDateExtensions on DateTime { 5 | 6 | datePicker.Jalali get toJalaliDateTime{ 7 | return datePicker.Jalali.fromDateTime(this); 8 | } 9 | 10 | int get daysInMonth { 11 | return DateTime(year, month + 1, 0).day; 12 | } 13 | 14 | String? weekdayName() { 15 | const Map weekdayName = { 16 | 1: "Monday", 17 | 2: "Tuesday", 18 | 3: "Wednesday", 19 | 4: "Thursday", 20 | 5: "Friday", 21 | 6: "Saturday", 22 | 7: "Sunday" 23 | }; 24 | return weekdayName[weekday]; 25 | } 26 | 27 | DateTime toNoonUTC() { 28 | // Set time to 12:00 noon UTC 29 | return DateTime.utc(year, month, day, 12, 0, 0, 0, 0); 30 | } 31 | 32 | String getTime() { 33 | // Format time as HH:mm 34 | return "${hour.toString().padLeft(2, "0")}:${minute.toString().padLeft(2, "0")}"; 35 | } 36 | 37 | String toJalaliFullDate() { 38 | // Convert to Jalali and format as full date 39 | return datePicker.Jalali.fromDateTime(this).formatFullDate(); 40 | } 41 | 42 | String toJalaliCompactDate() { 43 | // Convert to Jalali and format as compact date 44 | return datePicker.Jalali.fromDateTime(this).formatCompactDate(); 45 | } 46 | 47 | String toJalaliShortDate() { 48 | // Convert to Jalali and format as short date 49 | return datePicker.Jalali.fromDateTime(this).formatShortDate(); 50 | } 51 | 52 | String toJalaliMediumDate() { 53 | // Convert to Jalali and format as medium date 54 | return datePicker.Jalali.fromDateTime(this).formatMediumDate(); 55 | } 56 | } 57 | 58 | extension DateExtension on String? { 59 | DateTime? get toDateTime { 60 | if (this == null) return null; 61 | return DateTime.tryParse(this!); 62 | } 63 | 64 | String get timeFormat { 65 | if (this == null) return ""; 66 | final dateTime = DateTime.tryParse(this!); 67 | return dateTime != null 68 | ? DateFormat(DateFormat.HOUR_MINUTE_TZ).format(dateTime) 69 | : ""; 70 | } 71 | 72 | String get abbrWeekDay { 73 | if (this == null) return ""; 74 | final dateTime = DateTime.tryParse(this!); 75 | return dateTime != null 76 | ? DateFormat(DateFormat.ABBR_WEEKDAY).format(dateTime) 77 | : ""; 78 | } 79 | 80 | String get YEAR_ABBR_MONTH_DAY { 81 | if (this == null) return ""; 82 | final dateTime = DateTime.tryParse(this!); 83 | return dateTime != null 84 | ? DateFormat(DateFormat.YEAR_ABBR_MONTH_DAY).format(dateTime) 85 | : ""; 86 | } 87 | 88 | String get ABBR_MONTH_WEEKDAY_DAY { 89 | if (this == null) return ""; 90 | final dateTime = DateTime.tryParse(this!); 91 | return dateTime != null 92 | ? DateFormat(DateFormat.ABBR_MONTH_WEEKDAY_DAY).format(dateTime) 93 | : ""; 94 | } 95 | 96 | String formatWithTime() { 97 | if (this == null) return ""; 98 | final dateTime = DateTime.tryParse(this!); 99 | if (dateTime == null) return ""; 100 | return "${DateFormat("yyyy-MM-dd").format(dateTime)} " 101 | "${dateTime.hour.toString().padLeft(2, "0")}:${dateTime.minute.toString().padLeft(2, "0")}"; 102 | } 103 | 104 | String get toJalaliFullDate { 105 | if (this == null) return ""; 106 | final dateTime = DateTime.tryParse(this!); 107 | if (dateTime == null) return ""; 108 | return dateTime.toJalaliFullDate(); 109 | } 110 | 111 | String get toJalaliCompactDate { 112 | if (this == null) return ""; 113 | final dateTime = DateTime.tryParse(this!); 114 | if (dateTime == null) return ""; 115 | return dateTime.toJalaliCompactDate().replaceAll("/", "-"); 116 | } 117 | 118 | String get toJalaliShortDate { 119 | if (this == null) return ""; 120 | final dateTime = DateTime.tryParse(this!); 121 | if (dateTime == null) return ""; 122 | return dateTime.toJalaliShortDate(); 123 | } 124 | 125 | String get toJalaliMediumDate { 126 | if (this == null) return ""; 127 | final dateTime = DateTime.tryParse(this!); 128 | if (dateTime == null) return ""; 129 | return dateTime.toJalaliMediumDate(); 130 | } 131 | 132 | String get toJalaliMediumDateWithTime { 133 | if (this == null) return ""; 134 | final dateTime = DateTime.tryParse(this!); 135 | if (dateTime == null) return ""; 136 | return "${dateTime.toJalaliMediumDate()} ${dateTime.getTime()}"; 137 | } 138 | 139 | String calculateDifference(String endDate) { 140 | if (this == null || endDate.isEmpty) return ""; 141 | 142 | final dateTime1 = DateTime.tryParse(this!); 143 | final dateTime2 = DateTime.tryParse(endDate); 144 | if (dateTime1 == null || dateTime2 == null) return ""; 145 | 146 | final difference = dateTime2.difference(dateTime1); 147 | 148 | final days = difference.inDays; 149 | final hours = difference.inHours % 24; 150 | final minutes = difference.inMinutes % 60; 151 | 152 | final parts = [ 153 | if (days > 0) "${days}d", 154 | if (hours > 0) "${hours}h", 155 | if (minutes > 0) "${minutes}m" 156 | ]; 157 | 158 | return parts.join(" "); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /example/lib/core/extensions/file_extension.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | extension FileExtension on File { 4 | 5 | Future getFileSizeInMB() async{ 6 | return await length()/(1024*1024); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /example/lib/core/extensions/theme_extension.dart: -------------------------------------------------------------------------------- 1 | import '/../library.dart'; 2 | 3 | extension ThemeExtensions on ThemeData { 4 | Color getColorAppTheme({required Color lightColor, required Color darkColor}) { 5 | return brightness == Brightness.light ? lightColor : darkColor; 6 | } 7 | 8 | BoxShadow getShadowAppTheme({required BoxShadow shadowLight, required BoxShadow shadowDark}) { 9 | return brightness == Brightness.light ? shadowLight : shadowDark; 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /example/lib/core/firebase/firebase_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 2 | import 'package:firebase_core/firebase_core.dart'; 3 | import 'package:firebase_messaging/firebase_messaging.dart'; 4 | import 'package:awesome_notifications/awesome_notifications.dart'; 5 | import '../logger/app_logger.dart'; 6 | import '../secure_storage/secure_storage.dart'; 7 | 8 | class FirebaseConfig with AppLogger { 9 | FirebaseConfig(); 10 | 11 | final SecureStorage _secureStorage = SecureStorage(); // استفاده از SecureStorage 12 | 13 | Future fetchToken() async { 14 | logger.w("Fetching Firebase Token"); 15 | 16 | String? token; 17 | 18 | try { 19 | // مقداردهی اولیه Firebase 20 | await Firebase.initializeApp(); 21 | 22 | // اشتراک در موضوع 23 | await FirebaseMessaging.instance.subscribeToTopic('news'); 24 | 25 | // خواندن توکن از SecureStorage 26 | token = await _secureStorage.read('firebaseToken'); 27 | logger.f(token); 28 | 29 | if (token == null || token.isEmpty) { 30 | // حذف توکن قدیمی و گرفتن توکن جدید 31 | await FirebaseMessaging.instance.deleteToken(); 32 | token = await FirebaseMessaging.instance.getToken(); 33 | logger.e(token); 34 | 35 | if (token != null) { 36 | // ذخیره توکن جدید در SecureStorage 37 | await _secureStorage.write('firebaseToken', token); 38 | } 39 | } 40 | // گوش دادن به نوتیفیکیشن‌ها 41 | listenOnNotifications(); 42 | listenOnNotificationsOpenedApp(); 43 | 44 | return token; 45 | } catch (e) { 46 | logger.e(e); 47 | return null; 48 | } 49 | } 50 | 51 | Future deleteFirebaseToken() async { 52 | try { 53 | // حذف توکن از SecureStorage 54 | await _secureStorage.delete('firebaseToken'); 55 | 56 | // حذف توکن از Firebase Messaging 57 | await FirebaseMessaging.instance.deleteToken(); 58 | 59 | String? newToken = await FirebaseMessaging.instance.getToken(); 60 | logger.i(newToken); 61 | 62 | if (newToken != null) { 63 | // ذخیره توکن جدید در SecureStorage 64 | await _secureStorage.write('firebaseToken', newToken); 65 | 66 | } 67 | 68 | logger.i("Firebase token has been removed."); 69 | return true; 70 | } catch (e) { 71 | logger.e(e); 72 | return false; 73 | } 74 | } 75 | 76 | void listenOnNotificationsOpenedApp() { 77 | FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { 78 | _handleBackgroundNotification(message); 79 | }); 80 | } 81 | 82 | void listenOnNotifications() { 83 | FirebaseMessaging.onMessage.listen((RemoteMessage message) async { 84 | _handleBackgroundNotification(message); 85 | }); 86 | } 87 | 88 | static Future _handleBackgroundNotification(RemoteMessage message) async { 89 | return AwesomeNotifications().createNotification( 90 | content: NotificationContent( 91 | actionType: ActionType.KeepOnTop, 92 | wakeUpScreen: true, 93 | id: 123, 94 | criticalAlert: true, 95 | channelKey: 'basic_channel', 96 | title: message.notification?.title ?? "", 97 | body: message.notification?.body ?? "", 98 | fullScreenIntent: true, 99 | payload: {"name": "FlutterCampus"}, 100 | ), 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /example/lib/core/firebase/notification_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:isolate'; 2 | import 'dart:ui'; 3 | 4 | import 'package:awesome_notifications/awesome_notifications.dart'; 5 | 6 | import '../../main_prod.dart'; 7 | 8 | class NotificationController { 9 | bool? _initialized; 10 | NotificationController() { 11 | ReceivePort port = ReceivePort(); 12 | IsolateNameServer.registerPortWithName( 13 | port.sendPort, 14 | 'background_notification_action', 15 | ); 16 | 17 | port.listen((var received) async { 18 | logger.w(received); 19 | onSilentActionHandle(received); 20 | }); 21 | _initialized = true; 22 | } 23 | 24 | @pragma("vm:entry-point") 25 | Future onSilentActionHandle(ReceivedAction received) async { 26 | logger.e(_initialized); 27 | if (!_initialized!) { 28 | SendPort? uiSendPort = 29 | IsolateNameServer.lookupPortByName('background_notification_action'); 30 | if (uiSendPort != null) { 31 | uiSendPort.send(received); 32 | return; 33 | } 34 | } 35 | 36 | await _handleBackgroundAction(received); 37 | } 38 | 39 | static Future _handleBackgroundAction(ReceivedAction received) async { 40 | 41 | } 42 | 43 | /// Use this method to detect when a new notification or a schedule is created 44 | @pragma("vm:entry-point") 45 | static Future onNotificationCreatedMethod( 46 | ReceivedNotification receivedNotification) async { 47 | logger.i("onNotificationCreatedMethod"); 48 | } 49 | 50 | /// Use this method to detect every time that a new notification is displayed 51 | @pragma("vm:entry-point") 52 | static Future onNotificationDisplayedMethod( 53 | ReceivedNotification receivedNotification) async { 54 | logger.i("onNotificationDisplayedMethod"); 55 | 56 | // Your code goes here 57 | } 58 | 59 | /// Use this method to detect if the user dismissed a notification 60 | @pragma("vm:entry-point") 61 | static Future onDismissActionReceivedMethod( 62 | ReceivedAction receivedAction) async { 63 | logger.i("onDismissActionReceivedMethod"); 64 | // Your code goes here 65 | } 66 | 67 | /// Use this method to detect when the user taps on a notification or action button 68 | @pragma("vm:entry-point") 69 | static Future onActionReceivedMethod( 70 | ReceivedAction receivedAction, 71 | ) async { 72 | logger.i("onActionReceivedMethod"); 73 | } 74 | } -------------------------------------------------------------------------------- /example/lib/core/global_app_setup/app_config.dart: -------------------------------------------------------------------------------- 1 | enum Flavor{ 2 | development, 3 | production, 4 | local 5 | } 6 | 7 | 8 | class AppConfig{ 9 | 10 | static Flavor appFlavor = Flavor.production; 11 | static String get baseUrl { 12 | switch(appFlavor){ 13 | case Flavor.development: 14 | return 'http://192.168.1.7:8080'; 15 | case Flavor.production: 16 | return 'http://192.168.1.3:8080'; 17 | case Flavor.local: 18 | return 'http://172.17.240.1:8080'; 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /example/lib/core/global_app_setup/global_app_setup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:awesome_notifications/awesome_notifications.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 7 | import '../data/network/api_provider.dart'; 8 | import '../firebase/notification_controller.dart'; 9 | import '../logger/app_logger.dart'; 10 | import '../service_locator.dart'; 11 | 12 | class GlobalAppSetup with AppLogger { 13 | GlobalAppSetup ({ 14 | required Widget child 15 | }){ 16 | if(kReleaseMode){ 17 | ErrorWidget.builder = (_)=> Container(width: 100, height: 200, color: Colors.red, child: const Center(child: Text("custom error builder"),),); 18 | } 19 | WidgetsFlutterBinding.ensureInitialized(); 20 | 21 | init(child); 22 | 23 | } 24 | 25 | 26 | void init(Widget child) async{ 27 | serviceLocator(); 28 | WidgetsFlutterBinding.ensureInitialized(); 29 | 30 | ///for firebase 31 | // final firebaseConfig = FirebaseConfig(); 32 | // // گرفتن توکن Firebase 33 | // final token = await firebaseConfig.fetchToken(); 34 | // logger.i('Firebase Token: $token'); 35 | // 36 | // // حذف توکن 37 | // final result = await firebaseConfig.deleteFirebaseToken(); 38 | // logger.i('Token Deleted: $result'); 39 | 40 | ApiProvider().initLogger(); 41 | await ScreenUtil.ensureScreenSize(); 42 | SystemChrome.setPreferredOrientations([ 43 | DeviceOrientation.portraitUp, 44 | DeviceOrientation.portraitDown, 45 | ]); 46 | 47 | runApp(child); 48 | 49 | } 50 | 51 | void _handleFlutterError(FlutterErrorDetails details) { 52 | if(kReleaseMode){ 53 | Zone.current.handleUncaughtError(details.exception, details.stack!); 54 | }else{ 55 | logger.e("error ***************** ${details.exception}"); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /example/lib/core/language/language_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import '../secure_storage/secure_storage.dart'; 4 | import 'language_event.dart'; 5 | import 'language_state.dart'; 6 | 7 | class LanguageBloc extends Bloc { 8 | final _storage = SecureStorage(); 9 | static const String _languageKey = 'selected_language'; 10 | 11 | LanguageBloc() : super(LanguageState.initial()) { 12 | on(_onChangeLanguage); 13 | on(_onAddSupportedLocale); 14 | on(_onRemoveSupportedLocale); 15 | 16 | // Load saved language on initialization 17 | _initializeLanguage(); 18 | } 19 | 20 | Future _initializeLanguage() async { 21 | try { 22 | final savedLanguage = await _storage.read(_languageKey); 23 | if (savedLanguage != null) { 24 | add(ChangeLanguage(Locale(savedLanguage))); 25 | } 26 | } catch (e) { 27 | // در صورت خطا در خواندن زبان، از زبان پیش‌فرض استفاده می‌شود 28 | } 29 | } 30 | 31 | Future _onChangeLanguage( 32 | ChangeLanguage event, 33 | Emitter emit, 34 | ) async { 35 | if (!state.supportedLocales.contains(event.locale)) { 36 | return; 37 | } 38 | 39 | emit(state.copyWith(isLoading: true)); 40 | 41 | try { 42 | await _storage.write(_languageKey, event.locale.languageCode); 43 | emit(state.copyWith( 44 | currentLocale: event.locale, 45 | isLoading: false, 46 | )); 47 | } catch (e) { 48 | emit(state.copyWith(isLoading: false)); 49 | } 50 | } 51 | 52 | void _onAddSupportedLocale( 53 | AddSupportedLocale event, 54 | Emitter emit, 55 | ) { 56 | if (state.supportedLocales.contains(event.locale)) { 57 | return; 58 | } 59 | 60 | final updatedLocales = List.from(state.supportedLocales) 61 | ..add(event.locale); 62 | emit(state.copyWith(supportedLocales: updatedLocales)); 63 | } 64 | 65 | void _onRemoveSupportedLocale( 66 | RemoveSupportedLocale event, 67 | Emitter emit, 68 | ) { 69 | if (event.locale == state.currentLocale || 70 | !state.supportedLocales.contains(event.locale)) { 71 | return; 72 | } 73 | 74 | final updatedLocales = List.from(state.supportedLocales) 75 | ..remove(event.locale); 76 | emit(state.copyWith(supportedLocales: updatedLocales)); 77 | } 78 | } -------------------------------------------------------------------------------- /example/lib/core/language/language_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | abstract class LanguageEvent extends Equatable { 5 | const LanguageEvent(); 6 | 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class ChangeLanguage extends LanguageEvent { 12 | final Locale locale; 13 | 14 | const ChangeLanguage(this.locale); 15 | 16 | @override 17 | List get props => [locale]; 18 | } 19 | 20 | class AddSupportedLocale extends LanguageEvent { 21 | final Locale locale; 22 | 23 | const AddSupportedLocale(this.locale); 24 | 25 | @override 26 | List get props => [locale]; 27 | } 28 | 29 | class RemoveSupportedLocale extends LanguageEvent { 30 | final Locale locale; 31 | 32 | const RemoveSupportedLocale(this.locale); 33 | 34 | @override 35 | List get props => [locale]; 36 | } -------------------------------------------------------------------------------- /example/lib/core/language/language_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | class LanguageState extends Equatable { 5 | final Locale currentLocale; 6 | final List supportedLocales; 7 | final bool isLoading; 8 | 9 | const LanguageState({ 10 | required this.currentLocale, 11 | required this.supportedLocales, 12 | this.isLoading = false, 13 | }); 14 | 15 | factory LanguageState.initial() { 16 | return LanguageState( 17 | currentLocale: const Locale('fa'), 18 | supportedLocales: const [ 19 | Locale('fa'), 20 | Locale('en'), 21 | ], 22 | isLoading: false, 23 | ); 24 | } 25 | 26 | LanguageState copyWith({ 27 | Locale? currentLocale, 28 | List? supportedLocales, 29 | bool? isLoading, 30 | }) { 31 | return LanguageState( 32 | currentLocale: currentLocale ?? this.currentLocale, 33 | supportedLocales: supportedLocales ?? this.supportedLocales, 34 | isLoading: isLoading ?? this.isLoading, 35 | ); 36 | } 37 | 38 | @override 39 | List get props => [currentLocale, supportedLocales, isLoading]; 40 | } -------------------------------------------------------------------------------- /example/lib/core/logger/app_logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | mixin AppLogger { 4 | Logger get logger => Logger( 5 | printer: PrettyPrinter( 6 | methodCount: 2, // Number of method calls to be displayed 7 | errorMethodCount: 8, // Number of method calls if stacktrace is provided 8 | lineLength: 120, // Width of the output 9 | colors: true, // Colorful log messages 10 | printEmojis: true, // Print an emoji for each log message 11 | printTime: false // Should each log print contain a timestamp 12 | ), 13 | ); 14 | } -------------------------------------------------------------------------------- /example/lib/core/navigation/app_navigation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:page_transition/page_transition.dart'; 3 | import '../logger/app_logger.dart'; 4 | import '../../features/splash_feature/presentation/pages/splash_page.dart'; 5 | 6 | class AppNavigator with AppLogger { 7 | final GlobalKey navigatorKey = GlobalKey(); 8 | 9 | NavigatorState get state => navigatorKey.currentState!; 10 | /// TODO : create splash page 11 | final initialRoute = SplashPage.route; 12 | Widget screen = SplashPage(); 13 | 14 | Route? onGenerateRoute(RouteSettings settings) { 15 | return PageTransition( 16 | type: PageTransitionType.fade, 17 | duration: const Duration(milliseconds: 200), 18 | child: screen 19 | ); 20 | } 21 | 22 | 23 | Future to(Widget destination) { 24 | logger.d("Navigate to $destination"); 25 | return state.push(MaterialPageRoute(builder: (_)=>destination)); 26 | } 27 | 28 | back(dynamic result) { 29 | state.pop(result); 30 | } 31 | 32 | Future off(Widget destination) { 33 | logger.d("Navigate to $destination"); 34 | return state.pushReplacement(MaterialPageRoute(builder: (_)=>destination)); 35 | } 36 | 37 | } 38 | 39 | class CustomRouteInformation extends RouteInformation { 40 | final String? myProperty; 41 | CustomRouteInformation({ 42 | required this.myProperty, 43 | super.location, 44 | super.state, 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /example/lib/core/params/no_params.dart: -------------------------------------------------------------------------------- 1 | class NoParams{ 2 | NoParams(); 3 | } -------------------------------------------------------------------------------- /example/lib/core/secure_storage/flutter_secure_storage_const.dart: -------------------------------------------------------------------------------- 1 | const String accessTokeKey = "accessKey"; 2 | const String refreshTokeKey = "refreshKey"; -------------------------------------------------------------------------------- /example/lib/core/secure_storage/secure_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 2 | import '/core/secure_storage/secure_storage_interface.dart'; 3 | 4 | class SecureStorage implements SecureStorageInterface { 5 | final FlutterSecureStorage _flutterSecureStorage; 6 | 7 | static const _androidOptions = AndroidOptions( 8 | encryptedSharedPreferences: true, 9 | ); 10 | 11 | static const _iosOptions = IOSOptions( 12 | accessibility: KeychainAccessibility.first_unlock, 13 | ); 14 | 15 | static final SecureStorage _instance = SecureStorage._internal(); 16 | 17 | SecureStorage._internal() 18 | : _flutterSecureStorage = const FlutterSecureStorage( 19 | aOptions: _androidOptions, 20 | iOptions: _iosOptions, 21 | ); 22 | 23 | factory SecureStorage() => _instance; 24 | 25 | @override 26 | Future read(String key) async { 27 | try { 28 | return await _flutterSecureStorage.read(key: key); 29 | } catch (e) { 30 | rethrow; 31 | } 32 | } 33 | 34 | @override 35 | Future write(String key, String value) async { 36 | try { 37 | await _flutterSecureStorage.write(key: key, value: value); 38 | } catch (e) { 39 | rethrow; 40 | } 41 | } 42 | 43 | @override 44 | Future delete(String key) async { 45 | try { 46 | await _flutterSecureStorage.delete(key: key); 47 | } catch (e) { 48 | rethrow; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /example/lib/core/secure_storage/secure_storage_interface.dart: -------------------------------------------------------------------------------- 1 | abstract interface class SecureStorageInterface { 2 | Future read(String key); 3 | Future write(String key, String value); 4 | Future delete(String key); 5 | } -------------------------------------------------------------------------------- /example/lib/core/service_locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:get_it/get_it.dart'; 3 | import '../core/navigation/app_navigation.dart'; 4 | GetIt locator = GetIt.instance; 5 | 6 | serviceLocator() async { 7 | locator.registerLazySingleton(() => AppNavigator()); 8 | locator.registerSingleton(Dio()); 9 | 10 | ///############################################## API Provider ############################# 11 | 12 | // locator.registerLazySingleton(() => UserApiProvider()); 13 | 14 | ///############################################## Repository ############################# 15 | 16 | //locator.registerLazySingleton(()=>UserRepositoryImp(userApiProvider: locator())); 17 | 18 | ///############################################## UseCase ############################# 19 | 20 | // locator.registerLazySingleton(()=>GetUserUseCase( userRepository: locator())); 21 | 22 | ///############################################## Bloc ############################# 23 | // locator.registerSingleton(ChatBloc( 24 | // createRoomUseCase: locator(), 25 | // getAllUserUseCase: locator(), 26 | // getOnlineUserUseCase: locator(), 27 | // getConversationUseCase: locator(), 28 | // connectToWebsocketUseCase: locator(), 29 | // getNotReadMessagesUseCase: locator())); 30 | } 31 | -------------------------------------------------------------------------------- /example/lib/core/success_response/success_response.dart: -------------------------------------------------------------------------------- 1 | class SuccessResponse { 2 | int? statusCode; 3 | String? message; 4 | SuccessResponse({ 5 | this.message, 6 | this.statusCode 7 | }); 8 | 9 | SuccessResponse.fromJson(dynamic json) { 10 | statusCode = json['statusCode']; 11 | message = json['message']; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /example/lib/core/theme/app_theme.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import "text_themes.dart"; 3 | import "color_themes.dart"; 4 | 5 | class AppTheme { 6 | static ThemeData lightTheme = ThemeData( 7 | extensions: [ 8 | LightColorThemes.appAdditionalColors, 9 | LightTextThemes.appTextTheme, 10 | ], 11 | splashColor: Colors.transparent, 12 | highlightColor: Colors.transparent, 13 | brightness: Brightness.light, 14 | fontFamily: "IranSans", 15 | primaryColor: Colors.blueGrey, 16 | useMaterial3: true, 17 | iconTheme: IconThemeData(color: Colors.grey[700]), 18 | ); 19 | 20 | static ThemeData darkTheme = ThemeData( 21 | extensions: [ 22 | DarkColorThemes.appAdditionalColors, 23 | DarkTextThemes.appTextTheme, 24 | ], 25 | splashColor: Colors.transparent, 26 | highlightColor: Colors.transparent, 27 | brightness: Brightness.dark, 28 | fontFamily: "IranSans", 29 | primaryColor: Colors.blueGrey, 30 | iconTheme: IconThemeData(color: Colors.grey[700]), 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /example/lib/core/theme/bloc/theme_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'theme_event.dart'; 3 | import 'theme_state.dart'; 4 | 5 | class ThemeBloc extends Bloc { 6 | ThemeBloc() : super(ThemeInitial()) { 7 | on((event, emit) { 8 | final currentState = state; 9 | if (currentState is ThemeInitial) { 10 | emit(ThemeChanged(!currentState.isDark)); 11 | } else if (currentState is ThemeChanged) { 12 | emit(ThemeChanged(!currentState.isDark)); 13 | } 14 | }); 15 | 16 | on((event, emit) { 17 | emit(ThemeChanged(event.isDark)); 18 | }); 19 | } 20 | } -------------------------------------------------------------------------------- /example/lib/core/theme/bloc/theme_event.dart: -------------------------------------------------------------------------------- 1 | abstract class ThemeEvent {} 2 | 3 | class ToggleTheme extends ThemeEvent {} 4 | 5 | class SetTheme extends ThemeEvent { 6 | final bool isDark; 7 | SetTheme(this.isDark); 8 | } -------------------------------------------------------------------------------- /example/lib/core/theme/bloc/theme_state.dart: -------------------------------------------------------------------------------- 1 | abstract class ThemeState {} 2 | 3 | class ThemeInitial extends ThemeState { 4 | final bool isDark; 5 | ThemeInitial({this.isDark = false}); 6 | } 7 | 8 | class ThemeChanged extends ThemeState { 9 | final bool isDark; 10 | ThemeChanged(this.isDark); 11 | } -------------------------------------------------------------------------------- /example/lib/core/theme/color_themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | 4 | /// تعریف رنگ‌های اضافی برای حالت روشن 5 | class LightColorThemes { 6 | static final appAdditionalColors = AppColorsTheme( 7 | success50: Color(0xffECFDF3), 8 | success700: Color(0xff027A48), 9 | warningBackground: Color(0xffFFF5DA), 10 | warning: Color(0xffF1A500), 11 | error700: Color(0xffB42318), 12 | error50: Color(0xffFEF3F2), 13 | backgroundtritary: Color(0xffFDFAF3), 14 | lightBackgroundSubded: Color(0xffFBDBDE3), 15 | lightBackgroundSubdedplus: Color(0xffFCF6F8), 16 | secondaryErrorText: Color(0xff9F2626), 17 | ); 18 | } 19 | 20 | /// تعریف رنگ‌های اضافی برای حالت تاریک 21 | class DarkColorThemes { 22 | static final appAdditionalColors = AppColorsTheme( 23 | success50: Color(0xffECFDF3), 24 | success700: Color(0xff027A48), 25 | warningBackground: Color(0xffFFF5DA), 26 | warning: Color(0xffF1A500), 27 | error700: Color(0xffB42318), 28 | error50: Color(0xffFEF3F2), 29 | backgroundtritary: Color(0xffFDFAF3), 30 | lightBackgroundSubded: Color(0xffFBDBDE3), 31 | lightBackgroundSubdedplus: Color(0xffFCF6F8), 32 | secondaryErrorText: Color(0xff9F2626), 33 | ); 34 | } 35 | 36 | 37 | class AppColorsTheme extends ThemeExtension { 38 | const AppColorsTheme({ 39 | required this.success50, 40 | required this.success700, 41 | required this.error50, 42 | required this.error700, 43 | required this.warning, 44 | required this.warningBackground, 45 | required this.backgroundtritary, 46 | required this.lightBackgroundSubdedplus, 47 | required this.lightBackgroundSubded, 48 | required this.secondaryErrorText, 49 | }); 50 | 51 | // رنگ‌های موفقیت 52 | final Color success50; 53 | final Color success700; 54 | 55 | // رنگ‌های خطا 56 | final Color error50; 57 | final Color error700; 58 | final Color secondaryErrorText; 59 | 60 | // رنگ‌های هشدار 61 | final Color warning; 62 | final Color warningBackground; 63 | 64 | // رنگ‌های پس‌زمینه 65 | final Color backgroundtritary; 66 | final Color lightBackgroundSubdedplus; 67 | final Color lightBackgroundSubded; 68 | 69 | @override 70 | ThemeExtension lerp(AppColorsTheme? other, double t) { 71 | if (other is! AppColorsTheme) { 72 | return this; 73 | } 74 | return AppColorsTheme( 75 | success700: Color.lerp(success700, other.success700, t)!, 76 | success50: Color.lerp(success50, other.success50, t)!, 77 | error50: Color.lerp(error50, other.error50, t)!, 78 | error700: Color.lerp(error700, other.error700, t)!, 79 | warning: Color.lerp(warning, other.warning, t)!, 80 | warningBackground: Color.lerp(warningBackground, other.warningBackground, t)!, 81 | backgroundtritary: Color.lerp(backgroundtritary, other.backgroundtritary, t)!, 82 | lightBackgroundSubded: Color.lerp(lightBackgroundSubded, other.lightBackgroundSubded, t)!, 83 | lightBackgroundSubdedplus: Color.lerp(lightBackgroundSubdedplus, other.lightBackgroundSubdedplus, t)!, 84 | secondaryErrorText: Color.lerp(secondaryErrorText, other.secondaryErrorText, t)!, 85 | ); 86 | } 87 | 88 | @override 89 | AppColorsTheme copyWith({ 90 | Color? success50, 91 | Color? success700, 92 | Color? error50, 93 | Color? error700, 94 | Color? warning, 95 | Color? warningBackground, 96 | Color? backgroundtritary, 97 | Color? lightBackgroundSubdedplus, 98 | Color? lightBackgroundSubded, 99 | Color? secondaryErrorText, 100 | }) { 101 | return AppColorsTheme( 102 | success50: success50 ?? this.success50, 103 | success700: success700 ?? this.success700, 104 | error50: error50 ?? this.error50, 105 | error700: error700 ?? this.error700, 106 | warning: warning ?? this.warning, 107 | warningBackground: warningBackground ?? this.warningBackground, 108 | backgroundtritary: backgroundtritary ?? this.backgroundtritary, 109 | lightBackgroundSubdedplus: lightBackgroundSubdedplus ?? this.lightBackgroundSubdedplus, 110 | lightBackgroundSubded: lightBackgroundSubded ?? this.lightBackgroundSubded, 111 | secondaryErrorText: secondaryErrorText ?? this.secondaryErrorText, 112 | ); 113 | } 114 | } -------------------------------------------------------------------------------- /example/lib/core/theme/text_themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | 4 | import 'color_themes.dart'; 5 | 6 | /// تعریف تم‌های متنی برای حالت روشن 7 | class LightTextThemes { 8 | static final appTextTheme = AppTextTheme( 9 | body1: TextStyle( 10 | fontSize: 14.sp, 11 | color: LightColorThemes.appAdditionalColors.error50, 12 | fontWeight: FontWeight.w400, 13 | ), 14 | h1: TextStyle( 15 | fontSize: 24.sp, 16 | color: LightColorThemes.appAdditionalColors.error50, 17 | fontWeight: FontWeight.bold, 18 | ), 19 | ); 20 | } 21 | 22 | /// تعریف تم‌های متنی برای حالت تاریک 23 | class DarkTextThemes { 24 | static final appTextTheme = AppTextTheme( 25 | body1: TextStyle( 26 | fontSize: 14.sp, 27 | color: DarkColorThemes.appAdditionalColors.error50, 28 | fontWeight: FontWeight.w400, 29 | ), 30 | h1: TextStyle( 31 | fontSize: 24.sp, 32 | color: DarkColorThemes.appAdditionalColors.error50, 33 | fontWeight: FontWeight.bold, 34 | ), 35 | ); 36 | } 37 | 38 | 39 | /// اکستنشن برای مدیریت استایل‌های متنی سفارشی 40 | class AppTextTheme extends ThemeExtension { 41 | const AppTextTheme({ 42 | required this.body1, 43 | required this.h1, 44 | }); 45 | 46 | final TextStyle body1; 47 | final TextStyle h1; 48 | 49 | @override 50 | ThemeExtension copyWith({ 51 | TextStyle? body1, 52 | TextStyle? h1, 53 | }) { 54 | return AppTextTheme( 55 | body1: body1 ?? this.body1, 56 | h1: h1 ?? this.h1, 57 | ); 58 | } 59 | 60 | @override 61 | ThemeExtension lerp( 62 | covariant ThemeExtension? other, 63 | double t, 64 | ) { 65 | if (other is! AppTextTheme) { 66 | return this; 67 | } 68 | 69 | return AppTextTheme( 70 | body1: TextStyle.lerp(body1, other.body1, t)!, 71 | h1: TextStyle.lerp(h1, other.h1, t)!, 72 | ); 73 | } 74 | } -------------------------------------------------------------------------------- /example/lib/core/theme/theme_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'color_themes.dart'; 3 | import 'text_themes.dart'; 4 | 5 | /// اکستنشن‌های مربوط به ThemeData 6 | extension AppThemeExtension on ThemeData { 7 | /// دسترسی به رنگ‌های سفارشی 8 | /// مثال: Theme.of(context).appColors 9 | AppColorsTheme get appColors => extension() ?? LightColorThemes.appAdditionalColors; 10 | 11 | /// دسترسی به استایل‌های متنی سفارشی 12 | /// مثال: Theme.of(context).appTextTheme 13 | AppTextTheme get appTextTheme => extension() ?? LightTextThemes.appTextTheme; 14 | } 15 | 16 | /// اکستنشن‌های مربوط به BuildContext 17 | extension ThemeGetter on BuildContext { 18 | /// دسترسی به ThemeData 19 | /// مثال: context.theme 20 | ThemeData get theme => Theme.of(this); 21 | 22 | /// دسترسی به رنگ‌های سفارشی 23 | /// مثال: context.appColors 24 | AppColorsTheme get appColors => theme.appColors; 25 | 26 | /// دسترسی به استایل‌های متنی سفارشی 27 | /// مثال: context.appTextTheme 28 | AppTextTheme get appTextTheme => theme.appTextTheme; 29 | } -------------------------------------------------------------------------------- /example/lib/core/use_case/base_usecase.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '/core/error/response_error.dart'; 3 | 4 | abstract class BaseUseCase { 5 | Future> call(InputParams params); 6 | } -------------------------------------------------------------------------------- /example/lib/features/splash_feature/data/data_sources/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/splash_feature/data/data_sources/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/splash_feature/data/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/splash_feature/data/models/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/splash_feature/data/repositories/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/splash_feature/data/repositories/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/splash_feature/domain/entities/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/splash_feature/domain/entities/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/splash_feature/domain/repositories/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/splash_feature/domain/repositories/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/splash_feature/domain/use_cases/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/splash_feature/domain/use_cases/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/splash_feature/presentation/manager/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/splash_feature/presentation/manager/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/splash_feature/presentation/pages/splash_page.dart: -------------------------------------------------------------------------------- 1 | import '../../../../library.dart'; 2 | 3 | class SplashPage extends StatelessWidget { 4 | const SplashPage({super.key}); 5 | static const route = '/'; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | AppLocalizations localizations = AppLocalizations.of(context)!; 10 | return Scaffold( 11 | body: Center( 12 | child: Text(localizations.hello), 13 | ), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/lib/features/splash_feature/presentation/widgets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/splash_feature/presentation/widgets/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/user_feature/data/data_sources/user_api_provider.dart: -------------------------------------------------------------------------------- 1 | import '../../../../core/data/network/api_provider.dart'; 2 | 3 | class UserApiProvider { 4 | 5 | Future callGetUsersRoute() async{ 6 | final res = await ApiProvider().get('/api/v1/project/get_suggestion_projects'); 7 | return res ; 8 | } 9 | 10 | } -------------------------------------------------------------------------------- /example/lib/features/user_feature/data/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/user_feature/data/models/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/user_feature/data/repositories/user_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import 'package:dio/dio.dart'; 3 | import '../../../../core/error/error_handling.dart'; 4 | import '../../../../core/error/response_error.dart'; 5 | import '../../../../core/success_response/success_response.dart'; 6 | import '../../domain/repositories/user_repository.dart'; 7 | import '../data_sources/user_api_provider.dart'; 8 | 9 | class UserRepositoryImpl extends UserRepository{ 10 | UserApiProvider userApiProvider; 11 | UserRepositoryImpl({required this.userApiProvider}); 12 | @override 13 | Future> getUsers() async{ 14 | try { 15 | final response = await userApiProvider.callGetUsersRoute(); 16 | return Right(SuccessResponse()); 17 | } on DioException catch (error) { 18 | ResponseError responseError = ErrorHandling().handleDioError(error, "getUsers"); 19 | return Left(responseError); 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /example/lib/features/user_feature/domain/entities/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/user_feature/domain/entities/.gitkeep -------------------------------------------------------------------------------- /example/lib/features/user_feature/domain/repositories/user_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../../core/error/response_error.dart'; 3 | import '../../../../core/success_response/success_response.dart'; 4 | 5 | abstract class UserRepository { 6 | Future> getUsers(); 7 | } -------------------------------------------------------------------------------- /example/lib/features/user_feature/domain/use_cases/get_users_use_case.dart: -------------------------------------------------------------------------------- 1 | import 'package:dartz/dartz.dart'; 2 | import '../../../../core/error/response_error.dart'; 3 | import '../../../../core/success_response/success_response.dart'; 4 | import '../../../../core/use_case/base_usecase.dart'; 5 | import '../repositories/user_repository.dart'; 6 | 7 | class GetUsersUseCase extends BaseUseCase{ 8 | 9 | UserRepository userRepository; 10 | GetUsersUseCase({required this.userRepository }); 11 | @override 12 | Future> call(void params)async{ 13 | return await userRepository.getUsers(); 14 | } 15 | } -------------------------------------------------------------------------------- /example/lib/features/user_feature/presentation/manager/status/get_users_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import '../../../../../core/error/response_error.dart'; 3 | abstract class GetUsersStatus extends Equatable{} 4 | 5 | class GetUsersInit extends GetUsersStatus{ 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class GetUsersLoading extends GetUsersStatus{ 11 | @override 12 | List get props => []; 13 | } 14 | 15 | 16 | class GetUsersCompleted extends GetUsersStatus{ 17 | @override 18 | List get props => []; 19 | } 20 | 21 | class GetUsersError extends GetUsersStatus{ 22 | final ResponseError responseError; 23 | GetUsersError({required this.responseError}); 24 | @override 25 | List get props => [responseError]; 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/lib/features/user_feature/presentation/manager/user_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:dartz/dartz.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | import '../../../../core/error/response_error.dart'; 5 | import '../../../../core/success_response/success_response.dart'; 6 | import '../../presentation/manager/status/get_users_status.dart'; 7 | import '../../domain/use_cases/get_users_use_case.dart'; 8 | import '/../core/params/no_params.dart'; 9 | 10 | part 'user_event.dart'; 11 | part 'user_state.dart'; 12 | 13 | class UserBloc extends Bloc { 14 | GetUsersUseCase getUsersUseCase; 15 | UserBloc({required this.getUsersUseCase}) : super(UserState( 16 | getUsersStatus: 17 | GetUsersInit(), 18 | )) { 19 | 20 | on((event, emit) async{ 21 | 22 | emit(state.copyWith(newGetUsersStatus: GetUsersLoading())); 23 | 24 | Either result = await getUsersUseCase(NoParams()); 25 | 26 | result.fold((l){ 27 | emit(state.copyWith(newGetUsersStatus: GetUsersError(responseError: l))); 28 | },(r){ 29 | emit(state.copyWith(newGetUsersStatus: GetUsersCompleted())); 30 | }); 31 | 32 | }); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/lib/features/user_feature/presentation/manager/user_event.dart: -------------------------------------------------------------------------------- 1 | part of 'user_bloc.dart'; 2 | 3 | abstract class UserEvent extends Equatable {} 4 | 5 | class GetUsers extends UserEvent{ 6 | @override 7 | List get props => []; 8 | } -------------------------------------------------------------------------------- /example/lib/features/user_feature/presentation/manager/user_state.dart: -------------------------------------------------------------------------------- 1 | part of 'user_bloc.dart'; 2 | 3 | 4 | class UserState { 5 | 6 | final GetUsersStatus getUsersStatus; 7 | 8 | const UserState({required this.getUsersStatus}); 9 | 10 | UserState copyWith({GetUsersStatus? newGetUsersStatus}){ 11 | return UserState( 12 | getUsersStatus : newGetUsersStatus ?? getUsersStatus, 13 | ); 14 | } 15 | } -------------------------------------------------------------------------------- /example/lib/features/user_feature/presentation/pages/user_page.dart: -------------------------------------------------------------------------------- 1 | import '../../../../library.dart'; 2 | import '../manager/user_bloc.dart'; 3 | import '../manager/status/get_users_status.dart'; 4 | class UserPage extends StatelessWidget { 5 | const UserPage({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return BlocProvider( 10 | create: (context) => UserBloc(getUsersUseCase: locator())..add(GetUsers()), 11 | child: Builder( 12 | builder: (context) { 13 | return Scaffold( 14 | body: BlocBuilder( 15 | builder: (context , state){ 16 | if(state.getUsersStatus is GetUsersLoading){ 17 | return CircularProgressIndicator(); 18 | } 19 | 20 | if(state.getUsersStatus is GetUsersCompleted){ 21 | GetUsersCompleted getUsersCompleted = state.getUsersStatus as GetUsersCompleted; 22 | return SizedBox(); 23 | } 24 | 25 | if(state.getUsersStatus is GetUsersError){ 26 | GetUsersError getUsersError = state.getUsersStatus as GetUsersError; 27 | return SizedBox(); 28 | } 29 | return SizedBox(); 30 | 31 | }), 32 | ); 33 | }), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /example/lib/features/user_feature/presentation/widgets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicFlutter/mason_bricks/91d946b727d4642b98973eadc6db8f663754f87a/example/lib/features/user_feature/presentation/widgets/.gitkeep -------------------------------------------------------------------------------- /example/lib/l10n/intl_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Hello", 3 | "@hello": {} 4 | } -------------------------------------------------------------------------------- /example/lib/l10n/intl_fa.arb: -------------------------------------------------------------------------------- 1 | { 2 | "hello" : "سلام" 3 | } -------------------------------------------------------------------------------- /example/lib/library.dart: -------------------------------------------------------------------------------- 1 | 2 | library tmanagix; 3 | 4 | export 'package:flutter/material.dart'; 5 | export 'package:flutter_bloc/flutter_bloc.dart'; 6 | export 'package:flutter_gen/gen_l10n/app_localizations.dart'; 7 | export 'package:flutter_screenutil/flutter_screenutil.dart'; 8 | export '../core/extensions/theme_extension.dart'; 9 | export '../core/navigation/app_navigation.dart'; 10 | export '../core/service_locator.dart'; 11 | export '../core/theme/theme_extensions.dart'; 12 | -------------------------------------------------------------------------------- /example/lib/main_dev.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'core/global_app_setup/app_config.dart'; 3 | import 'core/global_app_setup/global_app_setup.dart'; 4 | import 'my_app.dart'; 5 | 6 | Logger logger = Logger(); 7 | 8 | void main() async { 9 | AppConfig.appFlavor = Flavor.development; 10 | GlobalAppSetup (child: MyApp()); 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/lib/main_local.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'core/global_app_setup/app_config.dart'; 3 | import 'core/global_app_setup/global_app_setup.dart'; 4 | import 'my_app.dart'; 5 | 6 | Logger logger = Logger(); 7 | 8 | void main() async { 9 | AppConfig.appFlavor = Flavor.local; 10 | GlobalAppSetup (child: MyApp()); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /example/lib/main_prod.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'core/global_app_setup/app_config.dart'; 3 | import 'core/global_app_setup/global_app_setup.dart'; 4 | import 'my_app.dart'; 5 | 6 | Logger logger = Logger(); 7 | 8 | void main() async { 9 | AppConfig.appFlavor = Flavor.production; 10 | GlobalAppSetup (child: MyApp()); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /example/lib/my_app.dart: -------------------------------------------------------------------------------- 1 | import 'core/language/language_state.dart'; 2 | import 'library.dart'; 3 | import '/../core/theme/app_theme.dart'; 4 | import '/../core/theme/bloc/theme_bloc.dart'; 5 | import '/../core/theme/bloc/theme_state.dart'; 6 | import '/../core/language/language_bloc.dart'; 7 | 8 | class MyApp extends StatelessWidget { 9 | MyApp({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | final navigator = locator(); 14 | return MultiBlocProvider( 15 | providers: [ 16 | BlocProvider(create: (context) => ThemeBloc()), 17 | BlocProvider(create: (context) => LanguageBloc()), 18 | ], 19 | child: BlocBuilder( 20 | builder: (context, themeState) { 21 | final isDark = themeState is ThemeChanged ? themeState.isDark : false; 22 | return BlocBuilder( 23 | builder: (context, languageState) { 24 | return SafeArea( 25 | child: ScreenUtilInit( 26 | designSize: const Size(390, (844 + 33.5)), 27 | minTextAdapt: true, 28 | splitScreenMode: false, 29 | useInheritedMediaQuery: true, 30 | builder: (context, child) { 31 | return MaterialApp( 32 | supportedLocales: languageState.supportedLocales, 33 | localizationsDelegates: AppLocalizations.localizationsDelegates, 34 | locale: languageState.currentLocale, 35 | debugShowCheckedModeBanner: false, 36 | theme: AppTheme.lightTheme, 37 | darkTheme: AppTheme.darkTheme, 38 | themeMode: isDark ? ThemeMode.dark : ThemeMode.light, 39 | navigatorKey: navigator.navigatorKey, 40 | onGenerateRoute: navigator.onGenerateRoute, 41 | initialRoute: navigator.initialRoute, 42 | ); 43 | }, 44 | ), 45 | ); 46 | }, 47 | ); 48 | }, 49 | ), 50 | ); 51 | } 52 | } -------------------------------------------------------------------------------- /example/mason-lock.json: -------------------------------------------------------------------------------- 1 | {"bricks":{"base":{"git":{"url":"https://github.com/basicFlutter/mason_bricks.git","path":"bricks/base","ref":"8e4eb61d31fc30f237cf8f3381539ad5e104c7f9"}},"feature":{"git":{"url":"https://github.com/basicFlutter/mason_bricks.git","path":"bricks/feature","ref":"8e4eb61d31fc30f237cf8f3381539ad5e104c7f9"}}}} -------------------------------------------------------------------------------- /example/mason.yaml: -------------------------------------------------------------------------------- 1 | bricks: 2 | feature: 3 | git: 4 | url: https://github.com/basicFlutter/mason_bricks.git 5 | path: bricks/feature 6 | # ref: without_usecase 7 | base: 8 | git: 9 | url: https://github.com/basicFlutter/mason_bricks.git 10 | path: bricks/base -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: "A new Flutter project." 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | version: 1.0.0+1 6 | 7 | environment: 8 | sdk: ^3.6.1 9 | 10 | # Dependencies specify other packages that your package needs in order to work. 11 | # To automatically upgrade your package dependencies to the latest versions 12 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 13 | # dependencies can be manually updated by changing the version numbers below to 14 | # the latest version available on pub.dev. To see which dependencies have newer 15 | # versions available, run `flutter pub outdated`. 16 | dependencies: 17 | flutter: 18 | sdk: flutter 19 | flutter_localizations: 20 | sdk: flutter 21 | intl: ^0.19.0 22 | cupertino_icons: ^1.0.8 23 | flutter_bloc: ^9.0.0 24 | dio: ^5.3.3 25 | pretty_dio_logger: ^1.3.1 26 | firebase_messaging: ^15.1.1 27 | firebase_core: ^3.4.1 28 | awesome_notifications: ^0.10.0 29 | logger: ^2.0.2+1 30 | flutter_screenutil: ^5.9.0 31 | dartz: ^0.10.1 32 | page_transition: ^2.1.0 33 | equatable: ^2.0.5 34 | get_it: ^8.0.1 35 | gap: ^3.0.1 36 | persian_datetime_picker: ^3.1.0 37 | shamsi_date: ^1.0.4 38 | package_info_plus: ^8.0.2 39 | flutter_secure_storage: ^9.2.4 40 | 41 | 42 | dev_dependencies: 43 | flutter_test: 44 | sdk: flutter 45 | build_runner: ^2.4.14 46 | 47 | # The "flutter_lints" package below contains a set of recommended lints to 48 | # encourage good coding practices. The lint set provided by the package is 49 | # activated in the `analysis_options.yaml` file located at the root of your 50 | # package. See that file for information about deactivating specific lint 51 | # rules and activating additional ones. 52 | flutter_lints: ^5.0.0 53 | 54 | # For information on the generic Dart part of this file, see the 55 | # following page: https://dart.dev/tools/pub/pubspec 56 | 57 | # The following section is specific to Flutter packages. 58 | flutter: 59 | 60 | # The following line ensures that the Material Icons font is 61 | # included with your application, so that you can use the icons in 62 | # the material Icons class. 63 | uses-material-design: true 64 | generate: true 65 | # To add assets to your application, add an assets section, like this: 66 | # assets: 67 | # - images/a_dot_burr.jpeg 68 | # - images/a_dot_ham.jpeg 69 | 70 | # An image asset can refer to one or more resolution-specific "variants", see 71 | # https://flutter.dev/to/resolution-aware-images 72 | 73 | # For details regarding adding assets from package dependencies, see 74 | # https://flutter.dev/to/asset-from-package 75 | 76 | # To add custom fonts to your application, add a fonts section here, 77 | # in this "flutter" section. Each entry in this list should have a 78 | # "family" key with the font family name, and a "fonts" key with a 79 | # list giving the asset and other descriptors for the font. For 80 | # example: 81 | # fonts: 82 | # - family: Schyler 83 | # fonts: 84 | # - asset: fonts/Schyler-Regular.ttf 85 | # - asset: fonts/Schyler-Italic.ttf 86 | # style: italic 87 | # - family: Trajan Pro 88 | # fonts: 89 | # - asset: fonts/TrajanPro.ttf 90 | # - asset: fonts/TrajanPro_Bold.ttf 91 | # weight: 700 92 | # 93 | # For details regarding fonts from package dependencies, 94 | # see https://flutter.dev/to/font-from-package 95 | assets: 96 | - lib/l10n/intl_en.arb 97 | - lib/l10n/intl_fa.arb --------------------------------------------------------------------------------