Flutter插件开发(二)
1.简介
上一篇我们讲到Flutter,并有相应的案例,但是我们发现我们没有获取context,在实际开发中,这显然是不合理的,没有context,那么我们就无法申请权限,跳转图库等等功能,这一篇文章,主要是从实际开发的角度去分析问题。以Android为例。
registerWith旧的方式已经发生改变,推荐使用新的方式写插件
2.思考一个问题
插件获取的context和Activity是从哪里来的?
上一篇我们讲到example的案例,案例中,AndroidManifest中注册了application和MainActivity, 所以,就如同你所想的一样,context从这里面来的,我们的Flutter界面依托在FlutterActivity上,所以这也是我们可以获取context和Activity的原因
3.使用
1.获取context
flutterPluginBinding.applicationContext
2.获取activity
需要实现该ActivityAware
接口。
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
/**
* 创建日期:2021/5/28 on 16:57
* 描述:
* 作者: jialiang.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.后台服务
可选)如果希望您的插件在任何时间都保留在后台服务中,请实现该 ServiceAware
接口。与activity差不多,不再讲解
注意:
您的插件可能会或可能不会在任何给定的时间与给定的Flutter体验相关联。您应该注意在中初始化插件的行为onAttachedToEngine()
,然后在中清理插件的引用onDetachedFromEngine()
。
FlutterPluginBinding为您的插件提供了一些重要的参考:
- binding.getFlutterEngine()
返回的FlutterEngine
是你的插件安装到,到组件提供接入,如DartExecutor
,FlutterRenderer
等。 - binding.getApplicationContext()
返回Context
正在运行的应用程序的Android应用程序。
4.案例,编写一个权限请求插件
以Android为例,由于权限是需要用到Activity的,所以我们需要继承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: //申请位置权限
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;
/**
* 创建日期:2021/5/28 on 17:26
* 描述:
* 作者:jialiang.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) //申请位置权限
.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();
}
}
});
}
}
注意:XXPermissions为轮子哥大牛所写的权限申请框架,感谢轮子哥的无私奉献,GitHub:https://github.com/getActivity/XXPermissions
build.gradle导包
implementation 'com.hjq:xxpermissions:9.0'
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.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;
}
///申请权限
static Future<int> get requestPermission async {
final int permission = await _channel.invokeMethod("requestPermission");
return permission;
}
}
3.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();
});
}
///获取平台信息
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('权限测试$_permissionValue')),
OutlinedButton(
onPressed: initPlatformState, child: Text('版本号$_platformVersion'))
],
),
);
}
}
4.本地添加插件
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: ../
其他方式github和pub,不在本章说明。
5.总结
1.通过以上案例,我们发现Flutter插件的编写,并没有我们想象中难度那么大,仅仅只是Native和Flutter之间通信罢了
2.Flutter插件,通过MethodChannel(2.0之后有pigeon,未使用过)进行Native<------>Flutter之间双向通信
3.Flutter是,运行在FlutterActivity之上的(注意,这点非常重要,并不是玄幻般地渲染,也是有容器),这也是为什么Flutter widget会有生命周期等等原因
参考官方文档:
https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration#basic-plugin