STATE
🔄NOTIFY
💧PROVIDER
🎣RIVERPOD
👂CONSUMER
🌐GLOBAL
⚡ Mobile Development · Flutter

BAB 5 — PROVIDER & RIVERPOD

State Management Modern Flutter

Kelas 11 Semester 2 Mobile Development

Tekan atau klik tombol panah untuk mulai belajar

🗺️ Recap & Roadmap

Dari setState menuju State Management Modern

📌 Yang Sudah Kita Pelajari (BAB 4)

  • setState() — memperbarui UI dalam satu widget
  • InheritedWidget — membagi data ke widget anak
  • Masalah: setState jadi rumit saat aplikasi membesar 😵
  • Data sulit dibagi antar layar yang berjauhan

🚀 Yang Akan Kita Kuasai (BAB 5)

  • ChangeNotifier & Provider
  • Consumer & cara listen state
  • MultiProvider untuk banyak state
  • Riverpod — evolusi Provider
  • Ref, StateNotifier, & Global State
  • Mini Project: Shopping Cart App 🛒

🛤️ Peta Perjalanan BAB 5

ChangeNotifiersumber state
Providermenyalurkan
Consumermendengarkan
Riverpodlevel pro
💡 Konsep Dasar

Apa itu State Management Modern?

State = data yang bisa berubah dalam aplikasi (misalnya jumlah item keranjang, status login, isi form). State Management adalah cara kita mengatur, menyimpan, dan membagikan data itu ke seluruh layar dengan rapi.

🍽️ Analogi: Dapur Restoran

👨‍🍳
🍝
🧑‍🍳
Dapur (State) memasak → Pelayan (Provider) mengantar → Pelanggan (Widget) menerima

👨‍🍳 Dapur = State

Tempat data "dimasak" dan disimpan. Satu sumber kebenaran (single source of truth).

🧑‍🍳 Pelayan = Provider

Mengantarkan "masakan" (data) dari dapur ke meja pelanggan tanpa pelanggan harus masuk dapur.

🍽️ Pelanggan = Widget

Menerima data dan menampilkannya. Saat masakan diganti, piring otomatis diperbarui.

Inti: Tanpa state management, setiap pelanggan harus masuk ke dapur sendiri (kacau!). Dengan state management, semua rapi lewat pelayan. 🎯
🔔 Materi 1

ChangeNotifier — Si Pemberi Kabar

ChangeNotifier adalah kelas Flutter yang bisa memberi tahu ("notify") semua pendengar ketika datanya berubah. Ia memanggil notifyListeners() untuk berteriak: "Hei, ada perubahan!"

📣 Analogi: Lonceng Sekolah

🔔
🧑‍🎓
🧑‍🎓
🧑‍🎓

Lonceng (ChangeNotifier) berbunyi satu kali, dan semua siswa (widget pendengar) langsung bereaksi. ChangeNotifier tidak perlu tahu siapa saja yang mendengarkan — ia cukup membunyikan notifyListeners().

// counter_model.dart — sumber state kita class CounterModel extends ChangeNotifier { int _count = 0; // data privat int get count => _count; // cara baca data void increment() { _count++; // ubah data notifyListeners(); // 🔔 bunyikan lonceng! } }
Ingat: Tanpa notifyListeners(), data berubah tapi UI tidak ikut berubah. Lonceng wajib dibunyikan!
🎬 Demo Visual

ChangeNotifier Beraksi

Klik tombol di bawah. Saat increment() dipanggil → notifyListeners() berbunyi → layar otomatis diperbarui. Perhatikan kilatan emas saat UI rebuild!

0
menunggu aksi…

Alur yang terjadi

  • Tombol ditekan → memanggil increment()
  • _count++ mengubah data
  • notifyListeners() 🔔 memberi kabar
  • Widget pendengar rebuild → angka baru tampil

Tanpa notifyListeners()

Coba bayangkan baris notifyListeners() dihapus. Data _count tetap naik di belakang layar, tapi angka di layar diam di 0. UI dan data jadi tidak sinkron. ⚠️

💧 Materi 2

Provider — Penyalur State

Provider "membungkus" widget tree dan menyediakan objek state agar bisa diakses oleh widget mana pun di bawahnya — tanpa harus mengoper data manual lewat constructor.

🚰 Analogi: Mesin Dispenser Air

🚰
💧
💧
🥤
🥤

