---
title: 'Dart Temelleri'
source: 'https://academia.sh/tr/kurslar/flutter/dart-temelleri'
course: Flutter
language: tr
updated: '2026-10-21T05:02:52+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Dart'ın tip, null güvenliği ve sınıf yapılarını bir düzen çekirdeği üzerinde okur"
    - 'Düzen sözleşmesini açıklar; kısıtlar aşağı iner, boyutlar yukarı çıkar, ebeveyn konumu belirler'
    - 'Bir bileşenin genişliğini dayatılan, seçilen, kırpılan ve esnetilen olarak sınıflar'
    - 'Aynı ağacın farklı ekran genişliklerinde farklı boyutlanma verdiğini ölçer'
---

# Dart Temelleri

Bir bileşenin genişliğini kimin belirlediğini sınıflayan küçük bir Dart çekirdeği. Ders Dart'ın tiplerini, null güvenliğini ve sınıflarını bu çekirdek üzerinden anlatır ve dört boyutlanmayı tanıtır: dayatılan, seçilen, kırpılan, esnetilen.

Bu kurs bir arayüz çerçevesini tek bir soruyla açar: **bu bileşenin genişliğini kim belirliyor?** Burada **bileşen**, ekranda yer kaplayan arayüz parçasıdır: bir metin, bir görsel, bir düğme ya da başka bileşenleri içeren bir kapsayıcı. Sözcük kurs boyunca hep bu anlamda kalır; yazılım mimarisindeki modül ya da servis anlamına gelmez.

Her bileşene ebeveyninden bir **kısıt** iner: kabul edilen en küçük ve en büyük genişlik. Bileşenin kendi isteği olabilir ya da olmayabilir. Kısıt ile isteğin birleşimi genişliği verir ve genişliğin sahibi dört taraftan biridir. Adları kurs boyunca değişmez; çıktıda ASCII yazılır, düzyazıda Türkçe karşılığıyla geçer.

- **dayatılan** (`dayatilan`): kısıt sıkıdır, yani en küçük ve en büyük genişlik aynıdır. Bileşenin isteği sonucu değiştiremez.
- **seçilen** (`secilen`): bileşenin isteği kısıtın içinde kalır ve genişliği bileşen seçer.
- **kırpılan** (`kirpilan`): istek kısıtın dışına taşar ya da kısıt sınırsızken istek yoktur. Genişliği kısıt keser.
- **esnetilen** (`esnetilen`): istek yoktur, kısıtın üst sınırı vardır ve bileşen o sınıra kadar genişler.

Seçilen dışındaki üçünde genişliği bileşen değil, üstten gelen kısıt belirler. Bu üçünün toplamına **ebeveyn** denir; çıktının son satırı onu sayar.

## Düzen Sözleşmesi

Kısıtın nereden geldiğini üç cümle söyler. Kısıtlar aşağı iner: ebeveyn, çocuğa kabul edeceği aralığı verir. Boyutlar yukarı çıkar: çocuk o aralığın içinde bir genişlik seçer ve ebeveyne bildirir. Ebeveyn konumu belirler: çocuğun nerede duracağına çocuk değil, ebeveyn karar verir. Bu kurs yalnız ilk iki cümlenin genişlik ekseniyle ilgilenir.

**DB1.** Model, düzen sözleşmesini yalnız genişlik ekseninde kurar: her düğüm tek bir `min..max` aralığı alır ve tek bir tamsayı genişlik döndürür. Yükseklik, konum ve çizim modelde yoktur; model çerçevenin kendisi değildir.

## Çekirdek Model

Sınıflayıcı tek bir Dart dosyasıdır, `sizing.dart`. Kurs boyunca her ders onu ilk Dart bloğu olarak taşır ve içeriği hiç değişmez; sonraki derslerde yeniden okumak yerine yalnız üstüne eklenen ağaçlara bakılır. Bu ders dosyayı satır satır açar, çünkü Dart'ın temel yapıları burada birlikte çalışır.

