├── core ├── CHANGELOG.md ├── lib │ ├── src │ │ ├── error │ │ │ ├── exceptions.dart │ │ │ └── failures.dart │ │ ├── localizations │ │ │ ├── strings.dart │ │ │ ├── languages.dart │ │ │ ├── language_vi.dart │ │ │ └── localizations_delegate.dart │ │ ├── di │ │ │ └── locator.dart │ │ └── config │ │ │ └── app_config.dart │ └── core.dart ├── test │ └── core_test.dart ├── .metadata ├── pubspec.yaml ├── README.md ├── .gitignore └── pubspec.lock ├── data ├── CHANGELOG.md ├── test │ ├── data_test.dart │ ├── data_source │ │ └── remote │ │ │ └── screen_setting_api_test.dart │ └── repository │ │ └── internet_status_test.dart ├── lib │ ├── data.dart │ └── src │ │ ├── repository │ │ ├── api_error_handle_repo_impl.dart │ │ ├── popup_repo_impl.dart │ │ ├── internet_status_repo_impl.dart │ │ ├── screen_setting_repo_impl.dart │ │ ├── base │ │ │ └── base_repository.dart │ │ └── region_repo_impl.dart │ │ ├── data_source │ │ ├── remote │ │ │ ├── popup_api.dart │ │ │ ├── regions_api.dart │ │ │ ├── screen_setting_api.dart │ │ │ └── base │ │ │ │ └── api_client.dart │ │ ├── local │ │ │ └── shared_preferences_store.dart │ │ └── service │ │ │ └── firebase_remote_config.dart │ │ ├── models │ │ ├── metadata_model.dart │ │ ├── region_model.dart │ │ ├── list_response_model.dart │ │ ├── screen_setting_model.dart │ │ └── popup_model.dart │ │ └── di │ │ └── locator.dart ├── .metadata ├── README.md ├── pubspec.yaml └── .gitignore ├── domain ├── CHANGELOG.md ├── lib │ ├── src │ │ ├── common │ │ │ ├── error_type.dart │ │ │ └── result.dart │ │ ├── repositories │ │ │ ├── popup_repo.dart │ │ │ ├── internet_status_repo.dart │ │ │ ├── api_error_handler_repo.dart │ │ │ ├── screen_setting_repo.dart │ │ │ └── region_repo.dart │ │ ├── entities │ │ │ ├── metadata.dart │ │ │ ├── list_response.dart │ │ │ ├── screen_setting.dart │ │ │ ├── popup.dart │ │ │ ├── validator.dart │ │ │ └── region.dart │ │ ├── usecase │ │ │ ├── get_float_popup.dart │ │ │ ├── check_internet_status.dart │ │ │ ├── api_error_handler.dart │ │ │ ├── get_region.dart │ │ │ ├── update_region.dart │ │ │ ├── get_regions_local.dart │ │ │ ├── base │ │ │ │ └── use_case.dart │ │ │ ├── get_regions.dart │ │ │ ├── get_home_screen_setting.dart │ │ │ └── get_shopping_screen_setting.dart │ │ └── di │ │ │ └── locator.dart │ └── domain.dart ├── test │ └── domain_test.dart ├── .metadata ├── README.md ├── pubspec.yaml ├── .gitignore └── pubspec.lock ├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore └── Podfile ├── plugin └── utils │ ├── CHANGELOG.md │ ├── .metadata │ ├── pubspec.yaml │ ├── README.md │ ├── .gitignore │ ├── lib │ └── utils.dart │ ├── test │ └── utils_test.dart │ └── pubspec.lock ├── lib ├── presentation │ ├── routes │ │ ├── routes.dart │ │ └── app_routes.dart │ ├── common │ │ └── base_bloc.dart │ ├── values │ │ ├── assets.dart │ │ ├── constant.dart │ │ ├── themes.dart │ │ └── sizes.dart │ ├── extension │ │ └── screen_setting.dart │ ├── screen │ │ ├── home │ │ │ ├── home_bloc.dart │ │ │ ├── widget │ │ │ │ ├── home_bloc_container.dart │ │ │ │ ├── home_search_bar.dart │ │ │ │ └── home_sliver_app_bar.dart │ │ │ ├── home_setting_bloc.dart │ │ │ └── home_screen.dart │ │ ├── app_bottom_bar │ │ │ ├── app_bottom_bar_bloc.dart │ │ │ ├── widget │ │ │ │ ├── custom_bottom_navigation_bar.dart │ │ │ │ └── custom_bottom_app_bar.dart │ │ │ └── app_bottom_bar.dart │ │ ├── popup │ │ │ ├── floating_popup │ │ │ │ ├── floating_popup_bloc.dart │ │ │ │ └── floating_popup.dart │ │ │ └── region │ │ │ │ ├── region_bloc.dart │ │ │ │ └── region_dialog.dart │ │ ├── app │ │ │ ├── app_state.dart │ │ │ ├── app_bloc.dart │ │ │ └── app.dart │ │ └── shopping │ │ │ ├── shopping_bloc.dart │ │ │ ├── shopping_setting_bloc.dart │ │ │ ├── widget │ │ │ ├── shopping_search_bar.dart │ │ │ └── shopping_sliver_app_bar.dart │ │ │ └── shopping_screen.dart │ ├── widgets │ │ ├── choose_region.dart │ │ ├── search_bar.dart │ │ └── custom_sliver_app_bar_delegate.dart │ └── di │ │ └── locator.dart ├── main.dev.dart ├── main.prod.dart ├── main.staging.dart └── main.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── manifest.json └── index.html ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── 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 │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── flutter_clean_architecture │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── assets └── fonts │ └── Poppins │ ├── Poppins-Bold.ttf │ ├── Poppins-Medium.ttf │ └── Poppins-Regular.ttf ├── .metadata ├── test └── widget_test.dart ├── tool ├── coverage.sh └── generate_asset_metadata.sh ├── README.md ├── .gitignore ├── analysis_options.yaml ├── pubspec.yaml └── pubspec.lock /core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /data/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /domain/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /plugin/utils/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /lib/presentation/routes/routes.dart: -------------------------------------------------------------------------------- 1 | class Routes { 2 | static const String init = '/'; 3 | } 4 | -------------------------------------------------------------------------------- /domain/lib/src/common/error_type.dart: -------------------------------------------------------------------------------- 1 | enum ErrorType { POOR_NETWORK, NO_NETWORK, SERVER, GENERIC } 2 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /core/lib/src/error/exceptions.dart: -------------------------------------------------------------------------------- 1 | class ServerException implements Exception {} 2 | 3 | class CacheException implements Exception {} 4 | -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/assets/fonts/Poppins/Poppins-Bold.ttf -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/assets/fonts/Poppins/Poppins-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/assets/fonts/Poppins/Poppins-Regular.ttf -------------------------------------------------------------------------------- /core/test/core_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | void main() { 4 | test('adds one to input values', () {}); 5 | } 6 | -------------------------------------------------------------------------------- /data/test/data_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | void main() { 4 | test('adds one to input values', () {}); 5 | } 6 | -------------------------------------------------------------------------------- /domain/test/domain_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | void main() { 4 | test('adds one to input values', () {}); 5 | } 6 | -------------------------------------------------------------------------------- /domain/lib/src/repositories/popup_repo.dart: -------------------------------------------------------------------------------- 1 | import '../../domain.dart'; 2 | 3 | abstract class PopupRepo { 4 | Future> getFloatPopup(); 5 | } 6 | -------------------------------------------------------------------------------- /domain/lib/src/repositories/internet_status_repo.dart: -------------------------------------------------------------------------------- 1 | abstract class InternetStatusRepo { 2 | Future get isConnected; 3 | Stream onChange(); 4 | } 5 | -------------------------------------------------------------------------------- /lib/main.dev.dart: -------------------------------------------------------------------------------- 1 | import 'package:core/core.dart'; 2 | 3 | import 'main.dart'; 4 | 5 | void main() { 6 | Core.init(Environment.DEV); 7 | mainDelegate(); 8 | } 9 | -------------------------------------------------------------------------------- /lib/main.prod.dart: -------------------------------------------------------------------------------- 1 | import 'package:core/core.dart'; 2 | 3 | import 'main.dart'; 4 | 5 | void main() { 6 | Core.init(Environment.PROD); 7 | mainDelegate(); 8 | } 9 | -------------------------------------------------------------------------------- /lib/presentation/common/base_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | 3 | class BaseBloc extends BlocBase { 4 | BaseBloc() : super(false); 5 | } 6 | -------------------------------------------------------------------------------- /lib/presentation/values/assets.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Assets { 4 | static const appIcon = AssetImage('assets/app_icon.png'); 5 | } 6 | -------------------------------------------------------------------------------- /lib/presentation/values/constant.dart: -------------------------------------------------------------------------------- 1 | class Constant { 2 | double defpaultPadding = 16.0; 3 | Duration defaultDuration = const Duration(milliseconds: 300); 4 | } 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /lib/main.staging.dart: -------------------------------------------------------------------------------- 1 | import 'package:core/core.dart'; 2 | 3 | import 'main.dart'; 4 | 5 | void main() { 6 | Core.init(Environment.STAGING); 7 | mainDelegate(); 8 | } 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /data/lib/data.dart: -------------------------------------------------------------------------------- 1 | library data; 2 | 3 | import 'src/di/locator.dart'; 4 | 5 | class Data { 6 | static Future init() async { 7 | await setupLocator(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /domain/lib/src/repositories/api_error_handler_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | abstract class ApiErrorHandleRepo { 4 | void getError(Interceptor interceptor); 5 | } 6 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendy199x/Flutter_Clean_Architecture/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_clean_architecture/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_clean_architecture 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /domain/lib/src/entities/metadata.dart: -------------------------------------------------------------------------------- 1 | class Metadata { 2 | final int total; 3 | final int page; 4 | final int limit; 5 | 6 | const Metadata({ 7 | this.total = 0, 8 | this.page = 0, 9 | this.limit = 0, 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /domain/lib/src/entities/list_response.dart: -------------------------------------------------------------------------------- 1 | import 'metadata.dart'; 2 | 3 | class ListResponse { 4 | final Metadata metadata; 5 | final List data; 6 | 7 | ListResponse({ 8 | required this.metadata, 9 | required this.data, 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /lib/presentation/extension/screen_setting.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:domain/domain.dart'; 4 | import 'package:utils/utils.dart'; 5 | 6 | extension ScreenSettingExt on ScreenSetting { 7 | Color? get colorForHex => Utils.hexColor(color ?? ''); 8 | } 9 | -------------------------------------------------------------------------------- /core/lib/src/error/failures.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class Failure extends Equatable { 4 | @override 5 | List get props => []; 6 | } 7 | 8 | class ServerFailure extends Failure {} 9 | 10 | class CacheFailure extends Failure {} 11 | -------------------------------------------------------------------------------- /lib/presentation/screen/home/home_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | class HomeBloc extends BlocBase { 5 | final GetHomeScreenSetting getScreenSetting; 6 | 7 | HomeBloc(this.getScreenSetting) : super(null); 8 | } 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /domain/lib/src/repositories/screen_setting_repo.dart: -------------------------------------------------------------------------------- 1 | import '../common/result.dart'; 2 | import '../entities/screen_setting.dart'; 3 | 4 | abstract class ScreenSettingRepo { 5 | Future> getHomeSetting(); 6 | 7 | Future> getShoppingSetting(); 8 | } 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/get_float_popup.dart: -------------------------------------------------------------------------------- 1 | import '../../domain.dart'; 2 | 3 | class GetFloatPopup extends UseCaseResult { 4 | final PopupRepo _repo; 5 | 6 | GetFloatPopup(this._repo); 7 | 8 | @override 9 | Future> call({NoParams? params}) => _repo.getFloatPopup(); 10 | } 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /core/lib/src/localizations/strings.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'languages.dart'; 4 | 5 | class Strings { 6 | static late BuildContext _context; 7 | 8 | static init(BuildContext context) { 9 | _context = context; 10 | } 11 | 12 | static Languages get tr => Languages.of(_context); 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /domain/lib/src/entities/screen_setting.dart: -------------------------------------------------------------------------------- 1 | class ScreenSetting { 2 | final String? backgroundUrl; 3 | final String? color; 4 | final String? footBackgroundUrl; 5 | final String? text; 6 | 7 | const ScreenSetting({ 8 | this.backgroundUrl, 9 | this.color, 10 | this.footBackgroundUrl, 11 | this.text, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/check_internet_status.dart: -------------------------------------------------------------------------------- 1 | import '../repositories/internet_status_repo.dart'; 2 | import 'base/use_case.dart'; 3 | 4 | class CheckInternetStatus implements UseCase { 5 | final InternetStatusRepo _repo; 6 | 7 | CheckInternetStatus(this._repo); 8 | 9 | @override 10 | Stream call() => _repo.onChange(); 11 | } 12 | -------------------------------------------------------------------------------- /.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: ffb2ecea5223acdd139a5039be2f9c796962833d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/api_error_handler.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import '../repositories/api_error_handler_repo.dart'; 4 | 5 | class ApiErrorHandle { 6 | final ApiErrorHandleRepo _repo; 7 | 8 | ApiErrorHandle(this._repo); 9 | 10 | void call(Interceptor interceptor) { 11 | _repo.getError(interceptor); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/get_region.dart: -------------------------------------------------------------------------------- 1 | import '../entities/region.dart'; 2 | import '../repositories/region_repo.dart'; 3 | import 'base/use_case.dart'; 4 | 5 | class GetRegion implements UseCase { 6 | final RegionRepo _regionRepo; 7 | 8 | const GetRegion(this._regionRepo); 9 | 10 | @override 11 | Region? call() => _regionRepo.region; 12 | } 13 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /core/.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: f4abaa0735eba4dfd8f33f73363911d63931fe03 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /data/.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: f4abaa0735eba4dfd8f33f73363911d63931fe03 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /domain/.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: f4abaa0735eba4dfd8f33f73363911d63931fe03 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/update_region.dart: -------------------------------------------------------------------------------- 1 | import '../entities/region.dart'; 2 | import '../repositories/region_repo.dart'; 3 | 4 | class UpdateRegion { 5 | final RegionRepo _regionRepo; 6 | 7 | const UpdateRegion(this._regionRepo); 8 | 9 | Future call(Region region) async { 10 | return await _regionRepo.updateRegion(region); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /plugin/utils/.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: f4abaa0735eba4dfd8f33f73363911d63931fe03 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /plugin/utils/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: utils 2 | description: A new Flutter project. 3 | version: 0.0.1 4 | publish_to: none 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | intl: ^0.17.0 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | flutter: 21 | -------------------------------------------------------------------------------- /data/lib/src/repository/api_error_handle_repo_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:domain/domain.dart'; 3 | 4 | class ApiErrorHandleRepoImpl implements ApiErrorHandleRepo { 5 | final Dio _dio; 6 | 7 | ApiErrorHandleRepoImpl(this._dio); 8 | 9 | @override 10 | void getError(interceptor) { 11 | _dio.interceptors.add(interceptor); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /domain/lib/src/entities/popup.dart: -------------------------------------------------------------------------------- 1 | class Popup { 2 | final int id; 3 | final String? name; 4 | final String? imageUrl; 5 | final String? deepLink; 6 | final int? screenId; 7 | final String? status; 8 | 9 | Popup({ 10 | required this.id, 11 | this.name, 12 | this.imageUrl, 13 | this.deepLink, 14 | this.screenId, 15 | this.status, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/get_regions_local.dart: -------------------------------------------------------------------------------- 1 | import '../entities/region.dart'; 2 | import '../repositories/region_repo.dart'; 3 | import 'base/use_case.dart'; 4 | 5 | class GetRegionsLocal implements UseCase { 6 | final RegionRepo _regionRepo; 7 | 8 | const GetRegionsLocal(this._regionRepo); 9 | 10 | @override 11 | List call() => _regionRepo.getRegionsLocal(); 12 | } 13 | -------------------------------------------------------------------------------- /core/lib/src/di/locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_it/get_it.dart'; 2 | 3 | import '../config/app_config.dart'; 4 | import '../localizations/strings.dart'; 5 | 6 | final locator = GetIt.instance..allowReassignment = true; 7 | 8 | void setupLocator(Environment environment) { 9 | locator.registerSingleton(AppConfig(environment)); 10 | locator.registerLazySingleton(() => Strings()); 11 | } 12 | -------------------------------------------------------------------------------- /domain/lib/src/repositories/region_repo.dart: -------------------------------------------------------------------------------- 1 | import '../common/result.dart'; 2 | import '../entities/region.dart'; 3 | 4 | abstract class RegionRepo { 5 | Region? get region; 6 | 7 | Future>> getRegions(); 8 | 9 | List getRegionsLocal(); 10 | 11 | Future> getRegionById(int id); 12 | 13 | Future updateRegion(Region region); 14 | } 15 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/base/use_case.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | import '../../common/result.dart'; 4 | 5 | abstract class UseCase { 6 | call(); 7 | } 8 | 9 | abstract class UseCaseResult extends UseCase { 10 | Future> call({Params params}); 11 | } 12 | 13 | class NoParams extends Equatable { 14 | @override 15 | List get props => []; 16 | } 17 | -------------------------------------------------------------------------------- /core/lib/core.dart: -------------------------------------------------------------------------------- 1 | library core; 2 | 3 | import 'src/config/app_config.dart'; 4 | import 'src/di/locator.dart'; 5 | 6 | export 'src/config/app_config.dart'; 7 | export 'src/localizations/languages.dart'; 8 | export 'src/localizations/localizations_delegate.dart'; 9 | export 'src/localizations/strings.dart'; 10 | 11 | class Core { 12 | static void init(Environment environment) { 13 | setupLocator(environment); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /data/lib/src/data_source/remote/popup_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import 'base/api_client.dart'; 4 | 5 | abstract class PopupApi { 6 | Future getFloatPopup(); 7 | } 8 | 9 | class PopupApiImpl implements PopupApi { 10 | final ApiClient client; 11 | 12 | PopupApiImpl(this.client); 13 | 14 | @override 15 | Future getFloatPopup() { 16 | return client.get('/popup/default'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/presentation/screen/app_bottom_bar/app_bottom_bar_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | 3 | enum BottomBarIndex { 4 | home, 5 | coupon, 6 | shopping, 7 | newFeed, 8 | user, 9 | } 10 | 11 | class AppBottomBarBloc extends BlocBase { 12 | AppBottomBarBloc() : super(BottomBarIndex.home); 13 | 14 | BottomBarIndex get currentIndex => state; 15 | 16 | void changeTab(BottomBarIndex index) { 17 | emit(index); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | import 'presentation/di/locator.dart'; 5 | import 'presentation/screen/app/app.dart'; 6 | 7 | void mainDelegate() async { 8 | WidgetsFlutterBinding.ensureInitialized(); 9 | 10 | SystemChrome.setPreferredOrientations([ 11 | DeviceOrientation.portraitUp, 12 | DeviceOrientation.portraitDown, 13 | ]); 14 | 15 | await setupLocator(); 16 | 17 | runApp(const App()); 18 | } 19 | -------------------------------------------------------------------------------- /core/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: core 2 | description: A new Flutter project. 3 | version: 0.0.1 4 | publish_to: none 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | flutter_localizations: 14 | sdk: flutter 15 | 16 | get_it: 7.2.0 17 | equatable: 2.0.3 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | # The following section is specific to Flutter. 24 | flutter: 25 | 26 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/get_regions.dart: -------------------------------------------------------------------------------- 1 | import '../common/result.dart'; 2 | import '../entities/region.dart'; 3 | import '../repositories/region_repo.dart'; 4 | import 'base/use_case.dart'; 5 | 6 | class GetRegions implements UseCaseResult, NoParams> { 7 | final RegionRepo _regionRepo; 8 | 9 | const GetRegions(this._regionRepo); 10 | 11 | @override 12 | Future>> call({NoParams? params}) async { 13 | return await _regionRepo.getRegions(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/presentation/screen/home/widget/home_bloc_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HomeBlocContainer extends StatelessWidget { 4 | final Widget? child; 5 | 6 | const HomeBlocContainer({Key? key, this.child}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container( 11 | color: Colors.white, 12 | padding: const EdgeInsets.all(16).copyWith(right: 8), 13 | child: child, 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/README.md: -------------------------------------------------------------------------------- 1 | # core 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Dart 8 | [package](https://flutter.dev/developing-packages/), 9 | a library module containing code that can be shared easily across 10 | multiple Flutter or Dart projects. 11 | 12 | For help getting started with Flutter, view our 13 | [online documentation](https://flutter.dev/docs), which offers tutorials, 14 | samples, guidance on mobile development, and a full API reference. 15 | -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | # data 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Dart 8 | [package](https://flutter.dev/developing-packages/), 9 | a library module containing code that can be shared easily across 10 | multiple Flutter or Dart projects. 11 | 12 | For help getting started with Flutter, view our 13 | [online documentation](https://flutter.dev/docs), which offers tutorials, 14 | samples, guidance on mobile development, and a full API reference. 15 | -------------------------------------------------------------------------------- /domain/README.md: -------------------------------------------------------------------------------- 1 | # domain 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Dart 8 | [package](https://flutter.dev/developing-packages/), 9 | a library module containing code that can be shared easily across 10 | multiple Flutter or Dart projects. 11 | 12 | For help getting started with Flutter, view our 13 | [online documentation](https://flutter.dev/docs), which offers tutorials, 14 | samples, guidance on mobile development, and a full API reference. 15 | -------------------------------------------------------------------------------- /data/test/data_source/remote/screen_setting_api_test.dart: -------------------------------------------------------------------------------- 1 | // import 'package:data/src/data_source/remote/base/api_client.dart'; 2 | // import 'package:dio/dio.dart'; 3 | // import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | // late Dio dio; 7 | // late LogApiService log; 8 | // late ApiClient client; 9 | // 10 | // setUp(() { 11 | // dio = Dio(BaseOptions(baseUrl: baseUrl)); 12 | // log = LogApiService(baseUrl); 13 | // client = ApiClient(dio, log); 14 | // }); 15 | } 16 | -------------------------------------------------------------------------------- /plugin/utils/README.md: -------------------------------------------------------------------------------- 1 | # utils 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Dart 8 | [package](https://flutter.dev/developing-packages/), 9 | a library module containing code that can be shared easily across 10 | multiple Flutter or Dart projects. 11 | 12 | For help getting started with Flutter, view our 13 | [online documentation](https://flutter.dev/docs), which offers tutorials, 14 | samples, guidance on mobile development, and a full API reference. 15 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /core/lib/src/localizations/languages.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'language_vi.dart'; 4 | 5 | abstract class Languages { 6 | static Languages of(BuildContext context) { 7 | return Localizations.of(context, Languages) ?? LanguageVi(); 8 | } 9 | 10 | String get appName; 11 | 12 | String get home; 13 | 14 | String get coupon; 15 | 16 | String get shopping; 17 | 18 | String get newFeed; 19 | 20 | String get user; 21 | 22 | String get titleLocation; 23 | } 24 | -------------------------------------------------------------------------------- /lib/presentation/screen/popup/floating_popup/floating_popup_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | class FloatingPopupBloc extends BlocBase { 5 | FloatingPopupBloc(this.getFloatPopup) : super(null); 6 | 7 | final GetFloatPopup getFloatPopup; 8 | 9 | void init() async { 10 | final result = await getFloatPopup(); 11 | result.when(success: (popup) { 12 | if (popup.status == 'active') emit(popup); 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/get_home_screen_setting.dart: -------------------------------------------------------------------------------- 1 | import '../common/result.dart'; 2 | import '../entities/screen_setting.dart'; 3 | import '../repositories/screen_setting_repo.dart'; 4 | import 'base/use_case.dart'; 5 | 6 | class GetHomeScreenSetting implements UseCaseResult { 7 | final ScreenSettingRepo _repo; 8 | 9 | GetHomeScreenSetting(this._repo); 10 | 11 | @override 12 | Future> call({NoParams? params}) async { 13 | return await _repo.getHomeSetting(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/lib/src/localizations/language_vi.dart: -------------------------------------------------------------------------------- 1 | import 'languages.dart'; 2 | 3 | class LanguageVi extends Languages { 4 | @override 5 | String get appName => 'Flutter'; 6 | 7 | @override 8 | String get coupon => 'Ưu đãi'; 9 | 10 | @override 11 | String get home => 'Trang chủ'; 12 | 13 | @override 14 | String get newFeed => 'Bài viết'; 15 | 16 | @override 17 | String get shopping => 'Mua sắm'; 18 | 19 | @override 20 | String get user => 'Cá nhân'; 21 | 22 | @override 23 | String get titleLocation => 'Khu vực'; 24 | } 25 | -------------------------------------------------------------------------------- /domain/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: domain 2 | description: A new Flutter project. 3 | version: 0.0.1 4 | publish_to: none 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | get_it: 7.2.0 15 | equatable: 2.0.3 16 | 17 | dio: 4.0.0 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | # flutter packages pub run build_runner watch 24 | build_runner: 25 | 26 | # The following section is specific to Flutter. 27 | flutter: 28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /domain/lib/src/entities/validator.dart: -------------------------------------------------------------------------------- 1 | class Validator { 2 | // Positive integer 3 | static bool validateNumber(String text) { 4 | Pattern pattern = r'^(\(?\+?[0-9]*\)?)?[0-9(\)]*$'; 5 | RegExp regex = new RegExp(pattern.toString()); 6 | return regex.hasMatch(text); 7 | } 8 | 9 | // Only unsigned letters and positive integers 10 | static bool validateNumberAndText(String text) { 11 | Pattern pattern = r"^[0-9a-z]"; 12 | RegExp regex = new RegExp(pattern.toString()); 13 | return regex.hasMatch(text); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /domain/lib/src/usecase/get_shopping_screen_setting.dart: -------------------------------------------------------------------------------- 1 | import '../common/result.dart'; 2 | import '../entities/screen_setting.dart'; 3 | import '../repositories/screen_setting_repo.dart'; 4 | import 'base/use_case.dart'; 5 | 6 | class GetShoppingScreenSetting implements UseCaseResult { 7 | final ScreenSettingRepo _repo; 8 | 9 | GetShoppingScreenSetting(this._repo); 10 | 11 | @override 12 | Future> call({NoParams? params}) async { 13 | return await _repo.getShoppingSetting(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/presentation/screen/app/app_state.dart: -------------------------------------------------------------------------------- 1 | part of '../../../presentation/screen/app/app_bloc.dart'; 2 | 3 | class AppState extends Equatable { 4 | final Region? region; 5 | 6 | const AppState({ 7 | this.region, 8 | }); 9 | 10 | @override 11 | List get props => [region]; 12 | 13 | AppState copyWith({ 14 | Region? region, 15 | }) { 16 | return AppState( 17 | region: region ?? this.region, 18 | ); 19 | } 20 | 21 | String get regionName => region?.id == Region.idSouthern ? 'Miền Nam' : 'Miền Bắc'; 22 | } 23 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | import 'package:flutter_test/flutter_test.dart'; 8 | 9 | void main() { 10 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {}); 11 | } 12 | -------------------------------------------------------------------------------- /tool/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Usage: 4 | # flutter test --coverage --merge-coverage 5 | # sh coverage.sh core data domain presentation 6 | 7 | mkdir coverage 8 | touch coverage/lcov.base.info 9 | 10 | for module in "$@" 11 | do 12 | echo "testing $module..." 13 | cd "$module" || exit 14 | flutter test --coverage --coverage-path=coverage/"${module}".info 15 | var1=lib/ 16 | var2=${module}/lib/ 17 | 18 | sed -i "s@${var1}@${var2}@" coverage/"${module}".info 19 | cat coverage/"${module}".info >> ../coverage/lcov.base.info 20 | cd .. 21 | done -------------------------------------------------------------------------------- /data/lib/src/repository/popup_repo_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | 3 | import '../data_source/remote/popup_api.dart'; 4 | import '../models/popup_model.dart'; 5 | import 'base/base_repository.dart'; 6 | 7 | class PopupRepoImpl extends BaseRepository implements PopupRepo { 8 | final PopupApi _popupApi; 9 | 10 | PopupRepoImpl(this._popupApi); 11 | 12 | @override 13 | Future> getFloatPopup() { 14 | return safeApiCall( 15 | _popupApi.getFloatPopup(), 16 | mapper: (data) => PopupModel.fromJson(data), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data/lib/src/data_source/remote/regions_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import 'base/api_client.dart'; 4 | 5 | abstract class RegionApi { 6 | Future regions(); 7 | 8 | Future regionById(int id); 9 | } 10 | 11 | class RegionApiImpl implements RegionApi { 12 | final ApiClient client; 13 | 14 | RegionApiImpl(this.client); 15 | 16 | @override 17 | Future regions() { 18 | return client.get('/regions'); 19 | } 20 | 21 | @override 22 | Future regionById(int id) { 23 | return client.get('/regions/$id'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/presentation/screen/home/home_setting_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | class HomeSettingBloc extends BlocBase { 5 | final GetHomeScreenSetting getHomeSetting; 6 | static const initState = ScreenSetting( 7 | text: 'Search products...', 8 | color: '#949494', 9 | ); 10 | 11 | HomeSettingBloc(this.getHomeSetting) : super(initState) { 12 | init(); 13 | } 14 | 15 | Future init() async { 16 | final result = await getHomeSetting(); 17 | result.when(success: emit); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_clean_architecture 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /lib/presentation/screen/popup/region/region_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | class RegionBloc extends BlocBase { 5 | RegionBloc( 6 | this._getRegionsLocal, 7 | this._updateRegion, 8 | ) : super(false); 9 | 10 | final GetRegionsLocal _getRegionsLocal; 11 | final UpdateRegion _updateRegion; 12 | 13 | List get getRegions => _getRegionsLocal(); 14 | 15 | Future updateRegion(Region region) async { 16 | //Todo xử lý status loading 17 | final result = await _updateRegion(region); 18 | return result; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/presentation/screen/shopping/shopping_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | class ShoppingBloc extends BlocBase { 5 | final GetShoppingScreenSetting getScreenSetting; 6 | 7 | ShoppingBloc(this.getScreenSetting) : super(null); 8 | 9 | Future getPageSetting() async { 10 | final result = await getScreenSetting(); 11 | // ignore: avoid_print 12 | print('isSuccessful: ${result.isSuccessful}'); 13 | result.when( 14 | success: (data) => print('data: ${data.text}'), 15 | error: (error) => print(error.message), 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /data/lib/src/data_source/remote/screen_setting_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import 'base/api_client.dart'; 4 | 5 | abstract class ScreenSettingApi { 6 | Future getHomeSetting(); 7 | 8 | Future getShoppingSetting(); 9 | } 10 | 11 | class ScreenSettingApiImpl implements ScreenSettingApi { 12 | final ApiClient client; 13 | 14 | ScreenSettingApiImpl(this.client); 15 | 16 | @override 17 | Future getHomeSetting() { 18 | return client.get('/home'); 19 | } 20 | 21 | @override 22 | Future getShoppingSetting() { 23 | return client.get('/shopping'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /data/test/repository/internet_status_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:data/src/repository/internet_status_repo_impl.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | late InternetStatusRepoImpl internetStatus; 6 | 7 | setUp(() { 8 | internetStatus = InternetStatusRepoImpl(); 9 | }); 10 | 11 | test('Un awaited isConnected', () async { 12 | final result = internetStatus.isConnected; 13 | expect(result, isA>()); 14 | }); 15 | 16 | test('Awaited isConnected', () async { 17 | final result = await internetStatus.isConnected; 18 | expect(result, isA()); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /lib/presentation/screen/shopping/shopping_setting_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | class ShoppingSettingBloc extends BlocBase { 5 | final GetShoppingScreenSetting _getShoppingScreenSetting; 6 | static const initState = ScreenSetting( 7 | text: 'Search products...', 8 | color: '#949494', 9 | ); 10 | 11 | ShoppingSettingBloc(this._getShoppingScreenSetting) : super(initState) { 12 | init(); 13 | } 14 | 15 | Future init() async { 16 | final result = await _getShoppingScreenSetting(); 17 | result.when(success: emit); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data/lib/src/models/metadata_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:utils/utils.dart'; 3 | 4 | class MetadataModel extends Metadata { 5 | MetadataModel({ 6 | int total = 0, 7 | int page = 0, 8 | int limit = 0, 9 | }) : super( 10 | total: total, 11 | page: page, 12 | limit: limit, 13 | ); 14 | 15 | factory MetadataModel.fromJson(Map json) { 16 | return MetadataModel( 17 | total: Utils.parseJsonToInt('total', json)!, 18 | page: Utils.parseJsonToInt('page', json)!, 19 | limit: Utils.parseJsonToInt('limit', json)!, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /data/lib/src/models/region_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:utils/utils.dart'; 3 | 4 | class RegionModel extends Region { 5 | const RegionModel({ 6 | required int id, 7 | required String name, 8 | }) : super( 9 | id: id, 10 | name: name, 11 | ); 12 | 13 | factory RegionModel.fromJson(Map json) { 14 | return RegionModel( 15 | id: Utils.parseJsonToInt('id', json)!, 16 | name: Utils.parseJson('name', json)!, 17 | ); 18 | } 19 | 20 | static List fromList(List json) { 21 | return Utils.listFromJson( 22 | json, 23 | (item) => RegionModel.fromJson(item), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /data/lib/src/data_source/local/shared_preferences_store.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:domain/domain.dart'; 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | 6 | import '../../models/region_model.dart'; 7 | 8 | class SharedPrefsStore { 9 | static const String KEY_REGION = 'KEY_REGION'; 10 | final SharedPreferences _prefs; 11 | 12 | SharedPrefsStore(this._prefs); 13 | 14 | Region? getRegion() { 15 | final jsonString = _prefs.getString(KEY_REGION); 16 | if (jsonString != null) { 17 | return RegionModel.fromJson(jsonDecode(jsonString)); 18 | } 19 | } 20 | 21 | Future saveRegion(Region region) async { 22 | return await _prefs.setString(KEY_REGION, jsonEncode(region.toJson())); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /data/lib/src/repository/internet_status_repo_impl.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:domain/domain.dart'; 4 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 5 | 6 | class InternetStatusRepoImpl implements InternetStatusRepo { 7 | final _connectivity = InternetConnectionChecker(); 8 | 9 | @override 10 | Stream onChange() => _connectivity.onStatusChange.transform( 11 | StreamTransformer.fromHandlers(handleData: (status, sink) { 12 | if (status == InternetConnectionStatus.connected) { 13 | sink.add(true); 14 | } 15 | sink.add(false); 16 | }), 17 | ); 18 | 19 | @override 20 | Future get isConnected async => await _connectivity.hasConnection; 21 | } 22 | -------------------------------------------------------------------------------- /domain/lib/src/entities/region.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class Region extends Equatable { 4 | final int id; 5 | final String name; 6 | 7 | const Region({ 8 | required this.id, 9 | required this.name, 10 | }); 11 | 12 | static const int idNorthern = 1; 13 | static const int idSouthern = 2; 14 | 15 | static List defaultRegions = [ 16 | const Region( 17 | id: idNorthern, 18 | name: 'Miền Bắc', 19 | ), 20 | const Region( 21 | id: idSouthern, 22 | name: 'Miền Nam', 23 | ), 24 | ]; 25 | 26 | Map toJson() { 27 | return { 28 | 'id': id, 29 | 'name': name, 30 | }; 31 | } 32 | 33 | @override 34 | List get props => [ 35 | id, 36 | name, 37 | ]; 38 | } 39 | -------------------------------------------------------------------------------- /data/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: data 2 | description: A new Flutter project. 3 | version: 0.0.1 4 | publish_to: none 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | core: 15 | path: ../core 16 | domain: 17 | path: ../domain 18 | 19 | utils: 20 | path: ../plugin/utils 21 | 22 | # inversion of control 23 | get_it: 7.2.0 24 | # network 25 | dio: 4.0.0 26 | shared_preferences: 2.0.8 27 | internet_connection_checker: 0.0.1+2 28 | package_info: 2.0.2 29 | firebase_remote_config: ^0.11.0+1 30 | 31 | dev_dependencies: 32 | flutter_test: 33 | sdk: flutter 34 | 35 | # flutter packages pub run build_runner watch 36 | build_runner: 37 | mockito: ^5.0.16 38 | 39 | # The following section is specific to Flutter. 40 | flutter: 41 | 42 | -------------------------------------------------------------------------------- /data/lib/src/models/list_response_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:utils/utils.dart'; 3 | 4 | import 'metadata_model.dart'; 5 | 6 | class ListResponseModel extends ListResponse { 7 | ListResponseModel({ 8 | required Metadata metadata, 9 | required List data, 10 | }) : super( 11 | metadata: metadata, 12 | data: data, 13 | ); 14 | 15 | factory ListResponseModel.fromJson( 16 | String key, 17 | Map json, 18 | T Function(Map) factoryFunction, 19 | ) { 20 | final metadataJson = Utils.parseJson('metadata', json); 21 | final dataList = Utils.parseJsonToList(key, json, factoryFunction); 22 | return ListResponseModel( 23 | metadata: MetadataModel.fromJson(metadataJson), 24 | data: dataList, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /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 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /data/lib/src/repository/screen_setting_repo_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | 3 | import '../data_source/remote/screen_setting_api.dart'; 4 | import '../models/screen_setting_model.dart'; 5 | import 'base/base_repository.dart'; 6 | 7 | class ScreenSettingRepoImpl extends BaseRepository implements ScreenSettingRepo { 8 | final ScreenSettingApi _remoteApi; 9 | 10 | ScreenSettingRepoImpl(this._remoteApi); 11 | 12 | @override 13 | Future> getHomeSetting() { 14 | return safeApiCall( 15 | _remoteApi.getHomeSetting(), 16 | mapper: (data) => ScreenSettingModel.fromJson(data), 17 | ); 18 | } 19 | 20 | @override 21 | Future> getShoppingSetting() { 22 | return safeApiCall( 23 | _remoteApi.getShoppingSetting(), 24 | mapper: (data) => ScreenSettingModel.fromJson(data), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/presentation/routes/app_routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | import '../screen/app_bottom_bar/app_bottom_bar.dart'; 5 | import 'routes.dart'; 6 | 7 | class AppRoutes { 8 | static final navigatorKey = GlobalKey(); 9 | static final routeObserver = RouteObserver(); 10 | 11 | static Map routes = {}; 12 | 13 | static Route? onGenerateRoute(RouteSettings settings) { 14 | WidgetBuilder? builder; 15 | 16 | switch (settings.name) { 17 | case Routes.init: 18 | builder = (context) => const AppBottomBarScreen(); 19 | break; 20 | 21 | default: 22 | break; 23 | } 24 | 25 | if (builder != null) { 26 | return MaterialPageRoute( 27 | settings: settings, 28 | builder: builder, 29 | ); 30 | } 31 | 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /domain/lib/src/di/locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_it/get_it.dart'; 2 | 3 | import '../../domain.dart'; 4 | 5 | final locator = GetIt.instance..allowReassignment = true; 6 | 7 | void setupLocator() { 8 | /// Use case 9 | locator.registerFactory(() => CheckInternetStatus(locator())); 10 | locator.registerFactory(() => ApiErrorHandle(locator())); 11 | locator.registerFactory(() => GetRegions(locator())); 12 | locator.registerFactory(() => GetRegionsLocal(locator())); 13 | locator.registerFactory(() => GetRegion(locator())); 14 | locator.registerFactory(() => UpdateRegion(locator())); 15 | locator.registerFactory(() => GetHomeScreenSetting(locator())); 16 | locator.registerFactory(() => GetShoppingScreenSetting(locator())); 17 | locator.registerFactory(() => GetFloatPopup(locator())); 18 | } 19 | -------------------------------------------------------------------------------- /lib/presentation/screen/home/widget/home_search_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | 5 | import '../../../extension/screen_setting.dart'; 6 | import '../../../widgets/search_bar.dart'; 7 | import '../home_setting_bloc.dart'; 8 | 9 | class HomeSearchBar extends StatelessWidget { 10 | const HomeSearchBar({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return BlocBuilder( 15 | buildWhen: (prev, current) => prev.text != current.text || prev.color != current.color, 16 | builder: (_, state) => SearchBarWithRegion( 17 | onPressed: () { 18 | // Todo goto search screen 19 | }, 20 | label: state.text ?? '', 21 | labelStyle: TextStyle(color: state.colorForHex), 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /data/lib/src/models/screen_setting_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:utils/utils.dart'; 3 | 4 | class ScreenSettingModel extends ScreenSetting { 5 | ScreenSettingModel({ 6 | final String? backgroundUrl, 7 | final String? color, 8 | final String? footBackgroundUrl, 9 | final String? noticeDeliveryEpidemic, 10 | final String? text, 11 | }) : super( 12 | backgroundUrl: backgroundUrl, 13 | color: color, 14 | footBackgroundUrl: footBackgroundUrl, 15 | text: text, 16 | ); 17 | 18 | factory ScreenSettingModel.fromJson(Map json) { 19 | return ScreenSettingModel( 20 | backgroundUrl: Utils.parseJson('background_url', json), 21 | color: Utils.parseJson('color', json), 22 | footBackgroundUrl: Utils.parseJson('foot_background_url', json), 23 | text: Utils.parseJson('text', json), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/presentation/screen/shopping/widget/shopping_search_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | 5 | import '../../../extension/screen_setting.dart'; 6 | import '../../../widgets/search_bar.dart'; 7 | import '../shopping_setting_bloc.dart'; 8 | 9 | class ShoppingSearchBar extends StatelessWidget { 10 | const ShoppingSearchBar({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return BlocBuilder( 15 | buildWhen: (prev, current) => prev.text != current.text || prev.color != current.color, 16 | builder: (_, state) => SearchBarWithRegion( 17 | onPressed: () { 18 | // Todo goto search screen 19 | }, 20 | label: state.text ?? '', 21 | labelStyle: TextStyle(color: state.colorForHex), 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/presentation/screen/home/widget/home_sliver_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../../widgets/custom_sliver_app_bar_delegate.dart'; 5 | import 'home_search_bar.dart'; 6 | 7 | class HomeSliverAppBar extends CustomSliverAppBarDelegate { 8 | HomeSliverAppBar() 9 | : super( 10 | expandedHeight: 300, 11 | title: const FlutterLogo(), 12 | leading: IconButton( 13 | onPressed: () {}, 14 | splashRadius: 24, 15 | icon: const Icon( 16 | CupertinoIcons.app_badge, 17 | size: 24, 18 | ), 19 | ), 20 | autoHideLeading: false, 21 | actions: [ 22 | IconButton( 23 | onPressed: () {}, 24 | splashRadius: 24, 25 | icon: const Icon(CupertinoIcons.cart), 26 | ), 27 | ], 28 | searchBar: const HomeSearchBar(), 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /core/lib/src/localizations/localizations_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'language_vi.dart'; 4 | import 'languages.dart'; 5 | 6 | class AppLocalizationsDelegate extends LocalizationsDelegate { 7 | const AppLocalizationsDelegate._(); 8 | 9 | static const AppLocalizationsDelegate delegate = AppLocalizationsDelegate._(); 10 | 11 | static const List supportedLocales = const [ 12 | const Locale('vi', ''), 13 | ]; 14 | 15 | @override 16 | bool isSupported(Locale locale) => [ 17 | 'vi', 18 | ].contains(locale.languageCode); 19 | 20 | @override 21 | Future load(Locale locale) => _load(locale); 22 | 23 | static Future _load(Locale locale) async { 24 | switch (locale.languageCode) { 25 | case 'vi': 26 | return LanguageVi(); 27 | default: 28 | return LanguageVi(); 29 | } 30 | } 31 | 32 | @override 33 | bool shouldReload(LocalizationsDelegate old) => false; 34 | } 35 | -------------------------------------------------------------------------------- /core/lib/src/config/app_config.dart: -------------------------------------------------------------------------------- 1 | enum Environment { DEV, STAGING, PROD } 2 | 3 | class AppConfig { 4 | late Environment environment; 5 | late Map _config; 6 | 7 | String get baseUrl => _config[_Config.BASE_URL]; 8 | 9 | AppConfig(this.environment) { 10 | switch (environment) { 11 | case Environment.DEV: 12 | _config = _Config.devConstants; 13 | break; 14 | case Environment.STAGING: 15 | _config = _Config.stagingConstants; 16 | break; 17 | case Environment.PROD: 18 | _config = _Config.prodConstants; 19 | break; 20 | } 21 | } 22 | } 23 | 24 | class _Config { 25 | static const BASE_URL = 'BASE_URL'; 26 | 27 | static Map devConstants = { 28 | BASE_URL: 'https://api-dev', 29 | }; 30 | 31 | static Map stagingConstants = { 32 | BASE_URL: 'https://api-staging', 33 | }; 34 | 35 | static Map prodConstants = { 36 | BASE_URL: 'https://api-product', 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/presentation/screen/shopping/widget/shopping_sliver_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../../widgets/custom_sliver_app_bar_delegate.dart'; 5 | import 'shopping_search_bar.dart'; 6 | 7 | class ShoppingSliverAppBar extends CustomSliverAppBarDelegate { 8 | ShoppingSliverAppBar() 9 | : super( 10 | expandedHeight: 300, 11 | title: const FlutterLogo(size: 24), 12 | leading: IconButton( 13 | onPressed: () { 14 | /// todo open category 15 | }, 16 | splashRadius: 24, 17 | icon: const Icon(Icons.menu_rounded, size: 32), 18 | ), 19 | autoHideLeading: false, 20 | actions: [ 21 | IconButton( 22 | onPressed: () { 23 | ///todo open cart 24 | }, 25 | splashRadius: 24, 26 | icon: const Icon(CupertinoIcons.cart_fill), 27 | ), 28 | ], 29 | searchBar: const ShoppingSearchBar(), 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_clean_architecture", 3 | "short_name": "flutter_clean_architecture", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /data/lib/src/data_source/service/firebase_remote_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_remote_config/firebase_remote_config.dart'; 2 | 3 | class FirebaseRemoteConfig { 4 | /// Latest version 5 | static const String VERSION = 'version'; 6 | 7 | // The latest version is required to update 8 | static const String LAST_CONSTRAINT_VERSION = 'last_constraint_version'; 9 | 10 | // Force update if constraint = true 11 | static const String CONSTRAINT = 'constraint'; 12 | 13 | static const Map DEFAULT_REMOTE_CONFIG = { 14 | VERSION: '2.6.6', 15 | LAST_CONSTRAINT_VERSION: '2.6.0', 16 | CONSTRAINT: false, 17 | }; 18 | 19 | final RemoteConfig _remoteConfig; 20 | 21 | FirebaseRemoteConfig(this._remoteConfig); 22 | 23 | Future getConfig() async { 24 | await _remoteConfig.setDefaults(DEFAULT_REMOTE_CONFIG); 25 | await _remoteConfig.setConfigSettings(RemoteConfigSettings( 26 | fetchTimeout: const Duration(seconds: 30), 27 | minimumFetchInterval: const Duration(hours: 1), 28 | )); 29 | await _remoteConfig.fetchAndActivate(); 30 | return _remoteConfig; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/presentation/widgets/choose_region.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import '../screen/app/app_bloc.dart'; 5 | import '../screen/popup/region/region_dialog.dart'; 6 | 7 | class ChooseRegion extends StatelessWidget { 8 | const ChooseRegion({Key? key}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return TextButton( 13 | onPressed: RegionDialog.show, 14 | child: Row(children: [ 15 | BlocBuilder( 16 | buildWhen: (prev, current) => prev.region != current.region, 17 | builder: (_, state) => Text( 18 | state.regionName, 19 | style: TextStyle( 20 | fontWeight: FontWeight.w500, 21 | color: Theme.of(context).disabledColor, 22 | ), 23 | )), 24 | const SizedBox(width: 2), 25 | Icon( 26 | Icons.keyboard_arrow_down, 27 | color: Theme.of(context).disabledColor, 28 | size: 16, 29 | ), 30 | ]), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /data/lib/src/models/popup_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:utils/utils.dart'; 3 | 4 | class PopupModel extends Popup { 5 | PopupModel({ 6 | required int id, 7 | String? name, 8 | String? imageUrl, 9 | String? deepLink, 10 | int? screenId, 11 | String? status, 12 | }) : super( 13 | id: id, 14 | name: name, 15 | imageUrl: imageUrl, 16 | deepLink: deepLink, 17 | screenId: screenId, 18 | status: status, 19 | ); 20 | 21 | factory PopupModel.fromJson(Map json) { 22 | return PopupModel( 23 | id: Utils.parseJsonToInt('id', json)!, 24 | name: Utils.parseJson('name', json), 25 | imageUrl: Utils.parseJson('image_url', json), 26 | deepLink: Utils.parseJson('deep_link', json), 27 | screenId: Utils.parseJsonToInt('screen_id', json), 28 | status: Utils.parseJson('status', json), 29 | ); 30 | } 31 | 32 | static List fromList(List json) { 33 | return Utils.listFromJson( 34 | json, 35 | (item) => PopupModel.fromJson(item), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /domain/lib/domain.dart: -------------------------------------------------------------------------------- 1 | library domain; 2 | 3 | import 'src/di/locator.dart'; 4 | 5 | export 'src/common/error_type.dart'; 6 | export 'src/common/result.dart'; 7 | export 'src/entities/list_response.dart'; 8 | export 'src/entities/metadata.dart'; 9 | export 'src/entities/popup.dart'; 10 | export 'src/entities/region.dart'; 11 | export 'src/entities/screen_setting.dart'; 12 | export 'src/entities/validator.dart'; 13 | export 'src/repositories/api_error_handler_repo.dart'; 14 | export 'src/repositories/internet_status_repo.dart'; 15 | export 'src/repositories/popup_repo.dart'; 16 | export 'src/repositories/region_repo.dart'; 17 | export 'src/repositories/screen_setting_repo.dart'; 18 | export 'src/usecase/api_error_handler.dart'; 19 | export 'src/usecase/base/use_case.dart'; 20 | export 'src/usecase/check_internet_status.dart'; 21 | export 'src/usecase/get_float_popup.dart'; 22 | export 'src/usecase/get_home_screen_setting.dart'; 23 | export 'src/usecase/get_region.dart'; 24 | export 'src/usecase/get_regions.dart'; 25 | export 'src/usecase/get_regions_local.dart'; 26 | export 'src/usecase/get_shopping_screen_setting.dart'; 27 | export 'src/usecase/update_region.dart'; 28 | 29 | class Domain { 30 | static Future init() async { 31 | setupLocator(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /data/lib/src/repository/base/base_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:domain/domain.dart'; 3 | 4 | typedef ResponseToModel = Model Function(dynamic); 5 | 6 | abstract class BaseRepository { 7 | Future> safeApiCall( 8 | Future call, { 9 | required ResponseToModel mapper, 10 | }) async { 11 | try { 12 | var response = await call; 13 | return Success(mapper.call(response.data)); 14 | } on Exception catch (exception) { 15 | if (exception is DioError) { 16 | switch (exception.type) { 17 | case DioErrorType.connectTimeout: 18 | case DioErrorType.sendTimeout: 19 | case DioErrorType.receiveTimeout: 20 | case DioErrorType.cancel: 21 | return Error(ErrorType.POOR_NETWORK, 'Hãy kiểm tra kết nối mạng của bạn!'); 22 | 23 | case DioErrorType.other: 24 | return Error(ErrorType.NO_NETWORK, 'Không có kết nối Internet!'); 25 | 26 | case DioErrorType.response: 27 | return ServerError( 28 | type: ErrorType.SERVER, 29 | error: exception.message, 30 | code: exception.response!.statusCode!, 31 | ); 32 | } 33 | } 34 | return Error(ErrorType.GENERIC, "Xảy ra lỗi, hãy thử lại!"); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/presentation/screen/app/app_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:domain/domain.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | import '../popup/region/region_dialog.dart'; 7 | 8 | part 'app_state.dart'; 9 | 10 | class AppBloc extends BlocBase { 11 | final ApiErrorHandle apiErrorHandle; 12 | final GetRegions getRegions; 13 | final GetRegion region; 14 | 15 | AppBloc({ 16 | required this.apiErrorHandle, 17 | required this.getRegions, 18 | required this.region, 19 | }) : super(AppState( 20 | region: region(), 21 | )) { 22 | init(); 23 | } 24 | 25 | void init() { 26 | apiErrorHandling(); 27 | getAppRegions(); 28 | } 29 | 30 | void apiErrorHandling() { 31 | apiErrorHandle(InterceptorsWrapper( 32 | onError: (error, handle) { 33 | if (error.type == DioErrorType.response) { 34 | //Todo Logout user case 35 | } 36 | return handle.next(error); 37 | }, 38 | )); 39 | } 40 | 41 | Future getAppRegions() async { 42 | final result = await getRegions(); 43 | result.when( 44 | success: (data) { 45 | if (region() == null) { 46 | RegionDialog.show(); 47 | } 48 | }, 49 | ); 50 | } 51 | 52 | Future updateRegions(Region region) async { 53 | emit(state.copyWith(region: region)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /data/lib/src/repository/region_repo_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | 3 | import '../data_source/local/shared_preferences_store.dart'; 4 | import '../data_source/remote/regions_api.dart'; 5 | import '../models/region_model.dart'; 6 | import 'base/base_repository.dart'; 7 | 8 | class RegionRepoImpl extends BaseRepository implements RegionRepo { 9 | final RegionApi _regionApi; 10 | final SharedPrefsStore _prefs; 11 | 12 | List _defaultRegion = Region.defaultRegions; 13 | 14 | Region? _region = null; 15 | 16 | RegionRepoImpl(this._regionApi, this._prefs); 17 | 18 | @override 19 | Region? get region => _region ?? _prefs.getRegion(); 20 | 21 | @override 22 | Future>> getRegions() async { 23 | final result = await safeApiCall>( 24 | _regionApi.regions(), 25 | mapper: (data) => RegionModel.fromList(data), 26 | ); 27 | if (result.isSuccessful) { 28 | _defaultRegion = (result as Success>).data; 29 | } 30 | return result; 31 | } 32 | 33 | @override 34 | List getRegionsLocal() => _defaultRegion; 35 | 36 | @override 37 | Future updateRegion(Region region) async { 38 | _prefs.saveRegion(region); 39 | _region = region; 40 | return true; 41 | } 42 | 43 | @override 44 | Future> getRegionById(int id) { 45 | return safeApiCall( 46 | _regionApi.regionById(id), 47 | mapper: (data) => RegionModel.fromJson(data), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/presentation/di/locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:data/data.dart'; 2 | import 'package:domain/domain.dart'; 3 | import 'package:get_it/get_it.dart'; 4 | 5 | import '../screen/app/app_bloc.dart'; 6 | import '../screen/app_bottom_bar/app_bottom_bar_bloc.dart'; 7 | import '../screen/home/home_bloc.dart'; 8 | import '../screen/home/home_setting_bloc.dart'; 9 | import '../screen/popup/floating_popup/floating_popup_bloc.dart'; 10 | import '../screen/popup/region/region_bloc.dart'; 11 | import '../screen/shopping/shopping_bloc.dart'; 12 | import '../screen/shopping/shopping_setting_bloc.dart'; 13 | 14 | final locator = GetIt.instance..allowReassignment = true; 15 | 16 | Future setupLocator() async { 17 | _registerBloc(); 18 | await Data.init(); 19 | Domain.init(); 20 | } 21 | 22 | void _registerBloc() { 23 | locator.registerFactory(() => AppBloc( 24 | apiErrorHandle: locator(), 25 | getRegions: locator(), 26 | region: locator(), 27 | )); 28 | locator.registerFactory(() => AppBottomBarBloc()); 29 | 30 | locator.registerFactory(() => HomeBloc(locator())); 31 | locator.registerFactory(() => ShoppingBloc(locator())); 32 | locator.registerFactory(() => RegionBloc( 33 | locator(), 34 | locator(), 35 | )); 36 | 37 | locator.registerFactory(() => FloatingPopupBloc(locator())); 38 | 39 | ///Home page 40 | locator.registerFactory(() => HomeSettingBloc(locator())); 41 | 42 | ///Shopping page 43 | locator.registerFactory(() => ShoppingSettingBloc(locator())); 44 | } 45 | -------------------------------------------------------------------------------- /lib/presentation/screen/shopping/shopping_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import '../../di/locator.dart'; 5 | import 'shopping_bloc.dart'; 6 | import 'shopping_setting_bloc.dart'; 7 | import 'widget/shopping_sliver_app_bar.dart'; 8 | 9 | class ShoppingScreen extends StatelessWidget { 10 | const ShoppingScreen({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return MultiBlocProvider( 15 | providers: [ 16 | BlocProvider(create: (_) => locator()), 17 | BlocProvider(create: (_) => locator()), 18 | ], 19 | child: _ShoppingScreenBody(), 20 | ); 21 | } 22 | } 23 | 24 | class _ShoppingScreenBody extends StatelessWidget { 25 | @override 26 | Widget build(BuildContext context) { 27 | return Stack(children: [ 28 | Scaffold( 29 | backgroundColor: Colors.transparent, 30 | body: CustomScrollView( 31 | slivers: [ 32 | SliverPersistentHeader( 33 | delegate: ShoppingSliverAppBar(), 34 | pinned: true, 35 | ), 36 | SliverList( 37 | delegate: SliverChildListDelegate([ 38 | Container( 39 | height: 10000, 40 | color: Colors.white, 41 | ), 42 | ]), 43 | ), 44 | ], 45 | ), 46 | ), 47 | ]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /domain/lib/src/common/result.dart: -------------------------------------------------------------------------------- 1 | import 'error_type.dart'; 2 | 3 | class Result with SealedResult { 4 | bool get isSuccessful => this is Success; 5 | 6 | Result transform({ 7 | required T Function(T)? success, 8 | Error Function(Error)? error, 9 | }) { 10 | if (this is Success && success != null) { 11 | (this as Success).data = success.call((this as Success).data); 12 | } 13 | if (this is Error && error != null) { 14 | return error.call(this as Error); 15 | } 16 | return this; 17 | } 18 | } 19 | 20 | class Success extends Result { 21 | T data; 22 | 23 | Success(this.data); 24 | } 25 | 26 | class Error extends Result { 27 | ErrorType type; 28 | String message; 29 | 30 | Error(this.type, this.message); 31 | } 32 | 33 | class ServerError extends Error { 34 | int code; 35 | 36 | ServerError({ 37 | required ErrorType type, 38 | required String error, 39 | required this.code, 40 | }) : super(type, error); 41 | } 42 | 43 | abstract class SealedResult { 44 | R? when({ 45 | R Function(T)? success, 46 | R Function(Error)? error, 47 | }) { 48 | if (this is Success) { 49 | return success?.call((this as Success).data); 50 | } 51 | if (this is Error) { 52 | return error?.call(this as Error); 53 | } 54 | throw new Exception('If you got here, probably you forgot to regenerate the classes? ' 55 | 'Try running flutter packages pub run build_runner build'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /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 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /lib/presentation/screen/home/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import '../../di/locator.dart'; 5 | import '../popup/floating_popup/floating_popup.dart'; 6 | import 'home_bloc.dart'; 7 | import 'home_setting_bloc.dart'; 8 | import 'widget/home_sliver_app_bar.dart'; 9 | 10 | class HomeScreen extends StatelessWidget { 11 | const HomeScreen({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return MultiBlocProvider( 16 | providers: [ 17 | BlocProvider(create: (_) => locator()), 18 | BlocProvider(create: (_) => locator()), 19 | ], 20 | child: _HomeScreenBody(), 21 | ); 22 | } 23 | } 24 | 25 | class _HomeScreenBody extends StatelessWidget { 26 | @override 27 | Widget build(BuildContext context) { 28 | return Stack( 29 | children: [ 30 | Scaffold( 31 | backgroundColor: Colors.transparent, 32 | body: CustomScrollView( 33 | slivers: [ 34 | SliverPersistentHeader( 35 | delegate: HomeSliverAppBar(), 36 | pinned: true, 37 | ), 38 | SliverList( 39 | delegate: SliverChildListDelegate([ 40 | Container( 41 | height: 1000, 42 | color: Colors.white, 43 | ), 44 | ]), 45 | ), 46 | ], 47 | ), 48 | ), 49 | const FloatingPopup(), 50 | ], 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_clean_architecture 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /plugin/utils/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/ephemeral 64 | **/ios/Flutter/app.flx 65 | **/ios/Flutter/app.zip 66 | **/ios/Flutter/flutter_assets/ 67 | **/ios/Flutter/flutter_export_environment.sh 68 | **/ios/ServiceDefinitions.json 69 | **/ios/Runner/GeneratedPluginRegistrant.* 70 | 71 | # Exceptions to above rules. 72 | !**/ios/**/default.mode1v3 73 | !**/ios/**/default.mode2v3 74 | !**/ios/**/default.pbxuser 75 | !**/ios/**/default.perspectivev3 76 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/ephemeral 64 | **/ios/Flutter/app.flx 65 | **/ios/Flutter/app.zip 66 | **/ios/Flutter/flutter_assets/ 67 | **/ios/Flutter/flutter_export_environment.sh 68 | **/ios/ServiceDefinitions.json 69 | **/ios/Runner/GeneratedPluginRegistrant.* 70 | 71 | # Exceptions to above rules. 72 | !**/ios/**/default.mode1v3 73 | !**/ios/**/default.mode2v3 74 | !**/ios/**/default.pbxuser 75 | !**/ios/**/default.perspectivev3 76 | /coverage/ 77 | -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/ephemeral 64 | **/ios/Flutter/app.flx 65 | **/ios/Flutter/app.zip 66 | **/ios/Flutter/flutter_assets/ 67 | **/ios/Flutter/flutter_export_environment.sh 68 | **/ios/ServiceDefinitions.json 69 | **/ios/Runner/GeneratedPluginRegistrant.* 70 | 71 | # Exceptions to above rules. 72 | !**/ios/**/default.mode1v3 73 | !**/ios/**/default.mode2v3 74 | !**/ios/**/default.pbxuser 75 | !**/ios/**/default.perspectivev3 76 | /coverage/ 77 | -------------------------------------------------------------------------------- /domain/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/ephemeral 64 | **/ios/Flutter/app.flx 65 | **/ios/Flutter/app.zip 66 | **/ios/Flutter/flutter_assets/ 67 | **/ios/Flutter/flutter_export_environment.sh 68 | **/ios/ServiceDefinitions.json 69 | **/ios/Runner/GeneratedPluginRegistrant.* 70 | 71 | # Exceptions to above rules. 72 | !**/ios/**/default.mode1v3 73 | !**/ios/**/default.mode2v3 74 | !**/ios/**/default.pbxuser 75 | !**/ios/**/default.perspectivev3 76 | /coverage/ 77 | -------------------------------------------------------------------------------- /lib/presentation/values/themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class Themes { 5 | Themes._(); 6 | 7 | static const colorScheme = ColorScheme( 8 | primary: Color(0xFFfe832a), 9 | primaryVariant: Color(0xFFfe832a), 10 | secondary: Color(0xFFFDB434), 11 | secondaryVariant: Color(0xFFFDB434), 12 | surface: Colors.white, 13 | background: Colors.white, 14 | error: Color(0xffe44034), 15 | onPrimary: Colors.white, 16 | onSecondary: Colors.black, 17 | onSurface: Colors.black, 18 | onBackground: Colors.black, 19 | onError: Colors.white, 20 | brightness: Brightness.light, 21 | ); 22 | 23 | static final ThemeData lightTheme = ThemeData( 24 | primaryColor: colorScheme.primary, 25 | fontFamily: "Poppins", 26 | colorScheme: colorScheme, 27 | backgroundColor: Colors.white, 28 | scaffoldBackgroundColor: const Color(0xFFF3F3F3), 29 | bottomNavigationBarTheme: BottomNavigationBarThemeData( 30 | backgroundColor: Colors.white, 31 | showSelectedLabels: true, 32 | showUnselectedLabels: true, 33 | selectedItemColor: colorScheme.primary, 34 | selectedLabelStyle: TextStyle( 35 | color: colorScheme.primary, 36 | fontSize: 14, 37 | fontWeight: FontWeight.bold, 38 | ), 39 | unselectedItemColor: const Color(0xFFA4A4A4), 40 | unselectedLabelStyle: const TextStyle( 41 | color: Color(0xFFA4A4A4), 42 | fontSize: 14, 43 | ), 44 | type: BottomNavigationBarType.fixed, 45 | ), 46 | splashColor: colorScheme.primary.withOpacity(0.26), 47 | disabledColor: const Color(0xFFA4A4A4), 48 | dialogTheme: const DialogTheme( 49 | backgroundColor: Colors.white, 50 | shape: RoundedRectangleBorder( 51 | borderRadius: BorderRadius.all(Radius.circular(10)), 52 | ), 53 | ), 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 30 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.example.flutter_clean_architecture" 47 | minSdkVersion 16 48 | targetSdkVersion 30 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /data/lib/src/data_source/remote/base/api_client.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | 6 | const int kApiConnectTimeout = 1000; 7 | 8 | class ApiClient { 9 | final Dio _dio; 10 | 11 | ApiClient(this._dio) { 12 | _dio.options.connectTimeout = kApiConnectTimeout; 13 | _dio.options.receiveTimeout = kApiConnectTimeout; 14 | _dio.options.sendTimeout = kApiConnectTimeout; 15 | _dio.options.contentType = Headers.jsonContentType; 16 | _dio.options.responseType = ResponseType.json; 17 | 18 | if (kDebugMode) { 19 | _dio.interceptors.add(LogInterceptor( 20 | requestBody: true, 21 | responseHeader: false, 22 | responseBody: true, 23 | )); 24 | } else { 25 | _dio.interceptors.add( 26 | InterceptorsWrapper( 27 | onResponse: (Response response, handle) { 28 | return handle.next(response); 29 | }, 30 | onError: (DioError error, handle) { 31 | if (error.response != null) { 32 | /// add log 33 | } 34 | return handle.next(error); 35 | }, 36 | ), 37 | ); 38 | } 39 | } 40 | 41 | Future> get( 42 | String uri, { 43 | Map? params, 44 | CancelToken? cancelToken, 45 | }) async { 46 | return await _dio.get( 47 | uri, 48 | queryParameters: params, 49 | cancelToken: cancelToken, 50 | ); 51 | } 52 | 53 | Future> post( 54 | String uri, { 55 | data, 56 | Map? params, 57 | CancelToken? cancelToken, 58 | }) async { 59 | return await _dio.post( 60 | uri, 61 | data: data, 62 | queryParameters: params, 63 | cancelToken: cancelToken, 64 | ); 65 | } 66 | 67 | Future> delete( 68 | String uri, { 69 | Map? params, 70 | }) async { 71 | return await _dio.delete( 72 | uri, 73 | queryParameters: params, 74 | ); 75 | } 76 | 77 | Future> put( 78 | String uri, { 79 | data, 80 | Map? params, 81 | }) async { 82 | return await _dio.put( 83 | uri, 84 | data: data, 85 | queryParameters: params, 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /lib/presentation/screen/app/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:core/core.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:flutter_localizations/flutter_localizations.dart'; 5 | 6 | import '../../../presentation/values/themes.dart'; 7 | import '../../di/locator.dart'; 8 | import '../../routes/app_routes.dart'; 9 | import '../../routes/routes.dart'; 10 | import 'app_bloc.dart'; 11 | 12 | class App extends StatelessWidget { 13 | const App({Key? key}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return MultiBlocProvider( 18 | providers: [ 19 | BlocProvider( 20 | create: (_) => locator(), 21 | ), 22 | ], 23 | child: const AppView(), 24 | ); 25 | } 26 | } 27 | 28 | class AppView extends StatefulWidget { 29 | const AppView({Key? key}) : super(key: key); 30 | 31 | @override 32 | _AppViewState createState() => _AppViewState(); 33 | } 34 | 35 | class _AppViewState extends State { 36 | @override 37 | void initState() { 38 | super.initState(); 39 | Strings.init(context); 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return MaterialApp( 45 | title: 'Flutter', 46 | debugShowCheckedModeBanner: false, 47 | theme: Themes.lightTheme, 48 | locale: const Locale('vi', ''), 49 | supportedLocales: AppLocalizationsDelegate.supportedLocales, 50 | localizationsDelegates: const [ 51 | AppLocalizationsDelegate.delegate, 52 | GlobalMaterialLocalizations.delegate, 53 | GlobalWidgetsLocalizations.delegate, 54 | GlobalCupertinoLocalizations.delegate, 55 | ], 56 | localeResolutionCallback: (locale, supportedLocales) { 57 | for (var supportedLocale in supportedLocales) { 58 | if (supportedLocale.languageCode == locale?.languageCode && 59 | supportedLocale.countryCode == locale?.countryCode) { 60 | return supportedLocale; 61 | } 62 | } 63 | return supportedLocales.first; 64 | }, 65 | navigatorKey: AppRoutes.navigatorKey, 66 | navigatorObservers: [ 67 | AppRoutes.routeObserver, 68 | ], 69 | initialRoute: Routes.init, 70 | onGenerateRoute: AppRoutes.onGenerateRoute, 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /data/lib/src/di/locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:core/core.dart'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:domain/domain.dart'; 4 | import 'package:get_it/get_it.dart'; 5 | import 'package:package_info/package_info.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | 8 | import '../data_source/local/shared_preferences_store.dart'; 9 | import '../data_source/remote/base/api_client.dart'; 10 | import '../data_source/remote/popup_api.dart'; 11 | import '../data_source/remote/regions_api.dart'; 12 | import '../data_source/remote/screen_setting_api.dart'; 13 | import '../repository/api_error_handle_repo_impl.dart'; 14 | import '../repository/internet_status_repo_impl.dart'; 15 | import '../repository/popup_repo_impl.dart'; 16 | import '../repository/region_repo_impl.dart'; 17 | import '../repository/screen_setting_repo_impl.dart'; 18 | 19 | final locator = GetIt.instance..allowReassignment = true; 20 | 21 | Future setupLocator() async { 22 | await _registerExternal(); 23 | await _registerApiServices(); 24 | _registerNetworkModules(); 25 | _registerRepository(); 26 | } 27 | 28 | Future _registerRepository() async { 29 | locator.registerLazySingleton(() => InternetStatusRepoImpl()); 30 | locator.registerLazySingleton(() => ApiErrorHandleRepoImpl(locator())); 31 | 32 | locator.registerFactory(() => ScreenSettingRepoImpl(locator())); 33 | locator.registerLazySingleton(() => RegionRepoImpl(locator(), locator())); 34 | locator.registerFactory(() => PopupRepoImpl(locator())); 35 | } 36 | 37 | Future _registerApiServices() async { 38 | locator.registerLazySingleton(() => ScreenSettingApiImpl(locator())); 39 | locator.registerFactory(() => RegionApiImpl(locator())); 40 | locator.registerFactory(() => PopupApiImpl(locator())); 41 | } 42 | 43 | void _registerNetworkModules() { 44 | locator.registerLazySingleton(() => ApiClient(locator())); 45 | } 46 | 47 | Future _registerExternal() async { 48 | final prefs = await SharedPreferences.getInstance(); 49 | locator.registerSingleton(prefs); 50 | locator.registerSingleton(Dio(BaseOptions(baseUrl: locator().baseUrl))); 51 | locator.registerSingleton(SharedPrefsStore(locator())); 52 | final packageInfo = await PackageInfo.fromPlatform(); 53 | locator.registerSingleton(packageInfo); 54 | } 55 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/presentation/widgets/search_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'choose_region.dart'; 5 | 6 | class SearchBarWithRegion extends SearchBar { 7 | // ignore: use_key_in_widget_constructors 8 | const SearchBarWithRegion({ 9 | required Function() onPressed, 10 | required String label, 11 | TextStyle? labelStyle, 12 | BorderRadiusGeometry? borderRadius, 13 | Widget? prefixIcon, 14 | Widget suffixIcon = const ChooseRegion(), 15 | }) : super( 16 | onPressed: onPressed, 17 | label: label, 18 | labelStyle: labelStyle, 19 | borderRadius: borderRadius, 20 | suffixIcon: suffixIcon, 21 | ); 22 | } 23 | 24 | class SearchBar extends StatelessWidget { 25 | static const double height = 40; 26 | 27 | final Function() onPressed; 28 | final String label; 29 | final TextStyle? labelStyle; 30 | final BorderRadiusGeometry? borderRadius; 31 | final Widget prefixIcon; 32 | final Widget? suffixIcon; 33 | 34 | const SearchBar({ 35 | Key? key, 36 | required this.onPressed, 37 | required this.label, 38 | this.labelStyle, 39 | this.borderRadius, 40 | this.prefixIcon = const Icon( 41 | CupertinoIcons.search, 42 | size: 22, 43 | color: Color(0xffBCBCBC), 44 | ), 45 | this.suffixIcon, 46 | }) : super(key: key); 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return Container( 51 | height: height, 52 | child: ElevatedButton( 53 | onPressed: onPressed, 54 | style: ElevatedButton.styleFrom( 55 | primary: Colors.white, 56 | onPrimary: Theme.of(context).primaryColor, 57 | padding: const EdgeInsets.symmetric(horizontal: 5), 58 | shape: RoundedRectangleBorder( 59 | borderRadius: borderRadius ?? BorderRadius.circular(18), 60 | ), 61 | ), 62 | child: Row(children: [ 63 | Padding( 64 | padding: const EdgeInsets.symmetric(horizontal: 6), 65 | child: prefixIcon, 66 | ), 67 | Expanded( 68 | child: Text( 69 | label, 70 | maxLines: 1, 71 | textAlign: TextAlign.start, 72 | overflow: TextOverflow.ellipsis, 73 | style: labelStyle, 74 | )), 75 | Padding( 76 | padding: const EdgeInsets.only(left: 6), 77 | child: suffixIcon, 78 | ), 79 | ]), 80 | ), 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /plugin/utils/lib/utils.dart: -------------------------------------------------------------------------------- 1 | library utils; 2 | 3 | import 'dart:ui'; 4 | 5 | import 'package:intl/intl.dart'; 6 | 7 | class Utils { 8 | static final _formatter = NumberFormat.currency( 9 | symbol: "₫", 10 | decimalDigits: 0, 11 | customPattern: "##,###,###¤", 12 | ); 13 | 14 | static String formatMoney(num value) { 15 | return _formatter.format(value); 16 | } 17 | 18 | static int daysBetween(DateTime from, DateTime to) { 19 | try { 20 | final dFrom = DateTime(from.year, from.month, from.day); 21 | final dTo = DateTime(to.year, to.month, to.day); 22 | return (dTo.difference(dFrom).inHours / 24).round(); 23 | } catch (e) { 24 | return 0; 25 | } 26 | } 27 | 28 | static Color? hexColor(String colorCode) { 29 | var color = colorCode.toUpperCase().replaceAll("#", ""); 30 | if (color.length == 6) { 31 | color = "FF$color"; 32 | } 33 | if (color.length == 8) { 34 | try { 35 | return Color(int.parse("0x$color")); 36 | } catch (_) {} 37 | } 38 | return null; 39 | } 40 | 41 | static T? parseJson(String key, Map? json, {T? defaultValue}) { 42 | if (json == null) return defaultValue; 43 | try { 44 | return json.containsKey(key) && json[key] is T ? json[key] : defaultValue; 45 | } catch (e) { 46 | return defaultValue; 47 | } 48 | } 49 | 50 | static int? parseJsonToInt(String key, Map? json) { 51 | if (json != null) { 52 | final value = parseJson(key, json); 53 | if (value is num) return value.toInt(); 54 | if (value is String) return int.tryParse(value); 55 | } 56 | } 57 | 58 | static double? parseJsonToDouble(String key, Map? json) { 59 | if (json != null) { 60 | final value = parseJson(key, json); 61 | if (value is num) return value.toDouble(); 62 | if (value is String) return double.tryParse(value); 63 | } 64 | } 65 | 66 | static List parseJsonToList( 67 | String key, 68 | Map? json, 69 | T Function(Map) factoryFunction, 70 | ) { 71 | if (json == null) return []; 72 | final list = parseJson(key, json) ?? []; 73 | return listFromJson(list, factoryFunction); 74 | } 75 | 76 | static List listFromJson(List json, T Function(Map) factoryFunction) { 77 | final list = []; 78 | json.forEach((element) { 79 | if (element is Map) { 80 | final value = factoryFunction(element); 81 | list.add(value); 82 | } 83 | }); 84 | return list; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/presentation/widgets/custom_sliver_app_bar_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import '../values/sizes.dart'; 6 | 7 | class CustomSliverAppBarDelegate extends SliverPersistentHeaderDelegate { 8 | static double defaultExpandedHeight = 9 | kToolbarHeight * 2 + Sizes().statusBarHeight - 10; 10 | 11 | CustomSliverAppBarDelegate({ 12 | double? expandedHeight, 13 | required this.searchBar, 14 | this.background, 15 | this.title, 16 | List? actions, 17 | this.leading, 18 | this.backgroundColor, 19 | this.autoHideLeading, 20 | }) : expandedHeight = expandedHeight ?? defaultExpandedHeight, 21 | actions = actions?..add(const SizedBox(width: 4)); 22 | 23 | final Widget? title; 24 | final Widget? background; 25 | final Widget searchBar; 26 | final List? actions; 27 | final Widget? leading; 28 | final double expandedHeight; 29 | final Color? backgroundColor; 30 | final bool? autoHideLeading; 31 | 32 | @override 33 | Widget build( 34 | BuildContext context, double shrinkOffset, bool overlapsContent) { 35 | final shrinkOffsetPosition = 36 | min(40, shrinkOffset / (maxExtent - minExtent) * 40).toDouble(); 37 | final double show = shrinkOffset / maxExtent; 38 | final double hide = 1 - shrinkOffset / maxExtent; 39 | return Stack(children: [ 40 | if (background != null) background!, 41 | AppBar( 42 | elevation: 0, 43 | backgroundColor: Theme.of(context).primaryColor, 44 | centerTitle: true, 45 | leading: autoHideLeading != null 46 | ? IgnorePointer( 47 | ignoring: (autoHideLeading! ? hide : show) < 0.9, 48 | child: Opacity( 49 | opacity: autoHideLeading! ? hide : show, 50 | child: leading, 51 | ), 52 | ) 53 | : leading, 54 | title: title != null 55 | ? Opacity( 56 | opacity: hide, 57 | child: title, 58 | ) 59 | : null, 60 | actions: actions, 61 | ), 62 | Positioned( 63 | left: 8 + shrinkOffsetPosition, 64 | right: 8 + shrinkOffsetPosition, 65 | bottom: 0, 66 | child: Padding( 67 | padding: const EdgeInsets.all(8.0), 68 | child: searchBar, 69 | ), 70 | ), 71 | ]); 72 | } 73 | 74 | @override 75 | double get maxExtent => defaultExpandedHeight; 76 | 77 | @override 78 | double get minExtent => kToolbarHeight + Sizes().statusBarHeight; 79 | 80 | @override 81 | bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => 82 | true; 83 | } 84 | -------------------------------------------------------------------------------- /lib/presentation/screen/app_bottom_bar/widget/custom_bottom_navigation_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:core/core.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | import '../app_bottom_bar_bloc.dart'; 7 | 8 | class CustomBottomNavigationBar extends StatelessWidget { 9 | const CustomBottomNavigationBar({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | final currentIndex = context.watch().state; 14 | final unselectedItemColor = BottomNavigationBarTheme.of(context).unselectedItemColor; 15 | 16 | return BottomNavigationBar( 17 | currentIndex: currentIndex.index, 18 | onTap: (index) { 19 | context.read().emit(BottomBarIndex.values[index]); 20 | }, 21 | items: [ 22 | CustomBottomNavigationBarItem( 23 | icon: Icon( 24 | CupertinoIcons.home, 25 | color: currentIndex != BottomBarIndex.home ? unselectedItemColor : null, 26 | ), 27 | label: Strings.tr.home, 28 | ), 29 | CustomBottomNavigationBarItem( 30 | icon: Icon( 31 | CupertinoIcons.tickets, 32 | color: currentIndex != BottomBarIndex.coupon ? unselectedItemColor : null, 33 | ), 34 | label: Strings.tr.coupon, 35 | ), 36 | CustomBottomNavigationBarItem( 37 | iconSize: CustomBottomNavigationBarItem.iconSizeCenter, 38 | marginBottom: 4, 39 | icon: Icon( 40 | CupertinoIcons.bag_fill_badge_plus, 41 | color: currentIndex != BottomBarIndex.shopping ? unselectedItemColor : null, 42 | size: 36, 43 | ), 44 | label: Strings.tr.shopping, 45 | ), 46 | CustomBottomNavigationBarItem( 47 | icon: Icon( 48 | CupertinoIcons.book, 49 | color: currentIndex != BottomBarIndex.newFeed ? unselectedItemColor : null, 50 | ), 51 | label: Strings.tr.newFeed, 52 | ), 53 | CustomBottomNavigationBarItem( 54 | icon: Icon( 55 | CupertinoIcons.person, 56 | color: currentIndex != BottomBarIndex.user ? unselectedItemColor : null, 57 | ), 58 | label: Strings.tr.user, 59 | ), 60 | ], 61 | ); 62 | } 63 | } 64 | 65 | class CustomBottomNavigationBarItem extends BottomNavigationBarItem { 66 | static const iconSize = 25.0; 67 | static const iconSizeCenter = 45.0; 68 | 69 | CustomBottomNavigationBarItem({ 70 | double iconSize = iconSize, 71 | double marginBottom = 8, 72 | required Widget icon, 73 | String? label, 74 | }) : super( 75 | label: label, 76 | icon: Container( 77 | width: iconSizeCenter, 78 | height: iconSizeCenter, 79 | alignment: Alignment.bottomCenter, 80 | child: Container( 81 | width: iconSize, 82 | height: iconSize, 83 | margin: EdgeInsets.only(bottom: marginBottom), 84 | child: icon, 85 | ), 86 | ), 87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugin/utils/test/utils_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter_test/flutter_test.dart'; 4 | import 'package:utils/utils.dart'; 5 | 6 | void main() { 7 | test('formatMoney', () { 8 | expect(Utils.formatMoney(100), '100₫'); 9 | expect(Utils.formatMoney(1000), '1,000₫'); 10 | expect(Utils.formatMoney(10000), '10,000₫'); 11 | expect(Utils.formatMoney(100000), '100,000₫'); 12 | expect(Utils.formatMoney(1000000), '1,000,000₫'); 13 | 14 | expect(Utils.formatMoney(100.0), '100₫'); 15 | expect(Utils.formatMoney(1000.1), '1,000₫'); 16 | expect(Utils.formatMoney(10000.4), '10,000₫'); 17 | expect(Utils.formatMoney(10000.55), '10,001₫'); 18 | expect(Utils.formatMoney(100000.999), '100,001₫'); 19 | expect(Utils.formatMoney(1000000.9999), '1,000,001₫'); 20 | }); 21 | 22 | test('daysBetween', () { 23 | expect(Utils.daysBetween(DateTime(2021), DateTime(2021, 1, 2)), 1); 24 | expect(Utils.daysBetween(DateTime(2021), DateTime(2021, 1, 3)), 2); 25 | expect(Utils.daysBetween(DateTime(2021, 1, 0), DateTime(2021)), 1); 26 | expect(Utils.daysBetween(DateTime(2021), DateTime(2021, 2)), 31); 27 | expect(Utils.daysBetween(DateTime(2021), DateTime(2021, 1, 1, 12)), 0); 28 | expect(Utils.daysBetween(DateTime(2021), DateTime(2021, 1, 1, 48)), 2); 29 | }); 30 | 31 | test('hexColor', () { 32 | expect(Utils.hexColor('adfggn'), null); 33 | expect(Utils.hexColor('5ADFCBD'), null); 34 | expect(Utils.hexColor('ADFCBD'), Color(0xffADFCBD)); 35 | expect(Utils.hexColor('ffadfcbd'), Color(0xffADFCBD)); 36 | }); 37 | 38 | test('parseJson', () { 39 | dynamic jsonNull = null; 40 | final json = { 41 | 'aaa': 123, 42 | }; 43 | expect(Utils.parseJson('', null), null); 44 | expect(Utils.parseJson('', jsonNull), null); 45 | expect(Utils.parseJson('', json), null); 46 | expect(Utils.parseJson('aaa', jsonNull), null); 47 | expect(Utils.parseJson('aaa', json), 123); 48 | expect(Utils.parseJson('bbb', json), null); 49 | expect(Utils.parseJson('', json, defaultValue: 0), 0); 50 | expect(Utils.parseJson('aaa', json, defaultValue: 0), 123); 51 | expect(Utils.parseJson('bbb', json, defaultValue: 0), 0); 52 | }); 53 | 54 | test('parseJsonToInt', () { 55 | dynamic jsonNull = null; 56 | final json = { 57 | 'aaa': 123, 58 | 'bbb': 123.0, 59 | 'ccc': '123', 60 | 'ddd': '12a', 61 | }; 62 | expect(Utils.parseJsonToInt('', null), null); 63 | expect(Utils.parseJsonToInt('', jsonNull), null); 64 | expect(Utils.parseJsonToInt('', json), null); 65 | expect(Utils.parseJsonToInt('a', json), null); 66 | expect(Utils.parseJsonToInt('aaa', jsonNull), null); 67 | expect(Utils.parseJsonToInt('aaa', json), 123); 68 | expect(Utils.parseJsonToInt('bbb', json), 123); 69 | expect(Utils.parseJsonToInt('ccc', json), 123); 70 | expect(Utils.parseJsonToInt('ddd', json), null); 71 | }); 72 | 73 | test('parseJsonToDouble', () { 74 | dynamic jsonNull = null; 75 | final json = { 76 | 'aaa': 123, 77 | 'bbb': 123.0, 78 | 'ccc': '123', 79 | 'ddd': '12a', 80 | }; 81 | expect(Utils.parseJsonToDouble('', null), null); 82 | expect(Utils.parseJsonToDouble('', jsonNull), null); 83 | expect(Utils.parseJsonToDouble('', json), null); 84 | expect(Utils.parseJsonToDouble('a', json), null); 85 | expect(Utils.parseJsonToDouble('aaa', jsonNull), null); 86 | expect(Utils.parseJsonToDouble('aaa', json), 123.0); 87 | expect(Utils.parseJsonToDouble('bbb', json), 123.0); 88 | expect(Utils.parseJsonToDouble('ccc', json), 123.0); 89 | expect(Utils.parseJsonToDouble('ddd', json), null); 90 | }); 91 | } 92 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /tool/generate_asset_metadata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script used to analyze bundled assets and generate a dart file which contains 4 | # the relevant metadata needed at runtime without forcing the application to 5 | # download the assets. 6 | # 7 | # Usage: 8 | # ./tool/generate_asset_metadata.sh > lib/screen/sub_category/mid_autumn/view/photo_editor/models/assets.g.dart 9 | 10 | set -e 11 | 12 | output_metadata () { 13 | width=$(sips -g pixelWidth $1 | tail -n1 | cut -d" " -f4) 14 | height=$(sips -g pixelHeight $1 | tail -n1 | cut -d" " -f4) 15 | filepath=$1 16 | name=$(basename "${filepath%.*}") 17 | 18 | echo "Asset(name: '$name', path: '$1', size: Size($width, $height));" 19 | } 20 | 21 | echo "// GENERATED CODE - DO NOT MODIFY BY HAND" 22 | echo "" 23 | echo "import 'package:flutter/widgets.dart';" 24 | echo "import 'asset.dart';" 25 | echo "" 26 | echo "class Assets {" 27 | 28 | sticker=images/mid_autumn/sticker/*.webp 29 | 30 | echo " static const sticker = [" 31 | for prop in $sticker 32 | do 33 | width=$(sips -g pixelWidth $prop | tail -n1 | cut -d" " -f4) 34 | height=$(sips -g pixelHeight $prop | tail -n1 | cut -d" " -f4) 35 | name=$(basename "${prop%.*}") 36 | echo " Asset( 37 | name: '$name', 38 | path: '$prop', 39 | size: Size($width, $height), 40 | )," 41 | done 42 | 43 | echo " ];" 44 | 45 | boxTmpBg=images/mid_autumn/box_tmp_bg/*.webp 46 | 47 | echo " static const boxTmpbg = [" 48 | for prop in $boxTmpBg 49 | do 50 | width=$(sips -g pixelWidth $prop | tail -n1 | cut -d" " -f4) 51 | height=$(sips -g pixelHeight $prop | tail -n1 | cut -d" " -f4) 52 | name=$(basename "${prop%.*}") 53 | echo " Asset( 54 | name: '$name', 55 | path: '$prop', 56 | size: Size($width, $height), 57 | )," 58 | done 59 | 60 | echo " ];" 61 | 62 | boxTmpX2=images/mid_autumn/box_tmp_x2/*.png 63 | 64 | echo " static const boxTmpX2 = [" 65 | for prop in $boxTmpX2 66 | do 67 | width=$(sips -g pixelWidth $prop | tail -n1 | cut -d" " -f4) 68 | height=$(sips -g pixelHeight $prop | tail -n1 | cut -d" " -f4) 69 | name=$(basename "${prop%.*}") 70 | echo " Asset( 71 | name: '$name', 72 | path: '$prop', 73 | size: Size($width, $height), 74 | )," 75 | done 76 | 77 | echo " ];" 78 | 79 | boxTmpX4=images/mid_autumn/box_tmp_x4/*.png 80 | 81 | echo " static const boxTmpX4 = [" 82 | for prop in $boxTmpX4 83 | do 84 | width=$(sips -g pixelWidth $prop | tail -n1 | cut -d" " -f4) 85 | height=$(sips -g pixelHeight $prop | tail -n1 | cut -d" " -f4) 86 | name=$(basename "${prop%.*}") 87 | echo " Asset( 88 | name: '$name', 89 | path: '$prop', 90 | size: Size($width, $height), 91 | )," 92 | done 93 | 94 | echo " ];" 95 | 96 | boxTmpX6=images/mid_autumn/box_tmp_x6/*.png 97 | 98 | echo " static const boxTmpX6 = [" 99 | for prop in $boxTmpX6 100 | do 101 | width=$(sips -g pixelWidth $prop | tail -n1 | cut -d" " -f4) 102 | height=$(sips -g pixelHeight $prop | tail -n1 | cut -d" " -f4) 103 | name=$(basename "${prop%.*}") 104 | echo " Asset( 105 | name: '$name', 106 | path: '$prop', 107 | size: Size($width, $height), 108 | )," 109 | done 110 | 111 | echo " ];" 112 | 113 | echo " static const props = [ 114 | ...sticker, 115 | ...boxTmpX2, 116 | ...boxTmpX4, 117 | ...boxTmpX6, 118 | ];" 119 | echo "}" -------------------------------------------------------------------------------- /lib/presentation/values/sizes.dart: -------------------------------------------------------------------------------- 1 | library flutter_size; 2 | 3 | import 'dart:math'; 4 | import 'dart:ui' as ui; 5 | 6 | import 'package:flutter/material.dart'; 7 | 8 | class Sizes { 9 | static const Size defaultSize = Size(750, 1500); 10 | static late Sizes _instance; 11 | 12 | /// Size of the phone in UI Design , dp 13 | late Size uiSize; 14 | 15 | late Orientation _orientation; 16 | 17 | late double _pixelRatio; 18 | late double _textScaleFactor; 19 | late double _screenWidth; 20 | late double _screenHeight; 21 | late double _statusBarHeight; 22 | late double _bottomBarHeight; 23 | 24 | Sizes._(); 25 | 26 | factory Sizes() => _instance; 27 | 28 | static void init( 29 | BoxConstraints constraints, { 30 | Orientation orientation = Orientation.portrait, 31 | Size designSize = defaultSize, 32 | }) { 33 | _instance = Sizes._() 34 | ..uiSize = designSize 35 | .._orientation = orientation 36 | .._screenWidth = constraints.maxWidth 37 | .._screenHeight = constraints.maxHeight; 38 | 39 | final window = WidgetsBinding.instance?.window ?? ui.window; 40 | _instance._pixelRatio = window.devicePixelRatio; 41 | _instance._statusBarHeight = window.padding.top; 42 | _instance._bottomBarHeight = window.padding.bottom; 43 | _instance._textScaleFactor = window.textScaleFactor; 44 | } 45 | 46 | ///Get screen orientation 47 | Orientation get orientation => _orientation; 48 | 49 | /// The number of font pixels for each logical pixel. 50 | double get textScaleFactor => _textScaleFactor; 51 | 52 | /// The size of the media in logical pixels (e.g, the size of the screen). 53 | double get pixelRatio => _pixelRatio; 54 | 55 | /// The horizontal extent of this size. 56 | double get screenWidth => _screenWidth; 57 | 58 | ///The vertical extent of this size. dp 59 | double get screenHeight => _screenHeight; 60 | 61 | /// The offset from the top, in dp 62 | double get statusBarHeight => _statusBarHeight / _pixelRatio; 63 | 64 | /// 底部安全区距离 dp 65 | /// The offset from the bottom, in dp 66 | double get bottomBarHeight => _bottomBarHeight / _pixelRatio; 67 | 68 | /// The ratio of actual width to UI design 69 | double get scaleWidth => _screenWidth / uiSize.width; 70 | 71 | /// /// The ratio of actual height to UI design 72 | double get scaleHeight => _screenHeight / uiSize.height; 73 | 74 | double get scaleText => min(scaleWidth, scaleHeight); 75 | 76 | /// Adapted to the device width of the UI Design. 77 | /// Height can also be adapted according to this to ensure no deformation , 78 | /// if you want a square 79 | double setWidth(num width) => width * scaleWidth; 80 | 81 | /// Highly adaptable to the device according to UI Design 82 | /// It is recommended to use this method to achieve a high degree of adaptation 83 | /// when it is found that one screen in the UI design 84 | /// does not match the current style effect, or if there is a difference in shape. 85 | double setHeight(num height) => height * scaleHeight; 86 | 87 | ///Adapt according to the smaller of width or height 88 | double radius(num r) => r * scaleText; 89 | 90 | ///Font size adaptation method 91 | ///- [fontSize] The size of the font on the UI design, in dp. 92 | double setSp(num fontSize) => fontSize * scaleText; 93 | } 94 | 95 | extension SizeExtension on num { 96 | ///[Sizes.setWidth] 97 | double get w => Sizes().setWidth(this); 98 | 99 | ///[Sizes.setHeight] 100 | double get h => Sizes().setHeight(this); 101 | 102 | ///[Sizes.radius] 103 | double get r => Sizes().radius(this); 104 | 105 | ///[Sizes.setSp] 106 | double get sp => Sizes().setSp(this); 107 | 108 | ///Multiple of screen width 109 | double get sw => Sizes().screenWidth * this; 110 | 111 | ///Multiple of screen height 112 | double get sh => Sizes().screenHeight * this; 113 | } 114 | -------------------------------------------------------------------------------- /lib/presentation/screen/app_bottom_bar/widget/custom_bottom_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:core/core.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | 7 | import '../app_bottom_bar_bloc.dart'; 8 | 9 | class CustomBottomAppBar extends StatelessWidget { 10 | const CustomBottomAppBar({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final currentIndex = context.watch().state; 15 | //final unselectedItemColor = BottomNavigationBarTheme.of(context).unselectedItemColor; 16 | 17 | return BottomAppBar( 18 | shape: const CircularNotchedRectangle(), 19 | child: SizedBox( 20 | height: 70, 21 | child: Row( 22 | mainAxisAlignment: MainAxisAlignment.spaceAround, 23 | children: [ 24 | BottomAppBarItem( 25 | onPressed: () => 26 | context.read().emit(BottomBarIndex.home), 27 | isActive: currentIndex == BottomBarIndex.home, 28 | label: Strings.tr.home, 29 | icon: const Icon(CupertinoIcons.home), 30 | ), 31 | BottomAppBarItem( 32 | onPressed: () => 33 | context.read().emit(BottomBarIndex.coupon), 34 | isActive: currentIndex == BottomBarIndex.coupon, 35 | label: Strings.tr.coupon, 36 | icon: const Icon(CupertinoIcons.tickets), 37 | ), 38 | BottomAppBarItem( 39 | isActive: currentIndex == BottomBarIndex.shopping, 40 | onPressed: () => null, 41 | label: Strings.tr.shopping, 42 | icon: const SizedBox(), 43 | ), 44 | BottomAppBarItem( 45 | onPressed: () => 46 | context.read().emit(BottomBarIndex.newFeed), 47 | isActive: currentIndex == BottomBarIndex.newFeed, 48 | label: Strings.tr.newFeed, 49 | icon: const Icon(CupertinoIcons.book), 50 | ), 51 | BottomAppBarItem( 52 | onPressed: () => 53 | context.read().emit(BottomBarIndex.user), 54 | isActive: currentIndex == BottomBarIndex.user, 55 | label: Strings.tr.user, 56 | icon: const Icon(CupertinoIcons.person), 57 | ), 58 | ], 59 | ), 60 | ), 61 | ); 62 | } 63 | } 64 | 65 | class BottomAppBarItem extends StatelessWidget { 66 | static const iconSizeCenter = 45.0; 67 | 68 | const BottomAppBarItem({ 69 | Key? key, 70 | required this.isActive, 71 | required this.icon, 72 | required this.onPressed, 73 | this.label, 74 | this.iconSize = 25, 75 | this.marginBottom = 6, 76 | }) : super(key: key); 77 | 78 | final Function() onPressed; 79 | final Widget icon; 80 | final String? label; 81 | final double iconSize; 82 | 83 | final double marginBottom; 84 | final bool isActive; 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | final selectedLabelStyle = 89 | BottomNavigationBarTheme.of(context).selectedLabelStyle; 90 | final unselectedLabelStyle = 91 | BottomNavigationBarTheme.of(context).unselectedLabelStyle; 92 | 93 | return Stack( 94 | alignment: Alignment.bottomCenter, 95 | children: [ 96 | IconButton( 97 | onPressed: onPressed, 98 | padding: EdgeInsets.zero, 99 | icon: Column(children: [ 100 | Container( 101 | width: iconSize, 102 | height: iconSize, 103 | margin: EdgeInsets.only(bottom: marginBottom), 104 | child: icon, 105 | ), 106 | ]), 107 | ), 108 | if (label != null) 109 | Text(label!, 110 | style: isActive ? selectedLabelStyle : unselectedLabelStyle) 111 | ], 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /plugin/utils/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | fake_async: 47 | dependency: transitive 48 | description: 49 | name: fake_async 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.2.0" 53 | flutter: 54 | dependency: "direct main" 55 | description: flutter 56 | source: sdk 57 | version: "0.0.0" 58 | flutter_test: 59 | dependency: "direct dev" 60 | description: flutter 61 | source: sdk 62 | version: "0.0.0" 63 | intl: 64 | dependency: "direct main" 65 | description: 66 | name: intl 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "0.17.0" 70 | matcher: 71 | dependency: transitive 72 | description: 73 | name: matcher 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "0.12.10" 77 | meta: 78 | dependency: transitive 79 | description: 80 | name: meta 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "1.7.0" 84 | path: 85 | dependency: transitive 86 | description: 87 | name: path 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "1.8.0" 91 | sky_engine: 92 | dependency: transitive 93 | description: flutter 94 | source: sdk 95 | version: "0.0.99" 96 | source_span: 97 | dependency: transitive 98 | description: 99 | name: source_span 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.8.1" 103 | stack_trace: 104 | dependency: transitive 105 | description: 106 | name: stack_trace 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.10.0" 110 | stream_channel: 111 | dependency: transitive 112 | description: 113 | name: stream_channel 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "2.1.0" 117 | string_scanner: 118 | dependency: transitive 119 | description: 120 | name: string_scanner 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.1.0" 124 | term_glyph: 125 | dependency: transitive 126 | description: 127 | name: term_glyph 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.2.0" 131 | test_api: 132 | dependency: transitive 133 | description: 134 | name: test_api 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "0.4.2" 138 | typed_data: 139 | dependency: transitive 140 | description: 141 | name: typed_data 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.3.0" 145 | vector_math: 146 | dependency: transitive 147 | description: 148 | name: vector_math 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "2.1.0" 152 | sdks: 153 | dart: ">=2.12.0 <3.0.0" 154 | flutter: ">=1.17.0" 155 | -------------------------------------------------------------------------------- /lib/presentation/screen/app_bottom_bar/app_bottom_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/scheduler.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | 7 | import '../../di/locator.dart'; 8 | import '../../values/sizes.dart'; 9 | import '../home/home_screen.dart'; 10 | import '../shopping/shopping_screen.dart'; 11 | import 'app_bottom_bar_bloc.dart'; 12 | import 'widget/custom_bottom_navigation_bar.dart'; 13 | 14 | class AppBottomBarScreen extends StatelessWidget { 15 | const AppBottomBarScreen({Key? key}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return BlocProvider( 20 | create: (_) => locator(), 21 | child: const BottomBar(), 22 | ); 23 | } 24 | } 25 | 26 | class BottomBar extends StatefulWidget { 27 | const BottomBar({Key? key}) : super(key: key); 28 | 29 | @override 30 | _BottomBarState createState() => _BottomBarState(); 31 | } 32 | 33 | class _BottomBarState extends State with TickerProviderStateMixin { 34 | List pages = [ 35 | const HomeScreen(), 36 | const Center(child: Text('Coupon')), 37 | const ShoppingScreen(), 38 | const Center(child: Text('BeeFeed')), 39 | const Center(child: Text('Cá nhân')), 40 | ]; 41 | 42 | late List destinationKeys; 43 | late List faders; 44 | late AnimationController hideBottomBar; 45 | 46 | @override 47 | void initState() { 48 | super.initState(); 49 | faders = List.generate( 50 | pages.length, 51 | (_) => AnimationController( 52 | vsync: this, 53 | duration: kThemeAnimationDuration, 54 | ), 55 | ); 56 | faders[context.read().state.index].value = 1.0; 57 | destinationKeys = List.generate( 58 | pages.length, 59 | (int index) => GlobalKey(), 60 | ).toList(); 61 | hideBottomBar = AnimationController( 62 | vsync: this, 63 | duration: kThemeAnimationDuration, 64 | ); 65 | SchedulerBinding.instance?.addPostFrameCallback((_) { 66 | hideBottomBar.forward(); 67 | }); 68 | } 69 | 70 | @override 71 | void didChangeDependencies() { 72 | super.didChangeDependencies(); 73 | Sizes.init( 74 | BoxConstraints( 75 | maxWidth: MediaQuery.of(context).size.width, 76 | maxHeight: MediaQuery.of(context).size.height, 77 | ), 78 | designSize: const Size(750, 1450), 79 | ); 80 | } 81 | 82 | @override 83 | void dispose() { 84 | for (AnimationController controller in faders) { 85 | controller.dispose(); 86 | } 87 | hideBottomBar.dispose(); 88 | super.dispose(); 89 | } 90 | 91 | @override 92 | Widget build(BuildContext context) { 93 | final currentIndex = context.watch().state; 94 | 95 | return WillPopScope( 96 | onWillPop: () async { 97 | if (currentIndex != BottomBarIndex.shopping) { 98 | context.read().emit(BottomBarIndex.shopping); 99 | return false; 100 | } 101 | return true; 102 | }, 103 | child: Scaffold( 104 | body: Stack( 105 | fit: StackFit.expand, 106 | children: List.generate(pages.length, (int index) { 107 | final Widget view = FadeTransition( 108 | opacity: 109 | faders[index].drive(CurveTween(curve: Curves.fastOutSlowIn)), 110 | child: KeyedSubtree( 111 | key: destinationKeys[index], 112 | child: pages[index], 113 | ), 114 | ); 115 | if (index == currentIndex.index) { 116 | faders[index].forward(); 117 | return view; 118 | } else { 119 | faders[index].reverse(); 120 | if (faders[index].isAnimating) { 121 | return IgnorePointer(child: view); 122 | } 123 | return Offstage(child: view); 124 | } 125 | }).toList(), 126 | ), 127 | bottomNavigationBar: const CustomBottomNavigationBar(), 128 | ), 129 | ); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | flutter_clean_architecture 30 | 31 | 32 | 33 | 36 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_clean_architecture 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: ^1.0.2 37 | 38 | get_it: 7.2.0 39 | flutter_bloc: 7.3.0 40 | equatable: 2.0.3 41 | utils: 42 | path: plugin/utils 43 | 44 | core: 45 | path: core 46 | data: 47 | path: data 48 | domain: 49 | path: domain 50 | 51 | dio: 4.0.0 52 | cached_network_image: 3.1.0 53 | flutter_svg: ^0.22.0 54 | 55 | dev_dependencies: 56 | flutter_test: 57 | sdk: flutter 58 | flutter_launcher_icons: "^0.9.2" 59 | 60 | # The "flutter_lints" package below contains a set of recommended lints to 61 | # encourage good coding practices. The lint set provided by the package is 62 | # activated in the `analysis_options.yaml` file located at the root of your 63 | # package. See that file for information about deactivating specific lint 64 | # rules and activating additional ones. 65 | flutter_lints: ^1.0.0 66 | 67 | # For information on the generic Dart part of this file, see the 68 | # following page: https://dart.dev/tools/pub/pubspec 69 | 70 | # The following section is specific to Flutter. 71 | flutter: 72 | 73 | # The following line ensures that the Material Icons font is 74 | # included with your application, so that you can use the icons in 75 | # the material Icons class. 76 | uses-material-design: true 77 | 78 | # To add assets to your application, add an assets section, like this: 79 | # assets: 80 | # - assets/icons/ 81 | # - assets/images/ 82 | 83 | # An image asset can refer to one or more resolution-specific "variants", see 84 | # https://flutter.dev/assets-and-images/#resolution-aware. 85 | 86 | # For details regarding adding assets from package dependencies, see 87 | # https://flutter.dev/assets-and-images/#from-packages 88 | 89 | # To add custom fonts to your application, add a fonts section here, 90 | # in this "flutter" section. Each entry in this list should have a 91 | # "family" key with the font family name, and a "fonts" key with a 92 | # list giving the asset and other descriptors for the font. For 93 | # example: 94 | fonts: 95 | - family: Poppins 96 | fonts: 97 | - asset: assets/fonts/Poppins/Poppins-Regular.ttf 98 | - asset: assets/fonts/Poppins/Poppins-Medium.ttf 99 | weight: 600 100 | - asset: assets/fonts/Poppins/Poppins-Bold.ttf 101 | weight: 700 102 | # - family: Trajan Pro 103 | # fonts: 104 | # - asset: fonts/TrajanPro.ttf 105 | # - asset: fonts/TrajanPro_Bold.ttf 106 | # weight: 700 107 | # 108 | # For details regarding fonts from package dependencies, 109 | # see https://flutter.dev/custom-fonts/#from-packages 110 | -------------------------------------------------------------------------------- /lib/presentation/screen/popup/region/region_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:core/core.dart'; 2 | import 'package:domain/domain.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | import 'package:flutter_bloc/flutter_bloc.dart'; 7 | 8 | import '../../../di/locator.dart'; 9 | import '../../../routes/app_routes.dart'; 10 | import '../../app/app_bloc.dart'; 11 | import 'region_bloc.dart'; 12 | 13 | class RegionDialog extends StatelessWidget { 14 | const RegionDialog({Key? key}) : super(key: key); 15 | 16 | static Future show({ 17 | BuildContext? context, 18 | bool barrierDismissible = false, 19 | }) async { 20 | final appContext = AppRoutes.navigatorKey.currentState?.overlay?.context; 21 | if ((context ?? appContext) == null) throw 'Context call null'; 22 | final result = await showDialog( 23 | context: (context ?? appContext)!, 24 | barrierDismissible: barrierDismissible, 25 | builder: (_) => BlocProvider( 26 | create: (_) => locator(), 27 | child: const RegionDialog(), 28 | ), 29 | ); 30 | return result; 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return WillPopScope( 36 | onWillPop: () => Future.value(false), 37 | child: SimpleDialog( 38 | titlePadding: EdgeInsets.zero, 39 | contentPadding: const EdgeInsets.all(15), 40 | title: const RegionTitle(), 41 | children: [ 42 | Container( 43 | color: Colors.white, 44 | child: BlocBuilder( 45 | builder: (context, state) { 46 | return Row(children: [ 47 | Expanded( 48 | child: RegionButton( 49 | region: context.read().getRegions.first, 50 | ), 51 | ), 52 | const SizedBox(width: 15), 53 | Expanded( 54 | child: RegionButton( 55 | region: context.read().getRegions.last, 56 | ), 57 | ), 58 | ]); 59 | }, 60 | ), 61 | ), 62 | ], 63 | ), 64 | ); 65 | } 66 | } 67 | 68 | class RegionTitle extends StatelessWidget { 69 | const RegionTitle({Key? key}) : super(key: key); 70 | 71 | @override 72 | Widget build(BuildContext context) { 73 | return Container( 74 | padding: const EdgeInsets.all(15), 75 | decoration: BoxDecoration( 76 | color: Theme.of(context).primaryColor, 77 | borderRadius: const BorderRadius.vertical( 78 | top: Radius.circular(10), 79 | )), 80 | child: Row( 81 | children: [ 82 | const Icon( 83 | CupertinoIcons.location_solid, 84 | size: 18, 85 | color: Colors.white, 86 | ), 87 | const SizedBox(width: 10), 88 | Expanded( 89 | child: Text( 90 | Strings.tr.titleLocation, 91 | style: const TextStyle(color: Colors.white), 92 | )), 93 | ], 94 | ), 95 | ); 96 | } 97 | } 98 | 99 | class RegionButton extends StatelessWidget { 100 | final Region region; 101 | 102 | const RegionButton({ 103 | Key? key, 104 | required this.region, 105 | }) : super(key: key); 106 | 107 | @override 108 | Widget build(BuildContext context) { 109 | return ElevatedButton( 110 | style: ElevatedButton.styleFrom( 111 | padding: EdgeInsets.symmetric(horizontal: 5, vertical: 15), 112 | shape: RoundedRectangleBorder( 113 | borderRadius: BorderRadius.all(Radius.circular(10)), 114 | side: BorderSide(color: Theme.of(context).primaryColor), 115 | ), 116 | primary: Colors.white, 117 | onPrimary: Theme.of(context).primaryColor, 118 | //textStyle: TextStyle(color: Theme.of(context).primaryColor), 119 | ), 120 | onPressed: () async { 121 | final result = await context.read().updateRegion(region); 122 | if (result) { 123 | Navigator.of(context).pop(); 124 | context.read().updateRegions(region); 125 | } 126 | }, 127 | child: FittedBox( 128 | fit: BoxFit.scaleDown, 129 | child: Text( 130 | region.name, 131 | textAlign: TextAlign.center, 132 | ), 133 | ), 134 | ); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /core/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | equatable: 47 | dependency: "direct main" 48 | description: 49 | name: equatable 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.0.3" 53 | fake_async: 54 | dependency: transitive 55 | description: 56 | name: fake_async 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.2.0" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_localizations: 66 | dependency: "direct main" 67 | description: flutter 68 | source: sdk 69 | version: "0.0.0" 70 | flutter_test: 71 | dependency: "direct dev" 72 | description: flutter 73 | source: sdk 74 | version: "0.0.0" 75 | get_it: 76 | dependency: "direct main" 77 | description: 78 | name: get_it 79 | url: "https://pub.dartlang.org" 80 | source: hosted 81 | version: "7.2.0" 82 | intl: 83 | dependency: transitive 84 | description: 85 | name: intl 86 | url: "https://pub.dartlang.org" 87 | source: hosted 88 | version: "0.17.0" 89 | matcher: 90 | dependency: transitive 91 | description: 92 | name: matcher 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "0.12.10" 96 | meta: 97 | dependency: transitive 98 | description: 99 | name: meta 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.7.0" 103 | path: 104 | dependency: transitive 105 | description: 106 | name: path 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.8.0" 110 | sky_engine: 111 | dependency: transitive 112 | description: flutter 113 | source: sdk 114 | version: "0.0.99" 115 | source_span: 116 | dependency: transitive 117 | description: 118 | name: source_span 119 | url: "https://pub.dartlang.org" 120 | source: hosted 121 | version: "1.8.1" 122 | stack_trace: 123 | dependency: transitive 124 | description: 125 | name: stack_trace 126 | url: "https://pub.dartlang.org" 127 | source: hosted 128 | version: "1.10.0" 129 | stream_channel: 130 | dependency: transitive 131 | description: 132 | name: stream_channel 133 | url: "https://pub.dartlang.org" 134 | source: hosted 135 | version: "2.1.0" 136 | string_scanner: 137 | dependency: transitive 138 | description: 139 | name: string_scanner 140 | url: "https://pub.dartlang.org" 141 | source: hosted 142 | version: "1.1.0" 143 | term_glyph: 144 | dependency: transitive 145 | description: 146 | name: term_glyph 147 | url: "https://pub.dartlang.org" 148 | source: hosted 149 | version: "1.2.0" 150 | test_api: 151 | dependency: transitive 152 | description: 153 | name: test_api 154 | url: "https://pub.dartlang.org" 155 | source: hosted 156 | version: "0.4.2" 157 | typed_data: 158 | dependency: transitive 159 | description: 160 | name: typed_data 161 | url: "https://pub.dartlang.org" 162 | source: hosted 163 | version: "1.3.0" 164 | vector_math: 165 | dependency: transitive 166 | description: 167 | name: vector_math 168 | url: "https://pub.dartlang.org" 169 | source: hosted 170 | version: "2.1.0" 171 | sdks: 172 | dart: ">=2.12.0 <3.0.0" 173 | flutter: ">=1.17.0" 174 | -------------------------------------------------------------------------------- /lib/presentation/screen/popup/floating_popup/floating_popup.dart: -------------------------------------------------------------------------------- 1 | import 'package:domain/domain.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | 5 | import '../../../di/locator.dart'; 6 | import '../../../values/sizes.dart'; 7 | import 'floating_popup_bloc.dart'; 8 | 9 | class FloatingPopup extends StatelessWidget { 10 | const FloatingPopup({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return BlocProvider( 15 | create: (_) => locator(), 16 | child: _FloatingPopupDrag(), 17 | ); 18 | } 19 | } 20 | 21 | class _FloatingPopupDrag extends StatefulWidget { 22 | @override 23 | _FloatingPopupDragState createState() => _FloatingPopupDragState(); 24 | } 25 | 26 | class _FloatingPopupDragState extends State<_FloatingPopupDrag> 27 | with TickerProviderStateMixin { 28 | static const double bottomPadding = 16; 29 | static const double imgSize = 100; 30 | static const double padding = 15; 31 | static const double extraSpace = imgSize / 2 + padding; 32 | 33 | static double get extraSpaceVerBottom => 34 | extraSpace + kBottomNavigationBarHeight + bottomPadding; 35 | 36 | double get extraSpaceVerTop => 37 | extraSpace + (kToolbarHeight * 2 + MediaQuery.of(context).padding.top); 38 | 39 | Offset position = Offset( 40 | Sizes().screenWidth - extraSpace, 41 | Sizes().screenHeight - extraSpaceVerBottom - bottomPadding, 42 | ); 43 | late Animation _animation; 44 | late AnimationController _animationController; 45 | 46 | void onPressed(Popup? popup) { 47 | if (popup?.deepLink == null) return; 48 | // todo navigate page 49 | } 50 | 51 | @override 52 | void initState() { 53 | super.initState(); 54 | context.read().init(); 55 | } 56 | 57 | @override 58 | void dispose() { 59 | _animationController.dispose(); 60 | super.dispose(); 61 | } 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | final popup = context.watch().state; 66 | if (popup == null) return SizedBox(); 67 | return Positioned( 68 | top: position.dy - extraSpace, 69 | left: position.dx - extraSpace, 70 | child: GestureDetector( 71 | onPanUpdate: _onPanUpdate, 72 | onPanEnd: _onPanEnd, 73 | onTap: () => onPressed(popup), 74 | child: Stack(alignment: Alignment.topRight, children: [ 75 | const Padding( 76 | padding: EdgeInsets.all(padding), 77 | child: SizedBox( 78 | width: imgSize, 79 | height: imgSize, 80 | child: FlutterLogo(), 81 | ), 82 | ), 83 | IconButton( 84 | icon: const Icon( 85 | Icons.cancel, 86 | color: Color(0xFFA4A4A4), 87 | ), 88 | padding: EdgeInsets.zero, 89 | onPressed: () => context.read().emit(null), 90 | ), 91 | ]), 92 | ), 93 | ); 94 | } 95 | 96 | void _animateToEdge({double? from, double? to}) { 97 | _animationController = AnimationController( 98 | vsync: this, 99 | duration: Duration(milliseconds: Sizes().screenWidth ~/ 0.6), 100 | ); 101 | _animation = Tween( 102 | begin: from, 103 | end: to, 104 | ).chain(CurveTween(curve: Curves.easeOutBack)).animate(_animationController) 105 | ..addListener(() { 106 | setState(() { 107 | position = Offset(_animation.value, position.dy); 108 | }); 109 | }); 110 | _animationController.forward(); 111 | } 112 | 113 | void _moveToLeftEdge() { 114 | _animateToEdge( 115 | from: position.dx, 116 | to: extraSpace, 117 | ); 118 | } 119 | 120 | void _moveToRightEdge() { 121 | _animateToEdge( 122 | from: position.dx, 123 | to: Sizes().screenWidth - extraSpace, 124 | ); 125 | } 126 | 127 | void _onPanUpdate(DragUpdateDetails details) { 128 | setState(() { 129 | position = details.globalPosition; 130 | if (position.dx + extraSpace > Sizes().screenWidth) { 131 | position = Offset(Sizes().screenWidth - extraSpace, position.dy); 132 | } 133 | if (position.dx - extraSpace < 0) { 134 | position = Offset(extraSpace, position.dy); 135 | } 136 | if (position.dy + extraSpaceVerBottom > Sizes().screenHeight) { 137 | position = 138 | Offset(position.dx, Sizes().screenHeight - extraSpaceVerBottom); 139 | } 140 | if (position.dy - extraSpaceVerTop < 0) { 141 | position = Offset(position.dx, extraSpaceVerTop); 142 | } 143 | }); 144 | } 145 | 146 | void _onPanEnd(DragEndDetails details) { 147 | double spaceLeft = position.dx; 148 | double spaceRight = Sizes().screenWidth - spaceLeft; 149 | if (spaceLeft < spaceRight) { 150 | _moveToLeftEdge(); 151 | } else { 152 | _moveToRightEdge(); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /domain/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "25.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.2.0" 18 | args: 19 | dependency: transitive 20 | description: 21 | name: args 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.2.0" 25 | async: 26 | dependency: transitive 27 | description: 28 | name: async 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.8.1" 32 | boolean_selector: 33 | dependency: transitive 34 | description: 35 | name: boolean_selector 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.1.0" 39 | build: 40 | dependency: transitive 41 | description: 42 | name: build 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "2.1.0" 46 | build_config: 47 | dependency: transitive 48 | description: 49 | name: build_config 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.0.0" 53 | build_daemon: 54 | dependency: transitive 55 | description: 56 | name: build_daemon 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "3.0.0" 60 | build_resolvers: 61 | dependency: transitive 62 | description: 63 | name: build_resolvers 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "2.0.4" 67 | build_runner: 68 | dependency: "direct dev" 69 | description: 70 | name: build_runner 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "2.1.2" 74 | build_runner_core: 75 | dependency: transitive 76 | description: 77 | name: build_runner_core 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "7.1.0" 81 | built_collection: 82 | dependency: transitive 83 | description: 84 | name: built_collection 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "5.1.1" 88 | built_value: 89 | dependency: transitive 90 | description: 91 | name: built_value 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "8.1.2" 95 | characters: 96 | dependency: transitive 97 | description: 98 | name: characters 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "1.1.0" 102 | charcode: 103 | dependency: transitive 104 | description: 105 | name: charcode 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.3.1" 109 | checked_yaml: 110 | dependency: transitive 111 | description: 112 | name: checked_yaml 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "2.0.1" 116 | cli_util: 117 | dependency: transitive 118 | description: 119 | name: cli_util 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "0.3.3" 123 | clock: 124 | dependency: transitive 125 | description: 126 | name: clock 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "1.1.0" 130 | code_builder: 131 | dependency: transitive 132 | description: 133 | name: code_builder 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "4.1.0" 137 | collection: 138 | dependency: transitive 139 | description: 140 | name: collection 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "1.15.0" 144 | convert: 145 | dependency: transitive 146 | description: 147 | name: convert 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "3.0.1" 151 | crypto: 152 | dependency: transitive 153 | description: 154 | name: crypto 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "3.0.1" 158 | dart_style: 159 | dependency: transitive 160 | description: 161 | name: dart_style 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "2.0.3" 165 | dio: 166 | dependency: "direct main" 167 | description: 168 | name: dio 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "4.0.0" 172 | equatable: 173 | dependency: "direct main" 174 | description: 175 | name: equatable 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "2.0.3" 179 | fake_async: 180 | dependency: transitive 181 | description: 182 | name: fake_async 183 | url: "https://pub.dartlang.org" 184 | source: hosted 185 | version: "1.2.0" 186 | file: 187 | dependency: transitive 188 | description: 189 | name: file 190 | url: "https://pub.dartlang.org" 191 | source: hosted 192 | version: "6.1.2" 193 | fixnum: 194 | dependency: transitive 195 | description: 196 | name: fixnum 197 | url: "https://pub.dartlang.org" 198 | source: hosted 199 | version: "1.0.0" 200 | flutter: 201 | dependency: "direct main" 202 | description: flutter 203 | source: sdk 204 | version: "0.0.0" 205 | flutter_test: 206 | dependency: "direct dev" 207 | description: flutter 208 | source: sdk 209 | version: "0.0.0" 210 | frontend_server_client: 211 | dependency: transitive 212 | description: 213 | name: frontend_server_client 214 | url: "https://pub.dartlang.org" 215 | source: hosted 216 | version: "2.1.2" 217 | get_it: 218 | dependency: "direct main" 219 | description: 220 | name: get_it 221 | url: "https://pub.dartlang.org" 222 | source: hosted 223 | version: "7.2.0" 224 | glob: 225 | dependency: transitive 226 | description: 227 | name: glob 228 | url: "https://pub.dartlang.org" 229 | source: hosted 230 | version: "2.0.1" 231 | graphs: 232 | dependency: transitive 233 | description: 234 | name: graphs 235 | url: "https://pub.dartlang.org" 236 | source: hosted 237 | version: "2.0.0" 238 | http_multi_server: 239 | dependency: transitive 240 | description: 241 | name: http_multi_server 242 | url: "https://pub.dartlang.org" 243 | source: hosted 244 | version: "3.0.1" 245 | http_parser: 246 | dependency: transitive 247 | description: 248 | name: http_parser 249 | url: "https://pub.dartlang.org" 250 | source: hosted 251 | version: "4.0.0" 252 | io: 253 | dependency: transitive 254 | description: 255 | name: io 256 | url: "https://pub.dartlang.org" 257 | source: hosted 258 | version: "1.0.3" 259 | js: 260 | dependency: transitive 261 | description: 262 | name: js 263 | url: "https://pub.dartlang.org" 264 | source: hosted 265 | version: "0.6.3" 266 | json_annotation: 267 | dependency: transitive 268 | description: 269 | name: json_annotation 270 | url: "https://pub.dartlang.org" 271 | source: hosted 272 | version: "4.0.1" 273 | logging: 274 | dependency: transitive 275 | description: 276 | name: logging 277 | url: "https://pub.dartlang.org" 278 | source: hosted 279 | version: "1.0.1" 280 | matcher: 281 | dependency: transitive 282 | description: 283 | name: matcher 284 | url: "https://pub.dartlang.org" 285 | source: hosted 286 | version: "0.12.10" 287 | meta: 288 | dependency: transitive 289 | description: 290 | name: meta 291 | url: "https://pub.dartlang.org" 292 | source: hosted 293 | version: "1.7.0" 294 | mime: 295 | dependency: transitive 296 | description: 297 | name: mime 298 | url: "https://pub.dartlang.org" 299 | source: hosted 300 | version: "1.0.0" 301 | package_config: 302 | dependency: transitive 303 | description: 304 | name: package_config 305 | url: "https://pub.dartlang.org" 306 | source: hosted 307 | version: "2.0.0" 308 | path: 309 | dependency: transitive 310 | description: 311 | name: path 312 | url: "https://pub.dartlang.org" 313 | source: hosted 314 | version: "1.8.0" 315 | pedantic: 316 | dependency: transitive 317 | description: 318 | name: pedantic 319 | url: "https://pub.dartlang.org" 320 | source: hosted 321 | version: "1.11.1" 322 | pool: 323 | dependency: transitive 324 | description: 325 | name: pool 326 | url: "https://pub.dartlang.org" 327 | source: hosted 328 | version: "1.5.0" 329 | pub_semver: 330 | dependency: transitive 331 | description: 332 | name: pub_semver 333 | url: "https://pub.dartlang.org" 334 | source: hosted 335 | version: "2.0.0" 336 | pubspec_parse: 337 | dependency: transitive 338 | description: 339 | name: pubspec_parse 340 | url: "https://pub.dartlang.org" 341 | source: hosted 342 | version: "1.0.0" 343 | shelf: 344 | dependency: transitive 345 | description: 346 | name: shelf 347 | url: "https://pub.dartlang.org" 348 | source: hosted 349 | version: "1.2.0" 350 | shelf_web_socket: 351 | dependency: transitive 352 | description: 353 | name: shelf_web_socket 354 | url: "https://pub.dartlang.org" 355 | source: hosted 356 | version: "1.0.1" 357 | sky_engine: 358 | dependency: transitive 359 | description: flutter 360 | source: sdk 361 | version: "0.0.99" 362 | source_span: 363 | dependency: transitive 364 | description: 365 | name: source_span 366 | url: "https://pub.dartlang.org" 367 | source: hosted 368 | version: "1.8.1" 369 | stack_trace: 370 | dependency: transitive 371 | description: 372 | name: stack_trace 373 | url: "https://pub.dartlang.org" 374 | source: hosted 375 | version: "1.10.0" 376 | stream_channel: 377 | dependency: transitive 378 | description: 379 | name: stream_channel 380 | url: "https://pub.dartlang.org" 381 | source: hosted 382 | version: "2.1.0" 383 | stream_transform: 384 | dependency: transitive 385 | description: 386 | name: stream_transform 387 | url: "https://pub.dartlang.org" 388 | source: hosted 389 | version: "2.0.0" 390 | string_scanner: 391 | dependency: transitive 392 | description: 393 | name: string_scanner 394 | url: "https://pub.dartlang.org" 395 | source: hosted 396 | version: "1.1.0" 397 | term_glyph: 398 | dependency: transitive 399 | description: 400 | name: term_glyph 401 | url: "https://pub.dartlang.org" 402 | source: hosted 403 | version: "1.2.0" 404 | test_api: 405 | dependency: transitive 406 | description: 407 | name: test_api 408 | url: "https://pub.dartlang.org" 409 | source: hosted 410 | version: "0.4.2" 411 | timing: 412 | dependency: transitive 413 | description: 414 | name: timing 415 | url: "https://pub.dartlang.org" 416 | source: hosted 417 | version: "1.0.0" 418 | typed_data: 419 | dependency: transitive 420 | description: 421 | name: typed_data 422 | url: "https://pub.dartlang.org" 423 | source: hosted 424 | version: "1.3.0" 425 | vector_math: 426 | dependency: transitive 427 | description: 428 | name: vector_math 429 | url: "https://pub.dartlang.org" 430 | source: hosted 431 | version: "2.1.0" 432 | watcher: 433 | dependency: transitive 434 | description: 435 | name: watcher 436 | url: "https://pub.dartlang.org" 437 | source: hosted 438 | version: "1.0.0" 439 | web_socket_channel: 440 | dependency: transitive 441 | description: 442 | name: web_socket_channel 443 | url: "https://pub.dartlang.org" 444 | source: hosted 445 | version: "2.1.0" 446 | yaml: 447 | dependency: transitive 448 | description: 449 | name: yaml 450 | url: "https://pub.dartlang.org" 451 | source: hosted 452 | version: "3.1.0" 453 | sdks: 454 | dart: ">=2.12.0 <3.0.0" 455 | flutter: ">=1.17.0" 456 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "3.1.5" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.3.0" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.8.1" 25 | bloc: 26 | dependency: transitive 27 | description: 28 | name: bloc 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "7.2.1" 32 | boolean_selector: 33 | dependency: transitive 34 | description: 35 | name: boolean_selector 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.1.0" 39 | cached_network_image: 40 | dependency: "direct main" 41 | description: 42 | name: cached_network_image 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "3.1.0" 46 | cached_network_image_platform_interface: 47 | dependency: transitive 48 | description: 49 | name: cached_network_image_platform_interface 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.0.0" 53 | cached_network_image_web: 54 | dependency: transitive 55 | description: 56 | name: cached_network_image_web 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.0.1" 60 | characters: 61 | dependency: transitive 62 | description: 63 | name: characters 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.1.0" 67 | charcode: 68 | dependency: transitive 69 | description: 70 | name: charcode 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.3.1" 74 | clock: 75 | dependency: transitive 76 | description: 77 | name: clock 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.1.0" 81 | collection: 82 | dependency: transitive 83 | description: 84 | name: collection 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.15.0" 88 | core: 89 | dependency: "direct main" 90 | description: 91 | path: core 92 | relative: true 93 | source: path 94 | version: "0.0.1" 95 | crypto: 96 | dependency: transitive 97 | description: 98 | name: crypto 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "3.0.1" 102 | cupertino_icons: 103 | dependency: "direct main" 104 | description: 105 | name: cupertino_icons 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.0.3" 109 | data: 110 | dependency: "direct main" 111 | description: 112 | path: data 113 | relative: true 114 | source: path 115 | version: "0.0.1" 116 | dio: 117 | dependency: "direct main" 118 | description: 119 | name: dio 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "4.0.0" 123 | domain: 124 | dependency: "direct main" 125 | description: 126 | path: domain 127 | relative: true 128 | source: path 129 | version: "0.0.1" 130 | equatable: 131 | dependency: "direct main" 132 | description: 133 | name: equatable 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "2.0.3" 137 | fake_async: 138 | dependency: transitive 139 | description: 140 | name: fake_async 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "1.2.0" 144 | ffi: 145 | dependency: transitive 146 | description: 147 | name: ffi 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "1.1.2" 151 | file: 152 | dependency: transitive 153 | description: 154 | name: file 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "6.1.2" 158 | firebase_core: 159 | dependency: transitive 160 | description: 161 | name: firebase_core 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "1.7.0" 165 | firebase_core_platform_interface: 166 | dependency: transitive 167 | description: 168 | name: firebase_core_platform_interface 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "4.0.1" 172 | firebase_core_web: 173 | dependency: transitive 174 | description: 175 | name: firebase_core_web 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "1.1.0" 179 | firebase_remote_config: 180 | dependency: transitive 181 | description: 182 | name: firebase_remote_config 183 | url: "https://pub.dartlang.org" 184 | source: hosted 185 | version: "0.11.0+1" 186 | firebase_remote_config_platform_interface: 187 | dependency: transitive 188 | description: 189 | name: firebase_remote_config_platform_interface 190 | url: "https://pub.dartlang.org" 191 | source: hosted 192 | version: "0.3.0+6" 193 | flutter: 194 | dependency: "direct main" 195 | description: flutter 196 | source: sdk 197 | version: "0.0.0" 198 | flutter_bloc: 199 | dependency: "direct main" 200 | description: 201 | name: flutter_bloc 202 | url: "https://pub.dartlang.org" 203 | source: hosted 204 | version: "7.3.0" 205 | flutter_blurhash: 206 | dependency: transitive 207 | description: 208 | name: flutter_blurhash 209 | url: "https://pub.dartlang.org" 210 | source: hosted 211 | version: "0.6.0" 212 | flutter_cache_manager: 213 | dependency: transitive 214 | description: 215 | name: flutter_cache_manager 216 | url: "https://pub.dartlang.org" 217 | source: hosted 218 | version: "3.1.2" 219 | flutter_launcher_icons: 220 | dependency: "direct dev" 221 | description: 222 | name: flutter_launcher_icons 223 | url: "https://pub.dartlang.org" 224 | source: hosted 225 | version: "0.9.2" 226 | flutter_lints: 227 | dependency: "direct dev" 228 | description: 229 | name: flutter_lints 230 | url: "https://pub.dartlang.org" 231 | source: hosted 232 | version: "1.0.4" 233 | flutter_localizations: 234 | dependency: transitive 235 | description: flutter 236 | source: sdk 237 | version: "0.0.0" 238 | flutter_svg: 239 | dependency: "direct main" 240 | description: 241 | name: flutter_svg 242 | url: "https://pub.dartlang.org" 243 | source: hosted 244 | version: "0.22.0" 245 | flutter_test: 246 | dependency: "direct dev" 247 | description: flutter 248 | source: sdk 249 | version: "0.0.0" 250 | flutter_web_plugins: 251 | dependency: transitive 252 | description: flutter 253 | source: sdk 254 | version: "0.0.0" 255 | get_it: 256 | dependency: "direct main" 257 | description: 258 | name: get_it 259 | url: "https://pub.dartlang.org" 260 | source: hosted 261 | version: "7.2.0" 262 | http: 263 | dependency: transitive 264 | description: 265 | name: http 266 | url: "https://pub.dartlang.org" 267 | source: hosted 268 | version: "0.13.3" 269 | http_parser: 270 | dependency: transitive 271 | description: 272 | name: http_parser 273 | url: "https://pub.dartlang.org" 274 | source: hosted 275 | version: "4.0.0" 276 | image: 277 | dependency: transitive 278 | description: 279 | name: image 280 | url: "https://pub.dartlang.org" 281 | source: hosted 282 | version: "3.0.7" 283 | internet_connection_checker: 284 | dependency: transitive 285 | description: 286 | name: internet_connection_checker 287 | url: "https://pub.dartlang.org" 288 | source: hosted 289 | version: "0.0.1+2" 290 | intl: 291 | dependency: transitive 292 | description: 293 | name: intl 294 | url: "https://pub.dartlang.org" 295 | source: hosted 296 | version: "0.17.0" 297 | js: 298 | dependency: transitive 299 | description: 300 | name: js 301 | url: "https://pub.dartlang.org" 302 | source: hosted 303 | version: "0.6.3" 304 | lints: 305 | dependency: transitive 306 | description: 307 | name: lints 308 | url: "https://pub.dartlang.org" 309 | source: hosted 310 | version: "1.0.1" 311 | matcher: 312 | dependency: transitive 313 | description: 314 | name: matcher 315 | url: "https://pub.dartlang.org" 316 | source: hosted 317 | version: "0.12.10" 318 | meta: 319 | dependency: transitive 320 | description: 321 | name: meta 322 | url: "https://pub.dartlang.org" 323 | source: hosted 324 | version: "1.7.0" 325 | nested: 326 | dependency: transitive 327 | description: 328 | name: nested 329 | url: "https://pub.dartlang.org" 330 | source: hosted 331 | version: "1.0.0" 332 | octo_image: 333 | dependency: transitive 334 | description: 335 | name: octo_image 336 | url: "https://pub.dartlang.org" 337 | source: hosted 338 | version: "1.0.0+1" 339 | package_info: 340 | dependency: transitive 341 | description: 342 | name: package_info 343 | url: "https://pub.dartlang.org" 344 | source: hosted 345 | version: "2.0.2" 346 | path: 347 | dependency: transitive 348 | description: 349 | name: path 350 | url: "https://pub.dartlang.org" 351 | source: hosted 352 | version: "1.8.0" 353 | path_drawing: 354 | dependency: transitive 355 | description: 356 | name: path_drawing 357 | url: "https://pub.dartlang.org" 358 | source: hosted 359 | version: "0.5.1" 360 | path_parsing: 361 | dependency: transitive 362 | description: 363 | name: path_parsing 364 | url: "https://pub.dartlang.org" 365 | source: hosted 366 | version: "0.2.1" 367 | path_provider: 368 | dependency: transitive 369 | description: 370 | name: path_provider 371 | url: "https://pub.dartlang.org" 372 | source: hosted 373 | version: "2.0.5" 374 | path_provider_linux: 375 | dependency: transitive 376 | description: 377 | name: path_provider_linux 378 | url: "https://pub.dartlang.org" 379 | source: hosted 380 | version: "2.1.0" 381 | path_provider_macos: 382 | dependency: transitive 383 | description: 384 | name: path_provider_macos 385 | url: "https://pub.dartlang.org" 386 | source: hosted 387 | version: "2.0.2" 388 | path_provider_platform_interface: 389 | dependency: transitive 390 | description: 391 | name: path_provider_platform_interface 392 | url: "https://pub.dartlang.org" 393 | source: hosted 394 | version: "2.0.1" 395 | path_provider_windows: 396 | dependency: transitive 397 | description: 398 | name: path_provider_windows 399 | url: "https://pub.dartlang.org" 400 | source: hosted 401 | version: "2.0.3" 402 | pedantic: 403 | dependency: transitive 404 | description: 405 | name: pedantic 406 | url: "https://pub.dartlang.org" 407 | source: hosted 408 | version: "1.11.1" 409 | petitparser: 410 | dependency: transitive 411 | description: 412 | name: petitparser 413 | url: "https://pub.dartlang.org" 414 | source: hosted 415 | version: "4.3.0" 416 | platform: 417 | dependency: transitive 418 | description: 419 | name: platform 420 | url: "https://pub.dartlang.org" 421 | source: hosted 422 | version: "3.0.2" 423 | plugin_platform_interface: 424 | dependency: transitive 425 | description: 426 | name: plugin_platform_interface 427 | url: "https://pub.dartlang.org" 428 | source: hosted 429 | version: "2.0.1" 430 | process: 431 | dependency: transitive 432 | description: 433 | name: process 434 | url: "https://pub.dartlang.org" 435 | source: hosted 436 | version: "4.2.3" 437 | provider: 438 | dependency: transitive 439 | description: 440 | name: provider 441 | url: "https://pub.dartlang.org" 442 | source: hosted 443 | version: "6.0.1" 444 | rxdart: 445 | dependency: transitive 446 | description: 447 | name: rxdart 448 | url: "https://pub.dartlang.org" 449 | source: hosted 450 | version: "0.27.2" 451 | shared_preferences: 452 | dependency: transitive 453 | description: 454 | name: shared_preferences 455 | url: "https://pub.dartlang.org" 456 | source: hosted 457 | version: "2.0.8" 458 | shared_preferences_linux: 459 | dependency: transitive 460 | description: 461 | name: shared_preferences_linux 462 | url: "https://pub.dartlang.org" 463 | source: hosted 464 | version: "2.0.2" 465 | shared_preferences_macos: 466 | dependency: transitive 467 | description: 468 | name: shared_preferences_macos 469 | url: "https://pub.dartlang.org" 470 | source: hosted 471 | version: "2.0.2" 472 | shared_preferences_platform_interface: 473 | dependency: transitive 474 | description: 475 | name: shared_preferences_platform_interface 476 | url: "https://pub.dartlang.org" 477 | source: hosted 478 | version: "2.0.0" 479 | shared_preferences_web: 480 | dependency: transitive 481 | description: 482 | name: shared_preferences_web 483 | url: "https://pub.dartlang.org" 484 | source: hosted 485 | version: "2.0.2" 486 | shared_preferences_windows: 487 | dependency: transitive 488 | description: 489 | name: shared_preferences_windows 490 | url: "https://pub.dartlang.org" 491 | source: hosted 492 | version: "2.0.2" 493 | sky_engine: 494 | dependency: transitive 495 | description: flutter 496 | source: sdk 497 | version: "0.0.99" 498 | source_span: 499 | dependency: transitive 500 | description: 501 | name: source_span 502 | url: "https://pub.dartlang.org" 503 | source: hosted 504 | version: "1.8.1" 505 | sqflite: 506 | dependency: transitive 507 | description: 508 | name: sqflite 509 | url: "https://pub.dartlang.org" 510 | source: hosted 511 | version: "2.0.0+4" 512 | sqflite_common: 513 | dependency: transitive 514 | description: 515 | name: sqflite_common 516 | url: "https://pub.dartlang.org" 517 | source: hosted 518 | version: "2.0.1+1" 519 | stack_trace: 520 | dependency: transitive 521 | description: 522 | name: stack_trace 523 | url: "https://pub.dartlang.org" 524 | source: hosted 525 | version: "1.10.0" 526 | stream_channel: 527 | dependency: transitive 528 | description: 529 | name: stream_channel 530 | url: "https://pub.dartlang.org" 531 | source: hosted 532 | version: "2.1.0" 533 | string_scanner: 534 | dependency: transitive 535 | description: 536 | name: string_scanner 537 | url: "https://pub.dartlang.org" 538 | source: hosted 539 | version: "1.1.0" 540 | synchronized: 541 | dependency: transitive 542 | description: 543 | name: synchronized 544 | url: "https://pub.dartlang.org" 545 | source: hosted 546 | version: "3.0.0" 547 | term_glyph: 548 | dependency: transitive 549 | description: 550 | name: term_glyph 551 | url: "https://pub.dartlang.org" 552 | source: hosted 553 | version: "1.2.0" 554 | test_api: 555 | dependency: transitive 556 | description: 557 | name: test_api 558 | url: "https://pub.dartlang.org" 559 | source: hosted 560 | version: "0.4.2" 561 | typed_data: 562 | dependency: transitive 563 | description: 564 | name: typed_data 565 | url: "https://pub.dartlang.org" 566 | source: hosted 567 | version: "1.3.0" 568 | utils: 569 | dependency: "direct main" 570 | description: 571 | path: "plugin/utils" 572 | relative: true 573 | source: path 574 | version: "0.0.1" 575 | uuid: 576 | dependency: transitive 577 | description: 578 | name: uuid 579 | url: "https://pub.dartlang.org" 580 | source: hosted 581 | version: "3.0.4" 582 | vector_math: 583 | dependency: transitive 584 | description: 585 | name: vector_math 586 | url: "https://pub.dartlang.org" 587 | source: hosted 588 | version: "2.1.0" 589 | win32: 590 | dependency: transitive 591 | description: 592 | name: win32 593 | url: "https://pub.dartlang.org" 594 | source: hosted 595 | version: "2.2.9" 596 | xdg_directories: 597 | dependency: transitive 598 | description: 599 | name: xdg_directories 600 | url: "https://pub.dartlang.org" 601 | source: hosted 602 | version: "0.2.0" 603 | xml: 604 | dependency: transitive 605 | description: 606 | name: xml 607 | url: "https://pub.dartlang.org" 608 | source: hosted 609 | version: "5.3.0" 610 | yaml: 611 | dependency: transitive 612 | description: 613 | name: yaml 614 | url: "https://pub.dartlang.org" 615 | source: hosted 616 | version: "3.1.0" 617 | sdks: 618 | dart: ">=2.14.0 <3.0.0" 619 | flutter: ">=2.5.0" 620 | --------------------------------------------------------------------------------