Tutorial 7: Flutter Navigation, Input, dan Form
Pemrograman Berbasis Platform (CSGE602022) - diselenggarakan oleh Fakultas Ilmu Komputer, Universitas Indonesia, Semester Genap 2022/2023
Tujuan Pembelajaran
Setelah menyelesaikan tutorial ini, mahasiswa diharapkan untuk:
- Memahami elemen input dan form pada Flutter.
- Memahami navigasi dan routing dasar pada Flutter.
- Memahami alur pembuatan form dan data pada Flutter.
- Memahami clean architecture pada kode Flutter.
Navigasi Halaman pada Flutter
Pada saat belajar pengembangan web, kalian pasti sudah belajar bahwa dalam sebuah website kita dapat berpindah-pindah halaman sesuai dengan url yang diakses. Begitu juga pada sebuah aplikasi, kita dapat melakukan perpindahan dari satu halaman ke halaman lain. Bedanya, pada sebuah aplikasi, yang kita gunakan untuk berpindah bukanlah dengan mengakses url.
Flutter menyediakan sistem yang cukup lengkap untuk melakukan navigasi antar halaman. Salah satu cara yang dapat kita gunakan untuk berpindah-pindah halaman adalah dengan menggunakan widget Navigator
. Widget Navigator
menampilkan layar seakan sebagai sebuah tumpukan (stack). Untuk menavigasi sebuah halaman baru, kita dapat mengakses Navigator
melalui BuildContext
dan memanggil fungsi push()
atau pop()
. Berikut contoh penggunaan Navigator
.
...
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const MyNewScreen(myProp: prop),
),
);
},
child: Text(myProp.someValue),
...
Untuk mengetahui lebih dalam terkait Navigator
, dapat dibaca pada tautan berikut: https://api.flutter.dev/flutter/widgets/Navigator-class.html
Input dan Form pada Flutter
Sama halnya dengan sebuah web, sebuah aplikasi juga dapat berinteraksi dengan pengguna melalui input dan form. Flutter memiliki widget Form
yang dapat kita manfaatkan untuk menjadi wadah bagi beberapa input field widget yang kita buat. Sama hal nya dengan input field pada web, Flutter juga memiliki banyak tipe input field, salah satunya widget TextField
.
Untuk mencoba sampel dari widget Form
, jalankan perintah berikut:
Untuk mengetahui lebih lanjut terkait widget Form
, dapat dibaca pada tautan berikut: https://api.flutter.dev/flutter/widgets/Form-class.html.
Tutorial: Melakukan Refactoring pada Halaman Menu
Sebelum kita terjun ke dalam bagian kode program, kita akan melakukan refactoring struktur proyek kita terlebih dahulu. Hal ini dilakukan agar kode program lebih terstruktur. Dalam dunia nyata, praktik ini disebut dengan clean architecture.
Ikuti langkah-langkah berikut untuk menerapkan prinsip clean architecture pada proyekmu.
-
Buka proyek yang sebelumnya telah dibuat pada tutorial 6 dengan menggunakan IDE favoritmu.
-
Buatlah sebuah folder baru dengan nama
pages
pada folderlib
. Folder ini akan digunakan untuk menyimpan halaman-halaman dari aplikasimu. -
Pindahkan halaman menu (
menu.dart
) yang sudah dibuat pada tutorial 6 ke dalam folderlib/pages
. -
Ubah bagian
import 'package:money_tracker/menu.dart';
menjadiimport 'package:money_tracker/pages/menu.dart';
. -
Coba jalankan aplikasimu; jika tidak ada error, maka kamu telah berhasil melakukan simple refactoring.
Tutorial: Membuat dan Menambahkan Drawer
Setelah melakukan refactoring, kita akan menambahkan drawer/hamburger menu. Menu ini berguna bagi para pengguna aplikasi untuk mengakses fitur-fitur yang ada pada aplikasi tanpa harus kembali ke homepage untuk mengakses menu.
-
Buatlah sebuah folder baru dengan nama
widgets
pada folderlib
. Folder ini akan digunakan untuk menyimpan widgets pada aplikasimu. -
Buatlah sebuah file baru dengan nama
drawer.dart
pada folderlib/widgets
. -
Import beberapa file yang akan digunakan ke dalam
drawer.dart
. -
Tambahkan kode berikut pada
drawer.dart
. -
Tambahkan halaman menu yang akan dimunculkan pada drawer/hamburger menu dalam bentuk
ListTile
. -
Buka
pages/menu.dart
dan tambahkan drawer ke halaman tersebut dengan menambahkan potongan kode berikut. -
Jalankan aplikasimu dan drawer/hamburger menu sudah muncul pada bagian kiri atas halaman menu.
Tutorial: Membuat Halaman Form
Setelah membuat menu, kita akan membuat halaman form yang akan menerima input data dari pengguna. Untuk sekarang, kita hanya akan membuat halaman form dan menampilkan datanya secara lokal. Integrasi aplikasi Flutter dengan layanan Django akan dibahas pada lab berikutnya.
-
Buatlah file baru pada folder
lib/pages
dengan namaform.dart
. -
Tambahkan boilerplate berikut ke dalam file tersebut.
class MyFormPage extends StatefulWidget { const MyFormPage({super.key}); @override State<MyFormPage> createState() => _MyFormPageState(); } class _MyFormPageState extends State<MyFormPage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Form'), ), drawer: const DrawerMenu(), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('Hello World!'), ], ), ), ); } }
-
Tambahkan impor pada kode berikut.
/lib/pages/form.dart
import 'package:money_tracker/widgets/drawer.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; ...
/lib/pages/menu.dart
lib/widgets/drawer.dart
-
Ubah fungsi tombol
Tambah Transaksi
padalib/pages/menu.dart
agar mengarahkan ke halamanMyFormPage
. Kamu dapat melakukan redirection dengan menambahkan kode berikut pada bagianonTap: () { }
yang ada padaContainer
tombolTambah Transaksi
padaMyHomePage
. -
Coba jalankan aplikasi untuk melihat perubahan yang baru saja kamu buat. Seharusnya terdapat drawer atau hamburger menu pada pojok kiri atas dan halaman form berisi teks “Hello World!”.
Tutorial: Menambahkan Form dan Elemen Input
Kita akan mencoba untuk menambahkan dua tipe input form yang ada di Flutter, yaitu TextFormField
dan DropdownButton
.
-
Ubah widget
Center
menjadiForm
. -
Tambahkan form key sebagai handle dari state, validasi, dan penyimpanan form.
-
Buatlah widget
SingleChildScrollView
sebagai child dari widgetForm
. -
Buatlah widget
Container
sebagai child dari widgetSingleChildScrollView
. -
Tambahkan padding pada widget
Container
agar tampilan widget menjadi rapi. Sebagai contoh, kita akan memakai padding sebesar 20 pixels. -
Buatlah widget
Column
sebagai child dari widgetContainer
. -
Buatlah widget
TextFormField
yang dibungkus olehPadding
sebagai salah satu children dari widgetColumn
. Selain itu, tambahkan variabel baru sebagai placeholder dari nilai yang diketik padaTextFormField
nantinya. BuatlahTextFormField
sebagai penampung variabel nama transaksi. Berikut adalah contohnya.... class _MyFormPageState extends State<MyFormPage> { final _formKey = GlobalKey<FormState>(); String _namaTransaksi = ""; @override ... child: Column( children: [ Padding( // Menggunakan padding sebesar 8 pixels padding: const EdgeInsets.all(8.0), child: TextFormField( decoration: InputDecoration( hintText: "Contoh: Bayar UKT", labelText: "Nama Transaksi", // Menambahkan icon agar lebih intuitif icon: const Icon(Icons.edit_note), // Menambahkan circular border agar lebih rapi border: OutlineInputBorder( borderRadius: BorderRadius.circular(5.0), ), ), // Menambahkan behavior saat nama diketik onChanged: (String? value) { setState(() { _namaTransaksi = value!; }); }, // Menambahkan behavior saat data disimpan onSaved: (String? value) { setState(() { _namaTransaksi = value!; }); }, // Validator sebagai validasi form validator: (String? value) { if (value == null || value.isEmpty) { return 'Nama transaksi tidak boleh kosong!'; } return null; }, ), ), ...
-
Buatlah widget
DropdownButton
sebagai salah satu children dari widgetColumn
. Tambahkan variabel baru sebagai placeholder dari nilai dropdown nantinya. Selain itu, tambahkan pulaList
ofString
yang menampung opsi yang akan ditampilkan pada dropdown. Berikut adalah contohnya.... String tipeTransaksi = 'Pengeluaran'; List<String> listTipeTransaksi = ['Pengeluaran', 'Pemasukan']; @override ... ListTile( leading: const Icon(Icons.class_), title: const Text( 'Tipe Transaksi:', ), trailing: DropdownButton( value: tipeTransaksi, icon: const Icon(Icons.keyboard_arrow_down), items: listTipeTransaksi.map((String items) { return DropdownMenuItem( value: items, child: Text(items), ); }).toList(), onChanged: (String? newValue) { setState(() { tipeTransaksi = newValue!; }); }, ), ), ...
-
Buatlah widget T
extFormField
yang dibungkus olehPadding
sebagai salah satu children dari widgetColumn
. Selain itu, tambahkan variabel baru sebagai placeholder dari nilai yang diketik padaTextFormField
nantinya. BuatlahTextFormField
sebagai penampung variabel jumlah transaksi. Berikut adalah contohnya.... int jumlahTransaksi = 0; @override ... Padding( // Menggunakan padding sebesar 8 pixels padding: const EdgeInsets.all(8.0), child: TextFormField( keyboardType: TextInputType.number, inputFormatters: <TextInputFormatter>[ FilteringTextInputFormatter.digitsOnly ], decoration: InputDecoration( hintText: "Contoh: 1000000", labelText: "Jumlah Transaksi", // Menambahkan icon agar lebih intuitif icon: const Icon(Icons.edit_note), // Menambahkan circular border agar lebih rapi border: OutlineInputBorder( borderRadius: BorderRadius.circular(5.0), ), ), // Menambahkan behavior saat jumlah diketik onChanged: (String? value) { setState(() { jumlahTransaksi = int.parse(value!); }); }, // Menambahkan behavior saat data disimpan onSaved: (String? value) { setState(() { jumlahTransaksi = int.parse(value!); }); }, // Validator sebagai validasi form validator: (String? value) { if (value == null || value.isEmpty) { return 'Jumlah transaksi tidak boleh kosong!'; } return null; }, ), ), ...
-
Buatlah widget
TextFormField
yang dibungkus olehPadding
sebagai salah satu children dari widgetColumn
. Selain itu, tambahkan variabel baru sebagai placeholder dari nilai yang diketik padaTextFormField
nantinya. BuatlahTextFormField
sebagai penampung variabel deskripsi transaksi. Berikut adalah contohnya.... String _deskripsiTransaksi = ""; @override ... Padding( // Menggunakan padding sebesar 8 pixels padding: const EdgeInsets.all(8.0), child: TextFormField( decoration: InputDecoration( hintText: "Contoh: Sebelum masuk semester!", labelText: "Deskripsi Transaksi", // Menambahkan icon agar lebih intuitif icon: const Icon(Icons.notes), // Menambahkan circular border agar lebih rapi border: OutlineInputBorder( borderRadius: BorderRadius.circular(5.0), ), ), // Menambahkan behavior saat nama diketik onChanged: (String? value) { setState(() { _deskripsiTransaksi = value!; }); }, // Menambahkan behavior saat data disimpan onSaved: (String? value) { setState(() { _deskripsiTransaksi = value!; }); }, // Validator sebagai validasi form validator: (String? value) { if (value == null || value.isEmpty) { return 'Deskripsi transaksi tidak boleh kosong!'; } return null; }, ), ), ...
-
Buatlah tombol yang akan menyimpan data yang ada di setiap elemen input. Kali ini kita tidak akan menyimpan data ke dalam database, namun kita akan memunculkannya pada popup yang akan muncul setelah tombol ditekan.
Setelah semua input dan logika form dibuat, maka kita akan membuat popup yang akan memunculkan data yang ada pada input form saat tombol Tambah
ditekan.
Tutorial: Memunculkan Data
-
Tambahkan fungsi
showDialog()
pada bagianonPressed()
dan munculkan widgetDialog
pada fungsi tersebut. Berikut adalah contoh potongan kodenya.... onPressed: () { if (_formKey.currentState!.validate()) { showDialog( context: context, builder: (context) { return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), elevation: 15, child: Container( child: ListView( padding: const EdgeInsets.only(top: 20, bottom: 20), shrinkWrap: true, children: <Widget>[ Center(child: const Text('Informasi Data')), SizedBox(height: 20), // TODO: Munculkan informasi yang didapat dari form TextButton( onPressed: () { Navigator.pop(context); }, child: Text('Kembali'), ), ], ), ), ); }, ); } }, ...
-
Silakan tambahkan informasi yang didapat dari form secara mandiri. Kamu dapat menggunakan widget
Text
dan melakukan string interpolation agar keterangan data dan isi data dapat disajikan dalam satu widget. Contohnya adalahText('Nama Transaksi: $_namaTransaksi')
. -
Coba jalankan program kamu, gunakan form yang telah dibuat, dan lihat hasilnya.
Akhir Kata
Selamat, kamu telah mempelajari navigasi dasar dan pembuatan form dasar pada Flutter!
Setelah kamu menyelesaikan seluruh tutorial di atas, kamu dapat mencoba widget input lainnya yang ada di Flutter. Kamu juga dapat mencoba untuk membuat halaman baru dengan opsi navigasi yang berbeda, seperti Navigator.push()
dan Navigator.pop()
.
Jika kamu ingin mencoba tantangan, maka cobalah untuk menerapkan hal berikut pada tutorial ini.
- Modifikasi navigasi pada Drawer agar melakukan
Navigator.pop()
apabila halaman yang ingin dibuka adalah halaman yang sedang dibuka, alih-alih mengunakanNavigator.pushReplacement()
untuk semua navigasi. - Kustomisasi widget-widget yang telah kamu buat sebelumnya (styling), seperti warna, ikon, dll.
Referensi Tambahan
Credits
Tutorial ini dikembangkan berdasarkan PBP Ganjil 2023 yang ditulis oleh Tim Pengajar Pemrograman Berbasis Platform 2023. Segala tutorial serta instruksi yang dicantumkan pada repositori ini dirancang sedemikian rupa sehingga mahasiswa yang sedang mengambil mata kuliah Pemrograman Berbasis Platform dapat menyelesaikan tutorial saat sesi lab berlangsung.
Dibuat: 3 Mei 2023 07:05:33