Mert Özen Her Satırda Daha İleri
Frontend Flutter ile Mobil — #01

Flutter Nedir? Tek Codebase, Kendi Rendering Engine'i ve .NET MAUI / React Native / Native Karşısında Yeri

Mert Özen 15 Kas 2026 12 dk 5 görüntülenme
Flutter Nedir? Tek Codebase, Kendi Rendering Engine'i ve .NET MAUI / React Native / Native Karşısında Yeri

Flutter, platformun native kontrollerini kullanmak yerine her pikseli kendi engine'iyle (Impeller) çizen, tek Dart codebase'inden Android ve iOS uygulaması üreten bir UI toolkit'i. Katmanlarını, Dart'ın geliştirmede JIT ve release'te AOT derlenmesini gerçek ölçümle gösteriyor, .NET MAUI, React Native ve native seçenekleriyle karşılaştırıp Flutter'ın ne zaman seçilmeyeceğini anlatıyoruz.

Sürüm notu: Bu yazıdaki örnekler Flutter 3.47.8 ve Dart 3.13.7 ile denenmiştir. Karşılaştırmadaki diğer araçların sürümleri yazım günü (15 Kasım 2026) resmî kaynaklardan alındı. Flutter 3.50, 11 Kasım 2026'da stable oldu; bu seri 3.47 hattıyla yazılıyor, farkları serinin son yazısında topluyoruz.

Önce Tahmin Et

Sitedeki serilerin ortak örnek uygulaması Bookstore (bir online kitapçı) şimdiye kadar ASP.NET Core API'si ve Angular istemcisiyle anlatıldı. Bu seride aynı mağazanın müşteri mobil uygulamasını yazıyoruz: katalog, kitap detayı, sepet, sipariş geçmişi ve giriş. Tek bir Dart codebase'inden hem Android hem iOS uygulaması çıkacak.

