
Flutter Architecture
Structure Flutter apps with Command objects, ViewModels, and Result types so async UI actions stay single-flight and testable.
Overview
flutter-architecture is an agent skill for the Build phase that guides Flutter MVVM-style Command objects tied to Result types so async UI work runs once and reports clear success or failure.
Install
npx skills add https://github.com/madteacher/mad-agents-skills --skill flutter-architectureWhat is this skill?
- Documents Command0/Command1 wrappers that block double-taps and parallel launches until an action finishes
- Requires actions to return a typed Result<T> for explicit Ok/Error handling in the UI
- Exposes running, completed, and error flags via ChangeNotifier for reactive widgets
- Includes clearResult() so consumers acknowledge outcomes before the next run
- Template-oriented: copy Command base beside your app’s Result type and adapt imports
Adoption & trust: 1.5k installs on skills.sh; 101 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You keep getting duplicate API calls, stuck loading spinners, or unclear error handling because async actions are wired ad hoc in Flutter widgets.
Who is it for?
Indie Flutter apps where one developer owns UI and state and wants a copy-paste Command base aligned with Result-driven error handling.
Skip if: Teams that already standardize on Bloc/Riverpod-only patterns with no Command layer, or greenfield projects not using Flutter/Dart.
When should I use this skill?
When implementing or refactoring Flutter feature modules that need guarded async actions and Result-based outcomes on the UI thread.
What do I get? / Deliverables
After applying the skill, features expose Command-backed ViewModel actions with running/completed/error state and a single-flight guarantee listeners can bind to safely.
- Command base class usage aligned to your Result type
- ViewModel methods wrapped as Command0/Command1
Recommended Skills
Journey fit
Canonical shelf is Build because the skill encodes in-app architecture patterns you apply while implementing Flutter features, not while validating or shipping alone. Frontend subphase fits MVVM-style Commands and ViewModel wiring that directly shapes screens and state flow in the Dart UI layer.
How it compares
Architecture convention skill for Flutter state/actions—not a widget catalog, code generator, or backend API integration pack.
Common Questions / FAQ
Who is flutter-architecture for?
Solo and indie builders using Flutter who want ViewModel-friendly async commands without reinventing loading and error plumbing on every screen.
When should I use flutter-architecture?
During Build (frontend) when scaffolding features, refactoring messy onPressed handlers, or aligning agent output with Result-based domain types in a mobile codebase.
Is flutter-architecture safe to install?
Review the Security Audits panel on this Prism page for the upstream repo; the skill describes Dart patterns and does not inherently require network or shell access beyond your normal dev environment.
SKILL.md
READMESKILL.md - Flutter Architecture
import 'dart:async'; import 'package:flutter/foundation.dart'; import 'result.dart'; // Template asset: copy this beside the project's Result type or update this // import path to match the app's package structure. typedef CommandAction0<T> = Future<Result<T>> Function(); typedef CommandAction1<T, A> = Future<Result<T>> Function(A); /// Facilitates interaction with a ViewModel. /// /// Encapsulates an action, /// exposes its running and error states, /// and ensures that it can't be launched again until it finishes. /// /// Use [Command0] for actions without arguments. /// Use [Command1] for actions with one argument. /// /// Actions must return a [Result]. /// /// Consume the action result by listening to changes, /// then call to [clearResult] when the state is consumed. abstract class Command<T> extends ChangeNotifier { Command(); bool _running = false; /// True when the action is running. bool get running => _running; Result<T>? _result; /// true if action completed with error bool get error => _result is Error; /// true if action completed successfully bool get completed => _result is Ok; /// Get last action result Result<T>? get result => _result; /// Clear last action result void clearResult() { _result = null; notifyListeners(); } /// Internal execute implementation Future<void> _execute(CommandAction0<T> action) async { // Ensure the action can't launch multiple times. // e.g. avoid multiple taps on button if (_running) return; // Notify listeners. // e.g. button shows loading state _running = true; _result = null; notifyListeners(); try { _result = await action(); } finally { _running = false; notifyListeners(); } } } /// [Command] without arguments. /// Takes a [CommandAction0] as action. class Command0<T> extends Command<T> { Command0(this._action); final CommandAction0<T> _action; /// Executes the action. Future<void> execute() async { await _execute(_action); } } /// [Command] with one argument. /// Takes a [CommandAction1] as action. class Command1<T, A> extends Command<T> { Command1(this._action); final CommandAction1<T, A> _action; /// Executes the action with the argument. Future<void> execute(A argument) async { await _execute(() => _action(argument)); } } # Flutter Architecture Examples This directory contains example code demonstrating the Flutter architecture patterns. ## Using Command Pattern The Command pattern encapsulates actions with state management and Result handling. ### Basic Usage After copying `command.dart` and `result.dart` into your project, update these imports to match their final package path. ```dart import 'package:flutter/foundation.dart'; import 'command.dart'; import 'result.dart'; class TodoViewModel extends ChangeNotifier { Command0<void> get loadTodos => _loadTodosCommand; late final _loadTodosCommand = Command0<void>(_loadTodos); Future<Result<void>> _loadTodos() async { // Load todos from repository return Result.ok(null); } } ``` ### In Your Widget ```dart ListenableBuilder( listenable: viewModel.loadTodos, builder: (context, child) { if (viewModel.loadTodos.running) { return CircularProgressIndicator(); } return ElevatedButton( onPressed: viewModel.loadTodos.execute, child: Text('Load Todos'), ); }, ); ``` ## Using Result Type The Result type provides type-safe error handling. ```dart Future<Result<User>> fetchUser(String id) async { try { final user = await repository.getUser(id); return Result.ok(user); } catch (e) { return Result.error(Exception('Failed: $e')); } } // Handle result final result = await fetchUser('123'); switch (result) { case Ok(): print('User: ${result.value.name}'); case Error(): print('Error: ${result.error}'); } ``` ## Repository Pattern Example ```dart class TodoRepository { final ApiService _api;