# Dart'a Giriş

Bu sayfa, Dart programlama diline kısa bir giriş sunarak temel özellikleri örneklerle anlatmaktadır.

Dart dilini daha fazla tanımak ve öğrenmek için, sol taraftaki menüde listelenen konu sayfalarını ziyaret edebilirsiniz.

Unutmayın ki, Dart'ı keşfetmek heyecan verici bir yolculuktur ve bu yazı size bu yolculukta rehberlik etmeyi amaçlamaktadır. Keyifli okumalar!

### Merhaba Dünya

Dart dili yürütmenin başladığı `main()` fonksiyonuna ihtiyaç duyar. Geri dönüş değeri açıkca belirtilmeyen fonksiyonlar `void` geri dönüş türüne sahiptir. Metni konsolda görüntülemek için `print()` fonksiyonunu kullanabilirsiniz:

```dart
void main() {
  print('Merhaba, Dünya!');
}
```

Dart'taki `main()` fonksiyonu hakkında, komut satırı argümanları için isteğe bağlı parametreler dahil olmak üzere daha fazla bilgi için okumaya devam edin.

### Değişkenler

Bir çok değişkeni var anahtar kelimesini kullanarak açıkca veri tipi belirtmeden tanımlayabilirsiniz. Veri tipi çıkarımı özelliği sayesinde, bu değişkenlerin veri tipi başlangıç değerine göre otomatik olarak belirlenir.

```dart
var isim = 'Bayram';
var yil = 2000;
var boy = 172.5;
var gezegenler = ['Merkür', 'Venüs', 'Dünya', 'Mars', 'Jüpiter'];
var resim = {
  'etiketler': ['Venüs'],
  'url': '//dizin/venus.jpg'
};
```

Varsayılan değerler, `final` ve `const` anahtar kelimeleri, ve `static` türler dahil değişkenler hakkında daha fazla bilgi için okumaya devam edin.

### Kontrol Akışı İfadeleri

Dart, genel kontrol akışı ifadelerini destekler:

```dart
if (yil >= 2001) {
  print('21. yüzyıl');
} else if (yil >= 1901) {
  print('20. yüzyıl');
}

for (final gezegen in gezegenler) {
  print(gezegen);
}

for (int ay = 1; ay <= 12; ay++) {
  print(ay);
}

while (yil < 2016) {
  yil += 1;
}
```

Dart'taki kontrol akışı ifadeleri hakkında, `break` ve `continue`, `switch` ve `case`, ve `assert` dahil olmak üzere daha fazla bilgi için okumaya devam edin.

### Fonksiyonlar

Her fonksiyonun argümanları ve geri dönüş değeri türlerini belirtmenizi öneririz:

```dart
int fibonacci(int n) {
  if (n == 0 || n == 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

var sonuc = fibonacci(20);
```

Tek bir ifade içeren fonksiyonlar için ok atama (arrow) sözdizimi, özellikle anonim fonksiyonları argüman olarak iletmek için kullanışlıdır:

```dart
gezegenler.where((gezegen) => gezegen.contains('türn')).forEach(print);
```

Bu kod, bir anonim fonksiyonu (`where()` fonksiyonuna olan argümanı) göstermenin yanı sıra, bir fonksiyonu bir argüman olarak kullanabileceğinizi gösterir: `print()` fonksiyonu, `forEach()` fonksiyonuna olan bir argümandır.

Fonksiyonlar hakkında daha fazla [bilgi için](/fonksiyonlar-functions/genel-bakis), isteğe bağlı parametreler, varsayılan parametre değerleri ve leksikal kapsam dahil olmak üzere Dart'taki fonksiyonlar hakkında okumaya devam edin.

### Yorum Satırı

Dart yorumları satırları genellikle // ile başlar.

```dart
// Bu, normal tek satırlı bir yorumdur.

/// Bu, kütüphaneleri, sınıfları ve üyelerini belgelemek için kullanılan bir belgeleme yorumudur.
/// IDE'ler ve dartdoc gibi araçlar, belge yorumlarını özel olarak işler.
```

```dart
/* Bu tür yorumlar da desteklenir. */
```

Dart'ta yorumlar hakkında ve belgelendirme araçları nasıl çalışır, dahil olmak üzere daha fazla bilgi için okumaya devam edin.

### Import (İçe Aktarma)

Diğer kütüphanelerde tanımlanan API'lara erişmek için import kullanılır.

```dart
// Temel kütüphaneleri içe alma
import 'dart:math';

// Harici paketlerden kütüphaneleri içe alma
import 'package:test/test.dart';

// Dosyaları içe alma
import 'dizin/diger_dosya.dart';
```

Kütüphaneler ve görünürlük hakkında, kütüphane ön ekleri, `show` ve `hide`, ve `deferred` anahtar kelimesi aracılığıyla tembel yükleme dahil olmak üzere Dart'taki daha fazla bilgi için okumaya devam edin.

### Class (Sınıflar)

İşte üç özelliği, iki kurucusu ve bir yöntemi olan bir sınıfın örnekleriyle ilgili bir örnek. Bir özelliğin doğrudan ayarlanamaması durumunda, bir değişken yerine bir `getter` yöntemi kullanılarak tanımlanır.

```dart
class UzayAraci {
  String isim;
  DateTime? firlatmaTarihi;

  // Salt okunur, değiştirilemeyen özellik
  int? get firlatmaYili => firlatmaTarihi?.year;

  // Kurucu, üye değişkenlere atama için sentaktik şeker içerir.
  UzayAraci(this.isim, this.firlatmaTarii) {
    // Başlatma kodu buraya yazılır.
  }

  // Varsayılan kurucuya yönlendiren adlandırılmış kurucu.
  UzayAraci.firlatilmamis(String isim) : this(isim, null);

  // Yöntem.
  void tanimla() {
    print('Uzay Aracı: $isim');
    // Tip tanıma, getter'lar üzerinde çalışmaz.
    var firlatmaTarihi = this.firlatmaTarihi;
    if (firlatmaTarihi != null) {
      int yil = DateTime.now().difference(firlatmaTarihi).inDays ~/ 365;
      print('Fırlatıldı: $firlatmaYili ($yil yıl önce)');
    } else {
      print('Fırlatılmadı');
    }
  }
}
```

Daha fazla bilgi için, `string` içlemeleri, `string` yerine koyma, ifadeler, ve `toString()` yöntemi dahil stringler hakkında okumaya devam edin.

UzayAraci sınıfını şu şekilde kullanabilirsiniz:

```dart
var voyager = UzayAraci('Voyager I', DateTime(1977, 9, 5));
voyager.tanimla();

var voyager3 = UzayAraci.firlatilmamis('Voyager III');
voyager3.tanimla();

```

Başlatıcı listeleri, isteğe bağlı `new` ve `const`, yönlendiren kurucular, fabrika kurucular, `getter`'lar, `setter`'lar ve daha fazlası dahil olmak üzere Dart'taki sınıflar hakkında daha fazla bilgi için okumaya devam edin.

### Enum'lar

Enum'lar, belirli bir küme değeri veya örneği sıralamak için kullanılan bir yoldur ve bu türün başka örneklerinin olmamasını sağlar.

İşte basit bir enum örneği, önceden tanımlanmış gezegen türlerini içeren:

```dart
enum GezegenTuru { kara, gaz, buz }
```

İşte gezegenleri tanımlayan bir sınıfın gelişmiş enum deklarasyonu örneği, kendi güneş sistemimizin gezegenlerini içeren sabit örnek setini tanımlar.

```dart
/// Gezegen türünü ve bazı özelliklerini sıralayan bir enum
enum Gezegen {
  merkur(gezegenTuru: GezegenTuru.kara, uydular: 0, halkalariVar: false),
  venus(gezegenTuru: GezegenTuru.kara, uydular: 0, halkalariVar: false),
  // ···
  uranus(gezegenTürü: GezegenTuru.buz, uydular: 27, halkalariVar: true),
  neptun(gezegenTürü: GezegenTuru.buz, uydular: 14, halkalariVar: true);

  /// Sabit üreten kurucu
  const Gezegen(
      {required this.gezegenTuru, required this.uydular, required this.halkalariVar});

  /// Tüm örnek değişkenleri final
  final GezegenTuru gezegenTuru;
  final int uydular;
  final bool halkalariVar;

  /// Gelişmiş enum'lar getter ve diğer yöntemleri destekler
  bool get devGezegen =>
      gezegenTürü == GezegenTuru.gaz || gezegenTuru == GezegenTuru.buz;
}
```

Gezegen enum'ını şu şekilde kullanabilirsiniz:

```dart
final gezegeniniz = Gezegen.dunya;

if (!gezegeniniz.devGezegen) {
  print('Gezegeniniz bir "dev gezegen" değil.');
}
```

Dart'ta enum ile ilgili daha fazla bilgi edinmek için, geliştirilmiş enum gereksinimleri, otomatik olarak tanıtılan özellikler, enum değer isimlerine erişim, `switch` ifadesi desteği ve daha fazlasını içeren konulara odaklanabilirsiniz.

### Inheritance (Miras Alma) <a href="#inheritance" id="inheritance"></a>

Dart, tek miras alma özelliğine sahiptir.

```dart
class Uydu extends UzayAraci {
  double yukseklik;

  Uydu(super.isim, DateTime super.fizikselBaslangicTarihi, this.yukseklik);
}
```

Sınıfları genişletme, isteğe bağlı `@override` açıklaması ve daha fazlası hakkında bilgi edinin.

### Mixin

Mixin, kodu birden fazla sınıf hiyerarşisinde yeniden kullanmanın bir yoludur. Aşağıda bir mixin örneği bulunmaktadır:

```dart
mixin Pilotlu {
  int astronotlar = 1;

  void ekiptenBahset() {
    print('Astronot sayısı: $astronotlar');
  }
}
```

Bir sınıfa bir mixin'in yeteneklerini eklemek için sadece mixin'i sınıf ile genişletin.

```dart
class PilotluArac extends UzayAraci with Pilotlu {
  // ···
}
```

`PilotluArac` artık astronotlar alanına sahiptir ve `ekiptenBahset()` metodunu içerir.

Mixin'ler hakkında daha fazla bilgi edinin.

### Interface (Arayüz) ve Abstract (Soyut) Sınıflar

Tüm sınıflar örtük olarak bir arayüz tanımlar. Bu nedenle herhangi bir sınıfı uygulayabilirsiniz.

```dart
class SahteUzayGemisi implements UzayAraci {
  // ···
}
```

Örtük arayüzler veya açık arayüz anahtar kelimesi hakkında daha fazla bilgi edinin.

Bir soyut sınıf oluşturarak bir sınıf tarafından genişletilmek (veya uygulanmak) üzere tasarlanmış soyut metodlar içerebilirsiniz. Soyut sınıflar boş gövdeler içeren soyut metodları içerebilir.

```dart
abstract class Tanitilabilir {
  void tanimla();

  void vurguIleTanimla() {
    print('=========');
    tanimla();
    print('=========');
  }
}
```

`Tanitilabilir`'i genişleten herhangi bir sınıf, `tanimla()` metodunun uygulamasına sahip olacak ve `vurguIleTanimla()` metodunu çağıracaktır.

Abstract sınıfı ve metodlar hakkında daha fazla bilgi edinin.

### Async (Asenkron)

`Async` ve `await` kullanarak geri çağrı karmaşasını önleyin ve kodunuzu çok daha okunabilir hale getirin.

```dart
const birSaniye = Duration(seconds: 1);
// ···
Future<void> gecikmeliYazdir(String mesaj) async {
  await Future.delayed(birSaniye);
  print(mesaj);
}
```

Yukarıdaki yöntem, aşağıdaki gibi eşdeğerdir:

```dart
Future<void> gecikmeliYazdir(String mesaj) {
  return Future.delayed(birSaniye).then((_) {
    print(mesaj);
  });
}
```

Aşağıdaki örnek gösterildiği gibi, async ve await asenkron kodu okunması kolay hale getirir.

```dart
Future<void> aciklamalarOlustur(Iterable<String> nesneler) async {
  for (final nesne in nesneler) {
    try {
      var dosya = File('$nesne.txt');
      if (await dosya.exists()) {
        var degistirilmeTarihi = await dosya.lastModified();
        print('$nesne için dosya zaten var. Son değiştirme tarihi: $degistirilmeTarihi.');
        continue;
      }
      await dosya.create();
      await dosya.writeAsString('$nesne için açıklama yapmaya başla.');
    } on IOException catch (e) {
      print('$nesne için açıklama oluşturulamıyor: $e');
    }
  }
}
```

Ayrıca async\* kullanabilirsiniz, bu size güzel, okunabilir bir şekilde akışlar oluşturmanıza olanak tanır.

```dart
Stream<String> rapor(UzayAraci arac, Iterable<String> nesneler) async* {
  for (final nesne in nesneler) {
    await Future.delayed(birSaniye);
    yield '${arac.isim} $nesne tarafından uçuyor';
  }
}
```

Asenkron işlemleri destekleyen async fonksiyonlar, `Future`, `Stream` ve asenkron döngü (await for) hakkında daha fazla bilgi edinin.

### Exceptions (İstisnalar)

Bir istisna çıktısı için throw kullanın:

```dart
if (astronotlar == 0) {
  throw StateError('Astronot yok.');
}
```

Bir istisnayı yakalamak için, `try` ifadesini `on` veya `catch` (veya her ikisi) ile kullanın:

```dart
Future<void> ucanNesneleriAcikla(List<String> ucanNesneler) async {
  try {
    for (final nesne in ucanNesneler) {
      var aciklama = await File('$nesne.txt').readAsString();
      print(aciklama);
    }
  } on IOException catch (e) {
    print('Nesne açıklanamadı: $e');
  } finally {
    ucanNesneler.clear();
  }
}

```

Yukarıdaki kod asenkron olduğuna dikkat edin; `try`, hem senkron kod hem de asenkron fonksiyon içinde çalışır.

İstisnalar hakkında, yığın izleme, yeniden atma ve Hata ile İstisna arasındaki fark gibi konularda daha fazla bilgi edinin.

\
\ <br>


# Değişkenler (Variables)

İşte bir değişken oluşturmanın ve başlatmanın bir örneği:

```dart
var isim = 'Bayram';
```

Değişkenler referansları depolar. `isim` adlı değişken, "Bayram" değerine sahip bir String nesnesine bir referans içerir.

İsim değişkeninin türü String olarak çıkarılabilir, ancak türü belirterek bunu değiştirebilirsiniz. Eğer bir nesne tek bir türle sınırlı değilse, `Object` türünü belirtin (veya gerekiyorsa `dynamic`).

```dart
Object isim = 'Bayram';
```

Başka bir seçenek de türü açıkça belirtmektir:

```dart
String isim = 'Bayram';
```

### Null Güvenliği

Dart dilinde sağlam null güvenliği uygulanır.

Null güvenliği, null olarak ayarlanmış değişkenlere istenmeyen erişimden kaynaklanan hataları önler. Bu hata, null dereference hatası olarak adlandırılır. Null dereference hatası, bir ifadeyi null'a değerlendiren bir özelliğe veya bir yöntemi çağırdığınızda ortaya çıkar. Bu kurala bir istisna, null'un desteklediği bir özellik veya yöntem olduğunda söz konusu olabilir, örneğin toString() veya hashCode. Null güvenliği, Dart derleyicisinin bu potansiyel hataları derleme zamanında tespit etmesini sağlar.

Örneğin, bir int değişkeni `i`nin mutlak değerini bulmak istediğinizi varsayalım. Eğer `i` null ise, `i.abs()` çağrısı bir null dereference hatasına neden olur. Diğer dillerde bu denemek runtime hatasına yol açabilir, ancak Dart derleyicisi bu eylemleri yasaklar. Bu nedenle, Dart uygulamaları runtime hatalara neden olamaz.

* Bir değişken, parametre veya başka bir ilgili bileşen için bir tür belirttiğinizde, türün null'a izin verip vermediğini kontrol edebilirsiniz. Null kullanılabilirliği etkinleştirmek için tür bildiriminin sonuna bir `?` eklersiniz.

```dart
String? isim  // Null olabilir tür. Null veya string olabilir.
String isim   // Null olamaz tür. Null olamaz ancak string olabilir.
```

* Değişkenleri kullanmadan önce başlatmalısınız. Null olabilen değişkenler varsayılan olarak null ile başlatıldığından, bunlar varsayılan olarak başlatılır. Dart, başlatılmamış bir değişkeni gözlemlemenize izin vermez. Bu, alıcı türü null olabilir, ancak null kullanılan yöntemi veya özelliği desteklemez şeklinde hatalı bir erişimi önler.
* Null olabilen bir türdeki bir ifadenin özelliklerine veya yöntemlerine erişemezsiniz. Null desteklediği bir özellik veya yöntem olduğunda aynı istisna geçerli olur, örneğin hashCode veya toString().

Sağlam null güvenliği, potansiyel runtime hatalarını düzenleme zamanı analiz hatalarına dönüştürür. Null güvenliği, bir değişkenin;

* Başlangıçta null olmayan bir değerle başlatılmadığında,

ya da

* Null bir değer atandığında,

bu durumları kontrol eder ve bu hataları uygulamanızı dağıtmadan önce düzeltmenize olanak tanır.

### Varsayılan Değer

Başlatılmamış null olabilen türdeki değişkenlerin başlangıç değeri null'dir. Hatta sayılar gibi türlerin de başlangıçta null olmasının nedeni, Dart'ın her şeyin nesne olduğu bir dil olmasıdır.

```dart
int? satirSayisi;
assert(satirSayisi == null);
```

> **Not**
>
> Üretim kodu assert() çağrısını görmezden gelir. Bununla birlikte, geliştirme sırasında assert(şart) şartı yanlışsa bir istisna fırlatır. Ayrıntılar için Assert bölümüne bakın.

Null güvenliği ile, null olmayan değişkenlerin değerlerini kullanmadan önce başlatmalısınız:

```dart
int satirSayisi = 0;
```

Bir yerel değişkenin tanımlandığı yerde başlatılmasına gerek yoktur, ancak kullanılmadan önce bir değer atanmalıdır. Örneğin, aşağıdaki kod geçerlidir çünkü Dart, satirSayisi'nı print() fonksiyonuna geçirildiği noktada satirSayisi'nın null olmadığını algılayabilir:

```dart
int satirSayisi;

if (satirlariSayalim) {
  satirSayisi = satirSay();
} else {
  satirSayisi = 0;
}

print(satirSayisi);
```

### Late Değişkenler

Late değişken belirleyici (modifier), iki kullanım durumuna sahiptir:

Değişkenin bildiriminden sonra başlatılan bir boş olmayan değişkeni belirtme.

Bir değişkeni tembellikle başlatma.

Eğer bir değişkenin kullanılmadan önce ayarlandığından eminseniz, ancak Dart bu konuda anlaşmazlık yaşıyorsa, değişkeni `late` olarak işaretleyerek hatayı düzeltebilirsiniz:

Genellikle Dart'ın kontrol akış analizi, bir boş olmayan değişkenin kullanılmadan önce bir boş olmayan bir değere ayarlandığını algılayabilir, ancak bazen analiz başarısız olur. İki yaygın durum, üst düzey değişkenler ve örnek değişkenlerdir: Dart genellikle bunların ayarlanıp ayarlanmadığını belirleyemez, bu yüzden denemez.

```dart
late String aciklama;

void main() {
  aciklama = 'Dart diline hoş geldiniz!';
  print(aciklama);
}
```

> **Not**
>
> `late` değişkeni başlatmayı unutursanız, değişken kullanıldığında çalışma zamanında bir hata oluşur.

Bir değişkeni `late` olarak işaretlediğinizde, ancak değişken kullanıldığında ilk kez başlatılır. Bu tembel başlatma, birkaç durumda kullanışlıdır:

1. Değişken muhtemelen gerekli değildir.
2. Bir örnek değişkenini başlatıyorsanız ve başlatıcı bu değişkene erişime ihtiyaç duyuyorsa.

Aşağıdaki örnekte, sıcaklık değişkeni hiç kullanılmazsa, zahmetli `termometreGetir()` fonksiyonu hiç çağrılmaz:

```dart
// Bu, programın termometreGetir()'ı çağırışıdır.
late String sicaklik = termometreGetir(); // Tembel başlatma.
```

### Final ve Const

Eğer bir değişkeni asla değiştirmeyi düşünmüyorsanız, var yerine final veya const kullanın. Bir final değişkeni yalnızca bir kez ayarlanabilir; bir const değişkeni derleme zamanında sabittir. (Const değişkenler örtük olarak finaldir.)

> **Not**
>
> Örnek değişkenleri final olabilir, ancak const olamaz.

İşte final bir değişken oluşturma ve ayarlama örneği:

```dart
final isim = 'Bayram'; // Tür belirtimi olmadan
final String takmaAd = 'Bayramlcm';
```

Bir final değişkeninin değerini değiştiremezsiniz:

```dart
isim = 'Emirhan'; // Hata: bir final değişkeni yalnızca bir kez ayarlanabilir.
```

Sabit bir değere sahip olmasını istediğiniz değişkenler için const kullanın. Eğer const değişkeni sınıf düzeyinde kullanıyorsanız, onu `static const` olarak işaretleyin. Değişkeni bildiğiniz yerde, değeri bir derleme zamanı sabiti olarak ayarlayın; bu bir sayı veya string literali, bir const değişkeni veya sabit sayılar üzerinde yapılan bir aritmetik işlemin sonucu olabilir:

```dart
const bar = 1000000; // Basınç birimi (dyne/cm2)
const double atmosfer = 1.01325 * bar; // Standart atmosfer
```

Const anahtar kelimesi sadece sabit değişkenlerin deklarasyonu için değil, aynı zamanda sabit değerler oluşturmak ve sabit değerler yaratan yapıcıları deklare etmek için de kullanılabilir. Herhangi bir değişken, bir sabit değere sahip olabilir.

```dart
var foo = const [];
final bar = const [];
const baz = []; // `const []` ile eşdeğer
```

Const deklarasyonunun başlatma ifadesinden const'ı atlayabilirsiniz, örneğin baz'da olduğu gibi. Detaylar için, Const'ı gereksiz yere kullanma başlığına bakın.

Bir non-final (final olmayan), non-const (const olmayan) değişkenin değerini değiştirebilirsiniz, hatta başlangıçta const bir değeri olduğu halde:

```dart
foo = [1, 2, 3]; // Başlangıçta const []
```

Ancak, const bir değişkenin değerini değiştiremezsiniz:

```dart
baz = [42]; // Hata: Sabit değişkenlere değer atılamaz.
```

Tip kontrol ve dönüşümleri (is ve as), koleksiyon if, ve yayılma operatörleri (... ve ...?) kullanarak tür kontrolü yapan ve dönüşüm yapan sabitler tanımlayabilirsiniz:

```dart
const Object i = 3; // i, int değeri olan bir const Object'tir...
const list = [i as int]; // Bir tip dönüşümü kullanın.
const map = {if (i is int) i: 'int'}; // is ve koleksiyon if kullanın.
const set = {if (list is List<int>) ...list}; // ...ve yayılma operatörünü kullanın.
```

> **Not**
>
> Bir final nesnesi değiştirilemez, ancak alanları değiştirilebilir. Buna karşılık, bir const nesnesi ve alanları değiştirilemez: bunlar değişmezdir.

Daha fazla bilgi için, Const, Final, List, Maps ve Class (Sınıflar) başlıklarını ziyaret edin.


# Operatörler (Operators)

<table><thead><tr><th width="284">Açıklama</th><th>Operatör</th></tr></thead><tbody><tr><td>Son Ek</td><td><mark style="color:blue;">ifade++</mark>   <mark style="color:purple;">ifade--</mark>   <mark style="color:blue;">()</mark>   <mark style="color:purple;">[]</mark>   <mark style="color:blue;">?[]</mark>   <mark style="color:purple;">.</mark>   <mark style="color:purple;">?.</mark>   <mark style="color:blue;">!</mark></td></tr><tr><td>Ön Ek</td><td><mark style="color:blue;">-ifade</mark>   <mark style="color:purple;">!ifade</mark>   <mark style="color:blue;">~ifade</mark>   <mark style="color:purple;">++ifade</mark>   <mark style="color:blue;">--ifade</mark>  <mark style="color:purple;">await ifade</mark></td></tr><tr><td>Çarpımsal</td><td><mark style="color:blue;">*</mark>   <mark style="color:purple;">/</mark>   <mark style="color:blue;">%</mark>   <mark style="color:purple;">~/</mark></td></tr><tr><td>Toplama Çıkarma İşlemi</td><td><mark style="color:blue;">+</mark>   <mark style="color:purple;">-</mark></td></tr><tr><td>Kaydırma İşlemi</td><td><mark style="color:blue;">&#x3C;&#x3C;</mark>   <mark style="color:purple;">>></mark>   <mark style="color:blue;">>>></mark></td></tr><tr><td>Bit Düzeyinde AND</td><td><mark style="color:blue;">&#x26;</mark></td></tr><tr><td>Bit Düzeyinde XOR</td><td><mark style="color:blue;">^</mark></td></tr><tr><td>Bit Düzeyinde OR</td><td><mark style="color:blue;">|</mark></td></tr><tr><td>İlişkisel ve Tip Test İşlemleri</td><td><mark style="color:blue;">>=</mark>   <mark style="color:purple;">></mark>   <mark style="color:blue;">&#x3C;=</mark>   <mark style="color:purple;">&#x3C;</mark>   <mark style="color:blue;">as</mark>   <mark style="color:purple;">is</mark>   <mark style="color:blue;">is!</mark></td></tr><tr><td>Eşitlik</td><td><mark style="color:blue;">==</mark>   <mark style="color:purple;">!=</mark></td></tr><tr><td>Mantıksal AND</td><td><mark style="color:blue;">&#x26;&#x26;</mark></td></tr><tr><td>Mantıksal OR</td><td><mark style="color:blue;">||</mark></td></tr><tr><td>if null</td><td><mark style="color:blue;">??</mark></td></tr><tr><td>Koşul</td><td><mark style="color:blue;">ifade1</mark>  <mark style="color:purple;">?</mark> <mark style="color:blue;">ifade2</mark> <mark style="color:purple;">:</mark> <mark style="color:blue;">ifade3</mark></td></tr><tr><td>Cascade</td><td><mark style="color:blue;">..</mark>   <mark style="color:purple;">?..</mark></td></tr><tr><td>Atama</td><td><mark style="color:blue;">=</mark>   <mark style="color:purple;">*=</mark>   <mark style="color:blue;">/=</mark>   <mark style="color:purple;">+=</mark>   <mark style="color:blue;">-=</mark>   <mark style="color:purple;">&#x26;=</mark>   <mark style="color:blue;">^=</mark>   vs.</td></tr></tbody></table>

