Skip to content

Flutter Rehberi (Flutter Guide)

📌 Ne Zaman Kullanılır?

Kullan: Pixel-perfect UI, yüksek performans, tek codebase (iOS+Android+Web+Desktop)

⚠️ Dikkat: Dart öğrenme gerekli, native kütüphane entegrasyonu bazen zor

Kullanma: Sadece web projesi (React/Vue daha iyi), çok küçük proje (overkill)

Önerilen stack: Flutter + Dart + Riverpod/Bloc + GoRouter + Dio

Flutter ile cross-platform mobil, web ve masaüstü uygulama geliştirme rehberi. (Cross-platform mobile, web and desktop application development guide with Flutter.)


1) Flutter Nedir, Neden Flutter? (What is Flutter, Why Flutter?)

Flutter, Google tarafından geliştirilen açık kaynaklı bir UI toolkit'tir. (Flutter is an open-source UI toolkit developed by Google.)

Tek bir codebase ile iOS, Android, Web, Windows, macOS ve Linux uygulamaları geliştirmenizi sağlar. (It allows you to develop iOS, Android, Web, Windows, macOS and Linux applications from a single codebase.)

Flutter vs Diğer Framework'ler (Flutter vs Other Frameworks)

Özellik (Feature)FlutterReact NativeNative (Swift/Kotlin)
Dil (Language)DartJavaScript/TypeScriptSwift / Kotlin
Performans (Performance)Yüksek — native derleme (High — native compilation)Köprü bazlı (Bridge-based)En yüksek (Highest)
UI renderKendi motoru — Skia/Impeller (Own engine)Platform widget'ları (Platform widgets)Platform widget'ları
Hot ReloadEvet (Yes)Evet (Yes)Sınırlı (Limited)
Ekosistem (Ecosystem)Büyüyen (Growing)Olgun (Mature)En olgun (Most mature)
Öğrenme eğrisi (Learning curve)Orta (Medium)Düşük (Low — JS biliyorsan / if you know JS)Yüksek (High)
Platform sayısı (Platforms)6 (iOS, Android, Web, Win, Mac, Linux)2 (iOS, Android)1
Şirket (Company)GoogleMetaApple / Google

Neden Flutter? (Why Flutter?)

  • Tek codebase — iOS, Android, Web, Desktop aynı kodla (single codebase for all platforms).
  • Kendi render motoru — Platform bağımsız pixel-perfect UI (own rendering engine — Skia/Impeller).
  • Hot Reload — Kod değişiklikleri saniyeler içinde yansır (code changes reflect in seconds).
  • Zengin widget kütüphanesi — Material ve Cupertino widget'ları hazır gelir (rich widget library).
  • Dart dili — Hem AOT hem JIT derleme, null safety, async/await (both AOT and JIT compilation).
  • Büyük topluluk — pub.dev'de 40K+ paket (large community — 40K+ packages on pub.dev).
  • Firebase entegrasyonu — FlutterFire ile doğal entegrasyon (native Firebase integration).

Flutter Mimarisi (Flutter Architecture)

┌─────────────────────────────────────┐
│         Dart Uygulamanız            │
│      (Your Dart Application)        │
├─────────────────────────────────────┤
│         Flutter Framework           │
│  Material │ Cupertino │ Widgets     │
│  Rendering │ Animation │ Painting   │
│  Foundation │ Gestures              │
├─────────────────────────────────────┤
│         Flutter Engine (C++)        │
│  Skia/Impeller │ Dart Runtime       │
│  Platform Channels                  │
├─────────────────────────────────────┤
│         Platform (İşletim Sistemi)  │
│  iOS │ Android │ Web │ Desktop      │
└─────────────────────────────────────┘

2) Kurulum (Installation)

Flutter SDK Kurulumu (Flutter SDK Setup)

bash
# Linux — snap ile kurulum (install via snap)
sudo snap install flutter --classic

# macOS — Homebrew ile (with Homebrew)
brew install flutter

# veya manuel kurulum (or manual installation)
git clone https://github.com/flutter/flutter.git -b stable
export PATH="$PATH:$(pwd)/flutter/bin"

# Windows — Chocolatey ile (with Chocolatey)
choco install flutter

Android Studio Kurulumu (Android Studio Setup)

bash
# 1. Android Studio indir (download Android Studio)
# https://developer.android.com/studio

# 2. Flutter ve Dart pluginlerini kur
#    (Install Flutter and Dart plugins)
#    File → Settings → Plugins → "Flutter" ara → Install

# 3. Android SDK ayarları (Android SDK settings)
# SDK Manager → SDK Platforms → Android 14 (API 34)
# SDK Manager → SDK Tools → Android SDK Build-Tools, Command-line Tools

# 4. Android lisansları kabul et (accept Android licenses)
flutter doctor --android-licenses

Xcode Kurulumu — macOS (Xcode Setup — macOS)

bash
# Xcode'u App Store'dan indir (download Xcode from App Store)
# veya (or)
xcode-select --install

# CocoaPods kur (install CocoaPods)
sudo gem install cocoapods
# veya (or)
brew install cocoapods

# iOS simulator aç (open iOS simulator)
open -a Simulator

VS Code Kurulumu (VS Code Setup)

bash
# VS Code extensions:
# 1. Flutter (Dart-Code.flutter)
# 2. Dart (Dart-Code.dart-code)
# 3. Awesome Flutter Snippets
# 4. Flutter Widget Snippets
# 5. Error Lens

# Ayarlar (settings.json):
# "dart.flutterSdkPath": "/path/to/flutter"
# "editor.formatOnSave": true
# "dart.lineLength": 80

Flutter Doctor

bash
# Ortam kontrolü (environment check)
flutter doctor

# Detaylı kontrol (detailed check)
flutter doctor -v

# Beklenen çıktı (expected output):
# [✓] Flutter
# [✓] Android toolchain
# [✓] Xcode
# [✓] Chrome
# [✓] Android Studio
# [✓] VS Code
# [✓] Connected device

İlk Proje Oluşturma (Creating First Project)

bash
# Yeni proje oluştur (create new project)
flutter create my_app
cd my_app

# Organizasyonlu proje (project with organization)
flutter create --org com.example --project-name my_app .

# Belirli platformlar ile (with specific platforms)
flutter create --platforms android,ios,web my_app

# Çalıştır (run)
flutter run

# Belirli cihazda çalıştır (run on specific device)
flutter devices              # cihazları listele (list devices)
flutter run -d chrome        # Chrome'da çalıştır (run on Chrome)
flutter run -d emulator-5554 # emülatörde çalıştır (run on emulator)

Proje Yapısı (Project Structure)

my_app/
├── android/          # Android native kodu (Android native code)
├── ios/              # iOS native kodu (iOS native code)
├── web/              # Web dosyaları (Web files)
├── linux/            # Linux native kodu
├── macos/            # macOS native kodu
├── windows/          # Windows native kodu
├── lib/              # Dart kaynak kodu (Dart source code)
│   ├── main.dart     # Giriş noktası (entry point)
│   ├── screens/      # Sayfa widget'ları (page widgets)
│   ├── widgets/      # Yeniden kullanılabilir widget'lar (reusable widgets)
│   ├── models/       # Veri modelleri (data models)
│   ├── services/     # API ve servis katmanı (API and service layer)
│   ├── providers/    # State management
│   └── utils/        # Yardımcı fonksiyonlar (utility functions)
├── test/             # Test dosyaları (test files)
├── assets/           # Görseller, fontlar, JSON (images, fonts, JSON)
├── pubspec.yaml      # Bağımlılıklar (dependencies)
├── pubspec.lock      # Kilitlenen sürümler (locked versions)
├── analysis_options.yaml # Lint kuralları (lint rules)
└── .metadata

3) Dart Dili Temelleri (Dart Language Fundamentals)

Değişkenler ve Tipler (Variables and Types)

dart
// Tip belirterek (with type annotation)
String isim = 'Fahri';
int yas = 30;
double boy = 1.80;
bool aktif = true;
List<String> diller = ['Dart', 'Python', 'JavaScript'];
Map<String, dynamic> kullanici = {'ad': 'Fahri', 'yas': 30};
Set<int> sayilar = {1, 2, 3};

// Tip çıkarımı (type inference)
var mesaj = 'Merhaba'; // String olarak çıkarılır (inferred as String)
final suan = DateTime.now(); // final — bir kez atanır (assigned once)
const pi = 3.14159; // const — derleme zamanı sabiti (compile-time constant)

// late — geç başlatma (late initialization)
late String aciklama;
aciklama = 'Sonradan atandı'; // (assigned later)

// dynamic ve Object
dynamic hersey = 'string'; // herhangi bir tip olabilir (can be any type)
hersey = 42; // geçerli (valid)
Object nesne = 'string'; // tüm tiplerin üst tipi (supertype of all types)

Null Safety

dart
// Nullable tip (nullable type) — ? ile
String? nullOlabilir = null; // null olabilir (can be null)
String nullOlamaz = 'değer'; // null olamaz (cannot be null)

// Null kontrol operatörleri (null check operators)
String? isim;

// ?? — null ise varsayılan değer (default value if null)
String sonuc = isim ?? 'Bilinmiyor';

// ??= — null ise ata (assign if null)
isim ??= 'Varsayılan';

// ?. — null-aware erişim (null-aware access)
int? uzunluk = isim?.length;

// ! — null olmadığını garanti et (assert non-null) — DİKKATLİ KULLAN
String kesinDeger = isim!; // null ise hata fırlatır (throws if null)

// Null kontrol desenleri (null check patterns)
if (isim != null) {
  print(isim.length); // akıllı çıkarım — artık non-null (smart cast)
}

// late final — geç başlatılan değişmez (late initialized immutable)
late final String config;

Fonksiyonlar (Functions)

dart
// Temel fonksiyon (basic function)
int topla(int a, int b) {
  return a + b;
}

// Arrow fonksiyon (arrow function)
int topla2(int a, int b) => a + b;

// İsteğe bağlı konumsal parametreler (optional positional parameters)
String selamla(String ad, [String? soyad]) {
  return soyad != null ? 'Merhaba $ad $soyad' : 'Merhaba $ad';
}

// İsimli parametreler (named parameters)
void kullaniciOlustur({
  required String ad,
  required String email,
  int yas = 0, // varsayılan değer (default value)
  String? telefon,
}) {
  print('$ad - $email - $yas');
}
kullaniciOlustur(ad: 'Fahri', email: 'f@test.com');

// Fonksiyon tipi (function type — typedef)
typedef Hesapla = int Function(int, int);
Hesapla islem = topla;

// Closure / anonim fonksiyon (anonymous function)
var liste = [3, 1, 2];
liste.sort((a, b) => a.compareTo(b));

Async / Await

dart
// Future — gelecekte tamamlanacak değer (value that completes in the future)
Future<String> veriGetir() async {
  // Ağ isteği simülasyonu (network request simulation)
  await Future.delayed(Duration(seconds: 2));
  return 'Veri geldi';
}

// Kullanım (usage)
void main() async {
  String sonuc = await veriGetir();
  print(sonuc);
}

// Future hata yönetimi (error handling)
Future<void> guvenliGetir() async {
  try {
    var veri = await veriGetir();
    print(veri);
  } catch (e) {
    print('Hata: $e');
  } finally {
    print('İşlem tamamlandı');
  }
}

// Paralel Future'lar (parallel Futures)
Future<void> paralel() async {
  var sonuclar = await Future.wait([
    veriGetir(),
    veriGetir(),
    veriGetir(),
  ]);
  print(sonuclar); // [veri1, veri2, veri3]
}

// Stream — sürekli akan veri (continuous data flow)
Stream<int> sayaç() async* {
  for (int i = 0; i < 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i; // değer yayınla (emit value)
  }
}

// Stream dinleme (listening to Stream)
void dinle() async {
  await for (var deger in sayaç()) {
    print(deger);
  }

  // veya (or)
  sayaç().listen(
    (deger) => print(deger),
    onError: (e) => print('Hata: $e'),
    onDone: () => print('Bitti'),
  );
}

// StreamController — manuel stream yönetimi (manual stream management)
import 'dart:async';

final controller = StreamController<String>.broadcast();
controller.sink.add('Yeni veri');
controller.stream.listen((veri) => print(veri));
controller.close(); // kapatmayı unutma (don't forget to close)

Koleksiyonlar (Collections)

dart
// List (sıralı dizi — ordered array)
List<int> sayilar = [1, 2, 3, 4, 5];
sayilar.add(6);
sayilar.addAll([7, 8]);
sayilar.removeAt(0);
sayilar.where((s) => s > 3).toList(); // [4, 5, 6, 7, 8]
sayilar.map((s) => s * 2).toList();   // her elemanı 2 ile çarp (multiply each)

