Skip to content
FunCoding

Search

Search docs, Skills and MCP

flutter-app-architecture

Use when scaffolding a project, refactoring into layers, creating view models/repositories, configuring dependency injection, or implementing unidirectional data flow (MVVM).

代码质量与审查650skills/flutter-app-architecture/SKILL.md

Install

Send this to Claude Code, Codex or Cursor. The agent checks the Skill for safety first and installs it only after you confirm.

读取 https://funcoding.ai/skills/evanca/flutter-ai-rules/flutter-app-architecture/install.md ,按里面的步骤帮我安装这个 Skill。

SKILL.md

Flutter App Architecture Skill

This skill defines how to structure Flutter applications using layered architecture, proper data flow, and MVVM patterns for maintainability and testability.

When to Use

Use this skill when:

  • Scaffolding a new Flutter project with layered architecture.
  • Creating or refactoring View Models, Repositories, or Services.
  • Wiring dependency injection between architectural components.
  • Implementing unidirectional data flow across layers.
  • Adding a Domain (Logic) Layer for complex business logic or shared use cases.

1. Layer Structure

Separate every app into a UI Layer and a Data Layer. Add a Logic (Domain) Layer only for complex apps.

┌──────────────────────────────────────────────────────────────┐
│   UI Layer    │  Views + ViewModels                           │
├──────────────────────────────────────────────────────────────┤
│  Logic Layer  │  Use Cases / Interactors  (optional)         │
├──────────────────────────────────────────────────────────────┤
│   Data Layer  │  Repositories + Services                     │
└──────────────────────────────────────────────────────────────┘

Rules:

  • Only adjacent layers may communicate. The UI layer must never access a Service directly.
  • Data changes always happen in the Data layer (SSOT = Repository). No mutation in UI or Logic layers.
  • Follow unidirectional data flow: state flows down (Data → UI), events flow up (UI → Data).

2. Component Responsibilities

View

  • Describes how to present data; keep logic minimal and UI-related only.
  • Passes events to the ViewModel in response to user interactions.

ViewModel

  • Converts app data into UI state and maintains the current state needed by the View.
  • Exposes callbacks (commands) to the View and retrieves/transforms data from Repositories.
class BookingViewModel extends ChangeNotifier {
  final BookingRepository _repo;

  BookingViewModel(this._repo);

  List<Booking> _bookings = [];
  List<Booking> get bookings => List.unmodifiable(_bookings);

  bool _isLoading = false;
  bool get isLoading => _isLoading;

  Future<void> loadBookings() async {
    _isLoading = true;
    notifyListeners();

    _bookings = await _repo.getBookings();
    _isLoading = false;
    notifyListeners();
  }

  Future<void> cancelBooking(String id) async {
    await _repo.cancelBooking(id);
    _bookings = await _repo.getBookings();
    notifyListeners();
  }
}

Repository (Single Source of Truth)

  • The only class that may mutate its data; all other classes read from it.
  • Handles caching, error handling, and data refresh logic.
  • Transforms raw data from Services into domain models.
class BookingRepository {
  final BookingApiService _apiService;
  final BookingLocalService _localService;

  BookingRepository(this._apiService, this._localService);

  Future<List<Booking>> getBookings() async {
    try {
      final remote = await _apiService.fetchBookings();
      await _localService.cacheBookings(remote);
      return remote;
    } catch (_) {
      return _localService.getCachedBookings();
    }
  }

  Future<void> cancelBooking(String id) async {
    await _apiService.cancelBooking(id);
    await _localService.removeCachedBooking(id);
  }
}

Service

  • Wraps API endpoints and exposes asynchronous response objects.
  • Isolates data-loading and holds no state.
class BookingApiService {
  final http.Client _client;
  BookingApiService(this._client);

  Future<List<Booking>> fetchBookings() async {
    final response = await _client.get(Uri.parse('/api/bookings'));
    if (response.statusCode != 200) {
      throw HttpException('Failed to load bookings');
    }
    final data = jsonDecode(response.body) as List;
    return data.map((json) => Booking.fromJson(json)).toList();
  }
}

3. Dependency Injection

