博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Flutter插件使用
阅读量:4085 次
发布时间:2019-05-25

本文共 10517 字,大约阅读时间需要 35 分钟。

flutter的库是以package的方式来管理的。Package 分为两种:

  • Dart package:它只能使用 Dart 和 Flutter 提供的 API,使用纯dart语言开发。一些Dart包可能包含Flutter特定功能,因此对Flutter框架具有依赖性。
  • plugin package:使用 Dart 编写的,按需使用 Java 或 Kotlin、ObjC 或 Swift 分别在 Android 和/或 iOS 平台实现的 package。可以获取平台信息,比如电量、陀螺仪等。

添加依赖

Package 会被发布到 网站上。中文镜像。加载速度更快。

  • 添加依赖

    • 打开应用文件夹下的pubspec.yaml文件。然后添加dio:,例如:

      dependencies:    dio:^3.0.9 #latest version复制代码

      ^3.0.9 表示一系列版本,^2.0.1的意思是从2.0.1开始到3.0.0但不包含3.0.0的一系列版本。我们也可以指定依赖库的为特定的版本:

      • any:任意版本
      • 1.2.3:特定的版本
      • <1.2.3:小于 1.2.3 的版本,此外还有 <=、>、>= 可以使用
      • ‘>=1.2.3 <2.0.0’ 指定一个范围
  • 安装

    • 在命令行中运行:flutter pub get

      注意:必须与pubspec.yaml文件在同一文件夹下执行该命令。否则提示找不到命令。

       

      或者

       

    • 在 Android Studio/IntelliJ 中点击 pubspec.yaml 文件顶部操作功能区的 Packages get

  • 添加依赖的其他方式

    • Path依赖

      Flutter 应用可以通过文件系统 path: 依赖而依赖插件。路径可以是相对的也可以是绝对的。例如,要依赖位于应用相邻目录中的插件 plugin1,可以使用以下语法:

      dependencies:  plugin1:    path: ../plugin1/复制代码
    • git依赖

      你也可以依赖存储在 Git 仓库中的 package,如果 package 位于仓库的根目录,可以使用以下语法:

      dependencies:  plugin1:    git:      url: git://github.com/flutter/plugin1.git复制代码

      默认情况下,pub 工具会默认假定 package 位于 Git 仓库的根目录。如果不是这种情况,你可以使用 path 参数指定位置,例如:

      dependencies:  package1:    git:      url: git://github.com/flutter/packages.git      path: packages/package1复制代码

      最后,你可以使用 ref 参数将依赖固定到 git 特定的 commit、branch 或者 tag。更多详细信息,请参阅 。

  • 升级依赖

    第一次获取依赖时,Pub 会下载依赖及其兼容的最新版本。然后通过创建 lockfile 锁定依赖,以始终使用这个版本。 Pub 会在 pubspec 旁创建并存储一个名为 pubspec.lock 文件。它列出了使用的每个依赖包的指定版本(当前包或传递包的版本)。

    使用命令 pub upgrade :

    $ pub upgrade复制代码

    上面的命令用于重新生成 lockfile 文件,并使用最新可用版本的依赖包。如果仅升级某个依赖,可以在命令中指定需要升级的包:

    $ pub upgrade xxx复制代码

    上面的命令升级 xxx 到最新版本,但维持其它包不变。

插件开发

创建一个dart package

通过使用 packages (的模式)可以创建易于共享的模块化代码。一个最基本的 package 由以下内容构成:

下图展示了最简单的Dart package布局:

pubspec.yaml 文件

用于定义 package 名称、版本号、作者等其他信息的元数据文件。

lib 目录

包含共享代码的 lib 目录,其中至少包含一个 .dart 文件。lib目录下的dart代码对于其他package是公开的。你可以根据需要在 lib 下任意创建组织文件结构。按照惯例,实现代码会放在 lib/src 目录下。 lib/src 目录下的代码被认为是私有的。其他 Package 应该永远不需要导入 src/... 目录下代码。

  • Step 1: 创建package

    想要创建纯 Dart 库的 package,请使用带有 --template=package标志的 flutter create 命令:

    $ cd somepath  $ flutter create --template=package hello复制代码
  • Step 2: 实现package

    对于纯 Dart 库的 package,只要在 lib/.dart 文件中添加功能实现,或在 lib 目录中的多个文件中添加功能实现。
    如果要对 package 进行测试,在 test 目录下添加 单元测试。

