Code Generation for Immutable Classes in Flutter/Dart

A powerful code generation package that removes the boilerplate from your Dart/Flutter models. It makes writing immutable data classes a breeze while providing extensive features for JSON serialization, pattern matching, and sealed unions.
🔒 Immutable Data Classes
Guaranteed immutability for your models
Built-in copyWith functionality
Automatic ==, hashCode, and toString implementations
🔄 JSON Serialization
Seamless JSON encoding/decoding
Custom serialization support
Nested object handling
🎯 Union Types / Sealed Classes
Type-safe pattern matching
Exhaustive switch statements
Discriminated unions
🛠️ Generated Utilities
Deep copy functionality
Equality comparison
toString methods
Builder patterns
⚡ Zero Boilerplate
Minimal code writing
Maximum type safety
Clean, maintainable codebase
Perfect for projects that need:
Complex data models
API integration
State management
Event handling
Error handling with union types
Clean architecture implementation
Add these dependencies to your pubspec.yaml:
dependencies:
freezed_annotation: ^2.4.1
json_annotation: ^4.8.1
dev_dependencies:
build_runner: ^2.4.6
freezed: ^2.4.5
json_serializable: ^6.7.1Run:
flutter pub getLet's create a more realistic example with an e-commerce product model:
// product_model.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'product_model.freezed.dart';
part 'product_model.g.dart';
@freezed
class ProductResponse with _$ProductResponse {
const factory ProductResponse({
required String status,
required List<Product> products,
required Metadata metadata,
}) = _ProductResponse;
factory ProductResponse.fromJson(Map<String, dynamic> json) =>
_$ProductResponseFromJson(json);
}
@freezed
class Product with _$Product {
const factory Product({
required String id,
required String name,
required double price,
required String description,
required List<String> categories,
required ProductInventory inventory,
@JsonKey(name: 'image_urls') required List<String> imageUrls,
@Default(false) bool isFeatured,
DateTime? lastUpdated,
}) = _Product;
factory Product.fromJson(Map<String, dynamic> json) =>
_$ProductFromJson(json);
}
@freezed
class ProductInventory with _$ProductInventory {
const factory ProductInventory({
required int quantity,
required String warehouse,
@Default('IN_STOCK') String status,
}) = _ProductInventory;
factory ProductInventory.fromJson(Map<String, dynamic> json) =>
_$ProductInventoryFromJson(json);
}
@freezed
class Metadata with _$Metadata {
const factory Metadata({
required int totalCount,
required int page,
@JsonKey(name: 'items_per_page') required int itemsPerPage,
}) = _Metadata;
factory Metadata.fromJson(Map<String, dynamic> json) =>
_$MetadataFromJson(json);
}Run the build_runner to generate the necessary code:
# One-time generation
flutter pub run build_runner build --delete-conflicting-outputs
# Or watch for changes
flutter pub run build_runner watchvoid main() {
final jsonString = '''
{
"status": "success",
"products": [
{
"id": "prod_123",
"name": "Premium Headphones",
"price": 199.99,
"description": "High-quality wireless headphones",
"categories": ["electronics", "audio"],
"inventory": {
"quantity": 50,
"warehouse": "CENTRAL",
"status": "IN_STOCK"
},
"image_urls": [
"https://example.com/headphones1.jpg",
"https://example.com/headphones2.jpg"
],
"isFeatured": true,
"lastUpdated": "2024-03-15T14:30:00.000Z"
}
],
"metadata": {
"totalCount": 100,
"page": 1,
"items_per_page": 20
}
}
''';
// Parsing JSON
final Map<String, dynamic> responseMap = jsonDecode(jsonString);
final productResponse = ProductResponse.fromJson(responseMap);
// Accessing data
final firstProduct = productResponse.products.first;
print('Product name: ${firstProduct.name}');
print('Price: \$${firstProduct.price}');
// Using copyWith
final updatedProduct = firstProduct.copyWith(
price: 179.99,
inventory: firstProduct.inventory.copyWith(quantity: 45),
);
// Converting back to JSON
final jsonOutput = jsonEncode(productResponse.toJson());
print(jsonOutput);
}You can customize JSON serialization using @JsonKey:
Common scenarios where @JsonKey is essential:
Backend API returns different field names than your Dart model
Need to transform data during serialization/deserialization
Handle nullable or missing fields with default values
Custom parsing for complex data types
Field value validation
Conditional serialization
@freezed
class Product with _$Product {
const factory Product({
required String id,
@JsonKey(name: 'product_name') required String name,
@JsonKey(fromJson: _priceFromJson, toJson: _priceToJson) required double price,
}) = _Product;
static double _priceFromJson(dynamic json) => (json as num).toDouble();
static num _priceToJson(double price) => price;
factory Product.fromJson(Map<String, dynamic> json) => _$ProductFromJson(json);
}Naming Conventions:
Use clear, descriptive names for your classes
End model class names with "Model" if they represent data models
Use meaningful names for union cases
File Organization:
lib/
├── models/
│ ├── product/
│ │ ├── product_model.dart
│ │ ├── product_model.freezed.dart
│ │ └── product_model.g.dart
│ └── ...Default Values:
Use @Default() for fields that should have default values
Consider making non-critical fields nullable
Documentation:
Add documentation comments to your classes and complex fields
Include examples in the documentation
Common issues and solutions:
Build Runner Issues:
# Clean and rebuild
flutter pub run build_runner clean
flutter pub run build_runner build --delete-conflicting-outputsJSON Serialization Errors:
Ensure all nested objects have proper fromJson/toJson implementations
Check that required fields are present in JSON
Verify data types match between JSON and model
Type Errors:
// Handle potential type mismatches
@JsonKey(fromJson: _parseDate)
final DateTime date;
static DateTime _parseDate(dynamic json) {
if (json is String) {
return DateTime.parse(json);
}
throw FormatException('Invalid date format');
}Missing Part Files:
Ensure you have both part statements:
part 'model_name.freezed.dart'; part 'model_name.g.dart';Run build_runner after adding new files
Remember to regularly update your dependencies to get the latest features and bug fixes.
0
6
0