Tutorial May 27, 2026 · 6 min read · 1 dilihat

Belajar Flutter #3: Navigasi & State Management Provider

Setelah Episode 2 menguasai widget dasar, saatnya kita buat app multi-halaman yang proper. Navigasi di Flutter sebenarnya tidak serumit yang dibayangkan — cukup pahami konsep Navigator dan bagaimana push/pop bekerja, maka kamu sudah 80% menguasai flow app. Artikel ini juga akan membahas Provider, library state management paling beginner-friendly di ekosistem Flutter. Dengan Provider, state yang dibagikan antar halaman jadi simple dan tidak bikin kode berantakan. Mari kita mulai membuat app nyata dengan 3 halaman yang saling terhubung.

IKHSAN MAULANA

IKHSAN MAULANA

Web, Android, and RPA Development

Belajar Flutter #3: Navigasi & State Management Provider

Navigasi dan state management adalah dua pilar utama saat membangun aplikasi Flutter yang lebih kompleks dari sekadar dummy UI. Di Episode 2, kita belajar struktur widget dan layout — sekarang giliran menjawab pertanyaan: bagaimana caranya pindah ke halaman lain? Bagaimana data bisa diakses dari beberapa halaman sekaligus?

Kabar baiknya, Flutter menyediakan solusi built-in yang cukup straightforward. Kombinasi Navigator (untuk routing) dan Provider (untuk state) akan membuat architecture app kamu jauh lebih rapi dan scalable.

Recap Cepat: Widget Hierarchy dari Episode 2

Sebelum masuk navigasi, let's do a quick reminder. Di Flutter, setiap UI adalah widget — ada StatelessWidget (statis, tidak berubah) dan StatefulWidget (punya internal state). Episode 2 kita sudah bahas Scaffold, AppBar, FloatingActionButton, dan basic layout dengan Column/Row. Semua itu jadi building block untuk halaman-halaman yang akan kita navigasi sekarang.

Yang perlu dicatat: setiap halaman biasanya adalah satu StatefulWidget atau StatelessWidget. Ketika user menekan tombol, kita akan "push" halaman baru ke navigation stack. Ketika user tekan back, halaman itu "pop" dari stack. Ini adalah konsep fundamental yang perlu kamu pahami sebelum mulai koding.

Navigator 101: Push, Pop, dan Routing Sederhana

Flutter menyediakan Navigator class yang menghandle stack halaman. Bayangkan seperti tumpukan kartu — kartu teratas adalah halaman yang kita lihat sekarang. Ketika kita "push" halaman baru, kartu baru diletakkan di atas. Ketika "pop", kartu teratas dihilangkan dan kita kembali ke halaman sebelumnya.

Ada dua cara navigasi di Flutter: Navigator klasik (push/pop) dan Navigator 2.0 (named routes dengan routing layer yang lebih advanced). Untuk pemula, push/pop sudah cukup dan lebih mudah dimengerti. Mari kita mulai dari situ.

Untuk push ke halaman baru, gunakan:

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

Untuk pop kembali ke halaman sebelumnya:

Navigator.pop(context);

Sederhana, kan? context di sini adalah BuildContext dari widget saat ini — ia memberitahu Navigator halaman mana yang sekarang aktif dan di mana harus push/pop.

Contoh App 3 Halaman: Home, Profile, dan Settings

Agar lebih konkret, mari buat app dengan 3 halaman yang saling terhubung. Halaman Home punya 2 tombol: "Ke Profile" dan "Buka Settings". Halaman Profile menampilkan nama user (dari Provider) dan tombol kembali. Halaman Settings punya toggle untuk mengubah tema, dan state itu di-share ke Home via Provider.

Berikut struktur file:

lib/
  main.dart
  pages/
    home_page.dart
    profile_page.dart
    settings_page.dart
  providers/
    theme_provider.dart

Mari buat main.dart dulu:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/theme_provider.dart';
import 'pages/home_page.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => ThemeProvider(),
      child: Consumer(
        builder: (context, themeProvider, child) {
          return MaterialApp(
            title: 'Flutter Navigation Demo',
            theme: ThemeData.light(),
            darkTheme: ThemeData.dark(),
            themeMode: themeProvider.isDarkMode ? ThemeMode.dark : ThemeMode.light,
            home: const HomePage(),
          );
        },
      ),
    );
  }
}

Sekarang providers/theme_provider.dart:

import 'package:flutter/material.dart';

class ThemeProvider with ChangeNotifier {
  bool _isDarkMode = false;

  bool get isDarkMode => _isDarkMode;

  void toggleTheme() {
    _isDarkMode = !_isDarkMode;
    notifyListeners();
  }
}

Provider bekerja dengan ChangeNotifier — ketika data berubah, kita panggil notifyListeners() untuk memberitahu semua widget yang mendengarkan bahwa ada perubahan. Consumer widget di main.dart akan automatically rebuild ketika ThemeProvider berubah.