İki soruyla başlayalım. Birincisi: Flutter ile yazdığın "Add to cart" butonu Android'de bir android.widget.Button, iOS'ta bir UIButton mı olur? Yani Flutter, React Native ve .NET MAUI (Multi-platform App UI; Microsoft'un Xamarin.Forms'tan sonra gelen çok platformlu UI framework'ü) gibi platformun kendi kontrollerini mi kullanır, yoksa başka bir şey mi yapar?

İkincisi: aşağıdaki küçük Dart programı sepet toplamını hesaplıyor. Aynı dosyayı bir kez dart run ile, bir kez de dart compile exe ile makine koduna çevirip çalıştıracağız. main() fonksiyonunun içi hangisinde daha hızlı biter, aradaki fark kaç kat olur? Cevapları tahmin et; ikisini de yazının ortasında gerçek çıktıyla göreceğiz.

Flutter Ne Yapar? Kendi Pikselini Çizen Bir UI Toolkit

Flutter, Google'ın açık kaynak UI toolkit'i. Uygulamayı Dart diliyle yazarsın; aynı kod Android, iOS, web, Windows, macOS ve Linux için derlenir. Bu tarif ilk bakışta .NET MAUI ve React Native'e benziyor, ama arkadaki model farklı.

.NET MAUI'de bir Button yazdığında, handler katmanı onu Android'de Android'in, iOS'ta UIKit'in, Windows'ta WinUI'nin butonuna çevirir. React Native de JavaScript tarafındaki component'leri platformun native view'larına eşler. İki yaklaşımda da ekrandaki kontrolü işletim sistemi çizer.

Flutter bunu yapmaz. Buton, metin, liste, gölge, animasyon dahil ekrandaki her pikseli kendi rendering engine'i çizer. Platformdan istediği yalnızca bir çizim yüzeyi, dokunma ve klavye event'leri ve uygulamanın yaşam döngüsü bilgisidir. Birinci sorunun cevabı bu yüzden "hayır": Flutter'daki FilledButton ne Android'in ne iOS'un butonudur; Material Design kurallarına göre Flutter'ın çizdiği bir şekildir.

Bunu ikinci yazıda ölçülebilir hâlde göreceğiz: Bookstore uygulamasının web build'ini headless bir tarayıcıda açtığımızda sayfada tek bir HTML <button> element'i bile yok; her şey Flutter'ın çizdiği bir canvas'ın içinde. Mobilde de durum aynı, sadece canvas yerine platformun grafik yüzeyi var.

Bu modelin iki doğrudan sonucu var. İyi tarafı: uygulama her platformda piksel piksel aynı görünür, işletim sisteminin sürümüne göre kontrol davranışı değişmez, Android 7'de de Android 16'da da aynı listeyi görürsün. Bedeli: platformun kendi görünümünü (iOS'un ayar ekranı hissi, Android'in sistem diyalogları) istiyorsan onu Flutter'ın widget'larıyla taklit etmen gerekir. Cupertino widget'ları bu iş için var; ama işletim sistemi yeni bir görsel dil getirdiğinde güncellemeyi Flutter ekibinin yapması beklenir.

Katmanlar: Framework, Engine ve Embedder

Flutter'ın mimarisi üç katmandan oluşur ve her katman bir alttakine bağımlıdır:

01-flutter-layers-tr.png

En üstte framework var: tamamı Dart ile yazılmış bir kütüphane seti. Foundation, Animation, Painting ve Gestures temel yapı taşlarını, Rendering layout ve çizim modelini, Widgets ise uygulama yazarken kullandığın composition modelini verir. Material ve Cupertino tasarım dilleri bunların üstünde durur. 3.47 ile bu ikisi SDK'dan ayrılıp material_ui ve cupertino_ui paketleri olarak pub.dev'e çıktı; 3.50'de SDK içindeki eski kütüphaneler resmen deprecated oldu. Seride baştan bu paketleri kullanacağız.

Framework'ün altında C++ ile yazılmış engine var. Ekrana çizimi Impeller yapar; Dart runtime, metin yerleşimi (text layout) ve native kodla konuşmayı sağlayan platform channel'ları da burada. Impeller iOS'ta tek rendering engine; Android'de API 29 ve üstünde varsayılan, Vulkan desteklemeyen eski cihazlarda OpenGL tabanlı eski renderer'a düşer. 3.47'den beri Windows, macOS ve Linux'ta da varsayılan. Web tarafında hâlâ Skia kullanılıyor.

En altta embedder var: her platform için ayrı yazılmış, uygulamayı işletim sistemine bağlayan ince katman. Android'de Kotlin/Java ve C++, iOS'ta Swift ve Objective-C. Çizim yüzeyini hazırlar, dokunmaları engine'e iletir, uygulama arka plana gittiğinde haber verir. Bir Flutter uygulaması mağazaya yüklendiğinde, işletim sisteminin gördüğü şey sıradan bir Android ya da iOS uygulamasıdır; içinde senin derlenmiş Dart kodun ve Flutter engine'i bulunur.

Angular'dan gelen biri için kaba bir benzetme: framework katmanı Angular'ın kendisine, engine tarayıcının rendering motoruna, embedder ise tarayıcının işletim sistemiyle konuşan kısmına denk düşer. Fark şu: tarayıcı DOM'u (sayfayı tuttuğu ağaç yapısı) çizer, Flutter ise DOM benzeri bir ara katman olmadan kendi render ağacını doğrudan GPU'ya (Graphics Processing Unit; ekran kartı) gönderir.

Dart Neden? JIT ile Geliştirme, AOT ile Release

Flutter'ın Dart'ı seçmesinin en pratik nedeni, dilin iki ayrı derleme modunu desteklemesi. Geliştirme sırasında kod Dart VM (sanal makine) üzerinde JIT (Just-In-Time; kodun çalışma anında derlenmesi) ile çalışır. Bu sayede değiştirdiğin kodu çalışan uygulamaya enjekte edip ekranı saniyenin altında güncelleyebilirsin; buna hot reload deniyor. Release build'de ise kod AOT (Ahead-Of-Time; çalıştırmadan önce makine koduna derleme) ile doğrudan ARM (mobil cihazların çoğundaki işlemci mimarisi) ya da x64 makine koduna çevrilir. Uygulama açıldığında derleyici çalışmaz.

Farkı bir Flutter uygulaması kurmadan, yalnız Dart SDK ile görebiliriz. Serinin kod deposunda dart_samples adında küçük bir Dart paketi tutacağız; ilk dosyası sepet toplamını hesaplıyor:

// Bookstore sepet toplamı: aynı dosya JIT ile (dart run) ve AOT makine kodu olarak (dart compile exe) çalışır.
void main() {
  final watch = Stopwatch()..start();

  // Fiyatlar kuruş cinsinden (1 TRY = 100 kuruş), bu yüzden double toplamıyoruz.
  final cart = [
    {'title': 'Dune', 'price': 48900, 'quantity': 1},
    {'title': 'Emma', 'price': 21500, 'quantity': 2},
    {'title': 'The Hobbit', 'price': 32000, 'quantity': 1},
  ];

  var total = 0;
  for (final line in cart) {
    total += (line['price'] as int) * (line['quantity'] as int);
  }

  final lira = total ~/ 100;
  final kurus = (total % 100).toString().padLeft(2, '0');
  print('Items: ${cart.length}, total: $lira.$kurus TRY');
  print('main() took ${watch.elapsedMicroseconds} us');
}

Önce JIT ile, iki kez:

dart run bin/cart_total.dart
Items: 3, total: 1239.00 TRY
main() took 888 us
Items: 3, total: 1239.00 TRY
main() took 888 us

Şimdi aynı dosyayı tek bir çalıştırılabilir dosyaya derleyip çalıştıralım:

dart compile exe bin/cart_total.dart
bin/cart_total.exe
Generated: /home/user/src/bookstore-flutter/dart_samples/bin/cart_total.exe
Items: 3, total: 1239.00 TRY
main() took 51 us
Items: 3, total: 1239.00 TRY
main() took 43 us

İkinci sorunun cevabı: main()'in içi AOT'ta 43–51 mikrosaniye, JIT'te yaklaşık 890 mikrosaniye sürdü; bu küçük programda farkın büyük kısmı JIT'in kodu ilk çalıştırmada derlemesinden geliyor. Sürecin tamamını time ile ölçtüğümüzde fark daha da açılıyor: dart run üç denemede 0,13–0,14 saniye, derlenmiş dosya ölçüm hassasiyetinin altında, 0,00 saniye. Derlenmiş dosya 5,7 MB; içinde Dart runtime'ının küçük bir AOT sürümü ve senin kodun var, makinede Dart SDK kurulu olması gerekmiyor. Windows'ta aynı komut bin\cart_total.exe üretir.

Flutter aynı ikiliği kendi build modlarında kullanır. flutter run varsayılan olarak debug modda çalışır: JIT, hot reload, assert'ler ve DevTools bağlantısı açık. flutter build apk ya da flutter build ipa release modda AOT kodu üretir. Arada bir profile modu var: AOT derlenir ama performans ölçümü için gereken araçlar açık kalır. Debug modda uygulamanın yavaş hissettirmesi bu yüzden normal; performansı hiçbir zaman debug build'de ölçme.

C# tarafından bakınca tablo tanıdık: .NET de varsayılan olarak JIT kullanır, Native AOT ile tek dosyalık makine kodu üretebilir. Fark, Flutter'da geliştirme ve release'in bu iki moda baştan bölünmüş olması. iOS zaten çalışma anında kod üretmeye izin vermediği için release'te AOT zorunlu.

Karşılaştırma: Flutter, .NET MAUI, React Native ve Native

.NET bilen bir ekibin mobil için önündeki seçenekleri kabaca şöyle (MAUI'de UI, C# ya da XAML (Microsoft'un XML tabanlı UI tanımlama dili) ile yazılır):

