flutter_simple_architecture 2.0.0 copy "flutter_simple_architecture: ^2.0.0" to clipboard
flutter_simple_architecture: ^2.0.0 copied to clipboard

A robust Flutter bootstrapping tool and project template emphasizing a clean, layer-first architecture with strict state management.

FSA (Flutter Simple Architecture) #

A robust Flutter bootstrapping tool and project template emphasizing a clean, layer-first architecture with strict state management and a centralized design system.

FSA CLI #

The project includes a CLI tool (fsa) to automate project creation and component scaffolding.

Installation #

To install fsa globally directly from the repository:

fvm dart pub global activate --source git https://gitlab.com/godoytristanh/fsa.git

Install from Local Source

If you have cloned the repository locally:

fvm dart pub global activate --source path .

Note: Make sure your Dart SDK's bin directory is in your system's PATH (usually ~/.pub-cache/bin).

Local Usage

If you prefer not to install it globally, you can run it directly from the root:

fvm dart bin/fsa.dart <command>

Commands #

  • Create a new project:

    fsa create <project_name> <org_name>
    

    Bootstraps a new Flutter project with the FSA architecture.

    • Clean Template: Removes default counter app boilerplate.
    • Automated Config: Pre-configures hooks_riverpod, freezed, and l10n.
    • Environment Ready: Includes .fvmrc and analysis_options.yaml.
    • Analysis Passing: Generates code that passes flutter analyze immediately.
  • Generate a Page:

    fsa generate page <name> [--type=async|sync|static]
    

    Creates a routeable page in lib/presentation/<name>/.

    • Async (Default): Full AppViewBuilder + Notifier + Union State. For complex, async features.
    • Sync: AppViewBuilder + Notifier + Data Class. For synchronous features (forms, counters).
    • Static: Stateless Scaffold only. For informational pages.
  • Generate an Async Component:

    fsa generate async <path/to/name>
    

    Creates a complex component with its own independent state management. Useful for logic-heavy widgets nested within pages.

  • Generate a Dumb Component:

    fsa generate dumb <path/to/name>
    

    Creates a single-file StatelessWidget in the widgets/ subfolder of the specified path.


Architectural Principles #

1. Page Types (Screens) #

The CLI supports three strict page archetypes. Choose the one that matches your feature's complexity.

CRITICAL RULE: Async views (and AppViewBuilder in general) MUST ONLY be used if the view performs an initial asynchronous task or calculation (e.g., fetching data from an API, loading from a database, or running a heavy computation on initialization). If a view does not require an initial async load, use Sync (for local logic) or Static (for no logic).

Async Page (Default)

  • Command: fsa generate page <Name>
  • Structure: AppViewBuilder + Notifier + Union State (Initial/Loading/Success/Error).
  • When to use:
    • EXCLUSIVELY when the view performs an initial async calculation or data fetch.
    • You need to handle loading spinners, error states, or retry logic for that initial task.
    • The UI has complex distinct states derived from an async operation.

Sync Page

  • Command: fsa generate page <Name> --type=sync
  • Structure: AppViewBuilder + Notifier + Data Class (Sync State).
  • When to use:
    • The view has logic/interactions but DOES NOT perform an initial async load.
    • State management is synchronous and local (e.g., Form validation, Counter, Toggle settings).
    • You do not need a loading spinner or terminal error handling.

Static Page

  • Command: fsa generate page <Name> --type=static
  • Structure: StatelessWidget (Scaffold only).
  • When to use:
    • Purely informational pages (About Us, Terms of Service).
    • Static navigation menus.
    • NO logic, NO state, NO providers (except Theme/L10n).

2. Component Types (Widgets) #

Dumb Component (Preferred)

  • Command: fsa generate dumb <path/Name>
  • Structure: StatelessWidget in a widgets/ folder.
  • Rule:
    • Must receive ALL data via constructor arguments.
    • Must communicate actions via VoidCallback or Function(T) callbacks.
    • NEVER access a Provider or Notifier directly.

Async Component

  • Command: fsa generate async <path/Name>
  • Structure: AppViewBuilder + Notifier + Union State (nested folder).
  • Rule:
    • Use ONLY when the component must manage its own independent lifecycle (e.g., a UserAvatar that fetches its own image by ID, independent of the page).

3. State Management #

  • Manual Riverpod Providers: We strictly use NotifierProvider.autoDispose and Provider.autoDispose without code generation. No exceptions. Use non-autoDispose only for global persistent services (e.g., Auth, Database) after careful consideration.
  • UI Union States: Every async component MUST use a freezed union class (Initial, Loading, Success, Error) to represent its UI state.
  • View Isolation Rule (CRITICAL): view.dart files (the UI layer) ARE FORBIDDEN from using ref.watch(), ref.read(), or ref.listen() of any provider. All data dependencies MUST be managed by the UIStateNotifier and exposed via the UIState.
  • Notifier Reactivity: UIStateNotifier is the ONLY designated place to use ref.watch() to react to changes in domain providers or repositories.
  • Strict Privacy: UIStateNotifier providers (e.g., homeUIStateProvider) are private to their respective features and MUST ONLY be consumed by their corresponding view.dart. No other provider or component shall depend on a UI Notifier.
  • Clean View Rule: view.dart files SHALL NOT contain private helper classes, private widgets, or methods that return widgets (e.g., _buildItem). All sub-widgets MUST be extracted to a widgets/ folder within the feature or moved to lib/core/widgets if reusable.

