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

Flutter: AppBar Widget ve Özelleştirme

Ahmet Balaman

Son güncelleme:

4 dk okuma

FlutterAppBarMaterial 3NavigationWidgetUI
Flutter: AppBar Widget ve Özelleştirme

AppBar, Scaffold'un appBar parametresine verdiğimiz üst çubuktur. Kullanıcıya hangi ekranda olduğunu söyler, geri dönüş ya da menü butonunu taşır ve o ekrana özel eylemleri (ara, paylaş, sil) sağ tarafta toplar. Tek bir title ile başlarsınız; ekran büyüdükçe leading, actions ve bottom alanları devreye girer. Bu yazıda bu alanları, Material 3 ile gelen renk davranışını ve en sık yapılan hataları sırayla ele alıyorum.

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

Scaffold’un parçaları: üstte appBar, ortada body, sağ altta floatingActionButton, altta bottomNavigationBar, solda drawer

Scaffold(
  appBar: AppBar(
    title: const Text('Sayfa Başlığı'),
  ),
  body: const Center(child: Text('İçerik')),
)

AppBar'ı Scaffold'a verdiğinizde durum çubuğu boşluğu, yükseklik ve geri butonu sizin yerinize hesaplanır. Navigator ile açılan bir sayfada geri oku, drawer tanımlı bir Scaffold'da ise menü ikonu kendiliğinden gelir.

Önemli Özellikler

Özellik Açıklama
title Başlık widget'ı (genellikle Text)
leading Sol taraftaki widget (geri butonu, menü ikonu)
actions Sağ taraftaki butonlar listesi
bottom Çubuğun altına eklenen alan (TabBar, ilerleme çubuğu)
backgroundColor Arka plan rengi
foregroundColor Başlık ve ikonların rengi
elevation / shadowColor Gölge miktarı ve gölge rengi
scrolledUnderElevation İçerik çubuğun altına kaydığında uygulanan yükseklik
centerTitle Başlığı ortalar
automaticallyImplyLeading Otomatik geri/menü butonunu açar ya da kapatır
toolbarHeight Çubuğun yüksekliği

Actions Kullanımı

actions bir widget listesidir. İki, en fazla üç ikon koyun; geri kalanını bir taşma menüsüne alın. Bunun için PopupMenuButton biçilmiş kaftandır:

AppBar(
  title: const Text('Başlık'),
  actions: [
    IconButton(
      icon: const Icon(Icons.search),
      tooltip: 'Ara',
      onPressed: () {},
    ),
    PopupMenuButton<String>(
      onSelected: (value) {},
      itemBuilder: (context) => const [
        PopupMenuItem(value: 'settings', child: Text('Ayarlar')),
        PopupMenuItem(value: 'help', child: Text('Yardım')),
      ],
    ),
  ],
)

tooltip vermeyi atlamayın: uzun basınca açıklama gösterir ve ekran okuyucular butonu bu metinle okur.

Leading Özelleştirme

leading alanını kendiniz doldurursanız otomatik geri ve menü butonu devre dışı kalır; davranışı da siz yazarsınız. Menüyü açmak için Scaffold.of(context) gerekir ve bu çağrının Scaffold'un altındaki bir context ile yapılması şarttır, o yüzden Builder kullanıyoruz:

AppBar(
  leading: Builder(
    builder: (context) => IconButton(
      icon: const Icon(Icons.menu),
      onPressed: () => Scaffold.of(context).openDrawer(),
    ),
  ),
  title: const Text('Başlık'),
)

Bu tuzağın ayrıntısını Drawer yazısında anlattım. İkon yerine "Vazgeç" gibi bir metin butonu koyacaksanız alan dar gelir; leadingWidth ile genişletin.

bottom Alanı

bottom, PreferredSizeWidget bekler; yani yüksekliğini önceden bildiren bir widget. TabBar bu arayüzü zaten uygular, bu yüzden sekmeli ekranlarda doğrudan bottom'a verilir. Kendi widget'ınızı koyacaksanız PreferredSize ile sarın:

AppBar(
  title: const Text('Siparişler'),
  bottom: const PreferredSize(
    preferredSize: Size.fromHeight(4),
    child: LinearProgressIndicator(),
  ),
)

Arama Çubuğu ve Gradient

title herhangi bir widget olabildiği için içine TextField koyabilirsiniz. Arka plana gradient vermek için flexibleSpace kullanılır; bu alan çubuğun arkasına çizilir:

AppBar(
  title: const Text('Gradient'),
  foregroundColor: Colors.white,
  flexibleSpace: Container(
    decoration: const BoxDecoration(
      gradient: LinearGradient(
        colors: [Colors.purple, Colors.blue],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
      ),
    ),
  ),
)

Material 3'te Renk ve Gölge

Material 3'te AppBar varsayılan olarak colorScheme.surface renginde ve gölgesizdir. İçerik çubuğun altına kaydığında çubuk "scrolled under" durumuna geçer: scrolledUnderElevation devreye girer ve yüzey rengi bir ton değişir. Flutter sürümüne göre bu, surface tint bindirmesi ya da farklı bir surface tonu olarak görünür. Tasarımınızda çubuk hep aynı renkte kalacaksa bunu tema düzeyinde sabitleyin:

final scheme = ColorScheme.fromSeed(seedColor: Colors.indigo);

ThemeData(
  colorScheme: scheme,
  appBarTheme: AppBarTheme(
    backgroundColor: scheme.surface,
    foregroundColor: scheme.onSurface,
    scrolledUnderElevation: 0,
    surfaceTintColor: Colors.transparent,
  ),
)

AppBarTheme kullanmanın ikinci faydası, her ekranda aynı renkleri tekrar yazmaktan kurtulmaktır.

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

Başlığı ve birkaç eylemi olan her standart ekranda AppBar doğru seçimdir. Şu durumlarda başka bir şeye bakın:

  • Başlık kaydırmayla küçülecek, büyük bir görsel taşıyacak ya da yukarı kaydırınca geri gelecekse SliverAppBar gerekir. Bunu SliverAppBar ile kategori başlığı ve arama alanı yazısında işledim.
  • Tam ekran görsel, onboarding ya da video oynatıcı gibi ekranlarda çubuk hiç olmayabilir; appBar parametresini boş bırakın ve içeriği SafeArea ile koruyun.
  • İçerik çubuğun arkasından görünecekse (şeffaf çubuk) Scaffold'da extendBodyBehindAppBar: true verip backgroundColor: Colors.transparent kullanın.
  • Ana eylem tekse ve ekranın en önemli işiyse onu actions içine gömmek yerine FloatingActionButton yapın.

Sık Yapılan Hatalar

1. Liste kaydırılınca çubuğun rengi değişiyor. Belirti: sayfa açıldığında çubuk beyaz, liste biraz kayınca grimsi ya da renkli bir ton alıyor. Bu bir hata değil, yukarıda anlattığım Material 3 davranışıdır. İstemiyorsanız backgroundColor değerini açıkça verin ve scrolledUnderElevation: 0 yazın.

2. elevation verdim ama gölge çıkmıyor. Material 3'te varsayılan gölge rengi şeffaftır. Gölge istiyorsanız rengini de söyleyin:

AppBar(
  title: const Text('Gölgeli'),
  elevation: 4,
  shadowColor: Colors.black54,
)

3. Arka planı değiştirdim, başlık ve ikonlar okunmuyor. Koyu bir backgroundColor verip yazıyı varsayılan koyu renkte bırakmak çok sık görülür. Text'e tek tek stil vermek yerine foregroundColor kullanın; başlığı, geri okunu ve actions ikonlarını birlikte boyar:

AppBar(
  title: const Text('Profil'),
  backgroundColor: Colors.indigo,
  foregroundColor: Colors.white,
)