// List spread operatörü (spread operator)
var birlesik = [...sayilar, 9, 10];

// Collection if ve for
var liste = [
  1,
  2,
  if (true) 3,           // koşullu ekleme (conditional add)
  for (var i in [4, 5]) i, // döngü ile ekleme (add with loop)
];

// Map (anahtar-değer çifti — key-value pair)
Map<String, int> puan = {'Ali': 90, 'Veli': 85};
puan['Ayşe'] = 95;
puan.containsKey('Ali'); // true
puan.entries.forEach((e) => print('${e.key}: ${e.value}'));

// Set (benzersiz elemanlar — unique elements)
Set<String> renkler = {'kırmızı', 'mavi', 'yeşil'};
renkler.add('kırmızı'); // yok sayılır — zaten var (ignored — already exists)
renkler.intersection({'mavi', 'sarı'}); // {'mavi'}

// Iterable dönüşümler (transformations)
var isimler = ['Ali', 'Veli', 'Ayşe'];
isimler
  .where((i) => i.length > 3)
  .map((i) => i.toUpperCase())
  .toList(); // ['AYŞE']

Sınıflar ve OOP (Classes and OOP)

dart
// Temel sınıf (basic class)
class Kullanici {
  final String ad;
  final String email;
  int _yas; // _ ile private

  // Constructor
  Kullanici(this.ad, this.email, this._yas);

  // İsimli constructor (named constructor)
  Kullanici.misafir()
      : ad = 'Misafir',
        email = 'misafir@test.com',
        _yas = 0;

  // Factory constructor
  factory Kullanici.fromJson(Map<String, dynamic> json) {
    return Kullanici(
      json['ad'] as String,
      json['email'] as String,
      json['yas'] as int,
    );
  }

  // Getter
  int get yas => _yas;
  bool get yetiskin => _yas >= 18;

  // Setter
  set yas(int deger) {
    if (deger >= 0) _yas = deger;
  }

  // Method
  String tapisCikart() => '$ad ($email)';

  // toString override
  @override
  String toString() => 'Kullanici($ad, $email)';
}

// Kalıtım (Inheritance)
class Admin extends Kullanici {
  final String rol;

  Admin(super.ad, super.email, super.yas, this.rol);

  @override
  String tapisCikart() => '[$rol] ${super.tapisCikart()}';
}

// Soyut sınıf (Abstract class)
abstract class Depo<T> {
  Future<List<T>> hepsiniGetir();
  Future<T?> idIleGetir(String id);
  Future<void> kaydet(T nesne);
  Future<void> sil(String id);
}

// Abstract sınıf implementasyonu (implementation)
class KullaniciDepo implements Depo<Kullanici> {
  @override
  Future<List<Kullanici>> hepsiniGetir() async => [];

  @override
  Future<Kullanici?> idIleGetir(String id) async => null;

  @override
  Future<void> kaydet(Kullanici nesne) async {}

  @override
  Future<void> sil(String id) async {}
}

Mixins

dart
// Mixin — sınıflara işlevsellik ekleme (adding functionality to classes)
mixin Loglanabilir {
  void log(String mesaj) {
    print('[LOG ${DateTime.now()}] $mesaj');
  }
}

mixin Dogrulanabilir {
  bool emailGecerliMi(String email) {
    return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email);
  }
}

// Mixin kullanımı (usage)
class KullaniciServisi with Loglanabilir, Dogrulanabilir {
  void kaydet(String ad, String email) {
    if (!emailGecerliMi(email)) {
      log('Geçersiz email: $email');
      return;
    }
    log('Kullanıcı kaydedildi: $ad');
  }
}

// Mixin kısıtlaması (restriction with on)
mixin Animasyonlu on StatefulWidget {
  // Sadece StatefulWidget alt sınıflarında kullanılabilir
  // (Can only be used in StatefulWidget subclasses)
}

Enum ve Sealed Class

dart
// Enhanced Enum (gelişmiş enum — Dart 3)
enum Durum {
  aktif('Aktif', Colors.green),
  pasif('Pasif', Colors.grey),
  askida('Askıda', Colors.orange);

  final String etiket;
  final Color renk;

  const Durum(this.etiket, this.renk);
}

// Sealed class (mühürlü sınıf — Dart 3)
sealed class Sonuc<T> {}

class Basarili<T> extends Sonuc<T> {
  final T veri;
  Basarili(this.veri);
}

class Hatali<T> extends Sonuc<T> {
  final String mesaj;
  Hatali(this.mesaj);
}

class Yukleniyor<T> extends Sonuc<T> {}

// Pattern matching ile kullanım (usage with pattern matching)
Widget sonucGoster(Sonuc<String> sonuc) {
  return switch (sonuc) {
    Basarili(:final veri) => Text(veri),
    Hatali(:final mesaj) => Text('Hata: $mesaj'),
    Yukleniyor() => CircularProgressIndicator(),
  };
}

Extension Methods

dart
// String extension
extension StringUzanti on String {
  String basHarfBuyut() {
    if (isEmpty) return this;
    return '${this[0].toUpperCase()}${substring(1)}';
  }

  bool get gecerliEmail =>
      RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(this);
}

// DateTime extension
extension DateTimeUzanti on DateTime {
  String formatliTarih() => '$day/$month/$year';
  bool get bugun {
    final simdi = DateTime.now();
    return year == simdi.year && month == simdi.month && day == simdi.day;
  }
}

// Kullanım (usage)
'flutter'.basHarfBuyut(); // 'Flutter'
'test@mail.com'.gecerliEmail; // true
DateTime.now().formatliTarih(); // '11/4/2026'

4) Widget Yapısı (Widget Structure)

StatelessWidget

Durumu olmayan widget — build metodu her çağrıldığında aynı çıktıyı verir. (Stateless widget — produces the same output every time build is called.)

dart
class HosGeldinKarti extends StatelessWidget {
  final String isim;

  const HosGeldinKarti({super.key, required this.isim});

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Text(
          'Hoş geldin, $isim!',
          style: Theme.of(context).textTheme.headlineSmall,
        ),
      ),
    );
  }
}

StatefulWidget

Durumu olan widget — iç durumu değişebilir ve yeniden çizilir. (Stateful widget — internal state can change and triggers rebuild.)

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

  @override
  State<SayacWidget> createState() => _SayacWidgetState();
}

class _SayacWidgetState extends State<SayacWidget> {
  int _sayac = 0;

  void _artir() {
    setState(() {
      _sayac++;
    });
  }

  @override
  void initState() {
    super.initState();
    // Widget oluşturulduğunda çalışır (runs when widget is created)
  }

  @override
  void dispose() {
    // Temizlik işlemleri — controller dispose, listener kaldırma
    // (Cleanup — dispose controllers, remove listeners)
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Sayaç: $_sayac'),
        ElevatedButton(
          onPressed: _artir,
          child: const Text('Artır'),
        ),
      ],
    );
  }
}

Widget Lifecycle (Yaşam Döngüsü)

StatefulWidget Yaşam Döngüsü (Lifecycle):

createState()          → State nesnesi oluşturulur (State object created)

initState()            → İlk başlatma (initial setup)

didChangeDependencies() → InheritedWidget değiştiğinde (when InheritedWidget changes)

build()                → Widget ağacını oluştur (build widget tree)

didUpdateWidget()      → Parent yeniden build ettiğinde (when parent rebuilds)

setState()             → Durum değişikliği → build() tekrar çağrılır
    │                     (State change → build() called again)
deactivate()           → Widget ağaçtan geçici çıkarıldığında
    │                     (temporarily removed from tree)
dispose()              → Widget kalıcı olarak kaldırıldığında
                          (permanently removed — cleanup here)

BuildContext

dart
class ContextOrnegi extends StatelessWidget {
  const ContextOrnegi({super.key});

  @override
  Widget build(BuildContext context) {
    // Tema erişimi (theme access)
    final tema = Theme.of(context);

    // Ekran boyutu (screen size)
    final boyut = MediaQuery.of(context).size;
    final genislik = boyut.width;
    final yukseklik = boyut.height;

    // Navigasyon (navigation)
    // Navigator.of(context).push(...)

    // Scaffold erişimi (Scaffold access)
    // ScaffoldMessenger.of(context).showSnackBar(...)

    return Text(
      'Ekran: ${genislik.toInt()}x${yukseklik.toInt()}',
      style: tema.textTheme.bodyLarge,
    );
  }
}

Widget Key

dart
// Key — Flutter'ın widget'ları doğru eşleştirmesi için
// (Key — helps Flutter correctly match widgets)

// ValueKey — benzersiz bir değer ile (with a unique value)
ListView(
  children: items.map((item) =>
    ListTile(
      key: ValueKey(item.id), // önemli! (important!)
      title: Text(item.name),
    ),
  ).toList(),
);

// UniqueKey — her zaman benzersiz (always unique)
Container(key: UniqueKey());

// GlobalKey — widget'a dışarıdan erişim (external access to widget)
final formKey = GlobalKey<FormState>();
Form(
  key: formKey,
  child: Column(children: [
    TextFormField(validator: (v) => v!.isEmpty ? 'Zorunlu' : null),
    ElevatedButton(
      onPressed: () {
        if (formKey.currentState!.validate()) {
          formKey.currentState!.save();
        }
      },
      child: const Text('Gönder'),
    ),
  ]),
);

// ObjectKey — nesne referansı ile (with object reference)
ListTile(key: ObjectKey(kullanici));

5) Temel Widget'lar (Core Widgets)

Layout Widget'ları (Layout Widgets)

dart
// Container — kutu modeli (box model)
Container(
  width: 200,
  height: 100,
  padding: const EdgeInsets.all(16),
  margin: const EdgeInsets.symmetric(horizontal: 8),
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(12),
    boxShadow: [
      BoxShadow(
        color: Colors.black26,
        blurRadius: 8,
        offset: const Offset(0, 4),
      ),
    ],
  ),
  child: const Text('Merhaba'),
);

// Row — yatay düzenleme (horizontal layout)
Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween, // yatay hizalama
  crossAxisAlignment: CrossAxisAlignment.center,      // dikey hizalama
  children: [
    const Icon(Icons.star),
    const Text('Başlık'),
    ElevatedButton(onPressed: () {}, child: const Text('Tıkla')),
  ],
);

// Column — dikey düzenleme (vertical layout)
Column(
  mainAxisAlignment: MainAxisAlignment.start,
  crossAxisAlignment: CrossAxisAlignment.stretch, // tam genişlik (full width)
  children: [
    const Text('Üst'),
    const SizedBox(height: 16), // boşluk (spacing)
    const Text('Alt'),
  ],
);

// Stack — üst üste yerleştirme (overlapping layout)
Stack(
  alignment: Alignment.center,
  children: [
    Image.asset('assets/arkaplan.png'),
    Positioned(
      bottom: 16,
      right: 16,
      child: FloatingActionButton(
        onPressed: () {},
        child: const Icon(Icons.add),
      ),
    ),
  ],
);

// Expanded — kalan alanı doldur (fill remaining space)
Row(
  children: [
    Expanded(
      flex: 2, // 2/3 oranında (2/3 ratio)
      child: Container(color: Colors.red),
    ),
    Expanded(
      flex: 1, // 1/3 oranında (1/3 ratio)
      child: Container(color: Colors.blue),
    ),
  ],
);

// Flexible — esnek boyutlandırma (flexible sizing)
Row(
  children: [
    Flexible(
      fit: FlexFit.loose, // ihtiyacı kadar (as needed)
      child: Text('Kısa metin'),
    ),
  ],
);

// Wrap — satır taşması durumunda alt satıra geç (wrap to next line)
Wrap(
  spacing: 8, // yatay boşluk (horizontal gap)
  runSpacing: 8, // dikey boşluk (vertical gap)
  children: List.generate(10, (i) => Chip(label: Text('Etiket $i'))),
);

Padding ve SizedBox

dart
// Padding — iç boşluk (inner spacing)
Padding(
  padding: const EdgeInsets.only(left: 16, top: 8),
  child: const Text('Boşluklu metin'),
);

// SizedBox — sabit boyutlu kutu veya boşluk (fixed size box or spacing)
const SizedBox(height: 16);  // dikey boşluk (vertical spacing)
const SizedBox(width: 8);    // yatay boşluk (horizontal spacing)
SizedBox(
  width: 100,
  height: 50,
  child: ElevatedButton(onPressed: () {}, child: const Text('Sabit')),
);

// SizedBox.expand — tüm alanı kapla (fill all available space)
SizedBox.expand(
  child: Container(color: Colors.green),
);

Liste Widget'ları (List Widgets)

dart
// ListView — kaydırılabilir liste (scrollable list)
ListView(
  children: const [
    ListTile(title: Text('Öğe 1')),
    ListTile(title: Text('Öğe 2')),
    ListTile(title: Text('Öğe 3')),
  ],
);

