flutter_boilerplate_annotations
Annotation library untuk custom code generators. Package ini menyediakan annotation yang digunakan bersama flutter_boilerplate_generators untuk menghasilkan boilerplate: immutability, JSON serialization, entity mapping, cubit state pattern matching, dan dependency injection.
Annotations
| Annotation | Target | Keterangan |
|---|---|---|
@Model(...) |
class | Generate immutable model: copyWith, toJson, fromJson, toEntity, fromEntity |
@cubitState / @CubitState(...) |
abstract class | Generate concrete state classes + when/map extensions |
@JsonKey(name: ...) |
field | Override nama key JSON |
@Default(value) |
field | Nilai default saat key tidak ada di JSON |
@EnumValue('...') |
enum value | Override nilai JSON untuk enum |
@injectable / @Injectable(...) |
class | Register class sebagai factory di DI container |
@singleton / @Singleton(...) |
class | Register class sebagai singleton di DI container |
@lazySingleton / @LazySingleton(...) |
class | Register class sebagai lazy singleton di DI container |
@module / @Module() |
class | Tandai class sebagai DI module |
@Named('...') |
constructor param | Resolve dependency berdasarkan nama dari container |
Getting Started
Tambahkan dependency ke pubspec.yaml:
dependencies:
flutter_boilerplate_annotations: ^1.1.0
dev_dependencies:
build_runner: ^2.4.0
flutter_boilerplate_generators: ^1.0.0
Usage
@Model — Data Model
Digunakan pada abstract class untuk menghasilkan implementasi konkret, JSON serialization, dan entity mapping.
import 'package:flutter_boilerplate_annotations/flutter_boilerplate_annotations.dart';
part 'generated/user_model.g.dart';
@Model(entity: UserEntity, fromEntity: true)
abstract class UserModel with _$UserModel {
const factory UserModel({
final int? id,
@JsonKey(name: 'full_name') final String? fullName,
@Default(0) final int? age,
}) = _UserModel;
factory UserModel.fromJson(Map<String, dynamic> json) =>
_$UserModelFromJson(json);
factory UserModel.fromEntity(UserEntity entity) =>
_$UserModelFromEntity(entity);
}
Generator menghasilkan:
mixin _$UserModel—copyWith,==,hashCodeclass _UserModel— implementasi konkret_$UserModelFromJson/_$UserModelToJson— JSON serialization_$UserModelToEntity/_$UserModelFromEntity— entity mapping
Parameter @Model:
| Parameter | Default | Keterangan |
|---|---|---|
entity |
null |
Domain entity type untuk mapping |
fromJson |
true |
Generate fromJson |
toJson |
true |
Generate toJson |
toEntity |
true |
Generate toEntity |
fromEntity |
false |
Generate fromEntity (butuh entity di-set) |
genericAugmented |
false |
Aktifkan untuk generic class seperti BaseResponse<T> |
hiveObject |
null |
Hive entity type untuk local storage mapping |
toHiveObject |
true |
Generate toHiveObject |
fromHiveObject |
true |
Generate fromHiveObject |
@Model dengan Generic — BaseResponse<T>
import 'package:flutter_boilerplate_annotations/flutter_boilerplate_annotations.dart';
part 'generated/base_response.g.dart';
@Model(toEntity: false, fromEntity: false, genericAugmented: true)
abstract class BaseResponse<T> with _$BaseResponse<T> {
const factory BaseResponse({
@JsonKey(name: 'status_code') final int? statusCode,
final String? message,
final T? data,
}) = _BaseResponse<T>;
factory BaseResponse.fromJson(
Map<String, dynamic> json,
T Function(Object?) fromJsonT,
) => _$BaseResponseFromJson<T>(json, fromJsonT);
}
Perhatikan: class harus ditulis
with _$BaseResponse<T>(dengan type argument), bukanwith _$BaseResponse.
@cubitState — Cubit State
Digunakan pada abstract class yang mendefinisikan union state via const factory constructors.
import 'package:flutter_boilerplate_annotations/flutter_boilerplate_annotations.dart';
part 'generated/user_state.g.dart';
@cubitState
abstract class UserState {
const factory UserState.initial() = UserInitial;
const factory UserState.loading() = UserLoading;
const factory UserState.success({required UserModel user}) = UserSuccess;
const factory UserState.failure({required String message}) = UserFailure;
}
Generator menghasilkan:
- Concrete class
UserInitial,UserLoading,UserSuccess,UserFailure - Extension
UserStateExtdenganwhen,maybeWhen,whenOrNull,map,maybeMap,mapOrNull
Penggunaan extension di UI:
BlocBuilder<UserCubit, UserState>(
builder: (context, state) {
return state.when(
initial: () => const SizedBox.shrink(),
loading: () => const CircularProgressIndicator(),
success: (user) => Text(user.fullName ?? '-'),
failure: (message) => Text(message),
);
},
);
Parameter @CubitState (semua default true, set ke false untuk menonaktifkan method tertentu):
@CubitState(maybeMap: false, mapOrNull: false)
abstract class UserState { ... }
@JsonKey — Override Nama Key JSON
@JsonKey(name: 'full_name') final String? fullName,
@JsonKey(name: 'avatar_url') final String? avatarUrl,
@Default — Nilai Default Field
@Default(0) final int? count,
@Default(true) final bool? isActive,
@Default('unknown') final String? status,
Nilai default dipakai saat key tidak ada atau nilainya null di JSON.
@EnumValue — Serialisasi Enum
enum Gender {
@EnumValue('Laki-Laki')
male,
@EnumValue('Perempuan')
female,
}
Generator membuat lookup map JSON ↔ enum. Fallback ke nama field ('male', 'female') jika tidak ada @EnumValue.
@Injectable, @Singleton, @LazySingleton — Dependency Injection
Digunakan pada class untuk mendaftarkan dependency ke dalam DI container. Generator akan menghasilkan kode registrasi otomatis.
@injectable / @Injectable — Daftarkan sebagai factory (instance baru setiap kali di-resolve):
@injectable
class UserRepository {
UserRepository(this._apiClient);
final ApiClient _apiClient;
}
@singleton / @Singleton — Daftarkan sebagai singleton (instance yang sama sepanjang siklus hidup app):
@singleton
class AuthService {
AuthService();
}
@lazySingleton / @LazySingleton — Daftarkan sebagai lazy singleton (instance dibuat pertama kali saat di-resolve):
@lazySingleton
class CacheManager {
CacheManager();
}
Parameter @Injectable, @Singleton, @LazySingleton:
| Parameter | Default | Keterangan |
|---|---|---|
name |
null |
Nama alias untuk registrasi bernama |
asType |
null |
Register sebagai tipe lain (misalnya interface-nya) |
Contoh asType — Register sebagai abstract type:
abstract class UserRepository { ... }
@Injectable(asType: UserRepository)
class UserRepositoryImpl implements UserRepository { ... }
@Named — Resolve Dependency Berdasarkan Nama
Digunakan pada constructor parameter untuk mendapatkan dependency yang didaftarkan dengan nama tertentu.
@injectable
class PaymentService {
PaymentService(
@Named('primary') this._primaryGateway,
@Named('fallback') this._fallbackGateway,
);
final PaymentGateway _primaryGateway;
final PaymentGateway _fallbackGateway;
}
@module / @Module — DI Module
Digunakan untuk mengelompokkan registrasi dependency eksternal atau third-party yang tidak bisa dianotasi langsung. Setiap method di dalam module class dapat dianotasi dengan @injectable, @singleton, atau @lazySingleton.
@module
abstract class NetworkModule {
@singleton
Dio get dio => Dio(BaseOptions(baseUrl: 'https://api.example.com'));
@lazySingleton
ApiClient apiClient(Dio dio) => ApiClient(dio);
}
Module abstract harus menggunakan static method atau getter. Generator akan membaca return type method sebagai tipe yang didaftarkan.
ServiceContainer — Runtime DI Container
ServiceContainer adalah singleton container yang digunakan sebagai runtime registry dependency. Diakses via ServiceContainer.instance.
final container = ServiceContainer.instance;
Method tersedia:
| Method | Keterangan |
|---|---|
registerFactory<T>(create, {name}) |
Daftarkan factory (baru setiap resolve) |
registerSingleton<T>(instance, {name}) |
Daftarkan instance singleton |
registerLazySingleton<T>(create, {name}) |
Daftarkan lazy singleton |
get<T>({name}) |
Resolve dependency |
isRegistered<T>({name}) |
Cek apakah dependency sudah terdaftar |
reset() |
Hapus semua registrasi (berguna untuk testing) |
Contoh penggunaan manual:
// Registrasi
ServiceContainer.instance.registerSingleton<AuthService>(AuthService());
ServiceContainer.instance.registerFactory<UserRepository>(
() => UserRepositoryImpl(ServiceContainer.instance.get<ApiClient>()),
);
// Resolve
final auth = ServiceContainer.instance.get<AuthService>();
// Registrasi bernama
ServiceContainer.instance.registerFactory<PaymentGateway>(
() => StripeGateway(),
name: 'primary',
);
final gateway = ServiceContainer.instance.get<PaymentGateway>(name: 'primary');
// Testing — reset container
ServiceContainer.instance.reset();
Menjalankan Generator
dart run build_runner build --delete-conflicting-outputs
Atau watch mode selama development:
dart run build_runner watch --delete-conflicting-outputs
Output akan ditempatkan di folder generated/ relatif terhadap file sumber.
Libraries
- flutter_boilerplate_annotations
- Annotation library for flutter_boilerplate_generators.