Asleep SDK for Flutter
Experimental Android and iOS Flutter plugin for Asleep audio-based sleep tracking.
EXPERIMENTAL — NOT RECOMMENDED FOR PRODUCTION USE
This
0.xpackage 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.INTERNETandroid.permission.RECORD_AUDIOandroid.permission.FOREGROUND_SERVICEandroid.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
eventsasAnalysisResultEvent.
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.