Untuk teman-teman yang akan baru memulai belajar Flutter silahkan masuk ke artikel ini dulu ya teman-teman ➡ Aplikasi Pertamaku “Halo Semuaaa…“. Jika sudah yuk lanjut baca artikel ini…
Jangan lupa baca artikel sebelumnya ya teman-teman ➡ Membuat Download Button
Perlu diketahui, aplikasi mengumpulkan ratusan route selama operasinya berjalan. Beberapa route pasti didefinisikan sebagai top-level route, misalnya seperti, “/“, “profile”, “contact”, “social_feed”. Namun dapat teman-teman bayangkan semua route tersebut didefinisikan di dalam top-level widget Navigator
, sehingga list-nya akan sangat banyak dan banyak dari route ini akan lebih baik ditangani di dalam widget lain.
Pada artikel ini, kita akan membuat sebuah alur aplikasi IoT untuk setup bola lampu nirkabel yang nantinya dapat kita kontrol melalui aplikasi. Alur setup ini terdiri dari 4 pages:
- Temukan bola lampu terdekat
- Pilih bola lampu yang ingin ditambahkan
- Tambahkan bola lampu
- Selesaikan setup
Kita dapat mengatur behavior aplikasi dari top-level widget Navigator
. Namun, akan lebih masuk akal jika mendefinisikan nested widget Navigator
kedua di dalam widget SetupFlow
dan biarkan nested Navigator
mengambil alih kepemilikan atas 4 halaman pada alur setup. Penunjukan navigasi ini memfasilitasi kontrol lokal yang lebih besar, yang umumnya lebih disukai saat mengembangkan software. Berikut langkah-langkah yang akan dibahas pada penelitian ini:
1. Mempersiapkan Navigasi
Aplikasi IoT memiliki 2 top-level screens bersamaan dengan alur setup. Perlu diketahui, pendefinisian nama route sebagai konstanta di bawah ini bertujuan agar dapat dijadikan referensi dalam kode aplikasi yang dibuat.
const routeHome = '/'; const routeSettings = '/settings'; const routePrefixDeviceSetup = '/setup/'; const routeDeviceSetupStart = '/setup/$routeDeviceSetupStartPage'; const routeDeviceSetupStartPage = 'find_devices'; const routeDeviceSetupSelectDevicePage = 'select_device'; const routeDeviceSetupConnectingPage = 'connecting'; const routeDeviceSetupFinishedPage = 'finished';
Home dan setting screens direferensikan dengan nama statis. Alur setup menggunakan dua path supaya alur setup dapat membuat nama route mereka sendiri ('/setup/'
awalannya dapat diikuti dengan nama halaman tertentu). Selanjutnya dengan menggabungkan dua path, Navigator
dapat menentukan nama route untuk alur setup tanpa harus mengenali semua individual pages yang terkait dengan alur setup.
Top-level Navigator
tidak bertanggung jawab untuk mengidentifikasi halaman alur setup individu, oleh karena itu top-level Navigator
perlu mengurai nama route yang masuk untuk mengidentifikasi awalan alur setup. Kebutuhan untuk mengurai ini berarti kita tidak menggunakan properti route dari Top-level Navigator
, sebagai gantinya kita harus menyediakan fungsi untuk properti onGenerateRoute
.
Cuplikan berikut mengimplementasikan onGenerateRoute
untuk mengembalikan widget yang sesuai untuk masing-masing three top-level paths yang ada.
onGenerateRoute: (settings) { late Widget page; if (settings.name == routeHome) { page = HomeScreen(); } else if (settings.name == routeSettings) { page = SettingsScreen(); } else if (settings.name!.startsWith(routePrefixDeviceSetup)) { final subRoute = settings.name!.substring( routePrefixDeviceSetup.length, ); page = SetupFlow( initialSetupRoute: subRoute, ); } else { throw Exception('Unknown route: ${settings.name}'); } return MaterialPageRoute<dynamic>( builder: (context) { return page; }, settings: settings, ); },
Perhatikan bahwa route home dan settings dicocokkan dengan nama route yang tepat. Namun, kondisi setup flow route hanya memeriksa prefiks. Jika nama route berisi setup flow prefiks, maka nama route lainnya akan diabaikan dan diteruskan ke widget SetupFlow
untuk diproses. Pemisahan nama route inilah yang memungkinkan top-level Navigator
menjadi agnostik terhadap berbagai subroute dalam alur setup.
Kemudian buat widget stateful bernama SetupFlow
untuk menerima nama route.
class SetupFlow extends StatefulWidget { const SetupFlow({ Key? key, required this.setupPageRoute, }) : super(key: key); final String setupPageRoute; @override SetupFlowState createState() => SetupFlowState(); } class SetupFlowState extends State<SetupFlow> { //... }
2. Menampilkan App Bar untuk Alur Setup
Pada langkah ini kita akan mengatur app bar akan tetap muncul di semua halaman alur setup. Return widget Scaffold
dari widget’s SetupFlow
pada method build()
dan sertakan widget AppBar
yang diinginkan.
@override Widget build(BuildContext context) { return Scaffold( appBar: _buildFlowAppBar(), body: SizedBox(), ); } PreferredSizeWidget _buildFlowAppBar() { return AppBar( title: Text('Bulb Setup'), ); }
Selanjutnya app bar menampilkan ikon kembali dan keluar dari alur setup saat ikon kembali ditekan. Namun, jika pengguna keluar dari alur setup maka semua setup yang telah dilakukan hilang. Oleh karena itu ketika pengguna menekan ikon penggali, aplikasi akan melakukan konfirmasi apakah pengguna benar ingin keluar dari alur setup.
Future<void> _onExitPressed() async { final isConfirmed = await _isExitDesired(); if (isConfirmed && mounted) { _exitSetup(); } } Future<bool> _isExitDesired() async { return await showDialog<bool>( context: context, builder: (context) { return AlertDialog( title: Text('Are you sure?'), content: Text('If you exit device setup, your progress will be lost.'), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(true); }, child: Text('Leave'), ), TextButton( onPressed: () { Navigator.of(context).pop(false); }, child: Text('Stay'), ), ], ); }) ?? false; } void _exitSetup() { Navigator.of(context).pop(); } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: _isExitDesired, child: Scaffold( appBar: _buildFlowAppBar(), body: SizedBox(), ), ); } PreferredSizeWidget _buildFlowAppBar() { return AppBar( leading: IconButton( onPressed: _onExitPressed, icon: Icon(Icons.chevron_left), ), title: Text('Bulb Setup'), ); }
Jadi ketika user menekan ikon kembali pada app bar atau menekan tombol kembali pada Android, maka akan muncul pesan peringatan untuk mengkonfirmasi bahwa apakah benar pengguna ingin keluar dari alur setup. Jika pengguna menekan Leave, alur setup akan muncul dengan sendirinya dari top-level navigation stack. Kemudian jika pengguna menekan Stay, maka semua tindakan menuju keluar alur setup diabaikan.
Teman-teman mungkin memperhatikan kalau Navigator.pop()
dipanggil oleh kedua tombol Leave dan Stay, padahal sebenarnya action pop()
yang memunculkan pesan peringatan dari navigation stack dan bukan dari alur setup.
3. Membuat Nested Routes
Seperti yang teman-teman ketahui tugas dari alur setup adalah menampilkan halaman yang sesuai dengan alur tersebut. Dalam hal ini kita tambahkan widget Navigator
ke SetupFlow
dan implementasikan properti onGenerateRoute
.
final _navigatorKey = GlobalKey<NavigatorState>(); void _onDiscoveryComplete() { _navigatorKey.currentState!.pushNamed( routeDeviceSetupSelectDevicePage, ); } void _onDeviceSelected(String deviceId) { _navigatorKey.currentState!.pushNamed( routeDeviceSetupConnectingPage, ); } void _onConnectionEstablished() { _navigatorKey.currentState!.pushNamed( routeDeviceSetupFinishedPage, ); } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: _isExitDesired, child: Scaffold( appBar: _buildFlowAppBar(), body: Navigator( key: _navigatorKey, initialRoute: widget.setupPageRoute, onGenerateRoute: _onGenerateRoute, ), ), ); } Route _onGenerateRoute(RouteSettings settings) { late Widget page; switch (settings.name) { case routeDeviceSetupStartPage: page = WaitingPage( message: 'Searching for nearby bulb...', onWaitComplete: _onDiscoveryComplete, ); break; case routeDeviceSetupSelectDevicePage: page = SelectDevicePage( onDeviceSelected: _onDeviceSelected, ); break; case routeDeviceSetupConnectingPage: page = WaitingPage( message: 'Connecting...', onWaitComplete: _onConnectionEstablished, ); break; case routeDeviceSetupFinishedPage: page = FinishedPage( onFinishPressed: _exitSetup, ); break; } return MaterialPageRoute<dynamic>( builder: (context) { return page; }, settings: settings, ); }
Function _onGenerateRoute
berfungsi sama seperti top-level Navigator
. Objek RouteSettings
diteruskan ke fungsi _onGenerateRoute
yang menyertakan routes name
. Berdasarkan nama route tersebut, satu dari empat halaman alur akan di-returned.
Halaman pertama disebut find_devices
, menampilkan animasi pencarian jaringan dengan jeda beberapa detik. Setelah itu melakukan callback _onDiscoveryComplete
sehingga alur setup mengetahui bahwa pencarian perangkat telah selesai dan halaman untuk memilih perangkat harus ditampilkan. Oleh karena itu, _onDiscoveryComplete
, _navigatorKey
menginstruksikan nested Navigator
untuk menampilkan halaman select_device
.
Selanjutnya halaman select_device
meminta pengguna untuk memilih perangkat dari daftar perangkat yang tersedia. Dalam artikel ini, hanya satu perangkat yang ditampilkan ke pengguna, lalu saat pengguna mengetuk perangkat yang ada di daftar perangkat maka callback onDeviceSelected
dipanggil. Saat alur setup mengetahui perangkat yang dipilih, sehingga halaman connecting harus ditampilkan. Dalam hal ini, _onDeviceSelected
dan _navigatorKey
menginstruksikan nested Navigator
untuk membuka halaman ”connecting”
.
Kemudian untuk halaman connecting
, cara kerjanya hampir sama seperti halaman find_devices
. Halaman connecting
menunggu beberapa saat kemdian memanggil callback _onConnectionEstablished
. Alur setup akan mengenali hal tersebut ketika koneksi terjadi dan kemudian halaman final harus ditampilkan. Oleh karena itu, _onConnectionEstablished
dan _navigatorKey
menginstruksikan nested Navigator
untuk membuka halaman finished
.
Halaman finished
memberikan pengguna tombol Finish, ketika pengguna mengetuk tombol tersebut callback _exitSetup
dipanggil sehingga menampilkan seluruh alur setup dari top-level Navigator
stack dan membawa pengguna ke layar utama aplikasi.
Berikut cuplikan kode dan simulasinya, jika teman-teman menggunakan VSCode jalankan projectnya dengan menekan F5, klik hot reload (⚡) atau klik tombol ▶, berikut tampilannya:
Jika ada pertanyaan silahkan komen dan jika artikel ini dirasa bermanfaat, jangan lupa like dan sharenya ya teman-teman. ??????? Sampai bertemu di artikel selanjutnya.
Terima Kasih, Assalamu’alaykum… Salam KODINGINDONESIA
Referensi : https://flutter.dev//