---
title: 'Varlıklar ve Yazı Tipleri'
source: 'https://academia.sh/tr/kurslar/flutter/varliklar-ve-yazi-tipleri'
course: Flutter
language: tr
updated: '2026-10-21T05:02:52+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Varlık bildirimini, görsel çözünürlük çeşitlerini ve yazı tipi ölçeğini birbirinden ayırır'
    - 'Yazı tipi ölçeğinin bir düğmenin isteğini büyüttüğünü ölçer'
    - "Ölçek 1'de düğmenin seçilen, ölçek 2'de kırpılan olduğunu gösterir"
    - 'Aralık genişleyince aynı isteğin yeniden seçilen olabildiğini gösterir'
---

# Varlıklar ve Yazı Tipleri

Yazı tipi ölçeği bir düğmenin isteğini büyütür ve büyüyen istek aralığı aşınca düğme kırpılır. Ders ölçeği 1, 2 ve 3 verir ve aralığı genişletmenin sonucu nasıl değiştirdiğini ölçer.

Önceki ders, kayıtlı bir tercihin bir kartın isteğini kaldırdığını ve kartı esnetilen yaptığını gösterdi. Bu ders tersini yapan bir girdiyi ölçer: isteği büyüten bir girdiyi. Kullanıcı sistem ayarlarından yazı boyutunu büyüttüğünde uygulamanın her yazısı ve yazıyı taşıyan her düğme büyür. Kursun sorusu yine aynıdır: **bu bileşenin genişliğini kim belirliyor?** Ders yanıtı, ölçekle büyüyen bir düğmenin üzerinden arar.

## Varlıklar, Çözünürlük Çeşitleri ve Yazı Tipleri

Bu derste **varlık (asset)** uygulamayla birlikte paketlenen bir dosyadır: bir görsel, bir yazı tipi dosyası ya da bir ses. Veri modelindeki varlık (entity) ile ilgisi yoktur. Uygulama yapılandırmasında varlıkların hangi dosyalar olduğu bildirilir; bildirilmeyen dosya pakete girmez.

Görseller ekranın yoğunluğuna göre çözünürlük çeşitleriyle gelir: aynı görselin 1.0x, 2.0x gibi büyütülmüş dosyaları yan yana durur ve çerçeve ekrana uygun olanı seçer. Yazı tipleri de varlıktır; bir aile adı altında bildirilir.

## Çekirdek

Her ders aynı çekirdeği kullanır. `measure` ağacı ekran genişliğinde sıkı bir kısıtla dolaşır, her düğüm için bir kayıt üretir ve kayıttaki boyutlanmayı bir kural verir: kısıt sıkıysa **dayatılan**, isteğin aralık içinde kaldığı durumda **seçilen**, istek aralığın dışındaysa ya da yoksa ve üst sınır yoksa **kırpılan**, istek yok ve üst sınır sonluysa **esnetilen**. Çıktıda dört ad ASCII yazılır: `dayatilan`, `secilen`, `kirpilan`, `esnetilen`. `ebeveyn:` satırı dayatılan, kırpılan ve esnetilenin toplamıdır; genişliği bileşenin kendi isteği olmayan kayıtları sayar.

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

## Çerçeve Tarafı

Aşağıdaki parça yapılandırmayı gösterir. Paket adı kurgusaldır ve parça ölçülmez.

```yaml
# Platform kodu: varlik bildirimi
name: demo_shop
flutter:
  assets:
    - assets/images/logo.png
  fonts:
    - family: Body
      fonts:
        - asset: assets/fonts/body-regular.ttf
```

`assets` görsel dosyalarını, `fonts` yazı tipi ailesini ve dosyasını bildirir. Çözünürlük çeşitleri `assets/images/2.0x/logo.png` gibi bir alt dizinde yan yana durur ve ayrıca listelenmez. Bildirimden sonra kod, yazı tipinin ölçeğini kullanıcının ayarından okur.

```dart
// Platform kodu: yazi tipi olcegi
double labelSize(BuildContext context) {
  final scaler = MediaQuery.textScalerOf(context);
  return scaler.scale(14);
}
```

`textScalerOf` kullanıcının ayarladığı ölçeği verir ve `scale` temel boyutu o ölçekle çarpar. Yazı büyüyünce yazıyı taşıyan düğmenin isteği de büyür. Bu dersin sorusu o büyümenin aralıkla buluştuğu yerdir.

## Ölçekten İsteğe

Modelin düğmesi bir etiketin genişliğini taşır ve etraftaki çerçeve payını ekler. Etiketin genişliği ölçekle çarpılan bir tamsayıdır: temel genişlik 160 birimse ölçek 1'de etiket 160, ölçek 2'de 320 birim genişliğindedir. Çerçeve payı sabit 24 birimdir. İsteği hesaplayan işlev ve temel genişlikler bir yardımcı dosyada durur; yardımcı dosya çıktıya hiçbir şey basmaz.

```dart
// fonts.dart — etiket genisligi ve olcege bagli istek
const labelBase = {'Kaydet': 160, 'Tamam': 80};

int frameWidth(int base, int scale) => base * scale + 24;
```

`buildForm` ölçeği alır ve düğmeyi bu istekle kurar. Ağaç ekranın içinde bir sütun, sütunun içinde başlık ile düğmedir. İlk ölçüm ölçek 1'dedir.

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

Part buildForm(String label, int scale) {
  return pad('Screen', 16, column('Body', [
    leaf('Title', 120),
    leaf('Button', frameWidth(labelBase[label]!, scale)),
  ]));
}

void main() {
  final log = measure(buildForm('Kaydet', 1), 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
secilen: /Screen/Body/Button genislik=184 kisit=0..328
dayatilan: 2
secilen: 2
kirpilan: 0
esnetilen: 0
ebeveyn: 2
```

Ölçek 1'de düğme 184 ister; aralık `0..328` olduğu için istek aralığın içinde kalır ve `/Screen/Body/Button` **seçilen**dir. Başlık da seçilendir. Sayım iki dayatılan, iki seçilen gösterir; kırpılan ve esnetilen yoktur, ebeveyn ikidir.

## Ölçek Büyüdükçe

Şimdi ölçeği 1'den 3'e artıralım ve iki etiketi karşılaştıralım. `Kaydet` uzun bir etikettir; `Tamam` kısadır.

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

Part buildForm(String label, int scale) {
  return pad('Screen', 16, column('Body', [
    leaf('Title', 120),
    leaf('Button', frameWidth(labelBase[label]!, scale)),
  ]));
}

void main() {
  for (final label in ['Kaydet', 'Tamam']) {
    for (final scale in [1, 2, 3]) {
      final log = measure(buildForm(label, scale), 360);
      final button = log.firstWhere((entry) => entry.path == '/Screen/Body/Button');
      print('$label olcek=$scale istek=${frameWidth(labelBase[label]!, scale)} ${button.sizing} genislik=${button.width}');
    }
  }
}
```

```text
Kaydet olcek=1 istek=184 secilen genislik=184
Kaydet olcek=2 istek=344 kirpilan genislik=328
Kaydet olcek=3 istek=504 kirpilan genislik=328
Tamam olcek=1 istek=104 secilen genislik=104
Tamam olcek=2 istek=184 secilen genislik=184
Tamam olcek=3 istek=264 secilen genislik=264
```

`Kaydet` ölçek 1'de 184 ister ve seçilen kalır. Ölçek 2'de istek 344'e çıkar, 328'i aşar ve kayıt `kirpilan` olur; genişlik 328'dir. Ölçek 3'te istek 504'tür ve kayıt yine kırpılan, genişlik yine 328'dir. `Tamam` üç ölçekte de 104, 184 ve 264 ister ve üçünde de seçilen kalır. Ölçek tek başına boyutlanmayı belirlemez: ölçek isteği büyütür, boyutlanmayı isteğin aralıkla ilişkisi belirler.

**Bu işlem yinelenirse ya da sırası değişirse sonuç değişir mi** sorusu bir işlemin duyarlılığını arar. Bu ders onu sormaz. Yazı tipi ölçeği bir girdidir ve isteği büyütür; boyutlanma sonucun yinelemeye dayanıklılığını değil, büyüyen isteği aralığa kimin sığdırdığını sınıflar.

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

Ölçek 2'de `Kaydet` kırpılandı. Aralık genişlerse aynı istek sığabilir mi? Aynı ağacı iki ekran genişliğinde ölçelim: 360'ta aralık `0..328`, 480'de `0..448`'dir. İstek 344'te sabit kalır.

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

Part buildForm(String label, int scale) {
  return pad('Screen', 16, column('Body', [
    leaf('Title', 120),
    leaf('Button', frameWidth(labelBase[label]!, scale)),
  ]));
}

void main() {
  for (final screen in [360, 480]) {
    final log = measure(buildForm('Kaydet', 2), screen);
    final button = log.firstWhere((entry) => entry.path == '/Screen/Body/Button');
    print('ekran=$screen olcek=2 istek=344 ${button.sizing} genislik=${button.width} kisit=${button.limit.bounds()}');
  }
}
```

```text
ekran=360 olcek=2 istek=344 kirpilan genislik=328 kisit=0..328
ekran=480 olcek=2 istek=344 secilen genislik=344 kisit=0..448
```

Odak düğüm, ölçek 2'deki `/Screen/Body/Button`'dır. 360'lık ekranda istek 344, aralık `0..328` olduğundan kayıt `kirpilan`, genişlik 328'dir. 480'lik ekranda aralık `0..448` olur, istek içinde kalır ve kayıt `secilen`, genişlik 344'tür. Ekran ya da ölçek değiştiğinde sonuç değişebilir; sonucu değiştiren yazı tipi ölçeğinin kendisi değil, büyüyen isteğin aralığa sığıp sığmadığıdır.

**UY1.** Model yalnız genişliği ölçer. Etiketin satıra kırılması, taşan metnin görüntüsü, düğmenin yüksekliği ve yazı tipinin gerçek harf ölçüleri modelde yoktur. Model, bu boyutlanmanın kuralını kurar; model çerçevenin kendisi değildir. İsteğin ölçekle doğrusal büyümesi ve 24 birimlik sabit çerçeve payı dersin varsayımlarıdır; gerçek bir etiketin genişliği yazı tipinin ölçülerinden gelir.

Sonuç şudur: büyük yazı ayarı olan bir kullanıcıda düğme kırpılıyorsa çözüm ölçeği bastırmak değildir. Seçenekler iki yöndedir: etiketi kısaltarak isteği küçültmek ya da düğmeyi aralığı geniş bir yere taşıyarak aralığı genişletmek.

## Özet

- Bu dersin boyutlanması: kırpılan. Odak düğüm ölçek 2'deki `/Screen/Body/Button`'dır: 344 istek, `0..328` aralığını aştığı için 328'e çekilir.
- Varlık bildirimi paketlenecek dosyaları söyler; yazı tipi ölçeği ise kullanıcının ayarından okunur ve etiketin genişliğini büyütür.
- `Kaydet` ölçek 1'de 184 ile seçilen, ölçek 2 ve 3'te 344 ve 504 ile kırpılan çıktı; `Tamam` üç ölçekte de seçilen kaldı.
- Ekran 480'e çıkınca aralık `0..448` oldu ve 344 isteği yeniden seçilen çıktı; genişliği ölçek değil, istekle aralığın ilişkisi belirledi.
- Kural ölçeğin kendisini değil, büyüyen isteğin sığıp sığmadığını görür.

## Sonraki Adım

Bu derste isteği ölçek büyüttü ve sonuç iki ölçüm arasında değişti. Sıradaki ders, `Animasyonlar`, isteğin zamanla küçük adımlarla büyüdüğü durumu ölçer: her karede ağaç yeniden ölçülür ve her karede genişliğin sahibine bakılır.
