İçeriğe geç
academia.sh

Ders 14 / 19

Platform Kanalları

Platform tarafından kaç ileti gelirse gelsin gömülü görünümün genişliğini çerçeve dayatır. Ders üç farklı istekle ve yinelenen iletilerle bunu ölçer.

Yayın Güncelleme

Bu derste

  • Platform kanalını ve kanaldan gelen iletiyi tanımlar
  • Gömülü platform görünümünün genişliğini yuvanın dayattığını ölçer
  • İletideki istek değiştiğinde kaydın dayatılan kaldığını gösterir
  • Yinelenen iletilerin boyutlanma toplamını değiştirmediğini doğrular
İçindekiler

Önceki ders, çerçevenin animasyon karelerini ve her karede seçilen paneli ölçtü. Bu ders ağacın dışına, yerel platforma uzanır: uygulama bazen platformun kendi ekran parçasını gömmek ya da platformdan bir bilgi almak ister. Bu konuşma bir kanaldan geçer. Kanaldan gelen bir ileti gömülü görünümün genişliğini de önerebilir. Kursun sorusu yine aynıdır: bu bileşenin genişliğini kim belirliyor? Ders yanıtı, görünümü saran yuvada arar.

Kanal, İleti ve Gömülü Görünüm

Platform kanalı (platform channel) uygulamanın Dart tarafı ile yerel platform kodu arasındaki adlandırılmış bir konuşma hattıdır. Dart tarafı bir yöntem adı ve bağımsız değişkenlerle çağrı gönderir, platform tarafı çağrıyı karşılar ve yanıt döner. Platform da kendiliğinden ileti gönderebilir. Kanalın iki ucu iki ayrı dildedir; veriler her iki yönde de basit türlerle, sayı, dize, liste ve eşlemle taşınır.

Gömülü görünüm (platform view) platformun kendi ekran parçasının Dart ağacına konmasıdır. Ağaçta tek bir düğümdür, ama içeriğini yerel platform çizer. Bu düğümün genişliği de ağacın kurallarına uyar: çerçeve onu bir yuvaya koyar ve yuva ona bir kısıt verir.

Ç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.

// 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 iki parça kanalın iki ucunu gösterir. Parçalar ölçülmez; model çerçevenin ya da platformun kodunu çalıştırmaz.

// Platform kodu: Dart ucu
const channel = MethodChannel('app/embed');

Future<int?> askWidth() {
  return channel.invokeMethod<int>('preferredWidth');
}
// Platform kodu: yerel uc
let channel = FlutterMethodChannel(
  name: "app/embed",
  binaryMessenger: messenger
)
channel.setMethodCallHandler { call, result in
  if call.method == "preferredWidth" {
    result(180)
  } else {
    result(FlutterMethodNotImplemented)
  }
}

Dart ucu preferredWidth adlı yöntemi çağırır, yerel uç 180 döner. Bu 180 bir istektir: platform görünümünün kaç birim genişlik istediğini söyler. Dersin sorusu, bu isteğin görünümün genişliğine ne yaptığıdır.

Kanaldan Yuvaya

Modelde kanal, iletileri bir liste olarak getiren bir işlevdir. Her ileti bir eşlemdir: bir id ve platformun istediği bir width. Yardımcı dosya iki grup ileti taşır, ilk ve tekrar, ve bir iletiden genişliği okuyan işlevi verir. Dosya çıktıya hiçbir şey basmaz.

// channel.dart — kanaldan gelen iletiler ve istek okuma
const _batches = <String, List<Map<String, Object>>>{
  'ilk': [
    {'id': 1, 'width': 180},
    {'id': 2, 'width': 400},
    {'id': 3, 'width': 96},
  ],
  'tekrar': [
    {'id': 1, 'width': 180},
    {'id': 1, 'width': 180},
    {'id': 2, 'width': 400},
    {'id': 3, 'width': 96},
    {'id': 3, 'width': 96},
  ],
};

Future<List<Map<String, Object>>> receiveMessages(String batch) async {
  return _batches[batch]!;
}

int widthOf(Map<String, Object> message) => message['width'] as int;

Ekranın ağacı bir iç boşluk, bir sütun, bir başlık ve gömülü görünümün yuvasıdır. Yuva sabit 240 genişliktedir: fixed('Embed', 240, ...). Görünüm yuvanın tek çocuğudur ve iletiden gelen genişliği ister. İlk ölçüm ilk grubunun ilk iletisini kullanır.

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

Part buildScreen(int requested) {
  return pad('Screen', 16, column('Body', [
    leaf('Title', 120),
    fixed('Embed', 240, leaf('PlatformView', requested)),
  ]));
}

Future<void> main() async {
  final messages = await receiveMessages('ilk');
  final log = measure(buildScreen(widthOf(messages.first)), 360);
  printSizes(log);
  countSizings(log);
}
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/Embed genislik=240 kisit=0..328
dayatilan: /Screen/Body/Embed/PlatformView genislik=240 kisit=240..240
dayatilan: 3
secilen: 2
kirpilan: 0
esnetilen: 0
ebeveyn: 3

İlk ileti 180 ister, ama /Screen/Body/Embed/PlatformView kaydı dayatılandır: genişliği 240, kısıtı 240..240. Yuva görünüme sıkı bir kısıt verir; iletideki 180 yuvanın sıkı kısıtına çarpar ve genişlik 240’ta kalır. Yuvanın kendisi 328 genişliğindeki bir sütunun içinde 240 isteyen, aralığın içinde kalan bir düğümdür; onu seçen yuvadır, görünüm değil. Kanalın söylediği, görünümün boyutlanmasında bir şey değiştirmez.

Kanal Ne Derse Desin