```dart
// sizing.dart — bir bilesenin genisligini kimin belirledigini siniflandirir
const sizings = ['dayatilan', 'secilen', 'kirpilan', 'esnetilen'];

class Limit {
  final int min;
  final int? max; // null: sinirsiz
  const Limit(this.min, this.max);

  bool get tight => max == min;
  Limit loosen() => Limit(0, max);
  Limit deflate(int by) => Limit(_floor(min - by), max == null ? null : _floor(max! - by));
  String bounds() => '$min..${max ?? 'sonsuz'}';
}

int _floor(int value) => value < 0 ? 0 : value;

class Part {
  final String kind;
  final String name;
  final int? width; // null: alabildigi kadar genis
  final List<Part> children;
  final int factor;
  final bool tightFlex;
  const Part(this.kind, this.name, this.width, this.children,
      {this.factor = 0, this.tightFlex = true});
}

Part leaf(String name, int width) => Part('leaf', name, width, const []);
Part fill(String name) => Part('leaf', name, null, const []);
Part pad(String name, int padding, Part child) => Part('pad', name, padding, [child]);
Part center(String name, Part child) => Part('center', name, null, [child]);
Part fixed(String name, int width, Part child) => Part('fixed', name, width, [child]);
Part column(String name, List<Part> children) => Part('column', name, null, children);
Part row(String name, List<Part> children) => Part('row', name, null, children);
Part flex(String name, int factor, Part child, {bool tight = true}) =>
    Part('flex', name, null, [child], factor: factor, tightFlex: tight);

class Sizing {
  final String sizing;
  final String path;
  final int width;
  final Limit limit;
  const Sizing(this.sizing, this.path, this.width, this.limit);
}

Sizing classify(String path, int? desire, Limit limit) {
  if (limit.tight) return Sizing('dayatilan', path, limit.min, limit);
  if (desire == null) {
    return limit.max == null
        ? Sizing('kirpilan', path, limit.min, limit)
        : Sizing('esnetilen', path, limit.max!, limit);
  }
  if (desire < limit.min) return Sizing('kirpilan', path, limit.min, limit);
  if (limit.max != null && desire > limit.max!) {
    return Sizing('kirpilan', path, limit.max!, limit);
  }
  return Sizing('secilen', path, desire, limit);
}

int layout(Part part, Limit limit, List<Sizing> log, [String path = '']) {
  final here = '$path/${part.name}';
  final below = <Sizing>[];
  if (part.kind == 'fixed') {
    final own = classify(here, part.width, limit);
    layout(part.children.first, Limit(own.width, own.width), below, here);
    log..add(own)..addAll(below);
    return own.width;
  }
  int? desire;
  switch (part.kind) {
    case 'leaf':
      desire = part.width;
    case 'pad':
      final space = 2 * part.width!;
      desire = layout(part.children.first, limit.deflate(space), below, here) + space;
    case 'center':
      final inner = layout(part.children.first, limit.loosen(), below, here);
      desire = limit.max == null ? inner : null;
    case 'flex':
      desire = layout(part.children.first, limit, below, here);
    case 'column':
      var widest = 0;
      for (final child in part.children) {
        final width = layout(child, limit.loosen(), below, here);
        if (width > widest) widest = width;
      }
      desire = widest;
    case 'row':
      final logs = [for (final _ in part.children) <Sizing>[]];
      final bounded = limit.max != null;
      var used = 0;
      var factors = 0;
      for (var i = 0; i < part.children.length; i++) {
        final child = part.children[i];
        if (child.kind == 'flex' && bounded) {
          factors += child.factor;
        } else {
          used += layout(child, const Limit(0, null), logs[i], here);
        }
      }
      final left = bounded ? _floor(limit.max! - used) : 0;
      var given = 0;
      var seen = 0;
      for (var i = 0; i < part.children.length; i++) {
        final child = part.children[i];
        if (child.kind != 'flex' || !bounded) continue;
        seen += child.factor;
        final share = (seen == factors ? left : left * seen ~/ factors) - given;
        given += share;
        final room = child.tightFlex ? Limit(share, share) : Limit(0, share);
        used += layout(child, room, logs[i], here);
      }
      for (final entries in logs) {
        below.addAll(entries);
      }
      desire = bounded ? null : used;
  }
  final own = classify(here, desire, limit);
  log..add(own)..addAll(below);
  return own.width;
}

List<Sizing> measure(Part root, int screen) {
  final log = <Sizing>[];
  layout(root, Limit(screen, screen), log);
  return log;
}

void printSizes(List<Sizing> log) {
  for (final entry in log) {
    print('${entry.sizing}: ${entry.path} genislik=${entry.width} kisit=${entry.limit.bounds()}');
  }
}

void countSizings(List<Sizing> log) {
  for (final name in sizings) {
    print('$name: ${log.where((entry) => entry.sizing == name).length}');
  }
  print('ebeveyn: ${log.where((entry) => entry.sizing != 'secilen').length}');
}
```

**Tipler ve null güvenliği.** `Limit` sınıfının `min` alanı `int`, `max` alanı `int?` tipindedir. Dart 3.13.5'te bir tip `null` kabul etmez; kabul ediyorsa adının sonunda `?` taşır. `max`'ın `null` olması "üst sınır yok" demektir ve `bounds()` bunu `sonsuz` diye yazar. `null` olabilen bir değeri `null` olamayan bir yere vermek derleme hatasıdır. Değerin sonuna konan ünlem işareti derleyiciye "bu değer burada `null` değil" der; çekirdek bunu yalnız önce `null` denetimi yapılmış yerlerde kullanır. `max ?? 'sonsuz'` ise `max` `null` ise sağ tarafı seçer.

**Sınıflar ve değişmezlik.** `Limit` ve `Part` sınıflarının alanları `final`, kurucuları `const` olduğundan nesneler kurulduktan sonra değişmez. `loosen()` ve `deflate()` mevcut kısıtı değiştirmez, yenisini döndürür. `bool get tight => max == min;` bir **getter**'dır: çağrılırken parantez almaz, `limit.tight` diye okunur.

**Parametreler.** `Part` kurucusunun ilk dört parametresi konumsaldır. Küme parantezi içindeki `factor` ve `tightFlex` **adlandırılmış** parametrelerdir, varsayılan değerleri vardır ve çağrıda adıyla verilir: `flex('Note', 1, fill('Text'), tight: false)`. Yardımcı işlevler `leaf`, `fill`, `pad` gibi kısa adlarla ağaç kurar. `fill`, genişlik isteğini `null` bırakır: "alabildiğim kadar genişlerim".

**Koleksiyonlar ve ifadeler.** `[for (final _ in part.children) <Sizing>[]]` her çocuk için boş bir liste kurar. `log..add(own)..addAll(below)` bir **zincirleme** çağrıdır; her `..` aynı nesneye yeni bir işlem uygular. `~/` tamsayı bölmedir. `switch` içindeki `case` dalları `break` taşımaz; her dal kendi bloğunda biter.

### Kural sırası

`classify` işlevi sınıflamanın tek yeridir ve soruları şu sırayla sorar:

1. Kısıt sıkıysa sonuç dayatılandır.
2. İstek yoksa: üst sınır sonluysa esnetilen, sınırsızsa kırpılan.
3. İstek en küçük genişliğin altındaysa ya da üst sınırın üstündeyse kırpılan.
4. Aksi hâlde seçilen.

**DB2.** Sıkı kısıt ilk sorulur, çünkü kısıt tek bir genişliğe izin veriyorsa bileşenin isteği sonucu değiştiremez. Bu sıra modelin kararıdır; sınıflamayı başka bir sırayla da kurmak mümkündür, ama çıktıdaki ad ancak bu sırayla anlam taşır.

## Açılış Düzeni

Aşağıdaki ağaç kursun bağlayıcı açılış örneğidir. Kök `Screen`, her yandan 16 iç boşluk bırakır ve içinde `Body` sütunu durur. Sütunda 120 isteyen `Title`, 400 isteyen `Banner` ve bir satır olan `Toolbar` vardır. Satırda 48 isteyen `Back` ve `Menu` ile, aralarında esneklik çarpanı 1 olan `Search` yer alır; `Search` içindeki `Field` genişlik istemez.

```dart
// main.dart
import 'sizing.dart';

void main() {
  final screen = pad('Screen', 16, column('Body', [
    leaf('Title', 120),
    leaf('Banner', 400),
    row('Toolbar', [
      leaf('Back', 48),
      flex('Search', 1, fill('Field')),
      leaf('Menu', 48),
    ]),
  ]));

  final log = measure(screen, 360);
  printSizes(log);
  countSizings(log);
}
```