KonuFlutter.NET MAUIReact NativeSwift + Kotlin
DilDartC#, XAMLTypeScript/JavaScriptSwift (iOS), Kotlin (Android)
UI'ı kim çizerFlutter engine (Impeller)Platformun native kontrolleri (handler'lar)Platformun native view'ları (Fabric renderer)Platformun kendisi
Codebase sayısı1112
HedeflerAndroid, iOS, web, Windows, macOS, LinuxAndroid, iOS, macOS (Mac Catalyst), WindowsAndroid, iOS; diğerleri topluluk projeleriyleHer biri kendi platformu
Geliştirme döngüsüHot reload, state korunur.NET hot reload ve XAML hot reloadFast RefreshXcode Previews, Compose Preview
Yazım günü sürüm3.47 (3.50 yeni çıktı)MAUI 10, MAUI 11 (.NET 11 ile)0.88Xcode 26/27, Android Studio

.NET MAUI'nin bir C# ekibi için en cazip yanı açık: dil, IDE, NuGet, test araçları ve büyük ihtimalle backend'deki domain modelleri aynı. ViewModel'leri, validation kurallarını, hatta bir kısım iş mantığını ASP.NET Core API ile paylaşabilirsin. Native kontrol kullandığı için platform hissi ek iş gerektirmeden gelir. Karşılığında her platformun kontrol farkları sana yansır: bir kontrol Android'de bir şekilde, iOS'ta başka şekilde davranabilir, sorunu çözmek için handler'ı platform bazında özelleştirmen gerekebilir.

