Migrating your plugin to the new Android APIs

Translated from flutter. Dev/docs/develo…

If you do not write or maintain a Flutter plugin, you can skip this page.

As of version 1.10.17, the new plug-in API is available on the Master and Dev channels. The old API is not immediately obsolete, but we recommend that you migrate to the new API. Over time, the plugin using the old API may behave strangely when the Flutter is embedded into an Android app. Most of the Flutter plugins provided by flutter. Dev have been migrated. (Find out how to become Verified Publisher for Pub. dev!) For an example of a plug-in that uses the new API, see the Battery Package.

Migration steps

The following instructions outline the steps to support the new API:

  1. Update the main plug-in class (* plugin.java) to implement FlutterPlugin []. For more complex plug-ins, you can split FlutterPlugin and MethodCallHandler into two classes. See the Basic Plugin [] section for more details on how application resources are accessed: The latest version of embedding (v2). Also, note that the plug-in should still include the static registerWith() method to remain compatible with applications that do not use V2 embedding. The simplest operation, if possible, is to move the logic out of registerWith() into a private method that can be called by both registerWith() and onAttachedToEngine. Only one registerWith() or AttachToEngine() will be called. If you create channels in onAttachToEngine(), there is no need to clean up the creation in onDetachFromEngine(), it is ok to create them again when onAttachToEngine() is called a second time. In addition, you should document all non-overridden public members. In an Add-to-app scenario, these classes will be accessible to a developer and require documentation.

  2. (Optional) Implement ActivityAware if the plug-in requires a reference to the Activity.

  3. (Optional) Implement ServiceAware if your plug-in is expected to be saved in the background service.

  4. Update mainActivity. Java for v2 Embedding to use FlutterActivity. You may need a common constructor for your plugin (if not available), for example:

package io.flutter.plugins.firebasecoreexample;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.firebase.core.FirebaseCorePlugin;

public class MainActivity extends FlutterActivity {
  // TODO(<github-username>): Remove this once v2 of
  // GeneratedPluginRegistrant rolls to stable.
  // https://github.com/flutter/flutter/issues/42694
  @Override
  public void configureFlutterEngine(FlutterEngine flutterEngine) {
    flutterEngine.getPlugins().add(newFirebaseCorePlugin()); }}Copy the code
  1. (Optional) The plug-in for V2 embedding is not supported using ShimPluginRegistry. Such as:
ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
PathProviderPlugin.registerWith(
        shimPluginRegistry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));
VideoPlayerPlugin.registerWith(
        shimPluginRegistry.registrarFor("io.flutter.plugins.videoplayer.VideoPlayerPlugin"));
Copy the code
  1. inMainActivityCreate it in the same folderEmbeddingV1Activity.javaThe file uses V1 embedding. Such as:
package io.flutter.plugins.firebasecoreexample;

import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class EmbeddingV1Activity extends FlutterActivity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   GeneratedPluginRegistrant.registerWith(this); }}Copy the code
  1. addEmbeddingV1ActivityTo < plugin_name > / example/android/app/SRC/main/AndroidManifest. The XML. For example,
<activity
    android:name=".EmbeddingV1Activity"
    android:theme="@style/LaunchTheme"
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
    android:hardwareAccelerated="true"
    android:windowSoftInputMode="adjustResize">
</activity>
Copy the code
  1. To enable Flutter support on branch master and stable builds, add this gradle script to /android/build.gradle.
// TODO(<github-username>): Remove this hack once androidx
// lifecycle is included on stable.
// https://github.com/flutter/flutter/issues/42348

afterEvaluate {
    def containsEmbeddingDependencies = false
    for (def configuration : configurations.all) {
        for (def dependency : configuration.dependencies) {
            if (dependency.group == 'io.flutter' &&
                    dependency.name.startsWith('flutter_embedding') &&
                    dependency.isTransitive())
            {
                containsEmbeddingDependencies = true
                break}}}if(! containsEmbeddingDependencies) { android { dependencies {def lifecycle_version = 1.1.1 ""
                compileOnly "android.arch.lifecycle:runtime:$lifecycle_version"
                compileOnly "android.arch.lifecycle:common:$lifecycle_version"
                compileOnly "android.arch.lifecycle:common-java8:$lifecycle_version"}}}}Copy the code

To test the plug-in

The remaining steps involve testing your plug-in, which is encouraged, but not required.

  1. update<plugin_name>/example/android/app/build.gradleandroid.support.testReplace withandroidx.test:
