Flutterでアプリ開発を行っていると、
WebViewを使って広告や動的コンテンツを表示する場面が多くあります。
しかし、特にiOSでは注意しなければならない落とし穴があります。
それは、
「画面に表示されないとWebView内のJavaScriptが動作しない」
という問題です。
理由はシンプルでありながら、少し深い背景があります。
AppleのWKWebView
では、画面外にある場合、リソース消費を抑えるため、描画やJavaScriptの実行を停止する仕様となっております。
「見えていないものにCPUを使わない」という合理的な設計思想に基づく動作です。
また、FlutterにおけるWebViewWidget
は通常のWidgetとは異なり、ネイティブ側のPlatformView
として扱われます。
そのため、Flutterのスクロールやレイアウトサイクルとはタイミングがずれてしまい、WebViewの描画が遅れたり、JavaScriptの実行タイミングが遅延しやすくなるのです。
この二重の要因が重なることで、
「画面内に入りきるまではWebView内のJavaScriptが動かない」
という現象が発生します。
WebViewに設定したJavaScriptが最初は動かない
スクロールして画面内に入った瞬間に、ようやく動き始める
最悪の場合、画面外にある間は何も動作しない
リストやバナーに組み込んだ際、読み込み失敗のように見えてしまう
このような現象が発生することがあります。
Flutterにはvisibility_detector
という便利なパッケージが存在します。
これを利用することで、Widgetが画面に表示されたタイミングを検出することが可能です。
つまり、
「見えるようになった瞬間に初めてロードを開始する」
という設計に切り替えることで、問題を回避できます。
この方法は、現場でも最も多く採用されています。
可能であれば、WebViewの高さを固定(例えば高さ200pxなど)して、
初期表示時から画面内に収まる位置に配置する方法も有効です。
これにより、ページロード直後からJavaScriptが問題なく動作します。
ただし、ページ内容によって高さが変わるバナー広告など、動的な高さが必要なケースでは適用が難しい場合があります。
特にシンプルな画面構成であれば、CustomScrollView
のような複雑なSliver構造をやめて、SingleChildScrollView
+Column
構成に切り替えることも非常に有効です。
この方法であれば、スクロールの管理がFlutter側のみとなるため、
WebViewの表示判定やスクロール干渉に起因する問題を大きく軽減できます。
もちろん、リストアイテムが非常に多い場合には注意が必要ですが、
適度な量のコンテンツであれば、非常に効果的な解決策となります。
ちなみに、Androidでは
WebViewが画面外でも比較的自由に動作する
スクロール中でもJavaScriptの実行に大きな制約がない
という特性があるため、
この問題は基本的にiOS特有のものと考えたほうがよいでしょう。
クロスプラットフォーム開発を行う際には、
iOS実機での検証を怠らないことが非常に重要です。
問題 | 対策 |
---|---|
画面外だとWebView内のJavaScriptが動かない(特にiOS) | VisibilityDetectorで表示を検知してからloadする |
そもそも画面内に入る前に動作させたい | 高さを固定して初期表示時から画面内に配置する |
CustomScrollViewとの相性が悪い | SingleChildScrollViewに切り替える |