Flutter Navigation 2.0 ve GoRouter 2026: Deep Linking ve Mülakat Soruları

GoRouter 17.5 ile Flutter navigasyonunda ustalaşma: deklaratif routing, deep linking, ShellRoute, route guard'lar ve pratik örneklerle mülakat soruları.

GoRouter deep linking akış diyagramı ile Flutter navigasyon mimarisi

Flutter navigasyonu, Navigation 2.0'ın tanıtılmasından bu yana önemli bir evrim geçirdi. Flutter ekibinin resmi paketi olan GoRouter 17.5, Router API'nin karmaşıklığını yönetirken deklaratif routing, otomatik deep linking ve iç içe navigasyonu kutudan çıktığı gibi sunuyor.

GoRouter özellik açısından tamamlanmış durumda

Flutter ekibi, GoRouter'ı 2026 itibarıyla özellik açısından tamamlanmış kabul ediyor. Path parametreleri, query parametreleri, yönlendirmeler, kalıcı UI için ShellRoute ve sekme durumunu korumak için StatefulShellRoute destekleniyor. Ham Navigator 2.0 prodüksiyonda artık nadiren elle yazılıyor.

Navigator 2.0, Flutter navigasyonuna deklaratif, URL odaklı bir yaklaşım getirdi. Zorunlu push ve pop çağrıları yerine, uygulama durumu navigasyon yığınında neyin görüneceğini belirliyor. Mimari üç temel sınıfa dayanıyor:

  • Router: Navigasyonu koordine eden üst düzey widget
  • RouteInformationParser: URL'leri uygulama durumuna çeviren bileşen
  • RouterDelegate: Bu duruma göre widget ağacını oluşturan bileşen

İşletim sisteminden bir deep link geldiğinde veya web tarayıcısında URL değiştiğinde, framework durumu günceller ve UI tepki verir. Sorun şu ki, özel bir RouterDelegate ve RouteInformationParser yazmak önemli miktarda boilerplate kod gerektiriyor.

raw_navigator2_example.dartdart
// Raw Navigator 2.0 approach - verbose and rarely used directly
class AppRouterDelegate extends RouterDelegate<AppRoutePath>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<AppRoutePath> {
  
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  AppRoutePath? _currentPath;

  
  AppRoutePath? get currentConfiguration => _currentPath;

  
  Widget build(BuildContext context) {
    return Navigator(
      key: navigatorKey,
      pages: [
        const MaterialPage(child: HomeScreen()),
        if (_currentPath?.isProductPage == true)
          MaterialPage(child: ProductScreen(id: _currentPath!.productId!)),
      ],
      onDidRemovePage: (page) {
        // Handle page removal
      },
    );
  }

  
  Future<void> setNewRoutePath(AppRoutePath path) async {
    _currentPath = path;
    notifyListeners();
  }
}

Bu uzunluk, GoRouter'ın var olma sebebi. Navigator 2.0'ı sarmalayarak tüm boilerplate'i yönetiyor.

GoRouter 17.5 Kurulumu

GoRouter 17.5, route metadata desteği ve path parametreleri için düzenli ifade kısıtlamaları getiriyor. Minimum gereksinimler Flutter 3.32 ve Dart 3.8.

router_config.dartdart
import 'package:go_router/go_router.dart';

final goRouter = GoRouter(
  initialLocation: '/',
  debugLogDiagnostics: true, // Useful during development
  routes: [
    GoRoute(
      path: '/',
      name: 'home',
      builder: (context, state) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'products/:id', // Path parameter
          name: 'product',
          builder: (context, state) {
            final productId = state.pathParameters['id']!;
            return ProductScreen(productId: productId);
          },
        ),
        GoRoute(
          path: 'search',
          name: 'search',
          builder: (context, state) {
            // Query parameters: /search?q=flutter&category=books
            final query = state.uri.queryParameters['q'] ?? '';
            final category = state.uri.queryParameters['category'];
            return SearchScreen(query: query, category: category);
          },
        ),
      ],
    ),
  ],
);

Router, MaterialApp.router ile entegre edilir:

main.dartdart
import 'package:flutter/material.dart';
import 'router_config.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: goRouter,
      title: 'GoRouter Demo',
    );
  }
}

Android ve iOS için Deep Linking Yapılandırması

Deep linking, harici URL'lerin uygulamada belirli ekranları açmasına olanak tanır. Platform yapılandırması tamamlandığında GoRouter routing'i otomatik olarak yönetir.

Android, AndroidManifest.xml'de bir intent-filter ve domain'de barındırılan bir Digital Asset Links dosyası gerektirir:

xml
<!-- android/app/src/main/AndroidManifest.xml -->
<activity android:name=".MainActivity">
  <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
      android:scheme="https"
      android:host="example.com"
      android:pathPrefix="/products" />
  </intent-filter>
</activity>

assetlinks.json dosyası https://example.com/.well-known/assetlinks.json adresinde sunulmalıdır.

iOS için Xcode'da Associated Domains yeteneği etkinleştirilmeli ve bir apple-app-site-association dosyası barındırılmalıdır:

https://example.com/.well-known/apple-app-site-associationjson
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAMID.com.example.app",
        "paths": ["/products/*", "/search"]
      }
    ]
  }
}

GoRouter 17.5, boş path'li Android cold-start deep linklerinin scheme ve authority'yi kaybettiği kritik bir hatayı düzeltti. Bu, uygulama çalışmıyorken bile deep linklerin artık güvenilir şekilde çalıştığı anlamına geliyor.

Deep link testi

Android deep linklerini adb shell am start -a android.intent.action.VIEW -d "https://example.com/products/123" komutuyla test edebilirsiniz. iOS için xcrun simctl openurl booted "https://example.com/products/123" kullanılır.

Kalıcı Navigasyon UI için ShellRoute

ShellRoute, alt route'ları BottomNavigationBar veya Drawer gibi kalıcı bir UI öğesiyle sarar. Shell, çocuklar arasında gezinirken görünür kalır.

shell_router_config.dartdart
final goRouter = GoRouter(
  initialLocation: '/home',
  routes: [
    ShellRoute(
      builder: (context, state, child) {
        return ScaffoldWithNavBar(child: child);
      },
      routes: [
        GoRoute(
          path: '/home',
          name: 'home',
          builder: (context, state) => const HomeTab(),
        ),
        GoRoute(
          path: '/explore',
          name: 'explore',
          builder: (context, state) => const ExploreTab(),
        ),
        GoRoute(
          path: '/profile',
          name: 'profile',
          builder: (context, state) => const ProfileTab(),
        ),
      ],
    ),
  ],
);

// scaffold_with_nav_bar.dart
class ScaffoldWithNavBar extends StatelessWidget {
  final Widget child;
  const ScaffoldWithNavBar({super.key, required this.child});

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: child,
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _calculateSelectedIndex(context),
        onTap: (index) => _onItemTapped(index, context),
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.explore), label: 'Explore'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
        ],
      ),
    );
  }

  int _calculateSelectedIndex(BuildContext context) {
    final location = GoRouterState.of(context).uri.toString();
    if (location.startsWith('/explore')) return 1;
    if (location.startsWith('/profile')) return 2;
    return 0;
  }

  void _onItemTapped(int index, BuildContext context) {
    switch (index) {
      case 0: context.go('/home');
      case 1: context.go('/explore');
      case 2: context.go('/profile');
    }
  }
}

GoRouter 17.5, iOS geri hareketlerinin yalnızca aktif alt route yerine tüm ShellRoute'u pop ettiği sorunu düzeltti.

Flutter mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Sekme Durumunu Korumak için StatefulShellRoute

Kullanıcılar sekmeleri değiştirdiğinde, ShellRoute çocuğu yeniden oluşturur. StatefulShellRoute her dalın navigasyon yığınını korur:

stateful_shell_config.dartdart
final goRouter = GoRouter(
  initialLocation: '/home',
  routes: [
    StatefulShellRoute.indexedStack(
      builder: (context, state, navigationShell) {
        return ScaffoldWithNavBar(navigationShell: navigationShell);
      },
      branches: [
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/home',
              builder: (context, state) => const HomeTab(),
              routes: [
                GoRoute(
                  path: 'details/:id',
                  builder: (context, state) => DetailsScreen(
                    id: state.pathParameters['id']!,
                  ),
                ),
              ],
            ),
          ],
        ),
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/explore',
              builder: (context, state) => const ExploreTab(),
            ),
          ],
        ),
        StatefulShellBranch(
          routes: [
            GoRoute(
              path: '/profile',
              builder: (context, state) => const ProfileTab(),
            ),
          ],
        ),
      ],
    ),
  ],
);

Bu yapılandırmayla, kullanıcı /home/details/42'ye gidip Explore sekmesine geçip tekrar Home'a döndüğünde, hala detay ekranında olacaktır.

Redirect ile Route Guard'lar

Redirect'ler kimlik doğrulama kontrollerini ve koşullu navigasyonu yönetir. redirect callback'i her navigasyondan önce çalışır:

authenticated_router.dartdart
final goRouter = GoRouter(
  initialLocation: '/',
  redirect: (context, state) {
    final isLoggedIn = AuthService.instance.isLoggedIn;
    final isLoggingIn = state.matchedLocation == '/login';

    // Redirect to login if not authenticated
    if (!isLoggedIn && !isLoggingIn) {
      return '/login?redirect=${state.uri}';
    }

    // Redirect away from login if already authenticated
    if (isLoggedIn && isLoggingIn) {
      final redirect = state.uri.queryParameters['redirect'];
      return redirect ?? '/';
    }

    return null; // No redirect
  },
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
    GoRoute(
      path: '/login',
      builder: (context, state) => const LoginScreen(),
    ),
    GoRoute(
      path: '/admin',
      redirect: (context, state) {
        // Route-level redirect for admin-only access
        if (!AuthService.instance.isAdmin) {
          return '/';
        }
        return null;
      },
      builder: (context, state) => const AdminScreen(),
    ),
  ],
);

GoRouter 17.5, redirect callback'leri içinde context.namedLocation() gibi context extension metotlarının kullanımı için destek ekledi.

Yaygın Flutter Navigasyon Mülakat Soruları

