↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 12 — Konu 55: Flutter Web / Desktop

5 dk okuma #flutter
Dizi · 55/61 Flutter Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
  3. Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
  4. Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
  5. Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
  6. Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
  7. Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
  8. Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
  9. Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
  10. Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
  11. Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
  12. Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
  13. Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
  14. Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
  15. Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
  16. Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
  17. Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
  18. Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
  19. Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
  20. Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
  21. Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
  22. Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
  23. Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
  24. Bölüm 6 — Konu 24: Provider Paketi
  25. Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
  26. Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
  27. Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
  28. Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
  29. Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
  30. Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
  31. Bölüm 7 — Konu 31: Deep Linking
  32. Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
  33. Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
  34. Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
  35. Bölüm 8 — Konu 35: Implicit Animasyonlar
  36. Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
  37. Bölüm 8 — Konu 37: Hero Animasyonları
  38. Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
  39. Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
  40. Bölüm 9 — Konu 40: Platform Channels
  41. Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
  42. Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
  43. Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
  44. Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
  45. Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
  46. Bölüm 10 — Konu 46: CI/CD
  47. Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
  48. Bölüm 10 — Konu 48: DevTools Profiling
  49. Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
  50. Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
  51. Bölüm 11 — Konu 51: Supabase Alternatifi
  52. Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
  53. Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
  54. Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
  55. Bölüm 12 — Konu 55: Flutter Web / Desktop
  56. Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
  57. Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
  58. Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
  59. Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
  60. Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
  61. Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
Dizinin sayfası →
İçindekiler 13 başlık
  1. Flutter, Tek Kod Tabanından Kaç Platformu Hedefler?
  2. Web — Nasıl Çalışır?
  3. Render Motorları — Güncel Durum
  4. Web'in Kendine Özgü Sınırlamaları
  5. dart:io Web'de Çalışmaz
  6. Koşullu İçe Aktarma (Conditional Imports) — Çözüm
  7. kIsWeb — Çalışma Zamanında Platform Kontrolü
  8. Masaüstü (Windows/macOS/Linux) — Web'den Farklı Bir Dünya
  9. Masaüstüne Özgü Etkileşim Kalıpları
  10. Pencere Yönetimi — window_manager Paketi
  11. Responsive Tasarımın Önemi Katlanıyor
  12. 🎯 Bu Dersten Çıkarılması Gerekenler
  13. 📝 Ödevler

Bölüm 12'ye ("Uzmanlık Seviyesi") hoş geldin — bu son bölüm, seni "Flutter biliyorum"dan "Flutter'ın iç mekanizmalarına hakimim" seviyesine taşıyor. İlk konu: şimdiye kadar yazdığın aynı Dart kodunun, mobil dışında web tarayıcısında ve masaüstünde (Windows/macOS/Linux) nasıl çalıştığı.

Flutter, Tek Kod Tabanından Kaç Platformu Hedefler?

Bölüm 1 Konu 1'de öğrendiğimiz Skia/Impeller mimarisini hatırla — Flutter, kendi piksellerini kendi çizdiği için, teorik olarak her platformda aynı görünümü üretebilir. Bugün resmi olarak desteklenen platformlar: Android, iOS, Web, Windows, macOS, Linux — hepsi aynı lib/ klasöründen.

bash
flutter config --list                    # şu an hangi platformlar açık, gör
flutter create --platforms=web,windows,macos,linux .   # var olan projeye platform ekle

Web — Nasıl Çalışır?

bash
flutter run -d chrome

Flutter web, Dart kodunu JavaScript'e (ya da WebAssembly'ye) derleyip, Skia'nın web'e taşınmış bir versiyonuyla (CanvasKit) tarayıcının <canvas> elemanı üzerine piksel piksel çizer. Bu, Bölüm 1'de öğrendiğimiz "Flutter, native UI bileşenlerini kullanmaz, kendi çizer" ilkesinin, web'de de aynen geçerli olduğu anlamına gelir — bir Flutter web sayfası, normal bir HTML/CSS sayfası gibi yapılandırılmamıştır, tek bir <canvas> içine çizilmiş bir resim gibi düşünülebilir (etkileşimli olsa da).

Render Motorları — Güncel Durum

