Flutter Navigator Nedir?

4 minute read

Bu yazıda Flutter’ın can damarı Navigatorlardan bahsetmeye çalışacağım. Konuya başlamadan önce bu konuyla ilgili kaynakları vermek istiyorum. Flutter’ın kendi sitesinde yer alan, Navigator sınıfıyla ilgili hazırladığı official dokumanlar başlangıç için iyi bir fikir veriyor, bence güzel hazırlanmış. Öncelikle onları okumanızı tavsiye ederim. Aşağıya tüm linkleri bırakalım ,

  • Official Docs
  • https://medium.com/flutter-community/flutter-push-pop-push-1bb718b13c31

Linkleri zaman içinde güncelleyebilirim. Zira ben de yeniyim ve hala keşif aşamasındayım. Haydi yazımıza başlayalım:

From official docs ,

A widget that manages a set of child widgets with a stack discipline.

Flutter’da ekranda görmüş olduğumuz her şey bilindiği üzere widget ismi verilen komponentlerden oluşur. Bu widgetların kendi aralarında çocuk-ebeveyn (child-parent) ilişkisi vardır. Navigator dediğimiz widgetlar, bir stack gibi düşünülebilir. Biz yığının en üzerindeki ekranı görürüz. Navigator.push(…,…) methodu,kendisine verilen parametreler doğrultusunda bizi yeni ekrana taşırken eski ekranı da yığında canlı tutar. Geri tuşuna bastığımız zaman bir önceki ekrana döneriz ki burada geri tuşuna basmakla aslında Navigator.pop() fonksiyonunu çağırmış oluruz. Navigator.pop() fonksiyonu biraz önceki cümleden anlaşılacağı üzere yığının en üzerindeki, yani o anda görmekte olduğumuz ekranı yığından kaldırır ve biz böylece bir önceki ekranı görürüz. Yani geriye gideriz.

NOT Scaffold komponentini kullandığımız durumlarda, Navigator.push() çağrısından sonra Navigator.pop() kullanmak zorunda değiliz. Yukarıda da bahsettiğimiz gibi Scaffold internal şekilde, Navigator.pop() çağrısını çalıştıran bir “geri” butonunu AppBar’ın soluna yerleştirir. Android telefonlarda geri tuşu yine aynı işlevi görür.

