Navigasi dan Routing

Navigation 1.0

Pemrograman Mobile

 

Muhamad Saad Nurul Ishlah, M.Comp.

Dept. Sistem Informasi & Dept. Ilmu Komputer, Universitas Pakuan

Agenda Kuliah

  • Menavigasi ke layar baru dan kembali
  • Menavigasi menggunakan rute bernama
  • Meneruskan argumen ke rute bernama
  • Mengembalikan data dari layar baru
  • Mengirim data ke layar baru

Menavigasi ke Layar Baru dan Kembali

Menavigasi ke Layar Baru dan Kembali

  • Sebagian besar aplikasi memiliki banyak layar yang bertujuan untuk memunculkan informasi berbeda
    • Contoh: Aplikasi eCommerce – Layar produk > tap gambar > tampilkan detil produk
  • Routing – Menavigasi dari satu layar ke layar yang lain
  • Terminologi:
    • Route (Rute) = Layar atau Halaman
    • Route = Activity (Android)
    • Route = ViewController (iOS)
    • Route = Widget (Flutter)
  • Widget Navigator digunakan untuk menavigasi ke rute baru
  • Navigator 1.0 – Imperative API, Navigator 2.0 – Declarative
  • Package – go_router

Navigator 1.0

  • Navigator – Widget yang mengelola tumpukan objek Route
  • Route – objek yang dikelola oleh Navigator yang merepresentasikan layar, biasanya diimplementasikan menggunakan kelas, seperti MaterialPageRoute
  • Mekanisme navigasi – objek Route didorong (pushed - push) dan dikeluarkan (popped - pop) dari tumpukan di Navigator
    • Navigator.push() atau Navigator.pushNamed()
    • Navigator.pop()
  • Jenis Routing:
    • Anonymous Routes – Rute tidak bernama
    • Named Routes – Rute bernama 

Navigator 1.0

  • Navigator – Widget yang mengelola tumpukan objek Route
  • Route – objek yang dikelola oleh Navigator yang merepresentasikan layar, biasanya diimplementasikan menggunakan kelas, seperti MaterialPageRoute
  • Mekanisme navigasi – objek Route didorong (pushed - push) dan dikeluarkan (popped - pop) dari tumpukan di Navigator
    • Navigator.push() atau Navigator.pushNamed()
    • Navigator.pop()
  • Jenis Routing:
    • Anonymous Routes – Rute tidak bernama
    • Named Routes – Rute bernama 

Anonymous Routes

  • MaterialApp dan CupertinoApp menggunakan Navigator secara default – gunakan langsung
  • Navigator.push() digunakan untuk beralih ke rute tertentu
    • Metode push() menambahkan Route ke tumpukan Route yang dikelola oleh Navigator
  • Navigator.pop() digunakan untuk kembali ke layar sebelumnya
    • Mengeluarkan Route dari tumpukan

Anonymous Routes

  • Navigator.push()
  • Route dapat menggunakan MaterialPageRoute
@optionalTypeArgs
Future<T?> push<T extends Object?>(
    BuildContext context,
    Route<T> route
)
@optionalTypeArgs

Anonymous Routes

class FirstRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Open route'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SecondRoute()),
            );
          },
        ),
      ),
    );
  }
}
class SecondRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

Pindah ke layar baru (SecondRoute)

Kembali ke layar sebelumnya (FirstRoute)

Anonymous Routes

Anonymous Routes

Navigasi dengan Rute Bernama

(Named Routes)

Membuka layar yang sama dari bagian lain di aplikasi?

Membuka layar yang sama dari bagian lain di aplikasi?

anonymous routes – duplikasi kode

Named Routes

  • Menamai Route untuk kebutuhan navigasi
  • Gunakan Navigator.pushNamed() untuk beralih ke rute tertentu
  • Gunakan Navigator.pop() untuk kembali
@optionalTypeArgs
Future<T?> pushNamed <T extends Object?>(
	BuildContext context,
	String routeName,
	{Object? arguments}
)
@optionalTypeArgs

