Saltar al contenido principal

Añadir una pantalla de Flutter a una aplicación Android

Aprende cómo añadir una única pantalla de Flutter a tu aplicación Android existente.

Esta guía describe cómo añadir una única pantalla de Flutter a una existente Android app. Una pantalla de Flutter se puede añadir como una pantalla opaca normal, o como una pantalla translúcida transparente. Ambas opciones se describen en esta guía.

Añadir una pantalla de Flutter normal

#
Encabezado para añadir pantalla de Flutter

Paso 1: Añadir FlutterActivity a AndroidManifest.xml

#

Flutter proporciona FlutterActivity para mostrar una experiencia Flutter dentro de una aplicación Android. Como cualquier otra Activity, FlutterActivity debe registrarse en tu AndroidManifest.xml. Añade el siguiente XML a tu archivo AndroidManifest.xml bajo tu etiqueta application:

xml
<activity
  android:name="io.flutter.embedding.android.FlutterActivity"
  android:theme="@style/LaunchTheme"
  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
  android:hardwareAccelerated="true"
  android:windowSoftInputMode="adjustResize"
  />

La referencia a @style/LaunchTheme se puede reemplazar por cualquier tema de Android que desees aplicar a tu FlutterActivity. La elección del tema dicta los colores aplicados a los elementos del sistema de Android (system chrome), como la barra de navegación de Android, y al color de fondo de la FlutterActivity justo antes de que la UI de Flutter se renderice por primera vez.

Paso 2: Lanzar FlutterActivity

#

Con FlutterActivity registrada en tu archivo de manifiesto, añade código para lanzar la FlutterActivity desde cualquier punto en tu aplicación que desees. El siguiente ejemplo muestra el lanzamiento de la FlutterActivity desde un OnClickListener.

ExistingActivity.kt
kotlin
MyButton(onClick = {
    startActivity(
        FlutterActivity.createDefaultIntent(this)
    )
})

@Composable
fun MyButton(onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text("Launch Flutter!")
    }
}
ExistingActivity.kt
kotlin
myButton.setOnClickListener {
  startActivity(
    FlutterActivity.createDefaultIntent(this)
  )
}
ExistingActivity.java
java
myButton.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    startActivity(
      FlutterActivity.createDefaultIntent(currentActivity)
    );
  }
});

El ejemplo anterior asume que tu punto de entrada de Dart se llama main() y tu ruta inicial de Flutter es '/'. El punto de entrada de Dart no se puede cambiar usando un Intent, pero la ruta inicial sí se puede cambiar usando un Intent. El siguiente ejemplo demuestra cómo lanzar una FlutterActivity que renderiza inicialmente una ruta personalizada en Flutter.

ExistingActivity.kt
kotlin
MyButton(onClick = {
  startActivity(
    FlutterActivity
      .withNewEngine()
      .initialRoute("/my_route")
      .build(this)
  )
})

@Composable
fun MyButton(onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text("Launch Flutter!")
    }
}
ExistingActivity.kt
kotlin
myButton.setOnClickListener {
  startActivity(
    FlutterActivity
      .withNewEngine()
      .initialRoute("/my_route")
      .build(this)
  )
}
ExistingActivity.java
java
myButton.addOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    startActivity(
      FlutterActivity
        .withNewEngine()
        .initialRoute("/my_route")
        .build(currentActivity)
      );
  }
});

Reemplaza "/my_route" con tu ruta inicial deseada.

El uso del método de fábrica withNewEngine() configura una FlutterActivity que crea internamente su propia instancia de FlutterEngine. Esto conlleva un tiempo de inicialización no trivial. El enfoque alternativo consiste en indicarle a la FlutterActivity que utilice un FlutterEngine almacenado en caché y preparado previamente, lo que minimiza el tiempo de inicialización de Flutter. Ese enfoque se analiza a continuación.

Paso 3: (Opcional) Usar un FlutterEngine en caché

#

Cada FlutterActivity crea su propio FlutterEngine por defecto. Cada FlutterEngine tiene un tiempo de preparación no trivial. Esto significa que lanzar una FlutterActivity estándar conlleva un breve retraso antes de que tu experiencia Flutter se vuelva visible. Para minimizar este retraso, puedes preparar un FlutterEngine antes de llegar a tu FlutterActivity, y luego usar tu FlutterEngine preparado en su lugar.

Para preparar un FlutterEngine, busca una ubicación razonable en tu aplicación para instanciar un FlutterEngine. El siguiente ejemplo prepara de forma arbitraria un FlutterEngine en la clase Application:

MyApplication.kt
kotlin
class MyApplication : Application() {
  lateinit var flutterEngine : FlutterEngine

  override fun onCreate() {
    super.onCreate()

    // Instantiate a FlutterEngine.
    flutterEngine = FlutterEngine(this)

    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.dartExecutor.executeDartEntrypoint(
      DartExecutor.DartEntrypoint.createDefault()
    )

    // Cache the FlutterEngine to be used by FlutterActivity.
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine)
  }
}
MyApplication.java
java
public class MyApplication extends Application {
  public FlutterEngine flutterEngine;