4. Özel leading koyunca geri butonu kayboldu. leading verdiğinizde otomatik buton tamamen devre dışı kalır. Geri davranışını (Navigator.pop(context)) kendiniz yazın ya da yalnızca gerektiği durumda leading verin, diğer durumda null bırakın. Aşağıdaki senaryo tam bunu yapıyor.

Mini Senaryo: Çoklu Seçim Modu

Bir not listesi düşünün. Bir nota uzun basınca seçim modu açılsın: başlık seçili sayıyı göstersin, solda kapatma butonu, sağda silme butonu çıksın. Seçim bitince çubuk eski haline dönsün. Bunun için ikinci bir AppBar yazmaya gerek yok; aynı çubuğun alanlarını duruma göre dolduruyoruz:

class NotesPage extends StatefulWidget {
  const NotesPage({super.key});

  @override
  State<NotesPage> createState() => _NotesPageState();
}

class _NotesPageState extends State<NotesPage> {
  final List<String> _notes = List.generate(20, (i) => 'Not ${i + 1}');
  final Set<String> _selected = {};

  bool get _selectionMode => _selected.isNotEmpty;

  void _toggle(String note) {
    setState(() {
      if (!_selected.remove(note)) _selected.add(note);
    });
  }

  void _deleteSelected() {
    setState(() {
      _notes.removeWhere(_selected.contains);
      _selected.clear();
    });
  }

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        backgroundColor: _selectionMode ? scheme.primaryContainer : null,
        leading: _selectionMode
            ? IconButton(
                icon: const Icon(Icons.close),
                tooltip: 'Seçimi kapat',
                onPressed: () => setState(_selected.clear),
              )
            : null,
        title: Text(_selectionMode ? '${_selected.length} seçildi' : 'Notlarım'),
        actions: _selectionMode
            ? [
                IconButton(
                  icon: const Icon(Icons.delete_outline),
                  tooltip: 'Sil',
                  onPressed: _deleteSelected,
                ),
              ]
            : [
                IconButton(
                  icon: const Icon(Icons.search),
                  tooltip: 'Ara',
                  onPressed: () {},
                ),
              ],
      ),
      body: ListView.builder(
        itemCount: _notes.length,
        itemBuilder: (context, index) {
          final note = _notes[index];
          return ListTile(
            title: Text(note),
            selected: _selected.contains(note),
            onLongPress: () => _toggle(note),
            onTap: _selectionMode ? () => _toggle(note) : null,
          );
        },
      ),
    );
  }
}

Dikkat edilecek iki nokta var. leading seçim modu dışında null kalıyor, böylece bu sayfa Navigator ile açıldığında geri oku kendiliğinden görünmeye devam ediyor. backgroundColor da aynı şekilde null olunca temadaki değere düşüyor; kullanıcı modun değiştiğini renkten anlıyor, siz de iki ayrı çubuk yönetmiyorsunuz.

Sık Sorulan Sorular

AppBar yüksekliği nasıl değiştirilir?

toolbarHeight parametresiyle. bottom alanı kullanıyorsanız onun yüksekliği buna ayrıca eklenir.

Geri butonunu nasıl kaldırırım?

automaticallyImplyLeading: false yazın. Bu yalnızca butonu gizler; Android'in sistem geri hareketi çalışmaya devam eder, onu engellemek için PopScope gerekir.

Tüm ekranlarda aynı AppBar stilini nasıl kullanırım?

ThemeData içindeki appBarTheme alanına bir AppBarTheme verin. Renk, centerTitle, scrolledUnderElevation gibi ayarlar oradan tüm AppBar'lara uygulanır; tek bir ekranda farklı değer gerekiyorsa o AppBar'ın kendi parametresi temayı ezer.

AppBar ile SliverAppBar arasındaki fark nedir?

AppBar sabit yükseklikte durur ve Scaffold'a verilir. SliverAppBar bir CustomScrollView içinde yaşar ve kaydırmaya göre genişleyip daralabilir, gizlenip geri gelebilir.

Yorumlar