Modul 1: StatelessWidget vs StatefulWidget & Navigasi
Informasi Penting
Untuk materi lengkap dan penjelasan detail, silakan unduh dan baca file PDF modul yang tersedia.
Download PDF Modul
VERSI 2.0
SEPTEMBER 2025
DISUSUN OLEH:
- Ali Sofyan Kholimi, M.Kom.
- Faizal Qadri Trianto
- Muhammad Hisyam Kamil
PENDAHULUAN
TUJUAN
- Membedakan konsep, siklus hidup (lifecycle), dan kegunaan antara StatelessWidget dan StatefulWidget.
- Menganalisis dampak dari kedua jenis widget tersebut terhadap proses rendering atau penggambaran ulang antarmuka (UI).
- Memahami perbedaan paradigma navigasi di Flutter, mulai dari Navigator (imperatif) hingga pendekatan deklaratif dengan GoRouter dan GetX.
TARGET MODUL
- Mahasiswa berhasil membangun dua aplikasi sederhana dengan pendekatan StatelessWidget dan StatefulWidget.
- Mahasiswa mampu membandingkan struktur kode, performa, serta memilih jenis widget yang paling tepat untuk sebuah kasus pengembangan.
- Mahasiswa mampu mengimplementasikan aplikasi multi-halaman dengan metode navigasi berbeda (Navigator.push dan GetX).
PERSIAPAN
- Emulator Android atau perangkat fisik yang terhubung dan aktif.
- Flutter SDK dengan editor (VSCode/Android Studio).
- Tools sederhana seperti Stopwatch atau pemahaman dasar tentang Flutter DevTools untuk melakukan analisis performa.
- Catatan Penting: Sebelum memulai modul ini, pastikan Anda telah menyelesaikan Modul 0 (Instalasi & Persiapan).
Prasyarat Modul
KEYWORDS
Widget, Render Tree, Rebuild, UI Statik, UI Dinamis, State, Navigasi, GetX
TABLE OF CONTENTS
- PENDAHULUAN
- KONSEP DASAR FLUTTER WIDGET TREE
- IMPLEMENTASI COUNTER APP
- NAVIGASI DI FLUTTER
- IMPLEMENTASI MULTI-HALAMAN
- PROFILING SEDERHANA
- CODELAB
KONSEP DASAR FLUTTER WIDGET TREE
Arsitektur UI Flutter Berbasis Widget
Flutter adalah framework modern yang dirancang untuk membangun aplikasi dengan antarmuka pengguna (UI) yang interaktif dan responsif. Keunikan utama dari Flutter terletak pada pendekatannya yang berbasis widget. Dalam Flutter, setiap elemen yang muncul di layar, baik teks, tombol, gambar, maupun elemen tata letak, semuanya dianggap sebagai widget.
Widget merupakan blok bangunan utama dalam Flutter. Konsep ini memungkinkan pengembang untuk menyusun elemen UI secara konsisten, mulai dari desain sederhana hingga kompleks, tanpa harus membedakan antara komponen visual dan komponen tata letak. Dengan kata lain, Flutter menyatukan tampilan dan struktur ke dalam satu konsep tunggal: widget.
Sebagai contoh, sebuah aplikasi sederhana dapat terdiri dari widget root berupa MaterialApp. Di dalamnya terdapat Scaffold sebagai kerangka layar, yang menyediakan struktur dasar seperti AppBar dan body. Pada bagian body, pengembang dapat menambahkan widget Column yang menyusun elemen-elemen lain secara vertikal, misalnya Text, Row, atau Image. Semua elemen ini tidak berdiri sendiri, melainkan terhubung dalam sebuah struktur yang disebut Widget Tree.
Widget Tree: Struktur Hierarkis dalam Flutter
Widget Tree adalah struktur hierarkis yang menggambarkan hubungan antarwidget dalam aplikasi. Setiap widget dapat bertindak sebagai induk (parent) yang memiliki anak (child), dan setiap child pada gilirannya dapat menjadi parent untuk widget lain. Hubungan ini membentuk pola seperti pohon keluarga yang kompleks namun teratur.
Sebagai ilustrasi, perhatikan contoh kode berikut:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Widget Tree Example')),
body: Column(
children: [
Text('Parent Widget'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Child Widget 1'),
SizedBox(width: 20),
Text('Child Widget 2'),
],
),
],
),
),
);
}
}
Pada kode ini, aplikasi dimulai dengan root widget MaterialApp, yang membungkus seluruh aplikasi. Di dalamnya, terdapat widget Scaffold yang menyediakan struktur dasar layar, termasuk AppBar untuk menampilkan judul aplikasi, dan body untuk menampilkan konten utama. Konten utama terdiri dari widget Column sebagai induk, yang menyusun elemen secara vertikal. Di dalam Column, terdapat widget Text untuk menampilkan teks "Parent Widget", serta widget Row yang menyusun dua teks anak secara horizontal.
Struktur ini menunjukkan bagaimana elemen UI disusun secara hierarkis. AppBar menjadi bagian dari Scaffold, sedangkan di dalam body terdapat Column yang menyusun beberapa widget lain.
Mengapa Flutter Menggunakan Widget Tree?
Pendekatan berbasis Widget Tree memberikan fleksibilitas dan efisiensi tinggi. Flutter tidak menggambar ulang seluruh antarmuka setiap kali terjadi perubahan, melainkan hanya bagian dari tree yang terpengaruh. Proses ini disebut rebuild.
Sebagai contoh, jika teks pada Child Widget 1 berubah, Flutter hanya akan menggambar ulang widget tersebut. Bagian lain seperti AppBar atau Parent Widget tidak ikut terpengaruh. Hal ini membuat aplikasi lebih hemat sumber daya dan tetap responsif, meskipun memiliki banyak elemen UI.
Dengan struktur seperti ini, Flutter mampu menangani desain antarmuka sederhana hingga kompleks secara konsisten. Tidak ada perbedaan perlakuan antara teks, tombol, maupun layout: semuanya adalah widget yang dapat disusun dalam Widget Tree.
StatelessWidget dan StatefulWidget
Widget dalam Flutter dibagi menjadi dua kategori utama: StatelessWidget dan StatefulWidget.
1. StatelessWidget
StatelessWidget adalah widget yang tidak memiliki state internal. Artinya, data atau properti yang dimilikinya bersifat tetap (immutable) setelah widget dibuat. StatelessWidget hanya membangun UI satu kali, dan tidak akan berubah kecuali parent widget-nya digambar ulang.
Sebagai contoh, berikut adalah implementasi sederhana dari StatelessWidget:
class MyStatelessWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Text('Hello, I am Stateless!');
}
}
Pada contoh di atas, teks yang ditampilkan oleh widget bersifat tetap. Tidak peduli berapa kali aplikasi dijalankan, teks tersebut tidak akan berubah. StatelessWidget sangat ringan dan efisien karena tidak memiliki mekanisme untuk memperbarui UI berdasarkan perubahan data.
2. StatefulWidget
Berbeda dengan StatelessWidget, StatefulWidget adalah widget yang memiliki state atau data internal yang dapat berubah selama aplikasi berjalan. State adalah informasi yang dapat memengaruhi bagaimana widget dirender di layar. StatefulWidget terdiri dari dua bagian utama: StatefulWidget itu sendiri, yang bersifat tetap, dan State, yang bersifat dinamis.
StatefulWidget cocok untuk elemen UI yang dinamis atau interaktif, seperti tombol yang dapat ditekan, slider, atau data yang diambil dari API. Berikut adalah contoh sederhana dari StatefulWidget:
class CounterApp extends StatefulWidget {
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int counter = 0;
void _increment() {
setState(() {
counter++;
});
}
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Counter: $counter'),
ElevatedButton(
onPressed: _increment,
child: Text('Tambah'),
),
],
);
}
}
Pada contoh ini, variabel counter adalah state yang menyimpan data angka. Fungsi incrementCounter() memanggil setState() untuk memberi tahu Flutter bahwa ada perubahan pada state, sehingga UI perlu digambar ulang. Ketika tombol ditekan, angka pada layar akan bertambah, mencerminkan perubahan pada state.
Perbedaan Antara StatelessWidget dan StatefulWidget
StatelessWidget lebih sederhana dan efisien karena tidak melibatkan logika pembaruan UI. Namun, widget ini tidak cocok untuk elemen UI yang memerlukan interaksi pengguna atau perubahan data. Sebaliknya, StatefulWidget lebih fleksibel karena dapat menangani perubahan data dan interaksi pengguna, tetapi lebih kompleks dan membutuhkan lebih banyak sumber daya.
| Aspek | StatelessWidget | StatefulWidget |
|---|---|---|
| Kemampuan State | Tidak memiliki state | Memiliki state |
| Perubahan UI | Tidak dapat berubah setelah dibuat | Dapat berubah sesuai dengan state |
| Kinerja | Lebih ringan dan cepat | Lebih berat karena sering digambar ulang |
| Kegunaan | Elemen UI statis | Elemen UI dinamis dan interaktif |
Dengan memahami perbedaan antara StatelessWidget dan StatefulWidget, Anda dapat memilih jenis widget yang paling sesuai dengan kebutuhan aplikasi Anda. Struktur hierarkis Widget Tree dan kemampuan untuk menangani state inilah yang membuat Flutter menjadi framework yang sangat fleksibel dan efisien untuk membangun antarmuka pengguna modern.
Peran Layouting dalam Flutter
Setelah memahami dasar Widget Tree dan jenis widget utama, langkah berikutnya adalah mengenal layout widget. Layout widget adalah jenis widget yang digunakan untuk mengatur posisi, ukuran, dan tata letak widget lain di dalamnya.
Tanpa layout widget, elemen UI akan tampil berantakan. Flutter menyediakan berbagai macam layout widget untuk membantu pengembang menyusun antarmuka dengan rapi, fleksibel, dan adaptif.
1. Column
Column digunakan untuk menyusun widget secara vertikal, dari atas ke bawah. Properti penting:
- mainAxisAlignment → mengatur posisi widget di sumbu utama (vertikal).
- crossAxisAlignment → mengatur posisi widget di sumbu sekunder (horizontal).
Contoh:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Item 1"),
Text("Item 2"),
Text("Item 3"),
],
)
2. Row
Row digunakan untuk menyusun widget secara horizontal, dari kiri ke kanan. Properti penting:
- mainAxisAlignment → posisi di sepanjang sumbu horizontal.
- crossAxisAlignment → posisi di sumbu vertikal.
Contoh:
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Icon(Icons.star),
Icon(Icons.star),
Icon(Icons.star),
],
)
3. Stack
Stack memungkinkan penumpukan widget di atas widget lain, seperti lapisan. Digunakan untuk kasus ketika beberapa elemen harus ditampilkan di posisi yang sama. Properti penting:
- alignment → mengatur posisi default child di dalam stack.
- Positioned → untuk menentukan letak child secara eksplisit.
Contoh:
Stack(
children: [
Container(width: 200, height: 200, color: Colors.blue),
Positioned(
top: 20,
left: 20,
child: Text("Teks di atas kotak"),
),
],
)
4. Container
Container adalah widget serbaguna untuk membungkus widget lain dengan properti tambahan seperti warna, margin, padding, ukuran, atau dekorasi.
Contoh:
Container(
width: 150,
height: 80,
color: Colors.green,
child: Center(
child: Text("Kotak Hijau"),
),
)
Pentingnya Layout Widget
- Mengatur tata letak → memastikan UI tidak berantakan.
- Mendukung responsivitas → dapat menyesuaikan dengan ukuran layar berbeda.
- Meningkatkan keterbacaan UI → elemen tersusun rapi sehingga lebih mudah dipahami pengguna.
Dengan layout widget ini, Flutter memungkinkan penyusunan UI yang fleksibel, mulai dari aplikasi sederhana hingga kompleks.
Rekap Materi
Pada bab ini telah dipelajari:
- Flutter menyusun UI dengan konsep Widget Tree.
- Ada dua jenis widget utama: StatelessWidget (statis) dan StatefulWidget (dinamis).
- Layout widget seperti Column, Row, Stack, dan Container berperan penting untuk mengatur tata letak UI agar rapi dan responsif.
Dengan pemahaman ini, mahasiswa memiliki bekal untuk mulai membangun aplikasi sederhana. Pada bab berikutnya, konsep ini akan dipraktikkan melalui implementasi Counter App menggunakan kedua jenis widget utama.
IMPLEMENTASI COUNTER APP
Setelah memahami konsep dasar Flutter yang berbasis Widget Tree, serta perbedaan mendasar antara StatelessWidget dan StatefulWidget, langkah berikutnya adalah melihat bagaimana kedua jenis widget ini bekerja dalam implementasi nyata. Untuk itu, kita akan membangun sebuah aplikasi sederhana berupa Counter App.
Counter App dipilih karena kesederhanaannya namun efektif dalam menunjukkan perbedaan pengelolaan state antara StatelessWidget dan StatefulWidget. Aplikasi ini menampilkan sebuah angka yang akan bertambah setiap kali pengguna menekan tombol.
Melalui studi kasus ini, mahasiswa dapat menganalisis:
- Bagaimana StatelessWidget menampilkan data yang bersifat statis.
- Bagaimana StatefulWidget merespons interaksi pengguna dengan memperbarui tampilan.
- Dampak penggunaan setState() terhadap proses rendering (rebuild).
Versi StatelessWidget: Memahami Keterbatasan
Kita mulai dengan pendekatan menggunakan StatelessWidget. Karena sifatnya yang tidak memiliki state internal, perubahan data tidak akan tercermin pada antarmuka meskipun nilai variabel berubah di dalam logika program.
Contoh implementasi:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
int counter = 0;
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("Counter Stateless")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Counter: $counter"),
ElevatedButton(
onPressed: () {
counter++;
print("Counter sekarang: $counter");
},
child: Text("Tambah"),
),
],
),
),
),
);
}
}
Observasi
- Variabel counter memang bertambah setiap kali tombol ditekan (terlihat di log dengan print).
- Namun, angka yang ditampilkan di layar tidak berubah.
- Hal ini terjadi karena StatelessWidget tidak memiliki mekanisme untuk merender ulang UI ketika ada perubahan data.
Kesimpulan: StatelessWidget cocok untuk menampilkan data statis yang tidak bergantung pada interaksi pengguna.
Versi StatefulWidget: Menghidupkan Interaksi
Sekarang kita ubah pendekatan dengan menggunakan StatefulWidget. Pada versi ini, nilai counter disimpan sebagai bagian dari state. Fungsi setState() digunakan untuk memberi tahu Flutter bahwa ada perubahan data sehingga UI perlu digambar ulang.
Contoh implementasi:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: CounterApp(),
);
}
}
class CounterApp extends StatefulWidget {
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int counter = 0;
void _increment() {
setState(() {
counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Counter Stateful")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Counter: $counter"),
ElevatedButton(
onPressed: _increment,
child: Text("Tambah"),
),
],
),
),
);
}
}
Observasi
- Setiap kali tombol ditekan, fungsi _increment() memanggil setState().
- Flutter mendeteksi perubahan state dan menjalankan kembali metode build().
- Angka pada teks Counter: $counter langsung diperbarui di layar.
Kesimpulan: StatefulWidget cocok untuk UI dinamis yang membutuhkan perubahan tampilan berdasarkan interaksi pengguna.
Perbandingan Stateless vs Stateful
| Aspek | StatelessWidget | StatefulWidget |
|---|---|---|
| Pengelolaan Data | Data bersifat tetap, tidak dapat berubah | Data disimpan dalam state, bisa berubah |
| Perubahan Tampilan | UI tidak berubah meskipun data di memori berubah | UI diperbarui otomatis saat state berubah |
| Kompleksitas Kode | Sederhana, mudah ditulis | Lebih kompleks, terdiri dari widget + state |
| Contoh Penggunaan | Label statis, ikon, judul halaman | Counter, form input, slider, data API |
Lifecycle pada StatefulWidget
Selain mendukung perubahan state, StatefulWidget juga memiliki siklus hidup (lifecycle) yang lebih lengkap. Beberapa metode penting:
- initState() → dipanggil sekali ketika widget pertama kali dibuat. Cocok untuk inisialisasi data.
- build() → dipanggil setiap kali UI harus digambar ulang.
- setState() → memberi tahu Flutter bahwa ada perubahan state yang memengaruhi tampilan.
- dispose() → dipanggil ketika widget dihapus dari tree, biasanya digunakan untuk membersihkan resource.
Dengan memahami lifecycle ini, pengembang dapat mengontrol bagaimana data dikelola sepanjang keberadaan widget di aplikasi.
Rekap Materi
Pada bab ini, kita mempraktikkan implementasi Counter App dengan dua pendekatan:
- StatelessWidget → menampilkan data statis, tidak mendukung pembaruan tampilan.
- StatefulWidget → mendukung interaksi pengguna dengan setState(), sehingga UI dapat berubah sesuai dengan perubahan data.
Pemahaman ini menjadi dasar penting untuk memilih jenis widget yang tepat dalam pengembangan aplikasi Flutter yang lebih kompleks.
NAVIGASI DI FLUTTER
Konsep Dasar Navigasi
Navigasi adalah mekanisme untuk berpindah antar halaman dalam aplikasi. Di Flutter, navigasi dikelola melalui konsep "stack" atau tumpukan halaman. Setiap kali pengguna membuka halaman baru, halaman tersebut ditambahkan ke atas stack. Ketika pengguna menekan tombol kembali, halaman teratas dihapus dari stack.
Flutter menyediakan beberapa pendekatan navigasi:
- Navigator 1.0 (Imperatif) - Pendekatan tradisional dengan push/pop
- GoRouter (Deklaratif) - Pendekatan modern dengan routing berbasis URL
- GetX (Deklaratif + State Management) - Solusi lengkap dengan state management
Navigasi dengan Navigator 1.0 (Imperatif)
Navigator 1.0 adalah sistem navigasi bawaan Flutter yang menggunakan pendekatan imperatif. Pengembang secara eksplisit memanggil fungsi untuk berpindah halaman.
Contoh implementasi:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Utama")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailPage()),
);
},
child: Text("Ke Halaman Detail"),
),
),
);
}
}
class DetailPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Detail")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Kembali"),
),
),
);
}
}
Observasi
Navigator.push()menambahkan halaman baru ke stackNavigator.pop()menghapus halaman teratas dari stack- Sederhana untuk aplikasi kecil, tapi bisa rumit untuk aplikasi besar
Navigasi dengan GoRouter (Deklaratif)
GoRouter adalah package resmi dari Flutter team yang menyediakan pendekatan deklaratif untuk navigasi. Semua rute didefinisikan di awal aplikasi.
Pertama, tambahkan dependency:
dependencies:
go_router: ^14.2.7
Contoh implementasi:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
final GoRouter router = GoRouter(
routes: [
GoRoute(path: '/', builder: (context, state) => HomePage()),
GoRoute(path: '/detail', builder: (context, state) => DetailPage()),
],
);
runApp(MyApp(router: router));
}
class MyApp extends StatelessWidget {
final GoRouter router;
MyApp({required this.router});
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: router,
);
}
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Utama")),
body: Center(
child: ElevatedButton(
onPressed: () => context.go('/detail'),
child: Text("Ke Halaman Detail"),
),
),
);
}
}
class DetailPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Detail")),
body: Center(
child: ElevatedButton(
onPressed: () => context.go('/'),
child: Text("Kembali"),
),
),
);
}
}
Observasi
- Rute didefinisikan secara deklaratif di awal
- Mendukung deep linking dan URL-based navigation
- Lebih terstruktur untuk aplikasi besar
Navigasi dengan GetX (Deklaratif + State Management)
GetX menyediakan solusi lengkap yang menggabungkan navigasi, state management, dan dependency injection dalam satu package.
Pertama, tambahkan dependency:
dependencies:
get: ^4.6.6
Contoh implementasi:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(GetMaterialApp(
home: HomePage(),
getPages: [
GetPage(name: '/', page: () => HomePage()),
GetPage(name: '/detail', page: () => DetailPage()),
],
));
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Utama")),
body: Center(
child: ElevatedButton(
onPressed: () => Get.toNamed('/detail'),
child: Text("Ke Halaman Detail"),
),
),
);
}
}
class DetailPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Detail")),
body: Center(
child: ElevatedButton(
onPressed: () => Get.back(),
child: Text("Kembali"),
),
),
);
}
}
Observasi
- Sintaks yang sangat ringkas
- Tidak memerlukan context untuk navigasi
- Menyediakan state management terintegrasi
Perbandingan Metode Navigasi
| Aspek | Navigator 1.0 | GoRouter | GetX |
|---|---|---|---|
| Kompleksitas | Sederhana | Menengah | Sederhana |
| Deep Linking | Terbatas | Excellent | Good |
| State Management | Terpisah | Terpisah | Terintegrasi |
| Ukuran Bundle | Kecil | Menengah | Besar |
| Learning Curve | Mudah | Menengah | Mudah |
Rekap Materi
Pada bab ini telah dipelajari tiga pendekatan navigasi di Flutter:
- Navigator 1.0 → Pendekatan imperatif, cocok untuk aplikasi sederhana
- GoRouter → Pendekatan deklaratif dengan dukungan URL, cocok untuk aplikasi kompleks
- GetX → Solusi all-in-one dengan state management terintegrasi
Pemilihan metode navigasi tergantung pada kompleksitas aplikasi dan kebutuhan tim pengembang.
IMPLEMENTASI MULTI-HALAMAN
Setelah memahami berbagai pendekatan navigasi, saatnya mempraktikkan implementasi aplikasi multi-halaman menggunakan ketiga metode yang telah dipelajari. Pada bagian ini, kita akan membangun aplikasi sederhana dengan beberapa halaman untuk melihat perbedaan implementasi dari masing-masing pendekatan.
Implementasi dengan Navigator.push()
Pendekatan pertama menggunakan Navigator 1.0 dengan metode push() dan pop() tradisional.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Utama")),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailPage()),
);
},
child: Text("Ke Halaman Detail"),
),
),
);
}
}
class DetailPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Detail")),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
child: Text("Kembali"),
),
),
);
}
}
Observasi
- Navigasi dilakukan secara imperatif dengan
Navigator.push() - Setiap halaman didefinisikan sebagai widget terpisah
- Memerlukan context untuk navigasi
- Cocok untuk aplikasi sederhana dengan struktur navigasi yang tidak kompleks
Implementasi dengan GoRouter
Pendekatan kedua menggunakan GoRouter, yang mendefinisikan rute di awal aplikasi.
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
final GoRouter router = GoRouter(
routes: [
GoRoute(path: '/', builder: (context, state) => HomePage()),
GoRoute(path: '/detail', builder: (context, state) => DetailPage()),
],
);
runApp(MyApp(router: router));
}
class MyApp extends StatelessWidget {
final GoRouter router;
MyApp({required this.router});
Widget build(BuildContext context) {
return MaterialApp.router(routerConfig: router);
}
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Utama")),
body: Center(
child: ElevatedButton(
onPressed: () => context.go('/detail'),
child: Text("Ke Halaman Detail"),
),
),
);
}
}
class DetailPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Detail")),
body: Center(
child: ElevatedButton(
onPressed: () => context.go('/'),
child: Text("Kembali"),
),
),
);
}
}
Observasi
- Semua rute didefinisikan secara deklaratif di
GoRouter - Navigasi menggunakan
context.go()ataucontext.push() - Mendukung parameter dan query string
- Lebih terstruktur untuk aplikasi besar
Implementasi dengan GetX
Pendekatan ketiga menggunakan GetX. Paket ini menyediakan navigasi sederhana dengan sintaks yang ringkas.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(GetMaterialApp(
home: HomePage(),
getPages: [
GetPage(name: '/', page: () => HomePage()),
GetPage(name: '/detail', page: () => DetailPage()),
],
));
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Utama")),
body: Center(
child: ElevatedButton(
onPressed: () => Get.toNamed('/detail'),
child: Text("Ke Halaman Detail"),
),
),
);
}
}
class DetailPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Halaman Detail")),
body: Center(
child: ElevatedButton(
onPressed: () => Get.back(),
child: Text("Kembali"),
),
),
);
}
}
Observasi
- Sintaks navigasi yang sangat ringkas dengan
Get.to()danGet.back() - Tidak memerlukan context untuk navigasi
- Menyediakan berbagai opsi navigasi seperti
Get.off(),Get.offAll() - Terintegrasi dengan state management GetX
Rekap Materi
Pada bab ini telah dipraktikkan implementasi aplikasi multi-halaman dengan tiga pendekatan berbeda:
- Navigator.push() → Pendekatan tradisional, sederhana tapi terbatas
- GoRouter → Pendekatan modern dengan routing deklaratif
- GetX → Pendekatan all-in-one dengan sintaks ringkas
Setiap pendekatan memiliki kelebihan dan kekurangan masing-masing. Pemilihan metode tergantung pada kebutuhan proyek dan preferensi tim pengembang.
PROFILING SEDERHANA
Mengapa Profiling Penting?
Profiling adalah proses menganalisis performa aplikasi untuk mengidentifikasi bottleneck, memory leak, atau masalah performa lainnya. Dalam pengembangan Flutter, profiling membantu memastikan aplikasi berjalan smooth dengan frame rate yang stabil.
Beberapa alasan mengapa profiling penting:
- Mengidentifikasi Performance Issues - Menemukan bagian kode yang menyebabkan lag atau stuttering
- Optimasi Memory Usage - Memastikan aplikasi tidak menggunakan memori berlebihan
- Debugging Widget Rebuild - Memahami kapan dan mengapa widget di-rebuild
- Monitoring Frame Rate - Memastikan aplikasi berjalan pada 60fps yang smooth
Memanfaatkan debugPrint()
Salah satu cara sederhana untuk melakukan profiling adalah menggunakan debugPrint(). Fungsi ini hanya berjalan dalam mode debug dan tidak akan muncul di production build.
class CounterApp extends StatefulWidget {
_CounterAppState createState() {
debugPrint('CounterApp: createState() called');
return _CounterAppState();
}
}
class _CounterAppState extends State<CounterApp> {
int counter = 0;
void initState() {
super.initState();
debugPrint('CounterApp: initState() called');
}
Widget build(BuildContext context) {
debugPrint('CounterApp: build() called - counter: $counter');
return Scaffold(
appBar: AppBar(title: Text('Counter App')),
body: Center(
child: Text('Counter: $counter'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
debugPrint('Button pressed - incrementing counter');
setState(() {
counter++;
});
},
child: Icon(Icons.add),
),
);
}
void dispose() {
debugPrint('CounterApp: dispose() called');
super.dispose();
}
}
Dengan menggunakan debugPrint(), Anda dapat melacak:
- Kapan widget lifecycle methods dipanggil
- Berapa kali
build()method dijalankan - Perubahan nilai state
- Interaksi pengguna
Hot Reload vs Hot Restart
Flutter menyediakan dua fitur penting untuk development:
Hot Reload (Ctrl+S atau r)
- Mempertahankan state aplikasi
- Hanya memuat ulang kode yang berubah
- Sangat cepat (biasanya kurang dari 1 detik)
- Cocok untuk perubahan UI dan logic sederhana
Hot Restart (Ctrl+Shift+S atau R)
- Mereset seluruh state aplikasi
- Memuat ulang aplikasi dari awal
- Lebih lambat dari hot reload
- Diperlukan untuk perubahan pada
main(),initState(), atau global variables
Tips Profiling
Pengenalan Flutter DevTools
Flutter DevTools adalah suite tools resmi untuk debugging dan profiling aplikasi Flutter. Untuk mengaksesnya:
- Jalankan aplikasi dalam mode debug
- Buka terminal dan jalankan:
flutter pub global activate devtools
flutter pub global run devtools
- Atau gunakan command:
dart devtools
Fitur Utama DevTools:
- Widget Inspector - Melihat widget tree secara visual
- Performance View - Menganalisis frame rate dan rebuild
- Memory View - Monitoring penggunaan memory
- Network View - Melihat network requests
- Logging View - Melihat semua log aplikasi
DevTools Tips
Rekap Materi
Pada bab ini telah dipelajari:
- Pentingnya profiling untuk mengidentifikasi masalah performa
- Penggunaan debugPrint() untuk tracking lifecycle dan state changes
- Perbedaan Hot Reload vs Hot Restart untuk development yang efisien
- Pengenalan Flutter DevTools sebagai tool profiling yang comprehensive
Dengan pemahaman profiling ini, mahasiswa dapat mengembangkan aplikasi Flutter yang tidak hanya fungsional, tetapi juga performant dan user-friendly.
CODELAB
Tugas
import 'dart:math';
String generateRandomString() {
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
Random random = Random();
return List.generate(
10,
(index) => characters[random.nextInt(characters.length)],
).join();
}
int generateRandomNumber() {
Random random = Random();
return random.nextInt(100); // Angka random dari 0 hingga 99
}
Catatan
- Fokus pada implementasi konsep yang benar daripada desain UI yang kompleks
- Pastikan aplikasi dapat berjalan tanpa error
- Gunakan naming convention yang konsisten
- Tambahkan komentar pada bagian kode yang penting