clean_architect 0.6.0 copy "clean_architect: ^0.6.0" to clipboard
clean_architect: ^0.6.0 copied to clipboard

A configurable CLI generator for clean architecture Flutter and Dart feature modules.

clean_architect #

A configurable Dart CLI generator for Clean Architecture Flutter/Dart projects.

clean_architect generates boring, predictable architecture files that you can edit immediately. The generator does not try to hide your app behind runtime abstractions. The flexibility lives in clean_architect.yaml: paths, layer layout, state management, network client, local storage, model style, and dependency injection style.

Install #

dart pub global activate clean_architect

After global activation you can run the executable from any folder:

clean_architect init
clean_architect create architecture
clean_architect create auth
clean_architect create feature orders

Empty Folder Usage #

Use the global executable when generating into an empty folder:

mkdir my_app
cd my_app
clean_architect create architecture

Do not use dart run clean_architect ... in an empty folder. dart run needs a local pubspec.yaml before the CLI can start. Use dart run clean_architect ... only when developing this package from its own checkout or from another Dart project that already has a pubspec.yaml.

Commands #

clean_architect --version
clean_architect <command> --help
clean_architect create feature --help
clean_architect init
clean_architect doctor
clean_architect create architecture
clean_architect create base
clean_architect create auth
clean_architect create feature <name>
clean_architect create usecase <name> --feature <feature>
clean_architect create repository <feature>
clean_architect create remote-function <name> --feature <feature>
clean_architect create local-function <name> --feature <feature>
clean_architect create cached-function <name> --feature <feature>

Examples:

clean_architect init
clean_architect create architecture
clean_architect create feature orders
clean_architect create auth
clean_architect create usecase login --feature auth
clean_architect create repository auth
clean_architect create remote-function loadDetails --feature orders
clean_architect create local-function readDraft --feature orders
clean_architect create cached-function syncDetails --feature orders
clean_architect doctor

Useful flags:

clean_architect init --dry-run
clean_architect init --force

clean_architect create architecture --dry-run
clean_architect create auth --dry-run
clean_architect create auth --overwrite
clean_architect create auth --force
clean_architect create feature profile --skip-presentation

clean_architect create auth --state getx
clean_architect create auth --state bloc
clean_architect create auth --state provider
clean_architect create auth --state none
clean_architect create auth --network dio
clean_architect create auth --network abstract
clean_architect create auth --storage secure_storage
clean_architect create auth --storage shared_preferences
clean_architect create auth --storage hive
clean_architect create auth --storage objectbox
clean_architect create auth --storage abstract
clean_architect create auth --di injectable
clean_architect create auth --dependency-injection manual
clean_architect create feature orders --use-either-failure
clean_architect create feature orders --no-use-either-failure
clean_architect create architecture --flutter-create --platforms android,ios
clean_architect create auth --flutter-create --platforms android,ios,web

--overwrite and --force are required before conflicting generated files are replaced. Complete features and identical generated files are treated as idempotent no-ops.

Configuration File #

Run:

clean_architect init

This creates clean_architect.yaml:

clean_architect:
  config_version: 1
  structure: layered_packages # layered_packages or feature_first
  state_management: getx # getx, bloc, provider, or none
  network: dio # dio or abstract
  local_storage: secure_storage # secure_storage, shared_preferences, hive, objectbox, or abstract
  dependency_injection: manual # manual or injectable
  use_asset_generator: true
  use_either_failure: false
  flutter:
    create_presentation: false
    platforms:
      - android
      - ios
  models:
    use_freezed: true
    use_json_serializable: true
  paths:
    domain: domain/lib
    data: data/lib/features
    presentation: presentation/lib
    di: di/lib

Configuration Reference #

