Navigation atau Navigasi merupakan sebuah proses perpindahan halaman
(screen/page) dari satu halaman ke halaman lain dalam sebuah aplikasi flutter. Misalkan perpindahan darihalaman login aplikasi ke halaman utama aplikasi ketika tombol login ditekan. Navigasi pada flutter menggunakan widget Navigator. Wiget navigator ini bekerja menggunakan konsep tumpukan (stack), Gambar berkut ini merupakan konsep dasar navigasi pada flutter.

Gambar di atas menjelaskan bahwa halaman aplikasi flutter akan beradapa pada posisi dasar, pada contoh di atas ialah Screen1. Ketika akan berpindah ke halaman baru, flutter akan menggunakan perintah push sehingga halaman baru berada di atas rumpukan halaman sebelumnya. Pada contoh di atas, ketika menggunakan perintah push, Screen 2 berada di atas Screen 1. Nah setelah kita berada di Screen 2 dan ingin kembali ke Screen 1 kita cukup menggunakan perintah pop maka Screen 1 akan naik ke tumpukan paling atas.
Setelah memahami konsep tumpukan pada flutter, pertanyaan selanjutnya ialah bagaimana cara memanggil sebuah halaman ?
Pada flutter untuk memanggil sebuah halaman kita dapat menggunakan routing. Routing atau Rute ialah sebuah sistem yang digunakan untuk mendefinisikan dan mengelola routes dalam aplikasi. Jika kita mendefinisikan route maka ketika memanggil sebuah halaman kita cukup memanggil nama route tersebut. Hal ini mempermudah dalam mengelola route tanpa harus membuat instance baru setiap kali akan memanggil suatu halaman.
Terdapat beberapa jenis routing pada flutter sebagai berikut;
Navigator (Anonymous Routes)
Routing jenis navigator ini menampilkan halaman dengan konsep tumpukan menggunakan animasi transisi ketika berpindah halaman. Untuk berpindah ke halaman baru pada navigator kita menggunakan BuildContext. BuildContext adalah “penanda lokasi” suatu widget di dalam pohon widget (widget tree) Flutter. Seperti pembahasan sebelumnya, pada navigator untuk berpindah halaman kita juga menggunakan push() dan pop().
contoh penggunaan Navigator pada method push dan pop
//Method push
Center(
child: ElevatedButton(
onPressed: () {
Navigator.push( //ini untuk push
context, //ini BuildContex milik home
MaterialPageRoute(builder: (context) => const ProductDetail()), //pada push kita mau menambahkan halaman baru ke tumpukan jadi kita harus menentukan halaman mana yang harus ditampilkan
);
},
child: const Text('Go to Product Detail'),
),
),
//Method pop
Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context),
},
child: const Text('Back to Product'),
),
),
Named Routes
Name Routed ini ialah sebuah cara memberi nama unik (string) pada setiap halaman/ routes di aplikasi flutter. Dengan begitu, ketika mau pindah halaman kita cukup memanggil nama rute itu, bukan langsung bikin instance class. Named Routes ini biasanya langsung kita definisikan di dalam MaterialApp (atau CupertinoApp kalau gaya iOS). Berikut contoh penggunaan NamedRoutes
//Definisikan Routes pada MaterialApp
return MaterialApp(
initialRoute: '/', //ini artinya route utama, menentukan halaman pertama(default) yang akan ditampilkan saat aplikasi dijalankann
routes: {
'/': (context) => const Product(), //Mendefinisikan bahwa route utama mengarah ke widget Product().
'/product_detail': (context) => const ProductDetail(), //Mendefinisikan route dengan nama '/product_detail'.
},
);
Setelah mendefinisikan routesnya pada MaterialApp kita bisa tinggal panggil routenya seperti berikut :
onPressed: () {
Navigator.pushNamed(context, '/product_detail');
},
Generated Routes
Generated Routes adalah cara dinamis untuk menangani navigasi di flutter. Jadi, dibanding kita nulis daftar routes secara statis di MaterialApp, kita bisa pakai fungsi callback(onGeneratedRoute) yang berfungsi untuk :
- Membuat halaman sesuai permintaan user
- Bawa parameter (misal ID produk, username, dll)
- Menangani kalau route yang diminta tidak ada (eror)
Jika dianalogikan website sebagai mall besar, generated routes itu seperti repsionis mall. Di mana kita bisa nanya sama repsionis toko baju A di mana, dan repsionis akan mengantarkan kita ke toko tersebut namun jika toko A tidak tersedia, maka repsionis akan bilang “Maaf toko tersebut tidak tersedia” (halaman error/404).
Kalo make routes biasa, itu seperti peta tetap di mall. Kita bisa pilih halaman utama (/), halaman detail produk (/product_detail). Tapi cuma itu aja yang kita ketahui dan disediakan, kalo kita nanya toko A di mana, maka di peta ga ada jadi kita akan bingung.
Untuk memahami lebih lanjut bisa lihat implementasi berikut
MaterialApp(
onGenerateRoute: (settings) { //Ini fungsi callback yang dipanggil setiap kali user navigasi ke suatu rute dengan
if (settings.name == '/detail') { //Cek dulu apakah user mau ke halaman /detail kalo iya proses blok ini.
final args = settings.arguments as Map;
return MaterialPageRoute(
builder: (context) => DetailPage(data: args['data']),
);
}
return MaterialPageRoute(builder: (context) => NotFoundPage());
},
);
Setelah kita memahami routing mari kita memahami jenis-jenis method navigation
Push dan Pop
// Push - Menambahkan halaman baru
Navigator.push(context, route);
// Pop - Kembali ke halaman sebelumnya
Navigator.pop(context);
// Pop dengan mengirim data kembali
Navigator.pop(context, 'data yang dikembalikan');
Push Replacement
Mengganti halaman sekarang dengan halaman baru. Halaman sebelumnya dihapus dari stack, jadi nggak bisa balik lagi dengan tombol “back”.
// Mengganti halaman saat ini dengan halaman baru
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginPage()),
);
Push and Remove Until
Mengganti halaman sekarang dengan halaman baru, dan menghapus semua halaman sebelumnya sampai kondisi tertentu. Kalau pakai (route) => false, berarti hapus semua.
// Menghapus semua halaman sebelumnya
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => HomePage()),
(route) => false, // Hapus semua
);
Cara Mengirim dan Menerima Data
Mengirim data
// Dengan constructor
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPage(id: 123, name: 'Masnoer'),
),
);
// Dengan named routes dan arguments
Navigator.pushNamed(
context,
'/detail',
arguments: {'id': 123, 'name': 'Celly'},
);
Menerima data dengan constructor
class DetailPage extends StatelessWidget {
final int id;
final String name;
DetailPage({required this.id, required this.name});
@override
Widget build(BuildContext context) {
// Atau ambil dari arguments
final args = ModalRoute.of(context)!.settings.arguments as Map;
return Scaffold(
appBar: AppBar(title: Text('Detail: $name')),
body: Text('ID: $id'),
);
}
}
Menerima data Menggunakan ModalRoute.of(context)
class DetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Ambil arguments dari route
final args = ModalRoute.of(context)!.settings.arguments as Map;
// Akses data
final int id = args['id'];
final String name = args['name'];
return Scaffold(
appBar: AppBar(title: Text('Detail Page')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('ID: $id', style: TextStyle(fontSize: 20)),
Text('Name: $name', style: TextStyle(fontSize: 20)),
],
),
),
);
}
}
Setelah memahami beberapa hal di atas kita akan coba melakukan implementasi di program.
A. Multiple Screen
Berikut kode program lengkap untuk navigation dan routing
import 'package:flutter/material.dart';
void main() => runApp(const MyNav());
class MyNav extends StatelessWidget {
const MyNav({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const Product(),
'/product_detail': (context) => const ProductDetail(),
},
);
}
}
//BIKIN CLASS PRODUCT
class Product extends StatelessWidget {
const Product({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Product'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/product_detail'); //memanggil halaman product detail
},
child: const Text('Go to product detail'),
),
),
);
}
}
class ProductDetail extends StatelessWidget {
const ProductDetail({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Product Detail'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Back to product'),
),
),
);
}
}
B. Mengirim dan Menerima Data
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(
debugShowCheckedModeBanner: false,
initialRoute: '/',
routes: { //make route di initialapp
'/': (context) => const HomePage(),
'/product': (context) => const MyProduct(),
},
);
}
}
//CLASS HOMEPACKED
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Kirim data lewat Constructor
ElevatedButton(
onPressed: () {
Navigator.push( //pushnya menambahkan halaman baru
context,
MaterialPageRoute(
builder: (context) => //generated routes
const MyProfile(id: 1, name: 'Sherly'), //ini tu make namerute
),
);
},
child: const Text('Profile'),
),
const SizedBox(height: 20),
// Kirim data lewat Named Routes + arguments
ElevatedButton(
onPressed: () {
Navigator.pushNamed(
context,
'/product',
arguments: {'id': 101, 'name': 'Laptop'},
);
},
child: const Text('Product'),
),
],
),
),
);
}
}
// ============ PROFILE PAGE (terima data lewat Constructor) ============
class MyProfile extends StatelessWidget {
final int id;
final String name;
const MyProfile({super.key, required this.id, required this.name});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Profile'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('ID: $id'),
Text('Name: $name'),
],
),
),
);
}
}
// ============ PRODUCT PAGE (terima data lewat ModalRoute) ============
class MyProduct extends StatelessWidget {
const MyProduct({super.key});
@override
Widget build(BuildContext context) {
// Ambil arguments yang dikirim dari HomePage
final args =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>?;
final int id = args?['id'] ?? 0;
final String name = args?['name'] ?? 'Unknown';
return Scaffold(
appBar: AppBar(
title: const Text('Product'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Product ID: $id'),
Text('Product Name: $name'),
],
),
),
);
}
}
C. Tugas 1
Buatlah halaman login dan halaman utama, kemudian inputkan username dan password,
Ketika diklik login akan berpindah ke halaman utama dengan mengirimkan data username
dan password, tampilkan data tersebut pada halaman utama.
Jawab :
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: 'Tugas Login',
debugShowCheckedModeBanner: false,
initialRoute: '/login',
onGenerateRoute: (settings) {
if (settings.name == '/home') {
// ambil data dari arguments
final args = settings.arguments as Map<String, String>;
return MaterialPageRoute(
builder: (context) => HomePage(
username: args['username']!,
password: args['password']!,
),
);
}
// default route = login
return MaterialPageRoute(builder: (context) => const LoginPage());
},
);
}
}
// =================== HALAMAN LOGIN ===================
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
String _username = '';
String _password = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
decoration: const InputDecoration(
labelText: 'Username',
border: OutlineInputBorder(),
),
validator: (value) =>
value == null || value.isEmpty ? 'Enter username' : null,
onSaved: (value) => _username = value!,
),
const SizedBox(height: 16),
TextFormField(
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
obscureText: true,
validator: (value) =>
value == null || value.isEmpty ? 'Enter password' : null,
onSaved: (value) => _password = value!,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// Navigasi ke halaman utama dengan data
Navigator.pushNamed(
context,
'/home',
arguments: {
'username': _username,
'password': _password,
},
);
}
},
child: const Text('Login'),
),
],
),
),
),
);
}
}
class HomePage extends StatelessWidget {
final String username;
final String password;
const HomePage({super.key, required this.username, required this.password});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Halaman Utama')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Username: $username', style: const TextStyle(fontSize: 20)),
Text('Password: $password', style: const TextStyle(fontSize: 20)),
],
),
),
);
}
}


D. Tugas 2
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _selectedIndex = 0;
// Daftar halaman (pakai class terpisah)
final List<Widget> _pages = [
HomeScreen(),
SearchScreen(),
ProfileScreen(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Contoh Bottom Navigation")),
body: _pages[_selectedIndex], // halaman berubah sesuai index
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onItemTapped,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
// 🔹 Halaman Home
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text('Ini Halaman Home', style: TextStyle(fontSize: 24)),
);
}
}
// 🔹 Halaman Search
class SearchScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text('Ini Halaman Search', style: TextStyle(fontSize: 24)),
);
}
}
// 🔹 Halaman Profile
class ProfileScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text('Ini Halaman Profile', style: TextStyle(fontSize: 24)),
);
}
}
