Skip to content

Commit 37777f1

Browse files
ctrlVntctrlVntmariobehlinghpdang
authored
fix: replaced flutter_blue_plus library with universal_ble (#1718)
Co-authored-by: ctrlVnt <gordanobruno227@gmail.com> Co-authored-by: Mario Behling <mb@mariobehling.de> Co-authored-by: Hong Phuc Dang <dhppat@gmail.com>
1 parent 2f81cf5 commit 37777f1

21 files changed

Lines changed: 132 additions & 237 deletions

iOS/Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Uncomment this line to define a global platform for your project
2-
platform :ios, '13.0'
2+
platform :ios, '15.6'
33

44
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
55
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

lib/bademagic_module/bluetooth/connect_state.dart

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
import 'package:badgemagic/bademagic_module/bluetooth/datagenerator.dart';
22
import 'package:badgemagic/bademagic_module/bluetooth/write_state.dart';
3-
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
3+
import 'package:universal_ble/universal_ble.dart';
44
import 'base_ble_state.dart';
55

66
class ConnectState extends RetryBleState {
7-
final ScanResult scanResult;
7+
final BleDevice scanResult;
88
final DataTransferManager manager;
99

1010
ConnectState({required this.manager, required this.scanResult});
1111

1212
@override
1313
Future<BleState?> processState() async {
14+
final deviceId = scanResult.deviceId;
15+
1416
try {
1517
try {
16-
await scanResult.device.disconnect();
18+
await UniversalBle.disconnect(deviceId);
1719
logger.d("Pre-emptive disconnect for clean state");
1820
await Future.delayed(const Duration(seconds: 1));
1921
} catch (_) {
2022
logger.d("No existing connection to disconnect");
2123
}
2224

23-
await scanResult.device.connect(autoConnect: false);
24-
BluetoothConnectionState connectionState =
25-
await scanResult.device.connectionState.first;
25+
await UniversalBle.connect(deviceId);
26+
27+
final connectionState = await UniversalBle.getConnectionState(deviceId);
2628

27-
if (connectionState == BluetoothConnectionState.connected) {
29+
if (connectionState == BleConnectionState.connected) {
2830
logger.d("Device connected successfully");
2931
toast.showToast('Device connected successfully.');
3032

31-
manager.connectedDevice = scanResult.device;
33+
manager.connectedDevice = scanResult;
3234

3335
final writeState = WriteState(
34-
device: scanResult.device,
36+
device: scanResult,
3537
manager: manager,
3638
);
3739

lib/bademagic_module/bluetooth/datagenerator.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,24 @@ import 'package:badgemagic/bademagic_module/utils/data_to_bytearray_converter.da
33
import 'package:badgemagic/bademagic_module/utils/file_helper.dart';
44
import 'package:badgemagic/providers/badge_message_provider.dart';
55
import 'package:badgemagic/providers/imageprovider.dart';
6-
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
6+
import 'package:universal_ble/universal_ble.dart';
77
import 'package:get_it/get_it.dart';
88

9+
import '../utils/toast_utils.dart';
10+
11+
Future<bool> checkAdapterState() async {
12+
final adapterState = await UniversalBle.getBluetoothAvailabilityState();
13+
if (adapterState != AvailabilityState.poweredOn) {
14+
ToastUtils().showErrorToast('Please turn on Bluetooth');
15+
return false;
16+
}
17+
return true;
18+
}
19+
920
class DataTransferManager {
1021
final Data data;
1122

12-
BluetoothDevice? connectedDevice;
23+
BleDevice? connectedDevice;
1324

1425
final BadgeMessageProvider badgeData = BadgeMessageProvider();
1526
final DataToByteArrayConverter converter = DataToByteArrayConverter();

lib/bademagic_module/bluetooth/scan_state.dart

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ import 'dart:async';
22
import 'package:badgemagic/bademagic_module/bluetooth/connect_state.dart';
33
import 'package:badgemagic/bademagic_module/bluetooth/datagenerator.dart';
44
import 'package:badgemagic/providers/BadgeScanProvider.dart';
5-
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
5+
import 'package:universal_ble/universal_ble.dart';
6+
import '../../globals/globals.dart';
67
import 'base_ble_state.dart';
78

89
class ScanState extends NormalBleState {
910
final DataTransferManager manager;
1011
final BadgeScanMode mode;
1112
final List<String> allowedNames;
1213

14+
final String targetServiceUuid = serviceUuid;
15+
1316
ScanState({
1417
required this.manager,
1518
required this.mode,
@@ -19,54 +22,51 @@ class ScanState extends NormalBleState {
1922
@override
2023
Future<BleState?> processState() async {
2124
manager.clearConnectedDevice();
22-
await FlutterBluePlus.stopScan();
25+
await UniversalBle.stopScan();
2326

2427
toast.showToast("Searching for device...");
2528
Completer<BleState?> nextStateCompleter = Completer();
26-
StreamSubscription<List<ScanResult>>? subscription;
29+
StreamSubscription<BleDevice>? subscription;
30+
Timer? timeoutTimer;
2731

2832
bool isCompleted = false;
2933
try {
30-
subscription = FlutterBluePlus.scanResults.listen(
31-
(results) async {
32-
if (isCompleted || results.isEmpty) return;
34+
subscription = UniversalBle.scanStream.listen(
35+
(device) async {
36+
if (isCompleted) return;
3337

3438
try {
3539
final normalizedAllowedNames = allowedNames
3640
.map((e) => e.trim().toLowerCase())
3741
.where((e) => e.isNotEmpty)
3842
.toList();
3943

40-
final foundDevice = results.firstWhere(
41-
(result) {
42-
final matchesUuid = result.advertisementData.serviceUuids
43-
.contains(Guid("0000fee0-0000-1000-8000-00805f9b34fb"));
44-
45-
final deviceName = result.device.name.trim().toLowerCase();
46-
final matchesName = mode == BadgeScanMode.any ||
47-
normalizedAllowedNames.contains(deviceName);
44+
final matchesUuid = device.services.contains(targetServiceUuid);
4845

49-
return matchesUuid && matchesName;
50-
},
51-
orElse: () => throw Exception("Matching device not found."),
52-
);
46+
final deviceName = (device.name ?? "").trim().toLowerCase();
47+
final matchesName = mode == BadgeScanMode.any ||
48+
normalizedAllowedNames.contains(deviceName);
5349

54-
isCompleted = true;
55-
FlutterBluePlus.stopScan();
56-
toast.showToast('Device found. Connecting...');
50+
if (matchesUuid && matchesName) {
51+
isCompleted = true;
52+
timeoutTimer?.cancel();
53+
await UniversalBle.stopScan();
54+
toast.showToast('Device found. Connecting...');
5755

58-
nextStateCompleter.complete(ConnectState(
59-
scanResult: foundDevice,
60-
manager: manager,
61-
));
56+
nextStateCompleter.complete(ConnectState(
57+
scanResult: device,
58+
manager: manager,
59+
));
60+
}
6261
} catch (e) {
63-
logger.w("No matching device found in this batch: $e");
62+
logger.w("Device discovered but filtered out: $e");
6463
}
6564
},
6665
onError: (e) {
6766
if (!isCompleted) {
6867
isCompleted = true;
69-
FlutterBluePlus.stopScan();
68+
timeoutTimer?.cancel();
69+
UniversalBle.stopScan();
7070
logger.e("Scan error: $e");
7171
toast.showErrorToast('Scan error occurred.');
7272
nextStateCompleter.completeError(
@@ -75,29 +75,30 @@ class ScanState extends NormalBleState {
7575
}
7676
},
7777
);
78-
await FlutterBluePlus.startScan(
79-
withServices: [Guid("0000fee0-0000-1000-8000-00805f9b34fb")],
80-
removeIfGone: Duration(seconds: 5),
81-
continuousUpdates: true,
82-
timeout: const Duration(seconds: 15), // Reduced scan timeout.
83-
);
8478

85-
await Future.delayed(const Duration(seconds: 1));
79+
await UniversalBle.startScan(
80+
scanFilter: ScanFilter(
81+
withServices: [targetServiceUuid],
82+
),
83+
);
8684

87-
if (!isCompleted) {
88-
isCompleted = true;
89-
FlutterBluePlus.stopScan();
90-
toast.showErrorToast('Device not found.');
91-
nextStateCompleter.completeError(Exception('Device not found.'));
92-
}
85+
timeoutTimer = Timer(const Duration(seconds: 15), () async {
86+
if (!isCompleted) {
87+
isCompleted = true;
88+
await UniversalBle.stopScan();
89+
toast.showErrorToast('Device not found.');
90+
nextStateCompleter.completeError(Exception('Device not found.'));
91+
}
92+
});
9393

9494
return await nextStateCompleter.future;
9595
} catch (e) {
96+
timeoutTimer?.cancel();
9697
logger.e("Exception during scanning: $e");
9798
throw Exception("Please check if the device is turned on and retry.");
9899
} finally {
99100
await subscription?.cancel();
100-
await FlutterBluePlus.stopScan();
101+
await UniversalBle.stopScan();
101102
}
102103
}
103104
}

lib/bademagic_module/bluetooth/write_state.dart

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import 'dart:typed_data';
12
import 'package:badgemagic/bademagic_module/bluetooth/datagenerator.dart';
2-
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
3+
import 'package:universal_ble/universal_ble.dart';
4+
import '../../globals/globals.dart';
35
import 'base_ble_state.dart';
46
import 'completed_state.dart';
57

68
class WriteState extends NormalBleState {
7-
final BluetoothDevice device;
9+
final BleDevice device;
810
final DataTransferManager manager;
911

1012
WriteState({required this.manager, required this.device});
@@ -14,46 +16,46 @@ class WriteState extends NormalBleState {
1416
List<List<int>> dataChunks = await manager.generateDataChunk();
1517
logger.d("Data to write: $dataChunks");
1618

19+
final deviceId = device.deviceId;
20+
1721
try {
18-
List<BluetoothService> services = await device.discoverServices();
19-
for (BluetoothService service in services) {
20-
for (BluetoothCharacteristic characteristic
21-
in service.characteristics) {
22-
if (characteristic.uuid ==
23-
Guid("0000fee1-0000-1000-8000-00805f9b34fb") &&
24-
characteristic.properties.write) {
25-
for (List<int> chunk in dataChunks) {
26-
bool success = false;
27-
for (int attempt = 1; attempt <= 3; attempt++) {
28-
try {
29-
await characteristic.write(chunk, withoutResponse: false);
30-
logger.d("Chunk written successfully: $chunk");
31-
success = true;
32-
break;
33-
} catch (e) {
34-
logger.e("Write failed (attempt $attempt/3): $e");
35-
}
36-
}
37-
if (!success) {
38-
throw Exception("Failed to transfer data. Please try again.");
39-
}
40-
await Future.delayed(Duration(milliseconds: 50));
41-
}
22+
await Future.delayed(const Duration(milliseconds: 300));
23+
await UniversalBle.discoverServices(deviceId);
24+
25+
for (List<int> chunk in dataChunks) {
26+
bool success = false;
4227

43-
logger.d("Characteristic written successfully");
44-
return CompletedState(
45-
isSuccess: true, message: "Data transferred successfully");
28+
for (int attempt = 1; attempt <= 3; attempt++) {
29+
try {
30+
await UniversalBle.write(deviceId, serviceUuid, characteristicUuid,
31+
Uint8List.fromList(chunk),
32+
withoutResponse: false);
33+
34+
logger.d("Chunk written successfully: $chunk");
35+
success = true;
36+
break;
37+
} catch (e) {
38+
logger.e("Write failed (attempt $attempt/3): $e");
4639
}
4740
}
41+
42+
if (!success) {
43+
throw Exception("Failed to transfer data. Please try again.");
44+
}
45+
46+
await Future.delayed(const Duration(milliseconds: 120));
4847
}
49-
throw Exception("Please use the correct Badge");
48+
49+
logger.d("Characteristic written successfully");
50+
return CompletedState(
51+
isSuccess: true, message: "Data transferred successfully");
5052
} catch (e) {
5153
logger.e("Failed to write characteristic: $e");
5254
throw Exception("Failed to transfer data. Please try again.");
5355
} finally {
5456
try {
5557
logger.d("Disconnecting from device after write attempt...");
56-
await device.disconnect();
58+
await UniversalBle.disconnect(deviceId);
5759
await Future.delayed(const Duration(milliseconds: 700));
5860
logger.d("Device disconnected and delay complete.");
5961
} catch (e) {

lib/globals/globals.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
import 'package:flutter/material.dart';
22

33
final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
4+
5+
final String serviceUuid = "0000fee0-0000-1000-8000-00805f9b34fb";
6+
final String characteristicUuid = "0000fee1-0000-1000-8000-00805f9b34fb";

0 commit comments

Comments
 (0)