<

Android アプリに Flutter フラグメントを追加する

Add Flutter Fragment Header

このガイドでは、Flutter を追加する方法について説明します。Fragment既存のものに アンドロイドアプリ。 Android では、Fragmentモジュラーを表します より大きな UI の一部。あFragmentプレゼンテーションに使用されるかもしれません スライド式ドロワー、タブ付きコンテンツ、ページ内のページViewPager、 あるいは、単純に通常の画面を表す場合もあります。 独身-Activityアプリ。 Flutter が提供するのは、FlutterFragment開発者がどこでも Flutter エクスペリエンスを提供できるようにする 通常のを使用できることFragment

もしActivityアプリケーションのニーズにも同様に適用できます。 検討を使ってFlutterActivityの代わりにFlutterFragment、より速くて使いやすいです。

FlutterFragment開発者が以下を制御できるようにします Flutter エクスペリエンスの詳細Fragment:

  • 初期 flutterルート
  • 実行する Dart エントリポイント
  • 不透明な背景と半透明の背景
  • どうにかFlutterFragment周囲をコントロールする必要があるActivity
  • 新しいかどうかFlutterEngineまたはキャッシュされたFlutterEngine使用すべきです

FlutterFragment多くのコールも付属しています。 周囲から転送する必要があるActivity。 これらの呼び出しにより、Flutter は OS イベントに適切に反応できるようになります。

全種類のFlutterFragment、およびその要件、 については、このガイドで説明されています。

追加FlutterFragmentActivity新しいものでFlutterEngine

を使用するために最初に行うことは、FlutterFragmentそれをホストに追加することですActivity

を追加するにはFlutterFragmentホストにActivity、インスタンス化して、 のインスタンスをアタッチしますFlutterFragmentonCreate()以内Activityまたは、アプリに適した別のタイミングで次のようにします。

MyActivity.java
public class MyActivity extends FragmentActivity {
    // Define a tag String to represent the FlutterFragment within this
    // Activity's FragmentManager. This value can be whatever you'd like.
    private static final String TAG_FLUTTER_FRAGMENT = "flutter_fragment";

    // Declare a local variable to reference the FlutterFragment so that you
    // can forward calls to it later.
    private FlutterFragment flutterFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Inflate a layout that has a container for your FlutterFragment.
        // For this example, assume that a FrameLayout exists with an ID of
        // R.id.fragment_container.
        setContentView(R.layout.my_activity_layout);

        // Get a reference to the Activity's FragmentManager to add a new
        // FlutterFragment, or find an existing one.
        FragmentManager fragmentManager = getSupportFragmentManager();

        // Attempt to find an existing FlutterFragment,
        // in case this is not the first time that onCreate() was run.
        flutterFragment = (FlutterFragment) fragmentManager
            .findFragmentByTag(TAG_FLUTTER_FRAGMENT);

        // Create and attach a FlutterFragment if one does not exist.
        if (flutterFragment == null) {
            flutterFragment = FlutterFragment.createDefault();

            fragmentManager
                .beginTransaction()
                .add(
                    R.id.fragment_container,
                    flutterFragment,
                    TAG_FLUTTER_FRAGMENT
                )
                .commit();
        }
    }
}
MyActivity.kt
class MyActivity : FragmentActivity() {
  companion object {
    // Define a tag String to represent the FlutterFragment within this
    // Activity's FragmentManager. This value can be whatever you'd like.
    private const val TAG_FLUTTER_FRAGMENT = "flutter_fragment"
  }

  // Declare a local variable to reference the FlutterFragment so that you
  // can forward calls to it later.
  private var flutterFragment: FlutterFragment? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // Inflate a layout that has a container for your FlutterFragment. For
    // this example, assume that a FrameLayout exists with an ID of
    // R.id.fragment_container.
    setContentView(R.layout.my_activity_layout)

    // Get a reference to the Activity's FragmentManager to add a new
    // FlutterFragment, or find an existing one.
    val fragmentManager: FragmentManager = supportFragmentManager

    // Attempt to find an existing FlutterFragment, in case this is not the
    // first time that onCreate() was run.
    flutterFragment = fragmentManager
      .findFragmentByTag(TAG_FLUTTER_FRAGMENT) as FlutterFragment?

    // Create and attach a FlutterFragment if one does not exist.
    if (flutterFragment == null) {
      var newFlutterFragment = FlutterFragment.createDefault()
      flutterFragment = newFlutterFragment
      fragmentManager
        .beginTransaction()
        .add(
          R.id.fragment_container,
          newFlutterFragment,
          TAG_FLUTTER_FRAGMENT
        )
        .commit()
    }
  }
}

Flutter UI をレンダリングするには、前のコードで十分です。 それはあなたへの電話から始まりますmain()ダーツのエントリーポイント、 の初期 Flutter ルート/、そして新しいFlutterEngine。 ただし、このコードは期待されるすべてのことを達成するには十分ではありません。 flutter動作。 flutterはさまざまな OS 信号に依存します。 ホストから転送する必要がありますActivityFlutterFragment。 これらの呼び出しを次の例に示します。

MyActivity.java
public class MyActivity extends FragmentActivity {
    @Override
    public void onPostResume() {
        super.onPostResume();
        flutterFragment.onPostResume();
    }

    @Override
    protected void onNewIntent(@NonNull Intent intent) {
        flutterFragment.onNewIntent(intent);
    }

    @Override
    public void onBackPressed() {
        flutterFragment.onBackPressed();
    }

    @Override
    public void onRequestPermissionsResult(
        int requestCode,
        @NonNull String[] permissions,
        @NonNull int[] grantResults
    ) {
        flutterFragment.onRequestPermissionsResult(
            requestCode,
            permissions,
            grantResults
        );
    }

    @Override
    public void onUserLeaveHint() {
        flutterFragment.onUserLeaveHint();
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
        flutterFragment.onTrimMemory(level);
    }
}
MyActivity.kt
class MyActivity : FragmentActivity() {
  override fun onPostResume() {
    super.onPostResume()
    flutterFragment!!.onPostResume()
  }

  override fun onNewIntent(@NonNull intent: Intent) {
    flutterFragment!!.onNewIntent(intent)
  }

  override fun onBackPressed() {
    flutterFragment!!.onBackPressed()
  }

  override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<String?>,
    grantResults: IntArray
  ) {
    flutterFragment!!.onRequestPermissionsResult(
      requestCode,
      permissions,
      grantResults
    )
  }

  override fun onUserLeaveHint() {
    flutterFragment!!.onUserLeaveHint()
  }

  override fun onTrimMemory(level: Int) {
    super.onTrimMemory(level)
    flutterFragment!!.onTrimMemory(level)
  }
}

OS シグナルが Flutter に転送されると、 あなたのFlutterFragment期待通りに動作します。 これで、FlutterFragment既存の Android アプリに追加します。

最も単純な統合パスでは、新しいFlutterEngine、 これにはかなりの初期化時間がかかりますが、 Flutter が有効になるまで、空白の UI が表示されます。 初めて初期化されてレンダリングされます。 ほとんどの場合、オーバーヘッドは次の方法で回避できます。 キャッシュされ、事前にウォームアップされたFlutterEngine、これについては次に説明します。

あらかじめ温めたものを使用するFlutterEngine

デフォルトでは、FlutterFragment独自のインスタンスを作成する のFlutterEngine、これにはかなりのウォームアップ時間が必要です。 これは、ユーザーには空白が表示されることを意味しますFragmentほんの一瞬の間。 このウォームアップ時間のほとんどは、次の方法で軽減できます。 既存の事前にウォームアップされたインスタンスを使用するFlutterEngine

あらかじめ温めたものを使用するにはFlutterEngineFlutterFragment、 をインスタンス化するFlutterFragmentとともにwithCachedEngine()ファクトリーメソッド。

MyApplication.java
// Somewhere in your app, before your FlutterFragment is needed,
// like in the Application class ...
// Instantiate a FlutterEngine.
FlutterEngine flutterEngine = new FlutterEngine(context);

// Start executing Dart code in the FlutterEngine.
flutterEngine.getDartExecutor().executeDartEntrypoint(
    DartEntrypoint.createDefault()
);

// Cache the pre-warmed FlutterEngine to be used later by FlutterFragment.
FlutterEngineCache
  .getInstance()
  .put("my_engine_id", flutterEngine);
MyActivity.java
FlutterFragment.withCachedEngine("my_engine_id").build();
MyApplication.kt
// Somewhere in your app, before your FlutterFragment is needed,
// like in the Application class ...
// Instantiate a FlutterEngine.
val flutterEngine = FlutterEngine(context)

// Start executing Dart code in the FlutterEngine.
flutterEngine.getDartExecutor().executeDartEntrypoint(
    DartEntrypoint.createDefault()
)

// Cache the pre-warmed FlutterEngine to be used later by FlutterFragment.
FlutterEngineCache
  .getInstance()
  .put("my_engine_id", flutterEngine)
MyActivity.java
FlutterFragment.withCachedEngine("my_engine_id").build()

