(Original) Las páginas Flutter y Native saltan entre sí

Prefacio

Cuando en realidad se desarrollan proyectos híbridos, a menudo es necesario realizar saltos de página.
Si la interfaz nativa y la interfaz de aleteo necesitan saltar entre sí,
¿cómo se debe manejar esta situación?
El blog de hoy presentará principalmente esta situación,
de hecho, si lo piensas bien, este problema se puede dividir en cuatro pequeñas preguntas para su análisis:
1: ¿Cómo saltar a la interfaz nativa?
2: Las interfaces de Flutter saltan entre sí
2: La interfaz nativa salta a Flutter
3: Flutter salta a la interfaz nativa
El primer punto es fácil de entender.
Tome Android como ejemplo
, es startActivity .
Este blog habla principalmente sobre los métodos del siguiente. Tres puntos.
Por supuesto, este blog habla principalmente del método que conozco.
Si tienes un método mejor, deja un mensaje en el área de comentarios.

Las interfaces Flutter saltan entre sí

Salto del navegador

Las interfaces de Flutter saltan entre sí, generalmente usando la clase Navigator,
por ejemplo, si queremos saltar a otra página, podemos escribir así:

final result = await Navigator.push(
      context, MaterialPageRoute(builder: (context) => SecondPage(params: "firstpage",)));

Utilice Navigator.push para saltar. Los parámetros se colocan en el método de construcción de la página de destino.
El resultado es el valor pasado después de que regresa la página
. Cuando sale SecondPage, puede escribir así:

Navigator.pop(context, "hi good");

El segundo parámetro son los datos devueltos a la página anterior.

salto getx

Además de utilizar el Navegador integrado, también podemos utilizar bibliotecas de terceros para
presentar getx primero:

  get: 4.6.0

Al saltar:

final result = await Get.to(GetxSecondPage(params: 'firstpage',));

Al regresar:

Get.back(result: "hi good");

Por supuesto, este es el uso más básico.
También podemos personalizar el enrutamiento.
Primero necesitamos usar GetMaterialApp:

class GetxJump extends StatelessWidget {
    
    
  const GetxJump({
    
    super.key});

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    
    
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: "/",
      getPages: [
        GetPage(name: '/', page: () => GetxFirstPage()),
        GetPage(name: '/GetxSecondPage', page: () => GetxSecondPage()),
      ],
      // home: GetxFirstPage(),
    );
  }
}

Definimos nuestra ruta en getPages, '/' es la primera página predeterminada.
En este momento, no es necesario configurar el atributo de inicio
. Luego, cuando GetxFirstPage salta a GetxSecondPage:

final result = await Get.toNamed("/GetxSecondPage",arguments: "firstpage--arguments");

La lógica de retorno sigue siendo la misma, solo use Get.back

La interfaz nativa salta a Flutter

fragmento muestra la página Flutter

Las páginas de Flutter en realidad existen en una página de Android llamada FlutterActivity.
Podemos abrir cualquiera de nuestros proyectos de Flutter y ver que MainActivity hereda FlutterActivity.
Insertar descripción de la imagen aquí
De hecho, también podemos entender las páginas de Flutter como una vista web
que habla de saltar. Antes de transferir, intentemos usar un fragmento para mostrar la página de Flutter.
Creamos una Actividad en el proyecto de Flutter con el siguiente diseño:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  android:gravity="center">

  <FrameLayout
    android:id="@+id/flutterframelayout"
    android:layout_width="match_parent"
    android:layout_height="500dp"
    />

</LinearLayout>

Luego escribimos el siguiente código en el método onCreate de la actividad:

    var flutterFragment = FlutterFragment.createDefault()
    supportFragmentManager
      .beginTransaction()
      .add(R.id.flutterframelayout, flutterFragment)
      .commit()

