flutter mobil uygulama geliştirme projelerinde harita, WebView ve kamera gibi native ekranları Platform View ile gömerken kare bütçesini, yaşam döngüsünü ve gesture çakışmalarını ölçerek yönetmenin teknik yolu.
Flutter Mobil Uygulama Geliştirmede Platform View Performans Tasarımı
Flutter mobil uygulama geliştirmede doğru Platform View bileşimini seçmek
Harita, ödeme formu, kamera önizlemesi veya mevcut bir native SDK, Flutter widget'ına çevrilemediğinde Platform View kullanılır. Android tarafında `AndroidViewSurface` ile oluşturulan surface tabanlı yaklaşım, native `View` nesnesini Flutter sahnesine doğrudan bileştirir; texture veya sanal ekran tabanlı yaklaşım ise native çıktıyı bir dokuya kopyalar. Seçim teorik değil, cihaz hedefi ve etkileşim tipine göre yapılmalıdır: kaydırılan tam ekran harita için surface tabanlı yol, sık `Transform`, `Opacity` veya karmaşık clip altında kalan küçük bir preview için texture tabanlı yol ayrı ayrı profile edilmelidir. Kopyalama yolunda her karede GPU texture güncellemesi oluşabilir; surface yolunda ise Android compositor katmanları ve Flutter katmanları arasında z-order, klavye ve geçiş davranışları test edilmelidir.
Aşağıdaki düşük seviye kurulum, `AndroidView` yerine hangi platform view surface'inin üretildiğini açık eder. `initSurfaceAndroidView` seçimi, native görünümü Flutter render ağacına surface olarak bağlar. Bu ayrım, flutter eğitimi sırasında genellikle gizlenir; üretimde ise harita üstünde açılan bottom sheet, klavye ve hero geçişleri gibi senaryolarda belirleyicidir.
class NativeMapSurface extends StatefulWidget {
const NativeMapSurface({super.key, required this.styleJson});
final String styleJson;
@override
State<NativeMapSurface> createState() => _NativeMapSurfaceState();
}
class _NativeMapSurfaceState extends State<NativeMapSurface> {
static const _viewType = 'com.opendart/native_map';
@override
Widget build(BuildContext context) {
return PlatformViewLink(
viewType: _viewType,
surfaceFactory: (context, controller) => AndroidViewSurface(
controller: controller as AndroidViewController,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
),
onCreatePlatformView: (params) {
return PlatformViewsService.initSurfaceAndroidView(
id: params.id,
viewType: _viewType,
layoutDirection: TextDirection.ltr,
creationParams: {'styleJson': widget.styleJson},
creationParamsCodec: const StandardMessageCodec(),
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..create();
},
);
}
}Bu widget'a rastgele `Key` vermeyin. Örneğin filtre değiştiğinde `ValueKey(filter.hashCode)` kullanmak, Flutter'ın eski platform view'ı dispose edip yeni native harita yaratmasına neden olabilir. Harita SDK'sının GL context, tile cache ve ağ bağlantısını tekrar kurması, kullanıcı bir filtre çipine bastığında yüzlerce milisaniyelik ana iş parçacığı yükü yaratır. View kimliği yalnızca native örneğin gerçekten değişmesi gerektiğinde değişmelidir; marker ve kamera güncellemeleri mevcut controller'a kanal mesajıyla gönderilmelidir.
Native yaşam döngüsü ve kanal trafiğini sınırlandırma
Platform View factory'si her oluşturma işleminde yalnızca view üretmemeli, `dispose()` içinde SDK kaynaklarını kapatmalıdır. Özellikle kamera, video ve harita SDK'ları native callback'lerini Activity veya `ViewTreeLifecycleOwner` üzerinden tutabilir. Flutter widget'ı ağaçtan çıktığında callback kaldırılmazsa, kapatılmış bir `FlutterEngine` sonrasında eski view'a mesaj gönderen sızıntılar görülür. Android Studio Memory Profiler'da ekranı 20 kez açıp kapattıktan sonra retained `MapView`, `Activity` ve listener sayısını karşılaştırın.
class NativeMapFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
val style = (args as? Map<*, *>)?.get("styleJson") as? String ?: "{}"
return NativeMapPlatformView(context, style)
}
}
class NativeMapPlatformView(
context: Context,
style: String
) : PlatformView {
private val mapView = MapView(context).apply {
setStyleJson(style)
onStart()
}
override fun getView(): View = mapView
override fun dispose() {
mapView.onStop()
mapView.clearListeners()
mapView.onDestroy()
}
}Dart ile native taraf arasında her konum, zoom veya kamera hareketinde `MethodChannel.invokeMethod` çağırmak yerine olayları birleştirin. 60 Hz kamera callback'i, JSON ile serileştirilmiş 60 kanal mesajı demektir; hem Dart UI isolate'ında decode hem de Android main thread tarafında dispatch oluşur. Native tarafta son koordinatı 100 ms boyunca biriktirip tek `EventChannel` olayı gönderin veya yalnızca görünür marker kümesi değiştiğinde yayın yapın. Kanal yükünü Android Studio CPU Profiler ve Flutter DevTools Timeline'da `PlatformChannel` olaylarıyla doğrulayın; hedef, sürekli gesture sırasında olay sayısını örneğin 600/10 sn seviyesinden 100/10 sn altına indirmek değil, ürünün gerekli çözünürlüğünü koruyarak ölçülebilir bir üst sınır koymaktır.
Flutter state management ile native view yeniden oluşumunu engellemek
flutter state management katmanında en sık hata, ekranın tamamını seçilen marker, loading bayrağı ve tema verisiyle birlikte izlemektir. `Consumer` her marker güncellemesinde `NativeMapSurface` yeniden build edilse bile platform view çoğu durumda korunabilir; ancak yanlış key, değişen `viewType` veya koşullu widget dalı native instance'ı yok eder. Riverpod'un `select` API'siyle native surface'i yalnızca style değişiminde, bilgi panelini ise yalnızca seçili marker değişiminde dinletin. Bu ayrım, Dart programlama eğitimi içinde immutable state anlatımının ötesinde, native kaynak yaşam döngüsünü doğrudan etkiler.
class MapScreen extends ConsumerWidget {
const MapScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final styleJson = ref.watch(
mapControllerProvider.select((s) => s.styleJson),
);
final selectedMarker = ref.watch(
mapControllerProvider.select((s) => s.selectedMarker),
);
return Stack(
children: [
RepaintBoundary(
child: NativeMapSurface(
key: const PageStorageKey('primary-native-map'),
styleJson: styleJson,
),
),
if (selectedMarker != null)
Align(
alignment: Alignment.bottomCenter,
child: MarkerSheet(marker: selectedMarker),
),
],
);
}
}Buradaki `RepaintBoundary`, native view'ın kendisini raster cache'e almaz; Platform View dışarıdan bileştirildiği için bunu beklemek hatadır. Boundary'nin yararı, Flutter tarafındaki `MarkerSheet` animasyonlarının üst widget ağacına yaydığı paint invalidation alanını ayırmaktır. DevTools Inspector'da `Repaint Rainbow` seçeneğini açın: panel animasyonu sırasında tüm `Stack` yerine yalnızca overlay alanı renk değiştiriyorsa sınır doğru yerdedir. Harita görüntüsünün her frame'de rainbow ile yanması, çoğu kez Flutter repaint'inden değil native SDK'nın kendi render döngüsünden kaynaklanır ve ayrı ölçülmelidir.
Cross platform mobil uygulama geliştirme için ölçüm protokolü
Platform View kararını debug modundaki gözle değerlendirmeyin. Önce sabit bir senaryo tanımlayın: uygulamayı soğuk başlatın, harita ekranını açın, 10 saniye boyunca programatik olarak kamera kaydırın, bir marker seçin ve bottom sheet'i açıp kapatın. Android'de `flutter run --profile` ile çalıştırın, DevTools Performance görünümünde bu akışı kaydedin ve UI ile raster frame sürelerinin p50, p95 ve maksimum değerlerini dışa aktarın. Ardından aynı cihaz, aynı veri kümesi ve aynı ağ koşulunda surface ile alternatif bileşimi tekrar çalıştırın. Önce-sonra karşılaştırmasında iki ayrı koşuyu değil, en az beş koşunun medyanını kullanın; ilk koşu shader ve tile cache ısınmasını içerir.
# Profile APK ile gerçek cihaza bağlan
flutter run --profile -d emulator-5554
# Android framework frame istatistiklerini senaryo öncesi temizle
adb shell dumpsys gfxinfo com.example.app reset
# Senaryodan sonra jank ve frame istatistiklerini al
adb shell dumpsys gfxinfo com.example.app framestats > gfxinfo-surface.txt
# Flutter widget testinden değil, cihaz üstü akıştan timeline alın
flutter pub global run devtoolsAndroid `gfxinfo` çıktısı Flutter ve native view'ın pencere seviyesindeki frame maliyetini verir, fakat hangi Dart widget'ının pahalı olduğunu söylemez. Bu nedenle DevTools Timeline ile birlikte kullanılır: Timeline'da `Raster thread` uzunsa Flutter çizimi veya texture güncellemesi; Android System Trace içinde main thread'de uzun `doFrame` ve native SDK callback'leri varsa view tarafı araştırılır. iOS için aynı senaryoyu release veya profile benzeri optimize derlemede Instruments Core Animation ile kaydedin; `Color Blended Layers` ve `Hitches` işaretleri, opak olmayan native overlay'in compositor maliyetini gösterebilir. flutter kursu örneklerinde yalnızca FPS etiketi vermek yerine, ölçüm tablosuna cihaz modeli, işletim sistemi, build modu, veri sayısı ve p95 frame süresini eklemek yeniden üretilebilir sonuç sağlar.
Gesture, klavye ve test katmanındaki Platform View tuzakları
Boş `gestureRecognizers` kümesi, pointer olaylarının Flutter gesture arena tarafından native view'a otomatik devredileceği anlamına gelmez; karar, hit test ve recognizer rekabetiyle verilir. Harita yatay kaydırılırken sayfanın `PageView` bileşeni de hareket ediyorsa, native view için hedefli bir recognizer tanımlayın. Bu sayede tek parmak sürükleme haritaya giderken sayfa geçişini iki parmak hareketine veya ekran kenarı hareketine ayırabilirsiniz. Uygulama davranışını fiziksel Android ve iOS cihazlarda doğrulayın, çünkü platform view pointer forwarding ayrıntıları emülatörde aynı gecikme profilini vermeyebilir.
gestureRecognizers: {
Factory<OneSequenceGestureRecognizer>(
() => EagerGestureRecognizer(),
),
},`EagerGestureRecognizer` native view'ın gesture arena'yı hemen kazanmasını sağlar; bu nedenle parent içindeki `RefreshIndicator`, dikey `Scrollable` veya dismiss gesture'ları çalışmayabilir. Her yere eklemek yerine Flutter Integration Test ile gerçek çatışmayı kilitleyin. Ayrıca platform view içindeki native `EditText` veya `WKWebView` odak aldığında klavye yüksekliği değişir; Flutter layout'unun `MediaQuery.viewInsets.bottom` değerini kullandığını ve native view'ın görünür alan altında kalmadığını test edin.
void main() {
testWidgets('native map marker opens details without recreating view',
(tester) async {
await tester.pumpWidget(const ProviderScope(child: App()));
await tester.tap(find.byKey(const Key('open-map')));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('marker-istanbul')));
await tester.pump();
expect(find.text('Istanbul'), findsOneWidget);
expect(find.byType(NativeMapSurface), findsOneWidget);
});
}Widget testi native SDK'nın gerçek render'ını çalıştırmaz; burada yalnızca Flutter ağacının platform view host'unu koruduğunu denetlersiniz. Gesture, klavye, z-order ve GPU davranışı için `integration_test` paketiyle cihaz üzerinde ikinci bir test koşusu gerekir. Bu çift katman yaklaşımı, cross platform mobil uygulama geliştirme ekiplerinde Flutter kodundaki regressions ile platform compositor kaynaklı regressions'ı aynı hata sınıfına koymayı önler.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
flutter eğitimi kapsamında Platform View performansı nasıl ölçülür?
`flutter run --profile` ile gerçek cihazda sabit bir kullanıcı akışını kaydedin. Flutter DevTools Performance ekranından UI ve raster frame sürelerini, Android'de `adb shell dumpsys gfxinfo paket.adı framestats` çıktısından pencere frame verisini alın. Surface ve texture tabanlı iki uygulamayı en az beşer kez çalıştırıp ilk ısınma koşusunu ayrı raporlayın.
flutter state management native haritayı neden yeniden oluşturur?
State değişimi tek başına native view'ı yeniden oluşturmaz; değişen `Key`, koşullu olarak widget'ın ağaçtan çıkması, `viewType` değişimi veya farklı bir host widget'a taşınması oluşturur. Riverpod `select` ile harita style verisini marker seçimi gibi yüksek frekanslı state alanlarından ayırın ve platform view için sabit bir `PageStorageKey` kullanın.
flutter kursu projelerinde WebView ve Flutter gesture çakışması nasıl çözülür?
Önce çatışan gesture'ı cihazda belirleyin. WebView'ın tüm pointer akışını alması gerekiyorsa `EagerGestureRecognizer` kullanın, ancak bu seçim parent `Scrollable` ve `RefreshIndicator` davranışını devre dışı bırakabilir. Daha güvenli tasarım, WebView alanını sayfanın bağımsız kaydırma bölgesine almak veya gesture yönlerini ürün gereksinimine göre ayırmaktır.
dart programlama eğitimi alan biri Platform View kanal mesajlarını nasıl azaltır?
60 Hz konum veya scroll callback'ini her olayda `MethodChannel` ile göndermeyin. Native tarafta son değeri saklayıp 100 ms pencerede tek `EventChannel` olayı yayınlayın ya da anlamlı durum değişimlerini gönderin. DevTools Timeline'da `PlatformChannel` olay sayısını senaryo süresiyle birlikte kaydederek sınırı doğrulayın.
AI / LLM Discovery
Bu makale Opendart Akademi Flutter eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

