İçeriğe geç / Skip to content / Zum Inhalt

Flutter: SnackBar ve SnackBarAction Kullanımı

Ahmet Balaman

Son güncelleme:

4 dk okuma

FlutterSnackBarSnackBarActionScaffoldMessengerFeedbackUI
Flutter: SnackBar ve SnackBarAction Kullanımı

SnackBar, kullanıcının yaptığı işlemin sonucunu akışı kesmeden bildiren kısa mesajdır: ekranın altında belirir, birkaç saniye durur ve kendiliğinden kaybolur. "Kaydedildi", "Bağlantı koptu", "Öğe silindi" gibi bilgileri vermek için kullanırız. Üzerine SnackBarAction ile tek bir buton ekleyebiliriz; en bilinen örneği "Geri Al" butonudur.

Canlı Demo

Aşağıdaki interaktif örnekte bu widget'ı deneyebilirsiniz:

💡 Eğer yukarıdaki örnek açılmazsa, DartPad linkine tıklayarak yeni sekmede çalıştırabilirsiniz.

Temel Kullanım

SnackBar'ı ekrana ScaffoldMessenger getirir:

ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(
    content: Text('Profil güncellendi'),
  ),
);

Eski eğitimlerde Scaffold.of(context).showSnackBar(...) yazımını görebilirsiniz. Bu metot Flutter'dan kaldırıldı; bugün denerseniz "The method 'showSnackBar' isn't defined for the type 'ScaffoldState'" derleme hatası alırsınız. ScaffoldMessenger tek bir Scaffold'a değil uygulamaya bağlı çalıştığı için, mesaj gösterilirken sayfa değişse bile SnackBar ekranda kalmaya devam eder. "Kaydet"e basıp önceki sayfaya dönülen akışlarda işimize yarayan tam da budur.

SnackBarAction Kullanımı

Mesajın yanına kullanıcının basabileceği tek bir buton ekler:

ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(
    content: const Text('Öğe silindi'),
    action: SnackBarAction(
      label: 'GERİ AL',
      onPressed: () {
        // Geri alma işlemi
      },
    ),
  ),
);

Butona basıldığında SnackBar kendiliğinden kapanır. Aksiyon tektir ve kısa olmalıdır; iki seçenek sunmak gerekiyorsa aradığımız bileşen SnackBar değil dialog'dur.

Önemli Özellikler

Özellik Açıklama
content SnackBar içeriği (genellikle Text)
action Tıklanabilir buton (SnackBarAction)
duration Görünme süresi (varsayılan 4 saniye)
backgroundColor Arka plan rengi
behavior fixed veya floating
shape Şekil (köşe yuvarlaklığı vb.)
margin Floating modda kenar boşlukları
showCloseIcon Sağa bir kapatma ikonu ekler

Floating SnackBar

Ekranın altına yapışmak yerine kenarlardan boşluk bırakan, yüzen görünüm:

SnackBar(
  content: const Text('Floating SnackBar'),
  behavior: SnackBarBehavior.floating,
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(10),
  ),
  margin: const EdgeInsets.all(16),
)

Bütün uygulamada aynı görünümü istiyorsak bunu her çağrıda tekrarlamak yerine temaya taşırız: ThemeData(snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating)).

İkonlu SnackBar

SnackBar(
  content: Row(
    children: const [
      Icon(Icons.info, color: Colors.white),
      SizedBox(width: 10),
      Expanded(child: Text('Bilgilendirme mesajı')),
    ],
  ),
  backgroundColor: Colors.blue,
)

Metni Expanded ile sarmak önemli. Sarmazsak uzun bir mesajda Row sağa taşar; Expanded yazısında bunun nedenini anlatmıştım.

SnackBar'ı Programatik Kapatma

showSnackBar bir kontrolcü döndürür. Uzun süren bir işlemde mesajı biz kapatmak istiyorsak süreyi uzun tutup iş bitince close() çağırırız:

final snackBar = ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(
    content: Text('Yükleniyor...'),
    duration: Duration(minutes: 1),
  ),
);

// İşlem tamamlandığında kapat
await someAsyncOperation();
snackBar.close();

Ne Zaman Kullanılır, Ne Zaman Kullanılmaz?

SnackBar, kullanıcının görmese de bir şey kaybetmeyeceği bilgiler içindir. Karar verirken şu ayrımı kullanıyorum:

  • Kullan: tamamlanan bir işlemin teyidi, geri alınabilir işlemler, geçici ağ hataları.
  • Kullanma: kullanıcının mutlaka karar vermesi gereken durumlar. Mesaj dört saniyede kaybolur; silme onayı gibi bir soru için AlertDialog gerekir.
  • Kullanma: kalıcı durumlar. "Çevrimdışısın" gibi, sorun çözülene kadar ekranda kalması gereken bilgiler için ScaffoldMessenger.of(context).showMaterialBanner(...) ile açılan MaterialBanner daha uygun.
  • Kullanma: form doğrulama hataları. "E-posta geçersiz" mesajı alanın hemen altında, validator ile gösterilmeli; kullanıcı hatanın hangi alanda olduğunu aramak zorunda kalmamalı.

Sık Yapılan Hatalar

1. Mesajların kuyruğa girmesi

Belirti: Kullanıcı butona beş kez hızlıca basar, SnackBar'lar sırayla tek tek gösterilir ve son mesaj yirmi saniye sonra hâlâ ekrandadır.