创建一个plugin package

如果想要开发一个调用特定平台 API 的 package,你需要开发一个原生插件 packgae。原生插件 packgae 是 Dart package 的特别版本,除了要实现 Dart package 要实现的内容,还需要按需使用 Java 或 Kotlin、ObjC 或 Swift 分别在 Android 和/或 iOS 平台实现,你可以使用 [platform channels][] 中的 API 来实现特定平台的调用。

  • Step 1: 创建package

    想要创建原生插件 package,请使用带有 --template=plugin 标志的 flutter create 命令。

    使用 --org 选项,以反向域名表示法来指定你的组织。该值用于生成的 Android 及 iOS 代码。

    使用 -a 选项指定 Android 的语言,或使用 -i 选项指定 iOS 的语言。请选择以下 任一项:

    $ flutter create --org com.example --template=plugin -a kotlin hello  $ flutter create --org com.example --template=plugin -a java hello  $ flutter create --org com.example --template=plugin -i objc hello  $ flutter create --org com.example --template=plugin -i swift hello复制代码

    通过该命令创建的hello插件主要包括以下内容:

    • lib/hello.dart 文件

      Dart 插件 API 实现。

    • android/src/main/java/com/example/hello/HelloPlugin.kt 文件

      Android 平台原生插件 API 实现(使用 Kotlin 编程语言)。

    • ios/Classes/HelloPlugin.m 文件

      iOS 平台原生插件 API 实现(使用 Objective-C 编程语言)。

    • example/ 文件

      一个依赖于该插件并说明了如何使用它的 Flutter 应用。

  • Step 2: 实现package
    • 步骤a:定义 package API(.dart)

      原生插件类型 package 的 API 在 Dart 代码中要首先定义好,使用你钟爱的 Flutter 编辑器,打开 hello 主目录,并找到 lib/hello.dart 文件。

    • 步骤b:添加 Android 平台代码(.kt/.java)

      我们建议你使用 Android Studio 来编辑 Android 代码。使用 Android Studio 编辑Android 平台代码之前,首先确保代码至少被构建过一次(换句话说,即从 IDE/编辑器执行示例程序,或在终端中执行以下命令:cd hello/example; flutter build apk)。

      接下来进行如下步骤:

      1. 启动 Android Studio.
      2. 在 “Welcome to Android Studio” 对话框中选择 “Import project”,或在菜单中选择“File > New > Import Project…”,然后选择 hello/example/android/build.gradle 文件;
      3. 在“Gradle Sync”对话框中,选择“OK”;
      4. 在“Android Gradle Plugin Update”对话框中,选择“Don’t remind me again for this project”。
    • 步骤c:添加 iOS 平台代码(.swift/.h+.m)

      我们建议你使用 Xcode 来编辑 iOS 代码。使用 Xcode 编辑 iOS 平台代码之前,首先确保代码至少被构建过一次(即从 IDE/编辑器执行示例程序,或在终端中执行以下命令: cd hello/example; flutter build ios --no-codesign)。

      接下来执行下面步骤:

      1. 启动 Xcode
      2. 选择“File > Open”,然后选择 hello/example/ios/Runner.xcworkspace 文件。
    • 步骤d:关联 API 和平台代码

      最后将dart编写的代码和平台代码通过channel实现互联。

Flutter与平台通信

Flutter采用MethodChannel与各平台进行通信,如下图所示:

消息是以异步的形式进行传递,以确保用户界面能够保持响应。

需要注意的是,上图中的箭头是双向的。也就是说,我们不仅可以从 Flutter 调用 Android/iOS 的代码,也可以从 Android/iOS 调用 Flutter。调用时相关的参数对应如下:

PlatformChannel功能简介

 

1

. PlatformChannel类说明:

 

  • BasicMessageChannel: 用于传递数据。Flutter与原生项目的资源是不共享的,可以通过BasicMessageChannel来获取Native项目的图标等资源。
  • MethodChannel: 传递方法调用。Flutter主动调用Native的方法,并获取相应的返回值。比如获取系统电量,发起Toast等调用系统API,可以通过这个来完成。
  • EventChannel: 传递事件。这里是Native将事件通知到Flutter。比如Flutter需要监听网络情况,这时候MethodChannel就无法胜任这个需求了。EventChannel可以将Flutter的一个监听交给Native,Native去做网络广播的监听,当收到广播后借助EventChannel调用Flutter注册的监听,完成对Flutter的事件通知。

 