Sekarang pages/home_page.dart:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/theme_provider.dart';
import 'profile_page.dart';
import 'settings_page.dart';

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => const ProfilePage()),
                );
              },
              child: const Text('Go to Profile'),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => const SettingsPage()),
                );
              },
              child: const Text('Open Settings'),
            ),
            const SizedBox(height: 40),
            Text(
              'Dark Mode: ${context.read().isDarkMode}',
              style: Theme.of(context).textTheme.bodyLarge,
            ),
          ],
        ),
      ),
    );
  }
}

pages/profile_page.dart:

import 'package:flutter/material.dart';

class ProfilePage extends StatelessWidget {
  const ProfilePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Profile Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Welcome to Profile!'),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text('Back to Home'),
            ),
          ],
        ),
      ),
    );
  }
}

pages/settings_page.dart:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/theme_provider.dart';

class SettingsPage extends StatelessWidget {
  const SettingsPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Settings'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer(
              builder: (context, themeProvider, child) {
                return SwitchListTile(
                  title: const Text('Dark Mode'),
                  value: themeProvider.isDarkMode,
                  onChanged: (value) {
                    themeProvider.toggleTheme();
                  },
                );
              },
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text('Back to Home'),
            ),
          ],
        ),
      ),
    );
  }
}

Cara Kerja: Push vs Pop dalam Navigation Stack

Ketika app dimulai, HomePage ada di base navigation stack. Ketika user tap "Go to Profile", kita push ProfilePage ke stack — sekarang stack punya 2 halaman. Ketika user tap "Back to Home" di ProfilePage, kita pop — ProfilePage hilang dan stack kembali ke HomePage.

Penting untuk diingat:

  • Push menambah halaman baru ke stack — halaman lama tetap exist di belakang
  • Pop menghapus halaman teratas dari stack — user kembali ke halaman sebelumnya
  • Android's back button dan iOS's swipe gesture otomatis trigger pop — jadi tidak perlu manual handling di banyak kasus
  • Jangan lupa jalankan widget halaman baru, bukan sekadar string nama — gunakan MaterialPageRoute dengan builder

State Management dengan Provider: Sharing Data Antar Halaman

Navigasi push/pop hanya menangani routing. Tapi bagaimana kalau kita ingin data dari halaman A bisa diakses di halaman B tanpa passing parameter? Di situ Provider masuk.

Provider adalah state management library yang "mendengarkan" perubahan data. Ketika data berubah, semua widget yang subscribe akan automatically rebuild. Ini jauh lebih efficient daripada memaksa semua widget rebuild.

Di contoh kita, ThemeProvider menyimpan isDarkMode. Ketika user toggle di SettingsPage, notifyListeners() dipanggil. Home dan Settings yang subscribe akan rebuild dan menampilkan tema terbaru tanpa perlu navigation trick apapun.

Untuk use Provider, kamu butuh 3 hal:

  1. Provider class (extends ChangeNotifier) — menyimpan state dan logic
  2. ChangeNotifierProvider — di level root (main.dart) untuk provide instance global
  3. Consumer atau context.read/watch — di widget untuk consume data

Ada dua cara access Provider data:

// Consumer: build ulang ketika data berubah
Consumer(
  builder: (context, themeProvider, child) {
    return Text(themeProvider.isDarkMode ? 'Dark' : 'Light');
  },
)

// context.watch: lebih ringkas, juga rebuild otomatis
Text(context.watch().isDarkMode ? 'Dark' : 'Light')

// context.read: access data tanpa rebuild (untuk event, misal button press)
context.read().toggleTheme();

Kesimpulan

Navigasi dan state management adalah fondasi app Flutter yang serius. Navigator push/pop memberikan kontrol flow halaman yang intuitif — stack-based routing sudah proven di banyak platform. Provider memudahkan sharing state tanpa perlu drilling prop atau global variables yang berantakan.

App 3 halaman yang kita buat di atas sudah mendemonstrasikan kombinasi keduanya: navigasi antar halaman dengan push/pop, dan shared state (tema) yang bisa diubah dari satu halaman dan langsung terlihat di halaman lain. Dari sini kamu bisa extend: tambah halaman ke-4, implement provider untuk user profile data, atau bahkan integrasi API.

Di Episode 4, kita akan bahas justru itu — cara fetch data dari API, store di Provider, dan tampilkan di UI. Pastikan dependencies di pubspec.yaml sudah ada (flutter pub add provider), dan eksperimen dengan code di atas sampai benar-benar comfortable. See you di episode berikutnya!

Tags:

#Tutorial #Flutter #Navigation #Provider #State Management #Multi-Screen

Share this article:

IKHSAN MAULANA

Tentang Penulis

IKHSAN MAULANA

Web, Android, and RPA Development

I am an experienced IT programmer specializing in Web Development (Laravel/PHP), Android (Dart/Flutter), and RPA (UiPath). I love building clean, efficient solutions that solve real-world problems. With 4+ years of hands...

Download CV

Sebelum download, boleh kenalan dulu? Form ini opsional — kosongin juga gak apa-apa, langsung klik Download.