Sebep: ScaffoldMessenger mesajları kuyruğa alır; yeni mesaj eskisinin bitmesini bekler.

Çözüm: Yenisini göstermeden önce ekrandakini kapatmak. Dart'ın cascade (..) yazımı burada çok kullanışlı:

ScaffoldMessenger.of(context)
  ..hideCurrentSnackBar()
  ..showSnackBar(const SnackBar(content: Text('Sepete eklendi')));

Bekleyenlerin hepsini silmek için clearSnackBars() da kullanılabilir.

2. fixed davranışta margin vermek

Belirti: Uygulama "Margin can only be used with floating behavior" assertion hatasıyla durur.

Çözüm: margin ve width yalnızca behavior: SnackBarBehavior.floating ile birlikte çalışır. Ya davranışı floating yaparız ya da bu parametreleri kaldırırız. İkisini aynı anda vermek de hatadır; biri seçilir.

3. await sonrasında context kullanmak

Belirti: Analiz aracı use_build_context_synchronously uyarısı verir; kullanıcı istek sürerken sayfadan çıkmışsa çalışma anında hata alınır.

Çözüm: İki yol var. Ya await'ten sonra context.mounted kontrol edilir ya da messenger await'ten önce bir değişkene alınır:

Future<void> _save() async {
  final messenger = ScaffoldMessenger.of(context);
  await repository.save(profile);
  messenger.showSnackBar(const SnackBar(content: Text('Kaydedildi')));
}

İkinci yolun güzel yanı, sayfa kapanmış olsa bile mesajın gösterilebilmesi.

4. initState içinde SnackBar göstermek

Belirti: "dependOnInheritedWidgetOfExactType … was called before initState() completed" hatası.

Sebep: initState çalışırken widget henüz ağaca tam yerleşmemiştir, ScaffoldMessenger.of(context) araması yapılamaz. Aşamaların sırasını yaşam döngüsü yazısında anlatmıştım.

Çözüm: İlk kare çizildikten sonraya ertelemek:

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Tekrar hoş geldin')),
    );
  });
}

Mini Senaryo: Geri Alınabilir Silme

Bir görev listesi düşünelim. Kullanıcı bir görevi sildiğinde onay sormak yerine hemen listeden kaldırıp "Geri Al" fırsatı vereceğiz. Sunucudaki gerçek silmeyi ise ancak SnackBar kapandıktan sonra yapacağız. showSnackBar'ın döndürdüğü kontrolcüdeki closed alanı, SnackBar'ın neden kapandığını söyleyen bir Future'dır:

Future<void> _removeTask(int index) async {
  final task = _tasks[index];
  final messenger = ScaffoldMessenger.of(context);

  setState(() => _tasks.removeAt(index));

  messenger.hideCurrentSnackBar();
  final controller = messenger.showSnackBar(
    SnackBar(
      content: Text('"${task.title}" silindi'),
      action: SnackBarAction(label: 'GERİ AL', onPressed: () {}),
    ),
  );

  final reason = await controller.closed;

  if (reason == SnackBarClosedReason.action) {
    if (!mounted) return;
    setState(() => _tasks.insert(index, task)); // Eski yerine geri koy
  } else {
    await widget.repository.delete(task.id); // Süre doldu, kaydırıldı vb.
  }
}

onPressed boş, çünkü butona basıldığını zaten SnackBarClosedReason.action değerinden anlıyoruz. Yeni bir silme geldiğinde hideCurrentSnackBar() öncekini kapatır ve o görev kalıcı olarak silinir; yani aynı anda yalnızca son silme geri alınabilir ve index hep geçerli kalır. Dikkat edilmesi gereken bir nokta var: uygulama o birkaç saniye içinde kapatılırsa silme isteği sunucuya hiç gitmez. Verinin kritik olduğu yerlerde önce gerçekten silip "Geri Al"da kaydı yeniden oluşturmak daha güvenlidir.

Bu desen, listede FloatingActionButton varken de sorunsuz çalışır; Scaffold ikisinin üst üste binmemesini kendisi ayarlar.

Sık Sorulan Sorular

SnackBar ekranda ne kadar kalır?

Varsayılan süre 4 saniyedir. duration parametresiyle değiştirilebilir; okunması zaman alan ya da aksiyon içeren mesajlarda süreyi biraz uzatmak iyi olur.

context olmadan SnackBar gösterilebilir mi?

Evet. Bir GlobalKey<ScaffoldMessengerState> oluşturup MaterialApp'in scaffoldMessengerKey parametresine verilir. Sonra herhangi bir yerden key.currentState?.showSnackBar(...) çağrılabilir.

SnackBar ile Toast arasındaki fark nedir?

Flutter'da yerleşik bir Toast widget'ı yoktur; Material tasarımdaki karşılığı SnackBar'dır. SnackBar uygulamanın kendi widget ağacında çizilir, aksiyon butonu alabilir ve temayla uyumludur. Android tarzı toast için ek paket gerekir.

Aynı anda birden fazla SnackBar gösterilebilir mi?

Hayır, aynı anda yalnızca biri görünür; diğerleri kuyrukta bekler. Yeni mesajın hemen görünmesi için önce hideCurrentSnackBar() çağrılır.

Yorumlar