Skip to content
Back to skills

Flutter Guide

ASecurity

Flutter/BLoC Clean Architecture patterns, review checklists, and testing guides. Background knowledge for flutter-dev, flutter-reviewer, flutter-tester. Not user-invocable.

  • 5 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added May 26, 2026
developmentbashexpresstestingapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 7 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add aleksandr-chaika/flutter-clean-arch-skills --skill flutter-guide --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Flutter Guide?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Flutter Guide
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aleksandr-chaika-flutter-guide/badge)](https://www.skillsdirectory.com/skills/aleksandr-chaika-flutter-guide)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: flutter-guide
description: |
  Flutter/BLoC Clean Architecture patterns, review checklists, and testing guides.
  Background knowledge for flutter-dev, flutter-reviewer, flutter-tester.
  Not user-invocable.
user-invocable: false
---

# Flutter Guide — Clean Architecture + BLoC

## Reference Guide

| Topic | Reference | Use When |
|-------|-----------|----------|
| Clean Architecture | `references/clean-architecture.md` | Layer rules, dependency direction |
| BLoC Patterns | `references/bloc-patterns.md` | @freezed events/states, handlers, transformers |
| UI Guidelines | `references/ui-guidelines.md` | AppColors, AppTextStyles, SVGImage |
| Review Checklist | `references/review-checklist.md` | Code review, quality checks |
| Mocking Guide | `references/mocking-guide.md` | Mocktail, MockBloc, ProviderContainer |
| Test Patterns | `references/test-patterns.md` | UseCase tests, BLoC tests, widget tests |

## Project Structure

```
lib/
├── core/          # AppError, extensions, DI (GetIt)
├── data/
│   ├── mapper/    # Entity <-> Model mappers
│   ├── network/   # API client, models (@JsonSerializable), interceptors
│   ├── repository/ # Repository implementations
│   └── services/
├── domain/
│   ├── entity/    # Domain entities (@freezed)
│   ├── repository/ # Abstract interfaces
│   ├── services/
│   └── usecases/
├── presentation/
│   ├── {feature}/
│   │   ├── bloc.dart       # FeatureBloc
│   │   ├── events.dart     # @freezed events
│   │   ├── states.dart     # @freezed states
│   │   └── view/           # Pages + widgets
│   └── common/
└── resources/     # Colors, text styles, localization
```

## Dependency Rule

`Presentation -> Domain <- Data` — Domain depends on NOTHING.

## Critical Rules

### 1. MANDATORY: @freezed for Events and States

```dart
@freezed
class FeatureEvent with _$FeatureEvent {
  const factory FeatureEvent.started() = _Started;
  const factory FeatureEvent.loadData({required int id}) = _LoadData;
}

@freezed
class FeatureState with _$FeatureState {
  const factory FeatureState.initial() = _Initial;
  const factory FeatureState.loading() = _Loading;
  const factory FeatureState.loaded(Data data) = _Loaded;
  const factory FeatureState.error(AppError error) = _Error;
}
```

### 2. BLoC Individual Event Handlers

```dart
class FeatureBloc extends Bloc<FeatureEvent, FeatureState> {
  FeatureBloc() : super(const FeatureState.initial()) {
    on<_Started>(_onStarted);
    on<_LoadData>(_onLoadData);
  }

  Future<void> _onLoadData(_LoadData event, Emitter<FeatureState> emit) async {
    emit(const FeatureState.loading());
    final result = await _repository.getData(event.id);
    result.fold(
      (error) => emit(FeatureState.error(error)),
      (data) => emit(FeatureState.loaded(data)),
    );
  }
}
```

NEVER use `event.when()` or `state.when()` in BLoC handlers.
NEVER use a single handler for all events.

### 3. Either for Error Handling

```dart
abstract class UserRepository {
  Future<Either<AppError, User>> getUser(int id);
}
```

### 4. Type-Safe Models

```dart
// Domain Entity
@freezed
class UserEntity with _$UserEntity {
  const factory UserEntity({
    required String id,
    required String name,
    required UserRole role,
  }) = _UserEntity;
}

// API Model
@freezed
@JsonSerializable()
class UserApiModel with _$UserApiModel {
  const factory UserApiModel({...}) = _UserApiModel;
  factory UserApiModel.fromJson(Map<String, dynamic> json) =>
      _$UserApiModelFromJson(json);
}

// Mapper
extension UserApiMapper on UserApiModel {
  UserEntity toDomain() => UserEntity(id: id, name: name, role: UserRole.fromString(role));
}
```

## Dart 3.x: Use Modern Features

- **Sealed classes** for exhaustive state matching (alternative to @freezed unions)
- **Pattern matching** in switch expressions
- **Records** for lightweight multiple return values
- **Null safety** everywhere, no `!` operator without justification

## Performance Rules

- **const constructors** everywhere possible
- **Keys** on list items (`ValueKey(item.id)`)
- **ListView.builder** for large lists
- **buildWhen** in BlocBuilder for granular rebuilds
- **CachedNetworkImage** with `memCacheWidth`/`memCacheHeight`
- Extract expensive subtrees into separate const widgets
- No expensive operations in `build()` methods

## Anti-Patterns (FORBIDDEN)

| Pattern | Reason |
|---------|--------|
| `Map<String, dynamic>` for data | No type safety |
| `event.when()` in BLoC handlers | Use individual handlers |
| `state.when()` in BLoC handlers | Only for UI |
| Single handler for all events | Hard to maintain |
| Hardcoded strings in UI | Use AppLocalization |
| `SvgPicture.asset()` | Use SVGImage() |
| Redundant comments | Code must be self-documenting |
| `dynamic` type | No type safety |

## UI Patterns

```dart
// BlocBuilder with buildWhen
BlocBuilder<FeatureBloc, FeatureState>(
  buildWhen: (previous, current) => previous != current,
  builder: (context, state) {
    return state.when(
      initial: () => const SizedBox.shrink(),
      loading: () => const LoadingWidget(),
      loaded: (data) => DataWidget(data: data),
      error: (error) => ErrorWidget(error: error),
    );
  },
)

// BlocListener for side effects
BlocListener<FeatureBloc, FeatureState>(
  listenWhen: (previous, current) => current is _Error,
  listener: (context, state) {
    if (state is _Error) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(state.error.message)),
      );
    }
  },
)
```

## Code Generation

```bash
flutter pub run build_runner build --delete-conflicting-outputs
```

## Testing Summary

- **BLoC tests** (MANDATORY): `bloc_test` with `blocTest<>()`, seed, expect, verify
- **Flow tests** (MANDATORY): after create -> list updated, after delete -> item removed
- **Widget tests**: BlocProvider + MockBloc
- **Coverage**: 80%+ with `flutter test --coverage`

## Review Summary

1. Architecture compliance (correct layer, inward dependencies)
2. Type safety (no dynamic, no Map<String, dynamic>)
3. BLoC patterns (individual handlers, no event.when(), no state.when())
4. @freezed on all events/states
5. UI standards (AppColors, AppTextStyles, AppLocalization, const)
6. No redundant comments

Files in this skill

  • SKILL.md6.2 KB
  • references/bloc-patterns.md6.2 KB
  • references/clean-architecture.md6.6 KB
  • references/mocking-guide.md8.2 KB
  • references/review-checklist.md4.4 KB
  • references/test-patterns.md12 KB
  • references/ui-guidelines.md7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.