Flutter ile cross-platform uygulama geliştiriyorsun ama bazı özellikler native API gerektiriyor — HealthKit, Face ID, ARKit, Core Bluetooth. Bu durumda Platform Channel devreye girer. Flutter'ın Dart tarafı ile iOS'un Swift tarafı arasında köprü kurar.
💡 Hızlı Not: Flutter 3.0+ ile Pigeon code generator önerilir. Manuel MethodChannel hala çalışır ama Pigeon tip-güvenli ve daha az boilerplate sağlar.
İçindekiler
- Platform Channel Nedir?
- MethodChannel ile İletişim
- EventChannel ile Stream
- Pigeon ile Tip-Güvenli İletişim
- PlatformView: Native UIKit in Flutter
- Add-to-App: Flutter'ı Mevcut iOS App'e Ekle
- FlutterEngine Yönetimi
- Performance Karşılaştırma
- Ne Zaman Native, Ne Zaman Flutter?
- Best Practices
Platform Channel Nedir? {#platform-channel}
Platform Channel, Flutter (Dart) ile native platform (Swift/Kotlin) arasında asenkron mesajlaşma mekanizmasıdır:
Channel Tipi | Yön | Kullanım |
|---|---|---|
MethodChannel | İki yönlü | Tek seferlik çağrılar (getBatteryLevel) |
EventChannel | Native → Dart | Sürekli veri akışı (location updates) |
BasicMessageChannel | İki yönlü | Düşük seviye mesajlaşma |
MethodChannel ile İletişim {#method-channel}
dart
1// Dart tarafı2import 'package:flutter/services.dart';3 4class BatteryService {5 static const _channel = MethodChannel('com.myapp/battery');6 7 Future getBatteryLevel() async { 8 final int level = await _channel.invokeMethod('getBatteryLevel');9 return level;10 }11 12 Future isCharging() async { 13 return await _channel.invokeMethod('isCharging');14 }15}swift
1// iOS Swift tarafı (AppDelegate.swift)2import Flutter3import UIKit4 5@UIApplicationMain6@objc class AppDelegate: FlutterAppDelegate {7 override func application(8 _ application: UIApplication,9 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?10 ) -> Bool {11 let controller = window?.rootViewController as! FlutterViewController12 let channel = FlutterMethodChannel(13 name: "com.myapp/battery",14 binaryMessenger: controller.binaryMessenger15 )16 17 channel.setMethodCallHandler { [weak self] call, result in18 switch call.method {19 case "getBatteryLevel":20 let level = self?.getBatteryLevel() ?? -121 result(level)22 case "isCharging":23 let charging = self?.isCharging() ?? false24 result(charging)25 default:26 result(FlutterMethodNotImplemented)27 }28 }29 30 return super.application(application, didFinishLaunchingWithOptions: launchOptions)31 }32 33 private func getBatteryLevel() -> Int {34 UIDevice.current.isBatteryMonitoringEnabled = true35 return Int(UIDevice.current.batteryLevel * 100)36 }37 38 private func isCharging() -> Bool {39 UIDevice.current.isBatteryMonitoringEnabled = true40 return UIDevice.current.batteryState == .charging41 }42}EventChannel ile Stream {#event-channel}
dart
1// Dart - sürekli konum güncellemesi al2class LocationService {3 static const _channel = EventChannel('com.myapp/location');4 5 Stream6 return _channel.receiveBroadcastStream().map((event) {7 final map = Map.from(event); 8 return {9 'latitude': map['latitude'] as double,10 'longitude': map['longitude'] as double,11 };12 });13 }14}swift
1// iOS - EventChannel handler2class LocationStreamHandler: NSObject, FlutterStreamHandler, CLLocationManagerDelegate {3 private let locationManager = CLLocationManager()4 private var eventSink: FlutterEventSink?5 6 func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {7 self.eventSink = events8 locationManager.delegate = self9 locationManager.startUpdatingLocation()10 return nil11 }12 13 func onCancel(withArguments arguments: Any?) -> FlutterError? {14 locationManager.stopUpdatingLocation()15 eventSink = nil16 return nil17 }18 19 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {20 guard let location = locations.last else { return }21 eventSink?([22 "latitude": location.coordinate.latitude,23 "longitude": location.coordinate.longitude,24 ])25 }26}Pigeon ile Tip-Güvenli İletişim {#pigeon}
dart
1// pigeon/messages.dart - Pigeon şema tanımı2import 'package:pigeon/pigeon.dart';3 4class SearchRequest {5 String? query;6 int? limit;7}8 9class SearchResponse {10 List? results; 11 int? totalCount;12}13 14@HostApi()15abstract class SearchApi {16 SearchResponse search(SearchRequest request);17}18 19// pigeon generate komutu ile Swift ve Dart kodu otomatik oluşturulur20// flutter pub run pigeon --input pigeon/messages.dartPlatformView: Native UIKit in Flutter {#platform-view}
swift
1// iOS Native MKMapView'ı Flutter'da göster2class MapViewFactory: NSObject, FlutterPlatformViewFactory {3 func create(withFrame frame: CGRect, viewIdentifier viewId: Int64,4 arguments args: Any?) -> FlutterPlatformView {5 return MapPlatformView(frame: frame, viewId: viewId, args: args)6 }7}8 9class MapPlatformView: NSObject, FlutterPlatformView {10 private let mapView: MKMapView11 12 init(frame: CGRect, viewId: Int64, args: Any?) {13 mapView = MKMapView(frame: frame)14 super.init()15 }16 17 func view() -> UIView { mapView }18}dart
1// Dart tarafında kullanım2Widget build(BuildContext context) {3 return UiKitView(4 viewType: 'native-map-view',5 creationParams: {'latitude': 41.0, 'longitude': 29.0},6 creationParamsCodec: StandardMessageCodec(),7 );8}Add-to-App: Flutter'ı Mevcut iOS App'e Ekle {#add-to-app}
Mevcut native iOS uygulamana Flutter modülü ekleyebilirsin:
swift
1// Podfile'a ekle2// pod 'Flutter', :podspec => '../my_flutter/.ios/Flutter/Flutter.podspec'3 4// FlutterViewController'ı göster5let flutterEngine = FlutterEngine(name: "my engine")6flutterEngine.run()7 8let flutterVC = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)9present(flutterVC, animated: true)FlutterEngine Yönetimi {#flutter-engine}
swift
1// Engine group ile birden fazla Flutter ekranı2class FlutterEngineManager {3 static let shared = FlutterEngineManager()4 let engineGroup = FlutterEngineGroup(name: "multi-engine", project: nil)5 6 func makeEngine(entrypoint: String? = nil) -> FlutterEngine {7 engineGroup.makeEngine(withEntrypoint: entrypoint, libraryURI: nil)8 }9}Performance Karşılaştırma {#performance}
Metrik | Flutter | Native |
|---|---|---|
Startup time | +200-500ms | Baseline |
Memory overhead | +30-50MB | Baseline |
Frame rate | 60fps (çoğunlukla) | 60-120fps |
Platform channel latency | ~0.1ms | N/A |
App size | +5-15MB | Baseline |
Ne Zaman Native, Ne Zaman Flutter? {#ne-zaman}
Native tercih et:
- HealthKit, ARKit, Core Bluetooth gibi platform-specific API yoğunsa
- Maksimum performans kritikse (oyun, video)
- Küçük ekip, tek platform
Flutter tercih et:
- iOS + Android aynı anda
- UI-yoğun uygulamalar
- Hızlı prototipleme
- Büyük ekip, kod paylaşımı önemli
Best Practices {#best-practices}
- Pigeon kullan — manual MethodChannel yerine
- PlatformView minimize et — performans etkisi var
- Engine warming — FlutterEngine'i önceden başlat
- Channel isimlendirme — reverse domain notation (com.myapp/feature)
- Error handling — her iki tarafta da try-catch
Hata Yönetimi ve Debug {#error-handling}
Platform Channel'da hata ayıklama kritiktir. Dart ve Swift arasında hata iletişimi düzgün kurulmalıdır:
swift
1// Swift tarafında detaylı hata döndürme2channel.setMethodCallHandler { call, result in3 switch call.method {4 case "processImage":5 guard let args = call.arguments as? [String: Any],6 let path = args["path"] as? String else {7 result(FlutterError(8 code: "INVALID_ARGS",9 message: "path parametresi gerekli",10 details: "Beklenen: [String: Any] icinde 'path' key'i"11 ))12 return13 }14 15 do {16 let processed = try ImageProcessor.process(path: path)17 result(processed)18 } catch let error as ImageError {19 result(FlutterError(20 code: "PROCESSING_FAILED",21 message: error.localizedDescription,22 details: "File: \(path), Error code: \(error.code)"23 ))24 } catch {25 result(FlutterError(26 code: "UNKNOWN",27 message: error.localizedDescription,28 details: nil29 ))30 }31 default:32 result(FlutterMethodNotImplemented)33 }34}dart
1// Dart tarafında hata yakalama2Future processImage(String path) async { 3 try {4 final result = await _channel.invokeMethod('processImage', {'path': path}); 5 return result ?? '';6 } on PlatformException catch (e) {7 switch (e.code) {8 case 'INVALID_ARGS':9 throw ArgumentError(e.message ?? 'Gecersiz arguman');10 case 'PROCESSING_FAILED':11 throw ProcessingException(e.message ?? 'Islem basarisiz', details: e.details);12 default:13 throw Exception('Bilinmeyen hata: ' + e.message);14 }15 }16}Channel Performans Optimizasyonu {#channel-performance}
Teknik | Aciklama | Kazanc |
|---|---|---|
Batch calls | Birden fazla cagriyi tek mesajda birlestir | %40-60 latency azalma |
Binary codec | StandardMessageCodec yerine custom binary format | %30 daha az veri transferi |
Background thread | Agir native islemleri DispatchQueue.global()'da calistir | UI donmasi engellenir |
Pigeon | Tip-guvenli code generation, az boilerplate | Hata orani %80 duser |
Engine cache | FlutterEngine'i once baslatip hazirda tut | Ilk acilis %50 hizlanir |
swift
1// Background thread'de agir islem2channel.setMethodCallHandler { call, result in3 if call.method == "heavyComputation" {4 DispatchQueue.global(qos: .userInitiated).async {5 let output = performHeavyTask()6 DispatchQueue.main.async {7 result(output)8 }9 }10 }11}Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var: **Kaynaklar:** - [Flutter: Platform Channels](https://docs.flutter.dev/platform-integration/platform-channels) - [Flutter: Add-to-app](https://docs.flutter.dev/add-to-app) - [Pigeon Package](https://pub.dev/packages/pigeon)

