agentsclimarketplace

Flutter app

Skill FilippoDeSilva/skills/skills/flutter/flutter-app

Skills for my agentic development workflow.

Install
npx -y skills add FilippoDeSilva/skills --skill flutter-app

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Design Flutter applications to scale by strictly adhering to architectural principles: enforce separation of concerns, maintain single source of truth, implement unidirectional data flow, and treat UI as a function of state. Use when structuring layers, implementing data layer with services and repositories, or following feature implementation workflow.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

6.5 KB, as published. Nobody here has run it

Architecting Flutter Applications

Core Architectural Principles

Design Flutter applications to scale by strictly adhering to the following principles:

  • Enforce Separation of Concerns: Decouple UI rendering from business logic and data fetching. Organize the codebase into distinct layers (UI, Logic, Data) and further separate by feature within those layers.
  • Maintain a Single Source of Truth (SSOT): Centralize application state and data in the Data layer. Ensure the SSOT is the only component authorized to mutate its respective data.
  • Implement Unidirectional Data Flow (UDF): Flow state downwards from the Data layer to the UI layer. Flow events upwards from the UI layer to the Data layer.
  • Treat UI as a Function of State: Drive the UI entirely via immutable state objects. Rebuild widgets reactively when the underlying state changes.

Structuring the Layers

Separate the application into 2 to 3 distinct layers depending on complexity. Restrict communication so that a layer only interacts with the layer directly adjacent to it.

1. UI Layer (Presentation)

Views (Widgets): Build reusable, lean widgets. Strip all business and data-fetching logic from the widget tree. Restrict widget logic to UI-specific concerns (e.g., animations, routing, layout constraints).

ViewModels: Manage the UI state. Consume domain models from the Data/Logic layers and transform them into presentation-friendly formats. Expose state to the Views and handle user interaction events.

2. Logic Layer (Domain) - Conditional

  • If the application requires complex client-side business logic: Implement a Logic layer containing Use Cases or Interactors. Use this layer to orchestrate interactions between multiple repositories before passing data to the UI layer.
  • If the application is a standard CRUD app: Omit this layer. Allow ViewModels to interact directly with Repositories.

3. Data Layer (Model)

Responsibilities: Act as the SSOT for all application data. Handle business data, external API consumption, event processing, and data synchronization.

Components: Divide the Data layer strictly into Repositories and Services.

Implementing the Data Layer

Services

Role: Wrap external APIs (HTTP servers, local databases, platform plugins).

Implementation: Write Services as stateless Dart classes. Do not store application state here.

Mapping: Create exactly one Service class per external data source.

Repositories

Role: Act as the SSOT for domain data.

Implementation: Consume raw data from Services. Handle caching, offline synchronization, and retry logic.

Transformation: Transform raw API/Service data into clean Domain Models formatted for consumption by ViewModels.

Feature Implementation Workflow

Follow this sequential workflow when adding a new feature to the application.

  1. Step 1: Define Domain Models. Create immutable Dart classes representing the core data structures required by the feature.
  2. Step 2: Implement Services. Create stateless Service classes to handle raw data fetching (e.g., HTTP GET/POST).
  3. Step 3: Implement Repositories. Create Repository classes that consume the Services, handle caching, and return Domain Models.
  4. Step 4: Implement ViewModels. Create ViewModels that consume the Repositories. Expose immutable state and define methods (commands) for user actions.
  5. Step 5: Implement Views. Create Flutter Widgets that bind to the ViewModel state and trigger ViewModel methods on user interaction.
  6. Step 6: Run Validator. Execute unit tests for Services, Repositories, and ViewModels. Execute widget tests for Views.

Feedback Loop: Review test failures → Fix logic/mocking errors → Re-run tests until passing.

Examples

Data Layer: Service and Repository

// 1. Service (Stateless API Wrapper)
class UserApiService {
  final HttpClient _client;
  
  UserApiService(this._client);

  Future<Map<String, dynamic>> fetchUserRaw(String userId) async {
    final response = await _client.get('/users/$userId');
    return response.data;
  }
}

// 2. Domain Model (Immutable)
class User {
  final String id;
  final String name;
  
  const User({required this.id, required this.name});
}

// 3. Repository (SSOT & Data Transformer)
class UserRepository {
  final UserApiService _apiService;
  User? _cachedUser;

  UserRepository(this._apiService);

  Future<User> getUser(String userId) async {
    if (_cachedUser != null && _cachedUser!.id == userId) {
      return _cachedUser!;
    }
    
    final rawData = await _apiService.fetchUserRaw(userId);
    final user = User(id: rawData['id'], name: rawData['name']);
    
    _cachedUser = user; // Cache data
    return user;
  }
}

UI Layer: ViewModel and View

// 4. ViewModel (State Management)
class UserViewModel extends ChangeNotifier {
  final UserRepository _userRepository;
  
  User? user;
  bool isLoading = false;
  String? error;

  UserViewModel(this._userRepository);

  Future<void> loadUser(String userId) async {
    isLoading = true;
    error = null;
    notifyListeners();

    try {
      user = await _userRepository.getUser(userId);
    } catch (e) {
      error = e.toString();
    } finally {
      isLoading = false;
      notifyListeners();
    }
  }
}

// 5. View (Lean UI)
class UserProfileView extends StatelessWidget {
  final UserViewModel viewModel;

  const UserProfileView({Key? key, required this.viewModel}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: viewModel,
      builder: (context, child) {
        if (viewModel.isLoading) return const CircularProgressIndicator();
        if (viewModel.error != null) return Text('Error: ${viewModel.error}');
        if (viewModel.user == null) return const Text('No user data.');
        
        return Text('Hello, ${viewModel.user!.name}');
      },
    );
  }
}

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.