Ders 16 / 19
Geliştirici Araçları
Bileşen inceleyicisi, düzen inceleme ve başarım görünümü bir ağacı çalışırken gösterir. Ders inceleyicinin gösterdiği kısıt ve genişliği ölçülen kayıtla eşler.
Yayın Güncelleme
Bu derste
- Bileşen inceleyicisinin, düzen incelemenin ve başarım görünümünün neyi gösterdiğini ayırır
- İnceleyicideki kısıt ve genişlik değerlerini ölçülen kayıtla eşler
- Beklenmedik geniş görünen bir bileşenin nedenini kısıttan okur
- Sıcak yeniden yüklemenin ağacı yeniden kurduğunu, durumu korumasını açıklar
İçindekiler
Önceki derste bir test kaldı ve hatanın hangi yolda olduğu söylendi. Neden böyle olduğunu görmek ise başka bir işin konusu: ağacı çalışırken açıp bakmak. Bu ders, çerçevenin geliştirme araçlarını boyutlanma ekseniyle okur. Bir araç ekranda bir bileşenin genişliğini gösterdiğinde, aslında kursun sorusunu cevaplıyor: bu genişliği kim belirledi.
Dört Araç
Bileşen inceleyicisi çalışan uygulamanın bileşen ağacını gösterir. Bir bileşen seçildiğinde yalnız onun özelliklerini değil, aldığı kısıtı ve ortaya çıkan boyutu da görürsünüz. Çerçevenin belgesi bu konuda şunu söyler: “The constraints flow down the tree, and the sizes flow back up.” Yani kısıtlar ağaçta aşağı iner, boyutlar yukarı çıkar; düzen sorunlarında bakılacak iki alan size ve constraints alanlarıdır.
Düzen inceleme, inceleyicinin içinde esnek bileşenler için ayrı bir görünümdür. Satır, sütun ya da esnek çocuklarla ilgili bir bileşen seçildiğinde esnek çarpanı, esnek uyumu ve kısıtlar görselleştirilir; kısıt ihlalleri kırmızıyla, taşma hataları sarı şeritli bir desenle gösterilir. Bu görünümde değiştirdiğiniz değerler kaynak koda yazılmaz ve sıcak yeniden yüklemede geri alınır.
Başarım görünümü genişlik değil zaman gösterir: kareleri bir çizelgede dizer, yavaş kalan kareleri kırmızı işaretler ve bir karede hangi işin yapıldığını olay çizelgesinde açar. Bileşen yapılarının hangi anda kurulduğunu gösteren bir seçenek de vardır. Belge, ölçümün geliştirme kipinde değil başarım ölçüm kipinde alınmasını önerir, çünkü geliştirme kipindeki kare süreleri yayın sürümünün başarımını yansıtmaz. Bu dersin sayıları süre içermez; başarım görünümü bir sonraki dersin konusudur.
Sıcak yeniden yükleme değişen kaynak kodu çalışan uygulamaya yollar ve ağacı yeniden kurar. main ve initState yeniden çalışmaz; uygulamanın durumu korunur. Belgeye göre bu yalnız geliştirme kipinde çalışır. Durum korunduğu için kaynak koddaki bir değişiklik ekranda görünmeyebilir: örneğin başlangıç değerini değiştirmek, değer zaten kurulmuşsa bir etki yaratmaz. O zaman uygulamayı baştan başlatan sıcak yeniden başlatma gerekir.
Ö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 inceleyicinin bir bileşen için gösterdiği iki değer, printSizes çıktısındaki genislik ve kisit alanlarıdır. Model başarım görünümünü ya da sıcak yeniden yüklemeyi taşımaz; ikincisine ağacı yeniden ölçerek yaklaşılır.
İncelenen Ekran
Ekranın ağacı bir sütundur: 150 birim isteyen bir etiket ve bir Center içinde 80 birim isteyen bir Chip. Ağaç, çipin genişliğini alan bir işlevle kurulur.
// panel_model.dart — incelenen ekranin bilesen agaci
import 'sizing.dart';
Part buildPanel(int chipWidth) => pad('Screen', 16, column('Body', [
leaf('Label', 150),
center('Center', leaf('Chip', chipWidth)),
]));
Bu ağacın kaynak karşılığında Center yalnız çipi sarmalar. Geliştirici çipin 80 birim olduğunu bilir ve ona ayrılan alanın da yaklaşık o kadar olmasını bekler. Bu beklenti, inceleyiciye bakılana kadar sürer.
Beklenmedik Geniş Görünen Bileşen
Ekran 360 birim genişliğinde. Aşağıdaki blok kaydın tamamını basar; her satır, inceleyicinin bir bileşen için göstereceği değerlerdir.
// main.dart
import 'sizing.dart';
import 'panel_model.dart';
void main() {
final log = measure(buildPanel(80), 360);
printSizes(log);
}
dayatilan: /Screen genislik=360 kisit=360..360 dayatilan: /Screen/Body genislik=328 kisit=328..328 secilen: /Screen/Body/Label genislik=150 kisit=0..328 esnetilen: /Screen/Body/Center genislik=328 kisit=0..328 secilen: /Screen/Body/Center/Chip genislik=80 kisit=0..328
Chip 80 birim ister ve 80 birim alır: seçilen. Center ise 328 birimdir, çipten çok daha geniş. Sınırlı bir kısıtta ortalayan bileşen isteğini bildirmez; kendisine verilen en geniş değere yayılır. Kursun dilinde bu, esnetilen boyutlanmadır. İnceleyicide Center seçildiğinde göreceğiniz size 328, constraints ise 0 ile 328 arasıdır. Geliştirici çipe bakıp Center’ın da dar olmasını bekliyordu; oysa genişliği çip değil, ebeveynin kısıtı belirledi.
Kısıt Kardeşe de Aynen İner
M11/K05’te bir işlemin kaynağa ne bıraktığı sorulurdu: kaynak bir kez tüketildi mi, ardından hâli nedir. Düzen kısıtı için bu soru geçersizdir, çünkü düzen kısıtı tüketilmez. Sütun gövdeye gelen kısıtı gevşetir ve her çocuğa aynı gevşek kısıtı verir; etiket aldığında Center’ın payı eksilmez. Aşağıdaki blok, 0..328 kısıtını alan tüm bileşenleri listeler.
// main.dart
import 'sizing.dart';
import 'panel_model.dart';
void main() {
final log = measure(buildPanel(80), 360);
printSizes(log.where((entry) => entry.limit.bounds() == '0..328').toList());
}
secilen: /Screen/Body/Label genislik=150 kisit=0..328 esnetilen: /Screen/Body/Center genislik=328 kisit=0..328 secilen: /Screen/Body/Center/Chip genislik=80 kisit=0..328
Label ve Center aynı kısıtı alır. Chip de aynı kısıtı alır, çünkü ortalayan bileşen kısıtı gevşetir ama üst sınırı korur. İnceleyicide yan yana duran iki kardeşin kısıt alanı bu yüzden aynı görünür. Kısıtı daraltan tek şey iç boşluktur ve o ağaçta yalnız Screen düzeyindedir.
Taşma ve Düzen İnceleme
Çipin isteği kısıtı aşarsa ne olur? Aşağıdaki blok çipi 400 birim yapar ve Center altındaki kayıtlara bakar.
// main.dart
import 'sizing.dart';
import 'panel_model.dart';
void main() {
final log = measure(buildPanel(400), 360);
printSizes(log.where((entry) => entry.path.startsWith('/Screen/Body/Center')).toList());
}
esnetilen: /Screen/Body/Center genislik=328 kisit=0..328 kirpilan: /Screen/Body/Center/Chip genislik=328 kisit=0..328
Çip 400 ister, izin verilen en çok 328’dir. Genişliği 328’e çekilir ve kırpılan olur. Gerçek çerçevede aynı durum bir taşma hatası olarak görünür: düzen incelemenin sarı şeritli deseni tam burada işe yarar. Modelde taşma bir görüntü değil, bir sınıftır; kayıt yalnız isteğin aralık dışında kaldığını söyler.
Sıcak Yeniden Yükleme Ağacı Yeniden Ölçer
Sıcak yeniden yükleme ağacı yeniden kurar. Modelde bunun karşılığı, yeni durumla aynı ağaç işlevini çağırıp ağacı yeniden ölçmektir. Aşağıdaki blok çip genişliğini 80’den 400’e çevirir ve iki ölçümün sayımını yan yana basar.
// main.dart
import 'sizing.dart';
import 'panel_model.dart';
void main() {
for (final chip in [80, 400]) {
print('chip=$chip');
countSizings(measure(buildPanel(chip), 360));
}
}
chip=80 dayatilan: 2 secilen: 2 kirpilan: 0 esnetilen: 1 ebeveyn: 3 chip=400 dayatilan: 2 secilen: 1 kirpilan: 1 esnetilen: 1 ebeveyn: 4
Çip değeri değişince bir bileşen seçilenden kırpılana geçer; ebeveyn sayısı üçten dörde çıkar. Gerçek sıcak yeniden yüklemede uygulamanın durumu korunur; modelde ise yeniden ölçme her seferinde sıfırdan bir ağaç kurar. Modelin korunan durumu yoktur.
Aracın Gösterdiğini Okumak
Bir inceleyiciye bakarken izlenecek sıra basittir. Önce bileşeni seç. Sonra aldığı kısıta bak: sıkıysa boyutu ebeveyn dayatmıştır. Sınırlı ve gevşekse bileşenin kendi isteğine bak: istek aralığa sığıyorsa seçilen, sığmıyorsa kırpılan. İstek yoksa ve üst sınır sonluysa genişlik üst sınırdır: esnetilen. Bu üç soru dört boyutlanmanın hepsini ayırır.
Aşağıdaki iki parça, aynı sorunun çerçeve kodundaki karşılığıdır. İlki ekranda her bileşenin sınırlarını çizen bir bayrağı açar; ikincisi ortalanmış bir sarmalayıcıyı gösterir.
// Platform kodu: duzen sinirlarini cizmek
void main() {
debugPaintSizeEnabled = true;
runApp(const ExampleScreen());
}
Bu bayrak her bileşenin sınırını çizer ve yalnız geliştirme kipinde etkilidir.
// Platform kodu: ortalanmis sarmalayici
Column(
children: [
const Text('Etiket'),
Center(child: Chip(label: Text('Etiket'))),
],
)
Bu iki satırlık düzende Center kendisine verilen genişliğe yayılır. İnceleyici bu yayılmayı gösterir; modelde de aynı yayılma Center düğümü için ölçülmüştür.
Yaygın Yanılgı
İnceleyicide geniş görünen bileşenin içeriği de geniştir. Böyle değildir. Center 328 birimdir ama içindeki çip 80 birimdir. Bileşenin sınırı ile içeriği ayrı şeylerdir; inceleyici sınırı gösterir, içeriğin kendi isteğini ayrıca okumak gerekir.
Sınırlayıcı Ölçüm
Son ölçüm, kuralın inceleyici gibi yalnız o anki kısıtı gördüğünü gösterir. Ekran 360 birimden 240 birime inince Center’ın genişliği çipe değil ekrana bağlı kalır.
// main.dart
import 'sizing.dart';
import 'panel_model.dart';
void main() {
for (final screen in [360, 240]) {
final log = measure(buildPanel(80), screen);
printSizes(log.where((entry) => entry.path == '/Screen/Body/Center').toList());
}
}
esnetilen: /Screen/Body/Center genislik=328 kisit=0..328 esnetilen: /Screen/Body/Center genislik=208 kisit=0..208
Odak düğüm /Screen/Body/Center. İki ekranda da esnetilen çıkar; genişliği 328’den 208’e iner, çip ise iki ölçümde de 80 birimdir. Dersin boyutlanması bu yüzden esnetilendir: ortalayan bileşenin genişliğini ekran belirler.
KY1. Model, bu boyutlanmanın kuralını kurar; model çerçevenin kendisi değildir. Modelde inceleyicinin değeri bir kayıt satırıdır. Gerçek inceleyici bir bileşenin yalnız kısıt ve boyutunu değil, çizim nesnesinin özelliklerini ve esnek çarpanını da gösterir; bunlar modelde yoktur. Başarım görünümünün kare çizelgesi ve sıcak yeniden yüklemenin korunan durumu da modelde ölçülmez.
Özet
- Bu dersin boyutlanması: esnetilen.
Centersınırlı kısıtta isteğini bildirmedi ve 328 birime yayıldı; içindeki çip 80 birimde seçilen kaldı. - Bileşen inceleyicisi kısıt ve boyutu, düzen inceleme esnek bileşenleri, başarım görünümü kareleri gösterir.
- İnceleyicinin gösterdiği kısıt ve genişlik, modelde
printSizessatırınınkisitvegenislikalanlarıdır. - Düzen kısıtı tüketilmez: aynı gevşek kısıt
Label,CenterveChipiçin de0..328olarak indi. - Sıcak yeniden yükleme ağacı yeniden kurar ve durumu korur; modelde karşılığı ağacı yeni durumla yeniden ölçmektir.
Sonraki Adım
İnceleyici bir sorunu gösterir, ama çoğu zaman sorun yanlış genişlik değil gereksiz iştir. Sıradaki ders, Başarım Ayarı, ağaçta gereksiz yeniden kurmayı azaltan yöntemleri ve bir bileşenin değişikliğinin yukarıya sızmadığı yeniden düzen sınırını ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.