Belajar Flutter - Navigation & App Architecture
Episode 11 of 23

Belajar Flutter - Navigation & App Architecture

Episode ini mengorganisasi pergerakan antar layar: perbedaan Navigator 1.0 dan Navigator 2.0, named routes dan nested routes, deep linking, modular app architecture dengan feature modules, serta code organization dan separation of concerns.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Saat aplikasi memiliki puluhan layar, Navigator.push satu per satu menjadi tidak terkelola. Episode 11 membangun sistem navigasi yang jelas dan arsitektur kode yang mendukung pertumbuhan: perbedaan Navigator 1.0 dan 2.0, named routes dan nested routes, deep linking, modular architecture dengan feature modules, serta separation of concerns.

Imperatif vs Deklaratif

Navigator 1.0 bersifat imperatif: kalian memanggil push dan pop langsung. Navigasi 2.0 bersifat deklaratif: URL atau state menentukan tampilan, dan framework menyesuaikan stack secara otomatis.

Navigator 2.0 lebih kuat — mendukung deep linking dan kontrol penuh atas stack — tetapi boilerplate-nya berat. Solusi pragmatis yang banyak dipakai komunitas adalah package go_router yang membungkus Navigator 2.0 dengan API sederhana:

Memasang go_router
flutter pub add go_router

Memilih Pendekatan

Untuk aplikasi dengan navigasi sederhana, Navigator 1.0 dengan named routes masih valid. Untuk aplikasi yang butuh deep linking, state persistence, atau banyak nested flow, go_router adalah pilihan yang paling seimbang.

Named Routes, Nested Routes, dan Deep Linking

Named Routes dengan Navigator 1.0

Daftarkan rute sekali di MaterialApp, lalu navigasi pakai nama:

Named routes di MaterialApp
MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => const HomeScreen(),
    '/detail': (context) => const DetailScreen(),
    '/profil': (context) => const ProfilScreen(),
  },
)

Navigasi memakai nama rute:

Pindah lewat nama rute
Navigator.pushNamed(context, '/detail');

Navigator.pushNamed(context, '/detail') memindahkan pengguna ke rute yang terdaftar. Nama rute memisahkan keputusan "ke mana" dari detail widget, sehingga layar bisa diatur ulang tanpa menyentuh setiap call site.

Deep Linking dengan go_router

Dengan go_router, rute didefinisikan sebagai path yang juga bisa dibuka dari luar aplikasi:

Router dengan path
final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
    GoRoute(
      path: '/produk/:id',
      builder: (context, state) {
        return ProdukScreen(id: state.pathParameters['id']!);
      },
    ),
  ],
);

state.pathParameters['id'] mengambil parameter dari URL /produk/123. Karena path-nya URL nyata, rute yang sama bisa di-buka lewat deep link dari luar aplikasi — pola penting untuk notification dan share link.

Nested Routes

go_router mendukung routes bersarang melalui ShellRoute, yang memungkinkan navigasi di dalam tab atau drawer tanpa mengunci seluruh halaman. Ini cara standar membangun aplikasi dengan bottom navigation yang tiap tab-nya punya stack sendiri.

Modular App Architecture dan Feature Modules

Pecah Project per Fitur

Alih-alih folder per tipe (screens/, widgets/, models/), pecah per fitur. Setiap fitur berisi lapisan yang dibutuhkannya sendiri:

  • features/auth/ — model, repository, controller, dan layar login.
  • features/home/ — layar beranda dan widget terkait.
  • features/cart/ — state keranjang dan tampilannya.
Struktur project per fitur
lib/
  features/
    auth/
      auth_screen.dart
      auth_controller.dart
      auth_repository.dart
    home/
      home_screen.dart
  core/
    theme/
    widgets/
    network/
  main.dart

Struktur di atas memisahkan features (kode bisnis) dari core (kode bersama). Perubahan pada satu fitur tidak menyentuh fitur lain, dan on-boarding developer baru lebih cepat karena konteks berdekatan.

Code Organization dan Separation of Concerns

Lapisan yang Jelas

Bahkan dalam satu fitur, pisahkan tanggung jawab:

  • Presentation: widget dan layar — hanya tahu cara menampilkan.
  • Logic: controller atau state — menghubungkan UI dengan data.
  • Data: repository dan model — satu-satunya yang menyentuh network dan storage.
Repository memisahkan akses data
class AuthRepository {
  final http.Client client;
 
  const AuthRepository(this.client);
 
  Future<AuthSession> login(String email, String password) async {
    final response = await client.post(
      Uri.parse('https://api.example.com/login'),
      body: {'email': email, 'password': password},
    );
    return AuthSession.fromJson(response.body);
  }
}

AuthRepository menyembunyikan detail HTTP dari UI. Layar cukup memanggil repository.login(...), tanpa tahu bagaimana request dibentuk — ini separation of concerns dalam praktik.

Hindari God Object

Jangan taruh semua fungsi di satu file utils.dart atau satu controller raksasa. Setiap class harus punya satu alasan untuk berubah. Disiplin ini terasa berlebihan di project kecil, tetapi menjadi penentu di project berskala production.

Penutup

Inti yang harus dibawa pulang:

  • Navigator 1.0 imperatif untuk alur sederhana; Navigator 2.0 deklaratif untuk kendali penuh.
  • go_router membungkus Navigator 2.0 dengan API sederhana dan dukungan deep linking.
  • Named routes memisahkan keputusan navigasi dari widget.
  • state.pathParameters membaca parameter dari path URL.
  • Organisasi per fitur: setiap modul punya presentation, logic, dan data sendiri.
  • Pisahkan repository dari UI agar detail akses data tersembunyi.

Di episode 12 selanjutnya kita membahas platform integration dan plugins — menggunakan platform channels untuk fungsionalitas native, mengintegrasikan device API seperti camera, location, dan sensors, pengembangan custom plugin, serta pengelolaan kompatibilitas plugin dan kode spesifik platform. Aplikasi kalian mulai menyentuh kemampuan perangkat keras.

Belajar Flutter - Navigation & App Architecture | Belajar Flutter