Skip to content

bloc-generator

Generates Flutter BLoC/Cubit with events and states

From plugin
f5-framework
24104 skills104 agents69 commands
Install
$ npx -y skills add Fujigo-Software/f5-framework-claude --agent claude-code

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

Generates Flutter BLoC/Cubit with events and states

Agent definition

bloc-generator.md
name: flutter-bloc-generator
description: Generates Flutter BLoC/Cubit with events and states
applies_to: flutter

Flutter BLoC Generator Agent

Purpose

Generates complete BLoC implementation with events, states, and proper error handling using freezed for immutable classes.

Capabilities

  • Generate BLoC or Cubit based on complexity
  • Create freezed events and states
  • Add use case integration
  • Include error handling patterns
  • Generate corresponding tests

Input Requirements

| Field | Required | Description | |-------|----------|-------------| | `feature_name` | Yes | Feature/module name | | `bloc_name` | Yes | BLoC name | | `type` | No | `bloc` or `cubit` | | `use_cases` | No | List of use cases to inject | | `has_pagination` | No | Whether to include pagination |

Generated Files

features/{feature}/presentation/bloc/
├── {bloc}_bloc.dart
├── {bloc}_event.dart (for BLoC)
└── {bloc}_state.dart
test/features/{feature}/presentation/bloc/
└── {bloc}_bloc_test.dart

Example Usage

feature_name: products
bloc_name: products
type: bloc
use_cases:
  - GetProducts
  - DeleteProduct
has_pagination: true

Output Pattern

Event (with freezed)

// features/products/presentation/bloc/products_event.dart
import 'package:freezed_annotation/freezed_annotation.dart';

part 'products_event.freezed.dart';

@freezed
class ProductsEvent with _$ProductsEvent {
  const factory ProductsEvent.load({
    @Default(1) int page,
    String? search,
    String? categoryId,
  }) = LoadProducts;

  const factory ProductsEvent.refresh() = RefreshProducts;

  const factory ProductsEvent.loadMore() = LoadMoreProducts;

  const factory ProductsEvent.search(String query) = SearchProducts;

  const factory ProductsEvent.delete(String id) = DeleteProduct;

  const factory ProductsEvent.clearError() = ClearError;
}

State (with freezed)

// features/products/presentation/bloc/products_state.dart
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../domain/entities/product.dart';

part 'products_state.freezed.dart';

@freezed
class ProductsState with _$ProductsState {
  const factory ProductsState({
    @Default([]) List<Product> products,
    @Default(false) bool isLoading,
    @Default(false) bool isLoadingMore,
    @Default(false) bool hasReachedMax,
    @Default(1) int currentPage,
    @Default('') String searchQuery,
    String? error,
    Product? selectedProduct,
  }) = _ProductsState;

  const ProductsState._();

  bool get isEmpty => products.isEmpty && !isLoading;
  bool get hasProducts => products.isNotEmpty;
  bool get canLoadMore => !hasReachedMax && !isLoadingMore && !isLoading;
  bool get hasError => error != null;
}

BLoC Implementation

// features/products/presentation/bloc/products_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import '../../domain/usecases/get_products.dart';
import '../../domain/usecases/delete_product.dart';
import 'products_event.dart';
import 'products_state.dart';

@injectable
class ProductsBloc extends Bloc<ProductsEvent, ProductsState> {
  final GetProducts _getProducts;
  final DeleteProduct _deleteProduct;

  ProductsBloc({
    required GetProducts getProducts,
    required DeleteProduct deleteProduct,
  })  : _getProducts = getProducts,
        _deleteProduct = deleteProduct,
        super(const ProductsState()) {
    on<LoadProducts>(_onLoad);
    on<RefreshProducts>(_onRefresh);
    on<LoadMoreProducts>(_onLoadMore);
    on<SearchProducts>(_onSearch);
    on<DeleteProduct>(_onDelete);
    on<ClearError>(_onClearError);
  }

  Future<void> _onLoad(
    LoadProducts event,
    Emitter<ProductsState> emit,
  ) async {
    emit(state.copyWith(isLoading: true, error: null));

    final result = await _getProducts(
      GetProductsParams(
        page: event.page,
        search: event.search ?? state.searchQuery,
        categoryId: event.categoryId,
      ),
    );

    result.fold(
      (failure) => emit(state.copyWith(
        isLoading: false,
        error: failure.message,
      )),
      (paginated) => emit(state.copyWith(
        isLoading: false,
        products: paginated.items,
        currentPage: event.page,
        hasReachedMax: paginated.items.length < 20,
      )),
    );
  }

  Future<void> _onRefresh(
    RefreshProducts event,
    Emitter<ProductsState> emit,
  ) async {
    emit(state.copyWith(
      currentPage: 1,
      hasReachedMax: false,
    ));
    add(const ProductsEvent.load());
  }

  Future<void> _onLoadMore(
    LoadMoreProducts event,
    Emitter<ProductsState> emit,
  ) async {
    if (!state.canLoadMore) return;

    emit(state.copyWith(isLoadingMore: true));

    final result = await _getProducts(
      GetProductsParams(
        page: state.currentPage + 1,
        search: state.searchQuery.isNotEmpty ? state.searchQuery : null,
      ),
    );

    result.fold(
      (failure) => emit(state.copyWith(
        isLoadingMore: false,
        error: failure.message,
      )),
      (paginated) => emit(state.copyWith(
        isLoadingMore: false,
        products: [...state.products, ...paginated.items],
        currentPage: state.currentPage + 1,
        hasReachedMax: paginated.items.length < 20,
      )),
    );
  }

  Future<void> _onSearch(
    SearchProducts event,
    Emitter<ProductsState> emit,
  ) async {
    emit(state.copyWith(
      searchQuery: event.query,
      currentPage: 1,
      hasReachedMax: false,
    ));
    add(const ProductsEvent.load());
  }

  Future<void> _onDelete(
    DeleteProduct event,
    Emitter<ProductsState> emit,
  ) async {
    final result = await _deleteProduct(event.id);

    result.fold(
      (failure) => emit(state.copyWith(error: failure.message)),
      (_) => emit(state.copyWith(
        products: state.products.where((p) => p.id != event.id).toList(),
      )),
    );
  }

  void _onClearError(
    ClearError event,
    Emitte
Read more
Ships withf5-framework

AI-Powered Development Framework for Claude Code

Get the whole plugin, auto-invoked
Stats
24
Stars
0
Views
8
Forks
Quiet
Maintenance
Python
Language
MIT
License
6mo ago
Last commit
6mo ago
Created

Repo: Fujigo-Software/f5-framework-claude