Stats
Actions
Tags
Help us improve
Share bugs, ideas, or general feedback.
From ufil
GM Freezed patterns — model, DTO, state, event, failure, params for Flutter Clean Architecture
npx claudepluginhub ghozimahdi/gm-claude-plugins --plugin ufilHow this skill is triggered — by the user, by Claude, or both
Slash command
/ufil:freezedThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
```dart
Creates p5.js generative art with seeded randomness, noise fields, and interactive parameter exploration. Use for algorithmic art, flow fields, or particle systems.
Share bugs, ideas, or general feedback.
@Default, NO nullable@freezed
sealed class PropertyModel with _$PropertyModel {
const factory PropertyModel({
@Default('') String id,
@Default('') String name,
@Default('') String address,
@Default('') String type,
@Default(0) int totalUnits,
@Default(0) int occupiedUnits,
@Default(0) double monthlyRevenue,
}) = _PropertyModel;
}
Rules:
@Default() — no nullable ? fieldssealed class with _$ mixinfromJson/toJson — models are pure domain objectsconst factory only@JsonKey + .toModel()@freezed
sealed class PropertyDto with _$PropertyDto {
const PropertyDto._();
const factory PropertyDto({
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'address') String? address,
@JsonKey(name: 'property_type') String? type,
@JsonKey(name: 'total_units') int? totalUnits,
@JsonKey(name: 'occupied_units') int? occupiedUnits,
@JsonKey(name: 'monthly_revenue') double? monthlyRevenue,
}) = _PropertyDto;
factory PropertyDto.fromJson(Map<String, dynamic> json) {
return _$PropertyDtoFromJson(json);
}
PropertyModel toModel() {
return PropertyModel(
id: id ?? '',
name: name ?? '',
address: address ?? '',
type: type ?? '',
totalUnits: totalUnits ?? 0,
occupiedUnits: occupiedUnits ?? 0,
monthlyRevenue: monthlyRevenue ?? 0,
);
}
}
Rules:
?@JsonKey(name: '...') on EVERY field — even when Dart name matches JSON keyconst PropertyDto._() needed for .toModel() methodfromJson + toModel() mapper?? values in toModel() to match model @Default@freezed abstract class, part of bloc// property_detail_event.dart
part of 'property_detail_bloc.dart';
@freezed
abstract class PropertyDetailEvent with _$PropertyDetailEvent {
const factory PropertyDetailEvent.init({required String propertyId}) = _InitEvent;
const factory PropertyDetailEvent.refresh() = _RefreshEvent;
const factory PropertyDetailEvent.delete() = _DeleteEvent;
}
Rules:
part of the bloc file (not a standalone file)@freezed abstract class (not sealed class).init() → _InitEvent_InitEvent, _RefreshEvent) for pattern matching in Bloc// property_detail_state.dart
part of 'property_detail_bloc.dart';
// Main state — composes sub-states
@freezed
abstract class PropertyDetailState with _$PropertyDetailState {
const factory PropertyDetailState({
@Default(GetPropertyDetailState.idle()) GetPropertyDetailState propertyDetailState,
@Default(DeletePropertyDetailState.idle()) DeletePropertyDetailState deletePropertyDetailState,
}) = _PropertyDetailState;
}
// Sub-state per async action — naming: {Action}{BlocName}State
// All variants share the SAME named params with defaults (uniform shape)
@freezed
abstract class GetPropertyDetailState with _$GetPropertyDetailState {
const factory GetPropertyDetailState.idle({
@Default(PropertyModel()) PropertyModel property,
@Default(Failure.noFailure()) Failure failure,
}) = _GetPropertyDetailIdleState;
const factory GetPropertyDetailState.loading({
@Default(PropertyModel()) PropertyModel property,
@Default(Failure.noFailure()) Failure failure,
}) = GetPropertyDetailLoadingState;
const factory GetPropertyDetailState.done({
@Default(PropertyModel()) PropertyModel property,
@Default(Failure.noFailure()) Failure failure,
}) = GetPropertyDetailDoneState;
const factory GetPropertyDetailState.error({
@Default(PropertyModel()) PropertyModel property,
@Default(Failure.noFailure()) Failure failure,
}) = GetPropertyDetailErrorState;
}
@freezed
abstract class DeletePropertyDetailState with _$DeletePropertyDetailState {
const factory DeletePropertyDetailState.idle({
@Default(Failure.noFailure()) Failure failure,
}) = _DeletePropertyDetailIdleState;
const factory DeletePropertyDetailState.loading({
@Default(Failure.noFailure()) Failure failure,
}) = DeletePropertyDetailLoadingState;
const factory DeletePropertyDetailState.done({
@Default(Failure.noFailure()) Failure failure,
}) = DeletePropertyDetailDoneState;
const factory DeletePropertyDetailState.error({
@Default(Failure.noFailure()) Failure failure,
}) = DeletePropertyDetailErrorState;
}
Rules:
part of the bloc file@freezed abstract class (not sealed class)isLoading, hasError) — use sub-state unions{Action}{BlocName}State (e.g., GetPropertyDetailState, DeletePropertyDetailState)@Default(Failure.noFailure()) Failure failure always; data fields like @Default(...) {Type} {field} for queries).idle() variant is private (_ prefix), .loading()/.done()/.error() are public@Default({SubState}.idle())state.copyWith(propertyDetailState: ...) in bloc handlers; pass named args (done(property: value), error(failure: error))// core/error/failures.dart
@freezed
sealed class Failure with _$Failure {
const Failure._();
const factory Failure.server({
@Default('Server error occurred') String message,
int? statusCode,
}) = ServerFailure;
const factory Failure.network([
@Default('No internet connection') String message,
]) = NetworkFailure;
const factory Failure.cache([
@Default('Cache error') String message,
]) = CacheFailure;
const factory Failure.auth([
@Default('Authentication error') String message,
]) = AuthFailure;
const factory Failure.unexpected([
@Default('An unexpected error occurred') String message,
]) = UnexpectedFailure;
}
// packages/domain/domain_common/lib/src/models/failure.dart
@freezed
class Failure with _$Failure {
const Failure._();
const factory Failure.noFailure() = NoFailure;
const factory Failure.unexpectedError({
@Default('An unexpected error occurred') String message,
}) = UnexpectedErrorFailure;
// Network
const factory Failure.timeout() = TimeoutFailure;
const factory Failure.noConnection() = NoConnectionFailure;
const factory Failure.serverFailure() = ServerFailure;
const factory Failure.requestFailure() = RequestFailure;
// Auth
const factory Failure.userNotLoggedIn() = UserNotLoggedInFailure;
const factory Failure.tokenExpired() = TokenExpiredFailure;
const factory Failure.unauthorized() = UnauthorizedFailure;
// App-Specific
const factory Failure.permissionDenied() = PermissionDeniedFailure;
const factory Failure.forceUpdateRequired() = ForceUpdateFailure;
const factory Failure.businessLogicFailure({
@Default('Business logic validation failed') String message,
}) = BusinessLogicFailure;
}
@freezed
sealed class AddPropertyParams with _$AddPropertyParams {
const factory AddPropertyParams({
required String name,
required String address,
required String type,
required String category,
@Default('') String description,
@Default('') String photoUrl,
}) = _AddPropertyParams;
}
Rules:
required for mandatory fields, @Default for optional| Type | Freezed? | Location |
|---|---|---|
| Model | Yes — @Default, no nullable | domain/models/ |
| DTO | Yes — nullable, @JsonKey, .toModel() | data/models/ |
| Bloc Event | Yes — named constructors | presentation/blocs/ |
| Bloc State | Yes — sub-state unions | presentation/blocs/ |
| Failure | Yes — union type | core/error/ or domain_common/models/ |
| Params | Yes — for complex args | domain/models/ |
| Enums | No — plain Dart enum | domain/models/ |
After adding/modifying freezed classes:
# Non-modular
dart run build_runner build --delete-conflicting-outputs
# Modular (all packages)
melos run build
# Modular (specific layer)
melos run generate:domain
Generated files: *.freezed.dart, *.g.dart — add to .gitignore or .claudeignore
${CLAUDE_PLUGIN_ROOT}/docs/DOMAIN_LAYER.md — Domain layer model patterns${CLAUDE_PLUGIN_ROOT}/docs/DATA_LAYER.md — Data layer DTO patterns${CLAUDE_PLUGIN_ROOT}/docs/BLOC_PATTERN.md — Bloc event and state patterns