> **Uyarı**
>
> Yukarıdaki tablo, sadece yardımcı bir rehber olarak kullanılmalıdır. Operatör önceliği ve birlikteliği, dilin gramerinde bulunan gerçekliğin bir yaklaşımıdır. Dart'ın operatör ilişkilerinin yetkili davranışını dil spesifikasyonundaki gramerde bulabilirsiniz.

Operatörleri kullandığınızda, ifadeler oluşturursunuz. İşte operatör ifadelerine örnekler:

```dart
a++
a + b
a = b
a == b
c ? a : b
a is T
```

### Operatör Önceliği Örneği

```dart
// Operatör tablosunda her operatör, onu takip eden satırlardaki operatörlerden daha yüksek önceliğe sahiptir.
// Parantezler okunabilirliği artırır.
if ((n % i == 0) && (d % i == 0)) ...

// Okunması zor, ancak eşdeğer.
if (n % i == 0 && d % i == 0) ...
```

### Aritmetik Operatörler

<table><thead><tr><th width="142">Operatör</th><th>Anlamı</th></tr></thead><tbody><tr><td><mark style="color:blue;">+</mark></td><td>Ekleme</td></tr><tr><td><mark style="color:purple;">-</mark></td><td>Çıkarma</td></tr><tr><td><mark style="color:blue;">*</mark></td><td>Çarpma</td></tr><tr><td><mark style="color:purple;">/</mark></td><td>Bölme</td></tr><tr><td><mark style="color:blue;">~/</mark></td><td>Bölme, Tam Sayı Sonucu Döndürür</td></tr><tr><td><mark style="color:purple;">%</mark></td><td>Mod Alma</td></tr></tbody></table>

Örnek:

```dart
assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // Sonuç bir double
assert(5 ~/ 2 == 2); // Sonuç bir int
assert(5 % 2 == 1); // Kalan

assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
```

Ayrıca, artırma ve azaltma operatörlerini de destekler:

<table><thead><tr><th width="292">Operatör</th><th>Anlamı</th></tr></thead><tbody><tr><td><mark style="color:blue;">++var</mark></td><td><mark style="color:blue;">var = var + 1</mark> (ifade değeri <mark style="color:blue;">var + 1</mark>)</td></tr><tr><td><mark style="color:purple;">var++</mark></td><td><mark style="color:purple;">var = var + 1</mark> (ifade değeri <mark style="color:purple;">var</mark>)</td></tr><tr><td><mark style="color:blue;">--var</mark></td><td><mark style="color:blue;">var = var - 1</mark> (ifade değeri <mark style="color:purple;">var - 1</mark>)</td></tr><tr><td><mark style="color:purple;">var--</mark></td><td><mark style="color:blue;">var = var - 1</mark> (ifade değeri <mark style="color:purple;">var</mark>)</td></tr></tbody></table>

Örnek:

```dart
int a;
int b;

a = 0;
b = ++a; // a artırılır, sonra b değeri atanır.
assert(a == b); // 1 == 1

a = 0;
b = a++; // b değeri atanır, sonra a artırılır.
assert(a != b); // 1 != 0

a = 0;
b = --a; // a azaltılır, sonra b değeri atanır.
assert(a == b); // -1 == -1

a = 0;
b = a--; // b değeri atanır, sonra a azaltılır.
assert(a != b); // -1 != 0
```

### Eşitlik ve İlişkisel Operatörler

<table><thead><tr><th width="170">Operatör</th><th>Anlamı</th></tr></thead><tbody><tr><td><mark style="color:blue;">==</mark></td><td>Eşittir</td></tr><tr><td><mark style="color:purple;">!=</mark></td><td>Eşit değil</td></tr><tr><td><mark style="color:blue;">></mark></td><td>Büyüktür</td></tr><tr><td><mark style="color:purple;">&#x3C;</mark></td><td>Küçüktür</td></tr><tr><td><mark style="color:blue;">>=</mark></td><td>Büyüktür veya eşittir</td></tr><tr><td><mark style="color:purple;">&#x3C;=</mark></td><td>Küçüktür veya eşittir</td></tr></tbody></table>

İki nesnenin x ve y, aynı şeyi temsil edip etmediğini test etmek için == operatörünü kullanın. (İki nesnenin tam olarak aynı nesne olup olmadığını bilmek istemeniz durumunda, sadece `identical()` fonksiyonunu kullanın.) İşte == operatörünün nasıl çalıştığı:

1. x veya y null ise, her ikisi de null ise true döndürür, sadece biri null ise false döndürür.
2. x üzerinde y ile çağrılan == yönteminin sonucunu döndürür.&#x20;

İşte eşitlik ve ilişkisel operatörleri kullanma örnekleri:

```dart
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
```

### Tip Test Operatörleri

as, is ve is! operatörleri, tipleri çalışma zamanında kontrol etmek için kullanışlıdır.

<table><thead><tr><th width="154">Opeartör</th><th>Anlamı</th></tr></thead><tbody><tr><td><mark style="color:blue;">as</mark></td><td>Tip dönüşümü (ayrıca kütüphane öneklerini belirtmek için de kullanılır)</td></tr><tr><td><mark style="color:purple;">is</mark></td><td>Nesnenin belirtilen tipe sahip olup olmadığı doğru ise</td></tr><tr><td><mark style="color:blue;">is!</mark></td><td>Nesnenin belirtilen tipe sahip olmadığı doğru ise</td></tr></tbody></table>

Eğer nesnenin tipinin `T` tipi olduğundan eminseniz, objeyi bu tipe dönüştürmek için as operatörünü kullanın. Örnek:

```dart
(calisan as Kisi).firstName = 'Bayram';
```

Eğer nesnenin tipinin T tipi olup olmadığını bilmiyorsanız, objeyi kullanmadan önce tipi kontrol etmek için is T'yi kullanın.

```dart
if (calisan is Kisi) {
  // Tip kontrolü
  calisan.firstName = 'Bayram';
}
```

> **Not**
>
> Kod eşdeğer değildir. Eğer `calisan` null ise veya `Kisi` değilse, ilk örnek bir istisna fırlatır; ikinci örnek ise hiçbir şey yapmaz.

### Atama Operatörleri

Zaten gördüğünüz gibi, = operatörü ile değer atayabilirsiniz. Atanacak değişken null ise, ??= operatörünü kullanarak sadece atama yapabilirsiniz.

```dart
// Bir değer atayın
a = value;
// b null ise b'ye değer atayın; aksi takdirde b aynı kalır
b ??= value;
```

Zaten gördüğünüz gibi, = operatörü ile değer atayabilirsiniz. Atanacak değişken null ise, ??= operatörünü kullanarak sadece atama yapabilirsiniz.

<br>

<table data-header-hidden><thead><tr><th width="153"></th><th width="151"></th><th width="155"></th><th width="149"></th><th></th></tr></thead><tbody><tr><td>=</td><td>*=</td><td>%=</td><td>>>>=</td><td>^=</td></tr><tr><td>+=</td><td>/=</td><td>&#x3C;&#x3C;=</td><td>&#x26;=</td><td>|=</td></tr><tr><td>-=</td><td>~/=</td><td>>>=</td><td></td><td></td></tr></tbody></table>

İşte bileşik atama operatörlerinin nasıl çalıştığı:

<table data-header-hidden><thead><tr><th width="209"></th><th width="263"></th><th></th></tr></thead><tbody><tr><td>Bir operatör <mark style="color:purple;"><strong>op</strong></mark> için:</td><td><mark style="color:blue;">a</mark> <mark style="color:purple;">op=</mark> <mark style="color:blue;">b</mark></td><td><mark style="color:blue;">a = a</mark> <mark style="color:purple;">op</mark> <mark style="color:blue;">b</mark></td></tr><tr><td>Örneğin:</td><td><mark style="color:blue;">a</mark> <mark style="color:purple;">+= b</mark></td><td><mark style="color:blue;">a = a</mark> <mark style="color:purple;">+</mark> <mark style="color:blue;">b</mark></td></tr></tbody></table>

Aşağıdaki örnek, atama ve bileşik atama operatörlerini kullanır:

```dart
var a = 2; // = kullanarak değer atama
a *= 3; // Atama ve çarpma: a = a * 3
assert(a == 6);
```

### Mantıksal Operatörler

Mantıksal operatörleri kullanarak boolean ifadelerini tersine çevirebilir veya birleştirebilirsiniz.

<table><thead><tr><th width="135">Opeartör</th><th>Anlamı</th></tr></thead><tbody><tr><td><mark style="color:blue;">!ifade</mark></td><td>Takip eden ifadeyi tersine çevirir (<mark style="color:blue;">false</mark>'u <mark style="color:blue;">true</mark>'ya veya tam tersi)</td></tr><tr><td><mark style="color:purple;">||</mark></td><td>Mantıksal <mark style="color:purple;">OR</mark> (VEYA)</td></tr><tr><td><mark style="color:blue;">&#x26;&#x26;</mark></td><td>Mantıksal <mark style="color:blue;">AND</mark> (VE)</td></tr></tbody></table>

İşte mantıksal operatörleri kullanmanın bir örneği:

```dart
if (!tamamlandi && (col == 0 || col == 3)) {
  // ...Bir şey yap...
}
```

### Bitwise (Bit Düzeyi) ve Kaydırma Operatörleri

Dart'ta sayıların bitlerini manipüle edebilirsiniz. Genellikle bu bitwise ve kaydırma operatörlerini tamsayılarla kullanırsınız.

<table><thead><tr><th width="148">Operatör</th><th>Anlamı</th></tr></thead><tbody><tr><td><mark style="color:blue;">&#x26;</mark></td><td>AND</td></tr><tr><td><mark style="color:purple;">|</mark></td><td>OR</td></tr><tr><td><mark style="color:blue;">^</mark></td><td>XOR</td></tr><tr><td><mark style="color:purple;">~ifade</mark></td><td>Bit düzeyinde (0'ları 1 yapar; 1'leri 0 yapar)</td></tr><tr><td><mark style="color:blue;">&#x3C;&#x3C;</mark></td><td>Sola kaydırma</td></tr><tr><td><mark style="color:purple;">>></mark></td><td>Sağa kaydırma</td></tr><tr><td><mark style="color:blue;">>>></mark></td><td>İşaretsiz sağa kaydırma</td></tr></tbody></table>

> **Not**
>
> Büyük veya negatif operandlara sahip bitwise operasyonların davranışı platformlar arasında farklılık gösterebilir.&#x20;

İşte bitwise ve kaydırma operatörlerini kullanmanın bir örneği:

```dart
final deger = 0x22;
final maske = 0x0f;

assert((deger & maske) == 0x02); // VE (AND)
assert((deger & ~maske) == 0x20); // VE DEĞİL (AND NOT)
assert((deger | maske) == 0x2f); // VEYA (OR)
assert((deger ^ maske) == 0x2d); // XOR

assert((deger << 4) == 0x220); // Sola kaydırma
assert((deger >> 4) == 0x02); // Sağa kaydırma

// Web üzerinde farklı davranan bir örnekte sağa kaydırma
// çünkü operand değeri 32 bit olarak maskeleyince değişir:
assert((-deger >> 4) == -0x03);

assert((deger >>> 4) == 0x02); // İşaretsiz sağa kaydırma
assert((-deger >>> 4) > 0); // İşaretsiz sağa kaydırma
```

### Koşullu İfadeler

Dart dilinde, aksi takdirde if-else ifadeleri gerektirebilecek ifadeleri kısa bir şekilde değerlendirmenizi sağlayan iki operatör bulunmaktadır:

* `kosul ? ifade1 : ifade2` Eğer condition doğruysa, ifade1'i değerlendirir (ve değerini döndürür); aksi takdirde, ifade2'yi değerlendirir ve değerini döndürür.
* `ifade1 ?? ifade2` Eğer ifade1 değeri null değilse, değerini döndürür; aksi takdirde, ifade2'yi değerlendirir ve değerini döndürür.

```dart
var gorunurluk = gorunurMu ? 'görünür' : 'gizli';
// Eğer boolean ifade null kontrolü yapıyorsa, ?? operatörünü kullanmayı düşünün.
String playerName(String? isim) => isim ?? 'Misafir';
```

### Cascade Notasyonu

Cascade (.., ?..) notasyonları, aynı nesne üzerinde bir dizi işlem yapmanıza olanak tanır. Örnek olarak:

```dart
var boya = Paint()
  ..color = Colors.black
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0;
```

Yukarıdaki örnek, aşağıdaki kodla eşdeğerdir:

```dart
var boya = Paint();
boya.color = Colors.black;
boya.strokeCap = StrokeCap.round;
boya.strokeWidth = 5.0;
```

Eğer cascade'in çalıştığı nesne null olabilirse, ilk işlem için bir null kontrolü cascade'i kullanın (?..).

```dart
querySelector('#confirm') // Bir nesne al.
  ?..text = 'Onayla' // Üyelerini kullan.
  ..classes.add('önemli')
  ..onClick.listen((e) => window.alert('Onaylandı!'))
  ..scrollIntoView();
```

Cascade kullanarak iç içe cascade'ler oluşturabilirsiniz.

```dart
final adresDefteri = (AddressBookBuilder()
      ..name = 'Bayram'
      ..email = 'info@bayramalacam.com'
      ..phone = (PhoneNumberBuilder()
            ..number = '+90 5xx xxx xxxx'
            ..label = 'cep')
          .build())
    .build();
```

Cascade'inizi bir işlevin gerçek bir nesne döndürdüğünden emin olun. Aksi takdirde, cascade void üzerine kurulamaz.

> **Not**
>
> Cascade notasyonu, kesin olarak "çift nokta" notasyonu olarak adlandırılmaz. Bu, sadece Dart dilinin bir parçasıdır.

### Diğer Operatörler

Diğer operatörleri de başka örneklerde gördünüz:

<table><thead><tr><th width="118">Operatör</th><th width="133">Adı</th><th>Anlamı</th></tr></thead><tbody><tr><td><code>()</code></td><td>Fonksiyon uygulama</td><td>Bir fonksiyon çağrısını temsil eder.</td></tr><tr><td><code>[]</code></td><td>Parantez erişimi</td><td>Overridable <mark style="color:blue;">[]</mark> operatörüne çağrıyı temsil eder; örneğin: <mark style="color:blue;">fooList[1]</mark>, <mark style="color:blue;">fooList</mark>'e int <mark style="color:blue;">1</mark>'i iletilir ve <mark style="color:blue;">1</mark>. indeksteki elemana erişir.</td></tr><tr><td><code>?[]</code></td><td>Koşullu parantez erişimi</td><td><mark style="color:blue;">[]</mark>, ancak en sol operand null olabilir; örneğin: <mark style="color:blue;">fooList?[1]</mark>, <mark style="color:blue;">fooList</mark> null değilse <mark style="color:blue;">1</mark>. indeksteki elemana erişir; aksi takdirde ifade null olarak değerlendirilir.</td></tr><tr><td><code>.</code></td><td>Üye erişimi</td><td>Bir ifadenin özelliğine atıfta bulunur; örneğin: <mark style="color:blue;">foo.bar</mark>, <mark style="color:blue;">foo</mark> ifadesinden bar özelliğini seçer.</td></tr><tr><td><code>?.</code></td><td>Koşullu üye erişimi</td><td>En sol operand null olabilir; örneğin: <mark style="color:blue;">foo?.bar</mark>, <mark style="color:blue;">foo</mark> null değilse <mark style="color:blue;">foo</mark>'nun <mark style="color:blue;">bar</mark> özelliğini seçer; aksi takdirde <mark style="color:blue;">foo?.bar</mark> ifadesinin değeri null olur.</td></tr><tr><td><code>!</code></td><td>Null olmayan güvence operatörü</td><td>Bir ifadeyi altında yatan non-nullable türüne dönüştürür; dönüşüm başarısız olursa çalışma zamanında bir istisna fırlatır; örneğin: <mark style="color:blue;">foo!.bar</mark>, <mark style="color:blue;">foo</mark>'nun null olmadığını ve <mark style="color:blue;">bar</mark> özelliğini seçer, ancak <mark style="color:blue;">foo</mark> null ise çalışma zamanında bir istisna fırlatılır.</td></tr></tbody></table>


# Yorum Satırları (Comments)

Dart, tek satırlık yorumları, çok satırlı yorumları ve belgelendirme yorumlarını destekler.

### Tek Satırlık Yorumlar

Tek satırlık bir yorum, <mark style="color:blue;">//</mark> ile başlar ve satırın sonu arasındaki her şey Dart derleyicisi tarafından görmezden gelinir.

```dart
void main() {
  // TODO: soyut bir LamaSelamlamaFabrikası'na refaktör yapılacak mı?
  print('Lama çiftliğime hoş geldiniz!');
}
```

### Çok Satırlı Yorumlar

Çok satırlı bir yorum,  <mark style="color:blue;">/\*</mark> ile başlar ve <mark style="color:blue;">\*</mark>*<mark style="color:blue;">/</mark>*  biter ve arasındaki her şey Dart derleyicisi tarafından görmezden gelinir (ancak yorum belgelendirme yorumu değilse; bir sonraki bölüme bakınız). Çok satırlı yorumlar iç içe geçebilir.

```dart
void main() {
  /*
   * Bu çok iş. Tavukları beslemeyi düşünün.

  Lama larry = Llama();
  larry.besle();
  larry.egzersizYap();
  larry.temizle();
   */
}

```

### Belgelendirme Yorumları

Belgelendirme yorumları, <mark style="color:blue;">///</mark> veya <mark style="color:blue;">/\*\*</mark> ile başlayan çok satırlı veya tek satırlı yorumlardır. Ardışık satırlarda <mark style="color:blue;">///</mark> kullanmak, çok satırlı bir belgeleme yorumunun aynı etkiye sahip olmasını sağlar.

```dart
/// Güney Amerika'da evcilleştirilmiş bir devekuşu (Lama glama).
///
/// Andean kültürleri, lamaları Hispanik zamanlardan beri et ve yük hayvanı olarak kullanmıştır.
///
/// Diğer hayvanlar gibi, llamanın da [Besle] fonksiyonu ile biraz [Yiyecek]e ihtiyacı vardır.
class Lama {
  String? ad;

  /// Lamanıza [yiyecek] ile yem verir.
  ///
  /// Tipik bir lama haftada bir balya saman yer.
  void besle(Yiyecek yiyecek) {
    // ...
  }

  /// Lamanıza bir [aktivite] ile [zamanSınırı] dakika boyunca egzersiz yaptırır.
  void egzersizYap(Aktivite aktivite, int zamanSınırı) {
    // ...
  }
}
```

Sınıfın oluşturulan belgesinde, \[besle] bağlantıları besle metodunun belgelerine ve \[Yiyecek] bağlantıları Yiyecek sınıfının belgelerine yönlendirilir.


# Meta Veri (Metadata)

Metadata, kodunuz hakkında ek bilgiler vermek için kullanılır. Bir metadata açıklaması, derleme zamanı sabiti bir referansla başlar (@deprecated gibi) veya bir sabit yapılandırıcısına bir çağrıyla devam eder.

Dört metadata anotasyonu Dart dilinde genel olarak kullanılabilir: @Deprecated, @deprecated, @override ve @pragma. @override kullanımı için örnekler için bir sınıfı genişletme konusuna bakın. İşte @Deprecated anotasyonunun kullanımına dair bir örnek:

```dart
class Televizyon {
  /// Gücü açmak için [turnOn] kullanın.
  @Deprecated('turnOn kullanın')
  void etkinlestir() {
    turnOn();
  }

  /// Televizyonun gücünü açar.
  void turnOn() {...}
  // ···
}
```

@deprecated kullanmak istemiyorsanız bir mesaj belirtmek zorunda değilsiniz. Ancak, her zaman @Deprecated ile bir mesaj belirtmenizi öneririz.

Kendi metadata anotasyonlarınızı tanımlayabilirsiniz. İşte iki argüman alan @Todo anotasyonunu tanımlayan bir örnek:

```dart
class Todo {
  final String kim;
  final String ne;

  const Todo(this.kim, this.ne);
}
```

Ve işte bu @Todo anotasyonunu kullanmanın bir örneği:

```dart
@Todo('Dash', 'Bu fonksiyonu implement et')
void birSeyYap() {
  print('Bir şey yap');
}
```

Metadata, bir kütüphane, sınıf, typedef, tür parametresi, constructor, factory, fonksiyon, alan, parametre veya değişken bildirimi ve bir import veya export direktifi öncesinde görünebilir.


# Kütüphaneler & İçe Aktarma (Libraries & Imports)

Programınızı modüler ve paylaşılabilir bir kod tabanı oluşturmak için import ve library direktifleri kullanılabilir. Kütüphaneler sadece API'ları sağlamakla kalmaz, aynı zamanda gizlilik birimidir: alt çizgi (\_) ile başlayan tanımlayıcılar yalnızca ilgili kütüphane içinde görünür. Her Dart dosyası (ve parçaları), bir kütüphane oluştursa da, bir kütüphane direktifi kullanmasa bile.

Kütüphaneler, paketler aracılığıyla dağıtılabilir.

### Kütüphaneleri Kullanma

Bir kütüphanedeki bir isim alanının başka bir kütüphane içindeki kapsamda nasıl kullanılacağını belirtmek için import kullanın.

Örneğin, Dart web uygulamaları genellikle dart:html kütüphanesini kullanır ve şu şekilde içe aktarabilirler:

```dart
import 'dart:html';
```

İçe aktarmak için tek gereken argüman, kütüphaneyi belirten bir URI'dir. Dahili kütüphaneler için URI, özel dart: şemalıdır. Diğer kütüphaneler için dosya sistem yolu veya package: şemasını kullanabilirsiniz. package: şeması, pub aracı gibi bir paket yöneticisi tarafından sağlanan kütüphaneleri belirtir. Örneğin:

```dart
import 'package:test/test.dart';
```

> **Not**
>
> URI, uniform resource identifier'ın kısaltmasıdır. URL'ler (uniform resource locators), genel bir URI türüdür.

### Bir Kütüphane İsim Uzantısı Belirtme

Çakışan tanımlayıcılara sahip iki kütüphaneyi içe aktarıyorsanız, o zaman bir veya her iki kütüphaneye bir ön ek belirtebilirsiniz. Örneğin, library1 ve library2 her ikisinde de bir Element sınıfı varsa, kodunuz şöyle olabilir:

```dart
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// lib1'den Element kullanılır.
Element element1 = Element();

// lib2'den Element kullanılır.
lib2.Element element2 = lib2.Element();
```

### Bir Kütüphanenin Yalnızca Bir Kısmını İçe Aktarma

Bir kütüphanenin yalnızca bir kısmını kullanmak istiyorsanız, kütüphaneyi seçmeli olarak içe aktarabilirsiniz. Örneğin:

```dart
// Sadece foo'yu içe aktar.
import 'package:lib1/lib1.dart' show foo;

// foo hariç tüm isimleri içe aktar.
import 'package:lib2/lib2.dart' hide foo;
```

### Tembel Yükleme (Lazily loading) Bir Kütüphane

Tembel yükleme (ayrıca lazy load olarak da adlandırılır), bir web uygulamasının bir kütüphaneyi ihtiyaç duyulduğunda, yani kütüphane gerektiğinde, yüklemesine izin verir. Tembel yükleme kullanmanın bazı durumları şunlardır:

* Bir web uygulamasının başlangıç zamanını azaltmak için.
* A/B testi yapmak için - örneğin bir algoritmanın alternatif uygulamalarını denemek.
* Nadiren kullanılan işlevselliği yüklemek için, isteğe bağlı ekranlar ve iletişim kutuları gibi.

Bir kütüphaneyi tembel yüklemek için, önce onu `deferred as` ile içe aktarmalısınız.

```dart
import 'package:selamlar/selam.dart' deferred as selam;
```

Kütüphaneye ihtiyaç duyduğunuzda, kütüphaneyi yüklemek için `loadLibrary()` fonksiyonunu kütüphanenin tanımlayıcısını kullanarak çağırın.

```dart
Future<void> selamlasma() async {
  await selam.loadLibrary();
  selam.ekranaYazSelam();
}
```

Yukarıdaki kodda, await anahtar kelimesi kütüphane yüklenene kadar işlemi duraklatır. Async ve await hakkında daha fazla bilgi için, asenkron destek'e bakın.

`loadLibrary()` fonksiyonunu bir kütüphaneye birden fazla kez çağırabilirsiniz. Kütüphane yalnızca bir kez yüklenir.

Tembel yükleme kullanırken aşağıdaki konuları akılda tutun:

* Tembel bir kütüphanenin sabitleri içe aktaran dosyada sabit değildir. Unutmayın, bu sabitler, tembel kütüphane yüklenene kadar mevcut değildir.
* Bir dosyadan tembel bir kütüphanedeki türleri kullanamazsınız. Bunun yerine, arayüz türlerini hem tembel kütüphanede hem de içe aktaran dosyada içeren bir kütüphaneye taşımayı düşünün.
* Dart, `loadLibrary()` fonksiyonunu, deferred as namespace ile tanımladığınız ad uzayına otomatik olarak ekler. `loadLibrary()` fonksiyonu bir `Future` döndürür.

