---
title: 'Gezinme ve Yönlendirme'
source: 'https://academia.sh/tr/kurslar/flutter/gezinme-ve-yonlendirme'
course: Flutter
language: tr
updated: '2026-10-21T05:02:52+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Rota tanımını, rota yığınını ve derin bağlantıyı birbirinden ayırır'
    - 'Gezinme kabının sayfaya ekran genişliğinde sıkı bir kısıt verdiğini ölçer'
    - 'Rota değişse de sayfanın boyutlanmasının dayatılan kaldığını gösterir'
    - 'Derin bağlantıyla açılan sayfanın aynı kısıtı aldığını gösterir'
---

# Gezinme ve Yönlendirme

Sayfa hangi rotayla açılırsa açılsın genişliğini gezinme kabı belirler. Ders üç rotayı ve üç derin bağlantıyı ölçer ve sayfanın isteğinin sonuca girmediğini gösterir.

Önceki ders, paylaşılan bir sayaçtan beslenen rozetin genişliğini ölçtü ve durumun isteği değiştirdiğini, kısıtı değiştirmediğini gösterdi. Bu ders ağacın bir üst katmanına çıkar: uygulamanın hangi ekranı gösterdiğine karar veren gezinmeye. Bir sayfa rota yığınına konur, yığından çıkar ya da dışarıdan gelen bir bağlantıyla doğrudan açılır. Kursun sorusu yine aynıdır: **bu bileşenin genişliğini kim belirliyor?** Ders yanıtı, sayfayı saran gezinme kabında arar.

## Rota, Yığın ve Derin Bağlantı

Uygulamanın her ekranı bir **rota (route)** ile adlandırılır. Rota bir ad ve o adın açacağı sayfayı üreten bir işlevdir. Açık rotalar bir **yığında** tutulur: yeni rota yığının tepesine konur, geri dönüş tepeyi çıkarır ve ekranda her zaman tepedeki rotanın sayfası görünür. Yönlendirme kitaplıkları rota tanımını ve yığın yönetimini bildirimsel hâle getirir; kitaplık adı bu derste yazılmaz, işleviyle anılır.

**Derin bağlantı (deep link)** uygulamayı dışarıdan, bir adresle açar. Adresin yolu bir rota adına çözülür ve çözülen rota yığına konur. Kullanıcı sayfaya ana ekrandan gelmemiştir, ama sayfanın ekranda durduğu yer değişmez.

Gezinmenin çerçeve tarafı, rota yığınını yöneten bir kap ve o kabın içine konan sayfalardır.

## Ç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 bir rotanın yığına konmasını gösterir. Parça ölçülmez; model çerçevenin kodunu çalıştırmaz.

```dart
// Platform kodu: rota yigini
Future<void> openOrders(BuildContext context) {
  return Navigator.of(context).push(
    MaterialPageRoute<void>(
      builder: (context) => const OrdersPage(),
    ),
  );
}

void closeOrders(BuildContext context) {
  Navigator.of(context).pop();
}
```

`push` yığının tepesine bir rota koyar, `pop` tepeyi çıkarır. Sayfayı kuran `builder` işlevidir; gezinme kabı onu çağırırken sayfaya bir kısıt verir. Bu dersin sorusu o kısıtın nereden geldiği ve sayfanın kendi isteğinin ona ne yaptığıdır.

## Rotadan Sayfaya

Modelin gezinme kabı, ekran genişliğinde sabit bir kaptır: `fixed('Navigator', screen, page)`. Sayfa kabın tek çocuğudur ve çocuğa kabın genişliği sıkı kısıt olarak iner. Her rotanın sayfasının bir isteği vardır; istekler bir yardımcı dosyada durur. Yardımcı dosya ayrıca rota yığınını ve bir adresi rota adına çeviren işlevi taşır. Üçü de çıktıya hiçbir şey basmaz.