FlutterFragment内部的には知っているFlutterEngineCache事前に温めたものを取り出しますFlutterEngineIDに基づいて 与えられたwithCachedEngine()

あらかじめ温めたものを提供することで、FlutterEngine、 前に示したように、アプリは 最初の Flutter フレームをできるだけ早く実行します。

キャッシュされたエンジンを使用した初期ルート

初期ルートの概念は、FlutterActivityまたはFlutterFragment新しいものでFlutterEngine。 しかし、FlutterActivityFlutterFragmentを提供しないでください キャッシュされたエンジンを使用する場合の初期ルートの概念。 これは、キャッシュされたエンジンがすでに存在していることが想定されるためです。 Dart コードを実行しているため、設定するには遅すぎることを意味します。 初期ルート。

キャッシュされたエンジンを起動したい開発者 カスタムの初期ルートを使用すると、キャッシュされたルートを設定できますFlutterEngine直前にカスタムの初期ルートを使用するには Dart エントリポイントを実行します。次の例 キャッシュされたエンジンでの初期ルートの使用を示します。

MyApplication.java
public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    // Instantiate a FlutterEngine.
    flutterEngine = new FlutterEngine(this);
    // Configure an initial route.
    flutterEngine.getNavigationChannel().setInitialRoute("your/route/here");
    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.getDartExecutor().executeDartEntrypoint(
      DartEntrypoint.createDefault()
    );
    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine);
  }
}
MyApplication.kt
class MyApplication : Application() {
  lateinit var flutterEngine : FlutterEngine
  override fun onCreate() {
    super.onCreate()
    // Instantiate a FlutterEngine.
    flutterEngine = FlutterEngine(this)
    // Configure an initial route.
    flutterEngine.navigationChannel.setInitialRoute("your/route/here");
    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.dartExecutor.executeDartEntrypoint(
      DartExecutor.DartEntrypoint.createDefault()
    )
    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine)
  }
}

ナビゲーション チャネルの初期ルートを設定すると、関連付けられたFlutterEngineの最初の実行時に目的のルートを表示します。runApp()ダーツ機能。

ナビゲーションチャネルの初期ルートプロパティの変更 最初の実行後runApp()効果はありません。 同じものを使用したい開発者FlutterEngine異なる間ActivityFragmentとスイッチ これらのディスプレイ間のルートにはメソッド チャネルを設定する必要があります。 Dart コードに変更を明示的に指示するNavigatorルート。

スプラッシュ画面を表示する

Flutterコンテンツの初期表示には多少の待ち時間がかかりますが、 あらかじめ温めておいたとしてもFlutterEngine使用されている。 ユーザーエクスペリエンスを向上させるために この短い待ち時間の間、Flutter は Flutter までのスプラッシュ画面 (「起動画面」とも呼ばれます) の表示 最初のフレームをレンダリングします。打ち上げを表示する方法については、 画面を参照してください。スプラッシュスクリーンガイド

指定された初期ルートで Flutter を実行する

Android アプリには、多数の独立した Flutter エクスペリエンスが含まれる場合があります。 さまざまな場所で実行されているFlutterFragments、異なるFlutterEngines.これらのシナリオでは、 各 Flutter エクスペリエンスは異なることから始まるのが一般的です 初期ルート(以外のルート)/)。 これを容易にするために、FlutterFragmentBuilder以下に示すように、希望の初期ルートを指定できます。

MyActivity.java
// With a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
    .initialRoute("myInitialRoute/")
    .build();
MyActivity.kt
// With a new FlutterEngine.
val flutterFragment = FlutterFragment.withNewEngine()
    .initialRoute("myInitialRoute/")
    .build()

指定したエントリポイントから Flutter を実行する

初期ルートを変えるのと似ていますが、異なります。FlutterFragment別のことを実行することもできます ダーツのエントリーポイント。一般的な Flutter アプリには、次のものが 1 つだけあります。 Dart エントリーポイント:main()ですが、他のエントリポイントを定義することもできます。

FlutterFragment希望の仕様をサポートします 指定された Flutter エクスペリエンスに対して実行する Dart エントリポイント。 エントリポイントを指定するには、次のようにビルドします。FlutterFragmentに示すように、

MyActivity.java
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
    .dartEntrypoint("mySpecialEntrypoint")
    .build();
MyActivity.kt
val flutterFragment = FlutterFragment.withNewEngine()
    .dartEntrypoint("mySpecialEntrypoint")
    .build()

FlutterFragment構成の結果が実行される という Dart エントリポイントのmySpecialEntrypoint()。 括弧に注目してください()それは には含まれていませんdartEntrypoint String名前。

コントロールFlutterFragmentのレンダリングモード