Key Values Default What it controls
config_version positive integer 1 Configuration schema version used for future migrations.
structure layered_packages, feature_first layered_packages How feature paths are resolved from the configured layer paths.
state_management getx, bloc, provider, none getx Presentation controller/page style.
network dio, abstract dio Remote data source style and generated data dependencies.
local_storage secure_storage, shared_preferences, hive, objectbox, abstract secure_storage Local auth credential storage style and generated storage dependencies.
dependency_injection manual, injectable manual Manual DI builder files or injectable/get_it setup files and annotations.
use_asset_generator true, false true Whether presentation gets asset_generator_kit.yaml and the asset generator dependency.
use_either_failure true, false false Whether generated repositories, repository implementations, and use cases return Future<Either<Failure, T>>.
flutter.create_presentation true, false false Whether to run flutter create . automatically inside the generated presentation package.
flutter.platforms list or comma-separated text android, ios Platforms passed to flutter create . --platforms=....
models.use_freezed true, false true Whether entities/DTOs use Freezed.
models.use_json_serializable true, false true Whether DTOs include JSON serialization parts/factories.
paths.domain path domain/lib Domain layer feature root.
paths.data path data/lib/features Data layer feature root.
paths.presentation path presentation/lib Presentation layer root.
paths.di path di/lib Dependency injection layer root.

Use abstract when you want the source boundaries without a concrete local storage package.

CLI overrides are intentionally small and only affect the current command. They do not rewrite clean_architect.yaml.

Generated Project Shape #

The default architecture is a multi-package Flutter/Dart workspace shape:

my_app/
  domain/
    pubspec.yaml
    lib/
      features/
        base_feature/
          entities/
          repositories/
          usecases/

  data/
    pubspec.yaml
    lib/
      features/
        base_feature/
          remote/
            models/
          local/
            models/
          repositories/

  di/
    pubspec.yaml
    lib/

  presentation/
    pubspec.yaml
    analysis_options.yaml
    asset_generator_kit.yaml
    assets/
      images/
      icons/
    lib/
      main.dart
      widgets/
      pages/
      utils/
      controllers/
      constants/

Every layer gets its own pubspec.yaml:

  • domain is a pure Dart package for entities, repository contracts, and use cases.
  • data is a Dart/Flutter package for DTOs, remote data sources, local sources, mappers, and repository implementations.
  • di is a Dart package that connects domain and data dependencies.
  • presentation is a runnable Flutter package with main.dart, Flutter dependencies, and UI folders.

After generation, run Flutter setup inside presentation when you want a complete Flutter platform project:

cd presentation
flutter create .
flutter pub get

Or let clean_architect do the Flutter project bootstrap automatically:

clean_architect create architecture --flutter-create --platforms android,ios

The same behavior can be configured in clean_architect.yaml:

clean_architect:
  flutter:
    create_presentation: true
    platforms:
      - android
      - ios
      - web

When enabled, the CLI runs flutter create . --platforms=<platforms> from the generated presentation/ package root after writing the architecture files. --dry-run prints the command without executing it. --skip-presentation disables this step for the current command.

Run dart pub get in the other layer packages as needed.

Flutter Presentation Bootstrap #

By default, clean_architect creates the presentation package files but does not run Flutter tooling. This keeps generation fast and works even on machines without Flutter installed.

To generate Flutter platform folders automatically, use:

clean_architect create architecture --flutter-create --platforms android,ios

Supported platform names are the Flutter platform names: android, ios, web, macos, windows, and linux.

The YAML equivalent is:

clean_architect:
  flutter:
    create_presentation: true
    platforms: android,ios

or:

clean_architect:
  flutter:
    create_presentation: true
    platforms:
      - android
      - ios

This only runs for commands that generate presentation structure: create architecture, create base, create auth, and create feature <name>. It is skipped for operation commands, create usecase, create repository, and commands using --skip-presentation.

Structure Modes #

Default Layered Packages #

clean_architect:
  config_version: 1
  structure: layered_packages
  paths:
    domain: domain/lib
    data: data/lib/features
    presentation: presentation/lib
    di: di/lib

clean_architect create auth creates:

domain/lib/features/auth/...
data/lib/features/auth/...
di/lib/auth_di.dart
presentation/lib/controllers/auth_controller.dart
presentation/lib/pages/login_page.dart

Custom Layer Paths #

You can point the layers to existing packages or app folders:

clean_architect:
  config_version: 1
  structure: layered_packages
  paths:
    domain: packages/domain/lib/modules
    data: packages/data/lib/modules
    presentation: apps/customer_app/lib
    di: packages/di/lib

Then clean_architect create feature profile creates feature files under those configured roots.

Feature First #

feature_first is available for projects that still want feature-based grouping while keeping the same layer packages:

clean_architect:
  config_version: 1
  structure: feature_first
  paths:
    domain: domain/lib
    data: data/lib/features
    presentation: presentation/lib
    di: di/lib