2

. 消息编解码MessageCodec有4个子类:

 

  • StandardMessageCodec
    StandardMessageCodec是BasicMessageChannel的默认编解码器,其支持基础数据类型、二进制数据、列表、字典,其工作原理会在下文中详细介绍。
  • StringCodec
    StringCodec用于字符串与二进制数据之间的编解码,其编码格式为UTF-8。
  • JSONMessageCodec
    JSONMessageCodec用于基础数据与二进制数据之间的编解码,其支持基础数据类型以及列表、字典。其在iOS端使用了NSJSONSerialization作为序列化的工具,而在Android端则使用了其自定义的JSONUtil与StringCodec作为序列化工具。
  • BinaryCodec
    BinaryCodec是最为简单的一种Codec,因为其返回值类型和入参的类型相同,均为二进制格式(Android中为ByteBuffer,iOS中为NSData)。实际上,BinaryCodec在编解码过程中什么都没做,只是原封不动将二进制数据消息返回而已。或许你会因此觉得BinaryCodec没有意义,但是在某些情况下它非常有用,比如使用BinaryCodec可以使传递内存数据块时在编解码阶段免于内存拷贝。

 

3

. 方法编解码MethodCodec有两个子类:

 

  • StandardMethodCodec
    MethodCodec的默认实现,StandardMethodCodec的编解码依赖于StandardMessageCodec,当其编码MethodCall时,会将method和args依次使用StandardMessageCodec编码,写入二进制数据容器。其在编码方法的调用结果时,若调用成功,会先向二进制数据容器写入数值0(代表调用成功),再写入StandardMessageCodec编码后的result。而调用失败,则先向容器写入数据1(代表调用失败),再依次写入StandardMessageCodec编码后的code,message和detail。
  • JSONMethodCodec
    JSONMethodCodec的编解码依赖于JSONMessageCodec,当其在编码MethodCall时,会先将MethodCall转化为字典{"method":method,"args":args}。其在编码调用结果时,会将其转化为一个数组,调用成功为[result],调用失败为[code,message,detail]。再使用JSONMessageCodec将字典或数组转化为二进制数据。

 

4

. 通信工具BinaryMessager

 

BinaryMessenger是Platform端与Flutter端通信的工具,其通信使用的消息格式为二进制格式数据。当我们初始化一个Channel,并向该Channel注册处理消息的Handler时,实际上会生成一个与之对应的BinaryMessageHandler,并以channel name为key,注册到BinaryMessenger中。当Flutter端发送消息到BinaryMessenger时,BinaryMessenger会根据其入参channel找到对应的BinaryMessageHandler,并交由其处理。

Binarymessenger并不知道Channel的存在,它只和BinaryMessageHandler打交道。而Channel和BinaryMessageHandler则是一一对应的。由于Channel从BinaryMessageHandler接收到的消息是二进制格式数据,无法直接使用,故Channel会将该二进制消息通过Codec(消息编解码器)解码为能识别的消息并传递给Handler进行处理。

当Handler处理完消息之后,会通过回调函数返回result,并将result通过编解码器编码为二进制格式数据,通过BinaryMessenger发送回Flutter端。

具体实现

BasicMessageChannel

  • flutter端代码

    BasicMessageChannel _basicMessageChannel = BasicMessageChannel('my_flutter.io/message', StandardMessageCodec());BasicMessageChannel _basicMessageChannel2 = BasicMessageChannel('my_flutter.io/message2', StandardMessageCodec());Future
    _sendMessage() async { String reply = await _basicMessageChannel.send('发送给Native端的数据'); debugPrint(reply); }void receiveMessage() { _basicMessageChannel2.setMessageHandler((message) async { debugPrint("message : $message"); return '返回给Native端'; });}复制代码

    注意 native端像flutter发送消息时,需注意时机,待flutter注册监听后,才能收到native的消息。

  • native(ios)端代码

    let basicChannel = FlutterBasicMessageChannel(name: "my_flutter.io/message", binaryMessenger: flutterViewController.binaryMessenger)  basicChannel.setMessageHandler { (message, reply) in  debugPrint(message ?? "my flutter!")  reply("返回给Flutter端的数据")}		  		  		let basicChannel2 = FlutterBasicMessageChannel(name: "my_flutter.io/message2", binaryMessenger: flutterViewController.binaryMessenger)  basicChannel2.sendMessage("发送给Flutter端数据") { (reply) in  debugPrint(reply ?? "")}复制代码