Navigator.push(context, MaterialPageRoute<void>(
  builder: (BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('My Page')),
      body: Center(
        child: FlatButton(
          child: Text('POP'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  },
));

Yukarıdaki örneği incelersek, Navigator.push fonksiyonu, birinci parametre olarak context, ikinci parametre olarak MaterialPageRoute sınıfına ait build fonksiyonunu almıştır. Bu build fonksiyonu da Scaffold tipinde bir widget dönmüştür. İşte Navigator.push() fonksiyonu bu Scaffold widget’ını yığının en üstüne koyar. Dolayısıyla biz de bu widget’a gitmiş oluruz.

Navigator.pop(context);

From Official docs ,

It usually isn’t necessary to provide a widget that pops the Navigator in a route with a [Scaffold] because the Scaffold automatically adds a ‘back’ button to its AppBar. Pressing the back button causes [Navigator.pop] to be called. On Android, pressing the system back button does the same thing.

Named Navigator Routes

Uzun uzun betimlemeye gerek yok, hemen kodu verelim;

void main() {
  runApp(MaterialApp(
    home: MyAppHome(), // becomes the route named '/'
    routes: <String, WidgetBuilder> {
      '/a': (BuildContext context) => MyPage(title: 'page A'),
      '/b': (BuildContext context) => MyPage(title: 'page B'),
      '/c': (BuildContext context) => MyPage(title: 'page C'),
    },
  ));
}

Yukarıda belirtilen route’lardan birisine gitmek için ;

Navigator.pushNamed(context, '/b');

şeklinde pushNamed() fonksiyonu çağrılır.

Bu noktada benim için çok mühim bir soruyu sorma durumuna gelmiş olduk. Çoğunuzun da benimle aynı düşüncede olacağı kanaatindeyim. Soru şu ;

  • Peki bu named route denilen arkadaşa bir parametre geçirmek istersek ne olacak ? Örneğin */post/id=3 şeklinde bir endpointe istek atmak istiyorum ve sondaki id dinamik olarak değişiyor, /post kısmı gibi statik değil. Bu durumda ne yapmam gerekiyor ?*

Elcevap :

Öncelikle bu aynı sorunun mevzubahis olduğu bir S.O sorusunun linkini verelim ;

https://stackoverflow.com/questions/47419908/how-do-i-pass-non-string-data-to-a-named-route-in-flutter

Sorunun cevabı olarak da linkte en çok puanlanmış arkadaşın cevabını yapıştıralım ;

String id;
Navigator.pushNamed(context, '/users', arguments: id);

It can then be used within onGenerateRoute to customize route building with these arguments:

MaterialApp(
  title: 'Flutter Hooks Gallery',
  onGenerateRoute: (settings) {
    final arguments = settings.arguments;
    switch (settings.name) {
      case '/users':
        if (arguments is String) {
          // the details page for one specific user
          return UserDetails(arguments);
        }
        else {
          // a route showing the list of all users
          return UserList();
        }
      default:
        return null;
    }
  },
);

Cevabı kısaca özetleyecek olursak ,

  • Yukarıda bahsettiğimiz pushNamed fonksiyonuna parametre olarak vermek istediğimiz değişkeni veriyoruz.

  • Navigator.pushNamed(context, ‘/users’, arguments: id);

  • onGenerateRoute fonksiyonuna verilen settings objesi içerisinde pushNamed fonksiyonuna vermiş olduğumuz parametre(ler)i tutuyor. Bu parametreyi settings.arguments diyerek alıyoruz.

  • final arguments = settings.arguments;

  • Artık istediğimiz parametreyi elde ettik. Şimdi bu parametreyi gitmek istediğimiz sayfa objesinin constructoruna vermek kaldı.

  • return UserDetails(arguments);

  • UserDetails sınıfı içerisinde alınan parametrenin de eklenerek gereken URL stringinin oluşturulduğunu, bu URL’e gerektiği şekilde istek atılıp dönen sonuca göre bir Widget build edildiğiniz varsayıyoruz. O halde build edilen sayfamız bizi karşılamış demektir.

From docs ,

[Navigator.of] operates on the nearest ancestor [Navigator] from the given [BuildContext]. Be sure to provide a [BuildContext] below the intended [Navigator], especially in large [build] methods where nested [Navigator]s are created. The [Builder] widget can be used to access a [BuildContext] at a desired location in the widget subtree.

Navigator.of(context) , kendisine parametre olarak verilen context’in bir üstündeki parent’ının Navigator’una erişmemizi sağlar. Verilen context parametresi subtree’nin herhangi bir noktasına ait olabilir.

maybePop

Root widget’ında olduğumuzu düşünelim. Yani Navigator stack’imizde başka bir eleman yok. Bu durumda geri tuşuna basıp Navigator.pop fonksiyonunu çalıştırmamız, uygulamanın kapanmasına neden olacaktır. Fakat biz biliyoruz ki düzgün çalışan bir uygulamada bu hareket uygulamayı kapatmaz sadece arkaplana atar. Buna sorunu maybePop ile çözebiliyoruz.

İsminin de çağrışım yaptığı üzere maybePop, “pop only if you can” demektir. Yani “eğer root değilsen ve yığında senin altında başka screen varsa kapat, yoksa kapatma”.

Navigator.maybePop();
Navigator.of(context).maybePop();

Yukarıdaki gibi, pop fonksiyonu yerine kullanılabilmektedir.

canPop

Bu fonksiyon gerçekten pop yapma niyetiyle hareket etmez. Sadece o anda bulunulan sayfanın pop yapılıp yapılamayacağını yani kısaca root olup olmadığıyla alakalı bool tipinde bir değer döndürür. Sayfa pop edilebilirse True, edilemezse False döner.Örneğin normalde uygulamanın arkaplana atılmasına neden olacak şekilde,root sayfasında geri tuşuna basıldığı bir durumda kullanıcıya uyarı popup’u çıkarmak istediğimizi düşünelim, böylesi bir durumda canPop kullanılabilir.

Navigator.of(context).canPop();
print(Navigator.of(context).canPop().toString());

Leave a comment