Bagaimana analoginya?

  • Dispenser = Provider (sumber air/data)
  • Air = state (CounterModel)
  • Gelas = widget yang butuh data
  • Siapa pun bisa ambil air tanpa membawa galon sendiri

Kenapa hebat?

Tanpa Provider, kamu harus mengoper data lewat setiap constructor widget (disebut "prop drilling" — capek!). Provider menaruh dispenser di atas, semua anak tinggal "minum". 💧

// main.dart — pasang dispenser di paling atas ChangeNotifierProvider( create: (context) => CounterModel(), // isi air child: MyApp(), // seluruh app bisa minum )
🔀 Alur Data

Provider — Flowchart Aliran Data

CounterModelChangeNotifier
Providermembungkus app
context.watchmengambil data
Widget UImenampilkan

Node muncul satu per satu — perhatikan panah yang mengalir menandakan arah data 👀

📖 Cara membaca data

// context.watch → rebuild saat berubah final count = context.watch<CounterModel>().count; // context.read → sekali baca, TIDAK listen context.read<CounterModel>().increment();

🆚 watch vs read

  • watch → dipakai untuk menampilkan data (ikut update)
  • read → dipakai untuk memanggil aksi seperti tombol (tidak update)
Tips: Gunakan read di dalam onPressed, gunakan watch di dalam build untuk teks.
👂 Materi 3

Consumer — Sang Pendengar Setia

Consumer adalah widget yang "mendengarkan" Provider dan hanya membangun ulang bagian di dalamnya saat state berubah. Ini lebih hemat daripada me-rebuild seluruh layar.

📻 Analogi: Radio & Penyiar

📡
📻
🎧
Penyiar (Provider) siaran → Radio (Consumer) yang nyetel saja yang dengar

Hanya radio yang menyetel frekuensi (Consumer yang berlangganan) yang menerima siaran. Widget lain yang tidak "nyetel" tidak terganggu — itulah efisiensi Consumer.

Consumer<CounterModel>( builder: (context, model, child) { return Text('Jumlah: ${model.count}'); // hanya ini yang rebuild }, )
🎬 Demo Rebuild

Hanya Bagian Consumer yang Rebuild

Klik tombol. Perhatikan: hanya node Consumer<Counter> yang menyala (rebuild). Widget lain di pohon tidak ikut dibangun ulang — itu efisien!

MyApp
Scaffold
Column
Consumer<Counter> → 0

Node yang menyala emas = sedang rebuild. Lihat hanya Consumer yang bereaksi ✅

Kesimpulan: Bungkus hanya bagian kecil yang perlu berubah dengan Consumer agar aplikasi tetap cepat dan ringan. 🚀
🔌 Materi 4

MultiProvider — Banyak State Sekaligus

Aplikasi nyata punya banyak state: keranjang, user login, tema, bahasa… MultiProvider memasang banyak Provider sekaligus secara rapi.

⚡ Analogi: Panel Listrik Rumah

🔌
💡
📺
❄️
🔋

Satu panel listrik (MultiProvider) punya banyak sekring (Provider). Lampu, TV, AC, dan charger masing-masing mengambil listrik dari sekringnya sendiri. Rapi dan terpusat!

🛒 CartModel

State keranjang belanja

👤 UserModel

State login pengguna

🎨 ThemeModel

State tema gelap/terang

🔀 Flowchart + Code

MultiProvider — Struktur

MultiProviderpanel utama
Cart · User · Theme3 sekring
Seluruh Appakses semua state
MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => CartModel()), ChangeNotifierProvider(create: (_) => UserModel()), ChangeNotifierProvider(create: (_) => ThemeModel()), ], child: MyApp(), // semua anak bisa pakai ketiga state )
Kenapa tidak ditumpuk manual? Tanpa MultiProvider kamu harus menyusun Provider bersarang (nested) berlapis-lapis — sulit dibaca. MultiProvider membuatnya jadi daftar rapi. ✨
🎣 Materi 5

Riverpod — Evolusi dari Provider

Riverpod dibuat oleh pengembang yang sama dengan Provider (bahkan namanya anagram dari "Provider"! 🔤). Ia memperbaiki kelemahan Provider dan lebih aman.