```text
dayatilan: /Screen genislik=360 kisit=360..360
dayatilan: /Screen/Body genislik=328 kisit=328..328
secilen: /Screen/Body/Title genislik=120 kisit=0..328
kirpilan: /Screen/Body/Banner genislik=328 kisit=0..328
esnetilen: /Screen/Body/Toolbar genislik=328 kisit=0..328
secilen: /Screen/Body/Toolbar/Back genislik=48 kisit=0..sonsuz
dayatilan: /Screen/Body/Toolbar/Search genislik=232 kisit=232..232
dayatilan: /Screen/Body/Toolbar/Search/Field genislik=232 kisit=232..232
secilen: /Screen/Body/Toolbar/Menu genislik=48 kisit=0..sonsuz
dayatilan: 4
secilen: 3
kirpilan: 1
esnetilen: 1
ebeveyn: 6
```

Çıktı kuralları sırayla işletir. `Screen` 360 sıkı kısıt aldığı için dayatılandır. `pad` iki yandan 16 çıkarır; `Body`'ye 328 sıkı kısıt iner ve `Body` da dayatılandır. Sütun kısıtı gevşetir: çocuklar `0..328` alır. `Title` 120 ister ve aralığın içinde kaldığından seçilendir. `Banner` 400 ister, üst sınır 328'dir; genişliğini kısıt keser ve kırpılandır. `Toolbar` genişlik istemez, üst sınırı 328 olduğundan esnetilendir. Satır, esnek olmayan çocuklara `0..sonsuz` verir; `Back` ve `Menu` bu aralıkta 48'i seçer. Kalan 328 - 48 - 48 = 232, çarpanı 1 olan `Search`'e sıkı kısıt olarak iner ve `Search` ile `Field` dayatılan olur.

Sayım dört sınıfı ve ebeveyn toplamını verir: 4 dayatılan, 3 seçilen, 1 kırpılan, 1 esnetilen; ebeveyn 4 + 1 + 1 = 6. Bir ayrıntı şimdiden not edilmeli: `Search` esnek çocuktur, ama esnetilen değil dayatılandır. Esnetilen olan `Toolbar`'dır. İkisi aynı şey değildir; altıncı ders bu ayrımı açar.

## Bileşen Sınıfı

Dart'ta bir bileşen bir sınıftır. Aşağıdaki parça çerçevenin durumsuz bileşen sınıfından türeyen en küçük bileşeni gösterir. Parça ölçülmez; model çerçevenin kodunu çalıştırmaz.

```dart
// Platform kodu: bilesen sinifi
class TitleLabel extends StatelessWidget {
  const TitleLabel({super.key, required this.text});
  final String text;

  @override
  Widget build(BuildContext context) {
    return Text(text);
  }
}
```

Sınıf `StatelessWidget`'ten türer (`extends`). `required` adlandırılmış parametreyi zorunlu yapar; `text` verilmeden bileşen kurulamaz. `build` yöntemi bileşenin ne göstereceğini başka bir bileşen olarak döndürür, burada bir `Text`. `@override` üst sınıftaki yöntemi yeniden tanımladığını bildirir.

Sınıfın kendisi genişlik hakkında hiçbir şey söylemez. `build` yalnız ne gösterileceğini tarif eder; genişliği bileşenin ağaçta durduğu yer ve ona inen kısıt belirler. Bu kursun işi o ikinci yarıyı ölçmektir.

## İsteğin Yokluğu

Çekirdekteki `int?` yalnız bir kolaylık değildir; "istek yok" ile "istek sıfır" ayrımını taşır. Aşağıdaki blok iki yaprak kurar, isteklerini yazdırır ve ikisini bir sütunda ölçer.

```dart
// main.dart
import 'sizing.dart';

void main() {
  final label = leaf('Label', 120);
  final space = fill('Space');
  print('Label istegi: ${label.width}');
  print('Space istegi: ${space.width}');

  final screen = pad('Screen', 16, column('Body', [label, space]));
  printSizes(measure(screen, 360));
}
```

```text
Label istegi: 120
Space istegi: null
dayatilan: /Screen genislik=360 kisit=360..360
dayatilan: /Screen/Body genislik=328 kisit=328..328
secilen: /Screen/Body/Label genislik=120 kisit=0..328
esnetilen: /Screen/Body/Space genislik=328 kisit=0..328
```

`Label` 120 ister ve seçilendir. `Space` hiçbir şey istemez; isteği `null` olduğundan kısıt `0..328` iken üst sınıra kadar genişler ve esnetilendir. `null` ve `0` aynı şey olsaydı bu iki satır ayırt edilemezdi.

## Sınırlayıcı Ölçüm

Bir ilke ancak çürütebilecek bir ölçümle sınanır. Açılış ağacını bu kez iki ekran genişliğinde ölçüyoruz ve yalnız odak düğümü, `/Screen/Body/Banner`'ı süzüp yazdırıyoruz. Ağaç değişmez; yalnız kök kısıt değişir. Son satırlar 480'deki sayımı verir.

```dart
// main.dart
import 'sizing.dart';

Part buildScreen() {
  return pad('Screen', 16, column('Body', [
    leaf('Title', 120),
    leaf('Banner', 400),
    row('Toolbar', [
      leaf('Back', 48),
      flex('Search', 1, fill('Field')),
      leaf('Menu', 48),
    ]),
  ]));
}

void main() {
  for (final screenWidth in [480, 360]) {
    print('ekran=$screenWidth');
    final log = measure(buildScreen(), screenWidth);
    printSizes(log.where((entry) => entry.path == '/Screen/Body/Banner').toList());
  }
  print('sayim ekran=480');
  countSizings(measure(buildScreen(), 480));
}
```

```text
ekran=480
secilen: /Screen/Body/Banner genislik=400 kisit=0..448
ekran=360
kirpilan: /Screen/Body/Banner genislik=328 kisit=0..328
sayim ekran=480
dayatilan: 4
secilen: 4
kirpilan: 0
esnetilen: 1
ebeveyn: 5
```

**Bu dersin boyutlanması: kırpılan.** 480 genişlikte `Banner`'ın 400 isteği `0..448` aralığının içinde kalır ve genişliği `Banner` seçer. 360'ta aynı 400 isteği `0..328` aralığına sığmaz ve genişliği kısıt keser. Bileşen, ağaç ve istek aynıdır; sonucun sahibi yalnız kök kısıt değiştiği için değişir. 480'deki sayım da bunu doğrular: kırpılan sıfırdır, seçilen dörde çıkar.

**DB3.** `Banner`'ın 400 isteği modelde hazır bir sayıdır. Gerçek bir görselin genişliği çoğu zaman yüklendikten sonra belli olur; model bunu önceden bilinen bir istek sayar. Genişliğin sonradan geldiği durum bir sonraki dersin konusudur.

## Özet

- Bu dersin boyutlanması: kırpılan. `/Screen/Body/Banner` 400 ister, 360 ekranda kısıt `0..328` verir ve genişliği kısıt keser.
- Dart'ta `int?` "değer yok" ihtimalini tipin içine yazar; `null` olamayan tipe `null` vermek derleme hatasıdır ve çekirdek `int?` ile "istek yok" durumunu taşır.
- Düzen sözleşmesi üç cümledir: kısıtlar aşağı iner, boyutlar yukarı çıkar, ebeveyn konumu belirler.
- Genişliğin sahibi dört taraftan biridir: dayatılan, seçilen, kırpılan, esnetilen. Açılış ağacında 4 dayatılan, 3 seçilen, 1 kırpılan, 1 esnetilen vardır; ebeveyn 6'dır.
- Aynı ağaç 480'de seçilen, 360'ta kırpılan sonuç verir: genişliğin sahibi bileşenin tek başına özelliği değil, bileşenle kısıtın birleşimidir.

## Sonraki Adım

`Banner`'ın genişliği bu derste hazır bir sayıydı. Gerçek uygulamalarda böyle bir değer çoğu zaman hemen gelmez. **Eşzamansız Dart** dersi, genişliğin sonradan geldiği durumu `Future` ve `await` ile kurar ve aynı bileşenin değer gelmeden önce ve geldikten sonra hangi boyutlanmayı aldığını ölçer.
