bluetooth_low_energy
bluetooth_low_energy copied to clipboard
Cannot access value of empty optional (Windows)
Hey, I am developing on windows, and notice a crash when a BLE device disconnects unexpectedly.
How to reproduce:
- Connect to BLE device.
- Perform hardware reset on BLE device / turn off BLE device.
[ERROR:flutter/shell/common/shell.cc(1038)] The 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onConnectionStateChanged' channel sent a message from native to Flutter on a non-platform thread. Platform channel messages must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must be fixed in the plugin or application code creating that channel.
See https://docs.flutter.dev/platform-integration/platform-channels#channels-and-platform-threading for more information.
flutter: connectionStateChangedSubscription
Lost connection to device.
Exited.
my code:
class BledSign {
late final ValueNotifier<BluetoothLowEnergyState> state;
late final ValueNotifier<bool> discovering;
late final ValueNotifier<List<DiscoveredEventArgs>> discoveredEventArgs;
late final StreamSubscription stateChangedSubscription;
late final StreamSubscription discoveredSubscription;
late final ValueNotifier<Peripheral?> connectedDevice;
late final ValueNotifier<String> connectionDeviceName;
late final ValueNotifier<bool> connectionState;
late final DiscoveredEventArgs eventArgs;
late final ValueNotifier<List<GattService>> services;
late final ValueNotifier<List<GattCharacteristic>> characteristics;
late final ValueNotifier<GattService?> service;
late final ValueNotifier<GattCharacteristic?> characteristic;
late final ValueNotifier<GattCharacteristicWriteType> writeType;
late final TextEditingController writeController;
late final StreamSubscription connectionStateChangedSubscription;
late final StreamSubscription characteristicNotifiedSubscription;
BledSign._private() {
state = ValueNotifier(BluetoothLowEnergyState.unknown);
discovering = ValueNotifier(false);
discoveredEventArgs = ValueNotifier([]);
connectedDevice = ValueNotifier(null);
connectionDeviceName = ValueNotifier("");
connectionState = ValueNotifier(false);
services = ValueNotifier([]);
characteristics = ValueNotifier([]);
service = ValueNotifier(null);
characteristic = ValueNotifier(null);
writeType = ValueNotifier(GattCharacteristicWriteType.withResponse);
writeController = TextEditingController();
stateChangedSubscription = CentralManager.instance.stateChanged.listen(
(eventArgs) {
print("stateChangedSubscription");
state.value = eventArgs.state;
print('Change in stateChangedSubscription: $state.value');
},
onError: (error) {
// Handle errors
print('Error in stateChangedSubscription: $error');
},
onDone: () {
// Handle stream closure
print('stateChangedSubscription stream closed');
},
);
discoveredSubscription = CentralManager.instance.discovered.listen(
(eventArgs) {
print("discoveredSubscription");
final items = discoveredEventArgs.value;
bool isNewEntry = true;
int indexToUpdate = -1;
// Check if the device is already in the list
for (int i = 0; i < items.length; i++) {
if (items[i].peripheral == eventArgs.peripheral) {
isNewEntry = false;
indexToUpdate = i;
break;
}
}
if (isNewEntry) {
// Add a new entry if the device is not in the list
List<DiscoveredEventArgs> updatedItems = List.from(items);
updatedItems.add(eventArgs);
discoveredEventArgs.value = updatedItems;
print("UPDATE");
} else {
// Update the existing entry if the device is already in the list
List<DiscoveredEventArgs> updatedItems = List.from(items);
updatedItems[indexToUpdate] = eventArgs;
discoveredEventArgs.value = updatedItems;
}
},
);
connectionStateChangedSubscription = CentralManager.instance.connectionStateChanged.listen(
(eventArgs) async {
print("connectionStateChangedSubscription");
services.value = await CentralManager.instance.discoverGATT(eventArgs.peripheral);
final connectionState = eventArgs.connectionState;
this.connectionState.value = connectionState;
if (!connectionState) {
connectionDeviceName.value = "";
services.value = [];
characteristics.value = [];
service.value = null;
characteristic.value = null;
connectedDevice.value = null;
print("connectedDevice null");
} else {
discoveredEventArgs.value.forEach((device) {
if (device.peripheral.uuid == eventArgs.peripheral.uuid) {
connectionDeviceName.value = device.advertisement.name!;
}
});
}
},
);
characteristicNotifiedSubscription = CentralManager.instance.characteristicNotified.listen(
(eventArgs) {},
);
print("BLED SIGN INSTANCE CREATED");
}
// Static private instance variable
static BledSign? _instance;
// Static method to access the instance
static BledSign get instance {
// Initialize instance if null
_instance ??= BledSign._private();
return _instance!;
}
init() async {
hierarchicalLoggingEnabled = true;
CentralManager.instance.logLevel = Level.ALL;
await CentralManager.instance.setUp();
state.value = await CentralManager.instance.getState();
if (kDebugMode) {
print('BLED SIGN INSTANCE Initialized ${state.value}');
}
}
Future<void> startDiscovery() async {
print("START DISCOVERY");
//discoveredEventArgs.value = [];
await CentralManager.instance.stopDiscovery();
await CentralManager.instance.startDiscovery();
discovering.value = true;
Future.delayed(Duration(seconds: 5), stopDiscovery);
}
Future<void> stopDiscovery() async {
print("STOP DISCOVERY");
await CentralManager.instance.stopDiscovery();
discovering.value = false;
}
Future<bool> connect(Peripheral peripheral) async {
print("Connect to: ${peripheral.uuid}");
try {
await CentralManager.instance.connect(peripheral);
connectedDevice.value = peripheral;
return true;
} on Exception catch (_err) {
print('Connect failed: $_err');
return false;
}
}
Future<bool> disconnect(Peripheral? peripheral) async {
connectedDevice.value = null;
if (peripheral != null) {
print("Disconnect");
try {
await CentralManager.instance.disconnect(peripheral);
return true;
} on Exception catch (_err) {
print('Disconnect failed: $_err');
return false;
}
}
return false;
}
cleanup() {
stateChangedSubscription.cancel();
discoveredSubscription.cancel();
state.dispose();
discovering.dispose();
discoveredEventArgs.dispose();
connectionStateChangedSubscription.cancel();
characteristicNotifiedSubscription.cancel();
connectionState.dispose();
services.dispose();
characteristics.dispose();
service.dispose();
characteristic.dispose();
writeType.dispose();
writeController.dispose();
}
}