### Kütüphane Direktifi

Kütüphane düzeyinde belge açıklamalarını veya metadata açıklamalarını belirtmek için, bunları dosyanın başındaki bir kütüphane bildirimiyle iliştirebilirsiniz.

```
/// Gerçekten harika bir test kütüphanesi
@TestOn('browser')
library;
```


# Fonksiyonlar (Functions)

Dart, gerçek bir nesne yönelimli dil olduğu için fonksiyonlar bile nesnelerdir ve bir tipe sahiptirler. Bu, fonksiyonların değişkenlere atanabileceği veya başka fonksiyonlara argüman olarak geçirilebileceği anlamına gelir. Ayrıca, bir Dart sınıfının bir örneğini bir fonksiyon gibi çağırabilirsiniz. Detaylar için Çağrılabilir nesneleri inceleyin.

İşte bir fonksiyonu uygulamanın bir örneği:

```dart
bool gezegenMi(String gezegen) {
    return _gezegenler[gezegen] !== null;
}
```

Dart fonksiyonlar için tür açıklamalarını önerse de, fonksiyon türü dahi atlamışsanız çalışır:

```dart
gezegenMi(String gezegen) {
    return _gezegenler[gezegen] !== null;
}
```

Sadece bir ifade içeren fonksiyonlar için arrow fonksiyon kullanabilirsiniz:

```dart
bool gezegenMi(String gezegen) => return _gezegenler[gezegen] !== null;
```

`=> expr` sözdizimi, `{ return expr; }` için bir kısaltmadır. `=>` arrow fonksiyon olarak da adlandırılır.

Not: Arrow (=>) ve noktalı virgül (;) arasında sadece bir ifade —bir deyim— yer alabilir. Örneğin bir if deyimini oraya koyamazsınız, ancak bir koşullu ifade kullanabilirsiniz.

### Parametreler

Bir fonksiyonun herhangi bir sayıda zorunlu parametresi olabilir. Bu, adlandırılmış parametreler veya isteğe bağlı pozisyonel parametrelerle takip edilebilir (ancak ikisi birden değil).

### Adlandırılmış Parametreler

Adlandırılmış parametreler, açıkça zorunlu olarak işaretlenmediği sürece isteğe bağlıdır.

Bir fonksiyon tanımlarken, adlandırılmış parametreleri belirtmek için `{param1, param2, ...}` kullanın. Varsayılan bir değer sağlamaz veya adlandırılmış bir parametreyi zorunlu olarak işaretlememişseniz, türleri nullable olmalıdır, çünkü varsayılan değerleri null olacaktır:

```dart
/// [kalın] ve [gizli] bayraklarını ayarlar ...
void bayraklariEtkinlestir({bool? kalin, bool? gizli}) {...}
```

Fonksiyonu çağırırken, paramName: value kullanarak adlandırılmış argümanları belirtebilirsiniz. Örneğin:

```dart
bayraklariEtkinlestir(kalin: true, gizli: false);
```

Bir adlandırılmış parametre için varsayılan bir değer belirtmek için = kullanın. Belirtilen değer derleme zamanında bir sabit olmalıdır:

```dart
/// [kalin] ve [gizli] bayraklarını ayarlar ...
void bayraklariEtkinlestir({bool kalin = false, bool gizli = false}) {...}

// kalin true olacak; gizli false olacak.
bayraklariEtkinlestir(kalin: true);
```

Eğer adlandırılmış bir parametrenin zorunlu olmasını istiyorsanız, parametreleri required ile işaretleyebilirsiniz:

```dart
const KaydirmaCubugu({super.key, required Widget child});
```

Eğer birisi child argümanını belirtmeden bir `KaydirmaCubugu` oluşturmaya çalışırsa, analizör bir hatayı rapor eder.

Not: Zorunlu olarak işaretlenmiş bir parametre hâlâ nullable olabilir:

```dart
const KaydirmaCubugu({super.key, required Widget? child});
```

Pozisyonel argümanları önce koymak isteyebilirsiniz, Dart buna zorlama yapmaz.&#x20;

```dart
tekrar(kez: 2, () {
  ...
});
```

### İsteğe Bağlı Pozisyonel Parametreler

Fonksiyon parametrelerini \[] içine almak, onları isteğe bağlı pozisyonel parametre olarak işaretler. Varsayılan bir değer sağlamazsanız, türleri nullable olmalıdır, çünkü varsayılan değerleri null olacaktır:

```dart
String soyle(String kimden, String mesaj, [String? cihaz]) {
  var sonuc = '$kimden diyor ki $mesaj';
  if (cihaz != null) {
    sonuc = '$sonuc bir $cihaz ile gönderildi.';
  }
  return sonuc;
}
```

İsteğe bağlı parametre olmadan bu fonksiyonu çağırmak için örnek:

```dart
assert(soyle('Bayram', 'Merhaba') == 'Bayram diyor ki Merhaba');
```

Ve üçüncü parametre ile bu fonksiyonu çağırmak için örnek:

```dart
assert(soyle('Bayram', 'Merhaba', 'Android') ==
    'Bayram diyor ki Merhaba bir Android ile gönderildi.');
```

İsteğe bağlı pozisyonel bir parametre için varsayılan bir değer belirtmek için = kullanın. Belirtilen değer derleme zamanında bir sabit olmalıdır:

```dart
String soyle(String kimden, String mesaj, [String cihaz = 'telefon']) {
  var sonuc = '$kimden diyor ki $mesaj bir $cihaz ile gönderildi.';
  return sonuc;
}

assert(soyle('Bayram', 'Merhaba') == 'Bayram diyor ki Merhaba bir telefon ile gönderildi.');
```

### main() Fonksiyonu

Her uygulamanın, uygulamanın giriş noktası olarak hizmet eden bir main() fonksiyonu olmalıdır. main() fonksiyonu `void` döndürür ve opsiyonel olarak bir `List<String>` parametresi alır.

İşte basit bir main() fonksiyonu:

```dart
void main() {
  print('Merhaba, Dünya!');
}
```

İşte argümanları alan bir komut satırı uygulaması için main() fonksiyonunun bir örneği:

```dart
// Uygulamayı şu şekilde çalıştırın: dart run argumanlar.dart 1 test
void main(List<String> argumanlar) {
  print(argumanlar);

  assert(argumanlar.length == 2);
  assert(int.parse(argumanlar[0]) == 1);
  assert(argumanlar[1] == 'test');
}
```

Komut satırı argümanlarını tanımlamak ve çözmek için `args` kütüphanesini kullanabilirsiniz.

### Birinci Sınıf Fonksiyonlar

Bir fonksiyonu başka bir fonksiyona parametre olarak iletebilirsiniz. Örneğin:

```dart
void elemaniYazdir(int eleman) {
  print(eleman);
}

var liste = [1, 2, 3];

liste.forEach(elemaniYazdir);
```

Bir fonksiyonu bir değişkene atayabilirsiniz, örneğin:

```dart
var yuksekSes = (mesaj) => '!!! ${mesaj.toUpperCase()} !!!';
assert(yuksekSes('merhaba') == '!!! MERHABA !!!');
```

Bu örnek anonim bir fonksiyon kullanmaktadır. Daha fazlasını bir sonraki bölümde bulabilirsiniz.

### Anonim Fonksiyonlar

Çoğu fonksiyon adlıdır, örneğin `main()` veya `elemaniYazdir()`.  Bu fonksiyonu bir değişkene atayarak, bir koleksiyondan ekleyerek veya bir fonksiyonu çağırırken argüman olarak kullanarak bir koleksiyondan ekleyebilir veya kaldırabilirsiniz.

Anonim bir fonksiyon, parantez içinde sıfır veya daha fazla parametre, virgülle ayrılmış, opsiyonel tür açıklamaları ve parantez içinde fonksiyonun gövdesi olan bir kod bloğuna sahiptir:

```dart
([Type param1[, …]]) {
  kodBlogu;
};
```

Aşağıdaki örnek, bir listedeki her öğe için çağrılan map fonksiyonuna geçirilen bir anonim fonksiyonu tanımlar. Liste üzerinde çağrılan fonksiyon, her dizeyi büyük harfe çevirir ve ardından forEach'e geçirilen başka bir anonim fonksiyon tarafından her bir dize ve uzunluğu yazdırılır:

```dart
const liste = ['elma', 'muz', 'portakal'];
liste.map((eleman) {
  return eleman.toUpperCase();
}).forEach((eleman) {
  print('$eleman: ${eleman.length}');
});
```

Eğer fonksiyon sadece bir ifade veya return ifadesi içeriyorsa, arrow fonksiyon kullanarak bunu kısaltabilirsiniz.

```dart
liste
    .map((eleman) => eleman.toUpperCase())
    .forEach((eleman) => print('$eleman: ${eleman.length}'));
```

### Leksik Kapsam

Dart, leksik olarak kapsamlı bir dildir, bu da değişkenlerin kapsamının kodun düzenine bağlı olarak statik olarak belirlendiği anlamına gelir.&#x20;

İşte her kapsam düzeyinde değişkenlerle iç içe geçmiş fonksiyonlar içeren bir örnek:

```dart
bool ustDuzey = true;

void main() {
  var mainIc = true;

  void benimFonksiyonum() {
    var fonksiyonIc = true;

    void icIceFonksiyon() {
      var fonksiyonIcIce = true;

      assert(ustDuzey);
      assert(mainIc);
      assert(fonksiyonIc);
      assert(fonksiyonIcIce);
    }
  }
}

```

Dikkat edin ki `icIceFonksiyon()`, en üst düzeyden en alt düzeye kadar her düzeyden değişkenleri kullanabilir.

### Leksikal Kapanışlar

Bir kapanış (closure), kendi leksikal kapsamındaki değişkenlere erişimi olan bir fonksiyon nesnesidir, fonksiyon orijinal kapsamından çıkarıldığında bile. Aşağıdaki örnekte, makeAdder() fonksiyonu addBy değişkenine erişir ve döndürülen fonksiyon, addBy'yi hatırlar:

```dart
/// [miktar] değerini fonksiyonun argümanına ekleyen bir fonksiyon döndürür.
Function ekleyiciOlustur(int miktar) {
  return (int i) => miktar + i;
}

void main() {
  // 2 ekleyen bir fonksiyon oluşturun.
  var ekle2 = ekleyiciOlustur(2);

  // 4 ekleyen bir fonksiyon oluşturun.
  var ekle4 = ekleyiciOlustur(4);

  assert(ekle2(3) == 5);
  assert(ekle4(3) == 7);
}
```

### Fonksiyonların Eşitliğini Test Etme

İşte en üst seviye fonksiyonları, statik yöntemleri ve örnek yöntemleri eşitlik için test etme örneği:

```dart
void foo() {} // Bir üst seviye fonksiyon

class A {
  static void bar() {} // Bir statik yöntem
  void baz() {} // Bir örnek yöntemi
}

void main() {
  Function x;

  // Üst seviye fonksiyonları karşılaştırma.
  x = foo;
  assert(foo == x);

  // Statik yöntemleri karşılaştırma.
  x = A.bar;
  assert(A.bar == x);

  // Örnek yöntemleri karşılaştırma.
  var v = A(); // A'nın 1. örneği
  var w = A(); // A'nın 2. örneği
  var y = w;
  x = w.baz;

  // Bu kapanışlar aynı örneği (#2) referans aldığı için
  // bunlar eşittir.
  assert(y.baz == x);

  // Bu kapanışlar farklı örnekleri referans aldığı için
  // bunlar eşit değildir.
  assert(v.baz != w.baz);
}
```

Return (Dönüş) Değerleri

Tüm fonksiyonlar bir değer döndürür. Eğer belirli bir dönüş değeri belirtilmemişse, return null ifadesi otomatik olarak fonksiyon gövdesine eklenir.

```dart
foo() {}

assert(foo() == null);
```

Bir fonksiyonda birden çok değer döndürmek için, değerleri bir kayıt içinde toplayabilirsiniz.

```dart
(String, int) foo() {
  return ('bir şey', 42);
}
```

### Generators (Üreteçler)

Bir değer dizisi üretmeye ihtiyaç duyduğunuzda, bir üreteç fonksiyonu kullanmayı düşünebilirsiniz. Dart, iki tür üreteç fonksiyonunu destekler:

* Senkron üreteç: `Iterable` nesnesi döndürür.
* Asenkron üreteç: `Stream` nesnesi döndürür.

Senkron üreteç fonksiyonu uygulamak için, fonksiyon gövdesini `sync*` olarak işaretleyin ve değerleri iletmek için yield ifadelerini kullanın:

```dart
Iterable<int> dogalSayilar(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}
```

Asenkron üreteç fonksiyonu uygulamak için, fonksiyon gövdesini `async*` olarak işaretleyin ve değerleri iletmek için `yield` ifadelerini kullanın:

```dart
Stream<int> asenkronDogalSayilar(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}
```

Eğer üreteciniz özyineliyse (rekürsif), `yield*` kullanarak performansını artırabilirsiniz:

```dart
Iterable<int> dogalSayilariAsagidan(int n) sync* {
  if (n > 0) {
    yield n;
    yield* dogalSayilariAsagidan(n - 1);
  }
}
```

### Harici Fonksiyonlar

Bir harici fonksiyon, gövdesi bildirimi dışında bir yerde uygulanan bir fonksiyondur. Bir fonksiyon bildiriminden önce external kelimesini ekleyin, örneğin:

```dart
external void baziFonksiyon(int i);
```

Bir harici fonksiyonun uygulaması başka bir Dart kütüphanesinden gelebilir veya daha yaygın olarak başka bir dilde yazılmış olabilir. Entegrasyon bağlamında, harici, Dart'ta kullanılabilir hale getirmek için yabancı fonksiyonlar veya değerler için tür bilgisi sağlar.

Harici fonksiyonlar, birinci düzey fonksiyonlar, örnek yöntemler, `getter`'lar veya `setter`'lar veya yönlendirici olmayan kurucular olabilir. Eğer değişken `final` değilse, bir örnek değişkeni de harici olabilir ve bu, bir harici `getter` ve (değişken `final` değilse) bir harici `setter` ile eşdeğerdir.

<br>


# Dahili Tipler (Build-in Types)

Dart dilinde şu özel tiplere özel destek bulunmaktadır:

* Sayılar (int, double)
* Dizeler (String)
* Booleanlar (bool)
* Kayıtlar (value1, value2)
* Listeler (List, aynı zamanda diziler olarak bilinir)
* Setler (Set)
* Haritalar (Map)
* Runes (Runes; genellikle karakterler API tarafından değiştirilir)
* Semboller (Symbol)
* null değeri (Null)

Bu destek, nesneleri literaller kullanarak oluşturma yeteneğini içerir. Örneğin, 'bu bir dizedir' bir dize literali ve true bir boolean literali.

Dart'ta her değişkenin bir sınıf örneği olan bir nesneye atıfta bulunduğunu unutmayın. Genellikle değişkenleri başlatmak için yapılandırıcıları kullanabilirsiniz. Bazı yerleşik tiplerin kendi yapılandırıcıları vardır. Örneğin, bir harita oluşturmak için `Map()` yapılandırıcısını kullanabilirsiniz.

Bazı diğer tipler de Dart dilinde özel rolleri vardır:

* Object: Null hariç tüm Dart sınıflarının üst sınıfı.
* Enum: Tüm enumların üst sınıfı.
* Future ve Stream: Asenkron destekte kullanılır.
* Iterable: for-in döngülerinde ve senkron jeneratör fonksiyonlarında kullanılır.
* Never: Bir ifadenin asla başarıyla değerlendirilemeyeceğini belirtir. Genellikle her zaman istisna fırlatan fonksiyonlar için kullanılır.
* dynamic: Statik kontrolü devre dışı bırakmak istediğinizi belirtir. Genellikle bunun yerine Object veya Object? kullanmalısınız.
* void: Bir değerin asla kullanılmadığını belirtir. Genellikle bir dönüş türü olarak kullanılır.

Object, Object?, Null ve Never sınıfları sınıf hiyerarşisinde özel rolleri vardır. Bu roller hakkında bilgi için "null safety" anlayışını inceleyin.

### Sayılar

Dart sayıları iki türde gelir:

* int: Platforma bağlı olarak 64 bit'ten büyük olmayan tamsayı değerleri. Web üzerinde tamsayı değerleri, JavaScript sayıları olarak temsil edilir (kesirli kısmı olmayan 64 bit kayan noktalı sayılar) ve -253 ile 253 - 1 arasında olabilir.
* double: IEEE 754 standardında belirtildiği gibi 64 bit (çift hassasiyetli) kayan noktalı sayılar.

Hem `int` hem de `double`, `num` alt tipleridir. `num` tipi, +, -, / gibi temel operatörleri içerir ve aynı zamanda `abs()`, `ceil()`, `floor()` gibi diğer yöntemleri bulunan yerdir. (<< gibi bitişik operatörler `int` sınıfında tanımlanmıştır.) Eğer `num` ve alt tiplerinde aradığınız şey yoksa, `dart:math` kütüphanesinde bulabilirsiniz.

Tamsayılar ondalık noktasız sayılardır. İşte tamsayı literallerini tanımlamanın bazı örnekleri:

```dart
var x = 1;
var hex = 0xDEADBEEF;
```

Bir sayı ondalık içeriyorsa, o bir double'dır. İşte double literallerini tanımlamanın bazı örnekleri:

```dart
var y = 1.1;
var exponents = 1.42e5;
```

Ayrıca bir değişkeni `num` olarak bildirebilirsiniz. Bunu yaptığınızda, değişken hem `int` (tamsayı) hem de `double` değerlere sahip olabilir.

```dart
num x = 1; // x hem int hem de double değerlere sahip olabilir
x += 2.5;
```

Tamsayı literalleri otomatik olarak `double`'lara dönüştürülür:

```dart
// String -> int
var bir = int.parse('1');
assert(bir == 1);

// String -> double
var birNoktaBir = double.parse('1.1');
assert(birNoktaBir == 1.1);

// int -> String
String birAsString = 1.toString();
assert(birAsString == '1');

// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');
```

`int` türü, geleneksel bit kaydırma (<<, >>, >>>), tümleme (\~), VE (&), VEYA (|), ve XOR (^) operatörlerini belirtir. Bunlar bit alanlarında bayrakları manipüle etmek için kullanışlıdır. Örneğin:

```dart
assert((3 << 1) == 6); // 0011 << 1 == 0110
assert((3 | 4) == 7); // 0011 | 0100 == 0111
assert((3 & 4) == 0); // 0011 & 0100 == 0000
```

Daha fazla örnek için bitişik ve kaydırma operatörü bölümüne bakın.

Literal sayılar derleme zamanı sabitleridir. Operandları sayı olan derleme zamanı sabitler olduğu sürece, birçok aritmetik ifade de derleme zamanı sabitidir.

```dart
const msSaniyeBasi = 1000;
const tekrarDenemeyeKadarSaniyeler = 5;
const tekrarDenemeyeKadarMilisaniyeler = tekrarDenemeyeKadarSaniyeler * msSaniyeBasi;
```

Daha fazla bilgi için Dart'ta Sayılar sayfasına bakın.

### Dizeler

Bir Dart dizesi (String nesnesi), UTF-16 kod birimlerinin bir dizisini tutar. Bir dize oluşturmak için tek veya çift tırnakları kullanabilirsiniz:

```dart
var s1 = 'Tek tırnaklar dize literalleri için iyi çalışır.';
var s2 = "Çift tırnaklar da aynı şekilde çalışır.";
var s3 = 'Dize sınırlayıcısını kaçırmak kolaydır.';
var s4 = "Diğer sınırlayıcıyı kullanmak ise daha da kolaydır.";
```

Bir ifadenin değerini bir dizenin içine yerleştirmek için `${expression}` kullanabilirsiniz. Eğer ifade bir tanımlayıcı ise, `{}`'yi atlayabilirsiniz. Bir nesnenin karşılık gelen dizesini almak için Dart, nesnenin `toString()` yöntemini çağırır.

```dart
var s = 'dize enterpolasyonu';

assert('Dart\'ın $s özelliği çok kullanışlıdır.' ==
    'Dart\'ın dize enterpolasyonu, '
        'çok kullanışlıdır.');
assert('Bunu bütün harflerle yazmalısın. '
        '${s.toUpperCase()} çok kullanışlıdır!' ==
    'Bunu bütün harflerle yazmalısın. '
        'DİZE ENTERPOLASYONU çok kullanışlıdır!');
```

> **Not**
>
> `==` operatörü, iki nesnenin eşit olup olmadığını test eder. İki dize, aynı kod birimleri dizisini içeriyorsa eşittir.

Dizeleri bitişik dize literalleri veya + operatörü kullanarak birleştirebilirsiniz:

```dart
var s1 = 'Dize '
    'birleştirmesi'
    " hatta satır sonları üzerinde çalışır.";
assert(s1 ==
    'Dize birleştirmesi hatta satır sonları üzerinde '
        'çalışır.');

var s2 = 'Artı operatörü ' + 'çalışır, aynı şekilde.';
assert(s2 == 'Artı operatörü çalışır, aynı şekilde.');
```

Çok satırlı bir dize oluşturmak için üçlü tırnak kullanın, ya tek ya da çift tırnak işareti ile:

```dart
var s1 = '''
Bu şekilde
çok satırlı diziler oluşturabilirsiniz.
''';

var s2 = """Bu da bir
çok satırlı dizedir.""";
```

"Ham" bir dize oluşturmak için başına r ekleyin:

```dart
var s = r'Düz bir dizide, hatta \n bile özel işlem görmez.';
```

Unicode karakterlerini bir dizide nasıl ifade edeceğiniz hakkında detaylar için Runes ve grafem kümeleri bölümüne bakın.

Literal dizeler derleme zamanı sabitleridir, iç içe geçmiş ifade bir derleme zamanı sabitine dönüşürse ve bu sabit null veya sayısal, dize veya boolean bir değere değerlendirilirse.

```dart
// Bu bir const dizede çalışır.
const birSabitSayi = 0;
const birSabitBool = true;
const birSabitDize = 'sabit bir dize';

// Bu bir const dizede ÇALIŞMAZ.
var birSayi = 0;
var birBool = true;
var birDize = 'bir dize';
const birSabitListe = [1, 2, 3];

const gecerliSabitDize = ${birSabitSayi} ${birSabitBool} ${birSabitDize};
// const gecersizSabitDize = ${birSayi} ${birBool} ${birDize} ${birSabitListe};
```

Dizeleri kullanma hakkında daha fazla bilgi için Dizeler ve düzenli ifadeler sayfasına göz atın.

### Boolean

Boolean değerleri temsil etmek için Dart'ta bool adında bir tür bulunmaktadır. `bool` türünde yalnızca iki nesne bulunur: true ve false boolean literalleri, her ikisi de derleme zamanı sabitleridir.

Dart'ın tip güvenliği, `if (nonbooleanValue)` veya `assert (nonbooleanValue)` gibi kodları kullanamayacağınız anlamına gelir. Bunun yerine, değerleri açıkça kontrol etmelisiniz, örneğin:

```dart
// Boş bir dize için kontrol et.
var tamAd = '';
assert(tamAd.isEmpty);

// Sıfır için kontrol et.
var vurusNoktasi = 0;
assert(vurusNoktasi <= 0);

// Null için kontrol et.
var tekboynuz = null;
assert(tekboynuz == null);

// NaN için kontrol et.
var isteğimiYaptım = 0 / 0;
assert(isteğimiYaptım.isNaN);
```

### Runes ve Grafem Kümeleri

Dart'ta, runeler bir dizenin Unicode kod noktalarını ortaya çıkarır. Kullanıcı tarafından algılanan karakterleri, yani Unicode (genişletilmiş) grafem kümelerini görüntülemek veya manipüle etmek için characters paketini kullanabilirsiniz.

Unicode, dünyanın tüm yazı sistemlerinde kullanılan her harf, rakam ve simge için benzersiz bir sayısal değer tanımlar. Bir Dart dizesi UTF-16 kod birimlerinin bir dizisi olduğundan, bir dize içinde Unicode kod noktalarını ifade etmek için özel bir sözdizimi gereklidir. Bir Unicode kod noktasını ifade etmenin yaygın yolu `\uXXXX`, burada XXXX 4 haneli onaltılık bir değerdir. Örneğin, kalp karakteri (♥) `\u2665`'tir. 4 onaltılık hane yerine değeri süslü parantez içine alabilirsiniz. Örneğin, gülme emoji (😆) `\u{1f606}`'dır.

Bireysel Unicode karakterlerini okuma veya yazma ihtiyacınız varsa, characters paketi tarafından String üzerinde tanımlanan characters alıcısını kullanın. Dönen Characters nesnesi, dizenin bir dizi grafem kümesi olarak temsilidir. İşte characters API'ını kullanmanın bir örneği:

```dart
import 'package:characters/characters.dart';

void main() {
  var merhaba = 'Merhaba 🌍';
  print(merhaba);
  print('Dizenin sonu: ${merhaba.substring(merhaba.length - 1)}');
  print('Son karakter: ${merhaba.characters.last}');
}
```

Çıktı, ortamınıza bağlı olarak şuna benzer olacaktır:

```
dart run main.dart
Merhaba 🌍
Dizenin sonu: ???
Son karakter: 🌍
```

Dize manipülasyonları için characters paketini kullanma hakkında daha fazla bilgi için characters paketinin örnek ve API referansına bakın.

### Semboller

Bir Symbol nesnesi, bir Dart programında bildirilen bir operatörü veya tanımlayıcıyı temsil eder. Semboller kullanmanız gerekli olmayabilir, ancak tanımlayıcı adlarını değiştirmeğin, ancak sembol adlarını değiştirmediği API'lar için çok değerli oldukları durumlar vardır.

Bir tanımlayıcı için sembolü almak için, sadece tanımlayıcının yanına `#` ekleyin:

```dart
#foo
#bar
```


# Kayıtlar (Records)

