Asleep SDK for Flutter

Experimental Android and iOS Flutter plugin for Asleep audio-based sleep tracking.

EXPERIMENTAL — NOT RECOMMENDED FOR PRODUCTION USE

This 0.x package is an early preview. APIs, behavior, platform requirements, and native SDK compatibility may change without notice. Deployment requires Asleep credentials and application review. App Store privacy readiness and unattended overnight tracking have not been certified for this release.

Contract snapshot

Layer Experimental 0.1.0 baseline
React Native developer journey react-native-asleep 1.2.0
Android native artifact ai.asleep:asleepsdk:3.2.1
iOS native artifact AsleepSDK 3.2.0
Flutter toolchain Flutter 3.44.8 / Dart 3.12.2
Minimum platforms Android API 24 / iOS 15
State surface Immutable snapshot + Future commands + broadcast Stream
Transport Pigeon 27.3.0, internal generated API

Developer journey

Create one client for the application owner that controls sleep tracking:

final asleep = AsleepClient();

final stateSubscription = asleep.states.listen((snapshot) {
  print(snapshot.trackingStatus);
});

final eventSubscription = asleep.events.listen((event) {
  switch (event) {
    case AnalysisResultEvent(:final result):
      print(result.id);
    case TrackingFailedEvent(:final error):
      print('${error.code}: ${error.numericCode}');
    default:
      break;
  }
});

Restore, initialize or reconnect, and check platform prerequisites:

final restore = await asleep.checkAndRestoreTracking();
if (restore.hasActiveSession) {
  await asleep.configure(
    const AsleepConfiguration(apiKey: 'provided-at-runtime'),
  );
} else {
  await asleep.initialize(
    const AsleepSetupOptions(
      apiKey: 'provided-at-runtime',
      enableOnDeviceAnalysis: true,
    ),
  );
}

final battery = await asleep.checkBatteryOptimization();

if (!battery.exempted) {
  // Call from an app-controlled user interaction, then recheck after return.
  await asleep.requestBatteryOptimizationExemption();
  return;
}

if (!await asleep.hasRequiredPermissions()) {
  // Call from an app-controlled user interaction.
  final granted = await asleep.requestRequiredPermissions();
  if (!granted) {
    // Render the app's permission-denied UI.
  }
}

Start and stop tracking:

await asleep.startTracking(
  const AsleepTrackingOptions(
    androidNotification: AndroidNotificationOptions(
      title: 'Sleep tracking',
      text: 'Monitoring sleep',
    ),
    iosAudioSessionOptions: <IosAudioSessionOption>[
      IosAudioSessionOption.allowBluetoothA2DP,
    ],
  ),
);

await asleep.stopTracking();

Dispose subscriptions and the client from the same application owner:

await stateSubscription.cancel();
await eventSubscription.cancel();
await asleep.dispose();

The state stream is broadcast and does not replay the current value. Read asleep.state before subscribing when an immediate snapshot is required.

The native Asleep SDK is process-global. Exactly one Flutter engine may own it at a time. The first engine that initializes or configures the SDK owns it until that engine detaches; another engine receives an immediate native failure.

Permissions and platform configuration

The plugin checks and requests permissions separately. startTracking() never opens a permission dialog.

Android applications must declare:

  • android.permission.INTERNET
  • android.permission.RECORD_AUDIO
  • android.permission.FOREGROUND_SERVICE
  • android.permission.FOREGROUND_SERVICE_MICROPHONE

The plugin also declares android.permission.POST_NOTIFICATIONS for foreground-notification visibility on Android 13 and later. Notification denial does not mean microphone permission was denied and does not change the return value of requestRequiredPermissions().

Direct battery-optimization exemption is opt-in. The plugin does not inject android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS into consuming apps. Without that declaration, requestBatteryOptimizationExemption() opens the general battery-optimization settings. A consuming app may declare the direct exemption permission only after confirming that its core function qualifies under Google Play policy.

iOS applications must add NSMicrophoneUsageDescription and the audio background mode. The Asleep iOS SDK configures and activates AVAudioSession.playAndRecord for tracking and intentionally does not restore the previous audio-session state. Applications must coordinate other audio features accordingly. iOS process restoration is not supported; the iOS restore result is always false.

The native Android artifact contains ARM device libraries only. Use an ARM64 device for tracking and on-device analysis; Intel Android emulators are not a supported runtime target.

The native AsleepSDK 3.2.0 release is currently distributed through CocoaPods, so this plugin cannot yet provide a complete Swift Package Manager dependency chain.

The pinned AsleepSDK 3.2.0 CocoaPods artifact does not contain a privacy manifest. This does not prevent pub.dev publication or the existing native SDK from running, but applications remain responsible for App Store privacy compliance. A future native SDK release should bundle its required-reason API declarations directly.

State and event semantics

AsleepSnapshot contains durable projected state. AsleepEvent contains one-time native facts. Do not use the state stream as an event queue.

TrackingStatus.paused and TrackingStatus.recoveryRequired still describe a live native session. On iOS, call resumeTracking() after the app returns to the foreground when recovery is required. A successful subsequent upload is the proof that recovery completed. Android probes and reconnects to the native foreground service before native setup can replace the process context.

An AsleepErrorCategory.recordingDead failure means recording stopped while the native session remains open. Call stopTracking() before starting another session even though the projected tracking status is idle.

requestAnalysis() reflects a native platform difference:

  • Android can return an immediate result after native retry processing.
  • iOS returns an acknowledgement; the result arrives on events as AnalysisResultEvent.

The canonical cross-platform result path is the event stream.

getReportList() follows the native offset/limit APIs until the final partial page, rather than silently truncating the range at 100 sessions.

Error handling

Public commands fail with AsleepException for lifecycle, validation, and native failures. Native command failures preserve the platform SDK code and details. Tracking and setup delegate failures are also projected as typed events and AsleepSnapshot.error.

Use semantic AsleepError.code before numeric fallback. The same numeric value can have different historical meanings across platforms. On iOS, numericCode is produced by the SDK's error.errorCode.code accessor, never a Swift enum ordinal.

Development

Regenerate the transport after editing pigeons/asleep_messages.dart:

flutter pub run pigeon --input pigeons/asleep_messages.dart
dart format lib/src/transport.g.dart

Run the Dart gates:

dart format --output=none --set-exit-if-changed lib test example/lib \
  example/test example/integration_test pigeons
flutter analyze
flutter test
(cd example && flutter test)

Native builds require network access to the public Asleep Android Maven and iOS CocoaPods artifacts.

CI and delivery

GitHub Actions runs Dart contracts, Android bridge tests and an example APK build, plus CocoaPods validation and an iOS simulator build. Pushes to main and manual runs retain the non-production example artifacts for seven days.

Release tags must be signed, point to a commit contained in main, match the package and changelog versions, and pass Dart, Android, iOS, archive, API, and package-score validation. The first pub.dev release is published manually and transferred to the verified asleep.ai publisher. OIDC publishing and GitHub Release creation stay disabled until their repository variables are explicitly approved.

License

This package uses Asleep's proprietary SDK license. Use, modification, and redistribution require authorization from Asleep.

Libraries

asleep_sdk_flutter
Flutter access to the Asleep sleep-tracking SDK.