【Flutter web】ボタンがクリックできない?pointer_interceptorで解決

Flutter webでモーダル上のボタンなどがクリックできない謎
既存のFlutterアプリをweb化もして動作確認していたところ、Youtube動画のiframeを表示している画面でモーダルを表示してモーダルの「はい」「いいえ」のボタンをクリックしてみました。
なぜかYoutube動画の再生停止ボタンをクリックしたことになっていて、モーダル上のボタンがクリックできていません。
KeyboardDismissOnTap(
	dismissOnCapturedTaps: false,
	child: Dialog(),
),
初めは、KeyboardDismissOnTapが原因かと思いましたが、削除しても治らない…
Flutter webでボタンなどがクリックできない原因

In the dashed areas, mouse events won't work as expected. The
HtmlElementViewwill consume them before Flutter sees them. (破線の部分では、マウスイベントは期待通りに動作しません。Flutterがそれを見る前にHtmlElementViewがそれを消費してしまう。)
なるほど。。。
マウスジェスチャーに反応するHtmlElementView や PlatformViewウィジェットの上にFlutterウィジェットを重ねると、クリックはHtmlElementView や PlatformViewによって消費され、Flutterには中継されないみたいです。
その結果、FlutterウィジェットのonTapハンドラは期待通りに発火しないんですね。
HtmlElementView上でもonTapを実行するためにpointer_interceptorパッケージで解決
PointerInterceptorを使うことで、Web 上の基盤によってマウスイベントがキャプチャされるのを防ぐウィジェットです。
.png?w=1428&fit=max&fm=webp)
各PointerInterceptor(緑)はFlutterウィジェットとその下のHtmlElementViewの間でレンダリングします。
マウスイベントが背景のHtmlElementViewに届かないようになり、期待通りに動くようになりました。
kIsWeb
  ? PointerInterceptor(
      child: Dialog(),
    )
  : KeyboardDismissOnTap(
      dismissOnCapturedTaps: false,
      child: Dialog(),
    ),
注意点:多用するとパフォーマンスが悪化する
Using multiple
PointerInterceptorinstances on iOS can be slow and increases memory usage due to the performance overhead of the underlying platform view.
iOSで複数のPointerInterceptorインスタンスを使用すると、基盤となるプラットフォーム ビューのパフォーマンス オーバーヘッドにより、速度が低下し、メモリ使用量が増加する可能性があります。
まとめ
Flutter Webでは、HtmlElementView や PlatformView(今回のケースだとYouTubeの <iframe>)が背面にあると、その領域がタップやクリックイベントを奪ってしまい、上に重ねたFlutterウィジェットのボタンが反応しないという落とし穴がありました。
Web特有の挙動なので、モバイルアプリ開発だけだと気づきにくいですが、実際に遭遇してみると「なぜボタンが効かないのか?」という謎が理解できました。
今後、Flutterを Webに展開する際は、HtmlElementViewのクリック競合問題とPointerInterceptorの活用 を覚えておくと安心です。
Anycloudでは一緒に働くメンバーを募集しています!
Anycloudは、ユーザーの心を動かす体験を届けることを大切にしています。フルリモート・フルフレックスの環境のもと、ライフスタイルに合わせた働き方を実現しながら挑戦したい方を歓迎します。詳細はこちらをご覧ください。




