core_bluetooth 3.0.0
core_bluetooth: ^3.0.0 copied to clipboard
CoreBluetooth wrapper for Dart on iOS and macOS.
core_bluetooth #
A clean Dart wrapper around Apple's CoreBluetooth framework for iOS and macOS.
Both central mode and peripheral mode are supported.
Status #
✅ Fully implemented.
Apple CoreBluetooth symbol names are the primary public API. Helper wrapper
types exist where Dart needs them, but they intentionally do not use the CB
prefix.
Example #
The package includes a working example app:
cd ./example
flutter run
Usage - Central Mode #
For connecting to devices.
Initialize Central Manager #
Use the shared app-wide manager:
final central = CBCentralManager.shared;
Or configure the shared manager before first use:
CBCentralManager.ensureShared(
options: const CentralManagerOptions(
showPowerAlert: true,
restoreIdentifier: "my_restore_id",
),
);
final central = CBCentralManager.shared;
Or create an isolated manager:
final central = CBCentralManager.createIsolated(
options: const CentralManagerOptions(
showPowerAlert: true,
restoreIdentifier: "my_restore_id",
),
);
Bluetooth On & Off #
final central = CBCentralManager.shared;
final subscription = central.onDidUpdateState.listen((state) {
print(state);
if (state == CBManagerState.poweredOn) {
// start scanning, connecting, etc
}
});
Scan for Devices #
final subscription = central.onDidDiscoverPeripheral.listen((result) {
print(result.peripheral.identifier.uuidString);
print(result.advertisementData.localName);
print(result.rssi);
});
await central.scanForPeripherals(
withServices: [CBUUID("180D")],
options: const CentralManagerScanOptions(
allowDuplicates: true,
),
);
await central.stopScan();
Connect to a Device #
await central.connect(peripheral);
await central.cancelPeripheralConnection(peripheral);
Discover Services #
final subscription = peripheral.onDidDiscoverServices.listen((result) {
print(result.services);
});
await peripheral.discoverServices();
Iterate Services & Characteristics #
for (final service in peripheral.services ?? const <CBService>[]) {
print('service: ${service.uuid.uuidString}');
for (final characteristic in service.characteristics ?? const <CBCharacteristic>[]) {
print(' characteristic: ${characteristic.uuid.uuidString}');
for (final descriptor in characteristic.descriptors ?? const <CBDescriptor>[]) {
print(' descriptor: ${descriptor.uuid.uuidString}');
}
}
}
Read, Write, and Notify #
await peripheral.readValue(characteristic);
await peripheral.writeValue(
Uint8List.fromList([0x01, 0x02]),
forAttribute: characteristic,
type: CBCharacteristicWriteType.withResponse,
);
await peripheral.setNotifyValue(true, characteristic);
final subscription = peripheral.onDidUpdateValueForCharacteristic.listen((result) {
print(result.characteristic.value);
});
L2CAP #
final subscription = peripheral.onDidOpenL2CAPChannel.listen((result) async {
final channel = result.channel;
if (channel == null) {
return;
}
final incoming = await channel.inputStream.read();
print(incoming);
await channel.outputStream.write(Uint8List.fromList([0x01, 0x02]));
});
await peripheral.openL2CAPChannel(1234);
Usage - Peripheral Mode #
For advertising yourself as a device.
Initialize Peripheral Manager #
Use the shared app-wide manager:
final peripheralManager = CBPeripheralManager.shared;
Or configure the shared manager before first use:
CBPeripheralManager.ensureShared(
options: const PeripheralManagerOptions(
showPowerAlert: true,
restoreIdentifier: "my_restore_id",
),
);
final peripheralManager = CBPeripheralManager.shared;
Or create an isolated manager:
final peripheralManager = CBPeripheralManager.createIsolated(
options: const PeripheralManagerOptions(
showPowerAlert: true,
restoreIdentifier: "my_restore_id",
),
);
Start Advertising #
final peripheralManager = CBPeripheralManager.shared;
final service = CBMutableService.type(
type: CBUUID("1234"),
primary: true,
);
final characteristic = CBMutableCharacteristic.type(
type: CBUUID("ABCD"),
properties: const CBCharacteristicProperties(
CBCharacteristicProperties.read.rawValue | CBCharacteristicProperties.notify.rawValue,
),
value: null,
permissions: CBAttributePermissions.readable,
);
service.characteristics = [characteristic];
await peripheralManager.add(service);
await peripheralManager.startAdvertising(
const PeripheralManagerAdvertisingData(
localName: "core_bluetooth",
serviceUUIDs: [CBUUID("1234")],
),
);
Peripheral Read / Write Requests #
final readSubscription = peripheralManager.onDidReceiveReadRequest.listen((result) async {
final request = result.request;
request.value = Uint8List.fromList([0x01, 0x02, 0x03]);
await peripheralManager.respond(
to: request,
withResult: CBATTErrorCode.success,
);
});
final writeSubscription = peripheralManager.onDidReceiveWriteRequests.listen((result) async {
for (final request in result.requests) {
print(request.value);
}
});
Update Subscribed Centrals #
final didSend = await peripheralManager.updateValue(
Uint8List.fromList([0x10, 0x20]),
forCharacteristic: characteristic,
onSubscribedCentrals: null,
);
print(didSend);
Peripheral L2CAP #
final publishSubscription = peripheralManager.onDidPublishL2CAPChannel.listen((result) {
print(result.psm);
});
final openSubscription = peripheralManager.onDidOpenL2CAPChannel.listen((result) async {
final channel = result.channel;
if (channel == null) {
return;
}
await channel.outputStream.write(Uint8List.fromList([0x10, 0x20]));
});
await peripheralManager.publishL2CAPChannel(withEncryption: false);
Contract #
Below is the CoreBluetooth API map we should treat as the contract.
Legend:
✅fully implemented
Top-Level Framework #
✅CBManager✅CBPeer✅CBUUID✅CBError✅CBATTRequest✅CBL2CAPChannel✅CBL2CAPPSM
Central Role #
✅CBCentralManager✅CBCentralManagerDelegate✅CBCentral✅CBConnectionEvent
CBCentralManager #
✅shared✅ensureShared(options: ...)✅createIsolated()✅var delegate✅var isScanning✅class func supports(_ feature: CBCentralManager.Feature) -> Bool✅struct CBCentralManager.Feature✅func scanForPeripherals(withServices:options:)✅func stopScan()✅func retrievePeripherals(withIdentifiers:)✅func retrieveConnectedPeripherals(withServices:)✅func connect(_:options:)✅func cancelPeripheralConnection(_:)✅func registerForConnectionEvents(options:)✅class var authorization✅inherited fromCBManager:var state
CBCentralManagerDelegate #
✅centralManagerDidUpdateState(_:)✅centralManager(_:willRestoreState:)✅centralManager(_:didDiscover:advertisementData:rssi:)✅centralManager(_:didConnect:)✅centralManager(_:didFailToConnect:error:)✅centralManager(_:didDisconnectPeripheral:error:)✅centralManager(_:didDisconnectPeripheral:timestamp:isReconnecting:error:)✅centralManager(_:connectionEventDidOccur:for:)✅centralManager(_:didUpdateANCSAuthorizationFor:)
CBCentral #
✅inheritsCBPeer✅var maximumUpdateValueLength
Peripheral Role #
✅CBPeripheral✅CBPeripheralDelegate✅CBPeripheralManager✅CBPeripheralManagerDelegate
CBPeripheral #
✅var delegate✅var name✅var identifier✅var state✅var services✅var ancsAuthorized✅var canSendWriteWithoutResponse✅func readRSSI()✅func discoverServices(_:)✅func discoverIncludedServices(_:for:)✅func discoverCharacteristics(_:for:)✅func discoverDescriptors(for:)✅func readValue(for: CBCharacteristic)✅func readValue(for: CBDescriptor)✅func writeValue(_:for: CBCharacteristic,type:)✅func writeValue(_:for: CBDescriptor)✅func setNotifyValue(_:for:)✅func maximumWriteValueLength(for:)✅func openL2CAPChannel(_:)
CBPeripheralDelegate #
✅peripheralDidUpdateName(_:)✅peripheral(_:didModifyServices:)✅peripheral(_:didReadRSSI:error:)✅peripheral(_:didDiscoverServices:)✅peripheral(_:didDiscoverIncludedServicesFor:error:)✅peripheral(_:didDiscoverCharacteristicsFor:error:)✅peripheral(_:didDiscoverDescriptorsFor:error:)✅peripheral(_:didUpdateValueFor:error:)forCBCharacteristic✅peripheral(_:didUpdateValueFor:error:)forCBDescriptor✅peripheral(_:didWriteValueFor:error:)forCBCharacteristic✅peripheral(_:didWriteValueFor:error:)forCBDescriptor✅peripheral(_:didUpdateNotificationStateFor:error:)✅peripheralIsReady(toSendWriteWithoutResponse:)✅peripheral(_:didOpen:error:)
CBPeripheralManager #
✅shared✅ensureShared(options: ...)✅createIsolated()✅init()✅init(delegate:queue:)✅init(delegate:queue:options:)✅var delegate✅var isAdvertising✅func add(_:)✅func remove(_:)✅func removeAllServices()✅func startAdvertising(_:)✅func stopAdvertising()✅func updateValue(_:for:onSubscribedCentrals:) -> Bool✅func respond(to:withResult:)✅func setDesiredConnectionLatency(_:for:)✅func publishL2CAPChannel(withEncryption:)✅func unpublishL2CAPChannel(_:)✅class var authorization✅inherited fromCBManager:var state
CBPeripheralManagerDelegate #
✅peripheralManagerDidUpdateState(_:)✅peripheralManager(_:willRestoreState:)✅peripheralManagerDidStartAdvertising(_:error:)✅peripheralManager(_:didAdd:error:)✅peripheralManager(_:central:didSubscribeTo:)✅peripheralManager(_:central:didUnsubscribeFrom:)✅peripheralManagerIsReady(toUpdateSubscribers:)✅peripheralManager(_:didReceiveRead:)✅peripheralManager(_:didReceiveWrite:)✅peripheralManager(_:didPublishL2CAPChannel:error:)✅peripheralManager(_:didUnpublishL2CAPChannel:error:)✅peripheralManager(_:didOpen:error:)
GATT Model Types #
✅CBAttribute✅CBService✅CBMutableService✅CBCharacteristic✅CBMutableCharacteristic✅CBDescriptor✅CBMutableDescriptor
CBAttribute #
✅var uuid
CBService #
✅var peripheral✅var isPrimary✅var includedServices✅var characteristics
CBMutableService #
✅writable versions of service properties
CBCharacteristic #
✅var service✅var value✅var descriptors✅var properties✅var isNotifying✅var isBroadcasted
CBMutableCharacteristic #
✅init(type:properties:value:permissions:)
CBDescriptor #
✅var characteristic✅var value
CBMutableDescriptor #
✅init(type:value:)
Supporting Enums / Option Sets / Constants #
✅CBManagerState✅CBManagerAuthorization✅CBPeripheralState✅CBCharacteristicWriteType✅CBCentralManager.Feature✅CBCharacteristicProperties✅CBAttributePermissions✅CBPeripheralManagerConnectionLatency✅CBATTError.Code✅advertisement-data keys✅peripheral-scanning option keys✅peripheral-connection option keys✅peripheral-manager initialization option keys✅peripheral-manager restoration keys✅central-manager restoration keys✅connection-event matching option keys