🧱 moarch

A Flutter CLI tool to scaffold Clean Architecture projects with Riverpod. Your conventions, your structure.

pub version license: MIT


Features

  • ⚑ One command setup β€” moarch init scaffolds your full lib/ structure with routing, theme, security, and shared widgets
  • 🎯 Interactive checklist β€” moarch init prompts you to select your stack (Dio/Firebase) and features (Router, CI, Tests, Media, URL launcher)
  • 🎯 Layered feature generation β€” moarch create feature <n> generates Clean Architecture with an interactive checklist
  • ✨ Minimal code generation β€” build_runner used only for secure .env handling; no freezed or riverpod_annotation boilerplate
  • πŸ§ͺ Test scaffolding β€” moarch create feature auto-generates notifier/usecase tests in test/features/<feature>
  • πŸ—οΈ Your conventions β€” Fully customizable templates, pre-configured with proven patterns
  • πŸ”’ Security included β€” Secure storage integration with flutter_secure_storage
  • πŸ“ Router ready β€” GoRouter setup out of the box
  • 🎨 Reusable widgets β€” Pre-built buttons, inputs, loading, action overlay, error view, and design system components
  • πŸ§ͺ CI ready β€” moarch init scaffolds a GitHub Actions CI workflow with unit and integration jobs
  • πŸ” Flaky integration support β€” generated CI integration step is configured to continue on error so merges are not blocked by intermittent network/API issues
  • πŸ“¦ Environment-aware β€” .env and .fvmrc generated at project root
  • πŸ“± Media Service β€” Image picker, file picker, and video picker with permissions handling
  • πŸ”— URL Launcher β€” Launch URLs, phone numbers, and WhatsApp links
  • πŸ“Ά Connectivity Service β€” Network connectivity monitoring with Riverpod streams
  • πŸ”₯ Firebase ready β€” Optional Firebase Auth and Firestore providers

Installation

dart pub global activate moarch

Make sure ~/.pub-cache/bin is in your PATH:

# macOS / Linux β€” add to .zshrc or .bashrc
export PATH="$PATH:$HOME/.pub-cache/bin"

# Windows β€” add %APPDATA%\Pub\Cache\bin to your PATH via Environment Variables

Quick start

# 1. create your Flutter project
flutter create my_app && cd my_app

# 2. add dependencies to pubspec.yaml (see below)
flutter pub get

# 3. remove the generated main.dart
rm lib/main.dart

# 4. scaffold the project structure (with interactive checklist)
moarch init

# 4b. or skip checklist and generate everything
moarch init --all

# 5. create your first feature
moarch create feature auth

# 6. optional: skip test scaffolding
moarch create feature auth --no-unit --no-integration

Required dependencies

dependencies:
    flutter:
        sdk: flutter
    flutter_riverpod:
    envied:
    dio:
    dio_smart_retry:
    go_router:
    flutter_secure_storage:
    logger:
    mocktail:
    # Optional (selected via moarch init checklist):
    # firebase_core:
    # firebase_auth:
    # cloud_firestore:
    # image_picker:
    # file_picker:
    # permission_handler:
    # url_launcher:
    # connectivity_plus:

Dev dependencies (for build_runner + envied_generator)

dev_dependencies:
    lints:
    test:
    build_runner:
    envied_generator:

Envied support (added by moarch init)

When you run moarch init, it scaffolds:

  • lib/config/env/app_env.dart with @Envied(... obfuscate: true)
  • .env entries: BASE_URL= (auto-generated)
  • .gitignore entry .env

In your app, execute codegen:

fvm flutter pub add envied
fvm flutter pub add --dev build_runner envied_generator
dart run build_runner build --delete-conflicting-outputs

Then use AppEnv values safely:

final baseUrl = AppEnv.baseUrl;

moarch init

Generates a complete, production-ready project structure and creates a GitHub Actions CI pipeline in .github/workflows/ci.yml.

