Edited at
FlutterDay 10

Flutterの最後の秘宝、Platform Viewの実装を調べに行く

この記事はFlutter Advent Calendar 2018の10日目の記事です。


まえおき

Flutter 1.0のリリースがアナウンスされましたね、めでたい!! :confetti_ball: :congratulations: :tada:

さて、Flutter Liveで は紹介されていなかったようですが 1も紹介されていたようですが、Google Developers Blogのポストを見ていると、記事の中ほどに プラットフォーム ビュー 2という見出しとその説明があります。

今までAndroidでしか実現できていなかったのが、いよいよiOSでも使用できるようになった、ということのようです。

私がいま所属している会社的にはこの機能のおかげで大変救われました。

それはそうとして、この機能、どう実現されているのでしょうか?

気になりませんか? 気になりますよね? ね?


Platform View とは

Platform View とは、FlutterのWidgetツリーの中に、AndroidやiOSと言ったプラットフォームのViewを表示する機能、

またはそれを行うFlutterのWidgetとプラットフォーム側で実装するViewを指します。

通常、Flutterのアプリケーション中にプラットフォームのViewを表示することはできません。3

Flutterが全ての画面を自前で描画しているためです。

この自前で描画を行う戦略のおかげで、ワンソースでマルチプラットフォームを対応できているのですが、プラットフォーム固有のViewを表示したいケースでは困ることとなります。

どうしてもプラットフォームの機能を使用したい場合、通常はPluginを作成してプラットフォームの機能を呼び出します。

ところがViewに関しては、上記の事情でそうも行かなかった、という訳です。

Platform Viewは、今まで手が出なかったプラットフォームのViewの外観と機能を使用できるようにしてくれるものです。


使い方

この記事の趣旨ではないのでごく軽い説明に留めます。

Pluginを作成するのと似た要領です。

以下のものを用意してがっちゃんこします。


  • Dart


    • PlatformViewを表示するStatefulWidgetを作成

    • Android向けには AndroidView, iOS向けには UIKitView を作成して build() の戻り値としてreturn



  • Android


    • 表示したいViewを生成する、PlatformView を実装したクラスを用意


    • PlatformView 実装クラスをインスタンス化する、 PlatformViewFactory を実装したクラスを用意

    • plugin内で、PlatformViewFactory実装クラスを登録



  • iOS


    • 表示したいUIViewを生成する FlutterPlatformView プロトコルに準拠したクラスを用意


    • FlutterPlatformView 準拠クラスをインスタンス化する、 FlutterPlatformViewFactory プロトコル準拠クラスを用意

    • plugin内で、 FlutterPlatformViewFactory 準拠クラスを登録



Androidでの作り方はこちらに解説があるので、読んでみてください。

より実践的な作り方はwebview_flutter の実装が参考になると思います。


Platform View一番の謎

Platform Viewで一番の謎…それは

いかにしてプラットフォームのViewの描画をFlutterの世界に取り込んでいるか

ですね!

Android/iOS共に、Flutterで作成したアプリケーションは、Activity/UIViewControllerの上に FlutterView という、自前描画した画面内容を書き出すためのViewをひとつだけ持った構成になっています。

この FlutterView の上にプラットフォームのViewを重ねてしまうとFlutterの世界が見えなくなってしまいます。

そのため、Widgetツリー内にViewの内容を表示するという要求を満たすためには、どうしても FlutterView より前面にViewを配置するわけには行かないのです。

この制限を回避するため、各プラットフォームではまたトリッキーな実装が行われていました。


Android

VirtualDisplay というクラスが使用されています。

つまり、仮想的なディスプレイを作り、そこに目的のViewを配置することで、その表示内容をFlutterの世界に転送する、という手段を取っています。

図にすると大体こんな感じ。

(コード全部は追いきれていないので、大体こんな感じ、というものです。間違っていたらコメント等で教えてください)

ソースコードはFlutterのEngineリポジトリに入っています。


iOS

UIViewの描画内容は、UIViewが保持しているCALayerが保持しています。

その保持内容をSkiaのクラス経由でFlutterViewまで流しているようです。

図にするとこんな感じ。

(読み切れていないのであまり自信がないです。間違っていたらコメント等で教えてください)

ソースコードはAndroidと同じくEngineリポジトリに入っています。


おしまい

実は描画内容転送以外にもタッチイベントの転送など、随所に工夫が見られます。

興味のある人はソースを読んでみると楽しいと思います。4





  1. コメントをいただきました。私が見逃していただけだったようです… 



  2. 原文では Platform views 



  3. flutter_webview_pluginのように、Flutterの画面を保持しているプラットフォームのViewより前面に、出したいプラットフォームのViewを配置する、という荒業はあります。が、Flutterの画面が隠れてしまうので色々問題になるわけです。 



  4. しかしドキュメントコメントなどがそんなにないEngineの中を読み解いていくのはなかなかに大変です。今回の内容分を把握するだけでも1日かかりました。誰か読み方を教えてください…