flutter_simple_architecture 1.2.2
flutter_simple_architecture: ^1.2.2 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 #
Install from GitLab (Recommended)
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, andl10n. - Environment Ready: Includes
.fvmrcandanalysis_options.yaml. - Analysis Passing: Generates code that passes
flutter analyzeimmediately.
-
Generate a Page:
fsa generate page <name> [--type=smart|simple|static]Creates a routeable page in
lib/presentation/<name>/.- Smart (Default): Full
ViewBuilder+Notifier+Union State. For complex, async features. - Simple:
ViewBuilder+Notifier+Data Class. For synchronous features (forms, counters). - Static: Stateless
Scaffoldonly. For informational pages.
- Smart (Default): Full
-
Generate a Smart Component:
fsa generate smart <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
StatelessWidgetin thewidgets/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.
Smart Page (Default)
- Command:
fsa generate page <Name> - Structure:
ViewBuilder+Notifier+Union State(Initial/Loading/Success/Error). - When to use:
- You need to fetch data asynchronously (API, Database).
- You need to handle loading spinners, error states, or retry logic.
- The UI has complex distinct states (e.g., Empty vs. Content).
Simple Page
- Command:
fsa generate page <Name> --type=simple - Structure:
ViewBuilder+Notifier+Data Class(Simple State). - When to use:
- The state is synchronous and local (e.g., Form validation, Counter, Toggle settings).
- You do not need a loading spinner or error handling.
- The state is always valid (no "Initial" or "Error" state).
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:
StatelessWidgetin awidgets/folder. - Rule:
- Must receive ALL data via constructor arguments.
- Must communicate actions via
VoidCallbackorFunction(T)callbacks. - NEVER access a Provider or Notifier directly.
Smart Component
- Command:
fsa generate smart <path/Name> - Structure:
ViewBuilder+Notifier+Union State(nested folder). - Rule:
- Use ONLY when the component must manage its own independent lifecycle (e.g., a
UserAvatarthat fetches its own image by ID, independent of the page).
- Use ONLY when the component must manage its own independent lifecycle (e.g., a
3. State Management #
- Manual Riverpod Providers: We strictly use
NotifierProvider.autoDisposeandProvider.autoDisposewithout code generation. No exceptions. Use non-autoDispose only for global persistent services (e.g., Auth, Database) after careful consideration. - UI Union States: Every smart component MUST use a
freezedunion class (Initial,Loading,Success,Error) to represent its UI state. - View Isolation Rule (CRITICAL):
view.dartfiles (the UI layer) ARE FORBIDDEN from usingref.watch(),ref.read(), orref.listen()of any provider. All data dependencies MUST be managed by theUIStateNotifierand exposed via theUIState. - Notifier Reactivity:
UIStateNotifieris the ONLY designated place to useref.watch()to react to changes in domain providers or repositories. - Strict Privacy:
UIStateNotifierproviders (e.g.,homeUIStateProvider) are private to their respective features and MUST ONLY be consumed by their correspondingview.dart. No other provider or component shall depend on a UI Notifier. - Clean View Rule:
view.dartfiles SHALL NOT contain private helper classes, private widgets, or methods that return widgets (e.g.,_buildItem). All sub-widgets MUST be extracted to awidgets/folder within the feature or moved tolib/core/widgetsif reusable.
4. Error Handling & Side Effects #
- Functional Programming: Powered by
fpdart. Repositories returnEither<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 emitSideEffectobjects (Success, Failure, Info, Warning), and theViewBuilderautomatically listens and displays the appropriateAppDialog.
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 ViewBuilder 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/usingThemeExtension.
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 Smart Components.
Getting Started #
- Ensure FVM is installed.
- Run
fvm flutter pub get. - Generate boilerplate:
fvm flutter pub run build_runner build --delete-conflicting-outputs. - Run the app:
fvm flutter run.