概述
- 调用原生功能
- 嵌入原有项目
- Flutter模块调试
一、调用原生功能
- 1.1、Camera某些应用程序可能需要使用移动设备进行拍照或者选择相册中的照片,Flutter官方提供了插件:image_picker
- 1.1.1、添加依赖
添加对image_picker的依赖:https://pub.dev/packages/image_picker,在项目的pubspec.ymal
里面添加下面的依赖即可,然后执行右上角的Pub get
dependencies: image_picker: ^0.6.7+1
- 1.1.2、平台配置
对iOS平台,想要访问相册或者相机,需要获取用户的允许:
依然是修改info.plist
文件:/ios/Runner/Info.plist
添加对相册的访问权限:Privacy - Photo Library Usage Description
添加对相机的访问权限:Privacy - Camera Usage Description
拓展
:其他的权限- 相机权限:
Privacy - Camera Usage Description
是否允许此App使用你的相机?- 相册权限:
Privacy - Photo Library Usage Description
是否允许此App访问你的媒体资料库?- 通讯录权限:
Privacy - Contacts Usage Description
是否允许此App访问你的通讯录?- 蓝牙权限:
Privacy - Bluetooth Peripheral Usage Description
是否许允此App使用蓝牙?- 使用期间定位权限:
Privacy - Location When In Use Usage Description
是否允许此App使用定位服务?- 始终定位权限:
Privacy - Location Always Usage Description
是否允许此App始终使用定位服务?- 语音转文字权限:
Privacy - Speech Recognition Usage Description
是否允许此App使用语音识别?- 日历权限:
Privacy - Calendars Usage Description
是否允许此App使用日历?- 健康—读取数据:
Privacy - Health Share Usage Description
是否允许此App读取健康数据?- 健康—写入数据:
Privacy - Health Share Usage Description
是否允许此App写入健康数据?- 读取HomeKit:
Privacy - HomeKit Usage Description
是否允许此App访问HomeKit?- 麦克风:
Privacy - Microphone Usage Description
是否允许此App访问麦克风?- 提醒事项:
Privacy - Reminders Usage Description
是否允许此App访问提醒事项?- 运动与健身:
Privacy - Motion Usage Description
是否允许此App访问运动与健身?- 面部ID权限:
Privacy - Face ID Usage Description
是否允许此App访问Face ID?
之后选择相册或者访问相机时,会弹出如下的提示框:
1.1.3、代码实现
image_picker
的核心代码是 getImage
方法:
可以传入数据源、图片的大小、质量、前置后置摄像头等
数据源是必传参数:ImageSource
枚举类型: camera:相机
、gallery:相册
Future<PickedFile> getImage({ @required ImageSource source, double maxWidth, double maxHeight, int imageQuality, // 默认后置摄像头 CameraDevice preferredCameraDevice = CameraDevice.rear, }) { return platform.pickImage( source: source, maxWidth: maxWidth, maxHeight: maxHeight, imageQuality: imageQuality, preferredCameraDevice: preferredCameraDevice, ); }
案例演练:
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; class JKCameraScreen extends StatefulWidget { @override _JKCameraScreenState createState() => _JKCameraScreenState(); } class _JKCameraScreenState extends State<JKCameraScreen> { PickedFile _imageFile; final ImagePicker _picker = ImagePicker(); @override Widget build(BuildContext context) { return Center( child: Column( children: [ RaisedButton( child: Text('选择一个相册'), onPressed: _pickImage ), _imageFile == null ? Text('请选择一张照片') : Image.file(File(_imageFile.path)) ], ), ); } void _pickImage() async { print('选择相册'); PickedFile pickedFile = await _picker.getImage(source: ImageSource.gallery); setState(() { _imageFile = pickedFile; }); } }
1.2、电池信息
某些原生的信息,如果没有很好的插件,我们可以通过、platform channels(平台通道)
来获取信息。
- 1.2.1、平台通过介绍平台通过是如何工作的呢?
- 消息使用platform channels(平台通道)在客户端(UI)和宿主(平台)之间传递;
- 消息和响应以异步的形式进行传递,以确保用户界面能够保持响应;
调用过程大致如下:
- 1.客户端(Flutter端)发送与方法调用相对应的消息
- 2.平台端(iOS、Android端)接收方法,并返回结果;
- iOS端通过FlutterMethodChannel做出响应;
- Android端通过MethodChannel做出响应;
Flutter、iOS、Android端数据类型的对应关系:
- 1.2.2、创建测试项目我们这里创建一个获取电池电量信息的项目,分别通过iOS和Android原生代码来获取对应的信息:
- 创建方式一:默认创建方式,目前默认创建的Flutter项目,对应iOS的编程语言是Swift,对应Android的编程语言是kotlin
flutter create batterylevel
- 创建方式二:指定编程语言,如果我们希望指定编程语言,比如iOS编程语言为Objective-C,Android的编程语言为Java
flutter create -i objc -a java batterylevel2
提示:
i
代表iOS
,a
代表android
- 1.2.3、写Dart代码在Dart代码中,我们需要创建一个MethodChannel对象:
- 创建该对象时,需要传入一个name,该name是区分多个通信的名称
- 可以通过调用该对象的invokeMethod来给对应的平台发送消息进行通信
- 该调用是异步操作,需要通过await获取then回调来获取结果
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( // 启动要显示的界面 home: HomePage(), ); } } class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("原生电池的调用"), ), body: JKBatteryLevel(), ); } } class JKBatteryLevel extends StatefulWidget { @override _JKBatteryLevelState createState() => _JKBatteryLevelState(); } class _JKBatteryLevelState extends State<JKBatteryLevel> { // 定义一个平台通道 static const platform1 = const MethodChannel('com.jk/battery'); int _batterylevel = 0; @override Widget build(BuildContext context) { return Center( child: Column( children: [ RaisedButton( child: Text('获取剩余电量'), onPressed: _buildLevelInfo ), Text('电量:${_batterylevel}') ], ), ); } void _buildLevelInfo() async { // 调用原生的电池信息 final result = await platform1.invokeMethod('getBatteryInfo'); setState(() { _batterylevel = result; }); } }
- 当我们通过
platform.invokeMethod
调用对应平台方法时,需要在对应的平台实现其操作:iOS
中可以通过Objective-C
或Swift
来实现Android
中可以通过Java
或者Kotlin
来实现
- 1.2.4、编写iOS代码
- <1>、Swift 代码,在
AppDelegate.swift
里面写代码
import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // 实现获取电量信息的功能 // 1、获取FlutterViewController let flutterController: FlutterViewController = window.rootViewController as! FlutterViewController // 2、创建 FlutterMethodChannel /** name:static const platform1 = const MethodChannel('com.jk/battery'); 的 com.jk/battery,名字自己定义: 域名/名字 binaryMessenger: 二进制消息 */ let channel = FlutterMethodChannel(name: "com.jk/battery", binaryMessenger: flutterController.binaryMessenger); // 3.监听channnel方法 channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in guard call.method == "getBatteryInfo" else { // 找不到该方法 result(FlutterMethodNotImplemented) return; } let device = UIDevice.current // 电池电量的探测,设置为true,才能更好的获取电量 device.isBatteryMonitoringEnabled = true if device.batteryState == .unknown { result(FlutterError(code: "Unknown", message: "Battery is unknown", details: nil)) } else { result(Int(device.batteryLevel * 100)) } } GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
- <2>、OC 代码
#import "AppDelegate.h" #import "GeneratedPluginRegistrant.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // 1.获取FlutterViewController(是应用程序的默认Controller) FlutterViewController *flutterController = (FlutterViewController *)self.window.rootViewController; // 2.获取MethodChannel(方法通道) FlutterMethodChannel *batteryChannel = [FlutterMethodChannel methodChannelWithName:@"com.jk/battery" binaryMessenger:flutterController.binaryMessenger]; // 3.监听方法调用(会调用传入的回调函数) __weak typeof(self) weakSelf = self; [batteryChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) { // 3.1.判断是否是getBatteryInfo的调用 if ([@"getBatteryInfo" isEqualToString:call.method]) { // 1.iOS中获取信息的方式 int batteryLevel = [weakSelf getBatteryLevel]; // 2.如果没有获取到,那么返回给Flutter端一个异常 if (batteryLevel == -1) { result([FlutterError errorWithCode:@"UNAVAILABLE" message:@"Battery info unavailable" details:nil]); } else { // 3.通过result将结果回调给Flutter端 result(@(batteryLevel)); } } else { // 3.2.如果调用的是getBatteryInfo的方法, 那么通过封装的另外一个方法实现回调 result(FlutterMethodNotImplemented); } }]; [GeneratedPluginRegistrant registerWithRegistry:self]; // Override point for customization after application launch. return [super application:application didFinishLaunchingWithOptions:launchOptions]; } - (int)getBatteryLevel { // 获取信息的方法 UIDevice* device = UIDevice.currentDevice; device.batteryMonitoringEnabled = YES; if (device.batteryState == UIDeviceBatteryStateUnknown) { return -1; } else { return (int)(device.batteryLevel * 100); } } @end
- 1.2.5、编写 Android 代码
- <1>、Ktolin 代码
package com.example.batterylevel import android.content.Context import android.content.ContextWrapper import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager import android.os.Build import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity: FlutterActivity() { private val CHANNEL = "com.jk/battery" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) // 1.创建MethodChannel对象 val methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) // 2.添加调用方法的回调 methodChannel.setMethodCallHandler { call, result -> if (call.method == "getBatteryInfo") { // 2.1.1.调用另外一个自定义方法回去电量信息 val batteryLevel = getBatteryLevel() // 2.1.2. 判断是否正常获取到 if (batteryLevel != -1) { // 获取到返回结果 result.success(batteryLevel) } else { // 获取不到抛出异常 result.error("UNAVAILABLE", "Battery level not available.", null) } } else { // 2.2.如果调用的方法是getBatteryInfo,那么正常执行 result.notImplemented() } } } private fun getBatteryLevel(): Int { val batteryLevel: Int if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) } else { val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1) } return batteryLevel } }