前言
Flutter优势主要体现在UI上--高性能且跨平台表现一致。但是针对平台(Android、IOS)的实现,如:获取电量、判断WiFi使用、调起WebView加载网页等,得调用特定平台的API包。Flutter Plugin就是为调用平台API而生。
下文中所提及到的"平台"指的是Android、IOS两端。
介绍
Flutter Plugin
包含针对Android(Java或Kotlin代码)或iOS(Objective-C或Swift代码)的原生实现,通过Platform Channels 与FLutter(dart)层通讯并暴露API。
Platform Channels
- 允许Flutter UI和平台之间传递消息。
- Platform Channel中的消息和响应是异步传递的,以确保用户界面保持响应。
- 一个Flutter应用可以存在多个channel,使用
name 作为区分。 - Platform Channel并非是
线程安全 ,故平台跟Flutter Engine的所有交互必须在平台的主线程 中执行的。 - Flutter默认的消息编解码器是StandardMessageCodec class,现支持平台数据类型如下:
创建Flutter Plugin
接下来介绍使用Android Studio创建Flutter Plugin。使用Visual Studio Code创建的过程也是大同小异,机智的你一定能举一反三,在这里就不一一细说。
第一步:选择创建一个Flutter project
第二步:选择Flutter Project的类型为Flutter Plugin
- Flutter Application 是创建一个纯flutter项目工程
- Flutter Plugin 是创建一个可以暴露平台API的插件工程
- Flutter Package 是创建一个纯Dart的组件包。
- Fultter Module 是创建一个Flutter Module,用于被引入现有的原生App。
第三步:填写Project name等工程信息,完成Flutter Plugin创建。
工程创建完成后的目录结构如下: + lib/flutter_plugin_demo.dart 是插件包dart API实现 + android/src/main/ 是插件包API Android的实现 + ios/Classes/ 是插件包API IOS的实现 + example 是基于依赖当前插件的纯flutter示例工程,一般用作展示API调用。
注:从 Flutter 1.9 开始,iOS 新项目默认使用 Swift 语言,而非 Objective-C;Android 新项目则默认使用 Kotlin,而非 Java。如有需要,是可随时切换回之前的 Objective-C 或 Java。
具体实现类
使用Android/ios模拟器运行example工程
我们先看看示例工程在Android、iOS模拟器上的运行效果:
进入example工程目录,运行lib/main.dart。
APP在Android模拟器运行后,可以看到屏幕出现了"Running on: Android 11"。
APP在iOS模拟器运行后,可以看到屏幕出现了"Running on: iOS 15.2"。
接下来我们通过代码看看flutter是怎么显示出当前平台的系统版本。
示例工程 example/lib/main.dart
关键代码:
?
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
@override
void initState() {
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {
String platformVersion;
try {
platformVersion =
await FlutterPluginDemo.platformVersion ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('Running on: $_platformVersion\n'),
),
),
);
}
}
示例工程中的lib/main.dart引入了我们刚刚创建Flutter Plugin中dart API实现flutter_plugin_demo.dart 。
随后使用异步的方式调用flutter_plugin_demo.dart 的FlutterPluginEg.platformVersion 并把返回值赋值给platformVersion ,随后通过setState 方法把platformVersion 的值赋值给当前状态组件的_platformVersion ,触发UI重渲把_platformVersion 的值"Android 11"显示出来。
Flutter Plugin中dart API实现 lib/flutter_plugin_eg.dart?
全量代码:
import 'dart:async';
import 'package:flutter/services.dart';
class FlutterPluginDemo {
static const MethodChannel _channel = MethodChannel('flutter_plugin_demo');
static Future<String?> get platformVersion async {
final String? version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}
service.dart 暴露与平台通讯的API,如:MethodChannel Platform Channel的一种类型_channel 是FlutterPluginDemo类的属性,是一个实例化的MethodChannel ,name 为"flutter_plugin_demo"platformVersion 是FlutterPluginEg类的静态可计算属性 ,会异步返还一个String。platformVersion 中,调用_channel 的invokeMethod 方法,入参"getPlatformVersion"为调用平台约定的方法名。然后把invokeMethod 的异步结果赋值给String version 作为platformVersion 的返回值。
Flutter Plugin中Android实现?
android/src/main/java/.../FlutterPluginDemoPlugin 全量代码:
package com.qctt.flutter_plugin_demo;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
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;
/** FlutterPluginDemoPlugin */
public class FlutterPluginDemoPlugin implements FlutterPlugin, MethodCallHandler {
/// 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 MethodChannel channel;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_plugin_demo");
channel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
}
再次强调:一个Flutter应用是可以有多个
channel ,并而每个
channel 都可以有多个
method ,所以需要重点了解平台的代码是通过怎么样去对接channel name 与method name。从上文FlutterPluginDemoPlugin源码可以看到:
- 注册MethodChannel约定通道名"flutter_plugin_demo",并开始监听通道消息。
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_plugin_demo");
channel.setMethodCallHandler(this);
}
?实现onMethodCall方法,判断方法名"getPlatformVersion",返回Android ${android.os.Build.VERSION.RELEASE}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
}
当channel name 和 method name 约定后,Flutter Plugin就可以在dart中方便调用平台的实现,并暴露API供Flutter项目使用。
Flutter Plugin中IOS实现
ios/Classes/FlutterPluginDemoPlugin.m
#import "FlutterPluginDemoPlugin.h"
@implementation FlutterPluginDemoPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"flutter_plugin_demo"
binaryMessenger:[registrar messenger]];
FlutterPluginDemoPlugin* instance = [[FlutterPluginDemoPlugin alloc] init];
[registrar addMethodCallDelegate:instance channel:channel];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"getPlatformVersion" isEqualToString:call.method]) {
result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
} else {
result(FlutterMethodNotImplemented);
}
}
@end
?可以看出,IOS与Android实现思路雷同:通过"flutter_plugin_demo"注册FlutterMethodChannel 并开始监听。
但惊奇发现Flutter Plugin中OC默认生成的代码居然没FlutterMethodCall方法名判断,在OC断点看是能获取到"getPlatformVersion"这个方法名的。
源码demo:https://gitee.com/wywinstonwy/flutter_plugin_demo
|