Mülakatçılar sıklıkla navigasyon bilgisini test eder çünkü bu konu mimari, durum yönetimi ve platform entegrasyonuna değinir. İşte deneyimli adayları ayırt eden sorular:

S: Navigator 2.0, Navigator 1.0'ın çözemediği hangi sorunu çözüyor?

Navigator 1.0 zorunlu navigasyon kullanır: Navigator.push() ve Navigator.pop(). Uygulama, navigasyon durumunu URL olarak temsil edemez, bu da deep linking ve web desteğini zorlaştırır. Navigator 2.0 deklaratiftir: uygulama durumu navigasyon yığınını belirler, URL'ler birinci sınıf vatandaşlardır ve deep linkler otomatik çalışır.

S: context.go() ile context.push() ne zaman kullanılır?

context.go('/path') mevcut navigasyon yığınını eşleşen route'a kadar değiştirir. context.push('/path') mevcut yığının üstüne yeni bir route ekler. go() üst düzey navigasyon için (sekme değiştirme, ana sayfaya gitme), push() ise geri navigasyonu koruyarak detaylara inmek için kullanılır.

S: Sekme geçişleri arasında durum nasıl korunur?

StatefulShellRoute.indexedStack() kullanılır. Her dal için ayrı navigasyon yığınları tutar ve sekme değiştirildiğinde bunları korur. Kullanılmadığında, sekme değişimi tüm alt ağacı yeniden oluşturur.

S: Route'lar arasında karmaşık nesneler nasıl geçirilir?

Üç yaklaşım mevcuttur. Birincisi, path parametresinde ID geçirip hedef ekranda nesneyi fetch etmek. İkincisi, GoRouter'ın extra parametresini kullanarak nesneyi doğrudan geçirmek. Üçüncüsü, nesneyi bir durum yönetimi çözümünde (Riverpod, Bloc) depolayıp hedeften erişmek. İlk yaklaşım deep linking için en iyisidir çünkü URL paylaşılabilir kalır.

dart
// Passing via extra (not deep-linkable)
context.push('/product', extra: productObject);

// Receiving via extra
GoRoute(
  path: '/product',
  builder: (context, state) {
    final product = state.extra as Product?;
    return ProductScreen(product: product);
  },
),

S: GoRouter 17.5'teki onEnter callback nedir?

onEnter callback'i bir route'a girildiğinde çalışır ve hem mevcut hem de sonraki route durumlarına erişim sağlar. Route tam yüklenmeden önce mantık çalıştırmaya olanak tanır, analitik veya koşullu ön yükleme için kullanışlıdır.

Kod Üretimi ile Tip-Güvenli Route'lar

GoRouter, kod üretimi aracılığıyla tip-güvenli routing'i destekler. Route veri sınıfları tanımlanır ve build_runner navigasyon kodunu üretir:

routes.dartdart
import 'package:go_router/go_router.dart';

part 'routes.g.dart';

<HomeRoute>(path: '/')
class HomeRoute extends GoRouteData {
  const HomeRoute();

  
  Widget build(BuildContext context, GoRouterState state) => const HomeScreen();
}

<ProductRoute>(path: '/product/:id')
class ProductRoute extends GoRouteData {
  final String id;
  const ProductRoute({required this.id});

  
  Widget build(BuildContext context, GoRouterState state) =>
      ProductScreen(productId: id);
}

dart run build_runner build çalıştırıldıktan sonra, derleme zamanı güvenliği ile navigasyon yapılabilir:

dart
// Type-safe navigation
ProductRoute(id: '123').go(context);

Bu, route path'lerindeki yazım hatalarını ortadan kaldırır ve parametrelerin her zaman sağlandığını garanti eder.

Prodüksiyon Uygulamalarında Flutter Navigasyonunda Ustalaşma

  • GoRouter 17.5, deep linking, iç içe navigasyon veya web desteği gerektiren Flutter uygulamaları için önerilen navigasyon çözümüdür
  • Kalıcı alt navigasyon çubukları için ShellRoute kullanılır; her sekmenin navigasyon yığınını korumak için StatefulShellRoute kullanılır
  • Platforma özgü deep linking yapılandırması (Android App Links, iOS Universal Links) routing mantığını yöneten GoRouter'dan ayrı tutulur
  • Kimlik doğrulama guard'ları router seviyesinde veya bireysel route seviyesinde redirect callback'i ile uygulanır
  • Mülakat senaryolarında deklaratif modeli açıklamaya odaklanılmalıdır: uygulama durumu UI'ı yönlendirir, URL'ler durumdan türetilir ve GoRouter çeviriyi yönetir
  • Kod üretimi ile tip-güvenli route'lar, yanlış yazılmış path'lerden kaynaklanan runtime hatalarını ortadan kaldırır
  • Sürüme özgü özellikler için Flutter navigasyon dokümantasyonu ve GoRouter changelog kontrol edilmelidir

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Günün meydan okuması

Flutter kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

23 Ağustos 2026 tarihinde güncellendi

Etiketler

#flutter
#navigation
#gorouter
#deep-linking
#mobile

Paylaş

İlgili makaleler