Tenga en cuenta que nuestra actividad no hereda FlutterActivity
, sino que hereda directamente la AppCompatActivity más básica.
Cuando ejecutamos el código, encontraremos que también mostramos la interfaz Hola mundo de Flutter.
Insertar descripción de la imagen aquí
En este momento, el tamaño de la página de Flutter es el tamaño de nuestro fragmento. "Así que parece un poco extraño.
De hecho, también podemos dejar que esta página de Flutter se comunique con nuestra actividad.
De esta manera, una página tiene interfaces nativas y de Flutter.
Para una comunicación específica, puedes consultar este blog:
Cómo se comunica Flutter". con Native: MethodChannel
habla de esto principalmente para ayudar a todos. Entender mejor Flutter
Después de hablar de esto, introduzcamos una cosa más:
FlutterEngine
Acabamos de usar FlutterFragment.createDefault() para crear un FlutterFragment.
De hecho, hay dos formas de crear FlutterFragment. ,
los cuales se enumeran aquí:

FlutterFragment.withNewEngine().build<FlutterFragment>()  重新创建一个Engine,指定main方法作为Flutter程序入口
FlutterFragment.createDefault()  使用默认的,指定main方法作为Flutter程序入口
FlutterFragment.withCachedEngine("engine_id")  根据engine_id找到Engine,然后Engine可以配置自己指定的程序入口

Como puede ver, hay un motor en él, entonces, ¿qué es el motor?
El motor aquí en realidad se refiere a FlutterEngine,
lo presentaremos a continuación.

Conozca el motor

Engine se traduce como motor, lo que significa motor.
Muestra que de hecho es una parte muy importante de Flutter.
Engine tiene un papel muy importante y es responsable de configurar la entrada del método de la página de Flutter
. Por ejemplo, cuando escribimos el más simple Proyecto Flutter, encontraremos que todo el código de Flutter utiliza el método principal como punto de entrada
, como este:

void main() => runApp(const MyApp());

Además de lo dicho anteriormente, las páginas de Flutter se colocan en la Actividad de Android en forma de vista web
, entonces, ¿cómo se relacionan?
Hablaré de ello uno por uno a continuación.
Esta parte tiene mucho contenido. Si desea ver el uso directamente,
puede hacer clic en el directorio " Saltar a una actividad de Flutter " para ver cómo usarlo.

Cómo se crea FlutterFragment

Primero veamos cómo lo maneja FlutterFragmentActivity
. En el método onCreate de FlutterFragmentActivity, encontramos un método sureFlutterFragmentCreated:
Insertar descripción de la imagen aquí
publiqué el código de este método:

  private void ensureFlutterFragmentCreated() {
    
    
    if (flutterFragment == null) {
    
    
      // If both activity and fragment have been destroyed, the activity restore may have
      // already recreated a new instance of the fragment again via the FragmentActivity.onCreate
      // and the FragmentManager.
      flutterFragment = retrieveExistingFlutterFragmentIfPossible();
    }
    if (flutterFragment == null) {
    
    
      // No FlutterFragment exists yet. This must be the initial Activity creation. We will create
      // and add a new FlutterFragment to this Activity.
      flutterFragment = createFlutterFragment();
      FragmentManager fragmentManager = getSupportFragmentManager();
      fragmentManager
          .beginTransaction()
          .add(FRAGMENT_CONTAINER_ID, flutterFragment, TAG_FLUTTER_FRAGMENT)
          .commit();
    }
  }

