前言
本篇文章首要介绍 permission_handler、flutter_blue
运用,事例中会运用 image_picker、flutter_progress_hud
,因而后边两个也会简略介绍下
image_picker(挑选相片或许摄影库)
flutter_progress_hud(hud加载库)
permission_handler(权限恳求判别库)
flutter_blue(蓝牙库)]
image_gallery_saver(单纯的保存图片到相册)
事例demo
蓝牙播送外设demo-swift版别:假如想念 Object-C
版别,前面也有,这个就是那个翻译改动而来
flutter_progress_hud、image_picker
先简略介绍一下这两个,一个是加载框(flutter_progress_hud
),一个是图片挑选器(支撑相片和图片)(flutter_progress_hud
)
flutter_progress_hud
运用比较简略不多说了,用的也比较多,一般恳求网络数据运用,假如还有不满足,能够查找一下其他的,这个仅仅最基础的
Scaffold(
appBar: AppBar(
title: const Text("ProgressHUD"),
),
//嵌套在外层,展现加载框时会在中间,并盖住
//里边有些特点能够微调,能够点进去查看
body: ProgressHUD(
child: Container(),
),
);
显现或许躲藏
//显现HUD
ProgressHUD.of(context)?.show();
//显现带文字的HUD
ProgressHUD.of(context)?.showWithText(text);
//躲藏HUD
ProgressHUD.of(context)?.dismiss();
简略看一下效果吧
image_picker
这个用的也比较多,摄影、相册挑选图片,一般只需用户头像、上传反应的都会用到,也比较常见
import 'package:image_picker/image_picker.dart';
//声明参数
final ImagePicker _picker = ImagePicker();
运用相机摄影相片
_picker.pickImage(source: ImageSource.camera).then((value) {
print(value);
}).catchError((error) {
//失利了走这儿,能够在这儿判别权限
Permission.camera.status
});
运用相册挑选图片
_picker.pickImage(source: ImageSource.gallery).then((value) {
print(value);
}).catchError((error) {
//留意相册运用的是这个权限
Permission.photos.status
});
能够看到image_picker
运用过程中设计到了权限,后边会给出权限的运用
permission_handler
介绍权限的运用,会给出几个事例,一同也是蓝牙运用时判别权限时会用到的一个库
permission_handler(权限恳求判别库)
参数与基础运用
默许的权限状况
//常见的有下面几种状况
/*
denied, //没授权默许是这个,也保禁绝特殊情况是表明回绝的,最好是先恳求权限后用于判别
granted, //正常运用
restricted, //被操作体系回绝,例如家长操控等
limited, //被约束了部分功用,适用于部分权限,例如相册的
permanentlyDenied, //这个权限表明永久回绝,不显现弹窗,用户能够手动调理(也有或许是体系关闭了该权限导致的)
*/
恳求权限,在运用前,能够经过 request
来恳求权限,防止有些权限没办法给出准确提示,且或许犯错
//提前恳求权限,假如没给过权限,能够触发权限,以便于获取权限信息
final status = await Permission.camera.request(); //恳求单个权限
Map<Permission, PermissionStatus> statuses = await [
Permission.photosAddOnly,
Permission.photos
].request(); //一同恳求多个全新啊
//能够一同恳求多个 await,被约束的部分权限理论也能够运用才是,依据情况作出判别
if (status != PermissionStatus.granted) {
//无相机权限,请前往设置翻开
//openAppSettings();
return;
}
//运用对应功用即可
await _picker.pickImage(source: ImageSource.camera)
直接获取权限,可是初度运用并不会触发对应的权限恳求弹窗,因而或许判别犯错,适用于运用对应功用失利后给出相应的提示
//直接摄影获取图片
_picker.pickImage(source: ImageSource.camera).then((value) {
print(value);
}).catchError((error) {
//或许用户回绝了权限,因而会走到这儿
print(error);
//能够恳求用失利的时分在提示权限问题,或许翻开授权页面
Permission.camera.status.then((status) {
print(status);
if (status == PermissionStatus.denied || status == PermissionStatus.permanentlyDenied) {
//回绝,能够跳转权限页面
showToast(context, "相机权限被回绝");
}else if (status == PermissionStatus.granted) {
//正常访问
showToast(context, "相机权限能够正常运用");
}
});
});
留意
:不是一切的三方没给权限都会走到catch
,也或许直接报错闪退,因而最好先判别权限后给出提示
//简略给出几个常用权限,里边还有很多
Map<Permission, PermissionStatus> statuses = await [
Permission.camera, //相机
Permission.photosAddOnly, //写入相册
Permission.photos, //读取相册
Permission.locationWhenInUse, //运用运用期间获取定位
Permission.bluetooth, //获取蓝牙
].request();
跳转权限操作界面
假如用户没翻开权限,能够提示给用户翻开权限,下面能够跳转到运用的权限界面,便利用户更新权限
openAppSettings();
android权限声明
需求留意的是,运用什么权限查找一下即可,运用如下所示(需求留意的是,其有些权限跟ios端是不一样的,例如)
<uses-permission android:name="android.permission.CAMERA" />
<!--相册的读写权限,跟ios端不一样哈,其他的有些也是,依据平台动态调整即可-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
ios端权限声明
在 info.plist
中设置对应权限即可,$(PRODUCT_BUNDLE_NAME)
为app的姓名,动态最好
<string>$(PRODUCT_BUNDLE_NAME)想在app运用期间获取您的定位,以便于更新方位</string>
<key>NSBluetoothPeripheralUsageDescription</key>
此外,还需求在 podfile
中参加下面的脚本(放到最后即可),依据对应权限需求,将 PERMISSION_???=1
前面的注释 #
去掉即可(上面的不要撤销),这儿假定去掉的是 camera
post_install do |installer|
installer.pods_project.targets.each do |target|
... # Here are some configurations automatically generated by flutter
# Start of the permission_handler configuration
target.build_configurations.each do |config|
# You can enable the permissions needed here. For example to enable camera
# permission, just remove the `#` character in front so it looks like this:
#
# ## dart: PermissionGroup.camera
# 'PERMISSION_CAMERA=1'
#
# Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.calendar
# 'PERMISSION_EVENTS=1',
## dart: PermissionGroup.reminders
# 'PERMISSION_REMINDERS=1',
## dart: PermissionGroup.contacts
# 'PERMISSION_CONTACTS=1',
## dart: PermissionGroup.camera
## 假定这儿面用到了相机权限,只撤销掉其前面的注释即可
'PERMISSION_CAMERA=1',
## dart: PermissionGroup.microphone
# 'PERMISSION_MICROPHONE=1',
## dart: PermissionGroup.speech
# 'PERMISSION_SPEECH_RECOGNIZER=1',
## dart: PermissionGroup.photos
# 'PERMISSION_PHOTOS=1',
## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
# 'PERMISSION_LOCATION=1',
## dart: PermissionGroup.notification
# 'PERMISSION_NOTIFICATIONS=1',
## dart: PermissionGroup.mediaLibrary
# 'PERMISSION_MEDIA_LIBRARY=1',
## dart: PermissionGroup.sensors
# 'PERMISSION_SENSORS=1',
## dart: PermissionGroup.bluetooth
# 'PERMISSION_BLUETOOTH=1',
## dart: PermissionGroup.appTrackingTransparency
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
## dart: PermissionGroup.criticalAlerts
# 'PERMISSION_CRITICAL_ALERTS=1'
]
end
# End of the permission_handler configuration
end
end
再详细点的权限,都能够经过查找一下获取即可,详细的就不多讲述了(ios的文档给出了一些,先贴出来便利大家运用吧)
Permission | Info.plist | Macro |
---|---|---|
PermissionGroup.calendar | NSCalendarsUsageDescription | PERMISSION_EVENTS |
PermissionGroup.reminders | NSRemindersUsageDescription | PERMISSION_REMINDERS |
PermissionGroup.contacts | NSContactsUsageDescription | PERMISSION_CONTACTS |
PermissionGroup.camera | NSCameraUsageDescription | PERMISSION_CAMERA |
PermissionGroup.microphone | NSMicrophoneUsageDescription | PERMISSION_MICROPHONE |
PermissionGroup.speech | NSSpeechRecognitionUsageDescription | PERMISSION_SPEECH_RECOGNIZER |
PermissionGroup.photos | NSPhotoLibraryUsageDescription | PERMISSION_PHOTOS |
PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse | NSLocationUsageDescription, NSLocationAlwaysAndWhenInUseUsageDescription, NSLocationWhenInUseUsageDescription | PERMISSION_LOCATION |
PermissionGroup.notification | PermissionGroupNotification | PERMISSION_NOTIFICATIONS |
PermissionGroup.mediaLibrary | NSAppleMusicUsageDescription, kTCCServiceMediaLibrary | PERMISSION_MEDIA_LIBRARY |
PermissionGroup.sensors | NSMotionUsageDescription | PERMISSION_SENSORS |
PermissionGroup.bluetooth | NSBluetoothAlwaysUsageDescription, NSBluetoothPeripheralUsageDescription | PERMISSION_BLUETOOTH |
PermissionGroup.appTrackingTransparency | NSUserTrackingUsageDescription | PERMISSION_APP_TRACKING_TRANSPARENCY |
PermissionGroup.criticalAlerts | PermissionGroupCriticalAlerts | PERMISSION_CRITICAL_ALERTS |
flutter_blue 与 蓝牙权限
官方供给的蓝牙库,运用非常简略,运用前需求先判别权限
注
:ios体系有些版别是没有权限弹窗的,声明了就能够直接运用,但不代表不需求判别恳求了,后边大多数版别仍是需求用到的
flutter_blue(蓝牙库)
外设端demo:被衔接的外设播送端,由swift
编写,前面也有object-c
编写的版别
蓝牙播送外设demo-swift版别:依据上面的播送端代码翻译改动而来(都是自己的)
本事例demo:中心设备端,在本事例中的bluetooth
文件中
PS
: 为什么没有 flutter 的播送端事例,因为没供给,flutter 跨平台作为一个中心设备衔接现已很给力了,现在蓝牙对android、ios支撑不错,对鸿蒙支撑很拉跨
蓝牙权限装备
ios端装备
这儿面装备就比较固定,只不过有一个alway
权限的,表明答应后台持续运行蓝牙,假如不是有必要的话无需填写,否则易形成审核问题,现在无需恳求定位,也不用翻开即可正常运用蓝牙
info.plist
<key>NSBluetoothAlwaysUsageDescription</key>
<string>$(PRODUCT_BUNDLE_NAME)想运用您的蓝牙,以便于持续跟设备衔接发送信息</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>$(PRODUCT_BUNDLE_NAME)想运用您的蓝牙,以便于跟设备发送信息</string>
podfile
//撤销这一行的注释即可
'PERMISSION_BLUETOOTH=1',
安卓端装备
安卓端还有点不一样,android 12
后,推出新权限,能够在在 manifest.xml
中填写如下代码,也能够在代码中动态恳求
前面两个是兼容 android12
之前的版别,所以约束了最大版别,后边则是分隔两个,需求留意的是,现在都需求恳求定位权限了,否则功用无法正常运用
manifest.xml
<!-- android6之前运用该权限即可 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<!-- Required if your app derives physical location from Bluetooth scan results.-->
<!-- android6之后需求运用蓝牙也要声明定位权限,且用户还要翻开 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Request legacy Bluetooth permissions on older devices.-->
<!-- android6~12 之间运用该权限-->
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<!-- android12 之后权限分隔-->
<!-- android12 扫描周边设备需求该权限-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- android12 衔接交互运用的权限-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- android12 播送功用,让别人也能衔接搞设备,一般和connect一同运用-->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
build.gradle
Android {
defaultConfig {
minSdkVersion: 19
flutter 端中心设备运用
设备最为中心设备扫描周边设备,大致经过了下面几个阶段:
扫描
-> 衔接
-> 查找服务
-> 查找特征
-> 运用特征进行交互
细节直接看代码即可,下面简略上一个权限的判别,android
实践还要恳求定位,即:
ios
恳求一个 bluetooth
权限,android
恳求 bluetooth、bluetoothScan、bluetoothConnect、locationWhenInUse
四个权限,自己动态设置即可
ps
:除了要判别权限
还有蓝牙是否翻开
,此外这儿面权限判别不如原生端准确,一些体系版别的不需求权限,可是这儿或许会返回回绝,因而最好给这些特殊版别一些提示,以便于"没给权限"
也能持续履行(否则他们永久无法运用蓝牙了,只能更新体系)
//仅仅是一个事例,实践能够写的严谨简略一些,先判别翻开,后判别权限都能够,扫描前检测即可
//恳求一下权限,某些机型蓝牙默许能够直接运用,无需权限,可是权限会反应被回绝状况
//因而一些机型,能够恳求后给出持续向后履行的弹窗,防止用户永久无法运用该功用,失利了留意反应即可
[Permission.bluetoothScan, Permission.bluetoothConnect, Permission.bluetooth].request().then((status) {
print(status);
//只会有一个弹窗,android端多个权限,但一般一般显现了一个就完毕了,首要看ios的
//android端留意定位权限, Permission.locationWhenInUse 即可
if (status[Permission.bluetooth] != PermissionStatus.granted) {
print("没有蓝牙权限"); //这一个是都有的,能够用这个判别,当然最好分平台判别
}
});
//还要判别蓝牙是否现已翻开
_bluetooth.isOn.then((value) {
//获取蓝牙翻开状况
});
扫描、衔接、查找服务和特征逻辑如下所示,此外还设置类的告诉,以便于能够及时收到音讯
//开端扫描并衔接设备
void startConnectDevice({isRepeat = false}) {
bool isSearched = false; //为了防止衔接过程中没有中止扫描导致的屡次衔接问题,但不得不衔接后在撤销扫描
try {
_bluetooth.scanResults.listen((results) async {
//扫描回调,try-catch是统一处理里边的失利情况
if (isSearched) return; //现已找到了就完毕
for (ScanResult res in results) {
final device = res.device;
//检查是否是咱们需求的设备,device.name有时分不一定会有,最好运用advertisementData.localName
final name = res.advertisementData.localName;
if (name != "") print(name);
if (!isSearched && name.contains("marshal_")) {
print("找到了咱们需求的设备");
isSearched = true; //标记现已找到了
//找到了咱们要衔接的设备,并衔接,autoConnect需求设置为 false,默许为true其不会主动衔接
print("开端衔接");
//鸿蒙这个 autoConnect 会一次也衔接不上,设置为 false 则会呈现时而连得上时而连不上,支撑不太好,依据情况挑选
await device.connect(timeout: const Duration(seconds: 10), autoConnect: true);
print("衔接成功");
// 衔接成功后中止扫描,听说有些android手机中止扫描会呈现衔接功用反常,衔接成功后在中止扫描
await _bluetooth.stopScan();
//开端查找服务
print("开端查找服务");
List<BluetoothService> services = await device.discoverServices();
//遍历服务运用咱们想要的特征值
print("遍历服务找特征值");
print(services.length);
services.forEach((service) async {
var characteristics = service.characteristics;
//找咱们的特征值
for(BluetoothCharacteristic char in characteristics) {
print(char.uuid.toString());
//经过uuid过滤出咱们需求的特征值
if (char.uuid.toString().substring(0, 2) == "12") {
print("找到咱们特征值");
//假定咱们用这个,获取到特征值后保存,能够用来读写
_currentCharacteristic = char;
await setNotifiy();//设置告诉,能够接纳传递过来的音讯
await readMessage(); //随意读取一下音讯吧
}
}
});
}
}
});
}catch(error) {
print(error);
isSearched = false;
print('一般衔接失利后会走这儿,然后中止扫描');
_bluetooth.stopScan();
}
print("开端扫描");
_bluetooth.startScan(scanMode: ScanMode.balanced, timeout: const Duration(seconds: 30),);
}
接下来看看读写音讯代码,比较简略,需求留意的是,发送接纳的字符串一般运用 utf8
转化
此外,假如传输数据量比较大的话,需求屡次传递就行了(因为设备缘故一次不能传递数据过,因为传输长度约束问题,单次长度主张跟 UUID 长度一样16字节最佳,当然自己能够尝试更长的字符串(一般超越20字节就会丢失了))
//设置告诉
Future<void> setNotifiy() async {
//能够监听外设端主动发送过来的音讯
await _currentCharacteristic!.setNotifyValue(true);
_currentCharacteristic!.value.listen((event) {
print(event);
});
}
//读取外设音讯
Future<void> readMessage() async {
if (_currentCharacteristic == null) return;
List<int> value = await _currentCharacteristic!.read();
print("value");
print(value);
final string = utf8.decode(value);
print(string);
}
//向外设发送音讯
void sendMessage(String text) {
print(text);
_controller.clear();
_currentCharacteristic?.write(utf8.encode(text)).then((value) => print(value));
}
最后
快来尝试一下吧,两个事例都是通的,假如想玩更有趣的,能够更新两头代码,彼此交互,写一个情侣间的小型传输工具也不是未尝不可