// ListView.builder — tembel yükleme (lazy loading — performanslı)
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    final item = items[index];
    return ListTile(
      leading: CircleAvatar(child: Text('${index + 1}')),
      title: Text(item.baslik),
      subtitle: Text(item.aciklama),
      trailing: const Icon(Icons.arrow_forward_ios),
      onTap: () => Navigator.push(context, /* ... */),
    );
  },
);

// ListView.separated — ayraçlı liste (list with separators)
ListView.separated(
  itemCount: items.length,
  separatorBuilder: (context, index) => const Divider(),
  itemBuilder: (context, index) => ListTile(title: Text(items[index])),
);

// GridView — ızgara düzeni (grid layout)
GridView.builder(
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,        // sütun sayısı (column count)
    crossAxisSpacing: 8,      // yatay boşluk (horizontal spacing)
    mainAxisSpacing: 8,       // dikey boşluk (vertical spacing)
    childAspectRatio: 3 / 4,  // en/boy oranı (aspect ratio)
  ),
  itemCount: products.length,
  itemBuilder: (context, index) => ProductCard(products[index]),
);

// GridView.count — sabit sütun sayısı (fixed column count)
GridView.count(
  crossAxisCount: 3,
  children: List.generate(9, (i) => Container(color: Colors.primaries[i])),
);

Scaffold ve AppBar

dart
class AnaSayfa extends StatelessWidget {
  const AnaSayfa({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // Uygulama çubuğu (app bar)
      appBar: AppBar(
        title: const Text('Ana Sayfa'),
        centerTitle: true,
        actions: [
          IconButton(
            icon: const Icon(Icons.search),
            onPressed: () {},
          ),
          IconButton(
            icon: const Icon(Icons.notifications),
            onPressed: () {},
          ),
        ],
      ),

      // Çekmece menüsü (drawer menu)
      drawer: Drawer(
        child: ListView(
          children: [
            const DrawerHeader(
              decoration: BoxDecoration(color: Colors.blue),
              child: Text('Menü', style: TextStyle(color: Colors.white)),
            ),
            ListTile(
              leading: const Icon(Icons.home),
              title: const Text('Ana Sayfa'),
              onTap: () => Navigator.pop(context),
            ),
            ListTile(
              leading: const Icon(Icons.settings),
              title: const Text('Ayarlar'),
              onTap: () {},
            ),
          ],
        ),
      ),

      // Gövde (body)
      body: const Center(child: Text('İçerik')),

      // Alt gezinme çubuğu (bottom navigation bar)
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: 0,
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Ana Sayfa'),
          BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Arama'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profil'),
        ],
        onTap: (index) {},
      ),

      // Yüzen aksiyon düğmesi (floating action button)
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: const Icon(Icons.add),
      ),
    );
  }
}

Form Widget'ları (Form Widgets)

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

  @override
  State<GirisFormu> createState() => _GirisFormuState();
}

class _GirisFormuState extends State<GirisFormu> {
  final _formKey = GlobalKey<FormState>();
  final _emailController = TextEditingController();
  final _sifreController = TextEditingController();
  bool _sifreGizli = true;

  @override
  void dispose() {
    _emailController.dispose();
    _sifreController.dispose();
    super.dispose();
  }

  void _girisYap() {
    if (_formKey.currentState!.validate()) {
      // Form geçerli — giriş yap (form valid — login)
      print('Email: ${_emailController.text}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          // Email alanı (email field)
          TextFormField(
            controller: _emailController,
            keyboardType: TextInputType.emailAddress,
            decoration: const InputDecoration(
              labelText: 'Email',
              prefixIcon: Icon(Icons.email),
              border: OutlineInputBorder(),
            ),
            validator: (value) {
              if (value == null || value.isEmpty) return 'Email zorunlu';
              if (!value.contains('@')) return 'Geçerli email girin';
              return null;
            },
          ),
          const SizedBox(height: 16),

          // Şifre alanı (password field)
          TextFormField(
            controller: _sifreController,
            obscureText: _sifreGizli,
            decoration: InputDecoration(
              labelText: 'Şifre',
              prefixIcon: const Icon(Icons.lock),
              border: const OutlineInputBorder(),
              suffixIcon: IconButton(
                icon: Icon(
                  _sifreGizli ? Icons.visibility : Icons.visibility_off,
                ),
                onPressed: () => setState(() => _sifreGizli = !_sifreGizli),
              ),
            ),
            validator: (value) {
              if (value == null || value.length < 6) {
                return 'Şifre en az 6 karakter olmalı';
              }
              return null;
            },
          ),
          const SizedBox(height: 24),

          // Giriş butonu (login button)
          SizedBox(
            width: double.infinity,
            child: ElevatedButton(
              onPressed: _girisYap,
              child: const Text('Giriş Yap'),
            ),
          ),
        ],
      ),
    );
  }
}

6) Stil ve Tema (Style & Theme)

ThemeData ve Material 3

dart
// main.dart — uygulama teması (app theme)
MaterialApp(
  title: 'Uygulama',
  themeMode: ThemeMode.system, // system / light / dark
  theme: _acikTema(),
  darkTheme: _koyuTema(),
  home: const AnaSayfa(),
);

ThemeData _acikTema() {
  return ThemeData(
    useMaterial3: true, // Material 3 etkinleştir (enable Material 3)
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.blue,
      brightness: Brightness.light,
    ),
    // Metin teması (text theme)
    textTheme: const TextTheme(
      headlineLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
      headlineMedium: TextStyle(fontSize: 24, fontWeight: FontWeight.w600),
      bodyLarge: TextStyle(fontSize: 16),
      bodyMedium: TextStyle(fontSize: 14),
      labelLarge: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
    ),
    // AppBar teması (AppBar theme)
    appBarTheme: const AppBarTheme(
      centerTitle: true,
      elevation: 0,
    ),
    // Kart teması (Card theme)
    cardTheme: CardTheme(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
    ),
    // Girdi dekorasyonu (input decoration)
    inputDecorationTheme: InputDecorationTheme(
      filled: true,
      border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
    ),
    // Buton teması (button theme)
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      ),
    ),
  );
}

ThemeData _koyuTema() {
  return ThemeData(
    useMaterial3: true,
    colorScheme: ColorScheme.fromSeed(
      seedColor: Colors.blue,
      brightness: Brightness.dark,
    ),
  );
}

Tema Kullanımı (Using Theme)

dart
class TemaOrnek extends StatelessWidget {
  const TemaOrnek({super.key});

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

    return Column(
      children: [
        Text(
          'Başlık',
          style: tema.textTheme.headlineMedium?.copyWith(
            color: renkSemasi.primary,
          ),
        ),
        Container(
          color: renkSemasi.primaryContainer,
          child: Text(
            'Konteynır',
            style: TextStyle(color: renkSemasi.onPrimaryContainer),
          ),
        ),
        // ColorScheme renkleri (colors):
        // primary, onPrimary, primaryContainer, onPrimaryContainer
        // secondary, tertiary, error, surface, onSurface
      ],
    );
  }
}

Cupertino Widget'ları (iOS Tarzı — iOS Style)

dart
import 'package:flutter/cupertino.dart';

// Cupertino uygulama (Cupertino app)
CupertinoApp(
  theme: const CupertinoThemeData(
    primaryColor: CupertinoColors.systemBlue,
    brightness: Brightness.light,
  ),
  home: CupertinoPageScaffold(
    navigationBar: const CupertinoNavigationBar(
      middle: Text('iOS Tarzı'),
    ),
    child: Center(
      child: CupertinoButton.filled(
        onPressed: () {},
        child: const Text('Cupertino Buton'),
      ),
    ),
  ),
);

// Platform uyarlamalı widget (platform-adaptive widget)
Widget adaptifButon(String metin, VoidCallback onPressed) {
  if (Platform.isIOS) {
    return CupertinoButton(onPressed: onPressed, child: Text(metin));
  }
  return ElevatedButton(onPressed: onPressed, child: Text(metin));
}

Özel Font Kullanımı (Custom Fonts)

yaml
# pubspec.yaml
flutter:
  fonts:
    - family: PoppinsTR
      fonts:
        - asset: assets/fonts/Poppins-Regular.ttf
        - asset: assets/fonts/Poppins-Bold.ttf
          weight: 700
        - asset: assets/fonts/Poppins-Italic.ttf
          style: italic
dart
// Kullanım (usage)
Text(
  'Özel font',
  style: TextStyle(fontFamily: 'PoppinsTR', fontSize: 18),
);

// veya tema genelinde (or in theme globally)
ThemeData(
  fontFamily: 'PoppinsTR',
);

7) Navigasyon (Navigation)

dart
// Sayfaya git (navigate to page)
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => const DetaySayfasi()),
);

// Geri dön (go back)
Navigator.pop(context);

// Veri ile geri dön (return data)
Navigator.pop(context, 'sonuç verisi');

// Sonucu al (get result)
final sonuc = await Navigator.push<String>(
  context,
  MaterialPageRoute(builder: (context) => const SecimSayfasi()),
);

// Tüm sayfaları temizle ve git (clear all and navigate)
Navigator.pushAndRemoveUntil(
  context,
  MaterialPageRoute(builder: (context) => const GirisSayfasi()),
  (route) => false, // tüm önceki sayfaları kaldır (remove all previous)
);

// Değiştir (replace current page)
Navigator.pushReplacement(
  context,
  MaterialPageRoute(builder: (context) => const AnaSayfa()),
);
yaml
# pubspec.yaml
dependencies:
  go_router: ^14.0.0
dart
import 'package:go_router/go_router.dart';

// Router yapılandırması (router configuration)
final goRouter = GoRouter(
  initialLocation: '/',
  debugLogDiagnostics: true,

  // Yönlendirme (redirect — auth guard)
  redirect: (context, state) {
    final girisYapmisMi = authService.girisYapmisMi;
    final girisRotasi = state.matchedLocation == '/giris';

    if (!girisYapmisMi && !girisRotasi) return '/giris';
    if (girisYapmisMi && girisRotasi) return '/';
    return null; // yönlendirme yok (no redirect)
  },

  // Rotalar (routes)
  routes: [
    // Ana sayfa (home page)
    GoRoute(
      path: '/',
      name: 'anasayfa',
      builder: (context, state) => const AnaSayfa(),
    ),

    // Parametreli rota (route with params)
    GoRoute(
      path: '/urun/:id',
      name: 'urunDetay',
      builder: (context, state) {
        final id = state.pathParameters['id']!;
        return UrunDetaySayfasi(urunId: id);
      },
    ),

    // Query parametreleri (query parameters)
    GoRoute(
      path: '/arama',
      name: 'arama',
      builder: (context, state) {
        final sorgu = state.uri.queryParameters['q'] ?? '';
        return AramaSayfasi(sorgu: sorgu);
      },
    ),

    // İç içe rotalar — alt navigasyon (nested routes — bottom navigation)
    StatefulShellRoute.indexedStack(
      builder: (context, state, navigationShell) {
        return AltNavigasyonSayfasi(navigationShell: navigationShell);
      },
      branches: [
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/ana',
              builder: (context, state) => const AnaTab(),
            ),
          ],
        ),
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/arama',
              builder: (context, state) => const AramaTab(),
            ),
          ],
        ),
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/profil',
              builder: (context, state) => const ProfilTab(),
            ),
          ],
        ),
      ],
    ),
  ],

  // Hata sayfası (error page)
  errorBuilder: (context, state) => HataSayfasi(hata: state.error),
);

// MaterialApp ile kullanım (usage with MaterialApp)
MaterialApp.router(
  routerConfig: goRouter,
);

// Navigasyon (navigation)
context.go('/urun/42');                    // git (go — replaces stack)
context.push('/urun/42');                  // push (adds to stack)
context.pop();                             // geri (back)
context.goNamed('urunDetay', pathParameters: {'id': '42'});
context.pushNamed('arama', queryParameters: {'q': 'flutter'});

Deep Linking

dart
// Android — AndroidManifest.xml
// <intent-filter>
//   <action android:name="android.intent.action.VIEW" />
//   <category android:name="android.intent.category.DEFAULT" />
//   <category android:name="android.intent.category.BROWSABLE" />
//   <data android:scheme="https" android:host="myapp.com" />
// </intent-filter>

// iOS — Info.plist veya Associated Domains
// URL scheme ve Universal Links ayarlanmalı

// GoRouter deep linking otomatik destekler
// (GoRouter supports deep linking automatically)
// https://myapp.com/urun/42 → /urun/42 rotasına yönlendirilir

8) State Management (Durum Yönetimi)

setState — Basit Durum (Simple State)

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

  @override
  State<BasitSayac> createState() => _BasitSayacState();
}

class _BasitSayacState extends State<BasitSayac> {
  int _sayac = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('$_sayac'),
        ElevatedButton(
          onPressed: () => setState(() => _sayac++),
          child: const Text('+1'),
        ),
      ],
    );
  }
}
// Avantaj (advantage): Basit, hızlı (simple, fast)
// Dezavantaj (disadvantage): Widget büyüdükçe karmaşıklaşır,
//   prop drilling sorunu (gets complex as widget grows)

Provider

yaml
# pubspec.yaml
dependencies:
  provider: ^6.1.0
dart
// Model / ChangeNotifier
class SayacModel extends ChangeNotifier {
  int _deger = 0;
  int get deger => _deger;

  void artir() {
    _deger++;
    notifyListeners(); // dinleyicileri bilgilendir (notify listeners)
  }

  void azalt() {
    _deger--;
    notifyListeners();
  }
}

// Provider tanımla (provide)
void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => SayacModel()),
        ChangeNotifierProvider(create: (_) => KullaniciModel()),
      ],
      child: const MyApp(),
    ),
  );
}

// Provider oku (consume)
class SayacSayfasi extends StatelessWidget {
  const SayacSayfasi({super.key});

  @override
  Widget build(BuildContext context) {
    // Tüm widget'ı yeniden çizer (rebuilds entire widget)
    final sayac = context.watch<SayacModel>();

    // Yalnızca okur, rebuild yapmaz (reads only, no rebuild)
    // final sayac = context.read<SayacModel>();

    return Column(
      children: [
        Text('${sayac.deger}'),
        ElevatedButton(
          onPressed: () => context.read<SayacModel>().artir(),
          child: const Text('+1'),
        ),
      ],
    );
  }
}

// Consumer ile kısmi rebuild (partial rebuild with Consumer)
Consumer<SayacModel>(
  builder: (context, sayac, child) {
    return Text('${sayac.deger}');
  },
);

// Selector — belirli değere göre rebuild (rebuild on specific value)
Selector<SayacModel, int>(
  selector: (_, model) => model.deger,
  builder: (_, deger, __) => Text('$deger'),
);
yaml
# pubspec.yaml
dependencies:
  flutter_riverpod: ^2.5.0
  riverpod_annotation: ^2.3.0

dev_dependencies:
  riverpod_generator: ^2.4.0
  build_runner: ^2.4.0
dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

// Basit provider (simple provider)
final selamProvider = Provider<String>((ref) => 'Merhaba Flutter');

// StateProvider — basit durum (simple state)
final sayacProvider = StateProvider<int>((ref) => 0);

// StateNotifierProvider — karmaşık durum (complex state)
class TodoListNotifier extends StateNotifier<List<Todo>> {
  TodoListNotifier() : super([]);

  void ekle(Todo todo) {
    state = [...state, todo];
  }

  void sil(String id) {
    state = state.where((t) => t.id != id).toList();
  }

  void tpieDegistir(String id) {
    state = state.map((t) {
      return t.id == id ? t.copyWith(tamamlandi: !t.tamamlandi) : t;
    }).toList();
  }
}

final todoListProvider =
    StateNotifierProvider<TodoListNotifier, List<Todo>>((ref) {
  return TodoListNotifier();
});

// FutureProvider — asenkron veri (async data)
final kullanicilarProvider = FutureProvider<List<Kullanici>>((ref) async {
  final api = ref.read(apiServisProvider);
  return api.kullanicilariGetir();
});

// StreamProvider — gerçek zamanlı veri (real-time data)
final mesajlarProvider = StreamProvider<List<Mesaj>>((ref) {
  final firestore = ref.read(firestoreProvider);
  return firestore.collection('mesajlar').snapshots().map(
        (s) => s.docs.map((d) => Mesaj.fromJson(d.data())).toList(),
      );
});

// AsyncNotifierProvider — Riverpod 2.0 önerilen (recommended)
class KullaniciNotifier extends AsyncNotifier<Kullanici?> {
  @override
  Future<Kullanici?> build() async {
    // Başlangıç verisi yükle (load initial data)
    return await ref.read(apiServisProvider).meGetir();
  }

  Future<void> girisYap(String email, String sifre) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() async {
      return await ref.read(apiServisProvider).girisYap(email, sifre);
    });
  }

  Future<void> cikisYap() async {
    await ref.read(apiServisProvider).cikisYap();
    state = const AsyncData(null);
  }
}

final kullaniciProvider =
    AsyncNotifierProvider<KullaniciNotifier, Kullanici?>(() {
  return KullaniciNotifier();
});

// Uygulama başlangıcı (app initialization)
void main() {
  runApp(
    const ProviderScope(child: MyApp()),
  );
}

// Widget'ta kullanım (usage in widget)
class KullaniciSayfasi extends ConsumerWidget {
  const KullaniciSayfasi({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final kullanici = ref.watch(kullaniciProvider);

    return kullanici.when(
      data: (k) => k != null
          ? Text('Hoş geldin, ${k.ad}')
          : const Text('Giriş yapılmadı'),
      loading: () => const CircularProgressIndicator(),
      error: (e, st) => Text('Hata: $e'),
    );
  }
}

// StatefulWidget ile Riverpod (with ConsumerStatefulWidget)
class ProfilSayfasi extends ConsumerStatefulWidget {
  const ProfilSayfasi({super.key});

  @override
  ConsumerState<ProfilSayfasi> createState() => _ProfilSayfasiState();
}

class _ProfilSayfasiState extends ConsumerState<ProfilSayfasi> {
  @override
  void initState() {
    super.initState();
    // ref.read kullanılabilir (can use ref.read)
  }

  @override
  Widget build(BuildContext context) {
    final profil = ref.watch(profilProvider);
    return Text(profil.ad);
  }
}

Bloc / Cubit

yaml
# pubspec.yaml
dependencies:
  flutter_bloc: ^8.1.0
  equatable: ^2.0.0
dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';

// --- Cubit (basit durumlar için — for simple states) ---
class SayacCubit extends Cubit<int> {
  SayacCubit() : super(0);

  void artir() => emit(state + 1);
  void azalt() => emit(state - 1);
  void sifirla() => emit(0);
}

// Cubit kullanımı (usage)
BlocProvider(
  create: (_) => SayacCubit(),
  child: BlocBuilder<SayacCubit, int>(
    builder: (context, sayac) {
      return Text('$sayac');
    },
  ),
);

// --- Bloc (karmaşık durumlar için — for complex states) ---

// Events (olaylar)
sealed class AuthEvent extends Equatable {
  @override
  List<Object?> get props => [];
}

class GirisYapildi extends AuthEvent {
  final String email;
  final String sifre;

  GirisYapildi({required this.email, required this.sifre});

  @override
  List<Object?> get props => [email, sifre];
}

class CikisYapildi extends AuthEvent {}

// States (durumlar)
sealed class AuthState extends Equatable {
  @override
  List<Object?> get props => [];
}

class AuthBaslangic extends AuthState {}
class AuthYukleniyor extends AuthState {}
class AuthGirisYapildi extends AuthState {
  final Kullanici kullanici;
  AuthGirisYapildi(this.kullanici);
  @override
  List<Object?> get props => [kullanici];
}
class AuthHata extends AuthState {
  final String mesaj;
  AuthHata(this.mesaj);
  @override
  List<Object?> get props => [mesaj];
}

// Bloc
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final AuthRepository _repo;

  AuthBloc(this._repo) : super(AuthBaslangic()) {
    on<GirisYapildi>(_girisYap);
    on<CikisYapildi>(_cikisYap);
  }

  Future<void> _girisYap(
    GirisYapildi event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthYukleniyor());
    try {
      final kullanici = await _repo.girisYap(event.email, event.sifre);
      emit(AuthGirisYapildi(kullanici));
    } catch (e) {
      emit(AuthHata(e.toString()));
    }
  }

  Future<void> _cikisYap(
    CikisYapildi event,
    Emitter<AuthState> emit,
  ) async {
    await _repo.cikisYap();
    emit(AuthBaslangic());
  }
}

// Bloc widget'ta kullanımı (usage in widget)
BlocProvider(
  create: (context) => AuthBloc(context.read<AuthRepository>()),
  child: BlocConsumer<AuthBloc, AuthState>(
    listener: (context, state) {
      if (state is AuthHata) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text(state.mesaj)),
        );
      }
    },
    builder: (context, state) {
      return switch (state) {
        AuthBaslangic() => const GirisFormu(),
        AuthYukleniyor() => const CircularProgressIndicator(),
        AuthGirisYapildi(:final kullanici) => Text('Hoş geldin ${kullanici.ad}'),
        AuthHata(:final mesaj) => Text('Hata: $mesaj'),
      };
    },
  ),
);

Karşılaştırma Tablosu (Comparison Table)

Özellik (Feature)setStateProviderRiverpodBloc
Öğrenme eğrisi (Learning curve)Kolay (Easy)Orta (Medium)Orta-Zor (Med-Hard)Zor (Hard)
Ölçeklenebilirlik (Scalability)Düşük (Low)Orta (Medium)Yüksek (High)Yüksek (High)
Test edilebilirlik (Testability)Zor (Hard)Orta (Medium)Yüksek (High)Yüksek (High)
BoilerplateAz (Low)Orta (Medium)Orta (Medium)Çok (High)
Hata ayıklama (Debugging)Zor (Hard)Orta (Medium)İyi (Good)Çok iyi (Very good)
Kullanım alanı (Use case)Küçük widget (Small widget)Orta proje (Medium project)Her boyut (Any size)Büyük proje (Large project)
Reaktif (Reactive)Hayır (No)Evet (Yes)Evet (Yes)Evet (Yes)
DevTools desteğiYok (None)Sınırlı (Limited)Var (Yes)Çok iyi (Very good)

9) HTTP ve API (HTTP & API)

yaml
# pubspec.yaml
dependencies:
  dio: ^5.4.0
  json_annotation: ^4.8.0

dev_dependencies:
  json_serializable: ^6.7.0
  build_runner: ^2.4.0
dart
import 'package:dio/dio.dart';

// Dio istemci yapılandırması (Dio client configuration)
class ApiIstemci {
  late final Dio _dio;

  ApiIstemci() {
    _dio = Dio(BaseOptions(
      baseUrl: 'https://api.example.com/v1',
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    ));

    // İstisna yakalayıcı (interceptor)
    _dio.interceptors.addAll([
      _AuthInterceptor(),
      LogInterceptor(
        requestBody: true,
        responseBody: true,
      ),
    ]);
  }

  // GET isteği (GET request)
  Future<List<Urun>> urunleriGetir({int sayfa = 1}) async {
    try {
      final yanit = await _dio.get(
        '/urunler',
        queryParameters: {'sayfa': sayfa, 'limit': 20},
      );
      final liste = yanit.data['data'] as List;
      return liste.map((j) => Urun.fromJson(j)).toList();
    } on DioException catch (e) {
      throw _hataYonet(e);
    }
  }

  // POST isteği (POST request)
  Future<Urun> urunOlustur(UrunOlusturDto dto) async {
    try {
      final yanit = await _dio.post('/urunler', data: dto.toJson());
      return Urun.fromJson(yanit.data);
    } on DioException catch (e) {
      throw _hataYonet(e);
    }
  }

  // PUT isteği (PUT request)
  Future<Urun> urunGuncelle(String id, Map<String, dynamic> veri) async {
    try {
      final yanit = await _dio.put('/urunler/$id', data: veri);
      return Urun.fromJson(yanit.data);
    } on DioException catch (e) {
      throw _hataYonet(e);
    }
  }

  // DELETE isteği (DELETE request)
  Future<void> urunSil(String id) async {
    try {
      await _dio.delete('/urunler/$id');
    } on DioException catch (e) {
      throw _hataYonet(e);
    }
  }

  // Dosya yükleme (file upload)
  Future<String> dosyaYukle(File dosya) async {
    final formData = FormData.fromMap({
      'dosya': await MultipartFile.fromFile(
        dosya.path,
        filename: dosya.path.split('/').last,
      ),
    });
    final yanit = await _dio.post('/yukle', data: formData);
    return yanit.data['url'];
  }

  // Hata yönetimi (error handling)
  AppException _hataYonet(DioException e) {
    return switch (e.type) {
      DioExceptionType.connectionTimeout => AppException('Bağlantı zaman aşımı'),
      DioExceptionType.receiveTimeout => AppException('Yanıt zaman aşımı'),
      DioExceptionType.badResponse => _httpHata(e.response?.statusCode),
      DioExceptionType.cancel => AppException('İstek iptal edildi'),
      _ => AppException('Bağlantı hatası'),
    };
  }