Şimdi ilk grubundaki üç iletinin her birini ayrı ayrı ölçelim: 180, 400 ve 96. Üçü de aynı ağaca farklı bir istek verir.

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

Part buildScreen(int requested) {
  return pad('Screen', 16, column('Body', [
    leaf('Title', 120),
    fixed('Embed', 240, leaf('PlatformView', requested)),
  ]));
}

Future<void> main() async {
  final messages = await receiveMessages('ilk');
  for (final message in messages) {
    final log = measure(buildScreen(widthOf(message)), 360);
    final view = log.firstWhere((entry) => entry.path == '/Screen/Body/Embed/PlatformView');
    print('ileti=${message['id']} istek=${widthOf(message)} ${view.sizing} genislik=${view.width} kisit=${view.limit.bounds()}');
  }
}
ileti=1 istek=180 dayatilan genislik=240 kisit=240..240
ileti=2 istek=400 dayatilan genislik=240 kisit=240..240
ileti=3 istek=96 dayatilan genislik=240 kisit=240..240

Üç istek 180, 400 ve 96’dır ve üç satırda da boyutlanma dayatilan, genişlik 240’tır. 400 isteyen ileti yuvanın içinde görünümü büyütmez, 96 isteyen küçültmez. İleti platformdan geldiği için bir değer taşır, ama ağaçta bu değer yalnız bir istektir; sıkı kısıt altında istek, genişliğe etki eden bir şey değildir.

Bu, kanaldaki gönderilen olay alıcıya kaç kopya ulaştı sorusundan ayrı bir sorudur. O soru bir olayın kanaldan kaç kez geçtiğini sayar; bu ders bir iletinin sonucunda görünümün genişliğinin kimde olduğunu sorar. Kaç ileti gelirse gelsin, gömülü görünümün genişliğini çerçeve dayatır.

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

Kanal bir iletiyi birden çok kez de gönderebilir. tekrar grubunda beş ileti vardır: 1 iki kez, 2 bir kez, 3 iki kez. Aşağıdaki ölçüm beşini de ağaca verir ve toplam tabloyu tek bir yerde birleştirir. Ardından ekran genişliği 200’e iner.

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

Part buildScreen(int requested) {
  return pad('Screen', 16, column('Body', [
    leaf('Title', 120),
    fixed('Embed', 240, leaf('PlatformView', requested)),
  ]));
}

Future<void> main() async {
  final messages = await receiveMessages('tekrar');
  final all = <Sizing>[];
  for (final message in messages) {
    all.addAll(measure(buildScreen(widthOf(message)), 360));
  }
  print('ileti sayisi=${messages.length}');
  countSizings(all);
  final narrow = measure(buildScreen(widthOf(messages.first)), 200);
  final view = narrow.firstWhere((entry) => entry.path == '/Screen/Body/Embed/PlatformView');
  print('ekran=200 ${view.sizing} genislik=${view.width} kisit=${view.limit.bounds()}');
}
ileti sayisi=5
dayatilan: 15
secilen: 10
kirpilan: 0
esnetilen: 0
ebeveyn: 15
ekran=200 dayatilan genislik=168 kisit=168..168

Odak düğüm /Screen/Body/Embed/PlatformView’dur. Beş ileti ağacı beş kez ölçer ve her ölçüm üç dayatilan kayıt verir: /Screen, /Screen/Body ve görünüm. Toplam dayatilan: 15 ve secilen: 10 bu yüzden çıkar, yani 5 çarpı 3 ve 5 çarpı 2. Yinelenen iletiler kayıt sayısını artırır, ama hiçbir kaydın boyutlanmasını değiştirmez: görünüm beş ölçümün beşinde de dayatılandır. Kopyayı sayan şey sayıcıdır, genişliğin sahibini söyleyen şey kuraldır.

Ekran 200’e inince iç boşluktan sonra gövde 168 genişliğinde kalır. Yuva 240 ister, ama kısıt 0..168 olduğu için yuva 168’e iner; görünüm de yuvanın verdiği sıkı kısıtla 168 alır. Çıktıdaki son satır bunu gösterir: boyutlanma yine dayatilan, kısıt 168..168. İleti 180 isterken de sonuç değişmez, çünkü istek sıkı kısıtı değiştirmez.

UY1. Model yalnız genişliği ölçer. Yerel platformun görünümü, kendi içinde çizdiği şey, görünümün yüksekliği, dokunma olayları ve kanal çağrılarının zamanlaması modelde yoktur. Model, bu boyutlanmanın kuralını kurar; model çerçevenin kendisi değildir. Yuvayı sabit 240 saymak dersin varsayımıdır; gerçek bir yuva, görünüme kısıtı farklı biçimde verebilir.

Özet

  • Bu dersin boyutlanması: dayatılan. Odak düğüm /Screen/Body/Embed/PlatformView’dur ve yuvanın verdiği 240..240 kısıtıyla iletideki istek 180, 400 ya da 96 olsa da 240 alır.
  • Platform kanalı, Dart ve yerel platform arasında adlandırılmış bir konuşma hattıdır; ilettiği genişlik ağaçta yalnız bir istektir.
  • Üç farklı istek üç satırda da dayatılan çıktı; yinelenen iletiler boyutlanmayı değil yalnız kayıt sayısını artırdı.
  • Gömülü görünümün genişliğinin sahibi, kanalı dinleyen taraf değil, görünümü yuvaya koyan çerçevedir.

Sonraki Adım

Bu derste kanaldan gelen istek sonuca girmedi ve görünümün genişliğini yuva dayattı. Sıradaki konu, Test Türleri, aynı ekranı iki test yüzeyinde ölçer: 400 ve 320 genişliğinde. 300 isteyen /Screen/Body/Chart düğümü 400’de seçilen, 320’de kırpılan olur ve ölçülen her düğüm düzene girmiştir.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat