Pengenalan State Management Ketika aplikasi semakin kompleks dibuat, maka pasti akan ada saatnya dimana harus dibagikan state aplikasi ke berbagai halaman yang ada. Flutter adalah deklaratif, sehingga Flutter membangun user interface berdasarkan state saat ini. Dengan menggunakan state management, dapat dilakukan sentralisasi semua state dari berbagai macam UI Control untuk mengendalikan aliran data lintas aplikasi.
Berikut adalah struktur lengkap proyek Flutter menggunakan Provider untuk state management, termasuk semua file .dart yang diperlukan
dependencies
1 2 3 4 |
dependencies: flutter: sdk: flutter provider: ^6.0.0 |
1. Struktur Proyek
1 2 3 4 5 6 7 8 9 10 11 12 |
/lib │-- main.dart │-- models │ │-- item.dart │ │-- cart_model.dart │-- screens │ │-- home_screen.dart │ │-- cart_screen.dart │-- widgets │ │-- cart_item_widget.dart |
2. main.dart
(Entry Point Aplikasi)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import 'package:flutter/material.dart'; // Import material.dart import 'package:provider/provider.dart'; // Import provider.dart import 'models/cart_model.dart'; // Import cart_model.dart import 'screens/home_screen.dart'; // Import home_screen.dart void main() { runApp( ChangeNotifierProvider( create: (context) => CartModel(), // Inisialisasi Provider child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Flutter F Mart', // Set title theme: ThemeData(primarySwatch: Colors.blue), // Set theme home: HomeScreen(), // Set home screen ); } } |
3. models/item.dart
(Model untuk Item)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Item { final int id; final String name; final double price; int quantity; Item({ required this.id, required this.name, required this.price, this.quantity = 1, // Default quantity = 1 }); } |
4. models/cart_model.dart
(Model untuk State Keranjang Belanja)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import 'package:flutter/foundation.dart'; import 'item.dart'; // Pastikan import ini ada class CartModel extends ChangeNotifier { final List<Item> _items = []; List<Item> get items => _items; double get totalPrice => _items.fold(0, (total, item) => total + (item.price * item.quantity)); void add(Item item) { final index = _items.indexWhere((element) => element.id == item.id); if (index != -1) { _items[index].quantity++; // Tambah jumlah jika sudah ada di keranjang } else { _items.add(item); } notifyListeners(); } void decreaseQuantity(Item item) { final index = _items.indexWhere((element) => element.id == item.id); if (index != -1) { if (_items[index].quantity > 1) { _items[index].quantity--; // Kurangi jumlah jika lebih dari 1 } else { _items.removeAt(index); // Hapus jika jumlahnya 1 } notifyListeners(); } } } |
5. screens/home_screen.dart
(Halaman Utama)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/cart_model.dart'; import '../models/item.dart'; import 'cart_screen.dart'; class HomeScreen extends StatelessWidget { HomeScreen({super.key}); final List<Item> availableItems = [ Item(id: 1, name: "Beras", price: 15000000), Item(id: 2, name: "Gula", price: 250000), Item(id: 3, name: "Tepung", price: 500000), Item(id: 4, name: "Bumbu dapur", price: 1500000), Item(id: 5, name: "Minyak", price: 1500000), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("F Mart"), actions: [ IconButton( icon: const Icon(Icons.shopping_cart), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const CartScreen()), ); }, ), ], ), body: ListView.builder( itemCount: availableItems.length, itemBuilder: (context, index) { final item = availableItems[index]; return ListTile( title: Text(item.name), subtitle: Text("Rp ${item.price}"), trailing: ElevatedButton( onPressed: () { Provider.of<CartModel>(context, listen: false).add(item); }, child: const Text("Tambah"), ), ); }, ), ); } } |
6. screens/cart_screen.dart
(Halaman Keranjang)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/cart_model.dart'; class CartScreen extends StatelessWidget { const CartScreen({super.key}); @override Widget build(BuildContext context) { final cart = Provider.of<CartModel>(context); return Scaffold( appBar: AppBar(title: const Text("Keranjang Belanja")), body: cart.items.isEmpty ? const Center(child: Text("Keranjang kosong")) : ListView.builder( itemCount: cart.items.length, itemBuilder: (context, index) { final item = cart.items[index]; return Card( margin: const EdgeInsets.all(10), child: Padding( padding: const EdgeInsets.all(10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( "Rp ${item.price}", style: const TextStyle( fontSize: 16, color: Colors.grey, ), ), ], ), Row( children: [ IconButton( icon: const Icon(Icons.remove_circle_outline), onPressed: () { cart.decreaseQuantity(item); }, ), Text( "${item.quantity}", style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), IconButton( icon: const Icon(Icons.add_circle_outline), onPressed: () { cart.add(item); }, ), ], ), ], ), ), ); }, ), bottomNavigationBar: Container( padding: const EdgeInsets.all(16), child: Text( "Total: Rp ${cart.totalPrice}", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ), ); } } |
7. widgets/cart_item_widget.dart
(Widget Item di Keranjang)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import 'package:flutter/material.dart'; import '../models/item.dart'; class CartItemWidget extends StatelessWidget { final Item item; const CartItemWidget({super.key, required this.item}); @override Widget build(BuildContext context) { return ListTile(title: Text(item.name), subtitle: Text("Rp ${item.price}")); } } |
Penjelasan Urutan Eksekusi
main.dart
menjalankan aplikasi dan menyediakan ChangeNotifierProvider untukCartModel
.home_screen.dart
menampilkan daftar item dan memungkinkan pengguna menambah item ke keranjang.- Ketika tombol “Tambah” ditekan,
add()
dipanggil diCartModel
, yang memberi tahu Consumer untuk melakukan rebuild. - Saat pengguna membuka
cart_screen.dart
, daftar item dalam keranjang ditampilkan. - Di dalam
cart_screen.dart
, pengguna dapat melihat item dan total harga. - Tombol “Hapus Semua” memanggil
removeAll()
dariCartModel
, yang menghapus semua item dan memperbarui UI.
Kesimpulan
- State Management dengan Provider membuat data tetap sinkron di seluruh aplikasi.
- ChangeNotifier digunakan untuk mengatur state.
- ChangeNotifierProvider memberikan state ke widget dalam aplikasi.
- Consumer dan Provider.of(context, listen: false) memungkinkan interaksi dengan state secara efisien..
Tugas
-
Format Uang: Format harga agar tampil dengan format mata uang Indonesia (contoh: Rp 1.500.000).
- Tambah Item Baru: Tambahkan produk baru ke daftar yang tersedia di
home_screen.dart
-
Snackbar Konfirmasi: Tampilkan snackbar ketika item berhasil ditambahkan ke keranjang.