```dart
// routes.dart — rota yigini, sayfa istekleri ve baglanti cozumu
const pageRequests = {'Orders': 200, 'Profile': 480, 'Settings': 120};

const _linkRoutes = {'orders': 'Orders', 'profile': 'Profile', 'settings': 'Settings'};

class RouteStack {
  final List<String> _names;
  RouteStack(String first) : _names = [first];

  String get top => _names.last;
  int get depth => _names.length;

  void push(String name) => _names.add(name);

  void pop() {
    if (_names.length > 1) {
      _names.removeLast();
    }
  }
}

String routeFromLink(String link) {
  final segments = Uri.parse(link).pathSegments;
  return _linkRoutes[segments.first]!;
}
```

`buildRoute` yığının tepesindeki rotanın adını alır ve kabın içine o adın sayfasını koyar. İlk ölçüm `Orders` sayfasını açar.

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

Part buildRoute(String name, {int screen = 360}) {
  final page = leaf(name, pageRequests[name]!);
  return fixed('Navigator', screen, page);
}

void main() {
  final stack = RouteStack('Orders');
  final log = measure(buildRoute(stack.top), 360);
  printSizes(log);
  countSizings(log);
}
```

```text
dayatilan: /Navigator genislik=360 kisit=360..360
dayatilan: /Navigator/Orders genislik=360 kisit=360..360
dayatilan: 2
secilen: 0
kirpilan: 0
esnetilen: 0
ebeveyn: 2
```

`Orders` sayfası 200 birim ister, ama `/Navigator/Orders` kaydı **dayatılan**dır: genişliği 360, kısıtı `360..360`. Gezinme kabı sayfaya ekranın tamamını sıkı bir kısıt olarak verir; sayfanın isteği kayda girer girmez aralığın tek noktasına eşitlenir. Kap da ekranın verdiği kısıtla dayatılandır. Genişliği ne sayfa ne kap kendisi seçmiştir: ikisi de dışarıdan almıştır.

## Rota Değiştikçe

Şimdi yığına üç rota koyalım ve her konuşta sayfanın kaydına bakalım. Sonra iki kez geri dönelim. Yığın derinliği düzyazıda anlam taşır; ölçümün girdisi yalnız tepedeki rotanın adıdır.

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

Part buildRoute(String name, {int screen = 360}) {
  final page = leaf(name, pageRequests[name]!);
  return fixed('Navigator', screen, page);
}

void show(RouteStack stack) {
  final log = measure(buildRoute(stack.top), 360);
  final page = log.firstWhere((entry) => entry.path == '/Navigator/${stack.top}');
  print('${stack.top} derinlik=${stack.depth} istek=${pageRequests[stack.top]} ${page.sizing} genislik=${page.width}');
}

void main() {
  final stack = RouteStack('Orders');
  show(stack);
  stack.push('Profile');
  show(stack);
  stack.push('Settings');
  show(stack);
  stack.pop();
  show(stack);
  stack.pop();
  show(stack);
}
```

```text
Orders derinlik=1 istek=200 dayatilan genislik=360
Profile derinlik=2 istek=480 dayatilan genislik=360
Settings derinlik=3 istek=120 dayatilan genislik=360
Profile derinlik=2 istek=480 dayatilan genislik=360
Orders derinlik=1 istek=200 dayatilan genislik=360
```

Üç sayfanın istekleri 200, 480 ve 120'dir ve üçü de aynı genişlikte, 360'ta durur. Her satırda boyutlanma `dayatilan`dır. `Profile` ekrandan geniş bir sayfa ister; yine de kap onu 360'a indirir. `Settings` yalnız 120 ister; yine de 360'ı kaplar. Geri dönüşte `Profile` ve `Orders` aynı genişlikle geri gelir. Sayfa yığında ne kadar kalırsa kalsın, yığından çıkıp yeniden açılsa da genişliğini sayfa vermez; her açılışta kap yeniden verir.

**Ömür nerede bitiyor** sorusu bir değerin yaşam süresinin sonunu arar: sayfa yığından çıkınca sayfanın değerleri biter. Bu ders onu sormaz. Rota sayfasının ömrü ne olursa olsun, genişliğini kap dayatır; ölçüm ömrün başladığı ya da bittiği anı değil, sayfanın bir kez ekrandayken genişliğinin sahibini sınıflar.

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

