Skip to content

dotintent/flutter-android-cdm

Repository files navigation

flutter_android_cdm

Flutter wrapper around Android's CompanionDeviceManager (CDM).

Show the system-rendered accessory picker, let the user choose a nearby Bluetooth Low Energy peripheral filtered by Service UUID and/or name pattern, and receive the associated device back in Dart — without writing your own scan UI or asking the user for BLUETOOTH_SCAN/ACCESS_FINE_LOCATION runtime permissions.

CDM is available from Android 8.0 (API 26).

This plugin is the Android counterpart to flutter_accessorysetup (which wraps iOS AccessorySetupKit). Use both side-by-side for cross-platform OS-managed pairing.

Install

dependencies:
  flutter_android_cdm: ^0.0.2

Usage

import 'package:flutter_android_cdm/flutter_android_cdm.dart';

final cdm = FlutterAndroidCdm.instance;

try {
  final device = await cdm.associate(const CdmRequest(
    serviceUuid: '0000abcd-0000-1000-8000-00805f9b34fb', // your 128-bit UUID
    namePattern: r'^MyDevice-',
    singleDevice: false,
  ));
  print('Paired: ${device.name} @ ${device.address}');
} on CdmException catch (e) {
  if (e.code == CdmErrorCode.cancelled) {
    // User dismissed the picker.
  } else {
    rethrow;
  }
}

Device profiles & dialog options

CdmRequest exposes the optional parts of AssociationRequest.Builder:

await cdm.associate(const CdmRequest(
  namePattern: r'^MyWatch-',
  deviceProfile: CdmDeviceProfile.watch, // changes dialog copy + capabilities
  displayName: 'My Watch',               // name the system shows (API 33+)
  forceConfirmation: true,               // always show the chooser (API 33+)
));
  • deviceProfile maps to AssociationRequest.DEVICE_PROFILE_*. A profile is not cosmetic: it changes the system dialog copy, grants that profile's capabilities, and may require the app to hold a specific permission. Profiles are version-gated (watch API 31; computer/appStreaming/ automotiveProjection API 33; glasses/nearbyDeviceStreaming API 34). Requesting one the device can't support throws CdmErrorCode.unsupported.
  • displayName / forceConfirmation require API 33+ and are silently ignored on older devices.

Limitations

You cannot put a custom icon (or a Flutter asset) into the standard CDM picker — the dialog is rendered by the system. Its icon, layout, and body copy are not app-customizable; the body text is a function of deviceProfile only. A custom icon is possible only in the self-managed confirmation dialog (setDeviceIcon, API 36.1+), which forfeits CDM's system scanning and is not exposed by this plugin.

App-side setup

This plugin contributes <uses-feature android:name="android.software.companion_device_setup"/> via manifest merger, but the host app still needs the Bluetooth permissions it intends to use after pairing — the picker itself doesn't require runtime permissions, but reading the device name and connecting do.

android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

android/app/build.gradle.kts:

defaultConfig {
    minSdk = 26
}

API

  • FlutterAndroidCdm.associate(CdmRequest)Future<AssociatedDevice>
  • FlutterAndroidCdm.getAssociations()Future<List<AssociatedDevice>>
  • FlutterAndroidCdm.disassociate(AssociatedDevice)Future<void>
  • CdmRequest({serviceUuid, namePattern, singleDevice, deviceProfile, displayName, forceConfirmation})
  • CdmDeviceProfilewatch, computer, appStreaming, automotiveProjection, glasses, nearbyDeviceStreaming
  • AssociatedDevice(address, name, id)
  • CdmException(code, message) with codes: cancelled, busy, unsupported, cdm_failure, launch_failed, no_device, no_activity, bad_args, engine_detached, activity_detached, disassociate_failed, get_associations_failed

Example

See example/ for a runnable demo.

Pre-fill the picker filters via build args so you don't have to type them on every run. Copy example/cdm-config.example.json to example/cdm-config.json (gitignored — keep your own device identifiers out of the repo), edit, then:

flutter run --dart-define-from-file=cdm-config.json

Both keys are optional; their absence leaves the corresponding TextField empty, and the UI still lets the user type values at runtime.

License

BSD 3-Clause — see LICENSE.

About

Flutter wrapper around Android's CompanionDeviceManager (CDM). Lets a Flutter app discover and pair with nearby Bluetooth Low Energy peripherals through the system-rendered accessory picker — filtered by Service UUID, name pattern, or raw advertising data — without rolling your own scan UI

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors