Ders 19 / 19
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.
Yayın Güncelleme
Bu derste
- 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
İçindekiler
Ö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.
# 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.
// 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.
// 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.
// main.dart
import 'sizing.dart';
import 'release_model.dart';
void main() {
printSizes(measure(buildRelease('release'), targets['telefon']!));
}
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.
// main.dart
import 'sizing.dart';
import 'release_model.dart';
void main() {
printSizes(measure(buildRelease('release'), targets['tablet']!));
}
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.
// 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]!));
}
}
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.
// 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('|')}');
}
}
}
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.
// 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()}');
}
}
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.
Bannertelefonda kırpılırken tablette seçilen oldu,Heroiki 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.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.