How to get Activity and Context in Flutter plugin
Depends on flutter document in Create-Flutter-Plugin, Follow these steps:
1- Import ActivityAware:
import io.flutter.embedding.engine.plugins.activity.ActivityAware
2- implement ActivityAware in your class:
public class ClassName: FlutterPlugin, MethodCallHandler, ActivityAware {
3- Define lateinit variables to use it class:
private lateinit var context: Context
private lateinit var activity: Activity
4- Add these functions:
override fun onDetachedFromActivity() {
TODO("Not yet implemented")
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
TODO("Not yet implemented")
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity;
}
override fun onDetachedFromActivityForConfigChanges() {
TODO("Not yet implemented")
}
5- Add this line in onAttachedToEngine function:
context = flutterPluginBinding.applicationContext
You can see this full code for more understanding:
package com.example.flutter_plugin_name
import android.app.Activity
import android.content.Context
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
public class FlutterPluginName: FlutterPlugin, MethodCallHandler, ActivityAware {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
private lateinit var context: Context
private lateinit var activity: Activity
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutter_plugin_name")
channel.setMethodCallHandler(this);
context = flutterPluginBinding.applicationContext
}
// This static function is optional and equivalent to onAttachedToEngine. It supports the old
// pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
// plugin registration via this function while apps migrate to use the new Android APIs
// post-flutter-1.12 via https://flutter.dev/go/android-project-migration.
//
// It is encouraged to share logic between onAttachedToEngine and registerWith to keep
// them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called
// depending on the user's project. onAttachedToEngine or registerWith must both be defined
// in the same class.
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "flutter_plugin_name")
channel.setMethodCallHandler(FlutterMapboxTurnByTurnPlugin())
}
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}
else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onDetachedFromActivity() {
TODO("Not yet implemented")
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
TODO("Not yet implemented")
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity;
}
override fun onDetachedFromActivityForConfigChanges() {
TODO("Not yet implemented")
}
}
Looking at BatteryPlugin
source code (Since it is from Flutter team, I think this is the correct way):
private Context mContext; // Instance variable for context
// ....
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
onAttach(flutterPluginBinding.getApplicationContext(),flutterPluginBinding.getBinaryMessenger()); // <- this is the line we need here, a new method call
final MethodChannel channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "com.myplugin/my_plugin");
channel.setMethodCallHandler(new MyPlugin());
}
//here is the implementation of that new method
private void onAttachedToEngine(Context applicationContext, BinaryMessenger messenger) {
this.mContext = applicationContext;
methodChannel = new MethodChannel(messenger, "com.myplugin/my_plugin");
methodChannel.setMethodCallHandler(this);
}
// ....
And here is how Google Software Engineer from Flutter team recommend to use Activity
:
On a simple MethodChannel
with a annonymous MethodCallHandler
:
public class MyPlugin {
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "my_channel");
final Activity activity = registrar.activity();
channel.setMethodCallHandler(new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
// use activity here
}
});
}
}
By implementing your class with MethodCallHandler
:
Registrar
provides when registration time activity()
accessor. We can use this Activity
by implementing our plugin constructor and store it in a field for use in the onMethodCall
method:
public class MyPlugin implements MethodCallHandler {
private final Activity activity;
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "my_channel");
channel.setMethodCallHandler(new MyPlugin(registrar.activity()));
}
private MyPlugin(Activity activity) {
this.activity = activity;
}
@Override
public void onMethodCall(MethodCall call, Result result) {
// use activity here
}
}
I use PlatformViewFactory, I can get Activity, Activity's context and Application context from Registrar.
class YourPlatformViewFactory(private val registrar: PluginRegistry.Registrar) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context?, id: Int, args: Any?): PlatformView {
val activityContext = registrar.activeContext()
val appContext = registrar.context()
val activity = registrar.activity()
return YourPlatformView()
}
}
Add this abstract class to your project:
import android.app.Activity
import android.content.Context
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodChannel
import java.lang.ref.WeakReference
abstract class ContextAwarePlugin : FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler {
abstract val pluginName: String
private lateinit var channel : MethodChannel
protected val activity get() = activityReference.get()
protected val applicationContext get() =
contextReference.get() ?: activity?.applicationContext
private var activityReference = WeakReference<Activity>(null)
private var contextReference = WeakReference<Context>(null)
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activityReference = WeakReference(binding.activity)
}
override fun onDetachedFromActivityForConfigChanges() {
activityReference.clear()
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activityReference = WeakReference(binding.activity)
}
override fun onDetachedFromActivity() {
activityReference.clear()
}
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, pluginName)
channel.setMethodCallHandler(this)
contextReference = WeakReference(flutterPluginBinding.applicationContext)
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
Use it this way in your plugin:
class MyPlugin: ContextAwarePlugin() {
override val pluginName: String = "my-plugin"
override fun onMethodCall(call: MethodCall, result: Result) {
applicationContext //Do something
activity //Do something
}
}