Providerbutuh context
Riverpodtanpa context
Lebih aman & terujicompile-safe
AspekProviderRiverpod
Butuh BuildContext?Ya, perlu contextTidak wajib 🎉
Error saat salah ketik tipeKetahuan saat running (telat)Ketahuan saat menulis kode ✅
Global stateAgak sulitSangat mudah 🌐
TestingLumayanSangat mudah dites
Analogi singkat: Provider seperti motor bebek 🛵 (andal, dipakai banyak orang). Riverpod seperti motor matic injeksi 🏍️ (lebih modern, lebih aman, fitur lebih lengkap).
⚙️ Setup

ProviderScope — Akar Riverpod

Untuk memakai Riverpod, bungkus seluruh aplikasi dengan ProviderScope. Ia ibarat "fondasi" tempat semua provider Riverpod hidup.

1️⃣ Bungkus app

void main() { runApp( ProviderScope( // 🏗️ fondasi child: MyApp(), ), ); }

2️⃣ Buat provider global

// dideklarasikan di luar kelas (global) final counterProvider = StateProvider<int>((ref) => 0);

3️⃣ Pakai di widget (ConsumerWidget)

class CounterPage extends ConsumerWidget { Widget build(context, WidgetRef ref) { final count = ref.watch(counterProvider); // dengarkan return Text('$count'); } }
🔑 Materi 6

Ref — Kunci Akses Riverpod

Ref (reference) adalah objek "remote serbaguna" untuk berinteraksi dengan provider: membaca, mendengarkan, atau mengubah state.

🔑 Analogi: Kunci Master Hotel

🗝️
🚪
🚪
🚪

Satu kunci master (Ref) bisa membuka semua pintu (provider) di hotel. Dengan Ref kamu tidak butuh context untuk mengakses state.

ref.watch()

Dengarkan & rebuild saat berubah

ref.read()

Baca sekali, tanpa listen (untuk aksi)

ref.listen()

Jalankan efek samping (mis. tampilkan snackbar)

// menaikkan counter lewat ref (tanpa context!) ref.read(counterProvider.notifier).state++;
📺 Materi 7

StateNotifier — Pengatur Logika State

StateNotifier menyimpan state plus logika untuk mengubahnya di satu tempat yang rapi. State-nya immutable — diganti baru, bukan diubah langsung.

📺 Analogi: Remote TV

📺
🎮
Remote (StateNotifier) menekan tombol → channel (state) berganti

Kamu tidak mengubah TV secara langsung — kamu pakai remote (StateNotifier) yang punya tombol-tombol (method) untuk ganti channel (state) dengan aman.

class CounterNotifier extends StateNotifier<int> { CounterNotifier() : super(0); // nilai awal void increment() => state++; // ganti state baru void reset() => state = 0; } final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) => CounterNotifier());
🌐 Materi 8

Global State — Data Milik Seluruh App

Karena provider Riverpod dideklarasikan di luar widget, ia menjadi global: bisa diakses dari layar mana pun tanpa mengoper apa pun. Sempurna untuk data login, keranjang, atau tema.

🔀 Flowchart Alur Global State

cartProviderglobal, di luar widget
Layar Homeref.watch
Layar Detailref.read.add()
Layar Cartref.watch otomatis sinkron

Satu sumber data, dibaca banyak layar — semua otomatis tersinkronisasi 🔄

// 🌐 GLOBAL — bisa dipakai di SEMUA layar final cartProvider = StateNotifierProvider<CartNotifier, List<Item>>((ref) => CartNotifier()); // Layar Detail menambah item: ref.read(cartProvider.notifier).add(item); // Layar Cart langsung tahu (otomatis): final items = ref.watch(cartProvider);
🛒 Mini Project

Shopping Cart App — Wireframe & Arsitektur

Kita bangun aplikasi keranjang belanja sederhana memakai Riverpod + StateNotifier. Inilah penerapan semua materi BAB 5!

🖼️ Wireframe Layar

🏬
Home

daftar produk + tombol tambah

🛒
Cart

item terpilih + total harga

🏗️ Arsitektur State

  • Item — model data produk (id, nama, harga)
  • CartNotifier — StateNotifier<List<Item>>
  • cartProvider — global provider
  • method: add(), remove(), total

🔀 Alur Aplikasi

Tap "Tambah"di Home
cartProvider.add()state update
notify listenersotomatis
Cart & badge updateUI sinkron
⌨️ Step by Step

Shopping Cart — Kode Lengkap

① Model Item

class Item { final String id, name; final int price; Item(this.id, this.name, this.price); }

② CartNotifier (logika keranjang)

class CartNotifier extends StateNotifier<List<Item>> { CartNotifier() : super([]); // awal: keranjang kosong void add(Item item) { state = [...state, item]; // buat list BARU (immutable) } void remove(String id) { state = state.where((e) => e.id != id).toList(); } int get total => state.fold(0, (sum, e) => sum + e.price); }

③ Global Provider

final cartProvider = StateNotifierProvider<CartNotifier, List<Item>>( (ref) => CartNotifier());

④ UI memakai keranjang

// Home: tombol tambah ElevatedButton( onPressed: () => ref.read(cartProvider.notifier).add(item), child: Text('Tambah'), ) // Cart: tampilkan total (otomatis update) final cart = ref.watch(cartProvider); Text('Total: Rp ${ref.read(cartProvider.notifier).total}');
🎮 Demo Interaktif

Shopping Cart — Coba Langsung!

Klik produk untuk menambah ke keranjang. Perhatikan animasi item terbang 🛫, badge bertambah, dan total ter-update otomatis — persis cara cartProvider bekerja!

🏬 Produk

🛒 Keranjang 0

Keranjang masih kosong

TotalRp 0
📝 Latihan 1

Quiz Pilihan Ganda Interaktif

Jawab 5 soal berikut. Klik jawaban → langsung tahu benar/salah. Skor muncul di bawah!

💻 Latihan 2

Tantangan Code: Counter dengan Riverpod

Buat aplikasi counter sederhana memakai Riverpod. Ikuti langkah-langkah, lalu cocokkan dengan kunci jawaban.

🎯 Instruksi Step-by-Step

  • Langkah 1: Buat StateProvider<int> global bernama counterProvider dengan nilai awal 0.
  • Langkah 2: Bungkus MyApp dengan ProviderScope di main().
  • Langkah 3: Buat halaman extends ConsumerWidget.
  • Langkah 4: Baca nilai dengan ref.watch(counterProvider) dan tampilkan di Text.
  • Langkah 5: Tombol FAB memanggil ref.read(counterProvider.notifier).state++.
💡 Petunjuk: Ingat — watch untuk menampilkan, read untuk aksi tombol. Provider dideklarasikan di luar kelas agar global!
🔑 Lihat Kunci Jawaban (klik)
final counterProvider = StateProvider<int>((ref) => 0); void main() => runApp(ProviderScope(child: MyApp())); class CounterPage extends ConsumerWidget { Widget build(context, ref) { final count = ref.watch(counterProvider); return Scaffold( body: Center(child: Text('$count')), floatingActionButton: FloatingActionButton( onPressed: () => ref.read(counterProvider.notifier).state++, child: Icon(Icons.add), ), ); } }
🏆 Tugas Akhir

Membuat Todo App dengan StateNotifier

Terapkan seluruh ilmu BAB 5 untuk membangun aplikasi Todo List yang fungsional menggunakan Riverpod + StateNotifier.

📋 Deskripsi Tugas

  • Buat model Todo (id, judul, selesai/belum)
  • Buat TodoNotifier extends StateNotifier<List<Todo>>
  • Fitur: tambah, hapus, tandai selesai
  • Deklarasikan todoProvider global
  • UI: input teks, daftar todo, checkbox
  • Tampilkan jumlah todo yang belum selesai

📦 Yang Dikumpulkan

  • File project Flutter (.zip)
  • Screenshot aplikasi berjalan
  • Video demo singkat (maks 2 menit)
  • Penjelasan singkat alur state (1 paragraf)
📅 Deadline: [ ___ / ___ / ______ ] — diisi oleh guru

📊 Rubrik Penilaian

KriteriaBobotIndikator
StateNotifier benar30%State immutable, method add/remove/toggle bekerja
Global Provider20%todoProvider dideklarasikan global & dipakai dengan ref
Fungsionalitas UI25%Tambah, hapus, tandai selesai berjalan mulus
Reactivity15%UI update otomatis tanpa setState manual
Kerapian kode10%Struktur file rapi, penamaan jelas

🎉 Selamat! Kamu Telah Menyelesaikan BAB 5

Kamu kini menguasai Provider, Consumer, MultiProvider, Riverpod, Ref, StateNotifier, dan Global State. Lanjutkan ke BAB 6! 🚀

ChangeNotifierProviderConsumer RiverpodStateNotifierGlobal State