🔌

Flutter pluginのPlatformViewで、Assets画像を使用するには?

2022/06/22に公開

はじめに

AssetsをiOS/Androidの各プラットフォームで使用する方法は、公式ドキュメントに説明が載っています。しかし、いざPlatformViewを提供するFlutter Pluginを実装をしてみると、それでは不十分でした。(as of 2022/6/17)

では、Flutter Plugin内のPlatformViewのみ利用する画像を管理・使用したいとき、どうやって実装したらいいでしょうか?

以下に、実際の調査・実装をもとに、Assets画像をAndroid/iOSのPlatformViewで利用する方法をまとめました。

前提

  • Flutter 3.0
  • pubspec.yaml
flutter:
  assets:
    assets/favorite_icon.png
    assets/2.0x/favorite_icon.png
    assets/3.0x/favorite_icon.png

Android

公式ドキュメントは、registrarを使用する方法を説明しています。しかし、registerWithはdeprecatedとなっています

そのため、代わりにFlutterPluginBindingを使ってAssetを利用します。公式のサンプルがなかったため、試行錯誤した結果、以下の方法でAsset pathを取得し、bitmapを生成することができました。サンプルコードにこちらになります。

val assetPath = flutterPluginBinding.flutterAssets.getAssetFilePathByName(
    "assets/favorite_icon.png",
    "myplugin",
)
context.assets.open(assetPath).use {
    BitmapFactory.decodeStream(it).let { bitmap ->
        // Using bitmap
    }
}

※ PlatformView内での実装を想定しています。

💡ポイント

  • FlutterAssets.getAssetFilePathByNameの各パラメータについて
    • packageName: mypluginは、開発しているplugin nameで、javaのpackage nameではありません
    • assetFileName: pubspec.yamlで指定しているパスになります
  • 取得したassetPathは、apk内のassets/フォルダのサブパスを示しています

⚠️注意

  • うまくいかないときは、しれっとFileNotFoundのエラーが出ているので注意してください

iOS

iOSは公式ドキュメントに示された方法で、Asset pashは取得できます。ただ、そのAsset pathから画像を生成する際に、以下の工夫が必要です。

  • viewのDisplay scaleを取得して適切な画像ファイルを選択する
  • UIImageを生成するときに、そのscaleを指定して、UIImageインスタンスを生成する

上記をケアしないと想定と異なる画像サイズで表示されてしまいます。

以下がサンプルコードになります。

var scale = view.traitCollection.displayScale
if scale < 1 {
    scale = 2.0
}

let key = FlutterDartProject.lookupKey(forAsset: "assets/\(Int(scale)).0x/favorite_icon.png",
                                       fromPackage: "myplugin")
let url = Bundle.main.url(forResource: key, withExtension: nil)!
let data = try! Data(contentsOf: url)
let image = UIImage(data: data, scale: scale)!

※ PlatformView内での実装を想定しています。

💡ポイント

  • 実際のassetsは、App.frameworkというFlutterが自動生成するframework内に格納されています

⚠️注意

  • Force unwrappingは適時対応が必要です
  • CocoaPodsの配下Assetsフォルダに、アセット画像を入れ、Spec.sourcesで管理する方法を最初に試しましたが、シミュレータービルドでduplicated copyになってしまいました。
  • ios_platform_imagesがありますが、上記が適切にケアされていませんでした。

Discussion