Como puede ver, se llama a createFlutterFragment para crear un Fragmento.
De hecho, este Fragmento es el Fragmento que muestra nuestra página Flutter.
Si no lo cree, echemos un vistazo al método createFlutterFragment:

  
  protected FlutterFragment createFlutterFragment() {
    
    
    final BackgroundMode backgroundMode = getBackgroundMode();
    final RenderMode renderMode = getRenderMode();
    final TransparencyMode transparencyMode =
        backgroundMode == BackgroundMode.opaque
            ? TransparencyMode.opaque
            : TransparencyMode.transparent;
    final boolean shouldDelayFirstAndroidViewDraw = renderMode == RenderMode.surface;

    if (getCachedEngineId() != null) {
    
    
      Log.v(
          TAG,
          "Creating FlutterFragment with cached engine:\n"
              + "Cached engine ID: "
              + getCachedEngineId()
              + "\n"
              + "Will destroy engine when Activity is destroyed: "
              + shouldDestroyEngineWithHost()
              + "\n"
              + "Background transparency mode: "
              + backgroundMode
              + "\n"
              + "Will attach FlutterEngine to Activity: "
              + shouldAttachEngineToActivity());

      return FlutterFragment.withCachedEngine(getCachedEngineId())
          .renderMode(renderMode)
          .transparencyMode(transparencyMode)
          .handleDeeplinking(shouldHandleDeeplinking())
          .shouldAttachEngineToActivity(shouldAttachEngineToActivity())
          .destroyEngineWithFragment(shouldDestroyEngineWithHost())
          .shouldDelayFirstAndroidViewDraw(shouldDelayFirstAndroidViewDraw)
          .build();
    } else {
    
    
      Log.v(
          TAG,
          "Creating FlutterFragment with new engine:\n"
              + "Background transparency mode: "
              + backgroundMode
              + "\n"
              + "Dart entrypoint: "
              + getDartEntrypointFunctionName()
              + "\n"
              + "Dart entrypoint library uri: "
              + (getDartEntrypointLibraryUri() != null ? getDartEntrypointLibraryUri() : "\"\"")
              + "\n"
              + "Initial route: "
              + getInitialRoute()
              + "\n"
              + "App bundle path: "
              + getAppBundlePath()
              + "\n"
              + "Will attach FlutterEngine to Activity: "
              + shouldAttachEngineToActivity());

      return FlutterFragment.withNewEngine()
          .dartEntrypoint(getDartEntrypointFunctionName())
          .dartLibraryUri(getDartEntrypointLibraryUri())
          .dartEntrypointArgs(getDartEntrypointArgs())
          .initialRoute(getInitialRoute())
          .appBundlePath(getAppBundlePath())
          .flutterShellArgs(FlutterShellArgs.fromIntent(getIntent()))
          .handleDeeplinking(shouldHandleDeeplinking())
          .renderMode(renderMode)
          .transparencyMode(transparencyMode)
          .shouldAttachEngineToActivity(shouldAttachEngineToActivity())
          .shouldDelayFirstAndroidViewDraw(shouldDelayFirstAndroidViewDraw)
          .build();
    }
  }

Si es demasiado largo y no desea leerlo, simplemente lea la conclusión:
de hecho, es para determinar si getCachedEngineId (identificación en caché) tiene una identificación en caché
y luego crear un Fragmento
. Si hay una identificación, withCachedEngine El método creará un CachedEngineFragmentBuilder.
De lo contrario, withNewEngine creará un NewEngineFragmentBuilder.
Los dos métodos de construcción de FragmentBuilder De hecho, la reflexión se utiliza para crear FlutterFragment
y los parámetros se pasan en el método createArgs de la
siguiente manera:

    
    public <T extends FlutterFragment> T build() {
    
    
      try {
    
    
        ("unchecked")
        T frag = (T) fragmentClass.getDeclaredConstructor().newInstance();
        if (frag == null) {
    
    
          throw new RuntimeException(
              "The FlutterFragment subclass sent in the constructor ("
                  + fragmentClass.getCanonicalName()
                  + ") does not match the expected return type.");
        }

        Bundle args = createArgs();
        frag.setArguments(args);

        return frag;
      } catch (Exception e) {
    
    
        throw new RuntimeException(
            "Could not instantiate FlutterFragment subclass (" + fragmentClass.getName() + ")", e);
      }
    }

Espera, ¿no es esta forma de crear un Fragmento la misma que el ejemplo que escribimos anteriormente sobre el uso de un fragmento para mostrar una página de Flutter?
¡bien!
De hecho, escribimos un FlutterFragmentActivity simple arriba.
Creamos el FlutterFragment nosotros mismos y luego lo mostramos.
El FlutterFragmentActivity se creó para nosotros de forma predeterminada, y la entrada predeterminada es: método main().
Mirando el código anterior
, encontramos que si el getCachedEngineId no se puede encontrar, el método
es:

FlutterFragment.withNewEngine()

Entonces, veamos las tres formas de crear FlutterFragment:

FlutterFragment.withNewEngine().build<FlutterFragment>()  重新创建一个Engine,指定main方法作为Flutter程序入口
FlutterFragment.createDefault()  使用默认的,指定main方法作为Flutter程序入口
FlutterFragment.withCachedEngine("engine_id")  根据engine_id找到Engine,然后Engine可以配置自己指定的程序入口