  AppException _httpHata(int? kod) {
    return switch (kod) {
      400 => AppException('Geçersiz istek'),
      401 => AppException('Yetkisiz erişim'),
      403 => AppException('Erişim reddedildi'),
      404 => AppException('Bulunamadı'),
      422 => AppException('Doğrulama hatası'),
      500 => AppException('Sunucu hatası'),
      _ => AppException('Bilinmeyen hata: $kod'),
    };
  }
}

// Auth Interceptor — token ekleme (token injection)
class _AuthInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    final token = TokenServisi.token;
    if (token != null) {
      options.headers['Authorization'] = 'Bearer $token';
    }
    handler.next(options);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    if (err.response?.statusCode == 401) {
      // Token yenile veya çıkış yap (refresh token or logout)
    }
    handler.next(err);
  }
}

JSON Serialization — json_serializable

dart
import 'package:json_annotation/json_annotation.dart';

part 'kullanici.g.dart'; // dart run build_runner build ile oluştur

@JsonSerializable()
class Kullanici {
  final int id;
  final String ad;
  final String email;

  @JsonKey(name: 'olusturulma_tarihi') // farklı JSON key
  final DateTime olusturulmaTarihi;

  @JsonKey(includeIfNull: false) // null ise JSON'a ekleme
  final String? telefon;

  const Kullanici({
    required this.id,
    required this.ad,
    required this.email,
    required this.olusturulmaTarihi,
    this.telefon,
  });

  factory Kullanici.fromJson(Map<String, dynamic> json) =>
      _$KullaniciFromJson(json);

  Map<String, dynamic> toJson() => _$KullaniciToJson(this);
}

// Kod üretme komutu (code generation command)
// dart run build_runner build --delete-conflicting-outputs
// veya izleme modu (or watch mode):
// dart run build_runner watch

Freezed — İmmutable Modeller (Immutable Models)

yaml
# pubspec.yaml
dependencies:
  freezed_annotation: ^2.4.0
  json_annotation: ^4.8.0

dev_dependencies:
  freezed: ^2.5.0
  json_serializable: ^6.7.0
  build_runner: ^2.4.0
dart
import 'package:freezed_annotation/freezed_annotation.dart';

part 'urun.freezed.dart';
part 'urun.g.dart';

@freezed
class Urun with _$Urun {
  const factory Urun({
    required int id,
    required String ad,
    required double fiyat,
    @Default('') String aciklama,
    @JsonKey(name: 'kategori_id') required int kategoriId,
    String? gorselUrl,
  }) = _Urun;

  factory Urun.fromJson(Map<String, dynamic> json) => _$UrunFromJson(json);
}

// Kullanım (usage)
final urun = Urun(id: 1, ad: 'Flutter Kitabı', fiyat: 99.90, kategoriId: 5);

// copyWith — kopyala ve değiştir (copy and modify)
final guncellenmis = urun.copyWith(fiyat: 79.90);

// Eşitlik kontrolü — otomatik (equality check — automatic)
urun == guncellenmis; // false (farklı fiyat)

// Freezed union types (sealed durumlar için — for sealed states)
@freezed
sealed class ApiDurumu<T> with _$ApiDurumu<T> {
  const factory ApiDurumu.baslangic() = _Baslangic;
  const factory ApiDurumu.yukleniyor() = _Yukleniyor;
  const factory ApiDurumu.basarili(T veri) = _Basarili;
  const factory ApiDurumu.hata(String mesaj) = _Hata;
}

// Pattern matching
Widget goster(ApiDurumu<List<Urun>> durum) {
  return durum.when(
    baslangic: () => const SizedBox.shrink(),
    yukleniyor: () => const CircularProgressIndicator(),
    basarili: (urunler) => UrunListesi(urunler: urunler),
    hata: (mesaj) => HataMesaji(mesaj: mesaj),
  );
}

10) Yerel Depolama (Local Storage)

SharedPreferences — Basit Anahtar-Değer (Simple Key-Value)

yaml
# pubspec.yaml
dependencies:
  shared_preferences: ^2.2.0
dart
import 'package:shared_preferences/shared_preferences.dart';

class Tercihler {
  static late SharedPreferences _prefs;

  // Uygulama başlangıcında çağır (call at app start)
  static Future<void> baslat() async {
    _prefs = await SharedPreferences.getInstance();
  }

  // Tema modu (theme mode)
  static bool get koyuTema => _prefs.getBool('koyu_tema') ?? false;
  static Future<void> koyuTemaAyarla(bool deger) =>
      _prefs.setBool('koyu_tema', deger);

  // Dil ayarı (language setting)
  static String get dil => _prefs.getString('dil') ?? 'tr';
  static Future<void> dilAyarla(String dil) => _prefs.setString('dil', dil);

  // İlk açılış kontrolü (first launch check)
  static bool get ilkAcilis => _prefs.getBool('ilk_acilis') ?? true;
  static Future<void> ilkAcilisTamamlandi() =>
      _prefs.setBool('ilk_acilis', false);

  // Tümünü temizle (clear all)
  static Future<void> temizle() => _prefs.clear();
}

// Kullanım (usage) — main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Tercihler.baslat();
  runApp(const MyApp());
}

Hive — Hızlı NoSQL Veritabanı (Fast NoSQL Database)

yaml
# pubspec.yaml
dependencies:
  hive: ^2.2.3
  hive_flutter: ^1.1.0

dev_dependencies:
  hive_generator: ^2.0.1
  build_runner: ^2.4.0
dart
import 'package:hive_flutter/hive_flutter.dart';

// Model — Hive adaptörü ile (with Hive adapter)
part 'gorev.g.dart';

@HiveType(typeId: 0)
class Gorev extends HiveObject {
  @HiveField(0)
  String baslik;

  @HiveField(1)
  bool tamamlandi;

  @HiveField(2)
  DateTime olusturulma;

  Gorev({
    required this.baslik,
    this.tamamlandi = false,
    DateTime? olusturulma,
  }) : olusturulma = olusturulma ?? DateTime.now();
}

// Başlatma (initialization)
Future<void> main() async {
  await Hive.initFlutter();
  Hive.registerAdapter(GorevAdapter()); // otomatik üretilir (auto-generated)
  await Hive.openBox<Gorev>('gorevler');
  runApp(const MyApp());
}

// CRUD işlemleri (CRUD operations)
class GorevDepo {
  final Box<Gorev> _kutu = Hive.box<Gorev>('gorevler');

  // Ekle (add)
  Future<int> ekle(Gorev gorev) => _kutu.add(gorev);

  // Hepsini getir (get all)
  List<Gorev> hepsiniGetir() => _kutu.values.toList();

  // Güncelle (update)
  Future<void> guncelle(int index, Gorev gorev) => _kutu.putAt(index, gorev);

  // Sil (delete)
  Future<void> sil(int index) => _kutu.deleteAt(index);

  // Dinle — ValueListenableBuilder ile (listen with ValueListenableBuilder)
  ValueListenable<Box<Gorev>> dinle() => _kutu.listenable();
}

// Widget'ta dinleme (listening in widget)
ValueListenableBuilder<Box<Gorev>>(
  valueListenable: GorevDepo().dinle(),
  builder: (context, kutu, _) {
    final gorevler = kutu.values.toList();
    return ListView.builder(
      itemCount: gorevler.length,
      itemBuilder: (context, i) => ListTile(title: Text(gorevler[i].baslik)),
    );
  },
);

sqflite — SQLite Veritabanı (SQLite Database)

yaml
# pubspec.yaml
dependencies:
  sqflite: ^2.3.0
  path: ^1.9.0
dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class VeritabaniYardimcisi {
  static Database? _db;

  static Future<Database> get veritabani async {
    _db ??= await _baslat();
    return _db!;
  }

  static Future<Database> _baslat() async {
    final yol = join(await getDatabasesPath(), 'uygulama.db');

    return openDatabase(
      yol,
      version: 2,
      onCreate: (db, version) async {
        await db.execute('''
          CREATE TABLE kullanicilar (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            ad TEXT NOT NULL,
            email TEXT UNIQUE NOT NULL,
            olusturulma TEXT DEFAULT CURRENT_TIMESTAMP
          )
        ''');
      },
      onUpgrade: (db, oldVersion, newVersion) async {
        if (oldVersion < 2) {
          await db.execute('ALTER TABLE kullanicilar ADD COLUMN telefon TEXT');
        }
      },
    );
  }

  // CRUD
  static Future<int> ekle(Map<String, dynamic> veri) async {
    final db = await veritabani;
    return db.insert('kullanicilar', veri);
  }

  static Future<List<Map<String, dynamic>>> hepsiniGetir() async {
    final db = await veritabani;
    return db.query('kullanicilar', orderBy: 'id DESC');
  }

  static Future<int> guncelle(int id, Map<String, dynamic> veri) async {
    final db = await veritabani;
    return db.update('kullanicilar', veri, where: 'id = ?', whereArgs: [id]);
  }

  static Future<int> sil(int id) async {
    final db = await veritabani;
    return db.delete('kullanicilar', where: 'id = ?', whereArgs: [id]);
  }
}

flutter_secure_storage — Güvenli Depolama (Secure Storage)

yaml
# pubspec.yaml
dependencies:
  flutter_secure_storage: ^9.0.0
dart
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class GuvenliDepo {
  static const _depo = FlutterSecureStorage(
    aOptions: AndroidOptions(encryptedSharedPreferences: true),
    iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
  );

  // Token kaydet (save token)
  static Future<void> tokenKaydet(String token) =>
      _depo.write(key: 'auth_token', value: token);

  // Token oku (read token)
  static Future<String?> tokenOku() => _depo.read(key: 'auth_token');

  // Token sil (delete token)
  static Future<void> tokenSil() => _depo.delete(key: 'auth_token');

  // Tümünü temizle (clear all)
  static Future<void> temizle() => _depo.deleteAll();
}

Depolama Karşılaştırması (Storage Comparison)

Özellik (Feature)SharedPreferencesHivesqfliteSecureStorage
Tip (Type)Key-ValueNoSQLSQLKey-Value (şifreli/encrypted)
Hız (Speed)Hızlı (Fast)Çok hızlı (Very fast)Orta (Medium)Orta (Medium)
Karmaşıklık (Complexity)Düşük (Low)Orta (Medium)Yüksek (High)Düşük (Low)
Sorgu (Query)Yok (None)Sınırlı (Limited)Tam SQL (Full SQL)Yok (None)
Kullanım (Use case)Ayarlar (Settings)Önbellek, listeler (Cache, lists)İlişkisel veri (Relational data)Tokenlar, şifreler (Tokens, passwords)
Şifreleme (Encryption)Hayır (No)İsteğe bağlı (Optional)Hayır (No)Evet (Yes)

11) İzinler ve Cihaz Erişimi (Permissions & Device Access)

permission_handler

yaml
# pubspec.yaml
dependencies:
  permission_handler: ^11.3.0
dart
import 'package:permission_handler/permission_handler.dart';

class IzinYardimcisi {
  // Kamera izni iste (request camera permission)
  static Future<bool> kameraIzniIste() async {
    final durum = await Permission.camera.request();
    return durum.isGranted;
  }

  // Konum izni iste (request location permission)
  static Future<bool> konumIzniIste() async {
    final durum = await Permission.locationWhenInUse.request();
    if (durum.isPermanentlyDenied) {
      // Kullanıcıyı ayarlara yönlendir (redirect user to settings)
      await openAppSettings();
      return false;
    }
    return durum.isGranted;
  }

  // Birden fazla izin iste (request multiple permissions)
  static Future<Map<Permission, PermissionStatus>> cokluIzinIste() async {
    return await [
      Permission.camera,
      Permission.microphone,
      Permission.storage,
    ].request();
  }

  // İzin durumunu kontrol et (check permission status)
  static Future<void> izinKontrol(Permission izin) async {
    final durum = await izin.status;

    if (durum.isGranted) {
      // Verilmiş (granted)
    } else if (durum.isDenied) {
      // Reddedilmiş — tekrar isteyebilirsin (denied — can ask again)
    } else if (durum.isPermanentlyDenied) {
      // Kalıcı reddedilmiş — ayarlara yönlendir
      // (permanently denied — redirect to settings)
      await openAppSettings();
    } else if (durum.isRestricted) {
      // Kısıtlı — iOS'ta (restricted — iOS only)
    }
  }
}

İzin Tablosu (Permission Table)

İzin (Permission)Android ManifestiOS Info.plist
Kamera (Camera)CAMERANSCameraUsageDescription
Mikrofon (Microphone)RECORD_AUDIONSMicrophoneUsageDescription
Konum (Location)ACCESS_FINE_LOCATIONNSLocationWhenInUseUsageDescription
Fotoğraflar (Photos)READ_MEDIA_IMAGESNSPhotoLibraryUsageDescription
Bildirim (Notification)POST_NOTIFICATIONSOtomatik (Automatic)
Depolama (Storage)READ_EXTERNAL_STORAGE
Kişiler (Contacts)READ_CONTACTSNSContactsUsageDescription
BluetoothBLUETOOTH_CONNECTNSBluetoothAlwaysUsageDescription