  @Override
  public void onCreate() {
    super.onCreate();
    // Instantiate a FlutterEngine.
    flutterEngine = new FlutterEngine(this);

    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.getDartExecutor().executeDartEntrypoint(
      DartEntrypoint.createDefault()
    );

    // Cache the FlutterEngine to be used by FlutterActivity.
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine);
  }
}

El ID pasado a la clase FlutterEngineCache puede ser el que desees. Asegúrate de pasar el mismo ID a cualquier FlutterActivity o FlutterFragment que deba utilizar el FlutterEngine almacenado en caché. El uso de FlutterActivity con un FlutterEngine en caché se analiza a continuación.

Con un FlutterEngine en caché y preparado, ahora debes indicar a tu FlutterActivity que utilice el FlutterEngine en caché en lugar de crear uno nuevo. Para lograr esto, utiliza el constructor withCachedEngine() de FlutterActivity:

ExistingActivity.kt
kotlin
myButton.setOnClickListener {
  startActivity(
    FlutterActivity
      .withCachedEngine("my_engine_id")
      .build(this)
  )
}
ExistingActivity.java
java
myButton.addOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    startActivity(
      FlutterActivity
        .withCachedEngine("my_engine_id")
        .build(currentActivity)
      );
  }
});

Al utilizar el método de fábrica withCachedEngine(), pasa el mismo ID que utilizaste al almacenar en caché el FlutterEngine deseado.

Ahora, cuando lanzas la FlutterActivity, hay un retraso significativamente menor en la visualización del contenido de Flutter.

Ruta inicial con un motor en caché

#

El concepto de una ruta inicial está disponible al configurar una FlutterActivity o un FlutterFragment con un nuevo FlutterEngine. Sin embargo, FlutterActivity y FlutterFragment no ofrecen el concepto de una ruta inicial cuando se utiliza un motor en caché. Esto se debe a que se espera que un motor en caché ya esté ejecutando código Dart, lo que significa que es demasiado tarde para configurar la ruta inicial.

Los desarrolladores que deseen que su motor en caché comience con una ruta inicial personalizada pueden configurar su FlutterEngine en caché para usar una ruta inicial personalizada justo antes de ejecutar el punto de entrada de Dart. El siguiente ejemplo demuestra el uso de una ruta inicial con un motor en caché:

MyApplication.kt
kotlin
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)
  }
}
MyApplication.java
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);
  }
}

Al establecer la ruta inicial del canal de navegación, el FlutterEngine asociado muestra la ruta deseada tras la ejecución inicial de la función Dart runApp().

Cambiar la propiedad de ruta inicial del canal de navegación después de la ejecución inicial de runApp() no tiene ningún efecto. Los desarrolladores que deseen utilizar el mismo FlutterEngine entre diferentes Activitys y Fragments y cambiar la ruta entre esas pantallas deben configurar un canal de método (method channel) y indicar explícitamente a su código Dart que cambie las rutas del Navigator.

Añadir una pantalla de Flutter translúcida

#
Encabezado para añadir pantalla de Flutter con translucidez

La mayoría de las experiencias de Flutter a pantalla completa son opacas. Sin embargo, algunas aplicaciones desean implementar una pantalla de Flutter que parezca un modal, por ejemplo, un diálogo o una hoja inferior (bottom sheet). Flutter admite FlutterActivitys translúcidas de forma nativa.

Para que tu FlutterActivity sea translúcida, realiza los siguientes cambios en el proceso habitual de creación y lanzamiento de una FlutterActivity.

Paso 1: Usar un tema con translucidez

#

Android requiere una propiedad de tema especial para las Activitys que se renderizan con un fondo translúcido. Crea o actualiza un tema de Android con la siguiente propiedad:

xml
<style name="MyTheme" parent="@style/MyParentTheme">
  <item name="android:windowIsTranslucent">true</item>
</style>

Luego, aplica el tema translúcido a tu FlutterActivity.

xml
<activity
  android:name="io.flutter.embedding.android.FlutterActivity"
  android:theme="@style/MyTheme"
  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
  android:hardwareAccelerated="true"
  android:windowSoftInputMode="adjustResize"
  />

Tu FlutterActivity ahora admite translucidez. A continuación, debes lanzar tu FlutterActivity con soporte explícito de transparencia.

Paso 2: Iniciar FlutterActivity con transparencia

#

Para lanzar tu FlutterActivity con un fondo transparente, pasa el BackgroundMode adecuado al IntentBuilder:

ExistingActivity.kt
kotlin
// Using a new FlutterEngine.
startActivity(
  FlutterActivity
    .withNewEngine()
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(this)
);

// Using a cached FlutterEngine.
startActivity(
  FlutterActivity
    .withCachedEngine("my_engine_id")
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(this)
);
ExistingActivity.java
java
// Using a new FlutterEngine.
startActivity(
  FlutterActivity
    .withNewEngine()
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(context)
);

// Using a cached FlutterEngine.
startActivity(
  FlutterActivity
    .withCachedEngine("my_engine_id")
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(context)
);

Ahora tienes una FlutterActivity con un fondo transparente.