Dart dilinde, kayıtlar (records) anonim, değişmez ve birleşik bir türdür. Diğer koleksiyon türleri gibi, birden çok nesneyi tek bir nesne içinde paketlemenizi sağlar. Ancak kayıtlar, sabit boyutlu, heterojen ve tip belirlidir.

### Kayıt Sözdizimi

Kayıt ifadeleri, adlandırılmış veya konumsal alanların parantez içinde virgülle ayrılmış listeleridir:

```dart
var kayit = ('ilk', a: 2, b: true, 'son');
```

Kayıt türü açıklamaları, parantez içinde virgülle ayrılmış tür listeleridir. Bu açıklamaları kullanarak dönüş ve parametre türlerini tanımlamak mümkündür.

```dart
(int, int) swap((int, int) kayit) {
  var (a, b) = kayit;
  return (b, a);
}
```

Kayıt ifadesindeki sıralı alanlar, parantez içine doğrudan yerleştirilir:

```dart
// Bir değişken bildiriminde kayıt türü açıklaması:
(String, int) kayit;

// Bir kayıt ifadesi ile başlat:
kayit = ('Bir dize', 123);
```

Kayıt türü açıklamasında, adlandırılmış alanlar, tüm konumsal alanlardan sonra gelen bir tip ve isim çiftlerinden oluşan bir parantez içinde yer alır. Bir kayıt ifadesinde isimler, her alan değerinden önce gelen bir ikinci nokta üst üste ile belirtilir:

```dart
// Bir değişken bildiriminde kayıt türü açıklaması:
({int a, bool b}) kayit;

// Bir kayıt ifadesi ile başlat:
kayit = (a: 123, b: true);
```

Adlandırılmış alanların isimleri, bir kayıt türünün tanımı veya şekli olan kayıt türünün bir parçasıdır. Farklı isimlere sahip adlandırılmış alanlara sahip iki kayıt, farklı tiplere sahiptir:

```dart
({int a, int b}) kayitAB = (a: 1, b: 2);
({int x, int y}) kayitXY = (x: 3, y: 4);

// Derleme hatası! Bu kayıtlar aynı tipe sahip değil.
// kayitAB = kayitXY;
```

Kayıt türü açıklamasında konumsal alanlara da isim verilebilir, ancak bu isimler sadece belgeleme amaçlıdır ve kaydın türünü etkilemez:

```dart
(int a, int b) kayitAB = (1, 2);
(int x, int y) kayitXY = (3, 4);

kayitAB = kayitXY; // Tamam.
```

Bu, bir fonksiyon bildirimi veya fonksiyon typedef'indeki konumsal parametrelerin adlandırılabilir olduğu, ancak bu isimlerin fonksiyonun imzasını etkilemediği şekilde çalışır.

### Kayıt Alanları

Kayıt alanlarına, yerleşik getter'lar aracılığıyla erişilebilir. Kayıtlar değişmez olduğundan, alanlara setter'lar eklenmez.

Adlandırılmış alanlar, aynı adı taşıyan getter'lara sahiptir. Konumsal alanlar ise adlandırılmış alanları atlayarak `$<position>` adında getter'lara sahiptir:

```dart
var kayit = ('ilk', a: 2, b: true, 'son');

print(kayit.$1); // 'ilk' yazdırılır
print(kayit.a);  // 2 yazdırılır
print(kayit.b);  // true yazdırılır
print(kayit.$2); // 'son' yazdırılır
```

Kayıt alanlarına daha hızlı erişim için Desenler sayfasına göz atabilirsiniz.

### Kayıt Tipleri

Bireysel kayıt tipleri için tip bildirimi yoktur. Kayıtlar, alanlarının türlerine dayalı olarak yapısal olarak tipi belirlenen bir yapıya sahiptir. Bir kaydın şekli (alanlarının kümesi, alan türleri ve isimleri, varsa) bir kaydın türünü benzersiz olarak belirler.

Bir kayıttaki her alanın kendi türü vardır ve aynı kayıt içinde farklı türlerde olabilir. Tür sistemi, bir alanın türünü kayıttan nerede erişildiği konusunda bilir:

```dart
(num, Object) cift = (42, 'a');

var ilk = cift.$1; // Statik tip `num`, çalışma zamanı tipi `int`.
var ikinci = cift.$2; // Statik tip `Object`, çalışma zamanı tipi `String`.
```

Aynı alan kümesine sahip iki bağlantısız kütüphaneyi düşünün. Tür sistemi, bu kayıtların aynı tür olduğunu anlar, ancak kütüphaneler birbirine bağlı değildir.

### Kayıt Eşitliği

İki kayıt, aynı şekle (alan kümesi) sahipse ve karşılık gelen alanları aynı değerlere sahipse eşittir. Adlandırılmış alan sırası, bir kaydın şeklinin bir parçası olmadığından, adlandırılmış alanların sırası eşitliği etkilemez.

Örneğin:

```dart
(int x, int y, int z) nokta = (1, 2, 3);
(int r, int g, int b) renk = (1, 2, 3);

print(nokta == renk); // 'true' yazdırılır.
({int x, int y, int z}) nokta = (x: 1, y: 2, z: 3);
({int r, int g, int b}) renk = (r: 1, g: 2, b: 3);

print(nokta == renk); // 'false' yazdırılır. Lint: Farklı türlerde eşittir.
```

Kayıtlar otomatik olarak alan yapılarına dayalı hashCode ve == metodlarını tanımlar.

### Çoklu Değer Döndürme

Kayıtlar, fonksiyonların bir araya topluca birden çok değer döndürmelerine olanak tanır. Kayıt değerlerini bir dönüşten almak için desen eşleme kullanarak değerleri yerel değişkenlere çözebilirsiniz:

```dart
// Bir kayıt içinde birden çok değer döndür:
(String, int) kullaniciBilgisi(Map<String, dynamic> json) {
  return (json['isim'] as String, json['yas'] as int);
}

final json = <String, dynamic>{
  'isim': 'Dash',
  'yas': 10,
  'renk': 'mavi',
};

// Kayıt deseni kullanarak çöz:
var (isim, yas) = kullaniciBilgisi(json);

/* Şu ile eşdeğer:
  var bilgi = kullaniciBilgisi(json);
  var isim = bilgi.$1;
  var yas  = bilgi.$2;
*/
```

Kayıtlar olmadan bir fonksiyondan çoklu değer döndürmek mümkündür, ancak diğer yöntemler dezavantajlarla gelir. Örneğin, bir sınıf oluşturmak daha çok sözdizimi gerektirir ve List veya Map gibi diğer koleksiyon türlerini kullanmak tip güvenliğini kaybetmenize neden olabilir.

> **Not**
>
> Kayıtların çoklu dönüş ve heterojen tip özellikleri, farklı türlerdeki gelecekleri paralel hale getirme olanağı sağlar.&#x20;

Bu konuyla ilgili daha fazla bilgi için dart:async belgelerine bakabilirsiniz.

<br>


# Koleksiyonlar (Collections)

Dart, listeler, küme ve harita koleksiyonlarına yerleşik destek sunar. Tür koleksiyonlardaki türleri yapılandırmak için Generics konusuna göz atabilirsiniz.

### Listeler (List)

Belki de neredeyse her programlama dilinde en yaygın koleksiyon, dizi veya sıralı nesneler grubudur. Dart'ta diziler List nesneleridir, bu nedenle çoğu insan onlara sadece "lists" der.

Dart listeleri, virgülle ayrılmış bir ifade veya değer listesi ile köşeli parantez içinde belirtilen listelerdir (`[]`). İşte basit bir Dart liste örneği:

```dart
var liste = [1, 2, 3];
```

> **Not**
>
> Dart, listenin türünün `List<int>` olduğunu çıkarır. Bu liste üzerine tamsayı olmayan nesneler eklemeye çalışırsanız, analizör veya çalışma zamanı bir hata bildirir. Daha fazla bilgi için tip çıkarımını inceleyin.

Dart koleksiyon literallerinde son öğeden sonra bir virgül ekleyebilirsiniz. Bu virgül koleksiyonu etkilemez, ancak kopyala-yapıştır hatalarını önlemeye yardımcı olabilir.

```dart
var liste = [
  'Araba',
  'Tekne',
  'Uçak',
];
```

Listeler, 0'dan başlayan bir dizinle kullanılır, 0 ilk değerin dizini ve list.length - 1 son değerin dizinidir. Bir listenin uzunluğunu .length özelliğini kullanarak alabilir ve bir listenin değerlerine köşeli parantez operatörünü kullanarak erişebilirsiniz:

```dart
var liste = [1, 2, 3];
assert(liste.length == 3);
assert(liste[1] == 2);

liste[1] = 1;
assert(liste[1] == 1);
```

Derleme zamanı sabiti bir liste oluşturmak için, liste literali önüne const ekleyin:

```dart
var sabitListe = const [1, 2, 3];
// sabitListe[1] = 1; // Bu satır bir hata verecektir.
```

Daha fazla bilgi için listeler bölümüne bakın.

### Kümeler (Set)

Dart'ta bir küme, benzersiz öğelerin sırasız bir koleksiyonudur. Dart'ın kümelere yönelik desteği, küme literalleri ve Set türü tarafından sağlanır.

İşte küme literali kullanılarak oluşturulmuş basit bir Dart kümesi:

```dart
var halojenler = {'flor', 'klor', 'brom', 'iyot', 'astatin'};
```

> **Not**
>
> Dart, halojenlerin Set\<String> türünde olduğunu çıkarır. Eğer kümeye yanlış türde bir değer eklemeye çalışırsanız, analizör veya çalışma zamanı bir hata verecektir. Daha fazla bilgi için tip çıkarımını inceleyin.

Boş bir küme oluşturmak için, bir tür argümanını takiben {} kullanın veya {}'yi Set türünde bir değişkene atayın:

```dart
var isimler = <String>{};
// Set<String> isimler = {}; // Bu da çalışır.
// var isimler = {}; // Bu bir harita oluşturur, küme değil.
```

Set mi yoksa harita mı? Harita literallerinin sözdizimi, küme literallerinin sözdizimine benzer. Harita literalleri önce geldiği için, `{}` varsayılan olarak Map türüne sahiptir. `{}` veya ona atanan değişken üzerinde tür açıklamasını unutursanız, Dart `{}` veya ona atanan değişken için `Map<dynamic, dynamic>` türünde bir nesne oluşturur.

Varolan bir kümeye `add()` veya `addAll()` yöntemleri kullanarak öğe ekleyebilirsiniz:

```dart
var elementler = <String>{};
elementler.add('flor');
elementler.addAll(halojenler);
```

Kümedeki öğelerin sayısını almak için .length'yi kullanın:

```dart
var elementler = <String>{};
elementler.add('flor');
elementler.addAll(halojenler);
assert(elementler.length == 5);
```

Derleme zamanı sabiti bir küme oluşturmak için, küme literali önüne const ekleyin:

```dart
dartCopy codefinal sabitKume = const {
  'flor',
  'klor',
  'brom',
  'iyot',
  'astatin',
};
// sabitKume.add('helyum'); // Bu satır bir hata verecektir.
```

Daha fazla bilgi için setler bölümüne bakın.

### Haritalar (Maps)

Genel olarak bir harita, anahtarları ve değerleri ilişkilendiren bir nesnedir. Hem anahtarlar hem de değerler her türde nesne olabilir. Her anahtar yalnızca bir kez geçer, ancak aynı değeri birden çok kez kullanabilirsiniz. Dart'ın haritalara yönelik desteği, harita literalleri ve `Map` türü tarafından sağlanır.

İşte harita literalleri kullanılarak oluşturulmuş basit birkaç Dart haritası:

```dart
var hediyeler = {
  // Anahtar:    Değer
  'birinci': 'bayağı kuş',
  'ikinci': 'kumrular',
  'beşinci': 'altın yüzükler'
};

var soyluGazlar = {
  2: 'helyum',
  10: 'neon',
  18: 'argon',
};
```

Not: Dart, hediyelerin `Map<String, String>` türünde olduğunu ve soyluGazların `Map<int, String>` türünde olduğunu çıkarır. Haritaya yanlış türde bir değer eklemeye çalışırsanız, analizör veya çalışma zamanı bir hata verecektir. Daha fazla bilgi için tip çıkarımını inceleyin.

Aynı nesneleri bir `Map` yapıcı kullanarak oluşturabilirsiniz:

```dart
var hediyeler = Map<String, String>();
hediyeler['birinci'] = 'bayağı kuş';
hediyeler['ikinci'] = 'kumrular';
hediyeler['beşinci'] = 'altın yüzükler';

var soyluGazlar = Map<int, String>();
soyluGazlar[2] = 'helyum';
soyluGazlar[10] = 'neon';
soyluGazlar[18] = 'argon';
```

> **Not**
>
> C# veya Java gibi dillerden geliyorsanız, `new Map()` yerine sadece `Map()` görmeyi bekleyebilirsiniz. Dart'ta `new` anahtar kelimesi isteğe bağlıdır. Ayrıntılar için constructor kullanımına bakın.

Varolan bir haritaya yeni bir anahtar-değer çifti eklemek için subscript assignment operator (`[]=`) kullanın:

```dart
var hediyeler = {'birinci': 'bayağı kuş'};
hediyeler['dördüncü'] = 'cazip kuşlar'; // Bir anahtar-değer çifti ekle
```

Bir haritadan bir değeri subscript operator (`[]`) kullanarak alabilirsiniz:

```dart
var hediyeler = {'birinci': 'bayağı kuş'};
assert(hediyeler['birinci'] == 'bayağı kuş');
```

Bir haritada olmayan bir anahtarı ararsanız, null değeri alırsınız:

```dart
var hediyeler = {'birinci': 'bayağı kuş'};
assert(hediyeler['beşinci'] == null);
```

Key-value çiftlerinin sayısını almak için `.length` kullanın:

```dart
var hediyeler = {'birinci': 'bayağı kuş'};
hediyeler['dördüncü'] = 'cazip kuşlar';
assert(hediyeler.length == 2);
```

Derleme zamanı sabiti bir harita oluşturmak için, harita literali önüne `const` ekleyin:

```dart
final sabitHarita = const {
  2: 'helyum',
  10: 'neon',
  18: 'argon',
};

// sabitHarita[2] = 'Helyum'; // Bu satır bir hata verecektir.
```

Daha fazla bilgi için haritalar bölümüne bakın.

### Operatörler (Operators)

#### Yayılma operatörleri (Spread operatos)

Dart, `List`, `Map` ve `Set` literallerinde yayılma operatörünü (`...`) ve null bilincinden dolayı yayılma operatörünü (`...?`) destekler. Yayılma operatörleri, birden çok değeri bir koleksiyona eklemenin özlü bir yolunu sağlar.

Örneğin, yayılma operatörünü (`...`) kullanarak bir listenin tüm değerlerini başka bir liste ekleyebilirsiniz:

```dart
var liste = [1, 2, 3];
var liste2 = [0, ...liste];
assert(liste2.length == 4);
```

Eğer yayılma operatörünün sağındaki ifade null olabilirse, null bilincinden dolayı bir hata önlemek için null bilincinden haberdar yayılma operatörünü (`...?`) kullanabilirsiniz:

```dart
var liste2 = [0, ...?liste];
assert(liste2.length == 1);
```

Yayılma operatörünü kullanmanın ayrıntıları ve örnekleri için yayılma operatörü önerisine bakın.

#### **Kontrol Akış Operatörleri**&#x20;

Dart, `List`, `Map` ve `Set` literallerinde kullanılmak üzere koleksiyon `if` ve koleksiyon `for`'u sunar. Bu operatörleri kullanarak koşullar (`if`) ve tekrarlamalar (`for`) kullanarak koleksiyonlar oluşturabilirsiniz.

İşte koleksiyon `if`'i kullanarak üç veya dört öğeli bir liste oluşturmanın bir örneği:

```dart
var nav = ['Anasayfa', 'Mobilya', 'Bitkiler', if (promoAktif) 'Çıkış'];
```

Dart aynı zamanda koleksiyon içinde if-case'i de destekler:

```dart
var nav = ['Anasayfa', 'Mobilya', 'Bitkiler', if (giriş case 'Yönetici') 'Envanter'];
```

İşte koleksiyon `for`'u kullanarak bir listedeki öğeleri başka bir liste eklemeden önce listeyi manipüle etmenin bir örneği:

```dart
var tamsayiListesi = [1, 2, 3];
var stringListesi = ['#0', for (var i in tamsayiListesi) '#$i'];
assert(stringListesi[1] == '#1');
```

Daha fazla ayrıntı ve koleksiyon `if` ve `for` kullanma örnekleri için kontrol akış koleksiyonları önerisine bakın.


# Jenerikler (Generics)

Eğer temel dizi tipi olan `List`'in API belgelerine göz atarsanız, tipin aslında `List<E>` olduğunu göreceksiniz. `<...>` gösterimi, `List`'in bir jenerik (veya parametreli) tip olduğunu belirtir - yani, formal tip parametreleri olan bir tiptir. Genellikle, çoğu tip değişkeni tek harfli isimlere sahiptir, örneğin E, T, S, K ve V gibi.

### **Jenerik Türleri Neden Kullanmalıyız?**

Jenerikler genellikle tip güvenliği için gereklidir, ancak kodunuzun çalışmasına izin vermenin ötesinde daha fazla faydası vardır:

* Doğru jenerik tipleri belirlemek, daha iyi oluşturulmuş kodlara neden olur.
* Jenerikleri kullanarak kod tekrarını azaltabilirsiniz.

Eğer bir listenin sadece dizeleri içermesini istiyorsanız, onu `List<String>` olarak bildirebilirsiniz (bu, "dize listesi" olarak okunur). Bu şekilde, bir dize olmayan bir değeri listeye atamanın muhtemelen bir hata olduğunu, sizin ve diğer programcıların araçlarının tespit edebileceği bir şekilde ifade etmiş olursunuz. İşte bir örnek:

```dart
// Statik analiz: hata
var isimler = <String>[];
isimler.addAll(['Seth', 'Kathy', 'Lars']);
isimler.add(42); // Hata
```

Jenerikleri kullanmanın bir başka nedeni de kod tekrarını azaltmaktır. Jenerikler, birçok tür arasında tek bir arayüzü ve uygulamayı paylaşmanıza olanak tanırken hala statik analiz avantajlarından faydalanmanıza olanak tanır. Örneğin, bir nesneyi önbelleğe alma arayüzü oluşturduğunuzu varsayalım:

```dart
abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}
```

Bu arayüzün string özel bir versiyonunu istediğinizi fark ederseniz, başka bir arayüz oluşturabilirsiniz:

```dart
abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}
```

Daha sonra, bu arayüzün bir sayı özel versiyonunu istediğinize karar verirseniz... Fikri anladınız.

Jenerik tipler, tüm bu arayüzleri oluşturmanın zahmetinden sizi kurtarabilir. Bunun yerine, bir tür parametre alan tek bir arayüz oluşturabilirsiniz:

```dart
abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}
```

Bu kodda, T stand-in türdür. Bu, geliştiricinin daha sonra tanımlayacağı bir tür olarak düşünebileceğiniz bir yer tutucudur.

### **Toplama Litaralleri Kullanma**

`List`, `Set` ve `Map` literalleri parametreli olabilir. Parametreli literaller, zaten gördüğünüz literaller gibi, ancak açılış parantezinin önüne `<type>` (listeler ve setler için) veya `<keyType, valueType>` (`Maps`) için eklersiniz. İşte tip belirtilmiş literalleri kullanmanın bir örneği:

```dart
var isimler = <String>['Bayram', 'Hidayet', 'Mustafa', 'Yunus', 'Hakan'];
var benzersizIsimler = <String>{'Bayram', 'Hidayet', 'Mustafa', 'Yunus', 'Hakan'};
var sayfalar = <String, String>{
  'index.html': 'Ana Sayfa',
  'robots.txt': 'Web robotları için ipuçları',
  'insanlar.txt': 'Biz insanlarız, makine değiliz'
};
```

### **Kurucularla (Constructors) Parametreli Türleri Kullanma**

Bir veya daha fazla tür belirtmek istediğinizde bir constructor kullanırken, türleri sınıf adından hemen sonra açıkhız (`<...>`) içine koyun. Örneğin:

```dart
var isimSeti = Set<String>.from(isimler);
```

Aşağıdaki kod, tamsayı anahtarları olan ve `Gorunum` türünde değerlere sahip bir harita oluşturur:

```dart
var gorunumler = Map<int, Gorunum>();
```

### **Jenerik Koleksiyonlar ve İçerdikleri Türler**

Dart jenerik tipleri, tür bilgilerini çalışma zamanında taşıdıkları anlamına gelir. Örneğin, bir koleksiyonun türünü test edebilirsiniz:

```dart
var isimler = <String>[];
isimler.addAll(['Bayram', 'Hidayet', 'Mustafa', 'Yunus', 'Hakan']);
print(isimler is List<String>); // true
```

Not: Buna karşılık, Java'da jenerik tipler silinir, bu da jenerik tür parametrelerinin çalışma zamanında kaldırıldığı anlamına gelir. Java'da bir nesnenin bir `List` olup olmadığını test edebilirsiniz, ancak bir `List<String>` olup olmadığını test edemezsiniz.

### **Parametreli Türü Sınırlama**

Generik bir türü uygularken, belirli bir türün alt türü olan argümanları sağlamak isteyebilirsiniz, böylece argümanın belirli bir türün alt türü olması gerekmelidir. Bu, extends kullanarak yapılabilir.

Bir türün null olmayan bir alt tür olduğundan emin olmak için genellikle `extends` kullanılır (varsayılan olarak Object yerine). Örneğin:

```dart
class Foo<T extends Object> {
  // Foo için T'ye sağlanan herhangi bir tür, null olmayan bir alt tür olmalıdır.
}
```

Extends ile başka türler de kullanabilirsiniz. İşte `TemelSinif`'i genişleterek `TemelSinif`'in üyelerinin T türündeki nesnelerde çağrılmasına izin veren bir örnek:

```dart
class Foo<T extends TemelSinif> {
  // Uygulama buraya geliyor...
  String toString() => "Foo<$T>'nin örneği";
}

class Extender extends TemelSinif {...}
```

`TemelSinif` veya alt türlerini genişletmek için şu şekilde kullanabilirsiniz:

```dart
var temelSinifFoo = Foo<TemelSinif>();
var extenderFoo = Foo<Extender>();
```

Ayrıca hiçbir jenerik argüman belirtmek de mümkündür:

```dart
var foo = Foo();
print(foo); // 'Foo<TemelSinif>' örneği
```

Herhangi bir `TemelSinif` olmayan tür belirtmek bir hataya yol açar:

```dart
// Statik analiz: hata
var foo = Foo<Object>();
```

**Jenerik Metotları Kullanma**

Metodlar ve fonksiyonlar da tür argümanlarına izin verir:

```dart
T ilk<T>(List<T> ts) {
  // İlk işlemleri veya hata kontrolünü yapın, sonra...
  T tmp = ts[0];
  // Ek kontrol veya işlemleri yapın...
  return tmp;
}
```

Buradaki jenerik tür parametresi ilk (`<T>`), tür argümanını `T`'yi birkaç yerde kullanmanıza olanak tanır:

* Fonksiyonun dönüş türünde (`T`).
* Bir argümanın türünde (`List<T>`).
* Yerel bir değişkenin türünde (`T tmp`).

<br>


# Veri Türü Tanımlama (Typedef)

Dart dilinde, bir türü daha kısa bir şekilde ifade etmek için kullanılan tip takma adı, genellikle "typedef" olarak adlandırılır ve `typedef` anahtar kelimesi ile tanımlanır. İşte `IntList` adlı bir tip takma adını tanımlayarak kullanma örneği:

```dart
typedef IntList = List<int>;
IntList il = [1, 2, 3];
```

Bir tip takma adı, tür parametrelerine sahip olabilir:

```dart
typedef ListMapper<X> = Map<X, List<X>>;
Map<String, List<String>> m1 = {}; // Ayrıntılı hali.
ListMapper<String> m2 = {}; // Aynı şey ancak daha kısa ve anlaşılır.
```

Çoğu durumda, işlevler için inline işlev tiplerini kullanmanızı öneririz. Ancak, işlev `typedef`'leri hala kullanışlı olabilir:

```dart
typedef Compare<T> = int Function(T a, T b);

int sort(int a, int b) => a - b;

void main() {
  assert(sort is Compare<int>); // Doğru!
}
```

Bu örnekte, `Compare` adlı bir işlev tip takma adı tanımlanmış ve ardından bir sıralama işlevi bu tipte bir işlev olarak kullanılmıştır. Dart dilindeki tip takma adlarının kullanımı, kodu daha okunabilir hale getirir ve türleri daha kısa bir şekilde ifade etmemize olanak tanır.


# Desenler (Patterns)

Desenler, Dart dilinde ifadeler ve deyimler gibi bir sözdizimi kategorisidir. Bir desen, gerçek değerlere karşı eşleştirilebilecek bir değer kümesinin şeklini temsil eder.

Bu sayfa şunları içerir:

1. Desenlerin ne işe yaradığı.
2. Dart kodunda desenlerin nerede kullanılabileceği.
3. Desenlerin yaygın kullanım durumları.&#x20;

Desen türleri sayfasını inceleyerek farklı desen türleri hakkında bilgi edinebilirsiniz.

## Desenler Ne Yapar?

Desenlerin İşlevi Genel olarak, bir desen, bir değeri eşleştirebilir, bir değeri parçalayabilir veya her ikisini de yapabilir, bağlam ve desenin şekline bağlı olarak.

İlk olarak, desen eşleştirmesi, bir verinin belirli bir şekle sahip olup olmadığını kontrol etmenizi sağlar:

* Belirli bir şekle sahipse.
* Belirli bir sabite eşitse.
* Başka bir şeye eşitse.
* Belirli bir türe sahipse. Daha sonra, desen parçalaması, bir değeri bileşen parçalarına ayırmak için uygun bir deklaratif sözdizimi sağlar. Aynı desen, bu süreçte bazı veya tüm bileşenlere yeni değişkenler bağlamana da izin verebilir.

