调用原生功能
Camera
添加依赖
添加对 image_picker 的依赖:
dependencies:
flutter:
sdk: flutter
image_picker: ^0.8.4+8
平台配置
对iOS平台,想要访问相册或者相机,需要获取用户的允许,修改info.plist文件:/ios/Runner/Info.plist
- 添加对相册的访问权限:
NSPhotoLibraryUsageDescription
- 添加对相机的访问权限:
NSCameraUsageDescription
- 添加对麦克风的访问权限:
NSMicrophoneUsageDescription
代码实现
void _imagePicker() async {
/**
* 可以传入数据源、图片的大小、质量、前置后置摄像头等
* 数据源是必传参数:ImageSource枚举类型
* camera:相机
* gallery:相册
* */
PickedFile? pickedFile = await ImagePicker.platform.pickImage(source: ImageSource.gallery);
setState(() {
String path = (pickedFile?.path)!;
_imageFile = File(path);
});
}
// Image.file(_imageFile!)
电池信息
编写Dart代码
在Dart代码中,我们需要创建一个 MethodChannel
对象:
- 创建该对象时,需要传入一个
name
,该name
是区分多个通信的名称,必须唯一。 - 可以通过调用该对象的
invokeMethod
来给对应的平台发送消息进行通信,该调用是异步操作,需要通过 await 获取 then 回调来获取结果。
class _MyHomePageState extends State {
static const platform = const MethodChannel("gaowenli.com/battery");
int _result = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text("当前电量 $_result")],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
getBatteryInfo();
},
child: const Icon(Icons.add),
),
);
}
void getBatteryInfo() async {
final int result = await platform.invokeMethod("getBatteryInfo");
setState(() {
_result = result;
});
}
}
编写iOS代码
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// 1.获取FlutterViewController(是应用程序的默认Controller)
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
// 2.获取MethodChannel(方法通道)
let batteryChannel = FlutterMethodChannel(name: "gaowenli.com/battery",
binaryMessenger: controller.binaryMessenger)
// 3.监听方法调用(会调用传入的回调函数)
batteryChannel.setMethodCallHandler ({[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
// 3.1.判断是否是getBatteryInfo的调用,告知Flutter端没有实现对应的方法
guard call.method == "getBatteryInfo" else {
result(FlutterMethodNotImplemented)
return
}
// 3.2.如果调用的是getBatteryInfo的方法, 那么通过封装的另外一个方法实现回调
self?.receiveBatteryLevel(result: result)
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func receiveBatteryLevel(result: FlutterResult) {
// 1.iOS中获取信息的方式
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
// 2.如果没有获取到,那么返回给Flutter端一个异常
if device.batteryState == UIDevice.BatteryState.unknown {
result(FlutterError(code: "UNAVAILABLE",
message: "Battery info unavailable",
details: nil))
} else {
// 3.通过result将结果回调给Flutter端
result(Int(device.batteryLevel * 100))
}
}
}
嵌入原有项目
创建Flutter模块
对于需要进行混合开发的原有项目,Flutter可以作为一个库或者模块,继承进现有项目中。
- 模块引入到你的Android或iOS应用中,以使用Flutter渲染一部分的UI,或者共享的Dart代码。
- 在Flutter v1.12中,添加到现有应用的基本场景已经被支持,每个应用在同一时间可以集成一个全屏幕的Flutter实例。
但是,目前一些场景依然是有限制的:
- 运行多个Flutter实例,或在屏幕局部上运行Flutter可能会导致不可以预测的行为;
- 在后台模式使用Flutter的能力还在开发中(目前不支持);
- 将Flutter库打包到另一个可共享的库或将多个Flutter库打包到同一个应用中,都不支持;
- 添加到应用在Android平台的实现基于 FlutterPlugin 的 API,一些不支持 FlutterPlugin 的插件可能会有不可预知的行为。
创建Flutter Module
flutter create --template module hello_flutter
创建一个 iOS 项目
嵌入iOS项目
嵌入到现有iOS项目有多种方式:
- 可以使用 CocoaPods 依赖管理和已安装的 Flutter SDK ;
- 也可以通过手动编译 Flutter engine 、你的 dart 代码和所有 Flutter plugin 成 framework ,用 Xcode 手动集成到你的应用中,并更新编译设置;
iOS项目 hello_swift 初始化CocoaPods:
pod init
安装CocoaPods的依赖:
pod install
编译Podfile文件:
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
# 添加模块所在路径
flutter_application_path = '../hello_flutter'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'flutter_swift' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# 安装Flutter模块
install_all_flutter_pods(flutter_application_path)
# Pods for flutter_d
end
重新执行安装CocoaPods的依赖:
pod install
Swift代码
为了在既有的iOS应用中展示Flutter页面,需要启动 Flutter Engine
和 FlutterViewController
。
通常建议为我们的应用预热一个 长时间存活 的FlutterEngine:
- 我们将在应用启动的
app delegate
中创建一个FlutterEngine
,并作为属性暴露给外界。
import UIKit
import FlutterPluginRegistrant
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// 1.创建一个FlutterEngine对象
lazy var flutterEngine = FlutterEngine(name: "my flutter engine")
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 2.启动flutterEngine
flutterEngine.run()
return true
}
}
此时在启动的 ViewController 控制器中就可以弹出 FlutterViewController
import UIKit
import Flutter
class ViewController: UIViewController {
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
// 2.创建FlutterViewController对象(需要先获取flutterEngine)
let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine;
let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil);
flutterViewController.view.backgroundColor = .blue
self.present(flutterViewController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
}
}
我们也可以省略预先创建的 FlutterEngine :
- 不推荐这样来做,因为在第一针图像渲染完成之前,可能会出现明显的延迟。
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
let flutterViewController = FlutterViewController(project: nil, nibName: nil, bundle: nil)
flutterViewController.view.backgroundColor = .cyan
self.present(flutterViewController, animated: true, completion: nil)
}
出现错误 Failed to find assets path for "Frameworks/App.framework/flutter_assets"
导致flutter 白屏
Failed to find assets path for "Frameworks/App.framework/flutter_assets"
Metal API Validation Enabled
[VERBOSE-2:engine.cc(196)] Engine run configuration was invalid.
[VERBOSE-2:shell.cc(574)] Could not launch engine with configuration.
flutter: Observatory listening on http://127.0.0.1:59589/qPJkTN288T4=/
查看 flutter项目中 /hello_flutter/.ios/Flutter/
文件夹下面是否有 App.framework
和 Flutter.framework
。
iOS项目中是否有 App.framework
和 Flutter.framework
。
如果没有可以从 /hello_flutter/build/ios/Debug-iphoneos/
文件夹下复制一份
iOS项目重新执行 pod install
Flutter模块调试
一旦将Flutter模块继承到你的项目中,并且使用Flutter平台的API运行Flutter引擎或UI,那么就可以像普通的Android或者iOS一样来构建自己的Android或者iOS项目了
但是Flutter的有一个非常大的优势是其快速开发,也就是hot reload。
那么对应Flutter模块,我们如何使用hot reload加速我们的调试速度呢?
可以使用 flutter attach
- --app-id是指定哪一个应用程序
- -d是指定连接哪一个设备
连接成功后,按键 R 即可热更新