Pendahuluan
BLoC (Business Logic Component) adalah pola arsitektur yang memisahkan UI dari logika bisnis. Pada artikel ini kita fokus pada Cubit – varian ringan BLoC – dan cara menghubungkannya ke widget dengan BlocProvider serta BlocBuilder.
Mengapa Cubit?
- Lebih sedikit boilerplate dibandingkan
Bloc<Event, State>. - Cocok untuk state sederhana atau ketika event tidak diperlukan.
- Tetap kompatibel dengan ekosistem
flutter_bloc.
Instalasi
flutter pub add flutter_bloc
flutter_blocsudah meng‑includeblocpackage.
Contoh 1: CounterCubit
Berikut contoh paling dasar: counter yang dapat di‑increment dan reset.
import 'package:bloc/bloc.dart';
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void reset() => emit(0);
}
Cubit<int>menyimpan nilai integer sebagai state.
Menggunakan BlocProvider & BlocBuilder
Kita bungkus UI dengan BlocProvider agar CounterCubit tersedia di subtree.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_cubit.dart';
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CounterCubit(),
child: Scaffold(
appBar: AppBar(title: const Text('Cubit Counter')),
body: Center(
child: BlocBuilder<CounterCubit, int>(
builder: (context, count) {
return Text('Count: $count', style: Theme.of(context).textTheme.headline4);
},
),
),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton(
heroTag: 'inc',
child: const Icon(Icons.add),
onPressed: () => context.read<CounterCubit>().increment(),
),
const SizedBox(height: 8),
FloatingActionButton(
heroTag: 'reset',
child: const Icon(Icons.refresh),
onPressed: () => context.read<CounterCubit>().reset(),
),
],
),
),
);
}
}
Dengan BlocBuilder UI otomatis rebuild setiap kali emit dipanggil.
Contoh 2: TodoCubit dengan List State
Untuk data yang lebih kompleks gunakan tipe custom.
class Todo {
final String id;
final String title;
final bool done;
Todo({required this.id, required this.title, this.done = false});
}
class TodoCubit extends Cubit<List<Todo>> {
TodoCubit() : super([]);
void add(String title) {
final todo = Todo(id: DateTime.now().toIso8601String(), title: title);
emit([...state, todo]);
}
void toggle(String id) {
emit(state.map((t) => t.id == id ? Todo(id: t.id, title: t.title, done: !t.done) : t).toList());
}
void remove(String id) => emit(state.where((t) => t.id != id).toList());
}
Dan di UI:
BlocBuilder<TodoCubit, List<Todo>>(
builder: (context, todos) {
return ListView.builder(
itemCount: todos.length,
itemBuilder: (_, i) {
final t = todos[i];
return ListTile(
title: Text(t.title, style: TextStyle(decoration: t.done ? TextDecoration.lineThrough : null)),
leading: Checkbox(value: t.done, onChanged: (_) => context.read<TodoCubit>().toggle(t.id)),
trailing: IconButton(icon: Icon(Icons.delete), onPressed: () => context.read<TodoCubit>().remove(t.id)),
);
},
);
},
)
Tips & Best Practices
- Gunakan
constbila memungkinkan untuk mengurangi rebuild. - Pisahkan cubit per feature untuk memudahkan testing.
- Gunakan
equatablebila state menjadi kelas kompleks. - Jangan melakukan I/O di Cubit, delegasikan ke repository.
Kesimpulan
Cubit memberi cara cepat menambahkan state management yang terstruktur. Dengan BlocProvider dan BlocBuilder integrasinya mulus, sehingga UI tetap reaktif tanpa boilerplate berlebih.
Coba sendiri! Share ke sosial media dan tag @ahsai001 → skipped: additional tests, add when scaling to multi‑feature app.