De hecho, el segundo tipo se llama internamente y son iguales.

  
  public static CachedEngineFragmentBuilder withCachedEngine( String engineId) {
    
    
    return new CachedEngineFragmentBuilder(engineId);
  }
  
  
  public static FlutterFragment createDefault() {
    
    
    return new NewEngineFragmentBuilder().build();
  }
  
  //可以看到是一样的
  
  public static NewEngineFragmentBuilder withNewEngine() {
    
    
    return new NewEngineFragmentBuilder();
  }

Entonces, ¿cuál es la diferencia entre NewEngineFragmentBuilder y CachedEngineFragmentBuilder?

Generador de fragmentos

Se acaba de analizar. Al juzgar si getCachedEngineId (ID en caché) tiene una ID en caché,
cree un Fragmento a través de CachedEngineFragmentBuilder.
De lo contrario, cree un Fragmento a través de NewEngineFragmentBuilder.
Primero mire CachedEngineFragmentBuilder
, que obtiene el ID del motor en caché y lo pasa al FlutterFragment creado. a través del paquete.

Bundle args = new Bundle();
args.putString(ARG_CACHED_ENGINE_ID, engineId);

FlutterFragment obtuvo el ID del motor a través del método getCachedEngineId

  
  
  public String getCachedEngineId() {
    
    
    return getArguments().getString(ARG_CACHED_ENGINE_ID, null);
  }

Entonces, ¿cuándo se llama a getCachedEngineId?
Encontrará que este método proviene de la interfaz FlutterActivityAndFragmentDelegate.Host.
FlutterFragment simplemente implementa esta interfaz
. Luego, el método setupFlutterEngine de FlutterActivityAndFragmentDelegate llamará a getCachedEngineId
para obtener primero el cachedEngineId almacenado en caché
y luego usará la clase singleton FlutterEngineCache para obtenerlo. basado en cachedEngineId.

 String cachedEngineId = host.getCachedEngineId();
    if (cachedEngineId != null) {
    
    
      flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
      isFlutterEngineFromHost = true;
      if (flutterEngine == null) {
    
    
        throw new IllegalStateException(
            "The requested cached FlutterEngine did not exist in the FlutterEngineCache: '"
                + cachedEngineId
                + "'");
      }
      return;
    }

El método setupFlutterEngine es llamado por onAttach de FlutterActivityAndFragmentDelegate.
El método onAttach de FlutterActivityAndFragmentDelegate finalmente es llamado por el método onAttach de FlutterFragment
. El siguiente código:

  
  public void onAttach( Context context) {
    
    
    super.onAttach(context);
    delegate = delegateFactory.createDelegate(this);
    delegate.onAttach(context);
    if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) {
    
    
      requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
    }
    context.registerComponentCallbacks(this);
  }

En este momento sabemos claramente que cuando FlutterFragment llama al método de ciclo de vida onAttach,
el FlutterEngine almacenado en caché se obtiene de acuerdo con el cachedEngineId pasado , entonces, ¿qué pasa
si se usa NewEngineFragmentBuilder y no hay ningún cachedEngineId en este momento?
De hecho, un FlutterEngine se creará directamente,
por lo que tenemos una comprensión aproximada.
La principal diferencia entre los dos FragmentBuilders es si el FlutterFragment creado tiene un Engineid en caché.
Luego, en función de esta diferencia, se decide si usar el FlutterEngine en caché o Cree uno nuevo directamente
. Entonces, ¿cuál es el papel de FlutterEngine?

FlutterEngine

Como sugiere el nombre, FlutterEngine es el motor y el motor de Flutter.
Veamos los dos NewEngineFragmentBuilders y
descubramos que después de que FlutterFragment.withNewEngine() creó el NewEngineFragmentBuilder, inmediatamente llamó al
método dartEntrypoint (getDartEntrypointFunctionName()).
El método getDartEntrypointFunctionName llamado dentro es el siguiente.

  
  public String getDartEntrypointFunctionName() {
    
    
    try {
    
    
      Bundle metaData = getMetaData();
      String desiredDartEntrypoint =
          metaData != null ? metaData.getString(DART_ENTRYPOINT_META_DATA_KEY) : null;
      return desiredDartEntrypoint != null ? desiredDartEntrypoint : DEFAULT_DART_ENTRYPOINT;
    } catch (PackageManager.NameNotFoundException e) {
    
    
      return DEFAULT_DART_ENTRYPOINT;
    }
  }

