skills/flutter-architecture/assets/examples
2026-07-13 14:05:05 -04:00
..
README.md Central skills catalog: 472 skills + WORKING_SET + AGENTS 2026-07-13 14:05:05 -04:00

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.

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

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.

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

class TodoRepository {
  final ApiService _api;
  final DatabaseService _database;

  Stream<List<Todo>> get todos => _database.todosStream;

  TodoRepository(this._api, this._database);

  Future<Result<Todo>> addTodo(Todo todo) async {
    try {
      final savedTodo = await _api.createTodo(todo);
      await _database.saveTodo(savedTodo);
      return Result.ok(savedTodo);
    } catch (e) {
      return Result.error(e is Exception ? e : Exception(e.toString()));
    }
  }
}

Optimistic UI Example

Update UI immediately, then sync. The repository returns the server-confirmed Todo so the temporary item can be replaced safely:

Future<Result<Todo>> addTodo(Todo todo) async {
  // Optimistic update
  _todos = [..._todos, todo];
  notifyListeners();

  // Actual API call
  final result = await repository.addTodo(todo);

  if (result case Error()) {
    // Rollback on error
    _todos = _todos.where((t) => t.id != todo.id).toList();
    notifyListeners();
    return result;
  }

  final serverTodo = result.asOk.value;
  _todos = _todos.map((t) => t.id == todo.id ? serverTodo : t).toList();
  notifyListeners();

  return Result.ok(serverTodo);
}

Dependency Injection

Provide dependencies via constructor:

class TodoViewModel extends ChangeNotifier {
  final TodoRepository _repository;

  TodoViewModel(this._repository);
}

// In main.dart
final apiService = ApiService();
final databaseService = DatabaseService();
final repository = TodoRepository(apiService, databaseService);
final viewModel = TodoViewModel(repository);