Untuk berpindah dari satu halaman ke halaman lainnya pada Flutter kita bisa menggunakan widget Navigator. Widget Navigator sendiri memiliki beberapa method untuk menangani beberapa skenario / kondisi. Pada tutorial ini saya akan membahas bagaimana menggunakan widget Navigator untuk menangani beberapa skenario / kondisi yang umumnya ada pada aplikasi mobile, seperti pindah halaman biasa, menangani tombol back, pindah halaman dengan membawa data dan menerima data dari halaman lainnya.
Navigasi Sederhana
Misal kita memiliki halaman / class simple seperti ini
import 'package:flutter/material.dart'; class Simple extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Simple Page'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('Simple Page. Please press back button') ], ), ), ); } }
Untuk pindah halaman, misal dari halaman home ke halaman simple diatas kita perlu melakukan beberapa hal, yaitu :
Import file
Misal halaman simple diatas disimpan di file simple.dart maka kita perlu import file tersebut di halaman home dengan kode seperti ini
import 'simple.dart';
Buat Action
Selanjutnya, masih di halaman home, kita perlu menentukan kapan aksi pindah halaman tersebut dilakukan. Pada umumnya aksi ini dipanggil saat user menekan tombol tertentu misal pada FlatButton seperti dibawah ini
FlatButton( onPressed: () { // pindah ke halaman simple Navigator.push(context, MaterialPageRoute(builder: (context) => Simple())); }, child: Text( 'Basic Navigation', style: TextStyle( color: Colors.white ), ), color: Colors.green, ),
Saat user menekan tombol Basic Navigation diatas, aplikasi akan menampilkan halaman baru yaitu halaman simple, jika user menekan tombol back maka akan kembali ke halaman sebelumnya yaitu home.
Navigasi tanpa History
Dengan Navigator kita juga bisa mencegah saat user menekan tombol back agar tidak kembali ke halaman sebelumnya. Misal pada halaman login, setelah user berhasil login dan masuk ke halaman dashboard misalnya, jika user menekan tombol back tentu kita tidak menginginkan untuk kembali ke halaman login lagi bukan ? Untuk menangani skenario ini kita bisa menggunakan method pushReplacement seperti dibawah ini
FlatButton( onPressed: () { // pindah ke halaman WithoutHistory // tapi tidak menyimpan halaman saat ini Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => WithoutHistory())); }, child: Text( 'Navigation Without History', style: TextStyle( color: Colors.white ), ), color: Colors.green, ),
Dengan method pushReplacement halaman saat ini, misal home tidak akan di cache di history. Di halaman baru jika user menekan tombol back aplikasi akan menampilkan halaman sebelum halaman saat ini (home) jika ada, jika tidak ada aplikasi akan otomatis ditutup.
Menangani Tombol Back
Misal pada suatu halaman user sudah memasukkan banyak data, lalu tidak sengaja menekan tombol back. Apa yang terjadi ? Tentu saja aplikasi akan kembali ke halaman sebelumnya dan data yang sudah dimasukkan user akan hilang. Untuk mencegah hal ini kita perlu meng-override tombol back. Agar kita bisa melakukan beberapa aksi, paling tidak menampilkan peringatan kepada user apakah yakin ingin keluar dari halaman sekarang atau tidak. Lalu data yang sudah dimasukkan mau di proses atau diabaikan.
Untuk menangani tombol back kita bisa menggunakan widget WillPopScope. Widget WillPopScope harus ditempatkan sebagai root widget sedangkan widget lainnya yang berhubungan dengan tampilan bisa ditempatkan sebagai child. Widget WillPopScope juga harus meng-initialize method onWillPop. Method onWillPop sendiri harus return Future
import 'package:flutter/material.dart'; class HandleBack extends StatelessWidget { Future<bool> onBackButton(context) async { // tampilkan dialog saat user menekan tombol back // jika ditekan ok maka akan kembali ke halaman sebelumnya return showDialog( context: context, builder: (context){ return AlertDialog( title: Text('Back'), content: Text('Are you sure want to back to previous page ?'), actions: <Widget>[ FlatButton( onPressed: () { // tutup modal dan halaman ini Navigator.of(context).pop(true); }, child: Text('OK'), ), FlatButton( onPressed: () { // tutup modal saja Navigator.of(context).pop(false); }, child: Text('NO'), ) ], ); } ); } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: (){ // panggil method onBackButton saat user menekan tombol back return onBackButton(context); }, child: Scaffold( appBar: AppBar( title: Text('Handle Back'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('Try press back button') ], ), ), ), ); } }
Mengirim Data ke Halaman Baru
Untuk mengirim data dari halaman saat ini ke halaman baru kita cukup memasukkan data tersebut ke constructor halaman baru. Misal kita memiliki class seperti ini
import 'package:flutter/material.dart'; import 'customer.dart'; class ReceiptData extends StatefulWidget { // property final Customer customer; // constructor ReceiptData({Key key, @required this.customer}) : super(key: key); @override _ReceiptDataState createState() => _ReceiptDataState(customer: customer); } class _ReceiptDataState extends State<ReceiptData> { // property Customer customer; // constructor _ReceiptDataState({@required this.customer}) : super(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Receipt Data'), ), body: Center( child: Padding( padding: EdgeInsets.all(20), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text( 'Inserted Customer : ' ), Text( 'Name: ' + customer.name ), Text( 'Address: ' + customer.address ), ], ), ) ), ); } }
Untuk mengirim data customer ke halaman ReceiptData diatas kita bisa menggunakan kode seperti ini.
Customer customer = Customer(nameController.text, addressController.text); Navigator.push(context, MaterialPageRoute(builder: (context) => ReceiptData(customer: customer)));
Kode lengkapnya seperti dibawah ini
Class Customer
class Customer { String name; String address; Customer(String name, String address){ this.name = name; this.address = address; } }
Class ReceiptData. Menerima data dari halaman sebelumnya.
import 'package:flutter/material.dart'; import 'customer.dart'; class ReceiptData extends StatefulWidget { // property final Customer customer; // constructor ReceiptData({Key key, @required this.customer}) : super(key: key); @override _ReceiptDataState createState() => _ReceiptDataState(customer: customer); } class _ReceiptDataState extends State<ReceiptData> { // property Customer customer; // constructor _ReceiptDataState({@required this.customer}) : super(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Receipt Data'), ), body: Center( child: Padding( padding: EdgeInsets.all(20), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text( 'Inserted Customer : ' ), Text( 'Name: ' + customer.name ), Text( 'Address: ' + customer.address ), ], ), ) ), ); } }
Class CollectData. Menerima input user kemudian kirim ke halaman ReceiptData.
import 'package:flutter/material.dart'; import 'customer.dart'; import 'receipt_data.dart'; class CollectData extends StatefulWidget { CollectData({Key key}) : super(key: key); @override _CollectDataState createState() => _CollectDataState(); } class _CollectDataState extends State<CollectData> { TextEditingController nameController = TextEditingController(); TextEditingController addressController = TextEditingController(); bool isValid(){ if(nameController.text.isEmpty){ return false; } if(addressController.text.isEmpty){ return false; } return true; } sendData() { if(isValid()){ // buat object customer dari input user Customer customer = Customer(nameController.text, addressController.text); // kirim object customer ke halaman ReceiptData Navigator.push(context, MaterialPageRoute(builder: (context) => ReceiptData(customer: customer))); }else{ return showDialog( context: context, builder: (context){ return AlertDialog( content: Text('Please fill all field.'), actions: <Widget>[ FlatButton( onPressed: () { Navigator.pop(context); }, child: Text('OK'), ), ], ); } ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Collect Data'), actions: <Widget>[ FlatButton( onPressed: (){ sendData(); }, child: Text( 'SEND', style: TextStyle( color: Colors.white ), ), ) ], ), body: Center( child: Padding( padding: EdgeInsets.all(20), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text( 'Name' ), TextField( controller: nameController, ), Padding( padding: EdgeInsets.only(top: 20), ), Text( 'Address' ), TextField( controller: addressController, ) ], ), ) ), ); } }
Menerima Data dari Halaman Baru
Dengan method Navigator.pop() kita bisa mengirim balik data ke halaman sebelumnya dengan cara memasukkan data tersebut sebagai argument. Seperti kode dibawah ini
// buat object customer dari input user Customer customer = Customer(nameController.text, addressController.text); // kirim object customer ke halaman sebelumnya Navigator.pop(context, customer);
Sedangkan pada halaman sebelumnya (halaman pemanggil) kita bisa menerima data dari halaman baru dengan kode dibawah ini
waitForData() async { // pada dasarnya method Navigator.push return Future // jadi kita bisa menggunakan await untuk mendapatkan data sebenarnya // bisa juga menggunakan method then() Customer customer = await Navigator.push(context, MaterialPageRoute(builder: (context) => SendDataBack())); String content = ''; // cek data yang diterima null atau tidak // jika user menekan tombol back data akan bernilai null if(customer != null){ content = 'You insert Name : ${customer.name} and Address : ${customer.address}'; }else{ content = 'Please insert Name and Address'; } return showDialog( context: context, builder: (context){ return AlertDialog( content: Text(content), actions: <Widget>[ FlatButton( onPressed: () { Navigator.pop(context); }, child: Text('OK'), ), ], ); } ); }