Implementasi Flutter Widgets, State Management, Layouting & Styling
Dokumentasi praktikum Flutter yang berfokus pada penggunaan widget, state management dasar, layouting, styling, serta pengembangan dashboard sederhana menggunakan Flutter.
Ringkasan Praktikum Mobile Application
Praktikum ini membahas pengembangan antarmuka aplikasi menggunakan Flutter melalui implementasi widget, state management dasar, layouting, dan styling. Implementasi dilakukan dengan membangun dashboard sederhana bertema Expense Tracker yang memiliki informasi pengguna, saldo, tombol aksi, serta daftar transaksi.
Flutter Widgets
Memahami penggunaan StatelessWidget, StatefulWidget, State, Scaffold, AppBar, Card, CircleAvatar, dan IconButton.
State Management
Mengimplementasikan perubahan tampilan saldo menggunakan StatefulWidget, State, dan setState().
Layouting
Menggunakan Row, Column, Container, Card, dan ListTile untuk membangun struktur dashboard.
Transaction Dashboard
Membuat daftar transaksi dengan nominal pemasukan dan pengeluaran serta kemampuan scrolling menggunakan SingleChildScrollView.
1. Konsep Widget & State Management
Praktikum ini berfokus pada pemahaman dasar widget dan state management pada Flutter. Widget menjadi fondasi utama dalam membangun antarmuka, sementara state digunakan untuk mengelola data yang dapat berubah selama aplikasi berjalan.
StatelessWidget
Digunakan pada komponen yang tidak memiliki perubahan state. Contoh: MyApp, GreetingWidget, dan DashboardScreen.
StatefulWidget
Digunakan pada BalanceCardWidget karena visibilitas saldo dapat berubah.
State
bool _isBalanceVisible = true;
Variabel tersebut menentukan apakah saldo ditampilkan atau disembunyikan.
setState()
void _toggleVisibility() {
setState(() {
_isBalanceVisible = !_isBalanceVisible;
});
}
Fungsi setState() digunakan untuk memperbarui tampilan ketika state berubah.
Text(
_isBalanceVisible
? 'Rp 5.000.000'
: 'Rp *********',
)
Hasil
Implementasi berhasil menampilkan dashboard dengan komponen greeting dan kartu saldo yang memiliki interaksi untuk menampilkan atau menyembunyikan nominal saldo.
2. Implementasi Dashboard
Dashboard dibuat dengan komponen utama yang mudah dibaca dan memiliki struktur yang rapi. Implementasi ini mencakup element greeting, saldocard, action buttons, serta recent transactions.
Greeting
Halo, Budi
Selamat datang kembali!
Menggunakan CircleAvatar, Row, Column, dan Text.
Balance Card
Saldo Utama
Rp 5.000.000
Tombol visibility ditambahkan pada kartu saldo.
Pada hasil pengembangan, kartu saldo menggunakan Colors.teal dan memiliki informasi No. Rekening: 1234-5678.
Action Buttons
Terdapat tombol aksi untuk:
- Pemasukan
- Pengeluaran
- Transfer
Recent Transactions
Bagian ini menampilkan daftar transaksi terakhir dengan kategori pemasukan dan pengeluaran.
Hasil
Dashboard berhasil dibuat dengan struktur yang konsisten, menampilkan greeting, saldo, tombol aksi, dan daftar transaksi yang rapi.
3. Layouting & Styling
Pada praktikum ini, layouting dan styling digunakan untuk membangun tampilan dashboard yang rapi, modern, dan mudah dipahami.
Row
Digunakan untuk menyusun widget secara horizontal.
Column
Digunakan untuk menyusun widget secara vertikal.
Container
Digunakan untuk styling tombol aksi.
Card
Digunakan sebagai container untuk saldo dan transaksi.
ListTile
Digunakan untuk setiap item transaksi. Setiap ListTile memiliki leading, title, subtitle, dan trailing.
SingleChildScrollView
Digunakan agar dashboard dapat di-scroll secara vertikal.
Hasil
Dashboard berhasil menampilkan greeting, balance card, action buttons, transaction list, dan dapat di-scroll ketika konten bertambah.
4. Pengembangan Fitur
Selama praktikum, beberapa perubahan dilakukan untuk meningkatkan kualitas tampilan dan fungsi dashboard.
Perubahan Styling Balance Card
Warna awal:
Colors.blueAccent
Kemudian dikembangkan menjadi:
Colors.teal
Penambahan Nomor Rekening
No. Rekening: 1234-5678
Transaction Styling
Pengeluaran menggunakan warna merah.
Pemasukan menggunakan warna hijau.
Penambahan Data Transaksi
Ditambahkan beberapa transaksi seperti:
- Belanja Bulanan — 15 Sep 2026 — - Rp
300.000
- Freelance Design — 18 Sep 2026 — + Rp 750.000
Hasil
Implementasi dashboard mengalami peningkatan visual dan fungsionalitas dengan tampilan saldo lebih modern serta data transaksi yang lebih bervariasi.
5. Daftar Transaksi
| Transaksi | Tanggal | Nominal | Jenis |
|---|---|---|---|
| Makan Siang | 13 Sep 2026 | - Rp 50.000 | Pengeluaran |
| Gaji Bulanan | 01 Sep 2026 | + Rp 5.000.000 | Pemasukan |
| Isi Bensin | 10 Sep 2026 | - Rp 150.000 | Pengeluaran |
| Belanja Bulanan | 15 Sep 2026 | - Rp 300.000 | Pengeluaran |
| Freelance Design | 18 Sep 2026 | + Rp 750.000 | Pemasukan |
6. Source Code
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Expense Tracker',
debugShowCheckedModeBanner: false,
home: const DashboardScreen(),
);
}
}
class DashboardScreen extends StatefulWidget {
const DashboardScreen({super.key});
@override
State<DashboardScreen> createState() => _DashboardScreenState();
}
class _DashboardScreenState extends State<DashboardScreen> {
bool _isBalanceVisible = true;
void _toggleVisibility() {
setState(() {
_isBalanceVisible = !_isBalanceVisible;
});
}
final List<Map<String, dynamic>> transactions = [
{'title': 'Makan Siang', 'date': '13 Sep 2026', 'amount': -50000, 'type': 'Pengeluaran'},
{'title': 'Gaji Bulanan', 'date': '01 Sep 2026', 'amount': 5000000, 'type': 'Pemasukan'},
{'title': 'Isi Bensin', 'date': '10 Sep 2026', 'amount': -150000, 'type': 'Pengeluaran'},
{'title': 'Belanja Bulanan', 'date': '15 Sep 2026', 'amount': -300000, 'type': 'Pengeluaran'},
{'title': 'Freelance Design', 'date': '18 Sep 2026', 'amount': 750000, 'type': 'Pemasukan'},
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F7FF),
appBar: AppBar(
title: const Text('Expense Tracker'),
backgroundColor: Colors.teal,
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GreetingWidget(),
const SizedBox(height: 20),
BalanceCardWidget(
isBalanceVisible: _isBalanceVisible,
onToggle: _toggleVisibility,
),
const SizedBox(height: 20),
ActionButtonsWidget(),
const SizedBox(height: 25),
RecentTransactionsWidget(transactions: transactions),
],
),
),
),
);
}
}
class GreetingWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
const CircleAvatar(
radius: 24,
backgroundColor: Color(0xFFB3E5FC),
child: Icon(Icons.person, color: Colors.teal),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'Halo, Budi',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
Text(
'Selamat datang kembali!',
style: TextStyle(color: Colors.grey),
),
],
),
],
);
}
}
class BalanceCardWidget extends StatelessWidget {
final bool isBalanceVisible;
final VoidCallback onToggle;
const BalanceCardWidget({
super.key,
required this.isBalanceVisible,
required this.onToggle,
});
@override
Widget build(BuildContext context) {
return Card(
color: Colors.teal,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Saldo Utama', style: TextStyle(color: Colors.white, fontSize: 16)),
IconButton(
onPressed: onToggle,
icon: Icon(
isBalanceVisible ? Icons.visibility : Icons.visibility_off,
color: Colors.white,
),
),
],
),
const SizedBox(height: 10),
Text(
isBalanceVisible ? 'Rp 5.000.000' : 'Rp *********',
style: const TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
const Text(
'No. Rekening: 1234-5678',
style: TextStyle(color: Colors.white70),
),
],
),
),
);
}
}
class ActionButtonsWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: const Center(child: Text('Pemasukan')),
),
),
const SizedBox(width: 10),
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: const Center(child: Text('Pengeluaran')),
),
),
const SizedBox(width: 10),
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: const Center(child: Text('Transfer')),
),
),
],
);
}
}
class RecentTransactionsWidget extends StatelessWidget {
final List<Map<String, dynamic>> transactions;
const RecentTransactionsWidget({super.key, required this.transactions});
@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: transactions.map((item) {
final amount = item['amount'] as int;
final isIncome = amount > 0;
return ListTile(
leading: CircleAvatar(
backgroundColor: isIncome ? Colors.green[100] : Colors.red[100],
child: Icon(
isIncome ? Icons.arrow_downward : Icons.arrow_upward,
color: isIncome ? Colors.green : Colors.red,
),
),
title: Text(item['title']),
subtitle: Text(item['date']),
trailing: Text(
'${isIncome ? '+' : '-'} Rp ${amount.abs().toString()}',
style: TextStyle(
color: isIncome ? Colors.green : Colors.red,
fontWeight: FontWeight.bold,
),
),
);
}).toList(),
),
),
);
}
}
7. Pengujian
A. Pengujian Saldo Terlihat
Action: Aplikasi dijalankan dengan saldo dalam kondisi terlihat.
Expected: "Rp 5.000.000"
Status: Berhasil
B. Pengujian Saldo Disembunyikan
Action: Tekan icon visibility.
Expected: "Rp *********"
Status: Berhasil
C. Pengujian Daftar Transaksi
Pastikan seluruh transaksi tampil:
- Makan Siang
- Gaji Bulanan
- Isi Bensin
- Belanja Bulanan
- Freelance Design
Status: Berhasil
D. Pengujian Warna Transaksi
Pengeluaran: Merah
Pemasukan: Hijau
Status: Berhasil
E. Pengujian Scroll
Action: Scroll dashboard ke bawah.
Expected: Seluruh daftar transaksi dapat diakses tanpa overflow.
Status: Berhasil
8. Kesimpulan
Praktikum Mobile Application 2 berhasil menerapkan konsep dasar widget Flutter, state management, layouting, dan styling melalui pengembangan dashboard sederhana berbasis Expense Tracker.
StatelessWidget digunakan untuk komponen yang tidak membutuhkan perubahan state. StatefulWidget digunakan pada kartu saldo. setState() digunakan untuk mengubah visibilitas saldo. Row dan Column digunakan untuk layout. Container dan Card digunakan untuk styling. ListTile digunakan untuk daftar transaksi. SingleChildScrollView digunakan untuk scrolling. Warna merah dan hijau digunakan untuk membedakan pengeluaran dan pemasukan.
Hasil Akhir: Dashboard Expense Tracker berhasil dibuat dan seluruh komponen utama dapat ditampilkan serta digunakan sesuai dengan tujuan praktikum.
Hasil Akhir
Dashboard Expense Tracker berhasil dibuat dan seluruh komponen utama dapat ditampilkan serta digunakan sesuai dengan tujuan praktikum.
Dokumentasi Hasil
Tampilan Awal
Tampilan awal dashboard sebelum dilakukan pengembangan styling dan penambahan informasi.
Pengembangan Tampilan Dasar
Perubahan styling kartu saldo dan penambahan informasi nomor rekening pada tahap pengembangan awal aplikasi.
Hasil Implementasi Praktikum 2
Implementasi dashboard menggunakan Row, Column, Card, ListTile, dan SingleChildScrollView dengan tiga data transaksi.
Hasil Tugas Praktikum 2
Implementasi akhir setelah penambahan dua data transaksi dan penerapan styling warna pada nominal pemasukan dan pengeluaran.