Skip to content

anbuidevelopment/flutter_auth_app

Β 
Β 

Repository files navigation


Flutter App Auth πŸ“±

This is an App with Auth Functions like Login and Register. All API using reqres.in.
This app also implementing Flutter Clean Architecture with TDD.

light-theme.mp4
dark-theme.mp4

Pre-requisites πŸ“

Technology Recommended Version Installation Guide
Flutter v3.10.x Flutter Official Docs
Dart v3.0.x Installed automatically with Flutter

Get Started πŸš€

  • Clone this project
  • Run flutter pub get
  • Run flutter gen-l10n to generate localization files
  • Run flutter pub run build_runner build --delete-conflicting-outputs to generate freezes files
  • Run flutter run --flavor stg -t lib/main.dart --dart-define-from-file .env.stg.json for staging or
  • Run flutter run --flavor prd -t lib/main.dart --dart-define-from-file .env.prd.json for production
  • Run Test flutter test
  • To generate launcher icon based on Flavor dart run flutter_launcher_icons
  • To generate mock class dart pub run build_runner build

Feature βœ…

  • BLoC State Management
  • Clean Architecture with TDD
    • Unit Test
    • Widget Test
    • BLoC test
  • Theme Configuration: System, Light, Dark
  • Multi-Language: English, Bahasa
  • Login, Register Example
  • Pagination Example
  • Autofill Username and Password

TODO πŸ“

  • Integration Test
  • Implement multi-flavor
  • Implement Go Router

Maestro Test πŸ§ͺ

574ae4cc-e3a9-4755-8494-81bfe8763e00.mp4

Architecture Proposal by Resocoder


architecture-proposal

Project Structure