FlutterFragmentどちらかを使用できますSurfaceViewそれをレンダリングする Flutter コンテンツ、またはTextureView。 デフォルトはSurfaceView、これは大幅に よりもパフォーマンスに優れていますTextureView。しかし、SurfaceViewAndroid の途中でインターリーブすることはできませんView階層。 あSurfaceViewどちらかが最下位である必要がありますView階層構造の中で、 または一番上のView階層内で。 さらに、Android N より前の Android バージョンでは、SurfaceViewレイアウトとレンダリングが異なるためアニメーション化できません 残りの部分と同期していませんView階層。 これらのユースケースのいずれかがアプリの要件である場合、 それなら使う必要がありますTextureViewそれ以外のSurfaceView。 を選択TextureViewを構築することでFlutterFragmentとともにtexture RenderMode:

MyActivity.java
// With a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
    .renderMode(FlutterView.RenderMode.texture)
    .build();

// With a cached FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
    .renderMode(FlutterView.RenderMode.texture)
    .build();
MyActivity.kt
// With a new FlutterEngine.
val flutterFragment = FlutterFragment.withNewEngine()
    .renderMode(FlutterView.RenderMode.texture)
    .build()

// With a cached FlutterEngine.
val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
    .renderMode(FlutterView.RenderMode.texture)
    .build()

示されている構成を使用すると、結果は次のようになります。FlutterFragmentUI をTextureView

を表示しますFlutterFragment透明感のある

デフォルトでは、FlutterFragment不透明な背景でレンダリングします。 を使ってSurfaceView。 (「制御」を参照)FlutterFragmentのレンダリング モードではないピクセルの背景は黒になります。 flutterによって描かれました。不透明な背景でレンダリングすると、 パフォーマンス上の理由から優先されるレンダリング モード。 Android で透明度を使用した flutter レンダリングが否定的になる パフォーマンスに影響します。ただし、デザインもたくさんありますが、 Flutter エクスペリエンスでは透明なピクセルが必要です。 基盤となる Android UI まで表示されます。このために、 Flutter は半透明をサポートします。FlutterFragment

の透明性を有効にするにはFlutterFragment、 次の構成でビルドします。

MyActivity.java
// Using a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
    .transparencyMode(FlutterView.TransparencyMode.transparent)
    .build();

// Using a cached FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
    .transparencyMode(FlutterView.TransparencyMode.transparent)
    .build();
MyActivity.kt
// Using a new FlutterEngine.
val flutterFragment = FlutterFragment.withNewEngine()
    .transparencyMode(FlutterView.TransparencyMode.transparent)
    .build()

// Using a cached FlutterEngine.
val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
    .transparencyMode(FlutterView.TransparencyMode.transparent)
    .build()

間の関係FlutterFragmentそしてそのActivity

一部のアプリは使用を選択しますFragmentAndroid 画面全体として。 これらのアプリでは、Fragmentに Androidのステータスバーのようなコントロールシステムクロム、 ナビゲーション バーと方向。

Fullscreen Flutter

他のアプリでは、Fragmentは単に表すために使用されます UIの一部。あFlutterFragment慣れているかもしれない 引き出しの内部、ビデオプレーヤー、 またはシングルカード。このような状況では、次のようになります。 にとって不適切なbcbd389-4a45-459f-952f-a8d288ed4740影響する 他にもUIがあるのでAndroidのシステムクローム 同じ内の部分Window

Flutter as Partial UI

FlutterFragment役立つコンセプトが付属しています の場合を区別します。FlutterFragmentホストを制御できるはずですActivity、 そしてその 場合FlutterFragment影響を与えるだけである必要があります 自分自身の行動。を防ぐにはFlutterFragmentから それを暴露するActivityFlutter プラグイン、および Flutter による制御を防ぐActivityのシステム UI、 使用shouldAttachEngineToActivity()のメソッドFlutterFragmentBuilderに示すように、

MyActivity.java
// Using a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
    .shouldAttachEngineToActivity(false)
    .build();

// Using a cached FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
    .shouldAttachEngineToActivity(false)
    .build();
MyActivity.kt
// Using a new FlutterEngine.
val flutterFragment = FlutterFragment.withNewEngine()
    .shouldAttachEngineToActivity(false)
    .build()

// Using a cached FlutterEngine.
val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
    .shouldAttachEngineToActivity(false)
    .build()

通過falseshouldAttachEngineToActivity() Builderメソッドは Flutter との対話を防ぎます。 周囲のActivity。デフォルト値は次のとおりですtrue、 これにより、Flutter および Flutter プラグインが 周囲のActivity