name: flutter-freezed-models description: | Generate Dart models using Freezed following DDD Clean Architecture patterns for the Ochiq budget project. license: MIT
Flutter Freezed Models Skill
Purpose
Generate immutable Freezed models with JSON serialization following project conventions.
File Naming Convention
CRITICAL: Model files MUST end with _model.dart for code generation to work!
Example: user_model.dart, product_model.dart
Code Structure
import 'package:freezed_annotation/freezed_annotation.dart';
part 'model_name_model.freezed.dart';
part 'model_name_model. g.dart';
@freezed
class ModelName with _$ModelName {
const factory ModelName({
@JsonKey(name: 'user_id') @Default(0) int userId,
@JsonKey(name: 'user_name') @Default('') String userName,
@Default(false) bool isActive,
@Default([]) List<String> tags,
@Default(Status. UNKNOWN)
@JsonKey(unknownEnumValue: Status.UNKNOWN)
Status status,
}) = _ModelName;
factory ModelName.fromJson(Map<String, dynamic> json) =>
_$ModelNameFromJson(json);
}
enum Status {
@JsonValue('unknown')
UNKNOWN,
@JsonValue('active')
ACTIVE,
@JsonValue('inactive')
INACTIVE,
}
Rules
Structure
- Always use
@freezedannotation - Include both
.freezed.dartand.g.dartpart files - Provide both
const factoryconstructor andfromJsonfactory
Fields
-
@JsonKey: Use when API field name differs from Dart camelCase
- API:
user_name→ Dart:@JsonKey(name: 'user_name') String userName - API:
status→ Dart:String status(no JsonKey needed)
- API:
-
@Default values are MANDATORY:
- String:
@Default('') - int:
@Default(0) - double:
@Default(0.0) - bool:
@Default(false) - List:
@Default([]) - Enum:
@Default(EnumName.UNKNOWN)
- String:
-
Enums:
- First value must be
UNKNOWN - Use
@JsonKey(unknownEnumValue: Enum. UNKNOWN) - Use
@JsonValuefor API mapping
- First value must be
Naming Convention
- Class name: PascalCase (e.g.,
UserModel,ProductDetail) - Fields: camelCase (e.g.,
userName,productId) - File name: snake_case ending with
_model.dart
Code Reuse
- If multiple API endpoints return identical JSON structures, create ONE model and reuse it
Examples
Simple Model
@freezed
class UserModel with _$UserModel {
const factory UserModel({
@Default(0) int id,
@Default('') String name,
@Default('') String email,
}) = _UserModel;
factory UserModel.fromJson(Map<String, dynamic> json) =>
_$UserModelFromJson(json);
}
Model with API Name Mapping
@freezed
class ProductModel with _$ProductModel {
const factory ProductModel({
@JsonKey(name: 'product_id') @Default(0) int productId,
@JsonKey(name: 'product_name') @Default('') String productName,
@JsonKey(name: 'price_usd') @Default(0.0) double priceUsd,
@Default(true) bool isAvailable,
}) = _ProductModel;
factory ProductModel. fromJson(Map<String, dynamic> json) =>
_$ProductModelFromJson(json);
}
Model with Nested Objects
@freezed
class OrderModel with _$OrderModel {
const factory OrderModel({
@Default(0) int id,
@Default(UserModel()) UserModel user,
@Default([]) List<ProductModel> products,
@Default(0.0) double total,
}) = _OrderModel;
factory OrderModel.fromJson(Map<String, dynamic> json) =>
_$OrderModelFromJson(json);
}
After Generation
Run code generation:
make gen
# or
flutter pub run build_runner build --delete-conflicting-outputs
File Location
Models should be placed in: lib/infrastructure/models/