flutter的库是以package的方式来管理的。Package 分为两种:
Package 会被发布到 pub.dev 网站上。中文镜像dev.flutter-io。加载速度更快。
添加依赖
打开应用文件夹下的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的一系列版本。我们也可以指定依赖库的为特定的版本:
安装
在命令行中运行: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。更多详细信息,请参阅 Package dependencies。
升级依赖
第一次获取依赖时,Pub 会下载依赖及其兼容的最新版本。然后通过创建 lockfile 锁定依赖,以始终使用这个版本。 Pub 会在 pubspec 旁创建并存储一个名为 pubspec.lock 文件。它列出了使用的每个依赖包的指定版本(当前包或传递包的版本)。
使用命令 pub upgrade :
$ pub upgrade
复制代码
上面的命令用于重新生成 lockfile 文件,并使用最新可用版本的依赖包。如果仅升级某个依赖,可以在命令中指定需要升级的包:
$ pub upgrade xxx
复制代码
上面的命令升级 xxx 到最新版本,但维持其它包不变。
通过使用 packages (的模式)可以创建易于共享的模块化代码。一个最基本的 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 目录下添加 单元测试。
如果想要开发一个调用特定平台 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 应用。
步骤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)。
接下来进行如下步骤:
步骤c:添加 iOS 平台代码(.swift/.h+.m)
我们建议你使用 Xcode 来编辑 iOS 代码。使用 Xcode 编辑 iOS 平台代码之前,首先确保代码至少被构建过一次(即从 IDE/编辑器执行示例程序,或在终端中执行以下命令: cd hello/example; flutter build ios --no-codesign)。
接下来执行下面步骤:
hello/example/ios/Runner.xcworkspace
文件。步骤d:关联 API 和平台代码
最后将dart编写的代码和平台代码通过channel实现互联。
Flutter采用MethodChannel
与各平台进行通信,如下图所示:
消息是以异步的形式进行传递,以确保用户界面能够保持响应。
需要注意的是,上图中的箭头是双向的。也就是说,我们不仅可以从 Flutter 调用 Android/iOS 的代码,也可以从 Android/iOS 调用 Flutter。调用时相关的参数对应如下:
1
. PlatformChannel类说明:
2
. 消息编解码MessageCodec有4个子类:
3
. 方法编解码MethodCodec有两个子类:
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 ?? "")
}
复制代码
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))
}
}
复制代码
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")
}
}