1. Freezed — Immutable Data Classes
dependencies:
freezed_annotation: ^2.4.0
json_annotation: ^4.8.0
dev_dependencies:
freezed: ^2.5.0
json_serializable: ^6.7.0
build_runner: ^2.4.0
import 'package:freezed_annotation/freezed_annotation.dart';
part 'product.freezed.dart';
part 'product.g.dart';
@freezed
class Product with _$Product {
const factory Product({
required String id,
required String name,
required double price,
@Default('') String description,
@JsonKey(name: 'image_url') String? imageUrl,
@Default([]) List<String> tags,
}) = _Product;
factory Product.fromJson(Map<String, dynamic> json) => _$ProductFromJson(json);
}
// Sử dụng — immutable copyWith
final product = Product(id: '1', name: 'Laptop', price: 999);
final updated = product.copyWith(price: 899); // Immutable update
2. Freezed Union Types — Sealed Classes
@freezed
sealed class ApiResult<T> with _$ApiResult<T> {
const factory ApiResult.success(T data) = ApiSuccess;
const factory ApiResult.error(String message, {int? statusCode}) = ApiError;
const factory ApiResult.loading() = ApiLoading;
}
// Pattern matching
final result = await repository.getProducts();
result.when(
success: (data) => state = ProductLoaded(data),
error: (message, statusCode) => state = ProductError(message),
loading: () => state = const ProductLoading(),
);
// maybeWhen — xử lý một số case
result.maybeWhen(
success: (data) => showProducts(data),
orElse: () => showPlaceholder(),
);
3. Injectable — DI Code Generation
// Đánh dấu với annotations
@injectable
class ProductRepository {
final ApiClient _api;
final ProductCache _cache;
ProductRepository(this._api, this._cache);
}
@lazySingleton
class ApiClient {
final Dio _dio;
ApiClient(this._dio);
}
@module
abstract class RegisterModule {
@lazySingleton
Dio get dio => Dio(BaseOptions(baseUrl: 'https://api.example.com'));
}
// Generated — injection.config.dart
@InjectableInit()
void configureDependencies() => getIt.init();
4. Build Runner Workflow
# One-time build
dart run build_runner build --delete-conflicting-outputs
# Watch mode — auto-rebuild
dart run build_runner watch --delete-conflicting-outputs
# Clean generated files
dart run build_runner clean
| Package | Purpose | Generated files |
|---|---|---|
| frozen | Immutable classes, unions | *.freezed.dart |
| json_serializable | JSON serialization | *.g.dart |
| injectable | DI registration | injection.config.dart |
| auto_route | Router generation | *.gr.dart |
| riverpod_generator | Riverpod providers | *.g.dart |
Next article: Testing in Flutter.