### Eşleşme

Bir desen her zaman bir değeri test eder ve değerin beklediğiniz şekle sahip olup olmadığını belirler. Başka bir deyişle, değerin deseni eşleşip eşleşmediğini kontrol ediyorsunuz.

Eşleşenin ne oluşturduğu, hangi türde bir desen kullandığınıza bağlıdır. Örneğin, bir sabit deseni, değerin desenin sabitine eşit olup olmadığını kontrol eder:

```dart
switch (number) {
  // Sabit deseni, 1 == number ise eşleşir.
  case 1:
    print('bir');
}
```

Birçok desen, bazen dış ve iç desenler olarak adlandırılan alt desenleri kullanır. Desenler, alt desenlerinde özyineli (rekürsif) olarak eşleşir. Örneğin, herhangi bir koleksiyon tipi deseninin bireysel alanları değişken desenler veya sabit desenler olabilir:

```dart
const a = 'a';
const b = 'b';
switch (obj) {
  // Liste deseni [a, b], obj ilk olarak bir liste ise eşleşir
  // ardından alanları 'a' ve 'b' sabit desenlerle eşleşirse.
  case [a, b]:
    print('$a, $b');
}
```

Eşleşen bir değerin bazı bölümlerini görmezden gelmek için bir joker deseni kullanabilirsiniz. Liste desenleri durumunda bir dinlenme öğesi kullanabilirsiniz.

### Parçalama

Bir nesne ve desen eşleştiğinde, desen ardından nesnenin verilerine erişebilir ve bunları parçalayabilir. Başka bir deyişle, desen nesneyi parçalar:

```dart
var numList = [1, 2, 3];
// Liste deseni [a, b, c], numList'ten üç elemanı parçalar...
var [a, b, c] = numList;
// ...ve bunları yeni değişkenlere atar.
print(a + b + c);
```

Herhangi bir türdeki deseni bir parçalama desenine iç içe yerleştirebilirsiniz. Örneğin, bu durum deseni, ilk öğesi 'a' veya 'b' olan iki öğeli bir listeyi eşleşip parçalar:

```dart
switch (list) {
  case ['a' || 'b', var c]:
    print(c);
}
```

## Desenlerin Kullanıldığı Yerler&#x20;

Desenleri Dart dilinde birkaç yerde kullanabilirsiniz:

1. Yerel değişken bildirimleri ve atamaları
2. for ve for-in döngüleri
3. if-case ve switch-case
4. Toplama literallerinde kontrol akışı Bu bölüm, desenlerle eşleştirme ve parçalama için ortak kullanım durumlarını açıklar.

### Değişken Bildirimi

Deseni, Dart'ın yerel değişken bildirimi izin verdiği herhangi bir yerde kullanabilirsiniz. Desen, bildirimin sağında bulunan değere karşı eşleşir. Eşleştiğinde değeri parçalar ve yeni yerel değişkenlere bağlar:

```dart
// Yeni değişkenler a, b ve c'yi bildirir.
var (a, [b, c]) = ('str', [1, 2]);
```

Desen değişken bildirimi, var veya final ile başlamalıdır ve bir desenle devam etmelidir.

### Değişken Atama

Değişken atama deseni, atama operatörünün sol tarafına düşer. İlk olarak eşleştirilen nesneyi parçalar. Ardından değerleri mevcut değişkenlere atar, yeni bir değişken oluşturmak yerine:

İki değişkenin değerlerini üçüncü geçici bir değişken oluşturmadan değiştirmek için değişken atama deseni kullanın:

```dart
var (a, b) = ('sol', 'sağ');
(b, a) = (a, b); // Değerleri değiştir.
print('$a $b'); // "sağ sol" yazdırır.
```

### Switch Deyimleri ve İfadeleri&#x20;

Her case ifadesi bir deseni içerir. Bu, switch ifadeleri ve deyimleri, aynı zamanda if-case ifadeleri için de geçerlidir. Bir case için herhangi bir deseni kullanabilirsiniz.

Case desenleri reddedilebilir. Bunlar, anahtar üzerinde eşleşen nesneyi parçalayabilir ve değişmezse devam edebilir. Bir desenin case içinde parçaladığı değerler, yalnızca o case'in gövde kısmı içinde geçerlidir.

```dart
switch (obj) {
  // 1 == obj durumunda eşleşirse.
  case 1:
    print('bir');

  // obj değeri 'ilk' ve 'son' sabit değerleri arasında ise eşleşir.
  case >= ilk && <= son:
    print('aralıkta');

  // obj, iki alanı olan bir kayıt ise,
  // 'a' ve 'b' alanlarını 'a' ve 'b' değişkenlerine atar.
  case (var a, var b):
    print('a = $a, b = $b');

  default:
}
```

Switch ifadelerinde veya deyimlerinde birden çok case'in aynı gövdeyi paylaşmasını sağlamak için kullanışlıdır:

```dart
var birincilMi = switch (renk) {
  Renkler.kirmizi || Color.sari || Color.mavi => true,
  _ => false
};
```

Switch ifadelerinde birden çok case aynı gövdeyi paylaşabilir, ancak mantık-veya desenleri kullanmadan da yapılabilir, ancak mantık-veya desenleri, birden çok case'in bir koruma paylaşmasına izin vermek için hala benzersiz bir şekilde kullanışlıdır:

```dart
switch (sekil) {
  case Kare(boyut: var b) || Yuvarlak(boyut: var b) when b > 0:
    print('Boş olmayan simetrik şekil');
}
```

Koruma ifadeleri, bir case'in bir parçası olarak herhangi bir koşulu değerlendirir ve koşul yanlışsa switch'ten çıkmaz (bir if ifadesinde kullanmak, koşul yanlışsa ifadenin gövdesini atlamak gibi).

```dart
switch (eslemek) {
  case (int a, int b):
    if (a > b) print('İlk öğe daha büyük');
  // Eğer yanlışsa hiçbir şey yazdırmaz ve switch'ten çıkar.
  case (int a, int b) when a > b:
    // Eğer yanlışsa hiçbir şey yazdırmaz, ancak bir sonraki case'e geçer.
    print('İlk öğe daha büyük');
  case (int a, int b):
    print('İlk öğe daha büyük değil');
}
```

### For ve For-in Döngüleri&#x20;

For ve for-in döngülerinde desenleri kullanarak bir koleksiyon içinde dolaşabilir ve değerleri parçalayabilirsiniz.

Bu örnek, bir \<Map>.entries çağrısının döndürdüğü MapEntry nesnelerini parçalamak için for-in döngüsünde nesne parçalama kullanır:

```dart
Map<String, int> gecmis = {
  'a': 23,
  'b': 100,
};

for (var MapEntry(key: key, value: sayi) in gecmis.entries) {
  print('$key, $sayi kez meydana geldi');
}
```

Nesne deseni, `gecmis.entries`'in adlandırılmış türüne sahip olduğunu kontrol eder ve ardından adlandırılmış alan desenleri `key` ve `value` içine girer. Her tekrarda `MapEntry` üzerinde `key` ve `value` getter'ları çağırır ve sonuçları sırasıyla `key` ve `sayi` adlı yerel değişkenlere bağlar.

Getter çağrısının sonucunu aynı ada sahip bir değişkene bağlamak yaygın bir kullanım durumudur, bu nedenle nesne desenleri, değişken deseninden `key: key` gibi gereksiz bilgileri çıkarmak için de getter adını çıkarabilir. Bu, değişken desenini `key: key` yerine `:key` olarak basitleştirmenizi sağlar:

```dart
for (var MapEntry(:key, value: sayi) in gecmis.entries) {
  print('$key, $sayi kez meydana geldi');
}
```

## Desenlerin Kullanım Durumları

Önceki bölüm, desenlerin diğer Dart kod yapılarına nasıl uyduğunu açıklar. İki değişkenin değerlerini değiştirmek veya bir harita içindeki anahtar-değer çiftlerini parçalamak gibi ilginç kullanım durumlarına örnekler gördünüz. Bu bölüm, desenleri neden ve ne zaman kullanmak isteyebileceğinizi, hangi problemleri çözdüğünü ve hangi deyimleri en iyi şekilde tamamladığınıınızı açıklar.

### Çoklu Dönüşleri Parçalama

Kayıtlar, tek bir işlev çağrısından birden çok değeri bir araya getirme ve döndürme yeteneği sunar. Desenler, bu işlevin değerlerini doğrudan bir işlev çağrısının içindeki yerel değişkenlere parçalama yeteneği ekler.

Tek tek her bir kayıt alanı için yeni yerel değişkenler bildirmek yerine, bir işlevin döndürdüğü bir kaydın alanlarını yerel değişkenlere bağlamak için desen bildirimi veya atama deseni kullanabilirsiniz:

```dart
var bilgi = kisiBilgi(json);
var isim = bilgi.$1;
var yas = bilgi.$2;
```

Bir işlevin döndürdüğü bir kaydın alanlarını, bir değişken bildirimi veya atama deseni ve bir kayıt deseni kullanarak yerel değişkenlere bağlamak için:

```dart
var (isim, yas) = kisiBilgi(json);
```

### Sınıf Örneklerini Parçalama

Nesne desenleri, adlandırılmış nesne türleriyle eşleşir ve nesnenin sınıfının zaten açıkladığı getter'ları kullanarak verilerini parçalamanıza olanak tanır.

Bir sınıf örneğini parçalamak için, adlandırılmış türü ve parçalanacak özellikleri parantez içinde kullanabilirsiniz:

```dart
final Ogrenci benimOgrencim = Ogrenci(ad: 'Bulut', yas: 16);
var Ogrenci(isim, yas) = benimOgrencim;
print('İsim: $isim, Yaşı: $yas');
```

### Cebirsel Veri Tipleri

Nesne parçalama ve switch case ifadeleri, cebirsel bir veri tipi tarzında kod yazmaya olanak tanır. Bu yöntemi kullanmanız gereken durumlar şunlardır:

* İlgili türler ailesine sahipsiniz.
* Her bir tür için belirli davranışlara ihtiyaç duyan bir işleminiz var.
* Bu davranışı tüm farklı tür tanımlarına yaymak yerine, bu davranışı bir yerde gruplamak istiyorsunuz.

Her tür için işlemi her seferinde bir örnek yöntemi olarak uygulamak yerine, işlemin farklı varyasyonlarını tek bir fonksiyonda tutun ve bu fonksiyon içinde tür alt tiplerine geçiş yapın:

```dart
import 'dart:math' as math;

sealed class Sekil {}

class Kare implements Sekil {
  final double kenarUzunlugu;
  Kare(this.kenarUzunlugu);
}

class Daire implements Sekil {
  final double yariCap;
  Daire(this.yariCap);
}

double alanHesapla(Sekil sekil) => switch (sekil) {
  Kare(kenarUzunlugu: var k) => k * k,
  Daire(yariCap: var r) => math.pi * r * r
};
```

### Gelen JSON Verisinin Doğrulanması

Harita ve liste desenleri, JSON verilerindeki anahtar-değer çiftlerini parçalamak için oldukça kullanışlıdır:

```dart
var json = {
  'user': ['Bayram', 24]
};
var {'user': [isim, yas]} = json;
```

Ancak genellikle veri dış bir kaynaktan, örneğin bir ağ üzerinden gelir. Bu durumda, veriyi önce doğrulamanız gerekir.

Desenler olmadan doğrulama çok uzun olabilir:

```dart
if (json is Map<String, Object?> &&
    json.length == 1 &&
    json.containsKey('kullanici')) {
  var kullanici = json['kullanici'];
  if (kullanici is List<Object> &&
      kullanici.length == 2 &&
      kullanici[0] is String &&
      kullanici[1] is int) {
    var isim = kullanici[0] as String;
    var yas = kullanici[1] as int;
    print('Kullanıcı $isim $yas yaşında.');
  }
}
```

Tek bir durum deseni aynı doğrulamayı sağlayabilir. Tek durumlar genellikle if-case ifadeleri olarak en iyi çalışır. Desenler, JSON'u doğrulamanın daha deklaratif ve çok daha az uzun bir yolunu sağlar:

```dart
if (json case {'kullanici': [String isim, int yas]}) {
  print('Kullanıcı $isim $yas yaşında.');
}
```

Bu durum deseni aynı anda şunları doğrular:

* `json` bir Maps, çünkü devam edebilmek için önce dış harita deseniyle eşleşmelidir.
  * Ve, çünkü bir harita ise, aynı zamanda `json`'ın null olmadığını da onaylar.
* `json` bir `kullanici` anahtarını içerir.
* `kullanici` anahtarı iki değeri içeren bir liste ile eşleşir.
* Liste değerlerinin türleri `String` ve `int`'tir.
* Yeni yerel değişkenleri saklamak için `isim` ve `yas`.

<br>


# Döngüler (Loops)

Bu sayfa, Dart kodunuzun akışını kontrol etmek için döngüler ve destekleyici ifadeler kullanarak nasıl kullanabileceğinizi göstermektedir:

* for döngüleri
* while ve do while döngüleri
* break ve continue

Dart'ta kontrol akışını şu şekillerde de manipüle edebilirsiniz:

* if ve switch gibi şartlarla dallanma
* try, catch ve throw gibi istisnalarla For Döngüleri

## For Döngüsü (For Loops)

Standart for döngüsü ile ardışık işlemler yapabilirsiniz. Örneğin:

```dart
var mesaj = StringBuffer('Dart eğlencelidir');
for (var i = 0; i < 5; i++) {
  mesaj.write('!');
}
```

Dart'ın `for` döngüsündeki kapanışlar, indeksin değerini yakalar. Bu, JavaScript'te sıkça rastlanan bir hata durumunu engeller. Örneğin:

```dart
var geriDonusler = [];
for (var i = 0; i < 2; i++) {
  geriDonusler.add(() => print(i));
}

for (final c in geriDonusler) {
  c();
}
```

Çıktı olarak beklenen şekilde 0 ve ardından 1 yazdırılır. Bununla karşılaştırıldığında, JavaScript örneği 2 ve ardından 2 yazdırırdı.

Bazen bir `Iterable` türü üzerinde ardışık işlemler yaparken mevcut iterasyon sayısını bilmemeniz gerekebilir, örneğin `List` veya `Set` gibi. Bu durumda, daha temiz bir kod için `for-in` döngüsünü kullanın:

```dart
for (final aday in adaylar) {
  aday.mulakatYap();
}
```

Iterable'dan elde edilen değerleri işlemek için, `for-in` döngüsünde bir deseni de kullanabilirsiniz:

```dart
for (final Aday(isim, deneyimYil) in adaylar) {
  print('$isim, $deneyimYil deneyime sahip.');
}
```

Iterable sınıflar, başka bir seçenek olarak `forEach()` yöntemine sahiptir:

```dart
var koleksiyon = [1, 2, 3];
koleksiyon.forEach(print); // 1 2 3
```

## While ve Do-While

`While` döngüsü, döngüden önce koşulu değerlendirir:

```dart
while (!tamamlandiMi()) {
  birSeyYap();
}
```

`Do-while` döngüsü, döngüden sonra koşulu değerlendirir:

```dart
do {
  satirYaz();
} while (!sayfaSonundaMi());
```

## Break ve Continue

Döngüyü durdurmak için `break` kullanın:

```dart
while (true) {
  if (kapatmaIstegi()) break;
  gelenIstekleriIsle();
}
```

Bir sonraki döngü iterasyonuna atlamak için `continue` kullanın:

```dart
for (int i = 0; i < adaylar.length; i++) {
  var aday = adaylar[i];
  if (aday.deneyimYil < 5) {
    continue;
  }
  aday.mulakatYap();
}
```

Liste veya set gibi bir `Iterable` kullanıyorsanız, önceki örneği nasıl yazacağınız farklılık gösterebilir:

```dart
adaylar
    .where((c) => c.deneyimYil >= 5)
    .forEach((c) => c.mulakatYap());
```


# Dallar (Branches)

Bu sayfa, Dart kodunuzun akışını şu şekillerde kontrol edebileceğinizi gösterir:

* if ifadeleri ve öğeleri
* if-case ifadeleri ve öğeleri
* switch ifadeleri ve ifadeleri

Ayrıca Dart'ta kontrol akışını şu şekillerde manipüle edebilirsiniz:

* [Döngüler](/kontrol-akisi-control-flow/donguler-loops), örneğin for ve while
* İstisnalar, örneğin try, catch ve throw

## If İfadeleri

Dart, isteğe bağlı else şartlarını içeren if ifadelerini destekler. Parantez içindeki koşul, bir boolean değerine değerlendirilebilen bir ifade olmalıdır:

```dart
if (yagmurYagiyorMu()) {
  senMantariGetir();
} else if (karYagiyorMu()) {
  ceketiGiy();
} else {
  aracinUstunuAc();
}
```

if ifadesini bir ifade bağlamında nasıl kullanacağınızı öğrenmek için Koşullu İfadeler belgesine göz atabilirsiniz.

## if-case İfadeleri

Dart if ifadeleri, desenlere sahip case ifadelerini destekler:

```dart
if (cift case [int x, int y]) return Nokta(x, y);
```

Eğer desen değeriyle eşleşirse, o zaman desenin tanımladığı değişkenlerle birlikte şube çalışır.

Önceki örnekte, liste deseni \[int x, int y], cift değeri ile eşleşir, bu nedenle desenin tanımladığı x ve y değişkenleriyle birlikte return Nokta(x, y) şubesi çalışır.

Aksi takdirde, kontrol akışı else şubesine geçer:

```dart
if (cift case [int x, int y]) {
  print('Koordinat dizisiydi $x,$y');
} else {
  throw FormatException('Geçersiz koordinatlar.');
}
```

if-case ifadesi, tek bir desene karşı eşleşme ve yapıyı parçalama imkanı sağlar. Birden fazla desene karşı bir değeri test etmek için switch kullanabilirsiniz.

**Versiyon Notu:** if ifadelerinde case ifadeleri, en az 3.0 dil sürümünü gerektirir.

## Switch

Switch ifadesi, bir değer ifadesini bir dizi durumla karşılaştırır. Her durum ifadesi, değerin eşleşmesi için bir desen içerir. Bir durumun deseni eşleştiğinde, durum gövdesi çalışır. Dolu durum ifadeleri, tamamlandıktan sonra anahtarı sona erdirir ve break ifadesi gerektirmez. Boş durum ifadeleri eğer kullanılmak istenirse, gövde içinde bir break ifadesi kullanılmalıdır.

Belirli bir durumun eşleşmediği durumda kodun çalışmasını sağlamak için default veya \_ kullanabilirsiniz:

```dart
var komut = 'AC';
switch (komut) {
  case 'KAPALI':
    kapaliDurumuYurut();
  case 'BEKLEMEDE':
    beklemeDurumuYurut();
  case 'ONAYLANDI':
    onaylandiDurumuYurut();
  case 'REDDEDILDI':
    reddedildiDurumuYurut();
  case 'AC':
    acikDurumuYurut();
  default:
    bilinmeyenDurumuYurut();
}
```

Boş durumlar, bir sonraki duruma geçer, böylece durumlar bir gövdeyi paylaşabilir. Boş bir durumun geçmemesi için gövde içinde bir break ifadesi kullanılabilir. Non-sequential geçişler için continue ifadesi ve bir etiket kullanılabilir:

```dart
switch (komut) {
  case 'AC':
    acikDurumuYurut();
    continue yeniDurum; // yeniDurum etiketi üzerinde devam etmeye devam eder.

  case 'REDDEDILDI': // Boş durum geçer.
  case 'KAPALI':
    kapaliDurumuYurut(); // REDDEDILDI ve KAPALI için çalışır.

  yeniDurum:
  case 'BEKLEMEDE':
    simdiKapaliDurumuYurut(); // AC ve BEKLEMEDE için çalışır.
}
```

Durumların bir gövdeyi veya bir koruyucu kullanmasına izin vermek için mantıksal veya desenlerini kullanabilirsiniz. Desenler ve durum ifadeleri hakkında daha fazla bilgi için Switch İfadeleri ve İfadeleri belgesine göz atabilirsiniz.

**Versiyon Notu:** Switch ifadeleri, en az 3.0 dil sürümünü gerektirir.

### Switch İfadeleri

Switch ifadesi, eşleşen durumun ifade gövdesine dayalı olarak bir değer üretir. Bir switch ifadesini, Dart'ın ifadeleri izin verilen her yerde kullanabilirsiniz, ancak bir ifade ifadesinin başlangıcında kullanmak istiyorsanız, bir switch ifadesi kullanmalısınız.

Örneğin:

```dart
var x = switch (y) { ... };

print(switch (x) { ... });

return switch (x) { ... };
```

Bir switch ifadesini, aşağıdaki gibi bir switch ifadesine yeniden yazabilirsiniz:

```dart
// Slash, star, comma, semicolon vb. sabit değişkenler...
switch (charCode) {
  case slash || star || plus || minus: // Mantıksal veya deseni
    token = operator(charCode);
  case comma || semicolon: // Mantıksal veya deseni
    token = punctuation(charCode);
  case >= digit0 && <= digit9: // İlişkisel ve mantıksal ve desenleri
    token = number();
  default:
    throw FormatException('Geçersiz');
}
```

Şu şekilde bir ifadeye dönüştürebilirsiniz:

```dart
token = switch (charCode) {
  slash || star || plus || minus => operator(charCode),
  comma || semicolon => punctuation(charCode),
  >= digit0 && <= digit9 => number(),
  _ => throw FormatException('Geçersiz')
};
```

Switch ifadesinin sözdizimi, switch ifadesi sözdiziminden farklıdır:

* Durumlar case anahtar kelimesi ile başlamaz.
* Bir durum gövdesi bir dizi ifade yerine tek bir ifade olmalıdır.
* Her durumun bir gövdesi olmalıdır; boş durumlar için dolaylı geçiş yoktur.
* Durum desenleri, : yerine => ile gövdelerinden ayrılır.
* Durumlar , ile ayrılır (isteğe bağlı bir sondan virgül izin verilir).
* Varsayılan durumlar sadece \_, default ve \_ ikisi yerine yalnızca kullanılabilir.

**Versiyon Notu:** Switch ifadeleri, en az 3.0 dil sürümünü gerektirir.

### Tamamlanmışlık Kontrolü (Exhaustiveness checking)

Tamamlanmışlık kontrolü, bir değerin bir switch'e girebilecek ancak hiçbir durumun eşleşmemesi durumunda bir derleme hatası bildirmesi özelliğidir.

```dart
// Nullabilite olasılığını eşleştirmeyen bool türünde tamamlanmamış switch:
switch (nullDegerliBool) {
  case true:
    print('evet');
  case false:
    print('hayır');
}
```

Varsayılan bir durum (default veya \_) bir switch üzerinden geçebilecek tüm değerleri kapsar. Bu, bir switch üzerinde tamamlanmışlık kontrolünü sağlar.

Enum'lar ve mühürlü tipler, özellikle switch'ler için kullanışlıdır çünkü varsayılan durum olmadan bile, mümkün olan değerleri bilir ve tamamen sayılabilir. Bir sınıfın alt türleri üzerinde switch yaparken tamamlanmışlık kontrolünü etkinleştirmek için bir sınıfın üzerine mühür ekleyin:

```dart
sealed class Sekil {}

class Kare implements Sekil {
  final double kenarUzunlugu;
  Kare(this.kenarUzunlugu);
}

class Daire implements Sekil {
  final double yaricap;
  Daire(this.yaricap);
}

double hesaplaAlan(Sekil sekil) => switch (sekil) {
      Kare(kenarUzunlugu: var k) => k * k,
      Daire(yaricap: var r) => math.pi * r * r
    };
```

Eğer biri Sekil sınıfına yeni bir alt sınıf eklerse, bu switch ifadesi eksik olurdu ve tamamlanmamışlık kontrolü eksik alt türü size bildirir. Bu, Dart'ı biraz fonksiyonel algebratik veri tipi tarzında kullanmanıza olanak tanır.

## Muhafız Kümeleri (Guard clause)

Bir durum ifadesinden sonra isteğe bağlı bir muhafız kümesi eklemek için when anahtar kelimesini kullanın. Bir muhafız kümesi, ifade eşleştiğinde takip eden bir şartı değerlendirir. Bir muhafız şartı false olarak değerlendirildiğinde, yürütme tüm durumdan çıkarak bir sonraki duruma geçer:

```dart
// Switch ifadesi:
var deger = switch (birSey) {
  case birDesen when bir || boolean || ifade,
  //            ^^^^^^^^^^^^^^^^^^^ Muhafız kümesi.
  govde;
}

// Switch ifadesi:
var deger = switch (birSey) {
  birDesen when bir || boolean || ifade => govde,
  //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Muhafız kümesi.
}

// if-case ifadesi:
if (birSey case birDesen when bir || boolean || ifade) {
  //                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Muhafız kümesi.
  govde;
}
```

Muhafızlar, eşleşen durumdan sonraki herhangi bir kodun çalışmasını ek bir kısıtlama eklemek için kullanılır. Muhafız kümesi false olarak değerlendirildiğinde, yürütme tüm durumdan çıkarak bir sonraki duruma geçer.

Dart dilinde kontrol akışını yönetmek için if, switch ifadeleri ve döngüler kullanılır. Bu yapılar, kodunuzu esnek ve okunabilir kılmak için kullanılabilir. İster basit şartlar, ister karmaşık desenlerle eşleştirmeler yapmak isteseniz, Dart'ın kontrol akışı yapıları size ihtiyacınız olan araçları sunar.

<br>


# Hata Yönetimi (Error Handling)

## Exceptions

Dart dilinde, kodunuz istisnaları (exceptions) fırlatabilir ve bunları yakalayabilir. İstisnalar, beklenmeyen bir durumu belirten hatalardır. Eğer istisna yakalanmazsa, istisnayı atan izolat (isolate) askıya alınır ve genellikle izolat ve program sonlandırılır.