Feature-first resolution keeps the four layer packages while grouping each feature inside every layer:

domain/lib/features/<feature>/...
data/lib/features/<feature>/...
presentation/lib/features/<feature>/pages/...
presentation/lib/features/<feature>/controllers/...
di/lib/features/<feature>/...

create architecture #

clean_architect create architecture

Generates the layer packages and default folders only. It does not generate auth code.

Default placeholder feature name:

domain/lib/features/base_feature
data/lib/features/base_feature

Use this when you want the clean architecture project skeleton first, then add features later.

create feature <name> #

clean_architect create feature orders

Generates a generic feature module.

Domain:

domain/lib/features/orders/entities/orders_entity.dart
domain/lib/features/orders/repositories/orders_repository.dart
domain/lib/features/orders/usecases/get_orders_list_use_case.dart

Data:

data/lib/features/orders/remote/models/orders_dto.dart
data/lib/features/orders/remote/orders_remote_data_source.dart
data/lib/features/orders/local/models/orders_box.dart
data/lib/features/orders/local/orders_local_data_source.dart
data/lib/features/orders/mappers/orders_mapper.dart
data/lib/features/orders/repositories/orders_repository_impl.dart

Presentation, unless --skip-presentation is used:

presentation/lib/controllers/orders_controller.dart
presentation/lib/pages/orders_page.dart

The generic feature is intentionally minimal: entity, DTO, mapper, repository contract, repository implementation, list use case, local source, Retrofit remote data source, controller, and page. Its OrdersViewItem is a real widget declared in orders_page.dart, so a one-off UI element does not get its own file.

create auth #

clean_architect create auth

Generates a concrete auth starter feature.

Domain:

domain/lib/features/auth/entities/auth_token_entity.dart
domain/lib/features/auth/entities/auth_credentials_entity.dart
domain/lib/features/auth/repositories/auth_repository.dart
domain/lib/features/auth/usecases/login_use_case.dart
domain/lib/features/auth/usecases/logout_use_case.dart
domain/lib/features/auth/usecases/save_auth_credentials_use_case.dart
domain/lib/features/auth/usecases/get_auth_credentials_use_case.dart
domain/lib/features/auth/usecases/clear_auth_credentials_use_case.dart

Data:

data/lib/features/auth/remote/models/auth_token_dto.dart
data/lib/features/auth/remote/models/login_request_dto.dart
data/lib/features/auth/remote/auth_remote_data_source.dart
data/lib/features/auth/local/models/auth_box.dart
data/lib/features/auth/local/auth_local_data_source.dart
data/lib/features/auth/mappers/auth_token_mapper.dart
data/lib/features/auth/repositories/auth_repository_impl.dart

Presentation, unless --skip-presentation is used:

presentation/lib/controllers/auth_controller.dart
presentation/lib/pages/login_page.dart

Login form data is generated as LoginState in auth_controller.dart; it is not emitted as a non-widget file under widgets/.

The generated remote remote data source uses Dio + Retrofit style:

@lazySingleton
@RestApi(baseUrl: '')
abstract class AuthRemoteDataSource {
  @factoryMethod
  factory AuthRemoteDataSource(@Named("auth_dio") Dio dio) = _AuthRemoteDataSource;

  @POST('/authorization/token/')
  Future<AuthTokenDto> login(@Body() Map<String, dynamic> body);
}

The generated auth controller uses GetIt.instance.get<LoginUseCase>(), and the GetX page registers the controller in initState with Get.put(AuthController()).

Operation Commands #

Operation commands add a new function to an existing feature. They generate the required entity/model/usecase support files and patch the existing source, repository, repository implementation, and controller files.

Remote Function #

clean_architect create remote-function loadDetails --feature orders

Aliases: remote-function, remote-method.

Adds:

domain/lib/features/orders/entities/load_details_entity.dart
domain/lib/features/orders/usecases/load_details_use_case.dart
data/lib/features/orders/remote/models/load_details_dto.dart
data/lib/features/orders/mappers/load_details_mapper.dart

Patches:

data/lib/features/orders/remote/orders_remote_data_source.dart
domain/lib/features/orders/repositories/orders_repository.dart
data/lib/features/orders/repositories/orders_repository_impl.dart
presentation/lib/controllers/orders_controller.dart