.env                             ← BASE_URL= (generated by moarch init)
.fvmrc                           ← { "flutter": "stable" }
lib/
β”œβ”€β”€ main.dart                    ← App with routing & theme setup
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ constants/
β”‚   β”‚   β”œβ”€β”€ app_constants.dart   - spacing (4pt), text sizes, touch targets, radii, durations
β”‚   β”‚   └── api_constants.dart   - API timeout
β”‚   β”œβ”€β”€ errors/
β”‚   β”‚   └── app_exception.dart   - unified error handling
β”‚   β”œβ”€β”€ network/
β”‚   β”‚   └── dio_client.dart      - HTTP client with interceptors
β”‚   β”œβ”€β”€ security/
β”‚   β”‚   └── secure_storage.dart  - flutter_secure_storage wrapper
β”‚   └── utils/
β”‚       β”œβ”€β”€ extensions.dart      - ContextX, StringX, DateTimeX, TimeOfDayX
β”‚       └── logger.dart          - single log() function
β”œβ”€β”€ config/
β”‚   β”œβ”€β”€ env/
β”‚   β”‚   └── app_env.dart         - Envied wrapper for secure .env values
β”‚   β”œβ”€β”€ router/
β”‚   β”‚   └── app_router.dart      - GoRouter setup with routes
β”‚   └── theme/
β”‚       └── app_theme.dart       - Material 3 theme, light/dark modes
β”œβ”€β”€ shared/widgets/
β”‚   β”œβ”€β”€ buttons/
β”‚   β”‚   └── app_button.dart      - filled / outlined / text variants
β”‚   β”œβ”€β”€ inputs/
β”‚   β”‚   β”œβ”€β”€ app_input.dart       - themed input widget
β”‚   β”‚   β”œβ”€β”€ app_date_input.dart  - date picker input
β”‚   β”‚   β”œβ”€β”€ app_time_input.dart  - time picker input
β”‚   β”‚   └── app_dropdown_input.dart - dropdown input widget
β”‚   β”œβ”€β”€ loadings/
β”‚   β”‚   β”œβ”€β”€ app_loading_data.dart    - progress indicators for data loading
β”‚   β”‚   └── app_loading_action_overlay.dart  - indicators for actions (submit, delete)
β”‚   β”œβ”€β”€ error_view.dart          - error display component
β”‚   └── design_system_view.dart  - design system showcase
└── features/                    - your features go here
.github/
└── workflows/
    └── ci.yml                  - GitHub Actions CI scaffold
CHECKLIST.md                    - development checklist

Note on tests

  • moarch create feature generates tests in test/features/<feature> for selected layers:
    • ${feature}_notifier_test.dart
    • ${feature}_repository_test.dart (disable)
    • ${feature}_usecase_test.dart

Optional test folder for manual setup

test/
└── features/
    └── <feature>/
        β”œβ”€β”€ <feature>_notifier_test.dart
        β”œβ”€β”€ <feature>_repository_test.dart (disable)
        └── <feature>_usecase_test.dart

What you get:

  • βœ… Routing configured with GoRouter
  • βœ… Secure storage integration ready
  • βœ… DIO client with error handling (or Firebase Firestore/Auth)
  • βœ… Theme system with Material 3 support
  • βœ… Reusable widgets library (buttons, inputs, loading states, error view, design system)
  • βœ… Environment variables (.env) support
  • βœ… Envied config scaffolding (lib/config/env/app_env.dart, .env + .gitignore entry)
  • βœ… Test scaffolding in test/features/<feature> (notifier/repository/usecase as selected)
  • βœ… Extension methods for common operations (ContextX, StringX, DateTimeX, TimeOfDayX + toColor, isYesterday, timeAgo)
  • βœ… Optional: Media Service (image/file/video picker)
  • βœ… Optional: URL Launcher (URLs, phone, WhatsApp)
  • βœ… Optional: Connectivity Service (network monitoring)

moarch create feature

Generates a complete feature with Clean Architecture layers and an interactive checklist.

moarch create feature auth
moarch create feature user_profile
moarch create feature ProductCatalog    # casing doesn't matter
moarch create feature auth --all        # skip checklist, generate all layers
moarch create feature auth --no-unit --no-integration   # skip test generation

Interactive Checklist

The CLI presents a checklist to select which layers to generate:

  Select layers for "Auth" (type number, enter = confirm):
β–Ά 1 [βœ“]  Remote Datasource
  2 [ ]  Local/Cache Datasource        ← optional, default: off
  3 [βœ“]  Repository (interface + impl)
  4 [ ]  Use Cases                     ← optional, default: off
  5 [βœ“]  State + Notifier
  6 [βœ“]  View

This lets you generate only what you need β€” skip local datasources if your feature is API-only, or skip use cases if your logic fits in the notifier.

moarch create feature also prompts to generate tests by default. Use --no-unit --no-integration to skip test scaffolding.

Generated Structure

