---
title: 'Bileşen Kavramı'
source: 'https://academia.sh/tr/kurslar/flutter/bilesen-kavrami'
course: Flutter
language: tr
updated: '2026-10-21T05:02:53+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bileşenin değişmez bir tarif olduğunu ve iç boşluk, hizalama ve boyutun da birer bileşen olduğunu açıklar'
    - 'Kısıtın aşağı indiğini, boyutun yukarı çıktığını bir ağaç üzerinde ölçer'
    - 'Aynı bileşenin sıkı bir kısıtta dayatılan, gevşek bir kısıtta seçilen olduğunu gösterir'
---

# Bileşen Kavramı

Her şeyin bileşen olduğu bir arayüz modelinde iç boşluk da bir bileşendir. Ders bileşenin değişmez bir tarif olduğunu ve bir kartın aynı isteğe rağmen konumuna göre neden başka bir sahiple karşılaştığını ölçer.

Önceki iki ders bileşeni bir ağaç düğümü olarak kullandı: `pad`, `column` ve `leaf` çağrıları iç içe geçip bir ekran kurdu. Bu ders o düğümün ne olduğunu söyler. Bu arayüz modelinde ekranda gördüğünüz hemen her şey bir **bileşendir**: metin, görsel, düğme; ama aynı zamanda iç boşluk, hizalama, sabit boyut ve sütun da. Bileşenler başka bileşenleri çocuk olarak alır ve ağaç bu iç içe geçişten oluşur.

## Hemen Her Şey Bir Bileşendir

Birçok arayüz aracında iç boşluk bir bileşenin özelliğidir: kartın `margin` ya da `padding` ayarı vardır. Burada ise iç boşluk kendi başına bir bileşendir ve çocuğunu sarar. Hizalama ve sabit boyut da öyledir. Önce dersin çekirdeği gelir; bu, ilk dersin `sizing.dart` dosyasıdır ve hiç değişmemiştir.

```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}');
}
```

Aşağıdaki parça bir kartı iç boşluk bileşeninin içine koyar. Parça ölçülmez; model çerçevenin kodunu çalıştırmaz.

```dart
// Platform kodu: ic bosluk bileseni
Widget buildCard() {
  return const Padding(
    padding: EdgeInsets.all(16),
    child: Card(child: Text('Ozet')),
  );
}
```

`Padding` kartı sarar ve her yandan 16 birim boşluk bırakır; kart `Padding`'in `child` parametresidir. Modelde bu parçanın karşılığı `pad('Screen', 16, leaf('Card', 200))` çağrısıdır.

**DB1.** Model iç boşluğu, hizalamayı ve sabit boyutu birer düğüm olarak kurar (`pad`, `center`, `fixed`). Çerçevenin bileşen listesinin tamamını sunmaz; model çerçevenin kendisi değildir.

**DB2.** `Card` modelde yalnız genişlik isteyen bir yapraktır. Kartın içeriği, gölgesi ve köşeleri yoktur; geriye ölçülebilen tek şey olan 200 birimlik istek kalır.

İç boşluk bir bileşen olduğundan üst üste konabilir. Aşağıdaki blok iki iç boşluğu iç içe koyar ve her birinin kartın kısıtını ne kadar daralttığını gösterir.

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

void main() {
  final tree = pad('Outer', 8, pad('Inner', 8, leaf('Card', 200)));
  printSizes(measure(tree, 360));
}
```

```text
dayatilan: /Outer genislik=360 kisit=360..360
dayatilan: /Outer/Inner genislik=344 kisit=344..344
dayatilan: /Outer/Inner/Card genislik=328 kisit=328..328
```

Her `pad` iki yandan kendi değerini çıkarır: 360 sıkı kısıttan 344, ondan 328 çıkar. Bileşenlerin hiçbiri kendi başına bir şey "hesaplamaz"; her biri kısıtı alır, çocuğa değiştirilmiş hâlini verir ve çocuğun cevabını işler.

## Bileşen Bir Tariftir

Bir bileşen nesnesi ekranda duran bir şeyin kendisi değil, onun **tarifidir**. Nesne bir kez kurulur ve değişmez: alanları `final`, kurucusu `const`. Bir şey değişecekse yeni bir bileşen kurulur ve eskisi atılır. Bu seçim iki sonuç doğurur.

Birincisi, bileşenin kuruluşu ucuzdur. Tarif ekranın kendisini taşımadığı için bir ekranın tarifini yeniden kurmak, ekranı yeniden çizmek anlamına gelmez. İkincisi, aynı bileşen nesnesi birden çok yerde durabilir. Nesne hiçbir konum bilgisi taşımaz; konumu ağaçtaki yeri verir ve her yerde o yerin kısıtı ona iner. Aşağıdaki ölçümler bu ikinci sonucu gösterir.

## Kısıt Aşağı, Boyut Yukarı

Bir ağaçta iki akış vardır ve ters yönlere gider. **Kısıtlar aşağı iner**: ebeveyn, çocuğa kabul edeceği aralığı verir. **Boyutlar yukarı çıkar**: çocuk o aralıkta bir genişlik seçer ve ebeveyne bildirir. Ebeveyn bu boyutu alır, kendi genişliğini ona göre belirler ve çocuğu yerleştirir.

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

void main() {
  final tree = pad('Screen', 16, center('Center', column('Body', [
    leaf('A', 100),
    leaf('B', 250),
  ])));
  printSizes(measure(tree, 360));
}
```

```text
dayatilan: /Screen genislik=360 kisit=360..360
dayatilan: /Screen/Center genislik=328 kisit=328..328
secilen: /Screen/Center/Body genislik=250 kisit=0..328
secilen: /Screen/Center/Body/A genislik=100 kisit=0..328
secilen: /Screen/Center/Body/B genislik=250 kisit=0..328
```

`Screen`'den `Center`'a 328 sıkı kısıt iner. `Center` bu kısıtı gevşetir ve `Body`'ye `0..328` verir; `A` ve `B`'ye de aynı gevşek aralık ulaşır. Yukarı çıkan boyutlar şunlardır: `A` 100, `B` 250 bildirir; sütun bunların en genişini, 250'yi kendi genişliği yapar. Kısıt aşağı yönde azalarak, boyut yukarı yönde büyüyerek ilerlemiştir.

Jetpack Compose ile Arayüz kursunda "bu veri arayüz ağacında hangi yöne gidiyor?" diye sorulur ve yön, sorunun cevabıdır. Bu kursta yön sabittir: kısıt aşağı, boyut yukarı gider. Yön her ağaçta aynı olduğundan düğümleri birbirinden ayırmaz; boyutlanma ise ayırır. Boyutlanma, o iki akışın sonunda genişliği kimin belirlediğini sınıflar. Yukarıdaki çıktıda `Body` 250 genişliğindedir, çünkü çocuklarının en genişi onu seçmiştir; `Center` ise 328'dir, çünkü ebeveyni ona 328'i dayatmıştır.

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

Odak düğüm, iç boşluğun içindeki karttır: `/Screen/Card`. Kart 200 ister. `Screen` 360'lık sıkı kısıtı alır; iç boşluk bunu iki yandan 16 daraltır ve sıkı kalacak biçimde çocuğa iletir.

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

void main() {
  final tree = pad('Screen', 16, leaf('Card', 200));
  printSizes(measure(tree, 360));
}
```

```text
dayatilan: /Screen genislik=360 kisit=360..360
dayatilan: /Screen/Card genislik=328 kisit=328..328
```

**Bu dersin boyutlanması: dayatılan.** `/Screen/Card` 200 ister ve 328..328 alır. Kısıt sıkıdır, yani en küçük ve en büyük genişlik aynıdır; kartın isteği sonucu değiştirmez. Kart 200 değil, 328 genişliğindedir ve bu genişliği ona `Screen` ile iç boşluk vermiştir.

Aynı kartı bir sütunun içine koyalım. Sütun kısıtı gevşetir ve kart ilk kez kendi isteğini söyleyebilir.

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

void main() {
  final tree = pad('Screen', 16, column('Body', [leaf('Card', 200)]));
  printSizes(measure(tree, 360));
}
```

```text
dayatilan: /Screen genislik=360 kisit=360..360
dayatilan: /Screen/Body genislik=328 kisit=328..328
secilen: /Screen/Body/Card genislik=200 kisit=0..328
```

Kart aynı kart, isteği aynı 200'dür. `Body` sütunu 328 sıkı kısıtı alır ama çocuğuna `0..328` verir; bu aralıkta 200 geçerli bir seçimdir ve genişliği `Card` seçer. Aynı bileşen, aynı istek, iki farklı sonuç sahibi: konum sonucu belirlemiştir.

## Özet

- Bu dersin boyutlanması: dayatılan. `/Screen/Card` 200 ister, iç boşluk sıkı kısıtı 328..328 olarak iletir ve genişliği kısıt belirler.
- Bu arayüz modelinde iç boşluk, hizalama ve sabit boyut birer bileşendir; ağaç bileşenlerin iç içe geçmesiyle kurulur.
- Bileşen değişmez bir tariftir: alanları `final`, kurucusu `const`. Aynı nesne birden çok yerde durabilir ve her yerde o yerin kısıtını alır.
- Kısıtlar aşağı iner, boyutlar yukarı çıkar. Boyutlanma yönü değil, bu iki akışın sonunda genişliğin sahibini sınıflar.
- Aynı kart, iç boşluğun içinde dayatılan, sütunun içinde seçilen olur: sonucu bileşenin isteği değil, konumu belirler.

## Sonraki Adım

Bileşen değişmez bir tariftir; peki ekranda değişen bir şey nasıl gösterilir? **Durumsuz ve Durumlu Bileşenler** dersi, tarifin hiç değişmediği bileşeni ve kendi durumunu taşıyan bileşeni ayırır; durum değişince bir arama kutusunun genişliğini kimin verdiğini ölçer.