Como puede ver, se recupera una cadena según la clave. Si
no se puede recuperar, se devuelve el valor predeterminado DEFAULT_DART_ENTRYPOINT
y el valor predeterminado es

static final String DEFAULT_DART_ENTRYPOINT = "main";

Ahora finalmente vemos que
la cadena obtenida por el método principal getDartEntrypointFunctionName
se asignará al dartEntrypoint de NewEngineFragmentBuilder
y finalmente se pasará a FlutterFragment a través de Bundle.

args.putString(ARG_DART_ENTRYPOINT, dartEntrypoint);

FlutterFragment obtiene este parámetro en el método getDartEntrypointFunctionName.
Puede ver que si no está disponible, el valor predeterminado es principal.

  
  
  public String getDartEntrypointFunctionName() {
    
    
    return getArguments().getString(ARG_DART_ENTRYPOINT, "main");
  }

Ahora podemos hacer un resumen preliminar:
FlutterFragmentActivity crea un Fragmento para mostrar la página de Flutter y luego configura la entrada del programa Flutter
creando un NewEngineFragmentBuilder y llamando al método dartEntrypoint de NewEngineFragmentBuilder.

Entonces, ¿cómo se configura específicamente?
De hecho, el método getDartEntrypointFunctionName también proviene de la interfaz FlutterActivityAndFragmentDelegate.Host
y es llamado por el método doInitialFlutterViewRun de FlutterActivityAndFragmentDelegate. El
método doInitialFlutterViewRun es llamado por el método onStart de FlutterActivityAndFragmentDelegate en el método onStart de FlutterActivityAndFragmentDelegate
. Es llamado por onStar método
de FlutterFragment. Ahora que sabemos, en el análisis final O el ciclo de vida de FlutterFragment
Luego continuamos viendo qué hará el método doInitialFlutterViewRun después de obtener esta cadena. Aquí
viene el punto clave:

    DartExecutor.DartEntrypoint entrypoint =
        libraryUri == null
            ? new DartExecutor.DartEntrypoint(
                appBundlePathOverride, host.getDartEntrypointFunctionName())
            : new DartExecutor.DartEntrypoint(
                appBundlePathOverride, libraryUri, host.getDartEntrypointFunctionName());
    flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint, host.getDartEntrypointArgs());

Se puede ver que, según la entrada del programa devuelta por getDartEntrypointFunctionName,
se crea la clase DartExecutor.DartEntrypoint
y se llama a flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint, host.getDartEntrypointArgs());,
completando así la configuración de la entrada
. Al mismo tiempo, sabemos claramente que
cuando FlutterFragment llama al método de ciclo de vida onAttach,
el FlutterEngine almacenado en caché se obtiene en función del cachedEngineId pasado.
Si no hay caché, se crea un nuevo FlutterEngine.
Luego, cuando FlutterFragment llama al método de ciclo de vida onStart,
El método doInitialFlutterViewRun en el método onStart de FlutterActivityAndFragmentDelegate se activa
internamente para llamar a getDartEntrypointFunctionName. Después de obtener la entrada del programa
y configurar esta entrada en FlutterEngine

, la siguiente pregunta es: ¿
se puede cambiar la entrada configurada por NewEngineFragmentBuilder?
¡ciertamente! También podemos completar la configuración de entrada
llamando a flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint, host.getDartEntrypointArgs()); el artículo explicará cómo configurarlo más adelante. Volviendo a los dos FragmentBuilders anteriores , también podemos saber si se almacena en caché o En el análisis final, el FragmentBuilder recién creado debe obtener un FlutterEngine , y FlutterEngine es la clase que realmente establece la entrada. Su método ejecutarDartEntrypoint debe pasar una clase DartEntrypoint . El atributo dartEntrypointFunctionName de la clase DartEntrypoint se utiliza para configurar el nombre del método de entrada. Finalmente, echemos un vistazo al método ejecutarDartEntrypoint. Internamente, se usa flutterJNI. Vaya a buscar la entrada, no continuaremos mirando esta parte.










  public void executeDartEntrypoint(
       DartEntrypoint dartEntrypoint,  List<String> dartEntrypointArgs) {
    
    
    if (isApplicationRunning) {
    
    
      Log.w(TAG, "Attempted to run a DartExecutor that is already running.");
      return;
    }

    TraceSection.begin("DartExecutor#executeDartEntrypoint");
    try {
    
    
      Log.v(TAG, "Executing Dart entrypoint: " + dartEntrypoint);
      flutterJNI.runBundleAndSnapshotFromLibrary(
          dartEntrypoint.pathToBundle,
          dartEntrypoint.dartEntrypointFunctionName,
          dartEntrypoint.dartEntrypointLibrary,
          assetManager,
          dartEntrypointArgs);

      isApplicationRunning = true;
    } finally {
    
    
      TraceSection.end();
    }
  }

