meta_wearables_dat_flutter 1.0.0
meta_wearables_dat_flutter: ^1.0.0 copied to clipboard
Unofficial Flutter plugin for Meta Wearables Device Access Toolkit (DAT) 1.0: camera, photos, display and device state on Ray-Ban Meta, Oakley Meta and Ray-Ban Display glasses.
Changelog #
All notable changes to this project will be documented in this file.
The format is loosely based on Keep a Changelog, and this project adheres to Semantic Versioning.
1.0.0 #
Moves the plugin to Meta Wearables Device Access Toolkit (DAT) 1.0.0,
Meta's first stable, supported release. This is a breaking release; follow
doc/migration_0.7_to_1.0.md.
Highlights #
- Native pins:
facebook/meta-wearables-dat-iosexact: "1.0.0"andcom.meta.wearable:mwdat-*:1.0.0from Maven Central. - Android SDK artifacts now come from Maven Central. No GitHub Packages
repository and no
GITHUB_TOKENare needed. - Typed, sealed error model with per-category reason enums and a suggested
DatRecoveryAction. hvc1texture preview on Android (MediaCodec), matching iOS.- Live device state: battery, charging, wear, hinge, thermal level, link state and compatibility.
- Registrations started from the Meta AI app (
registrationRequestStream). dumpDiagnostics()returns a typedDatDiagnosticsthat validates Info.plist / AndroidManifest configuration and reports held native resources.- Bridges DAT 1.0's experimental modules (Inputs, Motion, Speech, voice
invocations, high-resolution photo, in-stream audio) behind
@experimental.
Breaking changes #
- Toolchain: Flutter >= 3.44.0, Dart ^3.8.0, Xcode 26.4+, iOS 17.2 minimum. iOS resolves only through Swift Package Manager; CocoaPods is no longer supported for this plugin.
- Android: remove the GitHub Packages Maven repository and token configuration from your Gradle files.
- Developer Center: create a new app version for builds made with DAT 1.0.
DatErroris now a sealed hierarchy.DatError.codeis the specific case name (for examplehingesClosed); the former meaning ofcode(for exampleSTREAM_ERROR) moved to the newDatError.category.detailsis now aMap<String, Object?>.SessionErroris replaced byStreamError(categorySTREAM_ERRORinstead ofSESSION_ERROR).SessionErrorremains as a typedef.dumpDiagnostics()returnsDatDiagnosticsinstead ofMap<String, Object?>(the map is still available asraw).deviceSessionErrorStream()emitsDeviceSessionErrorinstead ofObject.sendDisplayView()returnsFuture<List<String>>(substitution warnings) instead ofFuture<void>.stopStreamSession()andcapturePhoto()no longer takedeviceUUID; they act on the running stream.setMockCameraFeed()andsetMockCapturedImage()take a non-nullfilePath.setMockPermission()/setMockPermissionRequestResult()takePermission/PermissionStatus(MockPermissionandMockPermissionStatusare deprecated typedefs of those).- State enums (
RegistrationState,StreamSessionState,DeviceSessionState,DisplayState) no longer carry an integervalue; state travels as a string on the platform channels. UsefromWire. - Default stream frame rate is 24 fps (was 30), matching the SDK default.
MWDAT > DAMEnabled(iOS) andcom.meta.wearable.mwdat.DAM_ENABLED(Android) are obsolete and ignored.
Added #
StreamSessionConfig(quality,frameRateasStreamFrameRate,videoCodec,deviceKinds, experimentalaudio) forstartStreamSession(config: ...).streamErrorStream(),cameraStateStream()(CameraState).RegistrationState.unregistering,registrationErrorStream(),registrationRequestStream()withRegistrationRequest.continueRegistration()/cancel().requestPermission(Permission)andcheckPermissionStatus(Permission)returningPermissionStatus;Permission.microphone.openFirmwareUpdate()andopenDatGlassesAppUpdate().getDevice(uuid),getSessionDevice(),deviceStateStream(uuid).DeviceInfogainstype(DeviceType),linkState,compatibility,batteryLevel,chargingState,donState,hingeState,thermalLevel,supportsDisplayandisMock.DeviceKind.metaGlasses.VideoFrame.pixelFormat(i420,nv12,bgra),planes(VideoFramePlane) andisCodecConfig.PhotoResult.formatreports the actual encoding.- Display:
clearDisplay(),stopDisplayVideo(),displayErrorStream(),displayWarningStream(),DisplayButtonGroup,DisplayImage.bytes,DisplayButton.actionRole,DisplayIcon.style,DisplayEdgeInsets(FlexBox.paddingInsets),flexGrow/flexShrink/alignSelfon every node,DisplayNode.validate(), and the fullDisplayIconNameset (116 icons). - Errors:
RegistrationRequestError,NavigationError,StreamError,PhotoError,DisplayError,InputsError,MotionError,SpeechError,VoiceInvocationError,MockDeviceKitError,DatArgumentError,DatPluginError;DatError.platformCase,DatError.platform,DatError.recoveryAction(DatRecoveryAction);DeviceSessionError.isTerminal/isWarning;StreamError.isFatal/isWarning. DatDiagnostics:findings(DatFindingwithid,severity,message,fix),errors,resources,isIdle,experimentalModulesLinked.- Mock Device Kit:
pairMockGlasses(MockGlassesModel)returningDeviceInfo,mockTap,mockTapAndHold,setMockBatteryLevel,setMockChargingState,setMockThermalLevel,startMockTestServer,stopMockTestServer,sendMockDisplayClick. - Diagnostics findings for Info.plist (iOS) and AndroidManifest (Android).
- Tests and tooling: 125 Dart unit tests, Kotlin and Swift unit tests, Mock Device Kit integration tests, channel-parity, version-agreement and coverage-gate tools, and release / nightly CI workflows.
Changed #
- Plugin version tracks Meta's DAT version (major.minor); the patch number is
the plugin's own.
@experimentalAPIs are excluded from semantic versioning. - One device session per device is shared by camera, display and the experimental capabilities; it stays open while any of them runs.
- iOS: the stream session ends when the app enters the background unless
enableBackgroundStreaming()was called. Raw frames pause in the background;hvc1continues. videoStreamSizeStream()emits only when the size changes.- State streams replay the last value to new listeners.
Deprecated #
startStreamSession(fps:, quality:, videoCodec:, deviceKinds:): useconfig: StreamSessionConfig(...).streamSessionErrorStream(): usestreamErrorStream().requestCameraPermission()/getCameraPermissionStatus(): userequestPermission(Permission.camera)/checkPermissionStatus(Permission.camera).pairMockRayBanMeta(): usepairMockGlasses(MockGlassesModel.rayBanMeta).MockPermission/MockPermissionStatus: usePermission/PermissionStatus.- The
is*getters on error classes (for exampleisHingesClosed): switch onreasoninstead. They now work; in 0.7 they always returnedfalse. fromInton state enums: usefromWire.DatErrorCodes.sessionandDatErrorCodes.missingFragmentActivity.FlexBox.cornerRadius: not supported by the DAT Display SDK; ignored.
Removed #
pauseStreamSession()andresumeStreamSession()(they never did anything).sessionStateStream(),sessionErrorStream()and theSessionStatetypedef.startRegistration(appId:, urlScheme:)parameters; values come from Info.plist / AndroidManifest.- Sub-code constants in
DatErrorCodes(for exampleDatErrorCodes.hingesClosed); use the*ErrorCaseenums. Category constants remain.
Fixed #
- iOS: display icons never resolved.
DisplayIconalways rendered the checkmark andDisplayButton.iconNamewas dropped, because the SDK'sIconNameraw values are snake_case while the Dart names are camelCase (#1). All 116 icons are now mapped and covered by native tests. - Typed
is*getters on errors never matched. - Android:
handleUrl()never called the SDK. - Android:
capturePhoto(format:)was ignored. - Android:
enableMockDevice()ignoredinitiallyRegisteredandinitialPermissionsGranted; mock permission setters were no-ops. - Android: no
hvc1preview. - Android:
STARTEDwas mapped tostreaming; default quality washighwhile Dart defaulted tomedium. - Android: the display was torn down on the initial
STOPPEDstate. - iOS:
video_stream_sizewas emitted on every frame. - iOS:
stopDisplaySession()leaked its state listener token. - iOS:
getRegistrationState()returned a raw ordinal. - Display DSL:
space*alignments collapsed and thelargecorner radius mapped tomedium. dumpDiagnostics()returned a different shape on each platform.deviceStateStream()could hangfirst/firstWhereon cancel.
Experimental #
All APIs below are @experimental. Apps that use them can be tested in
Developer Mode and Beta release channels but cannot ship to production
release channels. They are also exported by
package:meta_wearables_dat_flutter/experimental.dart.
- Inputs:
startInputs(configuration:),stopInputs,inputEventsStream(NavInputEvent,SelectInputEvent,BackInputEvent,ButtonInputEvent,CaptureInputEvent,DragInputEvent),inputsStateStream,inputsErrorStream. - Motion:
startMotion(samplingRate:),stopMotion,motionSamplesStream,motionStateStream,motionErrorStream. - Speech:
startSpeech,stopSpeech,transcriptionStream,speechStateStream,speechErrorStream. - Voice invocations:
startVoiceInvocations,stopVoiceInvocations,voiceInvocationsStream(respond exactly once),voiceInvocationsStateStream,voiceInvocationErrorStream. - High-resolution photo:
captureHighResPhoto(resolution:, quality:),photoTransferProgressStream,photoStateStream,photoErrorStream. - In-stream audio:
StreamSessionConfig(audio: AudioStreamConfig(...)),audioFramesStream. - Mock simulators for inputs, speech, motion, voice invocations and high-resolution photo capture.
- Android: set
mwdat.experimental=falsein the app'sandroid/gradle.propertiesto leave out themwdat-inputs,mwdat-motionandmwdat-speechAARs; experimental calls then throwDatPluginErrorwith categoryEXPERIMENTAL_NOT_LINKED.
Migration #
0.7.1 #
Documentation-only release: README and getting-started guides now show
^0.7.0 (the install snippet in the 0.7.0 pub publish still said
^0.2.0 because docs were updated on GitHub after that upload).
0.7.0 #
Aligns the plugin version with Meta's native DAT SDKs and adds Display Access.
Display Access (new) #
- Bridge Meta DAT 0.7.0's
MWDATDisplay(iOS) /mwdat-display(Android) module: render a declarative UI tree on Ray-Ban Display glasses. - New Dart API:
startDisplaySession({deviceUUID?}),sendDisplayView(DisplayView),stopDisplaySession(), anddisplayStateStream()(DisplayState:starting/started/stopping/stopped). - Component DSL with
toJsonserialization and callback ids:FlexBox,DisplayText,DisplayImage,DisplayButton,DisplayIcon,VideoPlayer, plus layout / style enums andonTap/onClick/onPlaybackEventcallbacks dispatched over a newdisplay_eventsEventChannel. Display lifecycle is reported on a newdisplay_stateEventChannel. - Native
MetaDisplayManageron both platforms rebuilds the SDK DSL from JSON and routes interaction + playback callbacks back to Dart by id. - New sample app
samples/display_access/porting Meta's official "Car Maintenance" Display sample (list → detail → steps → video). - New
doc/display_access.md,display-accessskill, and Cursor rule entries.
SDK bump #
- Update native pins
0.6.0 → 0.7.0(iOS SPMmeta-wearables-dat-iosMWDATDisplay; Android Mavenmwdat-*+mwdat-display).
- Adapt the camera bridge to 0.7 renames (
Stream/StreamConfiguration/StreamState) with resilient, string-based state/error encoding. - Add
DeviceSessionError.datAppOnTheGlassesUpdateRequired(error.isDatAppUpdateRequired).
0.2.0 #
Android live preview — correct colours #
- Rewrite
YuvToArgbto mirror the official Meta DAT Android sample'sYuvToBitmapConverter: tightly-packed I420 only (no layout sniffing) with BT.709 limited-range coefficients. The previous BT.601 matrix produced a green/purple cast on real glasses frames; BT.709 matches the codec's advertisedraw.color.matrix = 1. - Cache the YUV byte buffer and ARGB int buffer across frames in
YuvToArgbso the hot path is allocation-free. Eliminates ~150 MiB/s of GC pressure on a 720p stream and stops mid-stream frame stalls. - Fix the "wrong-colour / squashed preview" bug: call
SurfaceTexture.setDefaultBufferSize(width, height)the first time we see a frame (or whenever the resolution changes). Without this the canvas returned bySurface.lockHardwareCanvas()was sized to Flutter's default 1×1 producer buffer and the scale-fit was clamping the bitmap into a tiny destination — the preview looked like a flat one-colour image even when YUV decode was correct.
Android session reliability #
- Replace
AutoDeviceSelectorwithSpecificDeviceSelectordriven by the paired device UUID (matches the iOS path). ResolvesSESSION_ERROR: No eligible device foundon the first start when Meta AI has just released the device. - Add an in-process retry loop around
Wearables.createSession(6 attempts × 1.5 s) for the transient warm-up failures the underlying SDK throws while the glasses transition fromAVAILABLEtoELIGIBLE_FOR_DAT. - Make
AndroidManifest.xmlDeveloper-Mode-ready in both bundled apps:APPLICATION_ID = "0",CLIENT_TOKEN = "0",ANALYTICS_OPT_OUT = "true",DAM_ENABLED = "true".
Sample app #
samples/camera_accessnow fetches the paired device UUID before callingstartStreamSessionand shows a "Connecting…" spinner on the Start button while the retry loop is in flight, so the user knows the first tap is doing something. The button is disabled during the warm-up window.
Diagnostics #
- Add per-frame Y / chroma min/mean/max diagnostics to
logcatfor the first 10 frames of a stream and a 1 Hz heartbeat afterwards. Flat Y → SDK is streaming placeholders (glasses not worn). Flat chroma + varying Y → real monochrome scene. Catches root-cause questions before a screen-recording round-trip with users.
Docs #
- README,
doc/getting_started.mdanddoc/troubleshooting.mdnow document every Android Developer-Mode meta-data key (including the newly-requiredDAM_ENABLED), call out that Developer Mode in the Meta AI app itself must be turned on as a one-time per-phone step, and add a dedicated troubleshooting entry for "No eligible device found".
0.1.5 #
- Fix Android
CLIENT_TOKENin both bundled apps (example/andsamples/camera_access/) — was an empty string"", which causes the SDK to throwTOKEN_NOT_CONFIGUREDand silently refuse to register even when Developer Mode is on. Set to"developer-mode-placeholder"(the SDK doesn't validate the value whenAPPLICATION_ID = "0"). - Add
ANALYTICS_OPT_OUT = trueto both Android manifests so failed analytics uploads to Meta's servers don't surface as misleading "Internal error" toasts during developer testing. - Same fixes applied to the README and
doc/getting_started.mdsnippets.
0.1.4 #
- Bump the recommended Android NDK to 28.2.13676358 in the README
and both bundled samples (
example/,samples/camera_access/). Newer Flutter plugin transitive deps (notablyjni, pulled byshare_plus) require r28.2; AGP enforces "use highest" so any consumer that pulls one of those breaks against r27. Meta'smwdat-coreAAR (built against r27) is fine on r28.2 — NDK is backward compatible.
0.1.3 #
- Inline the full iOS and Android setup walkthrough in the README so
the complete setup (deployment target,
MWDATdict with Developer ModeMetaAppID = "0",CFBundleURLTypes,LSApplicationQueriesSchemes,UIBackgroundModes, Bonjour, external-accessory protocol,SceneDelegate.swift,AndroidManifest.xmlmeta-data + deep-link intent-filter, GitHub Packages Maven repo) is visible directly on pub.dev — no click-through todoc/getting_started.mdrequired. - Add a dedicated "Enable Developer Mode in the Meta AI app" section
at the top of the setup so the two-sided contract (Meta AI toggle
↔
MetaAppID = "0") cannot be missed.
0.1.2 #
- Refresh README title and introduction to match the SDK's full name ("Meta Wearables Device Access Toolkit for Flutter") and improve first-impression clarity on pub.dev and GitHub.
0.1.1 #
Documentation, deprecation, and discoverability fixes only — no runtime behaviour changes vs. 0.1.0.
Deprecated #
MetaWearablesDat.startRegistration({appId, urlScheme})— both named parameters are now annotated@Deprecated. They have always been ignored on iOS (Wearables.shared.startRegistration()readsMetaAppID/AppLinkURLSchemefromInfo.plist.MWDAT) and on Android (Wearables.startRegistration(activity)reads the same values from<meta-data>and the activity's<intent-filter>). Call sites should drop the arguments. The parameters will be removed in v0.2.0.
Documentation #
- Fix the
Info.plistAppLinkURLSchemesnippet indoc/getting_started.mdand.claude/skills/getting-started.mdto end with://. Meta AI builds the registration callback URL by literally concatenating this value with the query string, so without the://separator the callback becomes a malformed URL that iOS silently drops. The example app anddoc/troubleshooting.mdwere already correct; the getting-started doc was the outdated one. Added a dedicated troubleshooting bullet so the symptom ("Allow → app reopens but nothing happens") is searchable. - Document the required iOS
SceneDelegate.swiftwiring for scene-based Flutter apps (Flutter ≥ 3.32). Without it, Meta AI's registration callback URL is silently dropped on iOS and the SDK never advances pastregistering. Added a dedicated section todoc/registration_flow.md, a setup step todoc/getting_started.md, a fresh troubleshooting entry, and a quick-reference note inREADME.md. Verified againstexample/ios/Runner/SceneDelegate.swift. - README and skill snippets no longer pass the vestigial
appId/urlSchemearguments tostartRegistration().
Other #
- Add
flutter-pluginto the pubspec topic list for improved discoverability on pub.dev.
0.1.0 #
Initial developer-preview release. Full feature and structural parity with Meta's official iOS / Android DAT 0.6 SDKs.
Added #
- Unified
MetaWearablesDatDart facade for Meta's iOS and Android DAT SDKs. requestAndroidPermissions()— runtime Bluetooth/Internet grant on Android, no-op on iOS.- Registration flow:
startRegistration,handleUrl,startUnregistration,getRegistrationState,registrationStateStream,activeDeviceStream. requestCameraPermission()/checkCameraPermissionStatus().- Device enumeration & compatibility:
devicesStream(),getDevices(),compatibilityStream(). NewDeviceCompatibilityenum (compatible,deviceUpdateRequired,sdkUpdateRequired,unknown). - Streaming:
startStreamSession,stopStreamSession,pauseStreamSession,resumeStreamSession,streamSessionStateStream,streamSessionErrorStream,videoStreamSizeStream. Frames are delivered zero-copy via Flutter's texture registry (CVPixelBuffer on iOS, SurfaceTexture on Android). NewdeviceKindsparameter for device-kind filtering. - Device-session lifecycle:
deviceSessionStateStream(),deviceSessionErrorStream(). NewDeviceSessionStateenum (idle,starting,started,paused,stopping,stopped). - Per-frame video stream:
videoFramesStream()emittingVideoFrameevents with raw BGRA (iOS) / I420 (Android) payloads. Subscriber-gated so the per-frame copy is free when no Dart listener is attached. - HEVC (
hvc1) codec:videoCodec: VideoCodecparameter onstartStreamSession. iOS routes compressedCMSampleBuffers through aVTDecompressionPipeline; Android setscompressVideo = true. - Background streaming:
enableBackgroundStreaming/disableBackgroundStreamingwithBackgroundNotificationmodel. iOS activatesAVAudioSessionand software HEVC decoding; Android starts a foreground service with wake lock. capturePhoto({format})— mid-stream high-res JPEG / HEIC capture.- Typed errors:
DatErrorhierarchy withRegistrationError,UnregistrationError,HandleUrlError,DeviceSessionError,SessionError,CaptureError— each withis*convenience getters so callers can switch on errors without string-matching codes. - Mock Device Kit:
enableMockDevice,disableMockDevice,isMockDeviceEnabled,pairMockRaybanMeta,pairedMockDevices,mockPowerOn,mockPowerOff,mockDon,mockDoff,mockFold,mockUnfold,setMockCameraFeed,setMockCapturedImage,setMockPermission,setMockPermissionRequestResult,mockDevicesStream. samples/camera_access/— polished Flutter clone of Meta's official iOS and Android Camera Access samples (settings sheet, photo capture, devices screen, video recording).- Long-form documentation in
doc/(getting started, registration, streaming, frame processing, mock device, troubleshooting). - AI-assisted development config:
AGENTS.md,.claude/skills/,.cursor/rules/,.github/copilot-instructions.md,install-skills.sh.
Notes #
- Audio (microphone capture, speaker playback) is intentionally out of scope
for
0.1.x— it is handled via standard Bluetooth Hands-Free Profile, not Meta's DAT SDK. SessionStateandsessionStateStream()/sessionErrorStream()are deprecated aliases forStreamSessionStateandstreamSessionStateStream()/streamSessionErrorStream(); they will be removed in v0.2.0.