【Flutter】PlatformViewの表示

実装手順

  • Dart
    • PlatFormViewの呼び出し
  • kotlin/Swift
    • 表示するViewの作成
    • 表示するViewのFactoryクラス作成
    • Pluginクラスの作成
    • Pluginの登録

Dart

PlatFormViewの呼び出し

class PlatformViewWidget extends StatelessWidget {
  const PlatformViewWidget({required this.text});

  final String text;

  @override
  Widget build(BuildContext context) {
    final param = {"text": text};
    // Platform側の実装と合わせる
    final viewType = "com.example.platform_view/plugin";
    if (Platform.isAndroid) {
      return AndroidView(
        viewType: viewType,
        layoutDirection: TextDirection.ltr,
        creationParams: param;
        creationParamsCodec: const StandardMessageCodec(),
      );
    } else if (Platform.isIOS) {
      return UiKitView(
        viewType: viewType,
        layoutDirection: TextDirection.ltr,
        creationParams: param;
        creationParamsCodec: const StandardMessageCodec(),
      );
    } else {
      return Container();
    }
  }
}

kotlin

表示するViewの作成

class MyAndroidView(context: Context?, messenger: BinaryMessenger, id: Int, creationParams: Map<String, String>): PlatformView {
  private val view: TextView

  init {
    view = TextView(context!!).apply {
      text = param["text"]
    }
  }

  override fun getView(): View {
    return view
  }

  override fun dispose() {}
}

表示するViewのFactoryクラス作成

class MyAndroidtViewFactory(private val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
    override fun create(context: Context?, id: Int, args: Any?): PlatformView {
        val creationParams = args as Map<String?, Any?>?
        return MyAndroidView(context, messenger, id, creationParams)
    }
}

Pluginクラスの作成

class MyAndroidViewPlugin: FlutterPlugin {
    override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        flutterPluginBinding.platformViewRegistry
            .registerViewFactory(
              "com.example.platform_view/plugin", // Dart側で指定した文字列を指定
              MyAndroidViewFactory(flutterPluginBinding.binaryMessenger) // 作成したFactoryクラスを指定
            )
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {}
}

MainActivityでPluginの登録

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine)
        flutterEngine.plugins.add(MyAndroidViewPlugin())
    }
}

Swift

表示するViewの作成

class MyUiKitView: NSObject, FlutterPlatformView {
    private var _view: UIView
    
    init(
        frame: CGRect,
        viewIdentifier viewId: Int64,
        arguments args: Any?,
        binaryMessenger messenger: FlutterBinaryMessenger?
    ) {
        _view = UIView()
        _view.backgroundColor = UIColor.white
        let label = UILabel()
        label.text = (arguments as! [String: String])["text"] label.textColor = UIColor.black
        label.textAlignment = .center
        label.frame = CGRect(x: 0, y: 0, width: 180, height: 48.0)
        _view.addSubview(label)
        super.init()
    }

    func view() -> UIView {
        return _view
    }
}

表示するViewのFactoryクラス作成

class MyUiKitViewFactory: NSObject, FlutterPlatformViewFactory {
    private var messenger: FlutterBinaryMessenger
    
    init(messenger: FlutterBinaryMessenger) {
        self.messenger = messenger
        super.init()
    }
    
    func create(
        withFrame frame: CGRect,
        viewIdentifier viewId: Int64,
        arguments args: Any?
    ) -> FlutterPlatformView {
        return MyUiKitView(
            frame: frame,
            viewIdentifier: viewId,
            arguments: args,
            binaryMessenger: messenger)
    }
    
    public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
        return FlutterStandardMessageCodec.sharedInstance()
    }
}

Pluginクラスの作成

class MyUiKitViewPlugin {
    public static func register(with registrar: FlutterPluginRegistrar) {
        // withIdにDart側で指定した文字列を指定
        registrar.register(
          MyUiKitViewFactory(messenger: registrar.messenger()),withId: "com.example.platform_view/plugin")
    }
}

AppDelegateでPluginの登録

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        // 作成したPluginクラスのregisterを呼び出す
        // forPluginに表示するNativeViewのクラス名を指定
        MyUiKitViewPlugin.register(with: self.registrar(forPlugin: "MyUiKitView")!)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

build.gradle覚書き

Android開発時によく使うライブラリのbuild.gradleの定義 バージョン部分は要更新

projectRoot/build.gradle

dependencies

// navigation safe args
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.5.3'

plugins




app/build.gradle

冒頭

// kapt
// Roomで使用
apply plugin: 'kotlin-kapt'
// navigation safe args
apply plugin: 'androidx.navigation.safeargs.kotlin'

dependencies

// kotlin coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
// ViewModelScope
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
// Room
implementation 'androidx.room:room-runtime:2.4.3'
implementation 'androidx.room:room-ktx:2.4.3'
kapt 'androidx.room:room-compiler:2.4.3'
// Jetpack Compose
implementation 'androidx.activity:activity-compose:1.6.1'
implementation 'androidx.compose.material:material:1.3.1'
implementation 'androidx.compose.animation:animation:1.3.2'
implementation 'androidx.compose.ui:ui-tooling:1.3.2'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.1'
// NavigationComponent
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'

【Android】DialogFragmentの全画面表示

DialogFragmentを全画面表示し、起動元画面を背景に表示させる

Androidでダイアログを全画面で表示したいときの実装方法

XML

ルートのViewGroupの幅、高さをmatch_parentに設定する

<?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    (以下省略)

View

表示したいダイアログのonStart()の中で処理を追加
- windowのbackgroudにnullを設定
- windosに対してsetLayoutでMATCH_PARENTを設定

override fun onStart() {
    super.onStart()
    // 背景タップでダイアログを閉じない
    dialog!!.setCancelable(false)
    // 背景のDrawableをなくす(起動元画面が背景に見える状態)
    dialog!!.window!!.setBackgroundDrawable(null)
    // ダイアログのウィンドウサイズをMATCH_PARENTに設定
    dialog!!.window!!.setLayout(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT)
}