Flutter web'in render motoru, zaman içinde değişti — bu, tam da senin "güncelliğe dikkat" isteğinin tam yerinde olduğu bir konu:

  • CanvasKit (WebAssembly ile derlenmiş Skia) — varsayılan ve önerilen render motoru; mobil/masaüstüyle piksel piksel aynı görünümü garanti eder.
  • Skwasm — CanvasKit'in WebAssembly + çoklu iş parçacığı (multi-threading) kullanan, daha yeni ve deneysel/gelişmekte olan bir varyantı; tarayıcı desteğine bağlı olarak daha yüksek performans hedefler.
  • HTML renderer — artık kaldırıldı. Eskiden (Flutter'ın erken web sürümlerinde) daha küçük indirme boyutu sunan, DOM/CSS tabanlı bir alternatifti, ama piksel tutarlılığı CanvasKit kadar güçlü değildi — Flutter ekibi, CanvasKit/Skwasm'ı tek, tutarlı bir standart haline getirmek için bunu kaldırdı. Eski kaynaklarda --web-renderer html gibi bayraklar görebilirsin — bunlar artık geçerli değil.

Pratik sonuç: Web'e build alırken render motoru seçme derdin yok — Flutter, senin için en uygun olanı (CanvasKit, uygun tarayıcılarda Skwasm) otomatik seçiyor.

Web'in Kendine Özgü Sınırlamaları

dart:io Web'de Çalışmaz

dart
import 'dart:io'; // ❌ WEB'DE DERLEME HATASI VERİR

Dart Bölüm 13'te öğrendiğimiz dart:io (dosya sistemi, soket bağlantıları), tarayıcı ortamında anlamsızdır — bir web sayfası, kullanıcının dosya sistemine doğrudan erişemez (güvenlik nedeniyle). File, Directory, Socket gibi sınıfları kullanan bir paket (örn. Bölüm 9'da gördüğümüz bazı native paketler), web'de derlenemez.

Koşullu İçe Aktarma (Conditional Imports) — Çözüm

dart
// depolama_servisi.dart (arayüz)
abstract class DepolamaServisi {
  Future<void> kaydet(String anahtar, String deger);
}

// depolama_servisi_io.dart (mobil/masaüstü — dart:io kullanabilir)
import 'dart:io';
class DepolamaServisiIO implements DepolamaServisi { /* ... */ }

// depolama_servisi_web.dart (web — dart:io YOK, farklı bir yaklaşım)
import 'package:web/web.dart' as web; // tarayıcı API'lerine erişim
class DepolamaServisiWeb implements DepolamaServisi { /* ... */ }
dart
// depolama_servisi_factory.dart
import 'depolama_servisi.dart';
import 'depolama_servisi_io.dart'
    if (dart.library.html) 'depolama_servisi_web.dart'; // KOŞULLU import!

DepolamaServisi depolamaServisiOlustur() {
  return DepolamaServisiIO(); // web'de derlenirken OTOMATİK olarak Web versiyonuyla değişir
}

if (dart.library.html) '...' — bu, Dart'ın derleme zamanında platforma göre hangi dosyanın kullanılacağına karar vermesini sağlayan bir mekanizma. Bu, Bölüm 7'de öğrendiğimiz Dependency Inversion prensibinin (soyut bir arayüze bağımlı olma, somut implementasyonu dışarıdan "enjekte etme") derleme zamanı versiyonu — DI genelde çalışma zamanında (get_it ile) karar verirken, koşullu import derleme zamanında karar veriyor.

kIsWeb — Çalışma Zamanında Platform Kontrolü

dart
import 'package:flutter/foundation.dart' show kIsWeb;

if (kIsWeb) {
  print('Web\'de çalışıyoruz');
} else {
  print('Native bir platformdayız (mobil/masaüstü)');
}

kIsWeb, Dart Bölüm 2'de öğrendiğimiz sabit (const) bir boolean — belirli bir davranış farkını (örn. bir dosya indirme butonunun web'de farklı çalışması) if ile koşullu olarak yönetmen gerektiğinde kullanılır. Koşullu import'tan farkı: kIsWeb, aynı dosya içinde, çalışma zamanı mantığı için; koşullu import, hangi dosyanın derleneceğini belirlemek için.

Masaüstü (Windows/macOS/Linux) — Web'den Farklı Bir Dünya

bash
flutter run -d windows   # veya -d macos, -d linux

Masaüstü, web'in aksine, native bir uygulama olarak derlenir (Bölüm 9 Konu 44'te öğrendiğimiz mobil build mekaniğine kavramsal olarak çok benzer, ama Windows .exe, macOS .app, Linux için genelde bir paket formatı üretir) — dart:io sorunsuz çalışır.

Masaüstüne Özgü Etkileşim Kalıpları

dart
MouseRegion(
  onEnter: (olay) => setState(() => _uzerindeMi = true),
  onExit: (olay) => setState(() => _uzerindeMi = false),
  child: Container(
    color: _uzerindeMi ? Colors.blue[100] : Colors.white,
    child: const Text('Üzerine gel'),
  ),
)