AndroidManifest.xml Ayarları (Settings)

xml
<!-- android/app/src/main/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- İzinler (Permissions) -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:label="My App"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <!-- ... -->
    </application>
</manifest>

iOS Info.plist Ayarları (Settings)

xml
<!-- ios/Runner/Info.plist -->
<dict>
    <key>NSCameraUsageDescription</key>
    <string>Fotoğraf çekmek için kamera erişimi gerekli</string>

    <key>NSMicrophoneUsageDescription</key>
    <string>Ses kaydı için mikrofon erişimi gerekli</string>

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Konum bazlı hizmetler için konum erişimi gerekli</string>

    <key>NSPhotoLibraryUsageDescription</key>
    <string>Fotoğraf seçmek için galeri erişimi gerekli</string>

    <key>NSContactsUsageDescription</key>
    <string>Kişi listesine erişim gerekli</string>
</dict>

iOS Podfile Ayarları (Podfile Settings)

ruby
# ios/Podfile
platform :ios, '13.0'

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
      # İzin ayarları (permission settings)
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        '$(inherited)',
        'PERMISSION_CAMERA=1',
        'PERMISSION_MICROPHONE=1',
        'PERMISSION_LOCATION=1',
        'PERMISSION_PHOTOS=1',
        'PERMISSION_NOTIFICATIONS=1',
        'PERMISSION_CONTACTS=1',
      ]
    end
  end
end

Kamera Kullanımı (Camera Usage)

yaml
# pubspec.yaml
dependencies:
  image_picker: ^1.0.0
  camera: ^0.11.0
dart
import 'package:image_picker/image_picker.dart';

class KameraServisi {
  final _picker = ImagePicker();

  // Galeriden fotoğraf seç (pick from gallery)
  Future<XFile?> galeridanSec() async {
    return await _picker.pickImage(
      source: ImageSource.gallery,
      maxWidth: 1920,
      maxHeight: 1080,
      imageQuality: 85,
    );
  }

  // Kamera ile çek (capture with camera)
  Future<XFile?> kameradanCek() async {
    return await _picker.pickImage(
      source: ImageSource.camera,
      preferredCameraDevice: CameraDevice.rear,
    );
  }

  // Birden fazla fotoğraf seç (pick multiple photos)
  Future<List<XFile>> cokluSec() async {
    return await _picker.pickMultiImage(
      maxWidth: 1920,
      imageQuality: 85,
    );
  }
}

Konum Servisi (Location Service)

yaml
# pubspec.yaml
dependencies:
  geolocator: ^12.0.0
  geocoding: ^3.0.0
dart
import 'package:geolocator/geolocator.dart';

class KonumServisi {
  // Mevcut konum al (get current location)
  Future<Position> mevcutKonum() async {
    final izin = await Geolocator.checkPermission();
    if (izin == LocationPermission.denied) {
      final yeniIzin = await Geolocator.requestPermission();
      if (yeniIzin == LocationPermission.denied) {
        throw Exception('Konum izni reddedildi');
      }
    }

    return await Geolocator.getCurrentPosition(
      desiredAccuracy: LocationAccuracy.high,
    );
  }

  // Konum değişikliklerini dinle (listen to location changes)
  Stream<Position> konumDinle() {
    return Geolocator.getPositionStream(
      locationSettings: const LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 10, // 10 metre minimum hareket (10m minimum movement)
      ),
    );
  }

  // İki nokta arası mesafe (distance between two points)
  double mesafeHesapla(Position a, Position b) {
    return Geolocator.distanceBetween(
      a.latitude, a.longitude,
      b.latitude, b.longitude,
    );
  }
}

Bildirim Servisi (Notification Service)

yaml
# pubspec.yaml
dependencies:
  flutter_local_notifications: ^17.0.0
dart
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

class BildirimServisi {
  static final _bildirimler = FlutterLocalNotificationsPlugin();

  static Future<void> baslat() async {
    const androidAyar = AndroidInitializationSettings('@mipmap/ic_launcher');
    const iosAyar = DarwinInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: true,
    );

    await _bildirimler.initialize(
      const InitializationSettings(android: androidAyar, iOS: iosAyar),
      onDidReceiveNotificationResponse: (yanit) {
        // Bildirime tıklanınca (when notification tapped)
        print('Bildirim tıklandı: ${yanit.payload}');
      },
    );
  }

  static Future<void> goster({
    required String baslik,
    required String icerik,
    String? payload,
  }) async {
    const androidDetay = AndroidNotificationDetails(
      'genel_kanal', // kanal ID (channel ID)
      'Genel Bildirimler', // kanal adı (channel name)
      importance: Importance.high,
      priority: Priority.high,
    );

    await _bildirimler.show(
      DateTime.now().millisecond, // benzersiz ID (unique ID)
      baslik,
      icerik,
      const NotificationDetails(android: androidDetay),
      payload: payload,
    );
  }
}

12) Animasyon (Animation)

Implicit Animasyonlar (Implicit Animations)

Otomatik — sadece değer değiştir, Flutter animasyonu halleder. (Automatic — just change the value, Flutter handles the animation.)

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

  @override
  State<ImplicitOrnekler> createState() => _ImplicitOrneklerState();
}

class _ImplicitOrneklerState extends State<ImplicitOrnekler> {
  bool _buyuk = false;
  bool _gorunur = true;
  Color _renk = Colors.blue;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // AnimatedContainer — boyut, renk, kenar animasyonu
        // (size, color, border animation)
        AnimatedContainer(
          duration: const Duration(milliseconds: 500),
          curve: Curves.easeInOut,
          width: _buyuk ? 200 : 100,
          height: _buyuk ? 200 : 100,
          decoration: BoxDecoration(
            color: _renk,
            borderRadius: BorderRadius.circular(_buyuk ? 24 : 8),
          ),
          child: const Center(child: Text('Kutu')),
        ),

        // AnimatedOpacity — opaklık animasyonu (opacity animation)
        AnimatedOpacity(
          duration: const Duration(milliseconds: 300),
          opacity: _gorunur ? 1.0 : 0.0,
          child: const Text('Görünür/Gizli metin'),
        ),

        // AnimatedAlign — hizalama animasyonu (alignment animation)
        AnimatedAlign(
          duration: const Duration(milliseconds: 400),
          alignment: _buyuk ? Alignment.centerRight : Alignment.centerLeft,
          child: Container(width: 50, height: 50, color: Colors.red),
        ),

        // AnimatedPadding — boşluk animasyonu (padding animation)
        AnimatedPadding(
          duration: const Duration(milliseconds: 300),
          padding: EdgeInsets.all(_buyuk ? 32 : 8),
          child: Container(color: Colors.green, height: 50),
        ),

        // AnimatedSwitcher — widget geçiş animasyonu (widget transition)
        AnimatedSwitcher(
          duration: const Duration(milliseconds: 500),
          transitionBuilder: (child, animation) {
            return ScaleTransition(scale: animation, child: child);
          },
          child: Text(
            '$_buyuk',
            key: ValueKey(_buyuk), // key değişince animasyon (animate on key change)
          ),
        ),

        // AnimatedCrossFade — iki widget arası geçiş (crossfade between two)
        AnimatedCrossFade(
          duration: const Duration(milliseconds: 300),
          crossFadeState:
              _gorunur ? CrossFadeState.showFirst : CrossFadeState.showSecond,
          firstChild: const Icon(Icons.check, size: 48, color: Colors.green),
          secondChild: const Icon(Icons.close, size: 48, color: Colors.red),
        ),

        ElevatedButton(
          onPressed: () => setState(() {
            _buyuk = !_buyuk;
            _gorunur = !_gorunur;
            _renk = _renk == Colors.blue ? Colors.orange : Colors.blue;
          }),
          child: const Text('Animasyonu Tetikle'),
        ),
      ],
    );
  }
}

Explicit Animasyonlar (Explicit Animations)

Tam kontrol — AnimationController ile yönetilir. (Full control — managed with AnimationController.)

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

  @override
  State<DonmeAnimasyonu> createState() => _DonmeAnimasyonuState();
}

class _DonmeAnimasyonuState extends State<DonmeAnimasyonu>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _donme;
  late Animation<double> _boyut;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this, // TickerProvider — performans için (for performance)
      duration: const Duration(seconds: 2),
    );

    // Tween — değer aralığı (value range)
    _donme = Tween<double>(begin: 0, end: 2 * 3.14159).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );

    _boyut = Tween<double>(begin: 50, end: 150).animate(
      CurvedAnimation(parent: _controller, curve: Curves.elasticOut),
    );

    // Tekrarlı animasyon (repeating animation)
    _controller.repeat(reverse: true);
  }

  @override
  void dispose() {
    _controller.dispose(); // Önemli! Bellek sızıntısını önler (prevents memory leak)
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Transform.rotate(
          angle: _donme.value,
          child: Container(
            width: _boyut.value,
            height: _boyut.value,
            color: Colors.purple,
          ),
        );
      },
    );
  }
}

Hero Animasyonu (Hero Animation)

dart
// Kaynak sayfa (source page)
GestureDetector(
  onTap: () => Navigator.push(
    context,
    MaterialPageRoute(builder: (_) => DetaySayfasi(urun: urun)),
  ),
  child: Hero(
    tag: 'urun-${urun.id}', // benzersiz etiket (unique tag)
    child: Image.network(urun.gorselUrl, width: 100, height: 100),
  ),
);

// Hedef sayfa (destination page)
class DetaySayfasi extends StatelessWidget {
  final Urun urun;
  const DetaySayfasi({super.key, required this.urun});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Hero(
        tag: 'urun-${urun.id}', // aynı etiket (same tag)
        child: Image.network(urun.gorselUrl, fit: BoxFit.cover),
      ),
    );
  }
}

Lottie Animasyonları (Lottie Animations)

yaml
# pubspec.yaml
dependencies:
  lottie: ^3.1.0
dart
import 'package:lottie/lottie.dart';

// Basit kullanım (simple usage)
Lottie.asset(
  'assets/animations/yukleniyor.json',
  width: 200,
  height: 200,
  repeat: true,
);

// Kontrollü kullanım (controlled usage)
class LottieKontrol extends StatefulWidget {
  const LottieKontrol({super.key});

  @override
  State<LottieKontrol> createState() => _LottieKontrolState();
}

class _LottieKontrolState extends State<LottieKontrol>
    with TickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        if (_controller.isCompleted) {
          _controller.reverse();
        } else {
          _controller.forward();
        }
      },
      child: Lottie.asset(
        'assets/animations/kalp.json',
        controller: _controller,
        onLoaded: (composition) {
          _controller.duration = composition.duration;
        },
      ),
    );
  }
}

Sayfa Geçiş Animasyonu (Page Transition)

dart
// Özel sayfa geçişi (custom page transition)
class KaydirmaGecisi extends PageRouteBuilder {
  final Widget sayfa;

  KaydirmaGecisi({required this.sayfa})
      : super(
          pageBuilder: (context, animation, secondaryAnimation) => sayfa,
          transitionsBuilder: (context, animation, secondaryAnimation, child) {
            const baslangic = Offset(1.0, 0.0); // sağdan (from right)
            const bitis = Offset.zero;
            final tween = Tween(begin: baslangic, end: bitis)
                .chain(CurveTween(curve: Curves.easeInOut));

            return SlideTransition(
              position: animation.drive(tween),
              child: child,
            );
          },
          transitionDuration: const Duration(milliseconds: 400),
        );
}

// Kullanım (usage)
Navigator.push(context, KaydirmaGecisi(sayfa: const DetaySayfasi()));

// Staggered animasyon — sıralı animasyonlar (sequential animations)
class SiraliAnimasyon extends StatefulWidget {
  const SiraliAnimasyon({super.key});

  @override
  State<SiraliAnimasyon> createState() => _SiraliAnimasyonState();
}

class _SiraliAnimasyonState extends State<SiraliAnimasyon>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late List<Animation<Offset>> _kaydirmalar;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1500),
    );

    // Her öğe farklı zamanda başlar (each item starts at different time)
    _kaydirmalar = List.generate(5, (i) {
      final baslangic = i * 0.1;
      final bitis = baslangic + 0.4;
      return Tween<Offset>(
        begin: const Offset(1, 0),
        end: Offset.zero,
      ).animate(CurvedAnimation(
        parent: _controller,
        curve: Interval(baslangic, bitis.clamp(0.0, 1.0), curve: Curves.easeOut),
      ));
    });

    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: List.generate(5, (i) {
        return SlideTransition(
          position: _kaydirmalar[i],
          child: FadeTransition(
            opacity: _controller,
            child: ListTile(title: Text('Öğe $i')),
          ),
        );
      }),
    );
  }
}

