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中无后顾之忧。
共同学习,写下你的评论
评论加载中...
作者其他优质文章