Java'nın aksine, Dart'ta tüm istisnalar kontrolsüz istisnalar (unchecked exceptions) olarak kabul edilir. Yani metodlar hangi istisnaları fırlatabileceğini belirtmez ve herhangi bir istisna yakalamak zorunlu değildir.

Dart, Exception ve Error tiplerini sağlar, ayrıca birçok önceden tanımlanmış alt tipler bulunmaktadır. Elbette kendi istisna tiplerinizi de tanımlayabilirsiniz. Ancak, Dart programları Exception ve Error objeleri olmak zorunda olmayan herhangi bir non-null objeyi istisna olarak fırlatabilir.

### **Throw (Fırlatma)**

İstisna fırlatmanın bir örneği aşağıda verilmiştir:

```dart
throw FormatException('En az 1 bölüm bekleniyordu');
```

Ayrıca, isteğe bağlı olarak herhangi bir objeyi de fırlatabilirsiniz:

```dart
throw 'Lamalar tükendi!';
```

> **Not**
>
> Üretim kalitesindeki kod genellikle Error veya Exception'ı uygulayan tipleri fırlatır.

Çünkü istisna fırlatmak bir ifade olduğundan dolayı, => ifadelerinde ve ifadeye izin veren diğer yerlerde istisna fırlatabilirsiniz:

```dart
void mesafeHesapla(Point diger) => throw UnimplementedError();
```

### **Catch (Yakalamak)**

İstisnayı yakalamak, istisnanın yayılmasını durdurur (eğer istisnayı tekrar fırlatmazsanız). İstisnayı yakalamak, onunla ilgilenme şansı verir:

```dart
try {
  breedMoreLlamas();
} on OutOfLlamasException {
  buyMoreLlamas();
}
```

Birden fazla türde istisna fırlatabilecek kodu ele almak için birden çok catch bloğu belirtebilirsiniz. Atılan objenin türüne eşleşen ilk catch bloğu istisnayı ele alır. Eğer catch bloğu bir tür belirtmezse, bu blok herhangi bir türde fırlatılan objeyi ele alabilir:

```dart
try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // Belirli bir istisna
  buyMoreLlamas();
} on Exception catch (e) {
  // Diğer tüm istisnalar
  print('Bilinmeyen istisna: $e');
} catch (e) {
  // Belirtilmemiş tür, tüm istisnaları ele alır
  print('Gerçekten bilinmeyen bir şey: $e');
```

Yukarıdaki kod gösterildiği gibi hem on, hem de catch veya ikisini birden kullanabilirsiniz. on, istisna türünü belirtmeniz gerektiğinde kullanılır. catch, istisna işleyicinizin istisna nesnesine ihtiyaç duyduğu durumlarda kullanılır.

catch() metoduna bir veya iki parametre belirleyebilirsiniz. İlk parametre fırlatılan istisnadır, ikinci parametre ise stack trace'i temsil eden bir StackTrace objesidir:

```dart
try {
  // ···
} on Exception catch (e) {
  print('İstisna detayları:\n $e');
} catch (e, s) {
  print('İstisna detayları:\n $e');
  print('Stack trace:\n $s');
}
```

Bir istisnayı kısmen ele alıp, onun yayılmasına izin vermek için rethrow anahtar kelimesini kullanabilirsiniz.

```dart
void hataYarat() {
  try {
    dynamic foo = true;
    print(foo++); // Çalışma zamanı hatası
  } catch (e) {
    print('hataYarat() kısmen ele aldı ${e.runtimeType}.');
    rethrow; // Çağrıyı yapanın istisnayı görmesine izin ver.
  }
}

void main() {
  try {
    hataYarat();
  } catch (e) {
    print('main() işlemi bitti ${e.runtimeType}.');
  }
}
```

### **Finally (En Sonunda)**

Herhangi bir istisna fırlatılıp fırlatılmamasına bakılmaksızın belirli bir kodun çalışmasını sağlamak için finally bloğunu kullanabilirsiniz. Eğer hiçbir catch bloğu istisnayı eşleştirmezse, finally bloğu çalıştıktan sonra istisna yayılır:

```dart
try {
  lamaUret(); // Örnek bir fonksiyon adı, gerçek bir kodunuzla değiştirin.
} finally {
  // Her zaman temizle, eğer bir istisna fırlatılmışsa bile.
  lamaAhirlariniTemizle();
}
```

finally bloğu, eşleşen catch bloklarından sonra çalışır:

```dart
try {
  lamaUret(); // Örnek bir fonksiyon adı, gerçek bir kodunuzla değiştirin.
} catch (e) {
  print('Hata: $e'); // İstisnayı ilk önce ele al.
} finally {
  lamaAhirlariniTemizle(); // Sonra temizle.
}
```

Daha fazla bilgi için core library exception dokümantasyonuna göz atabilirsiniz.

## **Assert** (İddia)

Geliştirme aşamasında, bir boolean koşulu false ise normal yürütmeyi bozmak için assert ifadesini kullanabilirsiniz:

```dart
// Değişkenin null olmadığından emin olun.
assert(metin != null);

// Değerin 100'den küçük olduğundan emin olun.
assert(sayi < 100);

// Bu bir https URL'si olduğundan emin olun.
assert(url.startsWith('https'));
```

Bir assert ifadesine bir mesaj eklemek için, assert'e ikinci argüman olarak bir dize ekleyebilirsiniz (isteğe bağlı olarak sondaki virgül ile):

```dart
assert(url.startsWith('https'),
    'URL ($url) "https" ile başlamalı.');
```

assert'in ilk argümanı, bir boolean değere çözülebilen herhangi bir ifade olabilir. Eğer ifadenin değeri true ise, assert başarılı olur ve yürütme devam eder. Eğer false ise, assert başarısız olur ve bir istisna (AssertionError) fırlatılır.

Assert ifadeleri ne zaman çalışır? Bu, kullandığınız araçlara ve çerçevelere bağlıdır:

* Flutter, hata ayıklama modunda assert'leri etkinleştirir.
* Webdev serve gibi yalnızca geliştirme araçları genellikle varsayılan olarak assert'leri etkinleştirir.
* Bazı araçlar, dart run ve dart compile js gibi, assert'leri --enable-asserts komut satırı bayrağı ile destekler.
* Üretim kodunda, assert'ler yok sayılır ve assert'in argümanları değerlendirilmez.

<br>


# Sınıflar (Classes)

Dart, sınıflar ve mixin tabanlı kalıtım kullanılarak nesne tabanlı bir dil olarak tasarlanmıştır. Her nesne, bir sınıf örneğidir ve `Null` dışındaki tüm sınıflar `Object` sınıfından türemiştir. Mixin tabanlı kalıtım, her sınıfın (`Object` sınıfı hariç) tam olarak bir üst sınıfa sahip olduğu anlamına gelir; ancak sınıfın içeriği birden fazla sınıf hiyerarşisinde yeniden kullanılabilir. Extension metodları, bir sınıfa fonksiyonellik eklemenin bir yoludur ve sınıfı değiştirmeden veya alt sınıf oluşturmadan bu işlevselliği sağlar. Sınıf modifikatörleri, kütüphanelerin bir sınıfı alt tür olarak nasıl kullanabileceğini kontrol etmenizi sağlar.

## Sınıf Üyelerini Kullanma

Nesneler, fonksiyonlar ve verilerden oluşan üyelere sahiptir (sırasıyla metotlar ve örnek değişkenler). Bir metodu çağırdığınızda, bunu bir nesne üzerinde çağırırsınız: metot, bu nesnenin fonksiyonlarına ve verilerine erişime sahiptir.

Bir örnek değişkeni veya metodu belirtmek için bir nokta (`.`) kullanılır:

```dart
var p = Nokta(2, 2);

// Y'nin değerini al.
assert(p.y == 2);

// p üzerinde mesafeHesapla() metodunu çağır.
double mesafe = p.mesafeHesapla(Nokta(4, 4));
```

Nesnenin en sol operandı null olduğunda bir istisna oluşmaması için `.` yerine `?.` kullanılır:

```dart
// Eğer p null değilse, bir değişkeni onun y değerine eşitle.
var a = p?.y;
```

## Yapıcıları (Constructors) Kullanma

Bir nesne oluşturmak için bir yapıcı kullanabilirsiniz. Yapıcı isimleri `SinifAdi` veya `SinifAdi.tanimlayici` olabilir. Örneğin, aşağıdaki kod, `Nokta()` ve `Nokta.fromJson()` yapıcılarını kullanarak Nokta nesneleri oluşturur:

```dart
var p1 = Nokta(2, 2);
var p2 = Nokta.fromJson({'x': 1, 'y': 2});
```

Aynı etkiye sahip olan aşağıdaki kod, yapıcı adından önce opsiyonel `new` anahtar kelimesini kullanır:

```dart
var p1 = new Nokta(2, 2);
var p2 = new Nokta.fromJson({'x': 1, 'y': 2});
```

Bazı sınıflar, sabit yapıcılar sağlar. Sabit bir yapıcı kullanarak derleme zamanında bir sabit oluşturmak için, `const` anahtar kelimesini yapıcı adından önce ekleyin:

```dart
var p = const SabitNokta(2, 2);
```

İki adet aynı sabit yapıcı oluşturmak, tek bir, standart örneği sonuçlandırır:

```dart
var a = const SabitNokta(1, 1);
var b = const SabitNokta(1, 1);

assert(identical(a, b)); // Aynı örnektir!
```

Bir sabit bağlam içinde, bir yapıcı veya literalin `const` önündeki tüm kullanımları atlayabilirsiniz. Örneğin, bu kodu inceleyin, bir const haritası oluşturan:

```dart
// Burada çok sayıda const anahtarı var.
const noktaVeCizgi = const {
  'nokta': const [const SabitNokta(0, 0)],
  'cizgi': const [const SabitNokta(1, 10), const SabitNokta(-2, 11)],
};
```

Tüm `const` anahtarını ilk kullanım haricinde atlayabilirsiniz:

```dart
// Sadece bir const var, bu sabit bağı oluşturur.
const noktaVeCizgi = {
  'nokta': [SabitNokta(0, 0)],
  'cizgi': [SabitNokta(1, 10), SabitNokta(-2, 11)],
};
```

Bir sabit yapıcı, sabit bir bağlam dışında ve `const` olmadan çağrıldığında, **sabit olmayan bir nesne** oluşturur:

```dart
var a = const SabitNokta(1, 1); // Sabit oluşturur
var b = SabitNokta(1, 1); // Sabit bir nesne OLUŞTURMAZ

assert(!identical(a, b)); // Aynı örnek değil!
```

## Bir Nesnenin Türünü Almak (Object's Type)

Bir nesnenin çalışma zamanındaki türünü almak için, `Object` özelliği olan `runtimeType` kullanabilirsiniz, bu da bir `Type` nesnesi döndürür.

```dart
print('a nesnesinin türü ${a.runtimeType}');
```

> **Uyarı**&#x20;
>
> Bir nesnenin türünü test etmek için runtimeType yerine bir tür test operatörü kullanın. Üretim ortamlarında, test nesnesi Type, test nesne.runtimeType == Type'den daha kararlıdır.

Buraya kadar, sınıfları nasıl kullanacağınızı gördünüz. Bu bölümün geri kalanı, sınıfları nasıl uygulayacağınızı gösterir.

## Örnek Değişkenler (Instance Variables)

İşte örnek değişkenlerin nasıl bildirileceği:

```dart
class Nokta {
  double? x; // İlk başta null olan x adlı örnek değişkenini bildir.
  double? y; // İlk başta null olan y adlı örnek değişkenini bildir.
  double z = 0; // İlk başta 0 olan z adlı örnek değişkenini bildir.
}
```

Nullable bir türle bildirilen başlatılmamış bir örnek değişkeninin değeri `null`'dır. Başlatılmamış, nullable olmayan örnek değişkenleri, örneğin oluşturulduğunda, yapıcı ve onun başlatıcı listesi çalışmadan önce değerini almalıdır.

Tüm örnek değişkenleri, gizli bir get metodu oluşturur. Nullable olmayan örnek değişkenleri ve başlatıcı listesi olmayan `late final` örnek değişkenleri, aynı zamanda gizli bir set metodu oluşturur. Ayrıntılar için getters ve setters'a bakın.

```dart
class Nokta {
  double? x; // İlk başta null olan x adlı örnek değişkenini bildir.
  double? y; // İlk başta null olan y adlı örnek değişkenini bildir.
}

void main() {
  var nokta = Nokta();
  nokta.x = 4; // x için setter metodunu kullan.
  assert(nokta.x == 4); // x için getter metodunu kullan.
  assert(nokta.y == null); // Değerler varsayılan olarak null'dur.
}
```