13) Gesture (Jest / Dokunma İşleme)

GestureDetector

dart
GestureDetector(
  // Dokunma (tap)
  onTap: () => print('Tıklandı'),
  onDoubleTap: () => print('Çift tıklandı'),
  onLongPress: () => print('Uzun basıldı'),

  // Sürükleme (drag)
  onPanStart: (detay) => print('Sürükleme başladı: ${detay.globalPosition}'),
  onPanUpdate: (detay) => print('Sürükleniyor: ${detay.delta}'),
  onPanEnd: (detay) => print('Sürükleme bitti'),

  // Ölçekleme — zoom (scale — zoom)
  onScaleStart: (detay) => print('Zoom başladı'),
  onScaleUpdate: (detay) => print('Ölçek: ${detay.scale}'),
  onScaleEnd: (detay) => print('Zoom bitti'),

  child: Container(
    width: 100,
    height: 100,
    color: Colors.blue,
    child: const Center(child: Text('Dokun')),
  ),
);

Dismissible — Kaydırarak Silme (Swipe to Dismiss)

dart
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return Dismissible(
      key: ValueKey(items[index].id),
      direction: DismissDirection.endToStart, // sağdan sola (right to left)

      // Arka plan (background)
      background: Container(
        color: Colors.red,
        alignment: Alignment.centerRight,
        padding: const EdgeInsets.only(right: 16),
        child: const Icon(Icons.delete, color: Colors.white),
      ),

      // Onay dialogu (confirmation dialog)
      confirmDismiss: (direction) async {
        return await showDialog<bool>(
          context: context,
          builder: (ctx) => AlertDialog(
            title: const Text('Silmek istediğinize emin misiniz?'),
            actions: [
              TextButton(
                onPressed: () => Navigator.pop(ctx, false),
                child: const Text('İptal'),
              ),
              TextButton(
                onPressed: () => Navigator.pop(ctx, true),
                child: const Text('Sil'),
              ),
            ],
          ),
        );
      },

      onDismissed: (direction) {
        setState(() => items.removeAt(index));
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Silindi')),
        );
      },
      child: ListTile(title: Text(items[index].baslik)),
    );
  },
);

Draggable ve DragTarget — Sürükle Bırak (Drag & Drop)

dart
// Sürüklenebilir widget (draggable widget)
Draggable<String>(
  data: 'Flutter',
  feedback: Material(
    elevation: 8,
    child: Container(
      padding: const EdgeInsets.all(16),
      color: Colors.blue,
      child: const Text('Sürükleniyor...', style: TextStyle(color: Colors.white)),
    ),
  ),
  childWhenDragging: Container(
    padding: const EdgeInsets.all(16),
    color: Colors.grey,
    child: const Text('Eski konum'),
  ),
  child: Container(
    padding: const EdgeInsets.all(16),
    color: Colors.blue,
    child: const Text('Beni sürükle'),
  ),
);

// Bırakma hedefi (drop target)
DragTarget<String>(
  onAcceptWithDetails: (detay) {
    print('Bırakılan veri: ${detay.data}');
  },
  onWillAcceptWithDetails: (detay) => true, // kabul et (accept)
  builder: (context, accepted, rejected) {
    return Container(
      width: 200,
      height: 200,
      color: accepted.isNotEmpty ? Colors.green.shade200 : Colors.grey.shade200,
      child: const Center(child: Text('Buraya bırak')),
    );
  },
);

RefreshIndicator — Çekerek Yenile (Pull to Refresh)

dart
RefreshIndicator(
  onRefresh: () async {
    await Future.delayed(const Duration(seconds: 2));
    // Verileri yenile (refresh data)
    await ref.read(urunlerProvider.notifier).yenile();
  },
  child: ListView.builder(
    itemCount: urunler.length,
    itemBuilder: (context, index) => UrunKarti(urun: urunler[index]),
  ),
);

InteractiveViewer — Yakınlaştırma/Uzaklaştırma (Zoom/Pan)

dart
InteractiveViewer(
  minScale: 0.5,
  maxScale: 4.0,
  boundaryMargin: const EdgeInsets.all(80),
  child: Image.network('https://example.com/buyuk-gorsel.jpg'),
);

14) Platform Kanalları (Platform Channels)

MethodChannel — Native Erişim (Native Access)

Dart tarafında kanal tanımlama (defining channel in Dart):

dart
import 'package:flutter/services.dart';

class PilServisi {
  static const _kanal = MethodChannel('com.example.app/pil');

  // Native'den veri al (get data from native)
  static Future<int> pilSeviyesiGetir() async {
    try {
      final seviye = await _kanal.invokeMethod<int>('pilSeviyesi');
      return seviye ?? -1;
    } on PlatformException catch (e) {
      print('Platform hatası: ${e.message}');
      return -1;
    }
  }

  // Native'e veri gönder (send data to native)
  static Future<String> cihazBilgisi() async {
    try {
      final bilgi = await _kanal.invokeMethod<String>('cihazBilgisi');
      return bilgi ?? 'Bilinmiyor';
    } on PlatformException catch (e) {
      return 'Hata: ${e.message}';
    }
  }
}

Android tarafı — Kotlin (Android side — Kotlin):

kotlin
// android/app/src/main/kotlin/.../MainActivity.kt
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.os.BatteryManager
import android.content.Context

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.example.app/pil"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                when (call.method) {
                    "pilSeviyesi" -> {
                        val batteryManager = getSystemService(Context.BATTERY_SERVICE)
                            as BatteryManager
                        val level = batteryManager.getIntProperty(
                            BatteryManager.BATTERY_PROPERTY_CAPACITY
                        )
                        result.success(level)
                    }
                    "cihazBilgisi" -> {
                        result.success("${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL}")
                    }
                    else -> result.notImplemented()
                }
            }
    }
}

iOS tarafı — Swift (iOS side — Swift):

swift
// ios/Runner/AppDelegate.swift
import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller = window?.rootViewController as! FlutterViewController
        let channel = FlutterMethodChannel(
            name: "com.example.app/pil",
            binaryMessenger: controller.binaryMessenger
        )

        channel.setMethodCallHandler { (call, result) in
            switch call.method {
            case "pilSeviyesi":
                UIDevice.current.isBatteryMonitoringEnabled = true
                let level = Int(UIDevice.current.batteryLevel * 100)
                result(level)
            case "cihazBilgisi":
                result("\(UIDevice.current.model) - \(UIDevice.current.systemVersion)")
            default:
                result(FlutterMethodNotImplemented)
            }
        }

        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

EventChannel — Sürekli Veri Akışı (Continuous Data Stream)

dart
// Dart tarafı (Dart side)
class SensorServisi {
  static const _kanal = EventChannel('com.example.app/sensor');

  static Stream<double> ivmeOlcerDinle() {
    return _kanal.receiveBroadcastStream().map((e) => e as double);
  }
}

// Kullanım (usage)
SensorServisi.ivmeOlcerDinle().listen(
  (deger) => print('İvme: $deger'),
  onError: (e) => print('Hata: $e'),
);

15) CLI Komutları Tablosu (CLI Commands Table)

Komut (Command)Açıklama (Description)
flutter create my_appYeni proje oluştur (create new project)
flutter create --org com.example .Organizasyonlu proje (project with org)
flutter runUygulamayı çalıştır (run app)
flutter run -d chromeChrome'da çalıştır (run on Chrome)
flutter run --releaseRelease modda çalıştır (run in release mode)
flutter run --profileProfile modda çalıştır (run in profile mode)
flutter build apkAndroid APK oluştur (build Android APK)
flutter build apk --split-per-abiABI bazında bölünmüş APK (split APK per ABI)
flutter build appbundleAndroid AAB oluştur (build Android AAB)
flutter build iosiOS derle (build iOS)
flutter build webWeb derle (build Web)
flutter pub getBağımlılıkları indir (fetch dependencies)
flutter pub add dioPaket ekle (add package)
flutter pub remove dioPaket kaldır (remove package)
flutter pub upgradePaketleri güncelle (upgrade packages)
flutter pub outdatedEski paketleri listele (list outdated packages)
flutter doctorOrtam kontrolü (environment check)
flutter doctor -vDetaylı ortam kontrolü (verbose environment check)
flutter analyzeStatik analiz (static analysis)
flutter testTestleri çalıştır (run tests)
flutter test --coverageKapsama raporu ile test (test with coverage)
flutter devicesBağlı cihazları listele (list connected devices)
flutter cleanBuild dosyalarını temizle (clean build files)
flutter channel stableStable kanala geç (switch to stable channel)
flutter upgradeFlutter SDK güncelle (upgrade Flutter SDK)
flutter gen-l10nYerelleştirme dosyaları oluştur (generate l10n files)
dart run build_runner buildKod üretme — freezed, json (code generation)
dart run build_runner watchSürekli kod üretme (continuous code generation)
dart fix --applyLint düzeltmelerini uygula (apply lint fixes)

16) Güvenlik (Security)

flutter_secure_storage — Hassas Veri Saklama (Storing Sensitive Data)

dart
// Bkz. Bölüm 10 — GuvenliDepo sınıfı
// (See Section 10 — GuvenliDepo class)

// Ek güvenlik ipuçları (additional security tips):
// - Token'ları asla SharedPreferences'ta saklama (never store tokens in SharedPreferences)
// - API key'leri koda gömme (don't embed API keys in code)
// - .env dosyası kullan (use .env file):
yaml
# pubspec.yaml
dependencies:
  flutter_dotenv: ^5.1.0
dart
import 'package:flutter_dotenv/flutter_dotenv.dart';

// .env dosyasından oku (read from .env)
await dotenv.load();
final apiKey = dotenv.env['API_KEY'];

// .gitignore'a .env ekle! (add .env to .gitignore!)

SSL Pinning — Sertifika Sabitleme (Certificate Pinning)

dart
// Dio ile SSL pinning (SSL pinning with Dio)
import 'dart:io';
import 'package:dio/io.dart';

class GuvenliApiIstemci {
  late final Dio _dio;

  GuvenliApiIstemci() {
    _dio = Dio();

    (_dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () {
      final client = HttpClient();

      // Sertifika doğrulama (certificate verification)
      client.badCertificateCallback = (cert, host, port) {
        // Sertifika parmak izini kontrol et (check certificate fingerprint)
        final beklenen = 'AB:CD:EF:12:34:56:...'; // SHA-256 fingerprint
        // Gerçek uygulamada parmak izini karşılaştır
        return false; // güvensiz sertifikayı reddet (reject unsafe cert)
      };

      return client;
    };
  }
}

Kod Gizleme — Obfuscation

bash
# Release build'de obfuscation
flutter build apk --obfuscate --split-debug-info=build/debug-info

# iOS
flutter build ios --obfuscate --split-debug-info=build/debug-info

# Debug info'yu saklayın — crash raporları için gerekli
# (Keep debug info — needed for crash reports)

Root / Jailbreak Tespiti (Root / Jailbreak Detection)

yaml
# pubspec.yaml
dependencies:
  flutter_jailbreak_detection: ^1.10.0
dart
import 'package:flutter_jailbreak_detection/flutter_jailbreak_detection.dart';

Future<void> guvenlikKontrol() async {
  final jailbroken = await FlutterJailbreakDetection.jailbroken;
  final developerMode = await FlutterJailbreakDetection.developerMode;

  if (jailbroken) {
    // Root/Jailbreak tespit edildi — uyar veya engelle
    // (Root/Jailbreak detected — warn or block)
    showDialog(
      context: context,
      builder: (_) => const AlertDialog(
        title: Text('Güvenlik Uyarısı'),
        content: Text('Bu cihaz root/jailbreak edilmiş görünüyor.'),
      ),
    );
  }
}

ProGuard Ayarları — Android (ProGuard Settings)

groovy
// android/app/build.gradle
android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                         'proguard-rules.pro'
        }
    }
}

Güvenlik Kontrol Listesi (Security Checklist)

Konu (Topic)Çözüm (Solution)
Hassas veri saklama (Sensitive data storage)flutter_secure_storage
API anahtarları (API keys).env dosyası + .gitignore
Ağ güvenliği (Network security)SSL pinning, HTTPS zorunlu
Kod koruma (Code protection)Obfuscation + ProGuard
Root tespiti (Root detection)flutter_jailbreak_detection
Ekran kaydı engelleme (Screenshot prevention)flutter_windowmanager (Android)
Clipboard temizleme (Clipboard clearing)Hassas veriyi kopyalama, zamanlayıcı ile temizle
Loglama (Logging)Release'de logları kapat (kReleaseMode)

