Flutter可以为你提供一个强大华丽简洁高效的跨平台UI界面,
但无论外表多么绚丽美女,没有内在也只是空壳,你会喜欢她吗?(还用问,当然会)
使用插件可以让Flutter轻松与当前平台进行联系,调用平台中的方法。
这篇先不虚头巴脑的介绍一堆MethodChannel的概念,先看怎么用。本文你将了解:
[1].如何创建一个Flutter插件的
[2].Flutter中如何和Android以及iOS交互(本文使用Kotlin和Swift)
[3].Flutter插件的使用
复制代码
1.Flutter插件创建与结构简析
1.1:创建一个Flutter插件
File-->new-->new Flutter Project...
填写信息
包名及语言选择
1.2:Flutter插件项目结构
写代码的地方有三块:
android下面写Android原生代码,使用Java或Kotlin,如果用JNI还可能涉及C++
ios文件夹下面写iOS原生代码,使用Object-c或Swift
lib文件夹下面写Flutter代码,使用Dart语言
复制代码
也就是说一个插件可能涉及到6种语言,哈哈,颤抖吧人类...
1.3:运行插件示例
虽然复杂,但是
简单必有简单的成本,复杂必有复杂的价值。
注意有个坑点:mac上需要装cocoapods
---->[本机信息]----
toly:~ mac$ ruby -v
ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin18]
toly:~ mac$ gem -v
2.5.2.3
toly:~ mac$ gem sources -l
*** CURRENT SOURCES ***
https://rubygems.org/
---->[替换ruby源]----
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
---->[替换ruby源完成]----
toly:~ mac$ gem sources -l
*** CURRENT SOURCES ***
https://gems.ruby-china.com
---->[安装cocoapods]----
toly:~ mac$ sudo gem install -n /usr/local/bin cocoapods
toly:~ mac$ pod setup
复制代码
2.第一个插件代码分析
这里创建一个ia_version的项目专门看看示例的插件是如何完成的。
2.1:Flutter代码:
可以看到ia_version.dart中定义了一个类
IaVersion
,其中有一个MethodChanne
l类型静态常量_channel,接受一个字符串,在静态方法platformVersion
中使用异步调用_channel的getPlatformVersion方法获取版本进行返回。
---->[lib/ia_version.dart]----
class IaVersion {
static const MethodChannel _channel =
const MethodChannel('ia_version');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}
复制代码
2.2:Android方Kotlin代码:
[1].定义IaVersionPlugin类继承自MethodCallHandler。
[2].创建静态方法registerWith,传入一个Registrar类型变量registrar。
[3].通过registrar的messenger和标识符创建MethodChannel对象,
[4].将IaVersionPlugin对象设置给MethodChannel进行回调处理。
[5].覆写了onMethodCall方法,回调MethodCall和Result对象,
在方法体中根据方法名`getPlatformVersion`来用result对象执行方法传入Android版本信息。
---->[com.toly1994.ia_version.IaVersionPlugin]----
class IaVersionPlugin: MethodCallHandler {
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "ia_version")
channel.setMethodCallHandler(IaVersionPlugin())
}
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
}
复制代码
2.3:iOS方Swift代码
[1].定义SwiftIaVersionPlugin类继承自NSObject, FlutterPlugin
[2].创建静态方法register,传入一个FlutterPluginRegistrar类型变量registrar。
[3].通过registrar的messenger和标识符创建FlutterMethodChannel对象,
[4].将SwiftIaVersionPlugin对象设置给MethodChannel进行回调处理。
[5].handle方法,回调FlutterMethodCall和FlutterResult对象,
用result对象执行方法传入iOS版本信息。
---->[ios/Classes/SwiftIaVersionPlugin.swift]----
public class SwiftIaVersionPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "ia_version", binaryMessenger: registrar.messenger())
let instance = SwiftIaVersionPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
}
复制代码
还有两个文件使用OC写的,关于OC我不是太懂,下面是Flutter群里一位朋友的介绍
---->[ios/Classes/IaVersionPlugin.h]----
@interface IaVersionPlugin : NSObject<FlutterPlugin>
@end
---->[ios/Classes/IaVersionPlugin.m]----
@implementation IaVersionPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[SwiftIaVersionPlugin registerWithRegistrar:registrar];
}
@end
复制代码
2.4:使用插件
使用的时候就很方便了,调用一下就ok。
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:ia_version/ia_version.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
@override
void initState() {
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {//异步初始化平台状态
String platformVersion;
try {//捕捉PlatformException.
platformVersion = await IaVersion.platformVersion;//通过插件获取平台版本
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// 如果在异步平台消息运行期间widget从树中删除,
// 我们希望丢弃响应,而不是调用setState来更新不存在的外观。
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'),
),
),
);
}
}
复制代码
3.获取缓存文件夹
相信大家都用过
path_provider
,感觉很方便就可以在Flutter中获取文件路径
下面我们看一下如何让一个插件获取缓存文件夹,如果前面看明白了,应该so easy
3.1:dart插件文件
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
class IaPath {
static const MethodChannel _channel =//插件的渠道标识名
const MethodChannel('com.toly1994.ia_path');
Future<Directory> getTemporaryDirectory() async {
final String path =
await _channel.invokeMethod<String>('getTemporaryDirectory');
if (path == null) {
return null;
}
return Directory(path);
}
}
复制代码
3.2:Android文件
class IaPathPlugin(registrar: Registrar) : MethodCallHandler {
private var mRegistrar: Registrar? = registrar
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "com.toly1994.ia_path")
val instance = IaPathPlugin(registrar)
channel.setMethodCallHandler(instance)
}
}
override fun onMethodCall(call: MethodCall, result: Result) {
when(call.method){
"getTemporaryDirectory"->{
result.success(geTemporaryDirectory())
}
}
}
private fun geTemporaryDirectory(): String {
return mRegistrar!!.context().cacheDir.path;
}
}
复制代码
3.3:iOS的文件
public class SwiftIaPathPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "com.toly1994.ia_path", binaryMessenger: registrar.messenger())
let instance = SwiftIaPathPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getTemporaryDirectory" :
result(NSTemporaryDirectory())
default :
result("UnKnown")
}
}
}
复制代码
3.4:使用
import 'package:flutter/material.dart';
import 'package:ia_path/ia_path.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _str = 'CacheDir:\n';
@override
void initState() {
super.initState();
IaPath().getTemporaryDirectory().then((dir){
setState(() {
_str+=dir.path;
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Container(
child: Text('Running on: $_str\n'),
),
),
);
}
}
复制代码
4.其他工程引用插件
4.1:发布到公网
有道墙隔着,发不发得了就看你自己了。
---->[发布]----
flutter packages pub publish
---->[使用]----
dependencies:
ia_path: ^0.0.1
复制代码
4.2:本地使用
经测试,使用无误
dependencies:
ia_path:
path: /Volumes/coder/Project/Flutter/plugins/ia_path
复制代码
当然你也可以直接在本项目中调用Android和iOS方法,就像插件里做的那样。
本文讲了一下插件的自定义和在两个平台上的代码处理,
下一篇将详细讲述MethodChannel,让你在Flutter中无后顾之忧。
结语
本文到此接近尾声了,如果想快速尝鲜Flutter,《Flutter七日》会是你的必备佳品;如果想细细探究它,那就跟随我的脚步,完成一次Flutter之旅。
另外本人有一个Flutter微信交流群,欢迎小伙伴加入,共同探讨Flutter的问题,本人微信号:zdl1994328
,期待与你的交流与切磋。