Local Function #

clean_architect create local-function readDraft --feature orders

Aliases: local-function, local-method.

Adds:

domain/lib/features/orders/entities/read_draft_entity.dart
domain/lib/features/orders/usecases/read_draft_use_case.dart
data/lib/features/orders/local/models/read_draft_box.dart
data/lib/features/orders/mappers/read_draft_mapper.dart

Patches the local source, repository contract, repository implementation, and controller.

Cached Function #

clean_architect create cached-function syncDetails --feature orders

Aliases: cached-function, cached-method.

Adds remote and local support together:

domain/lib/features/orders/entities/sync_details_entity.dart
domain/lib/features/orders/usecases/sync_details_use_case.dart
domain/lib/features/orders/usecases/stream_details_use_case.dart
data/lib/features/orders/remote/models/sync_details_dto.dart
data/lib/features/orders/local/models/sync_details_box.dart
data/lib/features/orders/mappers/sync_details_mapper.dart
data/lib/features/orders/mappers/sync_details_box_mapper.dart

Patches the remote source with syncDetails(), the local source with streamDetails(), and uses those same names in the repository, use cases, repository implementation, and controller. The generated use cases are SyncDetailsUseCase and StreamDetailsUseCase.

Dependency Injection Modes #

Manual #

dependency_injection: manual

Manual mode generates simple DI builder files, for example:

di/lib/auth_di.dart
di/lib/orders_di.dart

Use this when you want explicit constructors and simple dependency wiring you can edit by hand.

Injectable #

dependency_injection: injectable

Injectable mode adds injectable/get_it dependencies and generates injector entry files:

domain/lib/injector.dart
data/lib/injector.dart
di/lib/di.dart

Generated classes receive injectable annotations where supported. After generation, run build runner in the generated packages that contain injectable/freezed/json_serializable code.

cd domain
dart run build_runner build
cd ../data
dart run build_runner build

Either / Failure Return Type #

use_either_failure: true

When enabled, generated repository contracts, repository implementations, and use cases use:

Future<Either<Failure, T>>

instead of:

Future<T>

The generator also creates domain/lib/failures/failure.dart where needed and adds dartz to generated layer pubspecs. You can override the value for one command with --use-either-failure or --no-use-either-failure.

Model Modes #

Freezed + JSON Serializable #

models:
  use_freezed: true
  use_json_serializable: true

Entities and DTOs use Freezed. DTOs also include JSON serialization parts when JSON serialization is enabled.

Typical follow-up command in generated packages:

dart run build_runner build

Plain Dart Fallback #

models:
  use_freezed: false
  use_json_serializable: false

Entities and DTOs are generated as simple Dart classes.

State Management #

GetX #

state_management: getx

Presentation controllers extend GetxController, pages use Get.put(...), Get.find(...), and reactive values where needed.

Bloc #

state_management: bloc

Presentation controllers use flutter_bloc with event/state classes, and pages use BlocProvider/BlocBuilder.

Provider #

state_management: provider

Presentation controllers extend ChangeNotifier, and pages use ChangeNotifierProvider/Consumer.

None #

state_management: none

Presentation files are generated without state management package wiring. This is useful when you want to connect another state system manually.

Network #

Dio #

network: dio

Generated remote services use Dio + Retrofit imports and annotations. The data package receives the relevant dependencies.

Abstract #

network: abstract

Use this when you want the generated repositories and source boundaries but plan to implement networking yourself.

Local Storage #

Secure Storage #

local_storage: secure_storage

Auth local source uses flutter_secure_storage for credential persistence.

Hive #

local_storage: hive

The data package uses the maintained Hive CE runtime and generator packages. Local sources can initialize their boxes directly. Injectable projects get a generated module that initializes Hive, registers each adapter with a stable type ID, opens each box, and provides the box to its local data source:

data/lib/data_module.dart

Adding another Hive feature or local/cached operation updates the same module with the additional adapter and box provider.

Generated local models keep an integer id for the Hive/ObjectBox database key and a String remoteId for the API identity. DTO id values map to Entity.remoteId and Box.remoteId, so remote identifiers never overwrite local database keys.

ObjectBox #

local_storage: objectbox

The data package gets ObjectBox dependencies, local sources can initialize their box from a Store, and injectable projects get a generated module:

data/lib/data_module.dart

Run build runner in the data package after adding ObjectBox entities so objectbox.g.dart can be generated.

Abstract #

local_storage: abstract

Auth local source contains TODO methods so you can wire a custom persistence mechanism yourself.

Dependency Compatibility #

Version 0.3.0 emits only the dependencies required by the selected configuration. Runtime packages use their current stable releases. Builder packages use the newest mutually compatible stable set verified against generated Flutter projects:

build_runner: ^2.15.1
freezed: ^3.2.5
injectable_generator: ^3.0.2
hive_ce_generator: 1.11.1

Some newer individual builder releases target incompatible analyzer versions. These constraints avoid prerelease transitive dependencies while allowing Freezed, Retrofit, Injectable, and Hive CE generation to run together.

Presentation Package #

The generated presentation package includes:

presentation/lib/main.dart
presentation/lib/widgets/
presentation/lib/pages/
presentation/lib/utils/
presentation/lib/controllers/
presentation/lib/constants/
presentation/assets/images/
presentation/assets/icons/

When use_asset_generator: true, it also includes:

presentation/asset_generator_kit.yaml

and adds assetgeneratorkit to presentation/pubspec.yaml.

Safety #

Every generation command builds a complete in-memory plan first. Names, configuration, required features, duplicate outputs, and filesystem conflicts are validated before the first file is written. If any conflict is found, the command aborts without partially generating the remaining files.

Identical files are left untouched, and rerunning a completed feature or operation is an idempotent no-op. Operation commands require the feature to already exist:

clean_architect create feature orders
clean_architect create cached-function syncCatalog --feature orders

By default, conflicting existing files are not overwritten.

Preview generated files:

clean_architect create auth --dry-run

Overwrite intentionally:

clean_architect create auth --overwrite

or:

clean_architect create auth --force

Doctor #

clean_architect doctor

doctor validates the complete generated project:

  • Every configured package root and layer path exists and is structurally valid.
  • Each layer has a readable pubspec.yaml whose package name matches its root.
  • Required dependencies and dev dependencies are present with compatible version constraints.
  • Local path dependencies resolve to the configured domain, data, and DI packages.
  • The active Dart and Flutter versions satisfy the generated package constraints.
  • build_runner is declared and resolved in every package that needs it.
  • Every referenced generated .g.dart file exists.

A healthy project exits with code 0. Failed validation exits with code 1, so clean_architect doctor can be used directly in CI.

Integration Test Matrix #

The integration suite generates complete projects in temporary directories, then runs dependency resolution, code generation, analysis in every layer, and the project doctor.

Scenario Coverage
default_getx_manual_dio_secure Layered packages, GetX, manual DI, Dio, secure storage
bloc_injectable_hive_feature_first Feature first, Bloc, Injectable, Hive CE
provider_injectable_objectbox Layered packages, Provider, Injectable, ObjectBox
none_abstract_plain_feature_first Feature first, no state package, abstract sources, plain Dart models
either_enabled Layered packages with Either<Failure, T> returns

Normal dart test runs skip these expensive cases. Run the full matrix with:

CLEAN_ARCHITECT_INTEGRATION=all dart test test/integration/generated_project_matrix_test.dart

Run one scenario while developing:

CLEAN_ARCHITECT_INTEGRATION=provider_injectable_objectbox dart test test/integration/generated_project_matrix_test.dart

Keep generated projects after success for manual inspection:

CLEAN_ARCHITECT_INTEGRATION=all CLEAN_ARCHITECT_KEEP_INTEGRATION_PROJECTS=true dart test test/integration/generated_project_matrix_test.dart

Every scenario creates the architecture, auth, an orders feature, and remote, local, and cached operations, then requires clean_architect doctor to pass. GitHub Actions runs each scenario as a separate matrix job.

Publishing / Development Notes #

When working on this package locally:

dart format lib test bin
dart analyze
dart test
dart pub publish --dry-run

Run dart pub publish --dry-run before publishing to verify pub.dev metadata and package contents.

0
likes
0
points
1.06k
downloads

Publisher

verified publisherpinz.dev

Weekly Downloads

A configurable CLI generator for clean architecture Flutter and Dart feature modules.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

args, collection, mason_logger, meta, path, pub_semver, yaml

More

Packages that depend on clean_architect