17) İpuçları (Tips)

const Constructor — Performans

dart
// KÖTÜ — her build'de yeni nesne oluşturur (BAD — creates new object every build)
Container(
  padding: EdgeInsets.all(16), // her seferinde yeni EdgeInsets
  child: Text('Merhaba'),     // her seferinde yeni Text
);

// İYİ — const ile derleme zamanında oluşturulur (GOOD — created at compile time)
Container(
  padding: const EdgeInsets.all(16), // tek nesne, yeniden kullanılır
  child: const Text('Merhaba'),      // tek nesne, yeniden kullanılır
);

// Tüm widget'ı const yap (make entire widget const)
class Baslik extends StatelessWidget {
  const Baslik({super.key}); // const constructor

  @override
  Widget build(BuildContext context) {
    return const Text('Sabit Başlık');
  }
}

// Kullanım — const widget rebuild edilmez (const widget is not rebuilt)
const Baslik(); // build sırasında atlanır (skipped during build)

Widget Rebuild Optimizasyonu (Widget Rebuild Optimization)

dart
// KÖTÜ — anonim fonksiyon her build'de yeni oluşur
// (BAD — anonymous function recreated every build)
ElevatedButton(
  onPressed: () {
    // ...
  },
  child: const Text('Tıkla'),
);

// İYİ — metod referansı (GOOD — method reference)
ElevatedButton(
  onPressed: _tikla, // ayrı metod (separate method)
  child: const Text('Tıkla'),
);

// Widget'ı parçala — sadece değişen kısmı yeniden çiz
// (Split widget — only rebuild the changing part)

// KÖTÜ — tüm sayfa yeniden çizilir (BAD — entire page rebuilds)
class Sayfa extends StatefulWidget { /* ... */ }
class _SayfaState extends State<Sayfa> {
  int _sayac = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const BuyukWidget(), // Gereksiz rebuild! (unnecessary rebuild!)
        Text('$_sayac'),
      ],
    );
  }
}

// İYİ — sayacı ayrı widget'a taşı (GOOD — move counter to separate widget)
// veya const kullan, Consumer/Selector ile kısmi rebuild yap

Flutter DevTools

bash
# DevTools'u aç (open DevTools)
flutter run   # sonra tarayıcıda DevTools URL'sine git
# veya (or)
dart devtools  # bağımsız çalıştır (standalone launch)

# DevTools özellikleri (features):
# - Widget Inspector — widget ağacını incele (inspect widget tree)
# - Performance — jank, frame rendering analizi (frame analysis)
# - CPU Profiler — CPU kullanımı analizi (CPU usage analysis)
# - Memory — bellek sızıntıları tespiti (memory leak detection)
# - Network — ağ istekleri izleme (network request monitoring)
# - Logging — uygulama logları (application logs)

APK Boyutunu Küçültme (Reducing APK Size)

bash
# ABI bazında bölünmüş APK — farklı mimariler için ayrı dosya
# (Split per ABI — separate file for different architectures)
flutter build apk --split-per-abi

# Sonuç (result):
# app-armeabi-v7a-release.apk  ~8 MB  (eski cihazlar / old devices)
# app-arm64-v8a-release.apk    ~9 MB  (modern cihazlar / modern devices)
# app-x86_64-release.apk       ~9 MB  (emülatör / emulator)

# Tek APK yerine AAB kullan — Google Play otomatik böler
# (Use AAB instead of single APK — Google Play splits automatically)
flutter build appbundle

# Kullanılmayan kaynakları kaldır (remove unused resources)
# android/app/build.gradle
# shrinkResources true (yukarıda ProGuard bölümünde)

# tree-shake ikonları — kullanılmayan ikonları kaldır
# (tree-shake icons — remove unused icons)
flutter build apk --tree-shake-icons

Faydalı VS Code Kısayolları (Useful VS Code Shortcuts)

Kısayol (Shortcut)İşlev (Function)
Ctrl+.Quick Fix — widget sarma, const ekleme (wrap widget, add const)
Ctrl+Shift+RRefactor — widget çıkarma (extract widget)
stless + TabStatelessWidget snippet
stful + TabStatefulWidget snippet
Ctrl+Shift+P → "Flutter"Flutter komutları (Flutter commands)
r (terminalde / in terminal)Hot Reload
R (terminalde / in terminal)Hot Restart

Performans İpuçları Özeti (Performance Tips Summary)

1. const kullan — widget rebuild'i önler (use const — prevents rebuild)
2. ListView.builder kullan — tembel yükleme (use builder — lazy loading)
3. RepaintBoundary — gereksiz yeniden çizimi önle (prevent unnecessary repaint)
4. Widget'ları böl — küçük, odaklı widget'lar (split widgets — small, focused)
5. image.cache — ağ görsellerini önbellekle (cache network images)
6. Isolate — ağır hesaplamaları arka planda yap (heavy computation in background)
7. keys kullan — liste elemanlarında (use keys — in list items)
8. dispose() — controller ve listener temizliği (cleanup controllers/listeners)

18) Test (Testing)

Widget Test

dart
// test/widget/sayac_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/widgets/sayac_widget.dart';

void main() {
  group('SayacWidget', () {
    testWidgets('başlangıçta 0 gösterir', (tester) async {
      // Widget'ı oluştur (build widget)
      await tester.pumpWidget(const MaterialApp(home: SayacWidget()));

      // 0 metnini bul (find text "0")
      expect(find.text('0'), findsOneWidget);
      expect(find.text('1'), findsNothing);
    });

    testWidgets('artır butonuna basınca sayaç artar', (tester) async {
      await tester.pumpWidget(const MaterialApp(home: SayacWidget()));

      // Butona bas (tap button)
      await tester.tap(find.byIcon(Icons.add));
      await tester.pump(); // rebuild bekle (wait for rebuild)

      // 1 olduğunu kontrol et (check it shows 1)
      expect(find.text('1'), findsOneWidget);
    });

    testWidgets('birden fazla tıklama', (tester) async {
      await tester.pumpWidget(const MaterialApp(home: SayacWidget()));

      // 3 kez tıkla (tap 3 times)
      for (var i = 0; i < 3; i++) {
        await tester.tap(find.byIcon(Icons.add));
        await tester.pump();
      }

      expect(find.text('3'), findsOneWidget);
    });
  });
}

Unit Test

dart
// test/unit/kullanici_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/models/kullanici.dart';

void main() {
  group('Kullanici', () {
    test('fromJson doğru ayrıştırır', () {
      final json = {
        'id': 1,
        'ad': 'Fahri',
        'email': 'fahri@test.com',
        'olusturulma_tarihi': '2026-01-01T00:00:00.000Z',
      };

      final kullanici = Kullanici.fromJson(json);

      expect(kullanici.id, 1);
      expect(kullanici.ad, 'Fahri');
      expect(kullanici.email, 'fahri@test.com');
    });

    test('toJson doğru dönüştürür', () {
      final kullanici = Kullanici(
        id: 1,
        ad: 'Fahri',
        email: 'fahri@test.com',
        olusturulmaTarihi: DateTime(2026, 1, 1),
      );

      final json = kullanici.toJson();

      expect(json['ad'], 'Fahri');
      expect(json['email'], 'fahri@test.com');
    });

    test('yetiskin getter doğru çalışır', () {
      final yetiskin = Kullanici.test(yas: 20);
      final cocuk = Kullanici.test(yas: 15);

      expect(yetiskin.yetiskin, true);
      expect(cocuk.yetiskin, false);
    });
  });
}

Mockito ile Mock Test

yaml
# pubspec.yaml
dev_dependencies:
  mockito: ^5.4.0
  build_runner: ^2.4.0
dart
// test/mock/api_servis_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:my_app/services/api_servisi.dart';
import 'package:my_app/models/urun.dart';

@GenerateMocks([ApiServisi])
import 'api_servis_test.mocks.dart';

void main() {
  late MockApiServisi mockApi;

  setUp(() {
    mockApi = MockApiServisi();
  });

  group('UrunServisi', () {
    test('urunleri başarıyla getirir', () async {
      // Mock davranışı tanımla (define mock behavior)
      when(mockApi.urunleriGetir()).thenAnswer((_) async => [
            Urun(id: 1, ad: 'Test Ürün', fiyat: 99.90, kategoriId: 1),
          ]);

      final urunler = await mockApi.urunleriGetir();

      expect(urunler.length, 1);
      expect(urunler.first.ad, 'Test Ürün');
      verify(mockApi.urunleriGetir()).called(1); // 1 kez çağrıldı (called once)
    });

    test('hata durumunda exception fırlatır', () async {
      when(mockApi.urunleriGetir()).thenThrow(Exception('Ağ hatası'));

      expect(() => mockApi.urunleriGetir(), throwsException);
    });
  });
}

// Mock oluşturmak için (to generate mocks):
// dart run build_runner build

Integration Test (Entegrasyon Testi)

yaml
# pubspec.yaml
dev_dependencies:
  integration_test:
    sdk: flutter
dart
// integration_test/app_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('Uçtan uca test (end-to-end test)', () {
    testWidgets('giriş yapma akışı (login flow)', (tester) async {
      // Uygulamayı başlat (start app)
      app.main();
      await tester.pumpAndSettle();

      // Email gir (enter email)
      final emailAlani = find.byKey(const Key('email_alani'));
      await tester.enterText(emailAlani, 'test@test.com');

      // Şifre gir (enter password)
      final sifreAlani = find.byKey(const Key('sifre_alani'));
      await tester.enterText(sifreAlani, '123456');

      // Giriş butonuna bas (tap login button)
      await tester.tap(find.byKey(const Key('giris_butonu')));
      await tester.pumpAndSettle();

      // Ana sayfanın göründüğünü kontrol et
      // (verify home page is visible)
      expect(find.text('Ana Sayfa'), findsOneWidget);
    });
  });
}
bash
# Integration test çalıştır (run integration test)
flutter test integration_test/app_test.dart

# Belirli cihazda (on specific device)
flutter test integration_test/ -d emulator-5554

Golden Test — Görsel Karşılaştırma (Visual Comparison)

dart
// test/golden/kart_golden_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/widgets/urun_karti.dart';

void main() {
  testWidgets('UrunKarti golden test', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          body: Center(
            child: SizedBox(
              width: 300,
              child: UrunKarti(
                urun: Urun.test(ad: 'Test Ürün', fiyat: 99.90),
              ),
            ),
          ),
        ),
      ),
    );

    // Golden dosyası oluştur / karşılaştır (create / compare golden file)
    await expectLater(
      find.byType(UrunKarti),
      matchesGoldenFile('goldens/urun_karti.png'),
    );
  });
}
bash
# Golden dosyaları güncelle (update golden files)
flutter test --update-goldens

# Golden test çalıştır (run golden tests)
flutter test test/golden/

Patrol — E2E Test Framework

yaml
# pubspec.yaml
dev_dependencies:
  patrol: ^3.7.0

# patrol.yaml (kök dizinde / in root directory)
app_name: My App
android:
  package_name: com.example.my_app
ios:
  bundle_id: com.example.myApp
dart
// integration_test/patrol_test.dart
import 'package:patrol/patrol.dart';

void main() {
  patrolTest('giriş ve profil akışı (login and profile flow)', ($) async {
    await $.pumpWidgetAndSettle(const MyApp());

    // Native izin dialogu kabul et (accept native permission dialog)
    await $.native.grantPermissionWhenInUse();

    // Widget etkileşimi (widget interaction)
    await $(#email_alani).enterText('test@test.com');
    await $(#sifre_alani).enterText('123456');
    await $(#giris_butonu).tap();

    // Bekleme (wait for navigation)
    await $.pumpAndSettle();

    // Doğrulama (verification)
    expect($('Ana Sayfa'), findsOneWidget);
  });
}
bash
# Patrol test çalıştır (run Patrol test)
patrol test

# Belirli test (specific test)
patrol test integration_test/patrol_test.dart

Test Komutları Özeti (Test Commands Summary)

Komut (Command)Açıklama (Description)
flutter testTüm unit ve widget testleri çalıştır (run all unit/widget tests)
flutter test test/unit/Belirli dizindeki testler (tests in specific directory)
flutter test --coverageKapsama raporu oluştur (generate coverage report)
flutter test --update-goldensGolden dosyaları güncelle (update golden files)
flutter test integration_test/Entegrasyon testleri (integration tests)
patrol testPatrol E2E testleri (Patrol E2E tests)
genhtml coverage/lcov.info -o coverage/htmlHTML kapsama raporu (HTML coverage report)


Son güncelleme (Last updated): 2026-04 Flutter sürümü (Flutter version): 3.x stable Dart sürümü (Dart version): 3.x

Developer Guides & Technical References