Supply dependencies via constructors. Define abstract interfaces so implementations can be swapped for testing.

// Abstract interface for the repository
abstract class BookingRepository {
  Future<List<Booking>> getBookings();
  Future<void> cancelBooking(String id);
}

// Concrete implementation
class BookingRepositoryImpl implements BookingRepository {
  final BookingApiService _api;
  BookingRepositoryImpl(this._api);

  @override
  Future<List<Booking>> getBookings() => _api.fetchBookings();

  @override
  Future<void> cancelBooking(String id) => _api.cancelBooking(id);
}

4. Use Cases (Domain Layer)

Introduce use cases only when:

  • Logic is complex or does not fit cleanly in the UI or Data layers.
  • Logic is reused across multiple ViewModels or merges data from multiple Repositories.
class GetUpcomingBookingsUseCase {
  final BookingRepository _bookingRepo;
  final UserRepository _userRepo;

  GetUpcomingBookingsUseCase(this._bookingRepo, this._userRepo);

  Future<List<Booking>> call() async {
    final user = await _userRepo.getCurrentUser();
    final bookings = await _bookingRepo.getBookings();
    return bookings
        .where((b) => b.userId == user.id && b.date.isAfter(DateTime.now()))
        .toList();
  }
}

5. Workflow: Scaffold a New Feature

  1. Create the Service — implement the API wrapper with typed response parsing.
  2. Create the Repository — inject the Service, implement caching and error-handling logic.
  3. Create the ViewModel — inject the Repository, expose UI state and commands.
  4. Create the View — bind to the ViewModel, render state, dispatch events.
  5. Wire DI — register all components in the dependency injection container.
  6. Verify — confirm the View never accesses the Service directly and data flows unidirectionally.

6. Data Storage

  • Use key-value storage (e.g., shared_preferences) for configuration and preferences.
  • Use SQL storage (e.g., drift, sqflite) for complex relational data.
  • Implement optimistic updates to improve perceived responsiveness by updating UI before server confirms.
  • Support offline-first by combining local and remote data sources in Repositories.

7. Coding Conventions

  • Use StatelessWidget when possible; avoid unnecessary StatefulWidgets.
  • Keep build methods simple and focused on rendering.
  • Prefer final for fields and top-level variables. Prefer const constructors when the class supports it.
  • Prefer explicit typing on public APIs (e.g., Command0<void> over dynamic signatures).
  • Use descriptive constant names (e.g., _todoTableName over _kTableTodo).

References

Similar Skills

claude-api
anthropics/skills180k

claude-api

Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration. TRIGGER — read BEFORE opening the target file; don't skip because it "looks like a one-liner" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens). SKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).

Code quality & review

ponytail-review
DietrichGebert/ponytail158k

ponytail-review

Quality review of a change: is the logic right, is it safe, does it hold under real load, is risky code tested, is it fast enough, and is every line needed. Reads the connected code, not only the diff. Each finding is explained in plain English. Use for "review this", "code review", "review the last commit", "review my PR", "is this over-engineered", /ponytail-review.

Code quality & review

code-review-and-quality
addyosmani/agent-skills103k

code-review-and-quality

Conducts multi-axis code review. Use before merging any change. Use when reviewing code written by yourself, another agent, or a human. Use when you need to assess code quality across multiple dimensions before it enters the main branch. Use when asked to review a diff or a pull request, even when the diff is pasted inline.

Code quality & review

documentation-and-adrs
addyosmani/agent-skills103k

documentation-and-adrs

Records decisions and documentation. Use when you need to document an architecture decision (ADR) or the reasoning behind a design choice, when changing public APIs, shipping features, or when you need to record context that future engineers and agents will need to understand the codebase.

Code quality & review

code-simplification
addyosmani/agent-skills103k

code-simplification

Simplifies code for clarity. Use when refactoring code for clarity without changing behavior. Use when code works but is harder to read, maintain, or extend than it should be. Use when reviewing code that has accumulated unnecessary complexity.

Code quality & review

understand
Egonex-AI/Understand-Anything86k

understand

Analyze a codebase to produce an interactive knowledge graph for understanding architecture, components, and relationships

Code quality & review