Ahmet Balaman LogoAhmet Balaman

Flutter'da Navigator ile Sayfalar Arası Geçiş ve Veri Transferi

personAhmet Balaman
calendar_today
FlutterNavigatorRoutingNavigationState Management

Sayfalar Arası Geçiş - Navigator Kullanımı

İlk defa sayfalar arası geçiş yapmaya çalıştığımda biraz kafam karıştı. "Web'de link varken Flutter'da nasıl oluyor bu iş?" diye düşünmüştüm. Sonra Navigator'ı keşfettim ve her şey netleşti.

Flutter'da sayfalar arası geçiş yapmak için Navigator sınıfı kullanılır. Bir çeşit yığın (stack) yapısı gibi çalışır: Sayfa açarsınız üste eklenir, geri tuşuna basarsınız üstteki sayfa çıkar.

Canlı Demo: Navigator Kullanımı

Sayfalar arası geçiş ve veri transferini interaktif olarak deneyin:

Temel Navigator Kullanımı

En basit haliyle bir sayfadan diğerine geçiş:

// Ana sayfa
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Ana Sayfa')),
      body: Center(
        child: ElevatedButton(
          child: Text('Detay Sayfasına Git'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => DetailPage()),
            );
          },
        ),
      ),
    );
  }
}

// Detay sayfası
class DetailPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Detay Sayfa')),
      body: Center(
        child: Text('Bu detay sayfası'),
      ),
    );
  }
}

Navigator.push() yeni bir sayfa açar. MaterialPageRoute sayfa geçiş animasyonunu sağlar.

Geri Dönüş - pop()

Geri dönmek için iki yol var:

1. Otomatik Geri Tuşu

AppBar kullanırsanız Flutter otomatik olarak geri tuşu ekler. Kullanıcı buna tıklayınca geri döner.

2. Kod ile Geri Dönüş

ElevatedButton(
  child: Text('Geri Dön'),
  onPressed: () {
    Navigator.pop(context);
  },
)

Navigator.pop() üstteki sayfayı stack'ten çıkarır.

Sayfalar Arası Veri Gönderme

Asıl güzellik buradan başlıyor. Sayfaya veri göndermek için constructor kullanıyoruz.

Veri Gönderen Sayfa

class ProductListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Ürünler')),
      body: ListView(
        children: [
          ListTile(
            title: Text('iPhone 15'),
            subtitle: Text('50.000 TL'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => ProductDetailPage(
                    productName: 'iPhone 15',
                    price: 50000,
                    description: 'Yeni nesil akıllı telefon',
                  ),
                ),
              );
            },
          ),
          ListTile(
            title: Text('Samsung S24'),
            subtitle: Text('45.000 TL'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => ProductDetailPage(
                    productName: 'Samsung S24',
                    price: 45000,
                    description: 'Android\'in en iyisi',
                  ),
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

Veri Alan Sayfa

class ProductDetailPage extends StatelessWidget {
  final String productName;
  final int price;
  final String description;

  const ProductDetailPage({
    Key? key,
    required this.productName,
    required this.price,
    required this.description,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(productName)),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              productName,
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 8),
            Text(
              '${price.toString()} TL',
              style: TextStyle(fontSize: 20, color: Colors.green),
            ),
            SizedBox(height: 16),
            Text(
              description,
              style: TextStyle(fontSize: 16),
            ),
          ],
        ),
      ),
    );
  }
}

Constructor'da required ile parametreleri zorunlu kılıyoruz. Böylece sayfa açılırken mutlaka bu bilgiler verilmeli.

Geri Dönerken Veri Döndürme

Bazen açtığınız sayfadan veri almak istersiniz. Mesela kullanıcı bir şey seçti, bu seçimi önceki sayfaya göndermek istiyorsunuz.

Veri İsteyen Sayfa

class SelectCityPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Şehir Seç')),
      body: Center(
        child: ElevatedButton(
          child: Text('Şehir Seç'),
          onPressed: () async {
            // Sayfa aç ve sonuç bekle
            final selectedCity = await Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => CityListPage()),
            );
            
            // Sonuç geldi
            if (selectedCity != null) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Seçilen: $selectedCity')),
              );
            }
          },
        ),
      ),
    );
  }
}

Veri Döndüren Sayfa

class CityListPage extends StatelessWidget {
  final List<String> cities = [
    'İstanbul',
    'Ankara',
    'İzmir',
    'Antalya',
    'Bursa',
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Şehir Listesi')),
      body: ListView.builder(
        itemCount: cities.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(cities[index]),
            onTap: () {
              // Seçilen şehri geri gönder
              Navigator.pop(context, cities[index]);
            },
          );
        },
      ),
    );
  }
}

Navigator.pop(context, deger) ile ikinci parametre olarak veri gönderiyoruz.

pushReplacement - Geri Dönüşü Engelleme

Bazen kullanıcının geri tuşu ile önceki sayfaya dönmesini istemezsiniz. Tipik örnek: Giriş yaptıktan sonra login sayfasına geri dönülmesin.

push() ile Normal Geçiş

Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => HomePage()),
);
// Kullanıcı geri tuşuna basarsa login sayfasına döner

pushReplacement() ile Geçiş

Navigator.pushReplacement(
  context,
  MaterialPageRoute(builder: (context) => HomePage()),
);
// Geri tuşu stack'teki bir önceki sayfaya gider, login atlanır

Pratik Örnek: Login Sonrası

class LoginPage extends StatelessWidget {
  void _login(BuildContext context) {
    // Login işlemi başarılı
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(builder: (context) => HomePage()),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          child: Text('Giriş Yap'),
          onPressed: () => _login(context),
        ),
      ),
    );
  }
}