lib
β”œβ”€β”€ config.dart
β”œβ”€β”€ core
β”‚Β Β  β”œβ”€β”€ api
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ api.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ dio_client.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ dio_interceptor.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ isolate_parser.dart
β”‚Β Β  β”‚Β Β  └── list_api.dart
β”‚Β Β  β”œβ”€β”€ app_route.dart
β”‚Β Β  β”œβ”€β”€ core.dart
β”‚Β Β  β”œβ”€β”€ core_mapper.dart
β”‚Β Β  β”œβ”€β”€ error
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ error.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ exceptions.dart
β”‚Β Β  β”‚Β Β  └── failure.dart
β”‚Β Β  β”œβ”€β”€ localization
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ generated
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ strings.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ strings_en.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── strings_id.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ intl_en.arb
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ intl_id.arb
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ l10n.dart
β”‚Β Β  β”‚Β Β  └── localization.dart
β”‚Β Β  β”œβ”€β”€ resources
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ dimens.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ images.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ palette.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ resources.dart
β”‚Β Β  β”‚Β Β  └── styles.dart
β”‚Β Β  β”œβ”€β”€ usecase
β”‚Β Β  β”‚Β Β  └── usecase.dart
β”‚Β Β  └── widgets
β”‚Β Β      β”œβ”€β”€ button.dart
β”‚Β Β      β”œβ”€β”€ button_notification.dart
β”‚Β Β      β”œβ”€β”€ button_text.dart
β”‚Β Β      β”œβ”€β”€ circle_image.dart
β”‚Β Β      β”œβ”€β”€ color_loaders.dart
β”‚Β Β      β”œβ”€β”€ drop_down.dart
β”‚Β Β      β”œβ”€β”€ empty.dart
β”‚Β Β      β”œβ”€β”€ loading.dart
β”‚Β Β      β”œβ”€β”€ my_appbar.dart
β”‚Β Β      β”œβ”€β”€ parent.dart
β”‚Β Β      β”œβ”€β”€ spacer_h.dart
β”‚Β Β      β”œβ”€β”€ spacer_v.dart
β”‚Β Β      β”œβ”€β”€ text_f.dart
β”‚Β Β      β”œβ”€β”€ toast.dart
β”‚Β Β      └── widgets.dart
β”œβ”€β”€ dependencies_injection.dart
β”œβ”€β”€ features
β”‚Β Β  β”œβ”€β”€ auth
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ auth.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ data
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ data.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ datasources
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ auth_remote_datasources.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  └── datasources.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ models
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ login_response.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ login_response.freezed.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ login_response.g.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ models.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ register_response.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ register_response.freezed.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  └── register_response.g.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── repositories
β”‚Β Β  β”‚Β Β  β”‚Β Β      β”œβ”€β”€ auth_repository_impl.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β      └── repositories.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ domain
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ domain.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ entities
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ entities.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ login.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ login.freezed.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ register.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  └── register.freezed.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ repositories
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ auth_repository.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  └── repositories.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── usecases
β”‚Β Β  β”‚Β Β  β”‚Β Β      β”œβ”€β”€ post_login.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β      β”œβ”€β”€ post_login.freezed.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β      β”œβ”€β”€ post_login.g.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β      β”œβ”€β”€ post_register.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β      β”œβ”€β”€ post_register.freezed.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β      β”œβ”€β”€ post_register.g.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β      └── usecases.dart
β”‚Β Β  β”‚Β Β  └── pages
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ login
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”œβ”€β”€ cubit
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ auth_cubit.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ auth_cubit.freezed.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ auth_state.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”‚Β Β  └── cubit.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”œβ”€β”€ login.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  └── login_page.dart
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ pages.dart
β”‚Β Β  β”‚Β Β      └── register
β”‚Β Β  β”‚Β Β          β”œβ”€β”€ cubit
β”‚Β Β  β”‚Β Β          β”‚Β Β  β”œβ”€β”€ cubit.dart
β”‚Β Β  β”‚Β Β          β”‚Β Β  β”œβ”€β”€ register_cubit.dart
β”‚Β Β  β”‚Β Β          β”‚Β Β  β”œβ”€β”€ register_cubit.freezed.dart
β”‚Β Β  β”‚Β Β          β”‚Β Β  └── register_state.dart
β”‚Β Β  β”‚Β Β          β”œβ”€β”€ register.dart
β”‚Β Β  β”‚Β Β          └── register_page.dart
β”‚Β Β  β”œβ”€β”€ features.dart
β”‚Β Β  β”œβ”€β”€ general
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ general.dart
β”‚Β Β  β”‚Β Β  └── pages
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ main
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”œβ”€β”€ main.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”œβ”€β”€ main_page.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  └── menu_drawer.dart
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ pages.dart
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ settings
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”œβ”€β”€ cubit
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ cubit.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”‚Β Β  └── settings_cubit.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”œβ”€β”€ settings.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  └── settings_page.dart
β”‚Β Β  β”‚Β Β      └── splashscreen
β”‚Β Β  β”‚Β Β          β”œβ”€β”€ splash_screen_page.dart
β”‚Β Β  β”‚Β Β          └── splashscreen.dart
β”‚Β Β  └── users
β”‚Β Β      β”œβ”€β”€ data
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ data.dart
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ datasources
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ datasources.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── user_remote_datasources.dart
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ models
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ models.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ users_response.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ users_response.freezed.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── users_response.g.dart
β”‚Β Β      β”‚Β Β  └── repositories
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ repositories.dart
β”‚Β Β      β”‚Β Β      └── users_repository_impl.dart
β”‚Β Β      β”œβ”€β”€ domain
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ domain.dart
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ entities
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ entities.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ users.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── users.freezed.dart
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ repositories
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ repositories.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── users_repository.dart
β”‚Β Β      β”‚Β Β  └── usecases
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ get_users.dart
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ get_users.freezed.dart
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ get_users.g.dart
β”‚Β Β      β”‚Β Β      └── usecases.dart
β”‚Β Β      β”œβ”€β”€ pages
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ dashboard
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ cubit
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ cubit.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ users_cubit.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ users_cubit.freezed.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”‚Β Β  └── users_state.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ dashboard.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── dashboard_page.dart
β”‚Β Β      β”‚Β Β  └── pages.dart
β”‚Β Β      └── users.dart
β”œβ”€β”€ lzyct_app.dart
β”œβ”€β”€ main_prd.dart
β”œβ”€β”€ main_stg.dart
└── utils
    β”œβ”€β”€ ext
    β”‚Β Β  β”œβ”€β”€ context.dart
    β”‚Β Β  β”œβ”€β”€ ext.dart
    β”‚Β Β  β”œβ”€β”€ string.dart
    β”‚Β Β  └── text_theme.dart
    β”œβ”€β”€ helper
    β”‚Β Β  β”œβ”€β”€ common.dart
    β”‚Β Β  β”œβ”€β”€ constant.dart
    β”‚Β Β  β”œβ”€β”€ data_helper.dart
    β”‚Β Β  β”œβ”€β”€ data_helper.freezed.dart
    β”‚Β Β  β”œβ”€β”€ go_router_refresh_stream.dart
    β”‚Β Β  └── helper.dart
    β”œβ”€β”€ services
    β”‚Β Β  β”œβ”€β”€ firebase
    β”‚Β Β  β”‚Β Β  β”œβ”€β”€ firebase.dart
    β”‚Β Β  β”‚Β Β  β”œβ”€β”€ firebase_crashlogger.dart
    β”‚Β Β  β”‚Β Β  └── firebase_services.dart
    β”‚Β Β  β”œβ”€β”€ hive
    β”‚Β Β  β”‚Β Β  β”œβ”€β”€ hive.dart
    β”‚Β Β  β”‚Β Β  └── main_box.dart
    β”‚Β Β  └── services.dart
    └── utils.dart