Dışarıdan gelen bir bağlantı, sayfaya farklı bir yol açar ama kısıtı değiştirmez. Aşağıdaki ölçüm üç bağlantıyı rota adına çevirir, her birini iki ekran genişliğinde ölçer. Birinci genişlik 360, ikincisi 280'dir. Sayfaların istekleri yine 200, 480 ve 120'dir.

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

Part buildRoute(String name, {int screen = 360}) {
  final page = leaf(name, pageRequests[name]!);
  return fixed('Navigator', screen, page);
}

void main() {
  final links = [
    'app://shop/orders/42',
    'app://shop/profile',
    'app://shop/settings/theme',
  ];
  for (final screen in [360, 280]) {
    for (final link in links) {
      final name = routeFromLink(link);
      final log = measure(buildRoute(name, screen: screen), screen);
      final page = log.firstWhere((entry) => entry.path == '/Navigator/$name');
      print('ekran=$screen $name istek=${pageRequests[name]} ${page.sizing} genislik=${page.width} kisit=${page.limit.bounds()}');
    }
  }
}
```

```text
ekran=360 Orders istek=200 dayatilan genislik=360 kisit=360..360
ekran=360 Profile istek=480 dayatilan genislik=360 kisit=360..360
ekran=360 Settings istek=120 dayatilan genislik=360 kisit=360..360
ekran=280 Orders istek=200 dayatilan genislik=280 kisit=280..280
ekran=280 Profile istek=480 dayatilan genislik=280 kisit=280..280
ekran=280 Settings istek=120 dayatilan genislik=280 kisit=280..280
```

Odak düğüm bağlantıyla açılan sayfadır: `/Navigator/Orders`, `/Navigator/Profile` ve `/Navigator/Settings`. Altı satırın altısında da boyutlanma `dayatilan`, genişlik ekran genişliğine, kısıt `ekran..ekran` aralığına eşittir. Sayfanın isteği kayda girmez; çıktıdaki `istek` değerini ders satırı ayrıca basar. 480 isteyen `Profile` 280'lik ekranda 280'e iner. Derin bağlantı sayfaya farklı bir kısıt vermez: kap hangi yoldan gelinirse gelinsin ekran genişliğinde sabit kaldığı için yığına nasıl girildiği genişliğe yansımaz.

**UY1.** Model yalnız genişliği ölçer. Sayfanın yüksekliği, geçiş animasyonu, kabın üst çubuğu ve geri dönüşte durumun korunması modelde yoktur. Model, bu boyutlanmanın kuralını kurar; model çerçevenin kendisi değildir. Gezinme kabını ekran genişliğinde sabit saymak dersin varsayımıdır; gerçek bir kap, yığındaki bir sayfaya kendi kısıtını farklı verebilir.

Bu sonuç sayfa yazarı için bir sözleşmedir: sayfa kendi içindeki bileşenler için bir genişlik bekleyebilir, ama kendi genişliğini kap verdiği için sayfa kendini ekrandan geniş ya da dar isteyerek ayarlayamaz. Sayfanın içinde isteğin anlam taşıdığı yer, sayfanın çocuklarıdır; onlar sayfanın verdiği kısıtla ölçülür.

## Özet

- Bu dersin boyutlanması: dayatılan. Odak düğüm `/Navigator/Orders`'tır ve ekranın verdiği `360..360` kısıtıyla sayfanın isteği 200 olsa da 360 alır.
- Rota yığına konur ya da yığından çıkar; sayfanın genişliği her açılışta gezinme kabından gelir ve rotaya göre değişmez.
- Üç sayfa 200, 480 ve 120 istedi ve üçü de 360'ta dayatılan çıktı.
- Derin bağlantıyla açılan sayfa, yığına ana ekrandan konmuş sayfayla aynı kısıtı aldı; ekran 280'e inince genişlik 280'e indi.
- Kural sayfanın ömrünü değil, genişliğinin sahibini görür: sayfa kaç kez açılıp kapanırsa kapansın kayıt aynıdır.

## Sonraki Adım

Bu derste sayfanın genişliğini kap dayattı ve sayfanın isteği sonuca girmedi. Sıradaki ders, `Ağ ve Dizileştirme`, isteğin sunucudan geldiği durumu ölçer: dışarıdan gelen bir genişlik aralığın dışına çıkınca kuralın o isteğe ne yaptığına bakar.