Sabiendo esto, hagamos un resumen final:
1: FlutterFragmentActivity decide usar diferentes métodos para crear FlutterFragment en función de si el ID del motor en caché se pasa desde el Intent. Si hay
un caché, use CachedEngineFragmentBuilder
sin caché, luego use NewEngineFragmentBuilder.
Ambos usan reflexión. para crear FlutterFragment
y el id de motor se pasa a FlutterFragment
2: FlutterFragment obtiene un FlutterEngine
con id de motor en función de si tiene un id de motor, y obtiene el FlutterEngine almacenado en caché
sin id de motor a través del mapa de la clase singleton FlutterEngineCache. El paso para crear un nuevo FlutterEngine
es ejecutado en el método onAttach de FlutterFragment
3: FlutterFragment El método onStart determina si hay un parámetro ARG_DART_ENTRYPOINT.
Si no, por defecto es main como entrada del programa.
El valor del parámetro ARG_DART_ENTRYPOINT se asigna a la clase DartExecutor.DartEntrypoint creada
, y eventualmente lo utiliza el enableDartEntrypoint de FlutterEngine
para completar la configuración de entrada.

Se trata de la lógica general de FlutterFragmentActivity.
Hereda de FragmentActivity.
De hecho, hay otra clase FlutterActivity.
Hereda de Activity
e implementa la interfaz FlutterActivityAndFragmentDelegate.Host. La clase FlutterActivity también puede mostrar páginas de Flutter . Hay algunas diferencias entre
Activity y FlutterFragmentActivity . No he visto esto en detalle. Él usa FlutterActivityAndFragmentDelegate para crear diferentes flutterViews para mostrar páginas de Flutter . Si está interesado, puede leer esto. En la actualidad, básicamente uso FlutterFragmentActivity para entender mucho. En realidad, es Es muy sencillo para nosotros saltar a FlutterActivity.




Saltar a una actividad de Flutter

Al igual que NewEngineFragmentBuilder y CachedEngineFragmentBuilder mencionados anteriormente,
Flutter proporciona dos IntentBuilders para facilitar nuestros saltos. La diferencia entre
NewEngineIntentBuilder y CachedEngineIntentBuilder
es en realidad si hay un ID de motor en caché.
El método de salto específico se presenta a continuación.

NuevoEngineIntentBuilder

Esto es relativamente simple. Primero, cree NewEngineIntentBuilder en nuestra Actividad objetivo
. Tenga en cuenta que la Actividad objetivo debe heredar FlutterFragmentActivity.

  companion object {
    
    
    fun NewEngineIntentBuilder(): NewEngineIntentBuilder {
    
    
      return NewEngineIntentBuilder(MainActivity3::class.java)
    }
  }

Entonces el código de salto es:

      startActivity(MainActivity3
        .NewEngineIntentBuilder()
        .build(this))

Como puede ver, cree NewEngineIntentBuilder y llame a su método de compilación,
que en realidad devuelve una intención.

    
    public Intent build( Context context) {
    
    
      Intent intent =
          new Intent(context, activityClass)
              .putExtra(EXTRA_INITIAL_ROUTE, initialRoute)
              .putExtra(EXTRA_BACKGROUND_MODE, backgroundMode)
              .putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, true);
      if (dartEntrypointArgs != null) {
    
    
        intent.putExtra(EXTRA_DART_ENTRYPOINT_ARGS, new ArrayList(dartEntrypointArgs));
      }
      return intent;
    }

De esta manera, cuando la actividad objetivo final crea un fragmento,
utilizará NewEngineFragmentBuilder
para crear un nuevo FlutterEngine.
Esta forma de saltar a la página solo puede saltar al método principal de Flutter,
lo que significa que no puede determinar el
punto de entrada del programa de Flutter. Para cambiar la entrada, todavía se necesita FlutterEngine