React Native, ekip zaten React biliyorsa doğal bir seçim. 0.82'den beri yalnız New Architecture ile çalışıyor (JavaScript ile native taraf arasındaki eski asenkron köprü kalktı), 0.84'ten beri Hermes engine'inin yeni sürümü (V1) varsayılan. Bizim okurumuz Angular bildiği için bu avantaj sınırlı; React bilgisi ayrıca öğrenilmesi gereken bir şey. Sitede React serisi var, oradaki zihinsel model farkları burada da geçerli.

Native (Swift + Kotlin) en yüksek kontrolü ve platformun yeni API'lerine ilk gün erişimi verir; bedeli iki ayrı codebase, iki ayrı ekip ya da iki dili bilen bir ekip. Flutter bu tabloda "bir dil öğrenmeye değer mi?" sorusunun karşılığı: Dart, C# ve TypeScript bilen biri için birkaç günde okunur hâle gelen bir dil, ve UI'ın her platformda aynı davranması test ve tasarım maliyetini düşürür.

Ne Zaman Flutter Seçilmez?

Her proje için doğru seçim değil. Üç durumda başka bir yola bakmak mantıklı:

Uygulama platformun görünümüne birebir uymak zorundaysa. Örneğin bir bankanın iOS uygulaması iOS 26'nın yeni görsel diline ilk gün geçmek istiyorsa, Flutter'ın widget'larının bu görünümü yakalamasını beklemek gerekir. Native ya da native kontrol kullanan bir framework burada daha az iş çıkarır.

Uygulamanın ağırlığı UI değil, platform API'leriyse. Arka planda Bluetooth cihazlarıyla konuşan, sistem widget'ları, CarPlay ya da Android Auto ekranları sunan bir uygulamada kodun büyük kısmı zaten Kotlin ve Swift ile yazılacak. Flutter'ın ortak UI avantajı küçük, platform channel'larıyla köprü kurmanın maliyeti büyük kalır.

Ekip ve mevcut kod başka yöne bakıyorsa. Ekibin elinde büyük bir Xamarin.Forms uygulaması varsa, MAUI'ye geçiş Flutter ile sıfırdan yazmaktan çoğu zaman ucuzdur. Aynı şekilde mevcut bir React Native uygulamasını yalnız "Flutter daha hızlı" diye yeniden yazmak, ölçülmüş bir sorun yoksa iyi bir yatırım değildir.

Bookstore'un müşteri uygulaması bu durumların hiçbirine girmiyor: ekranların çoğu liste, detay, form ve sepet; platforma özgü ağır bir özellik yok. Bu tür uygulamalar Flutter'ın en çok işe yaradığı yer.

Platformlar: Android ve iOS Ana Hedef, Web ve Desktop Nerede?

Bu serinin hedefi Android ve iOS. Flutter 3.50 itibarıyla desteklenen en düşük sürümler Android'de API 24 (Android 7.0), iOS'ta 15. iOS uygulamasını build etmek ve imzalamak için bir Mac ve Xcode şart; Windows'ta çalışan bir geliştirici Android tarafını eksiksiz geliştirir, iOS build'i için bir Mac'e ya da macOS çalıştıran bir CI makinesine ihtiyaç duyar. Kurulumun ayrıntısı bir sonraki yazıda.

Web desteği stable. Varsayılan build JavaScript üretir; --wasm bayrağıyla WebAssembly'ye derleyen build de stable ve Flutter ekibi Wasm'ı varsayılan yapmaya doğru gidiyor. Ama Flutter web, ekrandaki her şeyi canvas'a çizdiği için klasik bir web sitesinin sağladığı şeyleri zayıf verir: arama motoru indekslemesi, metin seçimi, tarayıcının erişilebilirlik ağacı ancak ek katmanlarla gelir. Bookstore'un web arayüzü bu yüzden Angular ile yazıldı ve Angular serisinde anlatılıyor; bu seride web build'i yalnız ikinci yazıdaki hızlı kontrol için kullanacağız.

Desktop tarafında Windows, macOS ve Linux stable; 3.47 ile Impeller üçünde de varsayılan oldu. Mac tarafında bir geçiş var: Flutter Intel işlemcili Mac'lere desteği kademeli olarak kapatıyor; macOS 26 Intel'i destekleyen son sürüm, macOS 27 Apple Silicon istiyor. Bu, Mac'te geliştirme yapan ekiplerin donanım planını etkiler; Windows'ta Android geliştirenleri etkilemez.

Serinin Planı ve Aldığımız Kararlar

