---
title: 'Derleme ve Yayın'
source: 'https://academia.sh/tr/kurslar/flutter/derleme-ve-yayin'
course: Flutter
language: tr
updated: '2026-10-21T05:02:53+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Hata ayıklama, profil ve sürüm derleme kiplerinin amacını ayırt eder'
    - 'Android ve iOS için sürüm çıktısını, imzalamayı ve sürüm kanalını sıralar'
    - 'Aynı sürümü telefon ve tablet genişliğinde ölçüp boyutlanmaları karşılaştırır'
    - 'Kursun on dokuz dersinin boyutlanmasını kapanış tablosundan okur'
---

# Derleme ve Yayın

Bir uygulama yazıldıktan sonra kullanıcıya ulaşana dek derlenir, imzalanır ve bir kanaldan dağıtılır. Ders bu yolu platform başına sıralar; aynı sürümü telefon ve tablet için ölçer ve on dokuz dersin boyutlanmasını tek tabloda toplar.

Önceki ders paketleri ele aldı. Geriye uygulamayı kullanıcıya ulaştırmak kalıyor: derlemek, imzalamak ve bir kanaldan dağıtmak. Bu ders önce bu yolun parçalarını sıralar, sonra aynı sürümü telefon ve tablet için ölçer ve aynı görselin iki hedefte de kırpılıp kırpılmadığına bakar. Ders kursun son dersidir; sonunda on dokuz dersin boyutlanması tek tabloda toplanır.

## Derleme Kipleri

Çerçevenin belgesine göre üç derleme kipi vardır. **Hata ayıklama** kipi geliştirme içindir: sıcak yeniden yükleme yalnız bu kipte çalışır ve doğrulama ifadeleri açıktır. **Profil** kipi başarım çözümlemesi içindir; belge bu kipin emülatörde ve simülatörde kapalı olduğunu söyler, çünkü oradaki davranış gerçek başarımı temsil etmez. **Sürüm** kipi dağıtım içindir: doğrulama ifadeleri kapalıdır, hata ayıklama bilgisi çıkarılır ve derleme hızlı açılış ile küçük paket için en iyilenir.

## Platform Başına Çıktı

Her platform kendi paket biçimini ister. Android belgesi, uygulama mağazasının uygulama paketi biçimini tercih ettiğini söyler; paketi desteklemeyen mağazalar için işlemci mimarisi başına bölünmüş kurulum dosyaları önerilir. iOS derlemesi bir arşiv ve mağazaya yüklenecek bir paket üretir. Sürüm numarası iki platformda da tek yerde bildirilir: noktalı üç sayı sürümü, artı işaretinden sonraki sayı derleme numarasını verir.

```yaml
# Platform kodu: sürüm numarası
version: 1.4.0+12
```

`1.4.0` kullanıcıya görünen sürümdür, `12` derleme numarasıdır. iOS belgesi, mağazaya yapılan her yüklemenin benzersiz bir derleme numarası istediğini söyler.

## İmzalama

İmzalama, paketin kimden geldiğini ve yolda değişmediğini kanıtlar. Android belgesi iki anahtarı ayırır: geliştiricinin yüklerken kullandığı **yükleme anahtarı** ve kullanıcıya ulaşan paketi imzalayan **uygulama imzalama anahtarı**. Yükleme anahtarının saklandığı dosya ve ona anahtar bilgisini veren yapılandırma dosyası gizli tutulur; belge ikisinin de herkese açık sürüm denetimine eklenmemesini ister. iOS tarafında imzalama ve hazırlık profilini geliştirme ortamı yönetebilir; belgeye göre bu ayar varsayılan olarak açıktır ve geliştirici kayıtlı hesabına bağlı bir takım seçer.

## Sürüm Kanalı

İmzalı paket doğrudan herkese gitmek zorunda değildir. İki mağazanın yayın konsolu da paketi önce küçük bir gruba açan bir iç sınama kanalı sunar. Grup sürümü beğenirse paket inceleme için gönderilir ve onaydan sonra yayımlanır. İnceleme süresi bu derste yazılmaz.

Kanal, aynı derlemenin **kime** ulaştığını belirler. Hangi genişlikte çizildiğini belirlemez; onu hedefin ekranı ve ağaçtaki kısıtlar belirler.

## Ölçülen Katman

Kursun her dersi aynı düz Dart modelini kullanır. Model bir bileşen ağacına ekran genişliğini verir ve her düğümün genişliğini kimin belirlediğini dört adla kaydeder. Aşağıdaki dosya çıktı üretmez; ölçülen bloklar onu içe 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}');
}
```

Bu derste model, bu boyutlanmanın kuralını kurar; model çerçevenin kendisi değildir. Modelde sürüm, iki ekran genişliğinde ölçülen bir ağaçtır.

## İki Hedef

Sürüm iki hedef için ölçülür: 360 birimlik telefon ve 768 birimlik tablet. Ağaçta 900 birim isteyen büyük bir `Hero` görseli vardır. İşlev derleme kipini alır ve yalnız geçerli olup olmadığına bakar; kip ağacı değiştirmez.

```dart
// release_model.dart — surum hedefine gore ayni agaci kurar
import 'sizing.dart';

const modes = ['debug', 'profile', 'release'];
const targetOrder = ['telefon', 'tablet'];
const targets = {'telefon': 360, 'tablet': 768};

Part buildRelease(String mode) {
  if (!modes.contains(mode)) throw ArgumentError('bilinmeyen kip');
  return pad('Screen', 16, column('Body', [
    leaf('Title', 120),
    leaf('Banner', 500),
    leaf('Hero', 900),
    row('Toolbar', [
      leaf('Back', 48),
      flex('Search', 1, fill('Field')),
      leaf('Menu', 48),
    ]),
  ]));
}
```

İlk ölçüm telefonu tüm kayıtla basar.

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

void main() {
  printSizes(measure(buildRelease('release'), targets['telefon']!));
}
```

```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
kirpilan: /Screen/Body/Hero 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
```

Ekrandan iç boşluk düşünce gövdeye 328..328 iner. 120 isteyen `Title` aralığa sığar, seçilen olur. 500 isteyen `Banner` ile 900 isteyen `Hero` 328'e çekilir, kırpılan olur. Araç çubuğu istek bildirmez ve yayılır, esnetilen olur.

Şimdi aynı ağaç tablet genişliğinde ölçülür.

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

void main() {
  printSizes(measure(buildRelease('release'), targets['tablet']!));
}
```

```text
dayatilan: /Screen genislik=768 kisit=768..768
dayatilan: /Screen/Body genislik=736 kisit=736..736
secilen: /Screen/Body/Title genislik=120 kisit=0..736
secilen: /Screen/Body/Banner genislik=500 kisit=0..736
kirpilan: /Screen/Body/Hero genislik=736 kisit=0..736
esnetilen: /Screen/Body/Toolbar genislik=736 kisit=0..736
secilen: /Screen/Body/Toolbar/Back genislik=48 kisit=0..sonsuz
dayatilan: /Screen/Body/Toolbar/Search genislik=640 kisit=640..640
dayatilan: /Screen/Body/Toolbar/Search/Field genislik=640 kisit=640..640
secilen: /Screen/Body/Toolbar/Menu genislik=48 kisit=0..sonsuz
```

Gövde 736 olur. `Banner` bu kez 500 ile aralığa sığar, seçilen olur. `Hero` yine aralığı aşar ve 736'ya çekilir. Ağaç da kural da aynıdır; değişen yalnız kısıttır.

## İki Hedefin Sayımı

Sayım iki hedefte ayrı basılır.

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

void main() {
  for (final name in targetOrder) {
    print('hedef=$name');
    countSizings(measure(buildRelease('release'), targets[name]!));
  }
}
```

```text
hedef=telefon
dayatilan: 4
secilen: 3
kirpilan: 2
esnetilen: 1
ebeveyn: 7
hedef=tablet
dayatilan: 4
secilen: 4
kirpilan: 1
esnetilen: 1
ebeveyn: 6
```

Telefonda kırpılan 2 düğüm vardır, tablette 1; ebeveyn 7'den 6'ya iner. Fark `Banner`'dan gelir. `Hero`'nun sınıfı iki hedefte de değişmez.

## Derleme Kipi

Üç kip için aynı ağaç iki hedefte ölçülür; her kayıt hata ayıklama kipininkiyle karşılaştırılır.

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

List<String> lines(List<Sizing> log) =>
    [for (final entry in log) '${entry.sizing} ${entry.path} ${entry.width}'];

void main() {
  for (final name in targetOrder) {
    final base = lines(measure(buildRelease('debug'), targets[name]!));
    for (final mode in modes) {
      final same = lines(measure(buildRelease(mode), targets[name]!));
      print('hedef=$name kip=$mode kayit=${same.length} ayni=${same.join('|') == base.join('|')}');
    }
  }
}
```

```text
hedef=telefon kip=debug kayit=10 ayni=true
hedef=telefon kip=profile kayit=10 ayni=true
hedef=telefon kip=release kayit=10 ayni=true
hedef=tablet kip=debug kayit=10 ayni=true
hedef=tablet kip=profile kayit=10 ayni=true
hedef=tablet kip=release kayit=10 ayni=true
```

Her kipte ve her hedefte kayıt, hata ayıklama kipininkiyle aynıdır.

## Yaygın Yanılgı

**Sürüm kipinde derlemek bileşenlerin genişliğini değiştirir.** Modelde değiştirmez: kuralın girdisi kısıt ile istektir, kip değil. Sınıfı değiştiren, hedefin ekran genişliğidir.

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

Son ölçüm yalnız `Hero` düğümünü iki hedefte basar.

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

void main() {
  for (final name in targetOrder) {
    final log = measure(buildRelease('release'), targets[name]!);
    final hero = log.firstWhere((entry) => entry.path == '/Screen/Body/Hero');
    print('hedef=$name ${hero.sizing} genislik=${hero.width} kisit=${hero.limit.bounds()}');
  }
}
```

```text
hedef=telefon kirpilan genislik=328 kisit=0..328
hedef=tablet kirpilan genislik=736 kisit=0..736
```

Odak düğüm `/Screen/Body/Hero`. 900 birimlik istek telefonda `0..328`, tablette `0..736` aralığını aşar. İki hedefte de genişliği görselin isteği değil, aralığın üst sınırı belirler. Dersin boyutlanması bu yüzden kırpılandır.

**KY1.** Model, bu boyutlanmanın kuralını kurar; model çerçevenin kendisi değildir. Modelde derleme kipi yalnız bir addır. Gerçek derleme, imzalama, paket biçimleri ve mağaza yüklemesi modelde yoktur; ayrıntısı çerçevenin ve mağazaların belgelerindedir.

## Kapanış Tablosu

Tablo kursun on dokuz dersini sırayla toplar; boyutlanmalar kaynak derslerin özetlerinden alınmıştır.

| Ders | Ölçülen düzen | Boyutlanma | Sınırlayıcı ölçüm |
|---|---|---|---|
| Dart Temelleri | Başlık, afiş ve araç çubuğu | kırpılan | `Banner` 400 ister, 360 ekranda 328 alır |
| Eşzamansız Dart | Değer gelmeden ve geldikten sonra kurulan ağaç | seçilen | `Avatar` 200 gelince isteğini seçer |
| Bileşen Kavramı | İç boşluğun içinde kart | dayatılan | `Card` 200 ister, 328..328 alır |
| Durumsuz ve Durumlu Bileşenler | Kapalı ve açık arama kutusu | esnetilen | Açık `Search` 480'de 448, 320'de 288 olur |
| Üç Ağaç Modeli | Ortalanmış logo | seçilen | `Logo` iki ekranda da 96'yı seçer |
| Düzen Bileşenleri | Sıkı ve gevşek esnek çocuk | esnetilen | Gevşek çocuğun metni 360'ta 280, 480'de 400, 320'de 240 olur |
| Platform Bileşen Aileleri | Sabit yuvada eylem düğmesi | dayatılan | `Button` iki ailede de 120 alır |
| Durum Yönetimi Yaklaşımları | Başlıkta sayaç rozeti | seçilen | `Badge` 30'dan 66'ya büyür, seçilen kalır |
| Gezinme ve Yönlendirme | Ekran genişliğinde gezinme kabı | dayatılan | `Orders` 200 ister, 360 alır |
| Ağ ve Dizileştirme | Sunucudan gelen afiş | kırpılan | 1200 isteyen `Banner` 328'e çekilir |
| Yerel Depolama | Tercihle kurulan kart | esnetilen | `full` tercihinde `Card` 328 alır |
| Varlıklar ve Yazı Tipleri | Ölçekli düğme | kırpılan | Ölçek 2'de `Button` 344 ister, 328 alır |
| Animasyonlar | Beş karede ilerleyen panel | seçilen | Son karede `Panel` 240'ı seçer |
| Platform Kanalları | Gömülü platform görünümünün yuvası | dayatılan | `PlatformView` iletideki isteğe bakmadan 240 alır |
| Test Türleri | 400 ve 320 birimlik test yüzeyi | kırpılan | `Chart` 320'de 300 ister, 288 alır |
| Geliştirici Araçları | `Center` içindeki `Chip` | esnetilen | `Center` 328'e yayılır, `Chip` 80'i seçer |
| Başarım Ayarı | Sabit panelde sayaç | dayatılan | `Counter` 200 alır, değişen düğüm 0 |
| Paket Yönetimi | İki sürümlü derecelendirme | seçilen | `Rating` yeni sürümde 112'yi seçer |
| Derleme ve Yayın | Telefon ve tablet için aynı sürüm | kırpılan | `Hero` 900 ister, 328 ve 736 alır |

Dersler arasında dağılım şöyledir: dayatılan 5, seçilen 5, kırpılan 5, esnetilen 4. Toplam on dokuz derstir.

Bu sayım kursun açılışındaki sayımla aynı şey değildir. Açılışta çekirdeğin tek bir düzeni dokuz kayıtla basılmıştı: dayatılan 4, seçilen 3, kırpılan 1, esnetilen 1 ve ebeveyn 6. Çekirdek tek düzenin dokuz kaydını, tablo on dokuz dersi sayar.

## Özet

- Bu dersin boyutlanması: kırpılan. Odak düğüm `/Screen/Body/Hero`'dur: 900 birim isteyen görsel telefonda 328'e, tablette 736'ya çekilir.
- Hata ayıklama kipi geliştirme, profil kipi başarım çözümlemesi, sürüm kipi dağıtım içindir; platform başına paket, imzalama ve sürüm kanalı sürümü kullanıcıya ulaştırır.
- Aynı sürüm iki hedefte ölçüldü: telefonda kırpılan 2, tablette 1. `Banner` telefonda kırpılırken tablette seçilen oldu, `Hero` iki hedefte de kırpılan kaldı.
- Üç derleme kipinde de kayıt hata ayıklama kipininkiyle aynı çıktı.
- Kapanış tablosunda on dokuz dersin dağılımı dayatılan 5, seçilen 5, kırpılan 5, esnetilen 4'tür.

## Sonraki Adım

Bu ders kursun son dersidir. On dokuz ders boyunca tek soru sorduk: bu bileşenin genişliğini kim belirliyor. Sıradaki konu alanı çapraz platform üretim pratiğidir.