CachedEngineIntentBuilder

Utilice CachedEngineIntentBuilder para saltar y
crear CachedEngineIntentBuilder en la actividad de destino.

  companion object {
    
    
    fun withCachedEngine(cachedEngineId: String): CachedEngineIntentBuilder {
    
    
      return CachedEngineIntentBuilder(MainActivity3::class.java, cachedEngineId)
    }
  }

El código de salto está escrito así:

      startActivity(MainActivity3
        .withCachedEngine(App.your_engine_id)
        .build(this).apply {
    
    
          putExtra("method", "showtestpage")
          putExtra("params", "参数params")
        })

De hecho, se crea CachedEngineIntentBuilder y se llama a su método de compilación,
que en realidad devuelve una intención.

    
    public Intent build( Context context) {
    
    
      return new Intent(context, activityClass)
          .putExtra(EXTRA_CACHED_ENGINE_ID, cachedEngineId)
          .putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, destroyEngineWithActivity)
          .putExtra(EXTRA_BACKGROUND_MODE, backgroundMode);
    }

Entonces, ¿cuál es la diferencia? La razón es que necesitamos definir el cachedEngineId
pasado en el método withCachedEngine. Lo defino aquí en Application . Después de definirlo, creamos FlutterEngine.


  private val flutterEngine by lazy {
    
    
    //显示默认的页面,会找flutter的main方法
    FlutterEngine(this).apply {
    
    
      dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
    }
  }

Luego almacene el FlutterEngine creado en la clase singleton FlutterEngineCache

FlutterEngineCache.getInstance().put(your_engine_id, flutterEngine)

Aquí your_engine_id es la clave
que debe ser coherente con el cachedEngineId pasado en el método withCachedEngine para encontrar el FlutterEngine correspondiente.
De esta manera, cuando la Actividad objetivo final crea un Fragmento,
utilizará CachedEngineFragmentBuilder
para encontrar el FlutterEngine almacenado en caché
y esta página de salto. de esta manera, puede completar la configuración de entrada
creando la clase DartExecutor.DartEntrypoint
y llamando a flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint, host.getDartEntrypointArgs());

Parámetros de entrada y paso.

Entonces, ¿cómo configurarlo específicamente?
De hecho, radica en la forma en que creamos FlutterEngine.
De hecho, la forma de crear FlutterEngine es similar a la forma de crear FlutterFragment:
FlutterFragment proporciona estas tres formas de creación (la primera y la segunda son en realidad las mismas):

FlutterFragment.withNewEngine().build<FlutterFragment>()  重新创建一个Engine,指定main方法作为Flutter程序入口
FlutterFragment.createDefault()  使用默认的,指定main方法作为Flutter程序入口
FlutterFragment.withCachedEngine("engine_id")  根据engine_id找到Engine,然后Engine可以配置自己指定的程序入口

FlutterEngine proporciona tres formas de crear (la primera y la segunda son en realidad iguales):

FlutterEngine(this).apply {
    
    
  dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
}

//createDefault内部其实就是这样写的
FlutterEngine(this).apply {
    
    
  var entrypoint=DartEntrypoint(FlutterMain.findAppBundlePath(), "main")
  dartExecutor.executeDartEntrypoint(entrypoint)
}

FlutterEngine(this).apply {
    
    
  var entrypoint = DartExecutor.DartEntrypoint(
    FlutterMain.findAppBundlePath(), "testMethod")
  dartExecutor.executeDartEntrypoint(entrypoint)
}

Aquí testMethod es la entrada del programa que definimos.
De esta manera, después de saltar a nuestra actividad objetivo,
Flutter cargará el método testMethod() en el archivo main.dart como la entrada del programa.
Nota:
testMethod() debe escribirse en el archivo principal. Archivo .dart
. No he encontrado otra manera todavía.
El salto de página está ahí, pero ¿qué pasa con el paso de parámetros?
Puedes ver que pasé dos parámetros al Intent cuando acabo de saltar.

          putExtra("method", "showtestpage")
          putExtra("params", "参数params")

