Ercode CLI telah di-upgrade dengan 2 fitur baru yang sangat powerful:
Alternative modern dari StatefulWidget menggunakan Flutter Bloc
Local caching dengan Hive untuk aplikasi yang bisa bekerja offline
Cubit adalah state management yang lebih ringan dari Bloc, cocok untuk aplikasi dengan state yang tidak terlalu kompleks. Keuntungan menggunakan Cubit:
- ✅ Reactive: UI update otomatis saat state berubah
- ✅ Testable: Mudah untuk unit testing
- ✅ Predictable: State flow yang jelas
- ✅ Scalable: Cocok untuk project besar
Tambahkan parameter stateManagement di JSON config:
{
"name": "product",
"stateManagement": "cubit",
"fields": [...]
}Options:
"stateful"(default) - Menggunakan StatefulWidget"cubit"- Menggunakan Flutter Bloc Cubit
Config: generator/book.json
{
"name": "book",
"stateManagement": "cubit",
"fields": [
{
"name": "id",
"type": "int",
"primary": true,
"hidden": true
},
{
"name": "title",
"type": "String",
"list": true
},
{
"name": "author",
"type": "String",
"list": true
}
]
}Generate:
ercode generate generator/book.jsonGenerated Structure:
lib/modules/book/
├── cubit/
│ ├── book_cubit.dart # Cubit logic
│ └── book_state.dart # State definitions
├── data/
│ ├── book.dart
│ └── book_repository.dart
└── views/
├── book_view.dart # BlocBuilder UI
└── book_view_item.dart
Untuk List View, akan ada states:
BookInitial- State awalBookLoading- Sedang loading dataBookLoaded- Data berhasil dimuatBookEmpty- Data kosongBookError- Terjadi error
// Di view, otomatis menggunakan BlocBuilder
BlocBuilder<BookCubit, BookState>(
builder: (context, state) {
if (state is BookLoading) {
return CircularProgressIndicator();
}
if (state is BookLoaded) {
return ListView.builder(
itemCount: state.data.length,
itemBuilder: (context, index) {
return BookItem(item: state.data[index]);
},
);
}
return SizedBox();
},
)Offline-first adalah pendekatan dimana aplikasi:
- Menyimpan data di local storage (Hive)
- Menampilkan data cache saat offline
- Sync dengan server saat online
- User tetap bisa pakai app tanpa internet
Tambahkan parameter offlineFirst di JSON config:
{
"name": "product",
"stateManagement": "cubit",
"offlineFirst": true,
"fields": [...]
}Note: Offline-first requires Cubit state management
Config: generator/product.json
{
"name": "product",
"stateManagement": "cubit",
"offlineFirst": true,
"fields": [
{
"name": "id",
"type": "int",
"primary": true,
"hidden": true
},
{
"name": "image",
"type": "String",
"input": "image",
"list": true
},
{
"name": "name",
"type": "String",
"list": true
},
{
"name": "price",
"type": "int",
"list": true
},
{
"name": "description",
"type": "String"
}
]
}Dengan offlineFirst: true, generated code akan:
- Auto-cache data setelah fetch dari API
- Load dari cache saat app dibuka (instant loading)
- Fallback ke cache jika API error (no internet)
- Refresh dari server saat pull-to-refresh
- Track last sync time
App Start
↓
Load from Cache (instant) ← User sees data immediately
↓
Fetch from API
↓
Save to Cache ← Update cache
↓
Update UI
Jika Offline:
App Start
↓
Load from Cache
↓
Try Fetch from API → Failed
↓
Show Cached Data (with offline indicator)
File lib/helpers/local_db.dart akan di-generate dengan methods:
// Save list
await LocalDb.saveList<Product>(
boxName: 'product',
key: 'list',
data: products,
toJson: (item) => item.toMap(),
);
// Get list
final products = await LocalDb.getList<Product>(
boxName: 'product',
key: 'list',
fromJson: (json) => Product.fromMap(json),
);
// Get last sync time
final lastSync = await LocalDb.getLastSync(boxName: 'product');Kombinasi terbaik untuk aplikasi modern:
{
"name": "product",
"stateManagement": "cubit",
"offlineFirst": true,
"fields": [...]
}Benefits:
- ✅ Reactive UI dengan Cubit
- ✅ Instant loading dari cache
- ✅ Bekerja tanpa internet
- ✅ Auto sync saat online
Not Supported: Offline-first hanya tersedia untuk Cubit
Jika Anda butuh offline-first, harus pakai Cubit:
{
"stateManagement": "cubit",
"offlineFirst": true
}| Parameter | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
name |
String | ✅ | - | - | Module name |
api |
String | ❌ | = name | - | API endpoint |
modelName |
String | ❌ | = name | - | Model class name |
stateManagement |
String | ❌ | stateful | stateful, cubit |
State management type |
offlineFirst |
Boolean | ❌ | false | true, false |
Enable offline-first |
only |
Array | ❌ | [] | - | Generate specific modules |
fields |
Array | ✅ | - | - | Field definitions |
Config:
{
"name": "note",
"stateManagement": "cubit",
"fields": [
{"name": "id", "type": "int", "primary": true, "hidden": true},
{"name": "title", "list": true},
{"name": "content"}
]
}Generate:
ercode generate generator/note.jsonConfig:
{
"name": "product",
"stateManagement": "cubit",
"offlineFirst": true,
"fields": [
{"name": "id", "type": "int", "primary": true, "hidden": true},
{"name": "image", "input": "image", "list": true},
{"name": "name", "list": true},
{"name": "price", "type": "int", "list": true},
{"name": "stock", "type": "int"},
{"name": "description"}
]
}Generate:
ercode generate generator/product.jsonConfig:
{
"name": "article",
"stateManagement": "cubit",
"offlineFirst": true,
"fields": [
{"name": "id", "type": "int", "primary": true, "hidden": true},
{"name": "featured_image", "input": "image", "list": true},
{"name": "title", "list": true},
{"name": "summary", "list": true},
{"name": "content"},
{"name": "author"},
{"name": "published_at", "list": true}
]
}Config:
{
"name": "todo",
"stateManagement": "cubit",
"fields": [
{"name": "id", "type": "int", "primary": true, "hidden": true},
{"name": "title", "list": true},
{"name": "completed", "type": "bool", "list": true},
{"name": "description"}
]
}Jika Anda sudah punya module dengan StatefulWidget:
Before:
{
"name": "product",
"fields": [...]
}After:
{
"name": "product",
"stateManagement": "cubit",
"fields": [...]
}Steps:
- Backup existing module
- Update JSON config
- Generate with
forceflag:ercode generate generator/product.json force
- Update navigation calls jika ada
Before (Cubit Only):
{
"name": "product",
"stateManagement": "cubit",
"fields": [...]
}After (Cubit + Offline):
{
"name": "product",
"stateManagement": "cubit",
"offlineFirst": true,
"fields": [...]
}Steps:
- Update JSON config
- Re-generate:
ercode generate generator/product.json force
- Hive akan otomatis handle caching
ercode init sekarang otomatis install:
dependencies:
# Existing
dio: ^4.0.6
cached_network_image: ^3.2.3
# ... other deps
# NEW
flutter_bloc: ^8.1.3 # For Cubit
hive: ^2.2.3 # For offline storage
hive_flutter: ^1.1.0 # Hive Flutter adapter✅ Use Cubit when:
- Building new modules
- Need reactive UI
- Want testable code
- Building medium-large apps
❌ Use StatefulWidget when:
- Very simple UI
- Prototype/POC
- Familiar with StatefulWidget only
✅ Use Offline-First when:
- App needs to work offline
- Poor network conditions
- Cache for better UX
- News, articles, products apps
❌ Skip Offline-First when:
- Real-time data only
- Always-online apps
- Simple CRUD without cache
| Use Case | State Management | Offline-First |
|---|---|---|
| News App | cubit |
✅ true |
| E-commerce | cubit |
✅ true |
| Social Media Feed | cubit |
✅ true |
| Simple Admin | stateful |
❌ false |
| Real-time Chat | cubit |
❌ false |
| Todo App | cubit |
✅ true |
Solution:
- Run
flutter pub get - Check if
flutter_blocinstalled - Re-run
ercode init
Solution: Add init to main.dart:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await LocalDb.init(); // Add this
runApp(MyApp());
}Solution:
- Check
offlineFirst: truein JSON - Check
stateManagement: "cubit"is set - Verify Hive initialized
- Check console for cache errors
Solution: Make sure you're using the generated view correctly:
// Correct
Get.to(BookView());
// Wrong - don't create Cubit manually
Get.to(BlocProvider(create: ...));New features available:
-
Cubit State Management
- Add
"stateManagement": "cubit"to JSON - Modern, reactive, testable code
- Add
-
Offline-First Support
- Add
"offlineFirst": trueto JSON (requires Cubit) - Auto caching with Hive
- Works without internet
- Add
Migration:
- Existing modules still work (backward compatible)
- New modules can use Cubit
- Offline-first is optional
Next Steps:
- Run
ercode initto install new dependencies - Update JSON configs with new parameters
- Generate modules with
ercode generate
Happy Coding! 🚀