Test Project Structure


test
β”œβ”€β”€ features
β”‚Β Β  β”œβ”€β”€ auth
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ data
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ datasources
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ models
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ login_response_test.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  └── register_response_test.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β  └── repositories
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”‚Β Β      └── auth_remote_datasources_test.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── repositories
β”‚Β Β  β”‚Β Β  β”‚Β Β      └── auth_repository_impl_test.dart
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ domain
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── usecases
β”‚Β Β  β”‚Β Β  β”‚Β Β      β”œβ”€β”€ post_login_test.dart
β”‚Β Β  β”‚Β Β  β”‚Β Β      └── post_register_test.dart
β”‚Β Β  β”‚Β Β  └── pages
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ login
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”œβ”€β”€ cubit
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ auth_cubit_test.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ auth_cubit_test.mocks.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  β”‚Β Β  └── auth_state_test.dart
β”‚Β Β  β”‚Β Β      β”‚Β Β  └── login_page_test.dart
β”‚Β Β  β”‚Β Β      └── register
β”‚Β Β  β”‚Β Β          β”œβ”€β”€ cubit
β”‚Β Β  β”‚Β Β          β”‚Β Β  β”œβ”€β”€ register_cubit_test.dart
β”‚Β Β  β”‚Β Β          β”‚Β Β  β”œβ”€β”€ register_cubit_test.mocks.dart
β”‚Β Β  β”‚Β Β          β”‚Β Β  └── register_state_test.dart
β”‚Β Β  β”‚Β Β          └── register_page_test.dart
β”‚Β Β  β”œβ”€β”€ general
β”‚Β Β  β”‚Β Β  └── pages
β”‚Β Β  β”‚Β Β      └── settings
β”‚Β Β  β”‚Β Β          β”œβ”€β”€ cubit
β”‚Β Β  β”‚Β Β          β”‚Β Β  └── settings_cubit_test.dart
β”‚Β Β  β”‚Β Β          └── settings_page_test.dart
β”‚Β Β  └── users
β”‚Β Β      β”œβ”€β”€ data
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ datasources
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”œβ”€β”€ models
β”‚Β Β      β”‚Β Β  β”‚Β Β  β”‚Β Β  └── users_response_test.dart
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── repositories
β”‚Β Β      β”‚Β Β  β”‚Β Β      └── users_remote_datasources_test.dart
β”‚Β Β      β”‚Β Β  └── repositories
β”‚Β Β      β”‚Β Β      └── users_repository_impl_test.dart
β”‚Β Β      β”œβ”€β”€ domain
β”‚Β Β      β”‚Β Β  └── usecases
β”‚Β Β      β”‚Β Β      └── get_users_test.dart
β”‚Β Β      └── pages
β”‚Β Β          └── dashboard
β”‚Β Β              β”œβ”€β”€ cubit
β”‚Β Β              β”‚Β Β  β”œβ”€β”€ users_cubit_test.dart
β”‚Β Β              β”‚Β Β  β”œβ”€β”€ users_cubit_test.mocks.dart
β”‚Β Β              β”‚Β Β  └── users_state_test.dart
β”‚Β Β              └── dashboard_page_test.dart
└── helpers
    β”œβ”€β”€ fake_path_provider_platform.dart
    β”œβ”€β”€ json_reader.dart
    β”œβ”€β”€ paths.dart
    β”œβ”€β”€ stubs
    β”‚Β Β  β”œβ”€β”€ list_user_empty_response.json
    β”‚Β Β  β”œβ”€β”€ list_user_response.json
    β”‚Β Β  β”œβ”€β”€ login_success_response.json
    β”‚Β Β  β”œβ”€β”€ login_unsuccessful_response.json
    β”‚Β Β  β”œβ”€β”€ register_success_response.json
    β”‚Β Β  └── register_unsuccessful_response.json
    β”œβ”€β”€ test_mock.dart
    └── test_mock.mocks.dart



Buy me coffee if you love my works β˜•οΈ

buymeacoffe Β Β Β Β  ko-fi Β Β Β Β  paypal saweria



About

Flutter auth app with TDD Clean Architecture

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Dart 99.3%
  • Other 0.7%