lib/features/auth/
β”œβ”€β”€ domain/
β”‚   β”œβ”€β”€ entities/
β”‚   β”‚   └── auth_entity.dart
β”‚   β”œβ”€β”€ repositories/
β”‚   β”‚   └── auth_repository.dart     ← interface
β”‚   └── usecases/
β”‚       └── get_auth.dart            ← if selected
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ datasources/
β”‚   β”‚   β”œβ”€β”€ auth_remote_datasource.dart
β”‚   β”‚   └── auth_local_datasource.dart  ← if selected
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   └── auth_model.dart          - copyWith, fromJson, toJson
β”‚   └── repositories/
β”‚       └── auth_repository_impl.dart
└── presentation/
    β”œβ”€β”€ states/
    β”‚   └── auth_state.dart
    β”œβ”€β”€ notifiers/
    β”‚   └── auth_notifier.dart       ← StateNotifier with error handling
    β”œβ”€β”€ views/
    β”‚   └── auth_view.dart

Generated Test Files

moarch create feature now also generates tests in test/features/<feature>:

  • ${feature}_notifier_test.dart β€” when State + Notifier and Repository are selected
  • ${feature}_repository_test.dart β€” when Repository is selected (disable)
  • ${feature}_usecase_test.dart β€” when Use Cases is selected

State Management Pattern

Your state uses a simple, flexible model with copyWith:

class AuthState {
  const AuthState({
    this.isLoadingAction = false,
    this.error,
    this.success,
  });

  final bool isLoadingAction;
  final String? error;
  final String? success;

  AuthState copyWith({
    bool? isLoadingAction,
    String? error,
    String? success,
  }) {
    return AuthState(
      isLoadingAction: isLoadingAction ?? false,
      error: error,
      success: success,
    );
  }
}

Error handling in views uses state.value?.error β€” your AppException message from the repository, not the AsyncValue error:

ref.listen(authNotifierProvider, (_, next) {
  final value = next.value;
  if (value?.error != null) {
    // show snackbar with value.error
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(value!.error!)),
    );
  }
});

Tips

  • Remote datasource only? Unselect "Local/Cache Datasource" in the checklist
  • No use cases? Skip them if your feature is simple β€” the repository covers most cases
  • Reuse widgets? Put shared feature widgets in shared/widgets/, not in the feature folder
  • Models with JSON? The generated model includes fromJson() and toJson()

Customizing moarch

moarch templates are generated from production-ready code that matches default Flutter best practices. If you want to customize what gets generated, clone the repository and modify the templates:

Template files

File Generates
core_templates.dart main.dart, dio_client, secure_storage, constants, errors, utils, extensions, logger
config_templates.dart app_theme.dart, app_router.dart
shared_templates.dart app_button, app_input, app_loading_action, app_loading_data, error_view
feature_templates.dart entity, model, datasources, repository, state, notifier, view
test_templates.dart integration and notifier/use case tests
checklist_templates.dart dev checklist
ci_templates.dart ci workflow

Steps to customize

  1. Clone the repository

    git clone https://github.com/SuperMoooo/moarch.git
    cd moarch
    
  2. Edit templates in lib/src/templates/

    • Each method returns a string of Dart code
    • Your changes will be inserted as-is into generated files
  3. Re-activate locally

    dart pub global activate --source path ./
    
  4. Test your changes

    moarch init
    moarch create feature test_feature
    

Pro Tips

  • Keep method signatures consistent β€” users expect certain class names and patterns
  • Use triple-quoted strings (r'''...''') to avoid escaping special characters
  • Test across different feature names (snake_case, PascalCase, UPPER_CASE)
  • If you change core templates, test moarch init first before features
  • Pull requests for improvements are welcome!

Common use cases

Starting fresh

# Quick start with all layers
moarch create feature user --all

API-only feature

# Skip local datasource and use cases, generate only remote
moarch create feature products
# Then unselect "Local/Cache Datasource" and "Use Cases" in the checklist

Feature with offline support

# Select "Local/Cache Datasource" in the checklist
moarch create feature downloads

Add routing to your features

Your app_router.dart is ready for GoRouter routes. Add them under a new screen route:

GoRoute(
  path: '/auth',
  builder: (context, state) => const AuthView(),
),

Troubleshooting

Command not found: moarch

  • Check that ~/.pub-cache/bin (or %APPDATA%\Pub\Cache\bin on Windows) is in your PATH
  • Try: dart pub global activate moarch again

Feature already exists

  • moarch won't overwrite existing features β€” delete or rename the folder first

Wrong package imports after init

  • All generated files use relative imports for core/config/shared β€” verify your lib structure matches

Customization not working

  • After editing templates, run: dart pub global activate --source path ./
  • Make sure you've saved the file and are using the updated version

License

MIT Β© AndrΓ© Montoito

Libraries

moarch