Named Routes

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Named Routes Demo',
      // Start the app with the "/" named route. In this case, the app starts
      // on the FirstScreen widget.
      initialRoute: '/',
      routes: {
        // When navigating to the "/" route, build the FirstScreen widget.
        '/': (context) => FirstScreen(),
        // When navigating to the "/second" route, build the SecondScreen widget.
        '/second': (context) => SecondScreen(),
      },
    ),
  );
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          // Within the `FirstScreen` widget
          onPressed: () {
            // Navigate to the second screen using a named route.
            Navigator.pushNamed(context, '/second');
          },
          child: Text('Launch screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: ElevatedButton(
          // Within the SecondScreen widget
          onPressed: () {
            // Navigate back to the first screen by popping the current route
            // off the stack.
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

Menggunakan Named Routes

  • Definisikan Routes pada widget MaterialApp atau CupertinoApp
  • Definisikan rute inisial (initialRoute)
  • List semua route yang bisa dilakukan – beri nama (Dictionary)
  • Gunakan route dalam widget interaktif sebagai fungsi callback

Named Routes

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Named Routes Demo',
      // Start the app with the "/" named route. In this case, the app starts
      // on the FirstScreen widget.
      initialRoute: '/',
      routes: {
        // When navigating to the "/" route, build the FirstScreen widget.
        '/': (context) => FirstScreen(),
        // When navigating to the "/second" route, build the SecondScreen widget.
        '/second': (context) => SecondScreen(),
      },
    ),
  );
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          // Within the `FirstScreen` widget
          onPressed: () {
            // Navigate to the second screen using a named route.
            Navigator.pushNamed(context, '/second');
          },
          child: Text('Launch screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: ElevatedButton(
          // Within the SecondScreen widget
          onPressed: () {
            // Navigate back to the first screen by popping the current route
            // off the stack.
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

Kembali ke layar pertama

  • Panggil Navigator.pop(context) untuk kembali ke layar awal

Named Routes

Named Routes

Peringatan:

 

Saat menggunakan initialRoute, jangan tentukan properti home.

Meneruskan Argumen ke Rute Bernama

Meneruskan Argumen

  • Navigator menyediakan kemampuan untuk beralih ke rute lain
  • Dalam beberapa kasus, perlu meneruskan argumen ke rute bernama
  • Bagaimana caranya?
    • Gunakan parameter arguments pada metode Navigator.pushNamed()
    • Ekstrak argumen menggunakan metode ModalRoute.of atau dalam fungsi onGenerateRoute()
@optionalTypeArgs
Future<T?> pushNamed <T extends Object?>(
	BuildContext context,
	String routeName,
	{Object? arguments}
)
@optionalTypeArgs

Meneruskan & Mengekstrak Argumen (ModalRoute.of())

// You can pass any object to the arguments parameter.
// In this example, create a class that contains both
// a customizable title and message.
class ScreenArguments {
  final String title;
  final String message;

  ScreenArguments(this.title, this.message);
}

// A Widget that extracts the necessary arguments from
// the ModalRoute.
class ExtractArgumentsScreen extends StatelessWidget {
  static const routeName = '/extractArguments';

  @override
  Widget build(BuildContext context) {
    // Extract the arguments from the current ModalRoute
    // settings and cast them as ScreenArguments.
    final ScreenArguments args =
        ModalRoute.of(context)!.settings.arguments as ScreenArguments;

    return Scaffold(
      appBar: AppBar(
        title: Text(args.title),
      ),
      body: Center(
        child: Text(args.message),
      ),
    );
  }
}

// Register the widget in the routes
MaterialApp(
  routes: {
    ExtractArgumentsScreen.routeName: (context) => ExtractArgumentsScreen(),
  },
)

// A button that navigates to a named route.
// The named route extracts the arguments
// by itself.
ElevatedButton(
  child: Text("Navigate to screen that extracts arguments"),
  onPressed: () {
    // When the user taps the button,
    // navigate to a named route and
    // provide the arguments as an optional
    // parameter.
    Navigator.pushNamed(
      context,
      ExtractArgumentsScreen.routeName,
      arguments: ScreenArguments(
        'Extract Arguments Screen',
        'This message is extracted in the build method.',
      ),
    );
  },
),

Langkah-langkah

  1. Definisikan argumen yang ingin diteruskan – berupa objek
  2. Buat widget yang akan mengekstrak argumen yang diteruskan
  3. Daftarkan widget sebagai route
  4. Navigasi ke widget

Meneruskan & Mengekstrak Argumen (onGenerateRoute)

MaterialApp(
  // Provide a function to handle named routes.
  // Use this function to identify the named
  // route being pushed, and create the correct
  // Screen.
  onGenerateRoute: (settings) {
    // If you push the PassArguments route
    if (settings.name == PassArgumentsScreen.routeName) {
      // Cast the arguments to the correct
      // type: ScreenArguments.
      final ScreenArguments args = settings.arguments as ScreenArguments;

      // Then, extract the required data from
      // the arguments and pass the data to the
      // correct screen.
      return MaterialPageRoute(
        builder: (context) {
          return PassArgumentsScreen(
            title: args.title,
            message: args.message,
          );
        },
      );
    }
    // The code only supports
    // PassArgumentsScreen.routeName right now.
    // Other values need to be implemented if we
    // add them. The assertion here will help remind
    // us of that higher up in the call stack, since
    // this assertion would otherwise fire somewhere
    // in the framework.
    assert(false, 'Need to implement ${settings.name}');
    return null;
  },
)

Daripada mengesktrak langsung di widget, argumen dapat diekstrak dengan menggunakan fungsi onGenerateRoute() dan meneruskannya ke widget

Meneruskan & Mengekstrak Argumen

Mengembalikan Data dari Layar Baru

Mengembalikan Data dari Layar Baru

  • Dalam banyak kasus, kita butuh mengembalikan data dari layar baru kembali ke layar sebelumnya
  • Contoh:
    • Push layar baru > 2 opsi > tap opsi > kembali ke layar dengan data opsi
  • Navigator.pop()
@optionalTypeArgs
void pop <T extends Object?>(
	BuildContext context,
	[T? result]
)
@optionalTypeArgs

Mengembalikan Data dari Layar Baru

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Returning Data Demo'),
      ),
      // Create the SelectionButton widget in the next step.
      body: Center(child: SelectionButton()),
    );
  }
}

class SelectionButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        _navigateAndDisplaySelection(context);
      },
      child: Text('Pick an option, any option!'),
    );
  }

  // A method that launches the SelectionScreen and awaits the
  // result from Navigator.pop.
  _navigateAndDisplaySelection(BuildContext context) async {
    // Navigator.push returns a Future that completes after calling
    // Navigator.pop on the Selection Screen.
    final result = await Navigator.push(
      context,
      // Create the SelectionScreen in the next step.
      MaterialPageRoute(builder: (context) => SelectionScreen()),
    );
    
    // After the Selection Screen returns a result, hide any previous snackbars
    // and show the new result.
    ScaffoldMessenger.of(context)
      ..removeCurrentSnackBar()
      ..showSnackBar(SnackBar(content: Text("$result")));
    }
}

class SelectionScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Pick an option'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: ElevatedButton(
                onPressed: () {
                  // The Yep button returns "Yep!" as the result.
			      Navigator.pop(context, 'Yep!');
                },
                child: Text('Yep!'),
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: ElevatedButton(
                onPressed: () {
                  // The Nope button returns "Nope!" as the result.
			      Navigator.pop(context, 'Nope!');
                },
                child: Text('Nope.'),
              ),
            )
          ],
        ),
      ),
    );
  }
}

Langkah-langkah:

  • Definisikan layar home
  • Buat widget yang melakukan hal berikut:
    • Membuka layar baru
    • Menunggu layar baru mengembalikan hasil
  • Buat layar yang menyediakan opsi data
  • Gunakan data yang dikembalikan pada layar awal

Mengembalikan Data dari Layar Baru

Mengirim Data ke Layar Baru

Mengirim Data ke Layar Baru

  • Layar merupakan Widget
  • Dua opsi
    • Widget dapat diberikan argumen ketika inisiasi objek baru pada bagian Constructor. Data dapat dikirimkan ketika menginisiasi objek widget baru
    • RouteSettings – ModalRoute.of. Butuh ektraksi di layar baru

Mengirim Data ke Layar Baru (Constructor)

https://gist.github.com/nurulishlah/0b9239de66a366cb60e0500224da421d

Mengirim Data ke Layar Baru (RouteSettigns)

Diskusi

Ada pertanyaan?

Referensi

Terima Kasih

Navigasi dan Routing

By Muhamad Ishlah

Navigasi dan Routing

  • 4,626