Lewati ke isi

Routing Modern dengan go_router


Tujuan Pembelajaran

Setelah mengikuti pembelajaran ini, siswa mampu:
1. Memahami cara kerja perpindahan halaman di Flutter
2. Menjelaskan konsep URL dan sub-URL dalam routing
3. Mengirim data antar halaman dengan beberapa cara
4. Mengelola navigasi halaman dengan go_router
5. Menerapkan praktik routing yang rapi dan umum digunakan di industri


Pertanyaan Pemantik

  • Pernahkah kalian membuka aplikasi lalu tiba-tiba kembali ke halaman login tanpa alasan jelas?
  • Menurut kalian, bagaimana aplikasi besar mengatur alur pindah halaman agar tidak berantakan?

    Diskusikan singkat selama 5 menit.


1. Konsep Perpindahan Halaman di Flutter

Pada Flutter, perpindahan halaman bukan mengganti halaman lama, tetapi menumpuk halaman baru di atas halaman lama.

Contoh dengan Navigator klasik:

Navigator.push(context, MaterialPageRoute(builder: (_) => PageB()));

Yang terjadi:
- Halaman A masih ada di memori
- Halaman B ditumpuk di atasnya
- Tombol back akan menghapus (pop) halaman B

Perumpamaan:
Tumpukan buku.
Buku baru diletakkan di atas, bukan mengganti buku lama.

Masalah:
- Jika aplikasi besar, push/pop tersebar
- Sulit mengatur alur login, logout, dan deep link


2. Routing Berbasis URL

go_router memperkenalkan cara berpikir berbasis URL, mirip aplikasi web.

Contoh:
- /products → halaman daftar produk
- /products/detail → halaman detail produk
- /products/123 → detail produk dengan ID tertentu

Dengan URL:
- Setiap halaman punya alamat jelas
- Mudah untuk deep link dan debugging


3. Struktur URL: URL Inti dan Sub URL

Contoh struktur yang sering dipakai:
- URL inti: /products
- Sub URL: /products/:id

Artinya:
- /products menampilkan daftar produk
- /products/:id menampilkan detail satu produk

Struktur ini disebut nested route dan umum dipakai di perusahaan.


4. Pengiriman Data Antar Halaman

A. Mengirim 1 Data (ID / String / Angka)

Biasanya menggunakan path parameter.

Contoh:
- URL: /products/123
- Data yang dikirim: 123 (ID produk)

Digunakan untuk:
- deep link
- data kecil dan penting


B. Mengirim Banyak Data (Object / List)

Menggunakan extra.

Catatan penting:
- extra tidak terlihat di URL
- Cocok untuk data kompleks
- Untuk web, tetap kombinasikan dengan ID


C. Query Parameter

Contoh:
- /products?category=audio&sort=price

Digunakan untuk:
- filter
- sorting
- pencarian


5. Cara Pindah Halaman di go_router

A. go()

Digunakan untuk:
- pindah menu utama
- reset alur halaman
- setelah login / logout


B. push()

Digunakan untuk:
- masuk ke halaman detail
- halaman yang wajar untuk kembali (back)


C. replace()

Digunakan untuk:
- mengganti halaman saat ini
- mencegah user kembali ke halaman sebelumnya


6. Kembali ke Halaman Sebelumnya

context.pop();

7. Mengirim Data Kembali ke Halaman Sebelumnya

Digunakan untuk:
- form input
- halaman edit data

Data bisa berupa:
- boolean
- string
- object


8. Best Practice Routing di Dunia Kerja

  1. Routing terpusat di satu file
  2. Jangan hardcode path
  3. Gunakan path parameter untuk deep link
  4. Bedakan penggunaan go dan push
  5. Sediakan halaman error (404)
  6. Konsisten menggunakan satu sistem routing

Persiapan

Gunakan project dari Pertemuan 1:
smk_product_app


1. Menambahkan Dependency go_router

Buka file pubspec.yaml dan tambahkan:

dependencies:
  flutter:
    sdk: flutter
  go_router: ^14.0.0

Jalankan perintah:

flutter pub get


2. Menyiapkan Struktur Routing

Buat folder dan file berikut:

lib/
 ├── routes/
 │   ├── app_routes.dart
 │   └── app_router.dart
 ├── pages/
 ├── widgets/
 ├── models/
 └── main.dart

Tujuan:
- app_routes.dart untuk konstanta path dan nama route
- app_router.dart untuk konfigurasi GoRouter


3. Buat Konstanta Route

File: lib/routes/app_routes.dart

class AppRoutes {
  // Nama route (untuk goNamed/pushNamed)
  static const String productsName = 'products';
  static const String productDetailName = 'product-detail';

  // Path route (hindari hardcode di banyak tempat)
  static const String productsPath = '/products';
  static const String productDetailPath = ':id'; // nested di bawah /products
}

