Flutter plug-in development (II)
1. Introduction
In the last article, we talked about Flutter, and there are corresponding cases, but we found that we did not obtain the context, which is obviously unreasonable in the actual development. Without the context, we cannot apply for permission, jump to the gallery and other functions. This article mainly analyzes the problems from the perspective of actual development. Take Android as an example.
The old way of registerWith has changed. It is recommended to write plug-ins in a new way
2. Think about a problem
Where does the context and Activity obtained by the plug-in come from?
In the previous article, we talked about the case of example. In the case, application and MainActivity are registered in AndroidManifest. Therefore, just as you think, context comes from here. Our fluent interface relies on fluteractivity, so this is why we can obtain context and Activity
3. Use
1. Get context
flutterPluginBinding.applicationContext
2. Get activity
Need to implement this ActivityAware Interface.
package com.uih.flutternative 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 /** * Creation date: May 28, 2021 on 16:57 * Description: * Author: Jiang li. v */ class MyPlugin2 : FlutterPlugin, ActivityAware, MethodCallHandler { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { } override fun onDetachedFromActivity() { } override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { } override fun onAttachedToActivity(binding: ActivityPluginBinding) { binding.activity binding.addActivityResultListener { requestCode, resultCode, data -> return@addActivityResultListener false } } override fun onDetachedFromActivityForConfigChanges() { } override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { } }
3. Background service
Optional) if you want your plug-in to remain in the background service at any time, please implement this ServiceAware Interface. Similar to activity, no more explanation
be careful:
Your plug-in may or may not be associated with a given Flutter experience at any given time. You should pay attention to the behavior of initializing the plug-in onAttachedToEngine() in and then cleaning up the reference onDetachedFromEngine() of the plug-in in.
FlutterPluginBinding provides some important references for your plug-ins:
- binding.getFlutterEngine()
The returned FlutterEngine is where your plug-in is installed to provide access to components, such as DartExecutor, FlutterRenderer, etc. - binding.getApplicationContext()
Returns the Android Application of the running application in Context.
4. Case, write a permission request plug-in
Take Android as an example. Since the permission requires Activity, we need to inherit ActivityAware
1.Android:
package com.example.flutter_permission import androidx.annotation.NonNull import android.app.Activity 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 /** FlutterPermissionPlugin */ class FlutterPermissionPlugin: FlutterPlugin, ActivityAware, MethodCallHandler { private lateinit var channel: MethodChannel private lateinit var permissionUtils2: PermissionUtils2 private lateinit var activity: Activity override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(binding.binaryMessenger, "flutter_permission") channel.setMethodCallHandler(this) binding.applicationContext } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) } override fun onDetachedFromActivity() { } override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { } override fun onAttachedToActivity(binding: ActivityPluginBinding) { activity = binding.activity permissionUtils2 = PermissionUtils2() } override fun onDetachedFromActivityForConfigChanges() { } override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { "requestPermission" -> { permissionUtils2.setCallBack { result.success(it) } permissionUtils2.requestPermission(activity) } "getPlatformVersion" -> { result.success("Android ${android.os.Build.VERSION.RELEASE}") } else -> { result.notImplemented() } } } }
PermissionUtils2: / / apply for location permission
package com.example.flutter_permission; import android.app.Activity; import android.os.Handler; import android.os.Looper; import android.os.Message; import androidx.annotation.NonNull; import com.hjq.permissions.OnPermission; import com.hjq.permissions.Permission; import com.hjq.permissions.XXPermissions; import java.util.List; /** * Created on: May 28, 2021 on 17:26 * Description: * Author: Jiang li */ public class PermissionUtils2 { private static final int SUCCESS = 0; private static final int DENIED = -1; private static final int FOREVER_DENIED = -2; private PermissionCallBack callBack; public void setCallBack(PermissionCallBack callBack) { this.callBack = callBack; } private Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); if (msg.what == SUCCESS) { callBack.callBack(SUCCESS); } else if (msg.what == FOREVER_DENIED) { callBack.callBack(FOREVER_DENIED); } else { callBack.callBack(DENIED); } } }; public interface PermissionCallBack { void callBack(int value); } void requestPermission(Activity activity) { XXPermissions.with(activity) .permission(Permission.Group.LOCATION) //Apply for location permission .request(new OnPermission() { @Override public void hasPermission(List<String> granted, boolean all) { if (all) { handler.obtainMessage(SUCCESS).sendToTarget(); } else { handler.obtainMessage(DENIED).sendToTarget(); } } @Override public void noPermission(List<String> denied, boolean never) { if (never) { handler.obtainMessage(FOREVER_DENIED).sendToTarget(); } else { handler.obtainMessage(DENIED).sendToTarget(); } } }); } }
Note: xxpermissions is the permission application framework written by brother wheel Daniel. Thank brother wheel for his selfless dedication. GitHub:https://github.com/getActivity/XXPermissions
build.gradle guide bag
implementation 'com.hjq:xxpermissions:9.0'
Request permission in xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.flutter_permission"> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> </manifest>
2. In Lib
import 'dart:async'; import 'package:flutter/services.dart'; class FlutterPermission { static const MethodChannel _channel = const MethodChannel('flutter_permission'); static Future<String> get platformVersion async { final String version = await _channel.invokeMethod('getPlatformVersion'); return version; } ///Apply for permission static Future<int> get requestPermission async { final int permission = await _channel.invokeMethod("requestPermission"); return permission; } }
3. Test in Example
import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter_permission/flutter_permission.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( appBar: AppBar(title: Text("flutter demo")), body: HomeContent())); } } class HomeContent extends StatefulWidget { HomeContent({Key key}) : super(key: key); @override _HomeContentState createState() { return _HomeContentState(); } } class _HomeContentState extends State<HomeContent> { String _platformVersion = 'Unknown'; String _permissionValue = ""; @override void initState() { super.initState(); } @override void dispose() { super.dispose(); } Future<void> requestPermission() async { int result; try { result = await FlutterPermission.requestPermission; } on PlatformException { result = -3; } setState(() { _permissionValue = result.toString(); }); } ///Get platform information Future<void> initPlatformState() async { String platformVersion; try { platformVersion = await FlutterPermission.platformVersion; } on PlatformException { platformVersion = 'Failed to get platform version.'; } if (!mounted) return; setState(() { _platformVersion = platformVersion; }); } @override Widget build(BuildContext context) { // TODO: implement build return Center( child: Column( children: [ OutlinedButton( onPressed: requestPermission, child: Text('Permission test $_permissionValue')), OutlinedButton( onPressed: initPlatformState, child: Text('Version number $_platformVersion')) ], ), ); } }
4. Add plug-ins locally
pubspec.yaml
dependencies: flutter: sdk: flutter flutter_permission: # When depending on this package from a real application you should use: # flutter_permission: ^x.y.z # See https://dart.dev/tools/pub/dependencies#version-constraints # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../
Other methods, github and pub, are not described in this chapter.
5. Summary
1. Through the above cases, we find that the writing of the Flutter plug-in is not as difficult as we thought. It's just the communication between Native and Flutter
2. The flutter plug-in performs two-way communication between native < ------- > flutters through MethodChannel (there is a pigeon after 2.0, which has not been used)
3. The flutter runs on the FlutterActivity (note that this is very important. It is not unreal rendering, but also has containers), which is why the Flutter widget has a life cycle and so on
Refer to official documents:
https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration#basic-plugin