Artık HomePage'deyken geri tuşuna basarsa LoginPage'e dönmez, uygulamadan çıkar.

Kayıt Sonrası Örnek

class RegisterPage extends StatelessWidget {
  void _register(BuildContext context) {
    // Kayıt işlemi başarılı
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(builder: (context) => LoginPage()),
    );
    
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Kayıt başarılı! Giriş yapabilirsiniz.')),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Kayıt Ol')),
      body: Center(
        child: ElevatedButton(
          child: Text('Kaydı Tamamla'),
          onPressed: () => _register(context),
        ),
      ),
    );
  }
}

pushAndRemoveUntil - Tüm Stack'i Temizleme

Bazen sadece bir sayfayı değil, tüm stack'i temizlemek istersiniz.

Navigator.pushAndRemoveUntil(
  context,
  MaterialPageRoute(builder: (context) => HomePage()),
  (route) => false, // Tüm sayfaları kaldır
);

Bu, kullanıcıyı tamamen yeni bir akışa yönlendirmek için kullanılır. Örneğin çıkış yapınca tüm sayfalar temizlenir, sadece login ekranı kalır.

Çıkış Yapma Örneği

void logout(BuildContext context) {
  Navigator.pushAndRemoveUntil(
    context,
    MaterialPageRoute(builder: (context) => LoginPage()),
    (route) => false,
  );
}

Named Routes (İsimlendirilmiş Rotalar)

Daha büyük projelerde her seferinde MaterialPageRoute yazmak zahmetli. İsimlendirilmiş rotalar kullanabilirsiniz.

main.dart'ta Rota Tanımlama

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      initialRoute: '/',
      routes: {
        '/': (context) => HomePage(),
        '/detail': (context) => DetailPage(),
        '/profile': (context) => ProfilePage(),
        '/settings': (context) => SettingsPage(),
      },
    );
  }
}

İsimlendirilmiş Rota ile Geçiş

// Normal geçiş
Navigator.pushNamed(context, '/detail');

// Geri dönüşü engelleme
Navigator.pushReplacementNamed(context, '/home');

// Stack temizleme
Navigator.pushNamedAndRemoveUntil(
  context,
  '/login',
  (route) => false,
);

İsimlendirilmiş Rotada Veri Gönderme

// Veri gönderme
Navigator.pushNamed(
  context,
  '/detail',
  arguments: {
    'id': 123,
    'name': 'Ahmet',
  },
);

// Veri alma
class DetailPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context)!.settings.arguments as Map;
    
    return Scaffold(
      appBar: AppBar(title: Text(args['name'])),
      body: Center(
        child: Text('ID: ${args['id']}'),
      ),
    );
  }
}

Navigation ile Animasyon

Farklı geçiş animasyonları ekleyebilirsiniz:

Sağdan Sola Kayma (Varsayılan)

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

Aşağıdan Yukarı

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DetailPage(),
    fullscreenDialog: true,
  ),
);

Özel Animasyon

Navigator.push(
  context,
  PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => DetailPage(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      const begin = Offset(0.0, 1.0);
      const end = Offset.zero;
      const curve = Curves.easeInOut;

      var tween = Tween(begin: begin, end: end).chain(
        CurveTween(curve: curve),
      );

      return SlideTransition(
        position: animation.drive(tween),
        child: child,
      );
    },
  ),
);

Geri Tuşunu Yakalamak

Android'de geri tuşuna basıldığında özel işlem yapmak:

class MyPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        // Kullanıcıya sor
        final shouldPop = await showDialog<bool>(
          context: context,
          builder: (context) => AlertDialog(
            title: Text('Çıkmak istediğinize emin misiniz?'),
            actions: [
              TextButton(
                child: Text('Hayır'),
                onPressed: () => Navigator.pop(context, false),
              ),
              TextButton(
                child: Text('Evet'),
                onPressed: () => Navigator.pop(context, true),
              ),
            ],
          ),
        );
        
        return shouldPop ?? false;
      },
      child: Scaffold(
        appBar: AppBar(title: Text('Sayfa')),
        body: Center(child: Text('İçerik')),
      ),
    );
  }
}

Pratik İpuçları

1. Context'i Doğru Kullanın

// ❌ Yanlış - build context kullanımı
Navigator.push(context, ...);

// ✅ Doğru - doğru context
Builder(
  builder: (BuildContext context) {
    return ElevatedButton(
      onPressed: () => Navigator.push(context, ...),
      child: Text('Git'),
    );
  },
)

2. Async/Await Kullanımı

final result = await Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SelectPage()),
);

if (result != null) {
  print('Sonuç: $result');
}

3. Gereksiz Navigator Kullanımından Kaçının

// ❌ Gereksiz - aynı sayfa içinde
Navigator.push(context, MaterialPageRoute(...));

// ✅ State güncellemesi kullanın
setState(() {
  selectedIndex = 1;
});

Özet

Flutter'da Navigation:

  • push(): Yeni sayfa aç
  • pop(): Geri dön
  • pushReplacement(): Geri dönüşü engelle
  • pushAndRemoveUntil(): Stack'i temizle
  • pushNamed(): İsimli rota kullan

Veri Transferi:

  • Constructor ile ileri veri gönder
  • pop(context, veri) ile geri veri döndür

Back Stack Yönetimi:

  • Login/Register sonrası pushReplacement kullan
  • Çıkış yaparken pushAndRemoveUntil ile stack'i temizle

Navigator, Flutter'ın en temel özelliklerinden. Bir kere alıştıktan sonra çok rahat kullanıyorsunuz!


Navigation konusunda takıldınız mı?

Bir sonraki yazıda görüşmek üzere! 🚀