Bir non-late (late olmayan) örnek değişkenini bildirirken, değeri oluşturulan örnek oluşturulduğu anda, yani kurucu ve başlatıcı listesi çalışmadan önce belirlenir. Bu nedenle, bir non-late örnek değişkeninin başlatma ifadesi (`=`'den sonraki) `this`'e erişemez.

```dart
double baslangicX = 1.5;

class Nokta {
  // Tamam, `this`'e bağlı olmayan deklarasyonlara erişebilir:
  double? x = baslangicX;

  // HATA, non-`late` başlatıcıda `this`'e erişemez:
  double? y = this.x;

  // Tamam, `this`'e `late` başlatıcıda erişebilir:
  late double? z = this.x;

  // Tamam, `this.fieldName` bir parametre deklarasyonudur, bir ifade değil:
  Nokta(this.x, this.y);
}
```

Örnek değişkenleri `final` olabilir, bu durumda bir kereye mahsus olarak ayarlanmalıdır. `Final`, non-`late` örnek değişkenlerini tanımlama sırasında, bir yapılandırıcı (constructor) parametresiyle veya bir yapılandırıcının başlatıcı (constructor initializer) listesi kullanarak başlatın:

```dart
class Profil {
  final String isim;
  final DateTime baslangic = DateTime.now();

  Profil(this.isim);
  Profil.isimsiz() : isim = '';
}
```

Bir yapılandırıcı gövdesi başladıktan sonra bir `final` örnek değişkeninin değerini atamak gerekiyorsa, aşağıdaki yöntemlerden birini kullanabilirsiniz:

* Fabrika yapıcı kullanın.
* Late final kullanın, ancak dikkatli olun: başlatıcı eklemeyen late final, API'ye bir setter ekler.&#x20;

## Örtük Arabirimler (Implicit Interfaces)

Her sınıf, sınıfın tüm örnek üyelerini ve uyguladığı arabirimlerin tüm örnek üyelerini içeren örtük bir arabirim tanımlar. Eğer A sınıfının B sınıfının uygulamasını devralmadan sadece B'nin API'sini destekleyen bir A sınıfı oluşturmak istiyorsanız, A sınıfının B arabirimini uygulaması gerekir.

Bir sınıf, bu arabirimleri bir implements ifadesinde belirterek bir veya daha fazla arabirimi uygular ve ardından arabirimlerin gerektirdiği API'leri sağlar. Örneğin:

```dart
// Bir kişi. Implicit (örtük) arayüz, selamVer() metodunu içerir.
class Kisi {
  // Arayüzde bulunan ancak sadece bu kütüphanede görünür.
  final String _isim;

  // Arayüzde bulunmayan, çünkü bu bir kurucu metod.
  Kisi(this._isim);

  // Arayüzde bulunan bir metod.
  String selamVer(String kime) => 'Merhaba, $kime. Ben $_isim.';
}

// Kisi arayüzünü uygulayan bir sınıf.
class Sahtekar implements Kisi {
  String get _isim => '';

  String selamVer(String kime) => 'Selam $kime. Beni tanıyor musun?';
}

String selamVerBob(Kisi kisi) => kisi.selamVer('Mehmet');

void main() {
  print(selamVerBob(Kisi('Bayram')));
  print(selamVerBob(Sahtekar()));
}
```

Birden fazla arabirimi uyguladığınızı belirtmenin bir örneği:

```dart
class Nokta implements Karsilastirilabilir, Konum {...}
```

## Sınıf Değişkenleri ve Metotları (Class Variables and Methods)

Sınıf çapında değişkenler ve metodlar uygulamak için `static` anahtar kelimesini kullanın.

### Statik Değişkenler (Static Variables)

Statik değişkenler (sınıf değişkenleri), sınıf genelinde durumu ve sabitleri depolamak için kullanışlıdır:

```dart
class Kuyruk {
  static const baslangicKapasitesi = 16;
  // ···
}

void main() {
  assert(Kuyruk.baslangicKapasitesi == 16);
}
```

Statik değişkenler, kullanıldıkları zaman başlatılır.

### Statik Metotlar (Static Methods)

Statik metotlar (sınıf metotları), bir instance üzerinde çalışmaz ve bu nedenle `this`'e erişemez. Ancak, statik değişkenlere erişebilirler. Aşağıdaki örnek gösterildiği gibi, statik metotları bir sınıf üzerinde doğrudan çağırabilirsiniz:

```dart
import 'dart:math';

class Nokta {
  double x, y;
  Nokta(this.x, this.y);

  static double mesafe(Nokta a, Nokta b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Nokta(2, 2);
  var b = Nokta(4, 4);
  var mesafe = Nokta.mesafe(a, b);
  assert(2.8 < mesafe && mesafe < 2.9);
  print(mesafe);
}
```

> **Not**
>
> Ortak veya geniş bir kullanımı olan yardımcı program ve işlevsellik için statik metotlar yerine, statik metotlar yerine üst düzey fonksiyonları kullanmayı düşünün.

Statik metotları derleme zamanı sabitleri olarak kullanabilirsiniz. Örneğin, bir statik metodu bir sabit yapıcıya bir parametre olarak geçirebilirsiniz.<br>


# Yapıcılar (Constructors)

Bir constructor'ı, sınıfının adını (isteğe bağlı olarak Named constructor bölümünde açıklandığı gibi, ek bir tanımlayıcı) kullanarak bir fonksiyon oluşturarak tanımlayın.

Yeni bir sınıf örneği oluşturmak için en yaygın constructor olan "generative constructor"ı kullanın ve gerekirse herhangi bir örnek değişkenini başlatmak için initializing formal parameters'ları kullanın:

```dart
class Nokta {
  double x = 0;
  double y = 0;

  // İlk değer ataması yapılan kurucu method (generative constructor):
  Nokta(this.x, this.y);
}
```

`this` kelimesi mevcut örneğe referans yapar.

> **Not**
>
> `this`'i, bir isim çakışması olduğunda kullanın. Aksi takdirde, Dart stili `this`'i atlar.

## **Formal Parametreleri Başlatma (Initializing Formal Parameters)**

Dart, bir constructor argümanını bir örnek değişkenine atamak için yaygın bir deseni basitleştirmek için initializing formal parameters'ları kullanır. Constructor tanımında doğrudan `this.ozellikAdi`'ni kullanın ve gövdeyi atlayın.

Initializing parametreleri, non-nullable veya final örnek değişkenlerini başlatmanıza olanak tanır, çünkü her ikisi de başlatılmalı veya varsayılan bir değer verilmelidir:

```dart
class Nokta {
  final double x;
  final double y;

  Nokta(this.x, this.y);
  // Kurucu method gövdesi çalışmadan önce
  // x ve y örnek değişkenlerini ayarlar.
}
```

Başlatma formaliteleri tarafından tanıtılan değişkenler, içsel olarak finaldir ve yalnızca başlatıcı listesinin kapsamında geçerlidir.

Başlatıcı listesinde ifade edilemeyen bazı mantık gerçekleştirmeniz gerekiyorsa, o mantığı içeren bir fabrika kurucusu (veya statik metod) oluşturun ve ardından hesaplanan değerleri normal bir kurucuya geçirin.

## **Varsayılan Yapıcılar (Default Constructors)**

Eğer bir constructor tanımlamazsanız, size bir varsayılan constructor sağlanır. Varsayılan constructor'ın argümanı yoktur ve üst sınıftaki argümansız constructor'ı çağırır.

## **Yapıcılar Miras Edilmez (Constructors Aren't Inherited)**

Alt sınıflar, üst sınıflarından constructorları miras almazlar. Constructor tanımlamayan bir alt sınıf, yalnızca varsayılan (argüman olmayan, isimsiz) constructor'a sahiptir.

## **İsimlendirilmiş Yapıcılar (Named Constructors)**

Bir sınıf için birden çok constructor uygulamak veya ek açıklama sağlamak için isimlendirilmiş constructor'ları kullanın:

```dart
const double xOrigin = 0;
const double yOrigin = 0;

class Nokta {
  final double x;
  final double y;

  Nokta(this.x, this.y);

  // İsimlendirilmiş constructor
  Nokta.origin()
      : x = xOrigin,
        y = yOrigin;
}
```

Constructor'lar miras alınmadığı için, bir üst sınıftaki isimlendirilmiş constructor'ı bir alt sınıfta kullanmak istiyorsanız, o constructor'ı alt sınıfta implemente etmelisiniz.

## **Argümansız Üst Sınıf Constructor'ını Çağırmak**

Varsayılan olarak, bir alt sınıftaki bir constructor, üst sınıfın isimsiz, argümansız constructor'ını çağırır. Üst sınıfın constructor'ı, constructor gövdesinin başında çağrılır. Eğer bir initializer list de kullanılıyorsa, bu, üst sınıf çağrılmadan önce gerçekleştirilir. Özetle, yürütme sırası şu şekildedir:

* initializer list
* üst sınıfın argümansız constructor'u
* ana sınıfın argümansız constructor'u

Eğer üst sınıfın isimsiz, argümansız constructor'u yoksa, o zaman üst sınıfın bir constructor'ını manuel olarak çağırmanız gerekir. Bu, iki nokta üst üste (`:`) ve ardından constructor gövdesi (varsa) ile belirtilir.

Aşağıdaki örnekte, `Calisan` sınıfının constructor'ı, üst sınıfı olan `Kisi` sınıfının isimli constructor'ını çağırır:

```dart
class Kisi {
  String? ad;

  Kisi.fromJson(Map data) {
    print('Kisi sınıfında');
  }
}

class Calisan extends Kisi {
  // Kisi sınıfının varsayılan bir kurucusu yok;
  // bu nedenle super.fromJson() çağırmalısınız.
  Calisan.fromJson(Map data) : super.fromJson(data) {
    print('Calisan sınıfında');
  }
}

void main() {
  var calisan = Calisan.fromJson({});
  print(calisan);
  // Çıktı:
  // Kisi sınıfında
  // Calisan sınıfında
  // Instance of 'Calisan'
}
```

> **Uyarı**
>
> Üst sınıf constructor'ına argümanlar, `this`'e erişim sağlamaz. Örneğin, argümanlar static metodları çağırabilir, ancak örnek metodları çağıramaz.

### **Super Parametreleri (Super Prameters)**

Her bir parametreyi tek tek üst sınıf constructor'ına geçirmekten kaçınmak için, belirli veya varsayılan üst sınıf constructor'ına parametreleri iletmeniz için super-initializer parametrelerini kullanabilirsiniz. Bu özellik, initializing formal parameters ile benzer sözdizimi ve anlamı taşır:

```dart
class Vektor2D {
  final double x;
  final double y;

  Vektor2D(this.x, this.y);
}

class Vektor3D extends Vektor2D {
  final double z;

  // x ve y parametrelerini varsayılan süper kurucuya yönlendirin:
  // Vektor3D(final double x, final double y, this.z) : super(x, y);
  Vektor3D(double x, double y, this.z) : super(x, y);
}
```

Super-initializer parametreleri, super-constructor çağrısında zaten pozisyonel argümanlar varsa pozisyonel olamaz, ancak her zaman isimli olabilir:

```dart
class Vektor2D {
  // ...

  Vektor2D.named({required this.x, required this.y});
}

class Vektor3D extends Vektor2D {
  // ...

  // y parametresini isimli süper kurucuya yönlendirin:
  // Vektor3D.yzDuzlemi({required double y, required this.z})
  //       : super.named(x: 0, y: y);
  Vektor3D.yzDuzlemi({required super.y, required this.z}) : super.named(x: 0);
}
```

## **Initializer List**

Super sınıf constructor'ını çağırmak dışında, constructor gövdesi çalışmadan önce örnek değişkenlerini başlatmak için initializer list'ini kullanabilirsiniz. Virgül ile ayrılmış başlatıcıları kullanın.

```dart
// Başlatıcı liste, kurucu gövdesi çalışmadan önce
// örnek değişkenlerini ayarlar.
class Nokta {
  final double x;
  final double y;

  Nokta.fromJson(Map<String, double> json)
      : x = json['x']!,
        y = json['y']! {
    print('Nokta.fromJson() içinde: ($x, $y)');
  }
}
```

> **Uyarı**
>
> Initializer'ın sağ tarafı `this`'e erişim sağlamaz.

Geliştirme sırasında, initializer list içinde `assert` kullanarak girişleri doğrulayabilirsiniz.

```dart
Nokta.withAssert(this.x, this.y) : assert(x >= 0) {
    print('Nokta.withAssert() içinde: ($x, $y)');
}
```

Initializer list'leri, final alanları ayarlamak için kullanışlıdır. Aşağıdaki örnek, üç final alanı initializer list içinde başlatır:

```dart
import 'dart:math';

class Nokta {
  final double x;
  final double y;
  final double originUzaklik;

  Nokta(double x, double y)
      : x = x,
        y = y,
        originUzaklik = sqrt(x * x + y * y);
}

void main() {
  var nokta = Nokta(2, 3);
  print(nokta.originUzaklik);
}
```

## **Yönlendiren Yapıcılar (Redirect Constructors)**

Bazen bir constructor'ın tek amacı, aynı sınıftaki başka bir constructor'a yönlendirmektir. Yönlendiren constructor'ın gövdesi boştur ve constructor çağrısı (`this` yerine sınıf adını kullanarak) iki nokta üst üste ile gösterildikten sonra gelir.

```dart
class Nokta {
  double x, y;

  // Bu sınıfın ana kurucusu.
  Nokta(this.x, this.y);

  // Ana yapıcıya yönlendirir.
  Nokta.xEksenineUzanan(double x) : this(x, 0);
}
```

## **Sabit Yapıcılar (Constant Constructors)**

Eğer sınıfınız, asla değişmeyen nesneleri üretiyorsa, bu nesneleri derleme zamanı sabitleri yapabilirsiniz. Bunun için bir const constructor tanımlayın ve tüm örnek değişkenlerinin final olduğundan emin olun.

```dart
class SabitNokta {
  static const SabitNokta orijin = SabitNokta(0, 0);

  final double x, y;

  const SabitNokta(this.x, this.y);
}
```

Sabit constructor'lar her zaman sabitler oluşturmaz. Detaylar için constructor'ları kullanma bölümüne bakın.

## **Factory Yapıcılar (Factory ConstructorS)**

Constructor'ın her zaman sınıfının yeni bir örneğini oluşturmadığını uyguladığınızda, bir constructor'ı implement etmek için `factory` anahtar kelimesini kullanabilirsiniz. Örneğin, bir factory constructor, bir örneği önbellekten döndürebilir veya bir alt türün bir örneğini döndürebilir. Factory constructor'lar için bir başka kullanım da bir final değişkenini initializer list içinde ele alamayacak mantığı kullanarak başlatmaktır.

> **İpucu**
>
> Bir final değişkenini dikkatlice kullanmak için, bir final değişkenini geç başlatma yoluna başvurabilirsiniz!

Aşağıdaki örnekte, `Gunluk` factory constructor'ı, nesneleri bir önbellekten döndürür ve `Gunluk.fromJson` factory constructor'ı, bir JSON nesnesinden bir final değişkeni başlatır.

```dart
class Gunluk {
  final String ad;
  bool sessiz = false;

  // _cache, isminin önündeki _ sayesinde
  // kütüphane içinde özel (private) bir değişkendir.
  static final Map<String, Gunluk> _onbellek = <String, Gunluk>{};

  factory Gunluk(String ad) {
    return _onbellek.putIfAbsent(ad, () => Gunluk._icSel(ad));
  }

  factory Gunluk.fromJson(Map<String, Object> json) {
    return Gunluk(json['ad'].toString());
  }

  Gunluk._icSel(this.ad);

  void kayit(String mesaj) {
    if (!sessiz) print(mesaj);
  }
}
```

> **Not**
>
> Factory constructor'ları, `this`'e erişime sahip değildir.

Bir factory constructor'ını, diğer herhangi bir constructor gibi çağırın:

```dart
var gunluk = Gunluk('UI');
gunluk.kayit('Butona tıklandı');

var kayitHaritasi = {'isim': 'UI'};
var gunlukJson = Gunluk.fromJson(kayitHaritasi);
```


# Metodlar (Methods)

Metodlar, bir nesnenin davranışını sağlayan fonksiyonlardır.

## **Örnek Metodlar (Instance Methods)**

Nesne üzerindeki örnek metodlar, örnek değişkenlere ve `this` anahtar kelimesine erişebilir. Aşağıdaki örnekteki `digerineUzaklik()` metodu bir örnek metod örneğidir:

```dart
import 'dart:math';

class Nokta {
  final double x;
  final double y;

  Nokta(this.x, this.y);

  double digerineUzaklik(Nokta diger) {
    var dx = x - diger.x;
    var dy = y - diger.y;
    return sqrt(dx * dx + dy * dy);
  }
}
```

## **Operatörler (Operators)**

Operatörler, özel isimlere sahip örnek metodlardır. Dart, aşağıdaki isimlere sahip operatörleri tanımlamanıza izin verir:

`< | + | | | >>> > | / | ^ | [] <= | ~/ | & | []= >= | * | << | ~ - | % | >> | ==`

> **Not**
>
> Bazı operatörlerin, != gibi, listede olmadığını fark etmiş olabilirsiniz. Bu, onların sadece sözdizimsel şeker olduğu anlamına gelir. Örneğin, `e1 != e2` ifadesi, `!(e1 == e2)` için sözdizimi şekeridir.

Aşağıdaki örnek, vektör toplama (`+`), çıkarma (`-`) ve eşitlik (`==`) operatörlerini tanımlar:

```dart
class Vektor {
  final int x, y;

  Vektor(this.x, this.y);

  Vektor operator +(Vektor v) => Vektor(x + v.x, y + v.y);
  Vektor operator -(Vektor v) => Vektor(x - v.x, y - v.y);

  @override
  bool operator ==(Object diger) =>
      diger is Vektor && x == diger.x && y == diger.y;

  @override
  int get hashCode => Object.hash(x, y);
}

void main() {
  final v = Vektor(2, 3);
  final w = Vektor(2, 2);

  assert(v + w == Vektor(4, 5));
  assert(v - w == Vektor(0, 1));
}
```

## **Getters ve Setters**

Getters ve setters, bir nesnenin özelliklerine okuma ve yazma erişimi sağlayan özel metodlardır. Her örnek değişkeninin zaten varsayılan bir getter'ı ve uygunsa bir setter'ı vardır. Getters ve setters'ları kullanarak ek özellikler oluşturabilirsiniz. Bunun için `get` ve `set` anahtar kelimelerini kullanabilirsiniz:

```dart
class Dikdortgen {
  double sol, ust, genislik, yukseklik;

  Dikdortgen(this.sol, this.ust, this.genislik, this.yukseklik);

  // Hesaplanmış iki özellik tanımla: sag ve alt.
  double get sag => sol + genislik;
  set sag(double deger) => sol = deger - genislik;
  double get alt => ust + yukseklik;
  set alt(double deger) => ust = deger - yukseklik;
}

void main() {
  var dikdortgen = Dikdortgen(3, 4, 20, 15);
  assert(dikdortgen.sol == 3);
  dikdortgen.sag = 12;
  assert(dikdortgen.sol == -8);
}
```

Getters ve setters kullanarak, örnek değişkenleri metodlarla sarmadan başlayabilir ve daha sonra değiştirmeniz gerekmez.

> **Not**
>
> Artırma (++) gibi operatörler, beklenen şekilde çalışır, getter açıkça tanımlanmış olsa da olmasa da. Beklenmeyen yan etkileri önlemek için, operatör getter'ı tam olarak bir kez çağırır ve değerini geçici bir değişkende saklar.

## **Soyut Metodlar (Abstract Methods)**

Örnek, getter ve setter metodları soyut olabilir, yani bir arabirim tanımlar ancak uygulamasını başka sınıflara bırakır. Soyut metodlar yalnızca soyut sınıflarda veya mixin'lerde bulunabilir. Bir metodun soyut olması için, bir metot gövdesi yerine bir noktalı virgül (;) kullanın:

```dart
abstract class Yapan {
  // Örnek değişkenleri ve metodları tanımla...

  void birSeyYap(); // Soyut bir metod tanımla.
}

class EtkiliYapan extends Yapan {
  void birSeyYap() {
    // Metodu burada soyut olmaktan çıkaracak bir uygulama sağla...
  }
}
```

<br>


# Sınıfı Uzatma (Extending a Class)

Bir sınıftan türemek için `extends` kelimesi kullanılır. Ayrıca `super` anahtar kelimesi, türetilen sınıfta, üst sınıfın metotlarına referans için kullanılır. İşte bir örnek:

```dart
class Televizyon {
  void ac() {
    _ekraniAydinlat();
    _irSensorunuAktiveEt();
  }
  // ···
}

class AkilliTelevizyon extends Televizyon {
  void ac() {
    super.ac();
    _agArayuzunuBaslat();
    _bellegiBaslat();
    _uygulamalariGuncelle();
  }
  // ···
}
```

## **Üyeleri Geçersiz Kılma (Overriding Members)**

Alt sınıflar, üst sınıflarındaki metotları, getter'ları ve setter'ları geçersiz kılabilirler. Bu, alt sınıflarda özel davranış eklemek veya üst sınıftaki davranışı değiştirmek için kullanışlıdır. `@override` açıklaması, bir üyenin bilinçli olarak geçersiz kılındığını belirtmek için kullanılır. Örnek:

```dart
class Televizyon {
  // ···
  set parlaklik(int deger) {...}
}

class AkilliTelevizyon extends Televizyon {
  @override
  set parlaklik(num deger) {...}
  // ···
}
```

Bu örnekte, `AkilliTelevizyon` sınıfı, `Televizyon` sınıfındaki `parlaklik` setter'ını geçersiz kılar ve parametre türünü genişletir.

## **NoSuchMethod() Metodu**

Dart dilinde, olmayan bir metodu kullanmaya çalıştığınızda `NoSuchMethodError` hatası alırsınız. Ancak, bu hatayı özelleştirmek için `noSuchMethod()` metodunu geçersiz kılabilirsiniz:

```dart
class A {
  @override
  void noSuchMethod(Invocation davet) {
    print('Var olmayan bir üyeyi kullanmaya çalıştınız: '
        '${davet.memberName}');
  }
}
```

Bu sayede, olmayan bir üye kullanılmaya çalışıldığında özel bir mesaj görüntülenebilir.

Dart dilinde sınıfları genişletme ve üyeleri geçersiz kılma, kodunuzu daha modüler hale getirmenize ve tekrar kullanılabilirlik sağlamanıza yardımcı olabilir.


# Katma (Mixin)

Mixinler, kodunuzu birden çok sınıf hiyerarşisinde yeniden kullanmanın bir yoludur. Birçok üye uygulaması sağlamak amacıyla tasarlanmıştır.

Mixin kullanmak için, bir veya daha fazla mixin adından sonra gelen "with" anahtar kelimesini kullanın. Aşağıdaki örnek, iki sınıfın mixin'leri kullandığı bir durumu göstermektedir:

```dart
class Muzisyen extends PerformansSanatcisi with Muzikal {
  // ···
}

class Maestro extends Kisi with Muzikal, Agresif, Demente {
  Maestro(String maestroAdi) {
    ad = maestroAdi;
    orkestraSeftaliyor = true;
  }
}
```

Mixin tanımlamak için, mixin deyimini kullanın. Nadir durumlarda bir mixin ve bir sınıfı aynı anda tanımlamanız gerekiyorsa, mixin sınıfı deyimini kullanabilirsiniz.

Mixin ve mixin sınıfları "extends" kısmına sahip olamaz ve üretici kurucu bildirimi yapmamalıdır.

Örneğin:

```dart
mixin Muzikal {
  bool pianoCalabilir = false;
  bool besteYapabilir = false;
  bool orkestraSeftaliyor = false;

  void beniEglendir() {
    if (pianoCalabilir) {
      print('Piyano çalıyor');
    } else if (orkestraSeftaliyor) {
      print('Ellerini sallıyor');
    } else {
      print('Kendi kendine mırıldanıyor');
    }
  }
}
```

Bazen bir mixin'in kullanabileceği türleri sınırlamak isteyebilirsiniz. Örneğin, mixin, tanımlamadığı bir yöntemi çağırabilme yetisine bağlıysa, mixin'in kullanımını "on" anahtar kelimesiyle zorlayabilirsiniz:

```dart
class Muzisyen {
  // ...
}
mixin MuzikalSanatci on Muzisyen {
  // ...
}
class SarkiciDansci extends Muzisyen with MuzikalSanatci {
  // ...
}
```

Yukarıdaki kodda, MuzikalSanatci mixin'ini yalnızca Muzisyen sınıfını genişleten veya uygulayan sınıflar kullanabilir. Çünkü SarkiciDansci sınıfı Muzisyen'i genişlettiği için MuzikalSanatci mixin'ini kullanabilir.

### **Sınıf, mixin veya mixin sınıfı? (Class, Mixin or Mixin Class)**

Mixin tanımı bir mixin'i belirtir. Sınıf tanımı bir sınıfı belirtir. Mixin sınıfı tanımı ise, aynı ad ve aynı türle hem düzenli bir sınıf hem de bir mixin olarak kullanılabilen bir sınıfı belirtir.

Sınıflar veya mixin'ler için geçerli olan kısıtlamalar, mixin sınıfları için de geçerlidir:

* Mixin'lerin "`extends`" veya "`with`" kısmı olamaz, bu nedenle mixin sınıfları da olamaz.
* Sınıfların "`on`" kısmı olamaz, bu nedenle mixin sınıfları da olamaz.

### **Soyut Mixin Sınıfı (Abstract Mixin Class)**

Bir mixin sınıfının "on" direktifi için benzer bir davranışı başarabilirsiniz. Mixin sınıfını soyut yapın ve davranışının bağlı olduğu soyut yöntemleri tanımlayın:

```dart
abstract mixin class Muzisyen {
  // 'on' direktifi yok, ancak davranışının bağlı olduğu soyut bir yöntemi tanımlar:
  void enstrumanCal(String enstrumanAdi);

  void piyanoCal() {
    enstrumanCal('Piyano');
  }
  void flutCal() {
    enstrumanCal('Flüt');
  }
}

class Virtuoso with Muzisyen { // Muzisyen'i bir mixin olarak kullanın
  void enstrumanCal(String enstrumanAdi) {
    print('$enstrumanAdi güzel bir şekilde çalınıyor');
  }  
} 

class Acemi extends Muzisyen { // Muzisyen'i bir sınıf olarak kullanın
  void enstrumanCal(String enstrumanAdi) {
    print('$enstrumanAdi kötü bir şekilde çalınıyor');
  }  
}
```

Muzisyen mixin'ini soyut olarak tanımlayarak, onu kullanan herhangi bir türü davranışının bağlı olduğu soyut yöntemi tanımlamaya zorlarsınız.

Bu, "`on`" direktifinin bir mixin'in bağımlı olduğu arabirimlere erişim sağladığı şekilde benzer bir işlevi yerine getirir.

<br>


# Enumerasyon (Enums)

Enumerasyonlar veya kısaca enums olarak adlandırılan, sabit değerleri temsil etmek için kullanılan özel bir sınıf türüdür.

> **Not**
>
> Tüm enums otomatik olarak Enum sınıfını genişletir. Aynı zamanda mühürlüdürler, yani alt sınıf olarak belirlenemez, uygulanamaz, karıştırılamaz veya başka bir şekilde açıkça örneklendirilemezler.
>
> Soyut sınıflar ve mixin'ler Enum'ı açıkça uygulayabilir veya genişletebilir, ancak bir enum bildirisinde uygulanmadıkça veya karıştırılmadıkça, o sınıfın veya mixin'in türünü hiçbir nesne gerçekten uygulayamaz.

## Basit Enumerasyon (Simple Enums)

Basit bir numaralandırılmış türü bildirmek için enum anahtar kelimesini kullanın ve numaralandırmak istediğiniz değerleri listeleyin:

```dart
enum Renk { kirmizi, yesil, mavi }
```

## Gelişmiş Enumerasyon (Enchanced Enums)

Dart ayrıca enum bildirimlerinin, final alanlar, yöntemler ve belirli sayıda bilinen sabit örneklerle sınırlı olan sınıfları bildirmelerine izin verir.

Gelişmiş bir enum bildirmek için, normal sınıflarla benzer bir sözdizimini izleyin, ancak birkaç ek gereksinime sahip:

* Örnek değişkenleri final olmalıdır, bunlar arasında mixin'ler tarafından eklenenler de dahil.
* Tüm üretici kurucular sabit olmalıdır.
* Fabrika kurucuları yalnızca sabit, bilinen enum örneklerinden birini döndürebilir.
* Enum otomatik olarak genişletildiği için başka bir sınıf genişletilemez.
* index, hashCode, eşitlik operatörü == için geçersiz kılma olmamalıdır.
* Bir enum'da values adında bir üye bildirilemez, çünkü bu, otomatik olarak oluşturulan static values getter ile çakışacaktır.
* Enum'un tüm örnekleri bildirimin başında belirtilmelidir ve en az bir örnek bildirilmelidir.
* Gelişmiş bir enumdaki örnek yöntemleri, bu'yi kullanarak geçerli enum değerine başvurabilir.

Aşağıda, çoklu örnekler, örnek değişkenler, getter'lar ve uygulanan bir arayüzle bir gelişmiş enum bildiren bir örnek bulunmaktadır:

```dart
enum Arac implements Comparable<Arac> {
  araba(lastikler: 4, yolcular: 5, karbonKilometreBasina: 400),
  otobus(lastikler: 6, yolcular: 50, karbonKilometreBasina: 800),
  bisiklet(lastikler: 2, yolcular: 1, karbonKilometreBasina: 0);

  const Arac({
    required this.lastikler,
    required this.yolcular,
    required this.karbonKilometreBasina,
  });

  final int lastikler;
  final int yolcular;
  final int karbonKilometreBasina;

  int get karbonAyakizi => (karbonKilometreBasina / yolcular).round();

  bool get ikiTekerlekli => this == Arac.bisiklet;

  @override
  int compareTo(Arac diger) => karbonAyakizi - diger.karbonAyakizi;
}
```

## Enumerasyon Kullanma

Numaralandırılmış değerlere diğer statik değişkenler gibi erişebilirsiniz:

```dart
final begenilenRenk = Renk.mavi;
if (begenilenRenk == Renk.mavi) {
  print('En sevdiğin renk mavi!');
}
```

Her enum'daki her değerin bir index getter'ı vardır, bu da değerin enum bildirisindeki sıfır tabanlı konumunu döndürür. Örneğin, ilk değer index 0'a, ikinci değer index 1'e sahiptir.

```dart
assert(Renk.kirmizi.index == 0);
assert(Renk.yesil.index == 1);
assert(Renk.mavi.index == 2);
```

Tüm numaralandırılmış değerlerin bir listesini almak için enum'un values sabitini kullanın.

```dart
List<Renk> renkler = Renk.values;
assert(renkler[2] == Renk.mavi);
```

Enum'ları switch ifadelerinde kullanabilirsiniz ve enum'un tüm değerlerini işlemezseniz bir uyarı alırsınız:

```dart
var birRenk = Renk.mavi;

switch (birRenk) {
  case Renk.kirmizi:
    print('Gül gibi kırmızı!');
  case Renk.yesil:
    print('Çimen gibi yeşil!');
  default: // Bu olmadan, BİR UYARI görürsünüz.
    print(birRenk); // 'Renk.mavi'
}
```

Bir numaralandırılmış değerin adına erişmeniz gerekiyorsa, örneğin Renk.mavi'den 'mavi'yi kullanmak için .name özelliğini kullanın:

```dart
print(Renk.mavi.name); // 'mavi'
```

Enum değerinin bir üyesine normal bir nesne gibi erişebilirsiniz:

```dart
print(Arac.araba.karbonAyakizi);
```


# Genişletme Methodları (Extension Methods)

Genişletme metodları, mevcut kütüphanelere yeni işlevsellik eklemenizi sağlar. Bu metodları, bir IDE'de kod tamamlama özelliğini kullanırken, normal metodlarla birlikte önerilen genişletme metodları da görebilirsiniz.

Başkalarının veya yaygın olarak kullanılan bir kütüphaneyi uyguladığınızda, Kütüphaneyi değiştirmek genellikle pratik veya mümkün olmayabilir. Ancak yine de işlevsellik eklemek isteyebilirsiniz.

Örneğin, bir dizeyi bir tamsayıya çözümleyen aşağıdaki kodu düşünün:

```dart
int.parse('42');
```

Bu işlevselliğin `String` üzerinde olmasını istiyorsanız, şu şekilde kısaltabilir ve araçlarla daha kolay kullanabilirsiniz:

```dart
'42'.parseInt();
```

Bu kodu etkinleştirmek için, `String` sınıfına bir genişletme içeren bir kütüphaneyi içe aktarabilirsiniz:

```dart
import 'string_apis.dart';
// ···
print('42'.parseInt()); // Genişletme metodunu kullan.
```

Genişletmeler sadece metodları değil, aynı zamanda diğer üyeleri de (getter, setter ve operatörler gibi) tanımlayabilir. Ayrıca, genişletmelerin isimleri olabilir ve bu, bir API çakışması durumunda yardımcı olabilir. İşte, bir genişletme metodunu uygulayan (NumberParsing adlı) bir genişletmeyi içeren `string_apis.dart` dosyasını nasıl oluşturabileceğiniz:

`string_apis.dart`

```dart
extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }
  // ···
}
```

Sonraki bölümde genişletme metodlarını nasıl kullanacağınızı, ardından genişletme metodlarını nasıl uygulayacağınızı açıklıyoruz.

## Genişletme Metodlarını Kullanma (Using Extension Methods)

Tıpkı diğer Dart kodları gibi, genişletme metodları da kütüphaneler içindedir. Genişletme metodunu kullanmanın yolu, ilgili kütüphaneyi içe aktarmaktır ve bunu sıradan bir metod gibi kullanmaktır:

```dart
// String üzerinde bir genişletme içeren kütüphaneyi içe aktar.
import 'string_apis.dart';
// ···
print('42'.padLeft(5)); // String metodunu kullan.
print('42'.parseInt()); // Genişletme metodunu kullan.
```

Genellikle genişletme metodlarını kullanmak için bilmeniz gereken tek şey budur. Kodunuzu yazarken genişletme metodlarının statik tiplere (dinamik değil) nasıl bağlı olduğunu ve kütüphane çakışmalarını nasıl çözeceğinizi de bilmek isteyebilirsiniz.

### Statik Tipler ve Dinamik (Static Types and Dynamic)

Genişletme metodlarını dinamik türdeki değişkenler üzerinde çağıramazsınız. Örneğin, aşağıdaki kod bir çalışma zamanı hatasına neden olur:

```dart
dynamic d = '2';
print(d.parseInt()); // Çalışma zamanı hatası: NoSuchMethodError
```

Genişletme metodları, Dart'ın tür çıkarımı ile çalışır. Aşağıdaki kod sorunsuz çalışır çünkü değişken v'nin türü String olarak çıkarılır:

```dart
var v = '2';
print(v.parseInt()); // Çıktı: 2
```

Dinamik türün çalışmamasının nedeni, genişletme metodlarının alıcının statik türüne karşı çözümlendiğidir. Genişletme metodları statik olarak çözümlendiği için, bunlar statik bir fonksiyonu çağırmak kadar hızlıdır.

Daha fazla bilgi için statik tipler ve dinamik konusuna bakmak için Dart tür sistemine göz atın.

### API Çakışmaları (API Conflicts)

Eğer bir genişletme üyesi bir arayüzle veya başka bir genişletme üyesiyle çakışıyorsa, birkaç seçeneğiniz vardır.

Bir seçenek, çakışan genişletmeyi nasıl içe aktardığınızı değiştirmektir, show veya hide kullanarak maruz bırakılan API'yi sınırlayabilirsiniz:

```dart
// String üzerinde parseInt() genişletme metodunu tanımlar.
import 'string_apis.dart';

// parseInt() metodunu aynı şekilde tanımlar, ancak NumberParsing2'yi gizleyerek
// bu genişletme metodunu gizler.
import 'string_apis_2.dart' hide NumberParsing2;

// ···
// 'string_apis.dart' içinde tanımlanan parseInt()'i kullanır.
print('42'.parseInt());
```

Başka bir seçenek, genişletmeyi açıkça uygulamaktır; bu, genişletmenin bir sarma sınıfı gibi göründüğü kodu içerir:

```dart
dartCopy code// Hem 'string_apis.dart' hem de 'string_apis_2.dart'
// String üzerinde parseInt() içeren genişletmeleri tanımlar.
import 'string_apis.dart'; // NumberParsing genişletmesini içerir.
import 'string_apis_2.dart'; // NumberParsing2 genişletmesini içerir.

// ···
// print('42'.parseInt()); // Çalışmaz.
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());
```

Eğer her iki genişletme de aynı isme sahipse, o zaman bir ön ek kullanarak içe aktarmak gerekebilir:

```dart
dartCopy code// Her iki kütüphane de NumberParsing isimli genişletmeleri tanımlar
// ve parseInt() genişletme metodunu içerir. 'string_apis_3.dart'
// içindeki NumberParsing genişletmesi ayrıca parseNum() metodunu da tanımlar.
import 'string_apis.dart';
import 'string_apis_3.dart' as rad;

// ···
// print('42'.parseInt()); // Çalışmaz.

// 'string_apis.dart' içindeki ParseNumbers genişletmesini kullanın.
print(NumberParsing('42').parseInt());

// 'string_apis_3.dart' içindeki ParseNumbers genişletmesini kullanın.
print(rad.NumberParsing('42').parseInt());

// 'string_apis_3.dart' sadece parseNum() metodunu içerir.
print('42'.parseNum());
```

Bu örnek gösteriyor ki, bir ön ek kullanarak içe aktarırsanız, genişletme metodlarını açıkça çağırmadan da genişletme metodlarını örtük olarak çağırabilirsiniz. Sadece bir genişletmeyi açıkça çağırmak için bir isim çakışmasını önlemek için ön ek kullanmanız gerekir.

## Genişletme Metodlarını Uygulama (Implementing Extension Methods)

Bir genişletme oluşturmak için aşağıdaki sözdizimini kullanın:

```dart
extension <genişletme adı>? on <tip> {
  (<üye tanımı>)*
}
```

Örneğin, String sınıfına bir genişletme nasıl uygulayabileceğinizi aşağıda görebilirsiniz:

`string_apis.dart`

```dart
extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }

  double parseDouble() {
    return double.parse(this);
  }
}
```

Bir genişletmenin üyeleri metodlar, getter'lar, setter'lar veya operatörler olabilir. Genişletmeler ayrıca statik alanlara ve statik yardımcı metodlara da sahip olabilir. Genişletme dışında statik üyelere erişmek için, bunları sınıf değişkenleri ve metodları gibi adlarıyla çağırın.

### İsimsiz Genişletmeler (Unnamed Extension)

Bir genişletme bildirirken, ismi atlayabilirsiniz. İsimsiz genişletmeler, yalnızca tanımlandıkları kütüphanede görünür. İsimleri olmadığı için, açıkça uygulanamazlar ve API çakışmalarını çözemezler.

```dart
extension on String {
  bool get isBlank => trim().isEmpty;
}
```

> **Not**
>
> İsimsiz genişletmenin statik üyelerini, yalnızca genişletme bildirisi içinde kullanabilirsiniz.

## Jenerik Genişletmeleri Uygulama (Implementing Generic Extensions)

Genişletmeler, jenerik tür parametrelerine sahip olabilir. Örneğin, yerleşik List\<T> türünü bir getter, bir operatör ve bir metod ile genişleten bir kod parçası aşağıdaki gibidir:

```dart
extension MyFancyList<T> on List<T> {
  int get doubleLength => length * 2;
  List<T> operator -() => reversed.toList();
  List<List<T>> split(int at) => [sublist(0, at), sublist(at)];
}
```

Tür T, çağrılan metodların üzerinde çalıştığı listenin statik türüne bağlı olarak bağlanır.


# Genişletme Türleri (Extension Types)

Genişletme türleri, mevcut bir türü farklı, yalnızca derleme zamanında bir arayüzle saran bir derleme zamanı soyutlamasıdır. Bu soyutlama, statik JavaScript etkileşiminde kritik bir rol oynar çünkü gerçek bir sarmalayıcı nesnenin maliyetini gerektirmeden mevcut bir türün arayüzünü değiştirmek için kullanılabilirler.

Genişletme türleri, temsil türü olarak adlandırılan mevcut bir türle birlikte kullanılır. Bir genişletme türünün arayüzünü tanımlarken, temsil türünün disiplin altına alınmasını sağlarlar. Genişletme türünün arayüzünü tanımlarken, temsil türünün arayüzünden bazı üyeleri yeniden kullanabilir, bazılarını atlayabilir, bazılarını değiştirebilir ve yeni işlevsellik ekleyebilirsiniz.

Aşağıdaki örnek, `int` türünü sadece ID numaraları için anlamlı olan işlemlere izin veren bir genişletme türü oluşturur:

```dart
extension type IdNumber(int id) {
  // 'int' türünün '<' operatörünü genişletir:
  operator <(IdNumber diger) => id < diger.id;
  // '+' operatörünü bildirmez, çünkü toplama ID numaraları için anlamsızdır.
}

void main() {
  // Bir genişletme türünün disiplini olmadan, 'int' ID numaralarını güvensiz işlemlere açar:
  int guvensizId = 42424242;
  guvensizId = guvensizId + 10; // Bu çalışır, ancak ID'ler için izin verilmemeli.

  var guvenliId = IdNumber(42424242);
  guvenliId + 10; // Derleme zamanı hatası: '+' operatörü yok.
  guvensizId = guvenliId; // Derleme zamanı hatası: Yanlış tip.
  guvensizId = guvenliId as int; // Tamam: Temsil türüne run-time dönüşüm.
  guvenliId < IdNumber(42424241); // Tamam: Genişletilmiş '<' operatörü kullanılır.
}
```

> **Not**
>
> Genişletme türleri, sarmalayıcı sınıfların aksine ek bir çalışma zamanı nesnesi oluşturmayı gerektirmez, bu da birçok nesneyi genişletme ihtiyacı olduğunda maliyeti düşürür. Genişletme türleri, statik olarak belirlenmiş yapılar olup çalışma zamanında silinir, bu nedenle maliyet açısından neredeyse sıfır olarak kabul edilebilir.

## Sözdizimi (Syntax)

### Deklarasyon (Declaration)

Yeni bir genişletme türünü tanımlamak için, genişletme türü bildirimi ve bir ad ile temsil türü bildirimi kullanılır:

```dart
extension type E(int i) {
  // İşlemleri tanımla.
}
```

Temsil türü bildirimi `(int i)` ifadesiyle temsil türünü int olarak belirtir ve bu temsil nesnesine referans için i adını tanımlar. Ayrıca, temsil nesnesine erişim sağlamak için i adında bir özel getirici ve E(int i) adında bir varsayılan olmayan kurucu bildirimi de ekler.

Temsil nesnesi, genişletme türü gövdesinde i kullanılarak erişilebilir (veya kurucu içinde `this.i` olarak).

Genişletme türü bildirimleri, sınıf veya genişletme bildirimlerinde olduğu gibi tür parametrelerini içerebilir:

```dart
extension type E<T>(List<T> elemanlar) {
  // ...
}
```

### Yapıcılar (Constructors)

Genişletme türü gövdesinde isteğe bağlı olarak kurucuları bildirebilirsiniz. Temsil bildirimi zaten örtük bir kurucu görevi gördüğü için, adlandırılmış olmayan kurucu yerine kullanılır. Ek olmayan yönlendiren üretilmemiş kurucular, bu nesnenin başlatılmasını sağlar.

```dart
extension type E(int i) {
  E.n(this.i);
  E.m(int j, String foo) : i = j + foo.length;
}

void main() {
  E(4); // Adlandırılmamış kurucu.
  E.n(3); // Adlandırılmış kurucu.
  E.m(5, "Merhaba!"); // Adlandırılmış kurucu ek parametrelerle.
}
```

Veya temsil bildirimi adını taşıyan bir kurucu olabilir ve bu durumda adlandırılmamış kurucu eklenmez:

```dart
extension type const E._(int it) {
  E(): this._(42);
  E.digerAd(this.it);
}

void main2() {
  E();
  const E._(2);
  E.digerAd(3);
}
```

Ya da tamamen bir kurucu gizleyebilir ve aynı özel kurucu sözdizimini sınıflarda kullanabilirsiniz:

```dart
extension type E._(int i) {
  E.fromString(String foo) : i = int.parse(foo);
}
```

### Üyeler (Members)

Genişletme türü gövdesindeki üyeleri bildirerek arayüzü tanımlayabilirsiniz. Genişletme türü üyeleri, metodlar, getter'lar, setter'lar veya operatörler olabilir. non-external instance variables veya abstract üyeleri içeremezler.

```dart
extension type SayiE(int deger) {
  // Operatör:
  SayiE operator +(SayiE diger) =>
      SayiE(deger + diger.deger);
  // Getter:
  SayiE get benimSayim => this;
  // Metod:
  bool gecerliMi() => !deger.isNegative;
}
```

Varsayılan olarak, temsil türünün arayüz üyeleri, genişletme türü arayüzüne doğal olarak dahil edilmez. Bir temsil türü üyesini genişletme türü arayüzüne dahil etmek için, genişletme türü tanımında onu açıkça bildirmelisiniz, örneğin, NumberE'deki '+' operatörü gibi.

### Uygulamak (Implements)

İsterseniz `implements` ifadesini kullanarak uygulamaları dahil edebilirsiniz:

```dart
extension type SayiI(int i) 
  implements int {
  // 'SayiI', 'int' türünün tüm üyelerini çağırabilir,
  // ve burada başka şeyler de bildirebilir.
}
```

Veya temsil türünün bir üst türünü kullanabilirsiniz:

```dart
extension type Sira<T>(List<T> _) implements Iterable<T> {
  // Liste üzerindeki operasyonlar.
}

extension type Kimlik(int _id) implements Object {
  // Uzantı türünü null olamayan yapar.
  static Kimlik? tryParse(String kaynak) => int.tryParse(kaynak) as Kimlik?;
}
```

Aynı temsil türü üzerinde geçerli olan başka bir genişletme türünü de bildirebilirsiniz, bu da size çoklu kalıtımın bir türün üzerinde tekrar kullanılmasına benzer bir etki sağlar:

```dart
extension type const Secenek<T>._(({T deger})? _) { 
  const factory Secenek(T deger) = Deger<T>;
  const factory Secenek.yok() = Yok<T>;
}

extension type const Deger<T>._(({T deger}) _) implements Secenek<T> { 
  const Deger(T deger) : this._((deger: deger));
  T get deger => _.deger;
}

extension type const Yok<T>._(Null _) implements Secenek<Never> {
  const Yok() : this._(null);
}
```

Bu, farklı uzantı türleri arasında işlemleri yeniden kullanmanıza olanak tanır ve çoklu kalıtımın bir türde tekrar kullanılmasına benzer bir etki sağlar.

#### **`@redeclare`**&#x20;

Bir genişletme türü üyesini bildiren ad, sınıflar arasında olduğu gibi bir yeniden bildirim ilişkisi değil, yeniden bildirimdir. Bir genişletme türü üye bildirimi, aynı adı taşıyan bir üst tür üyesini tamamen değiştirir. Bu işlevi aynı fonksiyon için alternatif bir uygulama sağlamak mümkün değildir.

Aynı adı taşıyan bir üst tür üyesini bilerek kullanıyorsanız, derleyiciye bunun gerçekten doğru olduğunu bildirmek için `@redeclare` açıklamasını kullanabilirsiniz. Derleyici, bir adın yanlış yazıldığı durumları size bildirecektir.

```dart
extension type BenimString(String _) implements String {
  // 'String.operator[]'ı değiştirir.
  @yenidenTanimla
  int operator [](int index) => codeUnitAt(index);
}
```

Ayrıca, eğer bir genişletme türü yöntemi bir üst tür üyesini gizliyorsa ve bu gizli birim yanlışlıkla yapılırsa, @redeclare açıklamasını kullanarak derleyiciye bir uyarı vermesini sağlamak için lint annotate\_redeclares'ı etkinleştirebilirsiniz.

## Kullanım (Usage)

Bir genişletme türünü kullanmak için, bir inşa metodu aracılığıyla bir örneği oluşturarak sınıf gibi davranabilirsiniz:

```dart
extension type SayiE(int deger) {
  SayiE operator +(SayiE diger) =>
      SayiE(deger + diger.deger);

  SayiE get sonraki => SayiE(deger + 1);
  bool gecerliMi() => !deger.isNegative;
}

void testE() { 
  var num = SayiE(1);
}
```

Sonra nesne üzerinde sınıf nesneleri gibi üyeleri çağırabilirsiniz.

Genişletme türleri için iki eşit ancak önemli ölçüde farklı ana kullanım durumu vardır:

* Mevcut bir türe genişletilmiş bir arayüz sağlama
* Mevcut bir türe farklı bir arayüz sağlama

### **Mevcut bir türe genişletilmiş bir arayüz sağlama**&#x20;

Bir genişletme türü, temsil türünü uygularken "şeffaf" olarak düşünülebilir, çünkü genişletme türü, temsil türünü "görebilir". Bu, bir genişletme türünün temsil türünün tüm üyelerini (yeniden bildirilmemiş olanlar) çağırabilmesi anlamına gelir. Bu durumda, genişletme türü, temsil türünün arayüzüne genişletilmiş bir arayüz sağlar.

Bu, örneğin, bir üst sınıfın bazı parametrelerinin daha güvenli olacak şekilde yeniden tanımlandığı bir "çoğunlukla şeffaf" bir genişletme türü olabilir.

```dart
extension tipi SayiT(int deger) 
  implements int {
  // 'int' türünün herhangi bir üyesini açıkça bildirmez.
  SayiT get i => this;
}

void main () {
  // Tamam: Şeffaflık, genişletme türü üzerinde 'int' üyelerini çağırmayı sağlar:
  var v1 = SayiT(1); // v1 türü: SayiT
  int v2 = SayiT(2); // v2 türü: int
  var v3 = v1.i - v1;  // v3 türü: int
  var v4 = v2 + v1; // v4 türü: int
  var v5 = 2 + v1; // v5 türü: int
  // Hata: Genişletme türü arayüzü, temsil türü için kullanılamaz
  v2.i;
}
```

Ya da, bir yöntemin bazı parametrelerine daha katı türleri kullanarak veya farklı varsayılan değerlerle adapte edilen bir "çoğunlukla şeffaf" bir genişletme türü olabilir.

### **Mevcut bir türe farklı bir arayüz sağlama**

Şeffaf olmayan (temsil türünü uygulamayan yani implement olmayan) bir genişletme türü, tamamen yeni bir tür olarak ele alınır ve temsil türünün üyelerini açıkça göstermez.

Örneğin, kullanılan genişletme türü, temsil türü olan int'ten tamamen farklı bir tür olarak kabul edilir. Bu durumda, onu int türüne atayamazsınız ve temsil türünün üyelerine açıkça erişemezsiniz.

```dart
void testE() { 
  var num1 = SayiE(1);
  int num2 = SayiE(2); // Hata: 'SayiE' tipi 'int' tipine atanamaz.
  
  num1.gecerli(); // Tamam: Uzantı üye çağrısı.
  num1.negatifMi(); // Hata: 'SayiE' tipi 'int' tipinde 'negatifMi' üyesini tanımlamaz.
  
  var toplam1 = num1 + num1; // Tamam: 'SayiE' tipi '+' işlemini tanımlar.
  var fark1 = num1 - num1; // Hata: 'SayiE' tipi 'int' tipinde '-' üyesini tanımlamaz.
  var fark2 = num1.deger - 2; // Tamam: Referans ile temsil nesnesine erişilebilir.
  var toplam2 = num1 + 2; // Hata: 'int' tipini 'SayiE' tipine atayamazsınız.
  
  List<SayiE> sayilar = [
    SayiE(1), 
    num1.siradaki, // Tamam: 'siradaki' getter'ı 'SayiE' tipini döndürür.
    1, // Hata: 'int' elemanını 'SayiE' tipine atanamaz.
  ];
}
```

Genişletme türlerini kullanırken, bir türün arayüzünü değiştirmek için güçlü bir araçla performans ve kolaylık avantajına sahip olursunuz, ancak temsil türünün arayüzü asla alt türü olmadığı için bir temsil türünü genişletme türüne ihtiyaç duyulan yerde değiş tokuş yapamazsınız.

## Tür İle İlgili Hususlar (Type Considerations)

Genişletme türleri, derleme zamanında bir sarma yapısıdır. Çalışma zamanında, genişletme türünün hiçbir izi yoktur. Herhangi bir tür sorgusu veya benzeri çalışma zamanı operasyonları, temsil türü üzerinde çalışır.

Bu, genişletme türlerini güvensiz bir soyutlama yapar, çünkü her zaman çalışma zamanında temsil türünü bulabilir ve altındaki nesneye erişebilirsiniz.

Dinamik tür testleri (e is T), dönüşümler (e as T) ve diğer çalışma zamanı tür sorguları (switch (e) ... veya if (e case ...)) tümü temsil nesnesine değerlendirilir ve bu nesnenin çalışma zamanı türüne karşı kontrol eder. Bu durum, e'nin statik türü bir genişletme türü olduğunda ve genişletme türüne karşı test edildiğinde geçerlidir (case MyExtensionType(): ...).

```dart
void main() {
  var n = SayiE(1);

  // 'n'nin çalışma zamanı türü, temsil türü 'int' olarak değerlendirilir.
  if (n is int) print(n.deger); // 1 yazdırır.

  // 'n' üzerinde çalışma zamanında 'int' metotları kullanabiliriz.
  if (n case int x) print(x.toRadixString(10)); // 1 yazdırır.
  switch (n) {
    case int(:var isEven): print("$n (${isEven ? "çift" : "tek"})"); // 1 (tek) yazdırır.
  }
}
```

Benzer şekilde, eşleşen değerin statik türü bu örnekte genişletme türüdür:

```dart
void main() {
  int i = 2;
  if (i is SayiE) print("Bu"); // 'Bu' yazdırır.
  if (i case SayiE v) print("değer: ${v.deger}"); // 'değer: 2' yazdırır.
  switch (i) {
    case SayiE(:var value): print("değer: $value"); // 'değer: 2' yazdırır.
  }
}
```

Genişletme türlerini kullanırken bu özelliği akılda bulundurmak önemlidir. Her zaman genişletme türünün derleme zamanında var olduğunu ve önemli olduğunu, ancak derleme sırasında silindiğini unutmayın.

Bir ifade, genişletme türü E ve statik türü R olarak çalışma zamanında R türündeki bir nesne olacaktır. Hatta tür kendisi de silinir; List\<E>, çalışma zamanında tam olarak List\<R> ile aynı şeydir.

Başka bir deyişle, gerçek bir sarma sınıfı bir sarılı nesneyi kapsayabilirken, bir genişletme türü yalnızca sarılı nesnenin derleme zamanındaki bir görünümüdür. Gerçek bir sarma daha güvenlidir, ancak genişletme türleri size sarma nesnelerini önlemek için bir seçenek sunar, bu da bazı senaryolarda performansı büyük ölçüde artırabilir.


# Sınıf Modifikatörleri (Class Modifiers)

Sınıf modifikatörleri, bir sınıf veya karışımın hem kendi kitaplığı içinde hem de tanımlandığı kitaplığın dışında nasıl kullanılabileceğini kontrol eder.

Modifikatör anahtar kelimeleri, sınıf veya karışım bildirisinden önce gelir. Örneğin, abstract class yazarak soyut bir sınıf tanımlarsınız. Bir sınıf bildirisinden önce görünebilecek tam modifikatör kümesi şunları içerir:

* abstract
* base
* final
* interface
* sealed
* mixin

Sadece base modifikatörü, bir mixin bildirisinden önce görünebilir. Modifikatörler, enum, typedef, extension veya extension type gibi diğer bildirimlere uygulanmaz.

Sınıf modifikatörlerini kullanıp kullanmamaya karar verirken, sınıfın amaçlanan kullanımlarını ve sınıfın hangi davranışlara güvenebilmesi gerektiğini düşünmelisiniz.

> **Not**
>
> Eğer bir kütüphaneyi yönetiyorsanız, kütüphaneleriniz için bu değişiklikleri nasıl yönlendireceğiniz konusunda rehberlik için "API sağlayıcıları için Sınıf Modifikatörleri" sayfasını okuyun.

## **Modifikatör Olmadan (No Modifier)**

Sınıfa veya karışıma modifikatör eklenmemiş bir bildiri, herhangi bir kitaplıktan inşa etme veya alt tür oluşturma izni verir. Varsayılan olarak şunları yapabilirsiniz:

* Bir sınıfın yeni örneklerini oluşturun.
* Bir sınıfı genişleterek yeni bir alt tür oluşturun.
* Bir sınıfın veya karışımın arayüzünü uygulayın.
* Bir karışımı veya karışım sınıfını içe alın.

## **Soyut (Abstract)**

Tam bir arayüzün somut bir uygulamasını zorunlu kılmayan bir sınıf tanımlamak için soyut modifikatörü kullanın.

Soyut sınıflar herhangi bir kitaplık tarafından inşa edilemez, kendi kitaplığında olsun veya dış kitaplıklardan olsun. Soyut sınıflar genellikle soyut metodlara sahiptir.

`a.dart`

```dart
abstract class Arac {
  void ileriGit(int metre);
}
```

`b.dart`

```dart
import 'a.dart';

// Hata: Oluşturulamaz.
Arac aracim = Arac();

// Genişletilebilir.
class Araba extends Arac {
  int yolcuSayisi = 4;
  // ···
}

// Uygulanabilir.
class SahteArac implements Arac {
  @override
  void ileriGit(int metre) {
    // ...
  }
}
```

Eğer soyut sınıfınızın görünürde bir şekilde örneklenmesini istiyorsanız, bir fabrika kurucusu tanımlayın.

## **Base**

Bir sınıfın veya karışımın uygulamasının kalıtımını zorlamak için base modifikatörünü kullanın. Base sınıf, kendi kitaplığı dışında bir uygulamayı engeller. Bu şu garantileri sağlar:

* Base sınıfın kurucusu, sınıfın alt türünden bir örnek oluşturulduğunda her zaman çağrılır.
* Tüm uygulanan özel üyeler, alt türlerde mevcuttur.
* Bir base sınıfındaki yeni uygulanan bir üye, alt türleri bozmaz, çünkü tüm alt türler yeni üyeyi miras alır.
* Bu, alt tür zaten aynı ada sahip ve uyumsuz bir imza ile bir üye bildirirse geçerli değildir.
* Bir sınıf veya karışımı uygulayan veya genişleten herhangi bir sınıfın base, final veya sealed olarak işaretlenmesi gerekir. Bu, dış kitaplıkların base sınıfları bozmasını engeller.

`a.dart`

```dart
base class Arac {
  void ileriGit(int metre) {
    // ...
  }
}
```

`b.dart`

```dart
import 'a.dart';

// Oluşturulabilir.
Arac aracim = Arac();

// Genişletilebilir.
base class Araba extends Arac {
  int yolcuSayisi = 4;
  // ...
}

// HATA: Uygulanamaz.
base class SahteArac implements Arac {
  @override
  void ileriGit() {
    // ...
  }
}
```

## **Arayüz (Interface)**

Bir arayüz tanımlamak için interface modifikatörünü kullanın. Arayüzün kendi tanımlayan kitaplığının dışındaki kitaplıklar arayüzü uygulayabilir, ancak genişletemez. Bu şu garantileri sağlar:

* Bir sınıfın örnek metotlarından biri diğer bir metodu çağırdığında, bu her zaman aynı kitaplıktan bilinen bir metodun uygulanmasını çağırır.
* Diğer kitaplıklar, arayüz sınıfının kendi metotlarını beklenmedik şekillerde geçersiz kılabilecek metodları geçersiz kılamaz. Bu, kırılgan base sınıf sorununu azaltır.

`a.dart`

```dart
interface class Arac {
  void ileriGit(int metre) {
    // ...
  }
}
```

`b.dart`

```dart
import 'a.dart';

// Oluşturulabilir.
Arac aracim = Arac();

// HATA: Genişletilemez.
class Araba extends Arac {
  int yolcuSayisi = 4;
  // ...
}

// Uygulanabilir.
class SahteArac implements Arac {
  @override
  void ileriGit(int metre) {
    // ...
  }
}
```

### **Soyut Arayüz (Abstract Interface)**

interface modifikatörünü genellikle saf bir arayüzü tanımlamak için kullanın. Bir soyut sınıfa, başka kitaplıklar tarafından uygulanabilir ancak miras alınamayan bir arayüz için interface ve abstract modifikatörlerini birleştirin. Bu, başka kitaplıkların bir arayüzü uygulayabilmesine, ancak miras alamamasına olanak tanır.

## **Final**

Tip hiyerarşisini kapatmak için final modifikatörünü kullanın. Bu, mevcut kitaplığın dışındaki bir sınıftan alt türleme izin vermez. Hem kalıtım hem de uygulamayı engelleyerek tamamen alt türlemeyi engeller. Bu şu garantileri sağlar:

* API'ye aşamalı değişiklikler ekleyebilirsiniz.
* Birinci sınıf alt sınıflarının örnek metotlarını güvenle çağırabilirsiniz, çünkü bunların üçüncü taraf alt sınıfları tarafından üzerine yazılmamış olması garantilidir.
* Final sınıfları aynı kitaplık içinde genişletilebilir veya uygulanabilir. Final modifikatörü, base'nin etkilerini içerir ve bu nedenle alt sınıflar da base, final veya sealed olarak işaretlenmelidir.

`a.dart`

```dart
final class Arac {
  void ileriGit(int metre) {
    // ...
  }
}
```

`b.dart`

```dart
import 'a.dart';

// Oluşturulabilir.
Arac aracim = Arac();

// HATA: Genişletilemez.
class Araba extends Arac {
  int yolcuSayisi = 4;
  // ...
}

class SahteArac implements Arac {
  // HATA: Uygulanamaz.
  @override
  void ileriGit(int metre) {
    // ...
  }
}
```

## **Mühürlü (Sealed)**

Belli, sayılabilir bir alt tür kümesi oluşturmak için sealed modifikatörünü kullanın. Bu, bu alt türler üzerinde duraksızca geçiş yapılması için derleyici tarafından statik olarak sağlanan bir switch oluşturmanıza olanak tanır.

Sealed modifikatörü, bir sınıfın kendi kitaplığı dışında genişletilmesine veya uygulanmasına izin vermez. Mühürlü sınıflar, varsayılan olarak soyut olarak kabul edilir.

* Kendi başlarına oluşturulamazlar.
* Fabrika kurucularına sahip olabilirler.
* Alt sınıflarının kullanması için kurucular tanımlayabilirler.

Derleyici, bu alt türlerin yalnızca aynı kitaplıkta var olabileceğini bildiği için herhangi bir olası doğrudan alt türün farkındadır. Bu, bir switch'in tüm olası alt türleri işlemediğinde sizi uyarabilir:

`a.dart`

```dart
sealed class Arac {}

class Araba extends Arac {}

class Kamyon implements Arac {}

class Bisiklet extends Arac {}

// HATA: Oluşturulamaz.
Arac aracim = Arac();

// Alt sınıflar oluşturulabilir.
Arac aracimAraba = Araba();

String getAracSesi(Arac arac) {
  // HATA: Switch, Bicycle alt türünü veya varsayılan durumu eksik bıraktı.
  return switch (arac) {
    case Araba() => 'vroom',
    case Kamyon() => 'VROOOOMM',
  };
}
```

Eğer kapsamlı bir geçiş istemiyorsanız veya daha sonra alt türler ekleyebilmek istiyorsanız, final modifikatörünü kullanın. Daha ayrıntılı bir karşılaştırma için "mühürlü versus final"i okuyun.

## **Modifikatörleri Birleştirme**

Bazı modifikatörleri katmanlı kısıtlamalar için birleştirebilirsiniz. Bir sınıf bildirimi, sırasıyla:

* (İsteğe Bağlı) Soyut olup olmadığını belirten abstract, sınıfın soyut üyeler içerebilme ve örneklenememe yeteneğini engeller.
* (İsteğe Bağlı) base, interface, final veya sealed'dan biri, sınıfın başka kitaplıkların alt türlerine yönelik kısıtlamalarını belirler.
* (İsteğe Bağlı) mixin, bildirimin karıştırılıp karıştırılamayacağını belirler.
* Sınıf anahtar kelimesi kendisi.

Bazı modifikatörleri birleştiremezsiniz çünkü bunlar çelişkili, gereksiz veya başka türlü birbirleriyle çakışan modifikatörlerdir:

* abstract ile sealed. Bir mühürlü sınıf zaten soyut olarak kabul edilir.
* mixin ile interface, final veya sealed. Bu erişim modifikatörleri karışmayı engeller.