MouseRegion, mobilde anlamsız (dokunmatik ekranda "hover" yoktur) ama masaüstünde (ve web'de, fare kullanıldığında) kritik bir widget — Dart Bölüm 5'te öğrendiğimiz callback kalıbının (onEnter/onExit), fare imlecinin widget üzerine gelip gelmediğini izlemek için kullanımı.

dart
Shortcuts(
  shortcuts: {
    LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyS):
        const KaydetIntent(),
  },
  child: Actions(
    actions: {
      KaydetIntent: CallbackAction<KaydetIntent>(onInvoke: (intent) => kaydet()),
    },
    child: Focus(autofocus: true, child: const AnaEkran()),
  ),
)

Klavye kısayolları (Ctrl+S gibi), masaüstü uygulamalarının temel beklentisi — Shortcuts + Actions widget'ları, Bölüm 7'de öğrendiğimiz Command pattern'e (bir eylemi bir "niyet" nesnesiyle temsil etme) benzer bir yapıda çalışır: LogicalKeySet, hangi tuş kombinasyonunun; Intent (burada KaydetIntent), "ne yapılmak istendiğini"; Action, "gerçekte ne olacağını" tanımlar — bu üçlü ayrım, aynı kısayolu farklı ekranlarda farklı davranışlara bağlamanı kolaylaştırır.

Pencere Yönetimi — window_manager Paketi

bash
flutter pub add window_manager
dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await windowManager.ensureInitialized();

  WindowOptions windowOptions = const WindowOptions(
    size: Size(1024, 768),
    minimumSize: Size(600, 400),
    center: true,
  );
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.show();
  });

  runApp(const MyApp());
}

Flutter'ın kendisi, pencere boyutu/konumu gibi işletim sistemi seviyesi özellikleri yerleşik olarak sunmaz (bu, mobilde "ekran boyutu" kavramının sabit olmasından farklı olarak, masaüstünde pencere yeniden boyutlandırılabilir, taşınabilir). window_manager, Bölüm 9 Konu 40'ta öğrendiğimiz platform channel mekanizmasının üzerine inşa edilmiş, pencere yönetimini Dart API'sine taşıyan bir pakettir.

Responsive Tasarımın Önemi Katlanıyor

Bölüm 4 Konu 17'de öğrendiğimiz MediaQuery/LayoutBuilder ile responsive tasarım, mobilde genelde "telefon vs tablet" ayrımıydı — web/masaüstünde, kullanıcı pencereyi istediği gibi boyutlandırabilir (küçük bir pencere, tam ekran, ultra geniş bir monitör). Bu yüzden, web/masaüstü hedefleyen bir uygulamada, Konu 17'deki breakpoint stratejisi "olsa iyi olur" değil, "zorunlu" hale gelir.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Flutter web, Dart kodunu CanvasKit/Skwasm (WebAssembly tabanlı Skia) ile bir <canvas> üzerine çizer — eski HTML renderer kaldırıldı, artık render motoru seçimi gerekmiyor.
  • dart:io, web'de çalışmaz — platforma göre farklı dosyaların derlenmesini sağlayan koşullu import (if (dart.library.html) '...') ile çözülür.
  • kIsWeb, aynı dosya içinde çalışma zamanı platform kontrolü için kullanılır; koşullu import, derleme zamanı kararı verir.
  • Masaüstü, native bir uygulama olarak derlenir (dart:io çalışır); MouseRegion (hover), Shortcuts/Actions (klavye kısayolları) gibi mobilde anlamsız etkileşim kalıpları burada gereklidir.
  • window_manager, pencere boyutu/konumu gibi Flutter'ın yerleşik sunmadığı masaüstü özelliklerini sağlar.
  • Web/masaüstünde, responsive tasarım (Bölüm 4 Konu 17) isteğe bağlı değil, zorunludur.

📝 Ödevler

  • [ ] Mevcut bir mobil projeni flutter run -d chrome ile web'de çalıştır, hangi widget'ların/layout'ların beklenmedik göründüğünü tespit et.
  • [ ] dart:io kullanan bir paket eklediğinde web build'inin neden hata verdiğini gözlemle, koşullu import ile bir çözüm tasarla (elle uygulamana gerek yok, tasarımını yaz).
  • [ ] kIsWeb kullanarak, web'de farklı davranan (örn. farklı bir buton metni gösteren) basit bir widget yaz.
  • [ ] (Masaüstünde çalışıyorsan) MouseRegion ile hover'da renk değiştiren bir kart, Shortcuts/Actions ile bir Ctrl+S kısayolu ekle.
  • [ ] Kendi cümlelerinle, "Flutter web'in neden normal bir HTML sayfası gibi değil, bir <canvas>'a çizilmiş bir resim gibi düşünülmesi gerektiğini" açıkla.

Sıradaki konu: Bölüm 12 — Konu 56: Custom RenderObject Yazımı