4. Error Handling & Side Effects #

  • Functional Programming: Powered by fpdart. Repositories return Either<Failure, T>.
  • The Guard Wrapper: Asynchronous operations are wrapped in a guard() function.
  • SideEffect Notifier: Non-interactive events (dialogs, alerts) are managed via a global sideEffectProvider. Notifiers emit SideEffect objects (Success, Failure, Info, Warning), and the AppViewBuilder automatically listens and displays the appropriate AppDialog.

Riverpod 3 Implementation Guide #

To ensure consistency and avoid common mistakes, follow these strict rules for Riverpod 3 within FSA:

1. Manual Providers (NO CODE GEN) #

We do not use @riverpod or build_runner for providers. Every provider must be explicitly defined using the NotifierProvider or Provider factories.

2. The Notifier Pattern (UI Layer) #

UI State is managed by a Notifier<TUIState>.

// lib/presentation/feature/ui_state_notifier.dart

class FeatureUIStateNotifier extends Notifier<FeatureUIState> {
  @override
  FeatureUIState build() {
    // 1. Reactive dependencies go here (watch other providers)
    final domainData = ref.watch(domainDataProvider);
    
    // 2. Initial state logic
    if (domainData.isEmpty) return const FeatureUIState.initial();
    return FeatureUIState.success(data: domainData);
  }

  // 3. Actions use ref.read() for non-reactive dependencies (repositories/services)
  Future<void> performAction() async {
    state = const FeatureUIState.loading(); // Terminal state change
    
    final repository = ref.read(repositoryProvider);
    final result = await repository.fetchData();

    result.fold(
      (failure) => state = FeatureUIState.error(failure.message),
      (data) => state = FeatureUIState.success(data: data),
    );
  }

  // 4. Side effects (Snackbars/Dialogs) use sideEffectProvider
  void showNotification() {
    ref.read(sideEffectProvider.notifier).notify(
      const SideEffect.info(title: 'Update', message: 'Action triggered!')
    );
  }
}

// ALWAYS use .autoDispose for UI-bound providers
final featureUIStateProvider = 
    NotifierProvider.autoDispose<FeatureUIStateNotifier, FeatureUIState>(() {
  return FeatureUIStateNotifier();
});

3. AsyncNotifier vs Notifier #

  • Prefer Notifier combined with a Union UIState (Freezed) for most UI screens. This gives you explicit control over Initial, Loading, Success, and Error states.
  • Use AsyncNotifier only for simple data fetching where the standard AsyncValue (loading/error/data) is sufficient without custom UI transitions.

4. Ref Usage Summary #

Location Method Why
build() ref.watch() To react to changes in domain/service providers and rebuild the state.
Methods ref.read() To access repositories or "trigger" actions on other notifiers without creating a subscription.
UI (view.dart) FORBIDDEN The UI must only interact with state and notifier provided by AppViewBuilder.

SideEffect Pattern #

To trigger a dialog without passing a BuildContext or using callbacks:

// In your Notifier
void someAction() {
  ref.read(sideEffectProvider.notifier).notify(
    SideEffect.success(title: 'Done', message: 'Action completed!')
  );
}

The AppViewBuilder handles this via the onSideEffect hook. By default, it shows an AppDialog, but you can override it in your Page to use SnackBars or other UI:

@override
void onSideEffect(BuildContext context, SideEffect effect) {
  // Custom implementation: use a SnackBar instead of a Dialog
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(content: Text(effect.message)),
  );
}

5. Design System #

  • Sealed Widgets: Base components (AppButton, AppText, AppDialog) use sealed classes and factory constructors to enforce semantic consistency.
  • Centralized Theme: All styling is managed in lib/core/theme/ using ThemeExtension.

Project Structure #

  • bin/: CLI entry point.
  • lib/core/: Global logic, services (Logger, ErrorHandler), and Design System (Sealed Widgets).
  • lib/domain/: Pure business logic, entities, and repository definitions.
  • lib/presentation/: UI layers, divided into Pages and nested Async Components.

Getting Started #

  1. Ensure FVM is installed.
  2. Run fvm flutter pub get.
  3. Generate boilerplate: fvm flutter pub run build_runner build --delete-conflicting-outputs.
  4. Run the app: fvm flutter run.
0
likes
0
points
658
downloads

Publisher

unverified uploader

Weekly Downloads

A robust Flutter bootstrapping tool and project template emphasizing a clean, layer-first architecture with strict state management.

Homepage
Repository (GitLab)
View/report issues

License

unknown (license)

Dependencies

args, cupertino_icons, dio, flutter, flutter_hooks, flutter_localizations, fpdart, freezed_annotation, google_fonts, hooks_riverpod, intl, json_annotation, logger, path

More

Packages that depend on flutter_simple_architecture