Flutter mobil uygulama geliştirmede WebView, harita ve yerel grafik gibi Platform View bileşenlerinin kare bütçesini nasıl tükettiğini; kompozisyon seçimi, profil ölçümü ve yaşam döngüsü kodlarıyla inceliyoruz.
Flutter Mobil Uygulama Geliştirmede Platform View Kare Zamanı
Flutter mobil uygulama geliştirmede Platform View maliyetini modellemek
Platform View, Flutter sahnesine yerel bir Android View veya UIKit UIView ekler. Bu nedenle maliyet sadece Dart tarafındaki build süresi değildir: yerel görünümün ölçüm-layout çizim zinciri, Flutter raster iş parçacığı ve işletim sistemi kompozitörü aynı ekranda çalışır. İlk adım olarak ekranları sınıflandırın: video, harita, WebView ve kamera önizlemesi sürekli piksel üreten bileşenlerdir; tek satırlık bir yerel tarih seçici ise geçici olarak açılmalıdır. Kod tabanında tüm yerel görünüm girişlerini bulmak için `rg "AndroidView|UiKitView|PlatformViewLink|WebViewWidget|GoogleMap" lib/` komutunu çalıştırın ve her sonuç için görünür kalma süresini, kaydırılabilir bir listenin içinde olup olmadığını ve etkileşim gereksinimini tabloya yazın.
Kare bütçesini ölçülebilir hale getirin: 60 Hz hedefte bir frame için yaklaşık 16.67 ms, 120 Hz hedefte yaklaşık 8.33 ms vardır. Flutter DevTools Performance görünümünde UI ve Raster çizgilerini ayrı okuyun. Yerel görünüm eklenince Raster çizgisi değil Android'in kompozisyon işi uzuyorsa, yalnızca `const` eklemek veya widget rebuild sayısını azaltmak sorunu çözmez. Android tarafında aynı senaryoda `adb shell dumpsys gfxinfo com.example.app framestats` çıktısını alarak geciken frame yüzdelerini ve `adb shell dumpsys SurfaceFlinger --latency` ile sunum aralıklarını doğrulayın. Bu ayrım, Dart CPU yükü ile SurfaceFlinger gecikmesini birbirine karıştırmayı önler.
Bir flutter eğitimi veya flutter kursu içinde Platform View anlatılırken sık atlanan ayrıntı şudur: yerel görünümü `ListView.builder` hücrelerinde oluşturmak, her hücreye bağımsız native view kimliği ve platform kaynağı tahsis eder. Örneğin 20 haber kartının her birinde canlı WebView açmak yerine, HTML önizlemesini Flutter `Text` ve `Image` ile gösterin; yalnızca seçilen makalede tek bir WebView route'u açın. Bu kararın teknik gerekçesi, kaydırma sırasında hem Flutter'ın sliver yerleşimi hem de yerel view hiyerarşisinin layout geçirmesinin aynı vsync penceresine girmesidir.
Platform View oluşturma, gesture arena ve yaşam döngüsü sınırı
Yerel görünümü açık bir platform dalıyla oluşturun ve gesture politikasını varsayıma bırakmayın. Aşağıdaki örnekte `EagerGestureRecognizer`, harita gibi tüm dokunma akışını yerel SDK'ya vermesi gereken bir bileşen içindir. Dikey kaydırılan bir Flutter sayfasına bunu doğrudan koyarsanız üstteki `Scrollable` drag yarışını kaybeder; bu durumda haritayı ayrı bir tam ekran route'a taşıyın veya yalnızca gereken recognizer türlerini kaydedin.
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class NativeChart extends StatelessWidget {
const NativeChart({super.key, required this.symbol});
final String symbol;
@override
Widget build(BuildContext context) {
final params = <String, Object>{'symbol': symbol, 'theme': 'dark'};
final gestures = <Factory<OneSequenceGestureRecognizer>>{
Factory(() => EagerGestureRecognizer()),
};
if (Platform.isAndroid) {
return AndroidView(
viewType: 'com.opendart/native-chart',
creationParams: params,
creationParamsCodec: const StandardMessageCodec(),
gestureRecognizers: gestures,
);
}
return UiKitView(
viewType: 'com.opendart/native-chart',
creationParams: params,
creationParamsCodec: const StandardMessageCodec(),
gestureRecognizers: gestures,
);
}
}Android tarafında factory kaydı `configureFlutterEngine` içinde bir kez yapılmalıdır. `PlatformView.dispose()` içinde WebView callback'leri, `CoroutineScope` işleri, sensor listener'ları ve adapter referansları kapatılmazsa Flutter widget ağacından çıkmış olsa bile Activity referansı tutulabilir. Özellikle ekran döndürme ve geri navigasyon testinde LeakCanary ile `Activity` sızıntısını doğrulayın.
class NativeChartFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
val options = args as? Map<*, *> ?: emptyMap<String, Any>()
return NativeChartPlatformView(context, options)
}
}
override fun configureFlutterEngine(engine: FlutterEngine) {
super.configureFlutterEngine(engine)
engine.platformViewsController.registry.registerViewFactory(
"com.opendart/native-chart",
NativeChartFactory()
)
}
class NativeChartPlatformView(
context: Context,
options: Map<*, *>
) : PlatformView {
private val chart = ChartView(context).apply {
setSymbol(options["symbol"] as? String ?: "UNKNOWN")
}
override fun getView(): View = chart
override fun dispose() {
chart.stopRendering()
chart.clearListeners()
}
}Cross platform mobil uygulama geliştirme için kompozisyon seçimi ve profil deneyi
Android WebView gibi bazı eklentiler, hibrit kompozisyon tercihini platforma özgü oluşturma parametresiyle açar. `webview_flutter_android` kullanılıyorsa aşağıdaki yapılandırmayı aday değişiklik olarak ayrı bir branch'te deneyin. Hibrit kompozisyon, yerel görünümün z-sırası, klavye ve erişilebilirlik davranışında daha tutarlı olabilir; karşılığında cihaz ve ekran içeriğine göre kompozisyon maliyeti değişebilir. Bu yüzden ayarı mimari kural değil, ölçülen ekran senaryosunun sonucu olarak seçin.
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';
Widget buildWebView(WebViewController controller) {
final params = AndroidWebViewWidgetCreationParams(
controller: controller.platform,
displayWithHybridComposition: true,
);
return WebViewWidget.fromPlatformCreationParams(params: params);
}Önce-sonra karşılaştırmasını tekrarlanabilir yapın. `flutter run --profile` ile fiziksel cihazda çalıştırın, ölçümden önce `adb shell dumpsys gfxinfo com.example.app reset` çağırın, ardından aynı 30 saniyelik senaryoyu uygulayın: WebView aç, üç kez hızlı kaydır, klavyeyi aç-kapat, geri dön. DevTools'ta timeline kaydını dışa aktarın; ardından `adb shell dumpsys gfxinfo com.example.app framestats` alın. Karar kriterini örneğin `p95 UI frame time`, `p95 raster frame time` ve 16.67 ms üstü frame oranı olarak belirleyin. Aynı testte Flutter çizgileri kısa ancak gfxinfo gecikmeleri uzunsa, sorun Dart kodu değil native kompozisyon veya WebView paint yüküdür.
iOS için aynı deneyde Instruments içindeki Core Animation ve Time Profiler şablonlarını kullanın. Core Animation'da offscreen rendering veya sürekli compositing görüldüğünde, Flutter tarafında `Opacity` ile yarı saydam bir üst katmanı UIKit görünümünün üstüne koymak yerine opak bir arka plan ve ayrı bir geçiş tasarlayın. Nedeni, yarı saydam katmanların alttaki piksel sonucuna ihtiyaç duyması ve kompozitörün ek ara yüzey oluşturabilmesidir. Platforma göre farklı bulgu çıkması normaldir; cross platform mobil uygulama geliştirme, aynı widget ağacının iki işletim sisteminde aynı render yolunu izlediği anlamına gelmez.
Flutter state management ile Platform View yeniden oluşturulmasını engellemek
Platform View'ı filtre, sekme veya yüklenme state'i değiştiğinde widget ağacından çıkarıp yeniden eklemek, yerel tarafta yeni WebView renderer'ı veya harita oturumu başlatabilir. flutter state management katmanında görünümün ömrünü veri state'inden ayırın: controller yalnızca URL, kamera konumu veya seçili sembol komutlarını taşısın; native view'un kendisi sabit bir `Key` ile aynı sekme ağacında kalsın. Aşağıdaki `IndexedStack`, sekme değişiminde platform görünümünü dispose etmez. Ancak görünmeyen WebView JavaScript veya video çalıştırıyorsa native tarafta ayrıca pause komutu göndermek gerekir; `IndexedStack` kaynak tüketimini kendiliğinden durdurmaz.
class ChartTabs extends StatefulWidget {
const ChartTabs({super.key});
@override
State<ChartTabs> createState() => _ChartTabsState();
}
class _ChartTabsState extends State<ChartTabs> {
int selected = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
SegmentedButton<int>(
segments: const [
ButtonSegment(value: 0, label: Text('Native chart')),
ButtonSegment(value: 1, label: Text('Orders')),
],
selected: {selected},
onSelectionChanged: (value) => setState(() => selected = value.first),
),
Expanded(
child: IndexedStack(
index: selected,
children: const [
KeyedSubtree(
key: ValueKey('native-chart-v1'),
child: NativeChart(symbol: 'AAPL'),
),
OrdersPanel(),
],
),
),
],
);
}
}Yaygın hata, state yönetim aracında `autoDispose` veya route-scoped provider kullanırken native kaynakları da aynı anda yok saymaktır. Riverpod, Bloc veya başka bir çözüm kullanmanız fark etmez: route kapanışında `MethodChannel.invokeMethod('pause')`, uygulama tekrar öne geldiğinde `resume` çağrısını açıkça modelleyin. Bunu `AppLifecycleListener` ile test edebilirsiniz: uygulamayı arka plana alıp geri getirirken ağ istek sayısını Android Studio Network Inspector veya Charles Proxy ile sayın. Amaç 'uygulama yaşam döngüsünü yönetmek' değil, örneğin arka planda dakikada 60 JavaScript timer çalışmasını sıfıra indirdiğinizi doğrulamaktır.
Erişilebilirlik, klavye ve katmanlama için üretim kontrolleri
Platform View içeren ekranda TalkBack veya VoiceOver ile odak sırasını manuel test edin. Flutter `Semantics` ağacı ile native view'un erişilebilirlik ağacı iki ayrı kaynak olduğundan, aynı başlığın iki kez okunması veya odak tuzağı oluşması mümkündür. Android özel görünümünde anlamlı açıklamayı doğrudan yerel view'a verin ve dekoratif alt görünümleri erişilebilirlikten çıkarın.
chart.contentDescription = "AAPL price chart"
chart.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
loadingDecoration.importantForAccessibility =
View.IMPORTANT_FOR_ACCESSIBILITY_NOKlavye testi için en az dört durumu otomatikleştirin: odak alma, IME açılması, cihaz döndürme, geri tuşuyla IME kapanması. Android tarafında `adb shell input keyevent KEYCODE_BACK` komutu, gerçek cihazdaki geri davranışını hızlıca tekrar etmek için kullanılabilir. WebView veya metin alanı olan Platform View'ı `SafeArea` ve `resizeToAvoidBottomInset` davranışıyla birlikte test edin; yerel view yüksekliği Flutter constraint'inden eski bir değer kullanıyorsa klavye altında kesilebilir. Bu tip hata, sadece emülatörde değil en az bir fiziksel cihazda DevTools timeline ve işletim sistemi klavyesiyle doğrulanmalıdır.
dart programlama eğitimi bağlamında önemli tasarım ayrımı şudur: `BuildContext` veya Flutter widget referansını native callback içinde saklamayın. Yerel callback'i `EventChannel` ya da `MethodChannel` üzerinden kimlikli veri olayına dönüştürün, Dart tarafında ekran halen mounted ise state'i uygulayın. Böylece geç gelen bir native callback'in kapanmış route üzerinde `setState() called after dispose()` üretmesini engellersiniz. Log korelasyonu için her Platform View örneğine `viewId` ve oturum UUID'si ekleyip hem Dart hem Android loglarına yazın.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Flutter mobil uygulama geliştirmede Platform View jank sorunu nasıl ölçülür?
`flutter run --profile` ile fiziksel cihazda aynı kullanıcı akışını çalıştırın. DevTools Performance kaydında UI ve Raster frame sürelerini alın, Android'de test öncesi `adb shell dumpsys gfxinfo paket.adi reset`, test sonrası `adb shell dumpsys gfxinfo paket.adi framestats` çalıştırın. Hibrit kompozisyon kapalı ve açık iki koşulda p95 frame süresi ile 16.67 ms üstü frame oranını karşılaştırın.
Flutter state management Platform View yeniden oluşmasını nasıl engeller?
Filtre veya sekme state'i değiştiğinde `AndroidView`, `UiKitView` ya da `WebViewWidget` widget'ını koşullu olarak kaldırmayın. Görünümü sabit `ValueKey` ile `IndexedStack` içinde koruyun; URL, kamera konumu ve seçimi controller olaylarıyla güncelleyin. Görünmeyen native görünümün timer, video veya konum kaynaklarını durdurmak için ayrıca `MethodChannel` üzerinden `pause` çağrısı gönderin.
Cross platform mobil uygulama geliştirmede WebView için hibrit kompozisyon ne zaman seçilmeli?
Klavye, erişilebilirlik, z-sırası veya Flutter ile native görünümün üst üste gelmesi sorunluysa hibrit kompozisyonu aday olarak deneyin. `webview_flutter_android` tarafında `AndroidWebViewWidgetCreationParams` içindeki `displayWithHybridComposition` değerini değiştirin. Kararı varsayımla değil, sabit bir kaydırma ve IME senaryosunda DevTools ile gfxinfo sonuçlarını karşılaştırarak verin.
Flutter eğitimi alırken Platform View için hangi edge case test edilmelidir?
Geri navigasyon sırasında geç gelen native callback test edilmelidir. Native callback'leri `EventChannel` veya `MethodChannel` ile olay verisine dönüştürün, Dart tarafında `mounted` kontrolü yapın ve route kapandıktan sonra callback geldiğinde state yazılmadığını widget testi ile doğrulayın. Android'de ayrıca LeakCanary ile PlatformView `dispose()` sonrasında Activity referansının tutulmadığını kontrol edin.
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.