Seri 17 yazı. İlk beş yazı kurulum ve Dart; C# bildiğini varsayıp yalnız farklara odaklanıyoruz. Sonra widget'lar, layout, liste, tema ve form; ardından state management, navigation, Bookstore API'sine bağlanma, local storage, test ve mağazalara yayınlama. Her yazının sonunda kodun o anki hâli serinin deposunda ayrı bir commit olarak duruyor.

Baştan verdiğimiz kararlar:

  • Flutter 3.47 hattı (bu yazıdaki örnekler 3.47.8 ile koştu) ve Dart 3.13. Flutter 3.50 bu yazıdan dört gün önce stable oldu; değişikliklerini son yazıda topluyoruz.
  • Material widget'ları için package:flutter/material.dart yerine material_ui paketi. 3.50'de SDK içindeki Material ve Cupertino kütüphaneleri deprecated olduğu için yeni projeye eski import'la başlamanın anlamı yok.
  • Mimari için Flutter'ın resmî rehberi: UI katmanında MVVM (View + ViewModel), data katmanında repository ve service. State management için provider + ChangeNotifier; Riverpod ve Bloc yalnız karşılaştırmada.
  • Navigation için go_router, HTTP için ASP.NET Core ile yazılmış Bookstore API'si.

Üretimde Dikkat

Flutter uygulaması içinde engine'i ve AOT derlenmiş Dart kodunu taşıdığı için aynı işi yapan native uygulamadan büyüktür; yukarıdaki küçük konsol programı bile 5,7 MB tuttu. Mağazanın kullanıcıya indirdiği boyut, Android App Bundle ve iOS App Thinning sayesinde cihazın mimarisine göre küçülür. Boyut senin için kritikse ilk sprintte release build alıp ölç (flutter build apk --analyze-size --target-platform android-arm64), sonra her büyük paket eklemede tekrar ölç.

Performans ölçümünü hiçbir zaman debug modda yapma. Debug build JIT ile çalışır, assert'leri açıktır ve kasıtlı olarak yavaştır. Profile modu (flutter run --profile) gerçek cihazda, AOT kodla ve DevTools açıkken ölçüm yapmak için var. Emulator ve simulator'ler de performans için güvenilir değil.

Sık Düşülen Tuzaklar

Birinci tuzak, Flutter'ı "her yerde native kontrol" sanmak. Ekip iOS'ta sistemin tarih seçicisinin otomatik geleceğini düşünürse sürpriz yaşar: Flutter'da showDatePicker Material görünümlü, CupertinoDatePicker ise iOS görünümlü bir widget'tır; hangisinin nerede kullanılacağına sen karar verirsin.

İkinci tuzak, Flutter web'i Angular'ın yerine koymak. Mobil uygulamanın kodundan bir web sürümü çıkarmak cazip, ama içerik ağırlıklı, arama motorunda bulunması gereken bir site için Flutter web doğru araç değil. Flutter web daha çok iç araçlar, dashboard'lar ya da mobil uygulamanın kısa ömürlü bir demosu için uygun.

Üçüncü tuzak, debug modda gördüğün takılmalara göre karar vermek. "Flutter yavaş" sonucuna varan ekiplerin önemli bir kısmı debug build'i emulator'de denemiştir. Release build'i gerçek bir cihazda çalıştırmadan performans hakkında konuşma.

Küçük Bir Deneme

★☆☆ cart_total.dart'a dördüncü bir kitap ekle (Middlemarch, 275,00 TL, 1 adet) ve programı hem dart run hem derlenmiş dosya ile çalıştır. Toplamı ve iki moddaki süreyi not et.

★★☆ Aynı hesabı C# ile yaz: bir konsol uygulaması, decimal fiyatlar ve Stopwatch. Önce dotnet run, sonra dotnet publish -p:PublishAot=true ile çalıştır. Dosya boyutu ve süre Dart'ın JIT/AOT farkına benziyor mu?

★★★ Tablodaki dört seçenek için Bookstore müşteri uygulamasını düşünerek bir karar tablosu hazırla: ekibin bildiği diller, iOS build'i için gereken Mac sayısı, ortak kod oranı ve iki yıl içinde beklediğin platform değişiklikleri. Hangi koşulda .NET MAUI'yi seçerdin?

Bir sonraki yazıda Flutter SDK'yı kuruyoruz: Windows'ta Android için, macOS'ta Android ve iOS için gerekenler, flutter doctor çıktısını okumak, bookstore_app projesini oluşturmak ve hot reload ile hot restart'ın farkını gerçek çıktıyla görmek.