Implement requestPermission API (#190)

* Implement requestPermission api

* Update example ios podfile

* Update example windows dependencies

* Update Readme and Changelog

* Resolve Ai Comments

* Improve bluetooth permission handling

* Update Readme

* Improve readme

---------

Co-authored-by: Foti Dim <foti@navideck.com>
This commit is contained in:
Rohit Sangwan
2025-11-13 21:52:17 +05:30
committed by GitHub
parent 6e82ceed77
commit 970db165dd
36 changed files with 606 additions and 305 deletions
+2 -2
View File
@@ -24,7 +24,7 @@ if (flutterVersionName == null) {
android {
namespace "com.navideck.universal_ble_example"
compileSdkVersion 35
compileSdkVersion 36
ndkVersion flutter.ndkVersion
compileOptions {
@@ -45,7 +45,7 @@ android {
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion 35
targetSdkVersion 36
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
+1 -1
View File
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https://services.gradle.org/distributions/gradle-8.14.3-all.zip
+2 -2
View File
@@ -18,8 +18,8 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.6.1" apply false
id "org.jetbrains.kotlin.android" version "1.9.0" apply false
id "com.android.application" version "8.13.1" apply false
id "org.jetbrains.kotlin.android" version "2.2.21" apply false
}
include ":app"
-12
View File
@@ -1,39 +1,27 @@
PODS:
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
- integration_test (0.0.1):
- Flutter
- permission_handler_apple (9.3.0):
- Flutter
- universal_ble (0.0.1):
- Flutter
- FlutterMacOS
DEPENDENCIES:
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- universal_ble (from `.symlinks/plugins/universal_ble/darwin`)
EXTERNAL SOURCES:
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
Flutter:
:path: Flutter
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
universal_ble:
:path: ".symlinks/plugins/universal_ble/darwin"
SPEC CHECKSUMS:
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
PODFILE CHECKSUM: 4f1c12611da7338d21589c0b2ecd6bd20b109694
@@ -198,7 +198,6 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
2F5EDC3CC74E7431E4CB95DF /* [CP] Embed Pods Frameworks */,
6B74A273405B2F59259FD41D /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -325,23 +324,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
6B74A273405B2F59259FD41D /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
7E79F47357B64513A488AC3E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
+5
View File
@@ -131,4 +131,9 @@ class MockUniversalBle extends UniversalBlePlatform {
Future<bool> disableBluetooth() {
throw UnimplementedError();
}
@override
Future<void> requestPermissions({bool withAndroidFineLocation = false}) {
throw UnimplementedError();
}
}
-101
View File
@@ -1,101 +0,0 @@
// ignore_for_file: avoid_print
import 'dart:async';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:permission_handler/permission_handler.dart';
/*
Required Permissions :
<----------->
IOS :
- Bluetooth
<----------->
Android :
if AndroidVersions < 12
- Location
- Bluetooth
else
- Bluetooth Scan
- Bluetooth Connect
<----------->
Macos :
<----------->
Windows : None
<----------->
Linux : None
<----------->
Web :
Check if Browser Supports Bluetooth
*/
class PermissionHandler {
static Future<bool> arePermissionsGranted() async {
if (!isMobilePlatform) return true;
var status = await _permissionStatus;
bool blePermissionGranted = status[0];
bool locationPermissionGranted = status[1];
if (locationPermissionGranted && blePermissionGranted) return true;
if (!blePermissionGranted) {
PermissionStatus blePermissionCheck =
await Permission.bluetooth.request();
if (blePermissionCheck.isPermanentlyDenied) {
print("Bluetooth Permission Permanently Denied");
openAppSettings();
}
return false;
}
if (!locationPermissionGranted) {
PermissionStatus locationPermissionCheck =
await Permission.location.request();
if (locationPermissionCheck.isPermanentlyDenied) {
print("Location Permission Permanently Denied");
openAppSettings();
}
return false;
}
return false;
}
static Future<List<bool>> get _permissionStatus async {
bool blePermissionGranted = false;
bool locationPermissionGranted = false;
if (await requiresExplicitAndroidBluetoothPermissions) {
bool bleConnectPermission =
(await Permission.bluetoothConnect.request()).isGranted;
bool bleScanPermission =
(await Permission.bluetoothScan.request()).isGranted;
blePermissionGranted = bleConnectPermission && bleScanPermission;
locationPermissionGranted = true;
} else {
PermissionStatus permissionStatus = await Permission.bluetooth.request();
blePermissionGranted = permissionStatus.isGranted;
locationPermissionGranted = await requiresLocationPermission
? (await Permission.locationWhenInUse.request()).isGranted
: true;
}
return [blePermissionGranted, locationPermissionGranted];
}
static bool get isMobilePlatform =>
!kIsWeb && (Platform.isAndroid || Platform.isIOS);
static Future<bool> get requiresLocationPermission async =>
!kIsWeb &&
Platform.isAndroid &&
(!await requiresExplicitAndroidBluetoothPermissions);
static Future<bool> get requiresExplicitAndroidBluetoothPermissions async {
if (kIsWeb || !Platform.isAndroid) return false;
AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
return androidInfo.version.sdkInt >= 31;
}
}
+8 -8
View File
@@ -7,7 +7,6 @@ import 'package:universal_ble_example/data/mock_universal_ble.dart';
import 'package:universal_ble_example/home/widgets/scan_filter_widget.dart';
import 'package:universal_ble_example/home/widgets/scanned_devices_placeholder_widget.dart';
import 'package:universal_ble_example/home/widgets/scanned_item_widget.dart';
import 'package:universal_ble_example/data/permission_handler.dart';
import 'package:universal_ble_example/peripheral_details/peripheral_detail_page.dart';
import 'package:universal_ble_example/widgets/platform_button.dart';
import 'package:universal_ble_example/widgets/responsive_buttons_grid.dart';
@@ -90,9 +89,7 @@ class _MyAppState extends State<MyApp> {
}
Future<void> startScan() async {
await UniversalBle.startScan(
scanFilter: scanFilter,
);
await UniversalBle.startScan(scanFilter: scanFilter);
}
Future<void> _getSystemDevices() async {
@@ -221,12 +218,15 @@ class _MyAppState extends State<MyApp> {
),
if (BleCapabilities.requiresRuntimePermission)
PlatformButton(
text: 'Check Permissions',
text: 'Request Permissions',
onPressed: () async {
bool hasPermissions =
await PermissionHandler.arePermissionsGranted();
if (hasPermissions) {
try {
await UniversalBle.requestPermissions(
withAndroidFineLocation: false,
);
showSnackbar("Permissions granted");
} catch (e) {
showSnackbar(e.toString());
}
},
),
@@ -5,10 +5,8 @@
import FlutterMacOS
import Foundation
import device_info_plus
import universal_ble
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
UniversalBlePlugin.register(with: registry.registrar(forPlugin: "UniversalBlePlugin"))
}
-6
View File
@@ -1,26 +1,20 @@
PODS:
- device_info_plus (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- universal_ble (0.0.1):
- Flutter
- FlutterMacOS
DEPENDENCIES:
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- universal_ble (from `Flutter/ephemeral/.symlinks/plugins/universal_ble/darwin`)
EXTERNAL SOURCES:
device_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
FlutterMacOS:
:path: Flutter/ephemeral
universal_ble:
:path: Flutter/ephemeral/.symlinks/plugins/universal_ble/darwin
SPEC CHECKSUMS:
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
+8 -93
View File
@@ -81,22 +81,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.11"
device_info_plus:
dependency: "direct main"
description:
name: device_info_plus
sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110"
url: "https://pub.dev"
source: hosted
version: "9.1.2"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
url: "https://pub.dev"
source: hosted
version: "7.0.3"
expandable:
dependency: "direct main"
description:
@@ -160,11 +144,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
@@ -179,10 +158,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0"
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.1"
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
@@ -235,10 +214,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.0"
path:
dependency: transitive
description:
@@ -247,54 +226,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849"
url: "https://pub.dev"
source: hosted
version: "11.4.0"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc
url: "https://pub.dev"
source: hosted
version: "12.1.0"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
url: "https://pub.dev"
source: hosted
version: "9.4.7"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
url: "https://pub.dev"
source: hosted
version: "0.1.3+5"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
url: "https://pub.dev"
source: hosted
version: "4.3.0"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.2.1"
petitparser:
dependency: transitive
description:
@@ -384,10 +315,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev"
source: hosted
version: "0.7.6"
version: "0.7.7"
typed_data:
dependency: transitive
description:
@@ -402,7 +333,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.21.1"
version: "1.0.0"
vector_math:
dependency: transitive
description:
@@ -435,22 +366,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.0"
win32:
dependency: transitive
description:
name: win32
sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03"
url: "https://pub.dev"
source: hosted
version: "5.14.0"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
url: "https://pub.dev"
source: hosted
version: "1.1.5"
xml:
dependency: transitive
description:
@@ -461,4 +376,4 @@ packages:
version: "6.6.1"
sdks:
dart: ">=3.8.0 <4.0.0"
flutter: ">=3.29.0"
flutter: ">=3.18.0-18.0.pre.54"
-2
View File
@@ -12,8 +12,6 @@ dependencies:
convert: ^3.1.1
expandable: ^5.0.1
cupertino_icons: ^1.0.2
permission_handler: ^11.3.1
device_info_plus: ^9.0.2
universal_ble:
path: ../
@@ -6,12 +6,9 @@
#include "generated_plugin_registrant.h"
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <universal_ble/universal_ble_plugin_c_api.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
UniversalBlePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UniversalBlePluginCApi"));
}
@@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
permission_handler_windows
universal_ble
)