Luego, en el método configureFlutterEngine de la Actividad de destino podemos manejarlo así:

  override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    
    
    super.configureFlutterEngine(flutterEngine)
    var   channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "paramsChannel")
    intent?.apply {
    
    
      val path = getStringExtra("method") ?: ""
      val params = getStringExtra("params") ?: ""
      Log.d("MainActivity3", "path是:"+path)
      Log.d("MainActivity3", "params是:"+params)
      channel.invokeMethod(path, params)
    }
  }

Encontrará que MethodChannel todavía se usa para la comunicación,
luego acceda al código en el lado de Flutter
para definir MethodChannel.

  var commonNativeChannel = MethodChannel('paramsChannel');

Dentro del estado de inicio:

  
  void initState() {
    
    
    commonNativeChannel.setMethodCallHandler((MethodCall call) async {
    
    
      print('拿到参数: = ${
      
      call.method}');
      switch (call.method) {
    
    
        case 'showtestpage':
          print('拿到参数: = ${
      
      call.arguments}');
          //这里可以根据参数设置跳转不同page,我这里刷新一下显示,不做跳转处理
          setState(() {
    
    
            params = call.arguments.toString();
          });
          break;
        default:
          print('Unknowm method ${
      
      call.method}');
          //触发Android端的notImplemented方法
          throw MissingPluginException();
      }
    });
    super.initState();
  }

De esta manera, se completa la especificación del salto a la página de Flutter y el paso de parámetros.

Flutter salta a la interfaz nativa

Flutter necesita usar MethodChannel para comunicarse cuando salta a una página nativa específica.
Si no conoce MethodChannel, puede leer mi blog:
Cómo se comunica Flutter con Native:
El uso específico de MethodChannel se muestra a continuación:
Primero, defina los campos y los campos de MethodChannel en el lado de Flutter. Método
Aquí, los eventos de clic de dos botones se utilizan para activar el salto.
Los métodos jumpActivity y Finish en el lado de Android se llaman respectivamente.
Por supuesto, también se pueden pasar parámetros. Para parámetros específicos , consulte el blog MethodChannel arriba.

class _MyjumpAndroidPageState extends State<MyjumpAndroidPage> {
    
    
  var commonNativeChannel = MethodChannel('ForNativePlugin');
  
  void initState() {
    
    
    super.initState();
  }

  
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(onPressed: () {
    
    
              commonNativeChannel.invokeMethod("jumpActivity");
            }, child: Text("点击跳转到android页面")),
            ElevatedButton(onPressed: () {
    
    
              commonNativeChannel.invokeMethod("finish");
            }, child: Text("点击关闭当前Flutter页面"))
          ],
        ),
      ),
    );
  }
}

Luego venga a nuestro lado de Android,
herede la clase FlutterFragmentActivity
, cree MethodChannel como de costumbre y procese estos métodos llamados
en el método configureFlutterEngine . Simplemente imprimo el registro aquí. En circunstancias normales, StartActivity es suficiente.


class MainActivity4 : FlutterFragmentActivity() {
    
    
  private lateinit var flutterMethodChannel: MethodChannel

  companion object {
    
    
    fun withCachedEngine(cachedEngineId: String): CachedEngineIntentBuilder {
    
    
      return CachedEngineIntentBuilder(MainActivity4::class.java, cachedEngineId)
    }
  }

  override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    
    
    super.configureFlutterEngine(flutterEngine)
    flutterMethodChannel = MethodChannel(flutterEngine.dartExecutor, "ForNativePlugin")
    flutterMethodChannel.setMethodCallHandler {
    
     methodCall, result ->
      when (methodCall.method) {
    
    
        "finish" -> {
    
    
          Toast.makeText(this, "关闭页面", Toast.LENGTH_SHORT).show()
          finish()
        }
        "jumpActivity" -> {
    
    
          Toast.makeText(this, "跳转页面", Toast.LENGTH_SHORT).show()
        }
        else -> {
    
    
          // 表明没有对应实现
          result.notImplemented()
        }
      }
    }
  }
}

Código fuente

Se trata de saltar entre páginas Flutter y Nativas.
Compartamos la dirección del código fuente involucrada en este artículo:
new_gradlesetting_native_jump_flutter

Supongo que te gusta

Origin blog.csdn.net/Android_xiong_st/article/details/131987137
Recomendado
Clasificación