Penjelasan singkat:
- Kita akan menempatkan detail sebagai nested route:
- List: /products
- Detail: /products/:id


4. Membuat Konfigurasi Router

File: lib/routes/app_router.dart

import 'package:go_router/go_router.dart';
import '../pages/home_page.dart';
import '../pages/detail_page.dart';
import 'app_routes.dart';

final GoRouter appRouter = GoRouter(
  initialLocation: AppRoutes.productsPath,
  routes: [
    GoRoute(
      name: AppRoutes.productsName,
      path: AppRoutes.productsPath,
      builder: (context, state) => const HomePage(),
      routes: [
        GoRoute(
          name: AppRoutes.productDetailName,
          path: AppRoutes.productDetailPath, // ':id'
          builder: (context, state) {
            final id = state.pathParameters['id']!;
            return DetailPage(productId: id);
          },
        ),
      ],
    ),
  ],
);

Catatan penting:
- Di sini, halaman detail menerima productId, bukan object produk.
- Nanti saat masuk API/Provider, detail akan mengambil data berdasarkan id.


5. Mengubah main.dart

File: lib/main.dart

Ganti MaterialApp(...) menjadi:

import 'package:flutter/material.dart';
import 'routes/app_router.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    final colorScheme = ColorScheme.fromSeed(
      seedColor: const Color(0xFF6D28D9),
      brightness: Brightness.light,
    );

    return MaterialApp.router(
      debugShowCheckedModeBanner: false,
      title: 'SMK Product App',
      routerConfig: appRouter,
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: colorScheme,
        appBarTheme: AppBarTheme(
          centerTitle: false,
          backgroundColor: colorScheme.primary,
          foregroundColor: colorScheme.onPrimary,
        ),
        cardTheme: const CardTheme(
          elevation: 0,
          margin: EdgeInsets.zero,
        ),
      ),
    );
  }
}

6. Sesuaikan DetailPage Agar Menerima productId

Karena router sekarang mengirim productId, ubah halaman detail.

File: lib/pages/detail_page.dart

A. Ubah constructor

Sebelumnya: - menerima Product product

Sekarang: - menerima String productId

Contoh:

final String productId;

const DetailPage({
  super.key,
  required this.productId,
});

B. Ambil data produk berdasarkan id (masih dummy)

Agar data Home dan Detail konsisten, pindahkan dummy data ke satu tempat.

Buat:

lib/
 └── data/
     └── dummy_products.dart

File: lib/data/dummy_products.dart

import '../models/product.dart';

const List<Product> dummyProducts = [
  Product(
    id: "p1",
    name: "Keyboard Mechanical Aurora",
    category: "Aksesoris",
    price: 349000,
    imageUrl: "https://picsum.photos/seed/keyboard/800/600",
    rating: 4.6,
    description:
        "Keyboard mechanical dengan switch empuk, backlight RGB, dan build quality kokoh. Cocok untuk coding dan gaming, nyaman dipakai lama.",
  ),
  // Tambahkan produk lain seperti pertemuan 1
];

Di detail_page.dart, import dan cari produk:

import '../data/dummy_products.dart';
import '../models/product.dart';

final Product product = dummyProducts.firstWhere((p) => p.id == productId); // taruh kode ini diatas return scaffold

Setelah itu, lanjutkan UI detail seperti sebelumnya menggunakan product.


7. Update HomePage: Pindahkan Navigasi ke go_router

File: lib/pages/home_page.dart

A. Import

Tambahkan:

import 'package:go_router/go_router.dart';
import '../routes/app_routes.dart';
import '../data/dummy_products.dart';

B. Gunakan dummyProducts dari file data

Ganti sumber data dari fungsi _dummyProducts() menjadi:

final products = dummyProducts;

C. Navigasi ke detail (pushNamed + pathParameters)

Ganti Navigator.push(...) menjadi:

context.pushNamed(
  AppRoutes.productDetailName,
  pathParameters: {'id': item.id},
);

Checklist Pengujian

Pastikan:
1. Aplikasi masuk ke /products
2. Klik produk → detail terbuka
3. Tombol back → kembali ke list
4. Tidak ada Navigator.push untuk halaman utama


Tugas

Tugas Level Dasar

  1. Tambahkan route baru: /profile
  2. Buat halaman ProfilePage
  3. Navigasikan dari Home ke Profile menggunakan go_router

Tugas Level Industri

  1. Tambahkan halaman error (404) ketika route tidak ditemukan
  2. Pastikan tidak ada hardcode path string di halaman (gunakan konstanta route)
  3. Uji 3 skenario:
  4. buka /products
  5. buka /products/p1
  6. buka /products/route-salah → harus masuk error page

Penutup

Hari ini kalian memindahkan aplikasi dari navigasi manual menjadi routing terpusat.

Ini pondasi penting sebelum masuk:
- state management (Provider)
- API
- Firebase