defaultConfig {
   ...
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner". }Copy the code
dependencies {
...
androidTestImplementation 'androidx. Test: runner: 1.2.0'
androidTestImplementation 'androidx. Test: rules: 1.2.0'
androidTestImplementation 'androidx. Test. Espresso: espresso - core: 3.2.0'. }Copy the code
  1. in<plugin_name>/example/android/app/src/androidTest/java/<plugin_path>/For theMainActivityEmbeddingV1ActivityAdd the test file. Such as:
package io.flutter.plugins.firebase.core;

import androidx.test.rule.ActivityTestRule;
import dev.flutter.plugins.e2e.FlutterRunner;
import io.flutter.plugins.firebasecoreexample.MainActivity;
import org.junit.Rule;
import org.junit.runner.RunWith;

@RunWith(FlutterRunner.class)
public class MainActivityTest {
  @Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
}
Copy the code
package io.flutter.plugins.firebase.core;

import androidx.test.rule.ActivityTestRule;
import dev.flutter.plugins.e2e.FlutterRunner;
import io.flutter.plugins.firebasecoreexample.EmbeddingV1Activity;
import org.junit.Rule;
import org.junit.runner.RunWith;

@RunWith(FlutterRunner.class)
public class EmbeddingV1ActivityTest {
  @Rule
  public ActivityTestRule<EmbeddingV1Activity> rule =
      new ActivityTestRule<>(EmbeddingV1Activity.class);
}
Copy the code
  1. adde2eflutter_driverDev_dependencies to<plugin_name>/pubspec.yaml<plugin_name>/example/pubspec.yaml.
e2e: ^ 0.2.1
flutter_driver:
  sdk: flutter
Copy the code
  1. inMainActivity.javaTo manually register the E2E plug-in and any other plug-ins used by the sample application.
package io.flutter.plugins.packageinfoexample;

import dev.flutter.plugins.e2e.E2EPlugin;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.packageinfo.PackageInfoPlugin;

public class MainActivity extends FlutterActivity {
  // TODO(jackson): Remove this once v2 of GeneratedPluginRegistrant
  // rolls to stable.
  // https://github.com/flutter/flutter/issues/42694
  @Override
  public void configureFlutterEngine(FlutterEngine flutterEngine) {
    flutterEngine.getPlugins().add(new PackageInfoPlugin());
    flutterEngine.getPlugins().add(newE2EPlugin()); }}Copy the code
  1. in<plugin_name> /pubspec.yamlThe current forward will set the minimum version of Flutter to 1.9.1 + Hotfix.4. This is the minimum version we can guarantee to support. Such as:
environment:
  sdk: "28.0 < 3.0.0 > = 2.0.0 - dev."
  flutter: "> = 1.9.1 + hotfix. 4 < 2.0.0." "

Copy the code
  1. in<plugin_name>/test/<plugin_name>_e2e.dartCreate a simple test. To test PR with the addition of v2 embedding support, we are trying to test some very basic functionality of the plug-in. This is a smoke test to ensure that the plug-in properly registers the new embed. Such as:
import 'package:flutter_test/flutter_test.dart';
import 'package:battery/battery.dart';
import 'package:e2e/e2e.dart';

void main() {
  E2EWidgetsFlutterBinding.ensureInitialized();

  testWidgets('Can get battery level', (WidgetTester tester) async {
    final Battery battery = Battery();
    final int batteryLevel = await battery.batteryLevel;
    expect(batteryLevel, isNotNull);
  });
}
Copy the code
  1. Run the e2E test locally and perform the following operations in Terminal:
cd <plugin_name>/example
flutter build apk
cd android
./gradlew app:connectedAndroidTest -Ptarget=`pwd` /.. /.. /test/<plugin_name>_e2e.dart

Copy the code

The basic plug-in

To start implementing the Flutter Android plugin, first implement the FlutterPlugin.

public class MyPlugin implements FlutterPlugin {
  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
    // TODO: your plugin is now attached to a Flutter experience.
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    // TODO: your plugin is no longer attached to a Flutter experience.}}Copy the code

As shown above, your plugin may or may not be associated with a particular Flutter experience at any given time. You should note that the behavior of initializing the plug-in is in onAttachedToEngine(), and then clearing the reference to the plug-in in onDetachedFromEngine().

FlutterPluginBinding provides several important references to your plug-in:

binding.getFlutterEngine() : Returns the FlutterEngine that your plugin is attached to, providing access to components like the DartExecutor, FlutterRenderer, and more.

binding.getApplicationContext() : Returns the Android application’s Context for the running app.

binding.getLifecycle() : Returns a reference that can be used to obtain a Lifecycle object. If you need to use this lifecycle reference then you need add a project dependency on Flutter’s Android lifecycle package.

The UI/Activity

If your plug-in needs to interact with the user interface, such as requesting permissions or changing Android UI edges, then you need to take additional steps to define your plug-in. You must implement the ActivityAware interface.

public class MyPlugin implements FlutterPlugin.ActivityAware {
  / /... normal plugin behavior is hidden...

  @Override
  public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) {
    // TODO: your plugin is now attached to an Activity
  }

  @Override
  public void onDetachedFromActivityForConfigChanges(a) {
    // TODO: the Activity your plugin was attached to was
    // destroyed to change configuration.
    // This call will be followed by onReattachedToActivityForConfigChanges().
  }

  @Override
  public void onReattachedToActivityForConfigChanges(ActivityPluginBinding activityPluginBinding) {
    // TODO: your plugin is now attached to a new Activity
    // after a configuration change.
  }

  @Override
  public void onDetachedFromActivity(a) {
    // TODO: your plugin is no longer associated with an Activity.
    // Clean up references.}}Copy the code

In order to interact with an Activity, your ActivityAware plug-in must implement the appropriate behavior in four phases. First your plugin is loaded into the Activity. You can access the Activity and a bunch of callbacks via the provided ActivityPluginBinding.

Due to destruction of the Activity during configuration changes, in onDetachedFromActivityForConfigChanges () you must remove all references to a given a given Activity, Then in onReattachedToActivityForConfigChanges () in the reconstruction of these references.

Finally, in onDetachedFromActivity(), your plug-in should clean up any references related to Activity behavior and then return to the non-UI configuration.

Service plugin

TODO

ContentProvider plugin

TODO