MethodChannel

  • flutter端代码

    static const platform = const MethodChannel('samples.flutter.dev/battery');Future
    _getBatteryLevel() async { String batteryLevel; try { final int result = await platform.invokeMethod('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { debugPrint("batteryLevel : $batteryLevel"); _batteryLevel = batteryLevel; });}复制代码
  • native端代码

    let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",  										binaryMessenger: flutterViewController.binaryMessenger)batteryChannel.setMethodCallHandler({  (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in    // Note: this method is invoked on the UI thread.    guard call.method == "getBatteryLevel" else {    result(FlutterMethodNotImplemented)    return  }self.receiveBatteryLevel(result: result)})private func receiveBatteryLevel(result: FlutterResult) {  let device = UIDevice.current  device.isBatteryMonitoringEnabled = true  if device.batteryState == UIDevice.BatteryState.unknown {  result(FlutterError(code: "UNAVAILABLE",  				message: "Battery info unavailable",  				details: nil))  } else {    result(Int(device.batteryLevel * 100))  }}复制代码

EventChannel

  • flutter

    //声明eventChannel实例static const _eventChannel = EventChannel('flutter.io/event');//定义两个回调方法void _onData(Object data) {  debugPrint("data: $data");  if (data is String) {  setState(() {    _orientation = data;  }); }}void _onError(Object error) {  debugPrint("error : $error");  PlatformException exception = error;  setState(() {    _orientation = exception?.message ?? 'unknown.';  });}//设置监听_eventChannel.receiveBroadcastStream().listen(_onData, onError: _onError);复制代码
  • native

    let eventChannel = FlutterEventChannel(name: "flutter.io/event", binaryMessenger: flutterViewController.binaryMessenger)eventChannel.setStreamHandler(self)//定义一个全局的回调var sink: FlutterEventSink? = nil//这是两个flutter引擎的回调方法//flutter开始监听这个channel时的回调, arguments是flutter传给native的参数,event作为native给flutter回调使用。func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {  sink = events  // arguments flutter给native的参数  events("portrait")  NotificationCenter.default.addObserver(self, selector: #selector(self.onBatteryStateDidChange(_:)), name:UIDevice.orientationDidChangeNotification, object: nil)  return nil;}	  //flutter不再监听回调func onCancel(withArguments arguments: Any?) -> FlutterError? {  sink = nil  return nil}		@objc func onBatteryStateDidChange(_ notification: NotificationCenter) {  let orientation = UIDevice.current.orientation  switch orientation {  case .portrait:  	sink?("portrait")  case .portraitUpsideDown:  	sink?("portraitUpsideDown")  case .landscapeLeft:  	sink?("landscapeLeft")  case .landscapeRight:  	sink?("landscapeRight")  case .faceUp:  	sink?("faceUp")  case .faceDown:  	sink?("faceDown")  default:  	sink?("unknown")  }}

作者:王飞1492762750110
链接:https://juejin.im/post/5f04273b5188252e7b469db8
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的文章
初识react(四) react中异步解决方案之 redux-saga
查看>>
初识react(一) 揭开jsx语法和虚拟DOM面纱
查看>>
初识react(五) 数据流终极解决方案 dva(零配置)
查看>>
Redux-saga框架使用详解及Demo教程
查看>>
dva框架使用详解及Demo教程
查看>>
精简版 koa 简单实现
查看>>
react16 源码阅读学习记录
查看>>
ES 6 装饰器与 React 高阶组件
查看>>
React 老版本的context API使用对比
查看>>
从零开始学习Koa(一)
查看>>
从零开始学习Koa(二)
查看>>
200行代码实现简版react
查看>>
重拾React: React 16.0
查看>>
聊聊 koa 中间件
查看>>
发布订阅模式还不会??戳这里,50行核心代码,手把手教你学会
查看>>
六种排序算法的JavaScript实现以及总结
查看>>
JS专题之去抖函数
查看>>
ES6面试、复习干货知识点汇总(全)
查看>>
React中使用styled-components的基础使用
查看>>
Vue和React的对比
查看>>