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.1
Run:
flutter pub get
Let'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 watch
void 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-outputs
JSON 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.
Join Priyam on Peerlist!
Join amazing folks like Priyam and thousands of other people in tech.
Create ProfileJoin with Priyam’s personal invite link.
0
6
0