morphui 0.1.3
morphui: ^0.1.3 copied to clipboard
Automatic UI adaptation for Flutter. Dark mode, accessibility, and behavioral layout adaptation — zero configuration.
Changelog #
0.1.3 #
Theme palette — persistent local cache #
GeneratedTheme.toJson()— new serialization method mirroringfromJson. Enables Hive persistence of AI-generated palettes.- Hive-backed palette cache in
MorphProvider— successful/flutter/theme/generateresponses are persisted tocml_config(same box as the license cache). On subsequent app launches the adapted palette is applied immediately with zero network latency. TTL: 48 h. Cache key includes the raw background hex + target brightness — changing your base colors auto-invalidates.
Fatigue detection — new APIs #
FatigueAdaptiveForm.onReset— optional callback invoked when the user taps the "Start over" button on the fatigue banner. The SDK resets its own score first, then calls this so host apps can clear form state.context.morphFatigueDetector— newBuildContextextension exposing the liveFatigueDetectordirectly. Use it to feedrecordKeystroke()/recordTapError()from anywhere without threading the detector manually.FatigueDetector.debugForceHighFatigue()— dev/test helper that bypasses the cold-start guard and immediately forces a high fatigue score so the banner and simplified form are reachable in demos without a real session.- Banner UI refresh: icon added, "Start over" label,
surfaceContainerHighestbackground, rounded corners.
Interruption recovery — new APIs #
morphSetCheckoutMultiStepContext— new extension method for multi-step checkouts (shipping → payment → review). Writes a fresh snapshot per step under the sameworkflowId; the engine fetches the full chain on resume viaInterruptionRecovery.pendingChain. Acceptsstrategyandttloverrides.morphSetProductContext— now accepts optionalstrategy(RecoveryStrategy.confirm/.auto/.silent) andttlparameters.
Suggestion overlay — improvements #
MorphSuggestionOverlay.onSuggestionRefused— new callback fired when the user dismisses a card. Receives theMorphSuggestionobject so host apps can react per suggestion type (e.g. reset checkout state when a recovery card is refused).- Post-resume check — the overlay now listens to
AppLifecycleStateand fires a card check 1 500 ms after the app resumes, so interruption-recovery cards surface immediately without waiting for the next scheduled tick. - Keyboard guard — uses
MediaQuery.viewInsets.bottominstead ofFocusManager.hasFocus; fixes a silent block on Android wherehasFocusstayedtrueafter returning from background even after the IME was dismissed. - Bug fix:
rootNavigator: truein suggestion navigation — resolves a routing dead-end on iOS when the suggestion context originates from the overlay (which sits above the router's navigator in the tree). - Removed secondary confirm dialog on zone-reorder suggestions — the card tap is already confirmation; the
showDialogpath caused a non-resolvingFutureon iOS.
Dev mode config (MorphConfig) #
devMinPauseSeconds— overrides the 30-second minimum background pause required for recovery to trigger. Set to5in demo builds; omit (null) in production.devEnableAllFeatures— treats all plan capability checks as Business tier, unlocking fatigue detection, grip detection, battery-aware UI, etc. regardless of the actual license key. Never settruein production.
0.1.2 #
Plan gating — never leak Morph branding to end users (behavioural change) #
PlanGateno longer auto-renders a Morph-branded upsell card when the resolved plan is insufficient. The default fallback is nowSizedBox.shrink()— gated features silently disappear instead. End users of integrating apps no longer see "Upgrade to Morph Pro" prompts when YOUR Morph subscription lapses; YOU receive renewal emails instead.- Internal
_UpgradeCardis now exposed as the publicMorphUpsellCardwidget. Pass it explicitly viaPlanGate.fallbackonly on YOUR admin / dev surfaces — never on end-user screens. context.requireMorphPro()/context.requireMorphAgency()no longer auto-show a dialog. The new contract isonAllowed(sufficient plan) andonDenied(insufficient — wire to your own UI, or toshowDialog(builder: (_) => MorphUpgradeDialog(...))on YOUR admin surface).- The
onUpgradeparameter onrequireMorphPro/Agencyis replaced byonDenied. Existing call sites still compile —onUpgradewas never required — but devs that relied on the auto-dialog need to passonDenied: () => showDialog(...)explicitly.
Interruption recovery — production-ready #
- Pause bucketing. Pauses are now classified into
quick(30s–2min),real(2min–10min) andabandonment(>10min) buckets viaInterruptionBucket. The recovery card adapts its title, tone, and confidence per bucket — abandonment recoveries surface with more cautious copy. - Robust persistence. The active context is now written to Hive on every
declareContextcall (debounced 1s) instead of only onpaused. An app crash between context declaration and lifecycle pause no longer loses the snapshot. - Recovery strategies. Each snapshot can declare a
RecoveryStrategy.auto(silent restore, no card — for safe values),RecoveryStrategy.confirm(default, card before restore — for stakes-sensitive flows), orRecoveryStrategy.silent(analytics-only). - Multi-step workflow chains. Snapshots can declare a
workflowId+workflowStep+workflowTotalSteps. On resume the engine fetches the entire chain and exposes it viaInterruptionRecovery.pendingChain, so a host app can restore "step 1 → step 4" of a KYC instead of just the last step. - Per-context TTL.
RecoverySnapshotnow has attlfield; expired snapshots are silently dropped on resume. Sensible defaults baked in viakRecoveryDefaultTtl— checkout 30 min, transfer 2 min, KYC 24 h, generic 1 h. Devs can override per call. - Local learning. New
recordOutcome(bucket, RecoveryOutcome.accepted | rejected | ignored)API tracks per-bucket acceptance. After 5 samples in a bucket, the engine suppresses suggestions there if the rejection rate exceeds 70% — the SDK respects users who consistently dismiss recoveries. Stays on-device, never transmitted.
GPS context — production-ready #
- Hysteresis on the speed→mode transitions. Walking→cycling kicks in at 7 km/h, but cycling→walking only at 5 km/h. A user steady at 7 km/h no longer flickers the UI between the two modes on every fix.
- New tunnel-mode behaviour. Updates with
accuracy > 50mno longer immediately drop the user back tounknown— the adapter keeps emitting the last good context for 30 seconds. Going through a tunnel or under a bridge holds the previous mode instead of popping the UI back to a default. - Accelerometer-aware fallback.
GpsContextAdapter.start()now subscribes to the accelerometer and detects sustained train-like vibration. When GPS reportsstationarybut the accelerometer shows ≥5 seconds of high-variance motion, the emitted context is upgraded tovehicle— useful in metros and trains where GPS loses lock for minutes at a time. - The accuracy-gate (drop fixes with >50m error) was already shipped in earlier versions but is now explicitly documented in the README.
Fatigue detection — production-ready #
- New continuous score stream —
FatigueDetector.scoreStreamemits a 0..100 value so UIs can interpolate smoothly instead of snapping between three buckets. The legacy bucketedstreamis kept for backward compatibility. - New
FatigueAdaptiveForm(adaptation: FatigueAdaptation.smooth)(default) — field scale interpolates continuously from 1.00 to 1.30, banner fades in past score 40. PassFatigueAdaptation.steppedto keep the old three-step behaviour. - Per-user baseline. After 3 completed sessions the detector grades the user against their own typical accuracy + typing speed instead of universal thresholds. A 8% miss rate is "fatigued" for a 2%-baseline user, "normal" for an 8%-baseline user — universal thresholds got both wrong.
- Auto-reset after 5 minutes paused. The next resume rolls the buffers so a user who put the phone down and came back doesn't carry the previous session's fatigue.
- Cold-start guard. The first 30 seconds of the day's first session are excluded from scoring — stiff fingers in the morning aren't fatigue.
- Post-resume retry suppression. For 30 seconds after returning from background, error counters don't accumulate — coming back after a notification doesn't penalise the user.
- New typed error API:
recordTapError,recordTypingError,recordNavigationError. Each carries its own weight in the score (5%, 5%, 15%). The legacyrecordRetryis now@Deprecatedbut still works. FatigueDetector.startSession()andresetFatigue()are nowFuture<void>(they reload the persisted baseline). Existingvoid-returning call sites should wrap withunawaited(...)— handled internally forMorphProvider/FatigueAdaptiveForm.
Battery — production-ready #
BatteryAdapternow records every foreground session (start/end level, duration, was-charging flag). Aggregated stats are exposed viaBatteryAdapter.getSessionStats(lookback: ...)so devs can credibly answer "how much battery does my app burn per minute?".- Charge-start events are timestamped per session and fed to a new
ChargePatternPredictor. When the user is approaching their typical charge window (3+ events at the same hour over the last 7 days, day-of-week-weighted) and the device isn't already plugged in, the adapter pre-shifts toBatteryMode.mediumto extend the runway. - New
BatteryAwareTheme(adaptiveMode: BatteryAdaptiveMode.suggestion)mode — instead of imposing the dimmed scaffold the moment battery drops, the theme stays untouched until the user accepts the "Battery saver mode" suggestion. The default remainsimposedfor backward compatibility. - New
BatteryAwareTheme(isOLED: bool?)override + automatic detection viaDeviceCapabilities.isLikelyOLED. Pure-black scaffolds only apply when the screen is actually OLED — LCD devices fall back to dim grey, since pure black saves no power on LCD. - Charging-aware behaviour (level rule short-circuits to
normalwhen plugged in) is now explicitly documented in the README — was already shipped, just invisible.
Grip detection — persistence + customisation #
GripDetectornow reads the previously detected hand fromBehaviorDBonstart()and uses it as a prior. The UI lands on the right alignment immediately on the second session, and a matching live signal locks in after 5 samples instead of 10. A signal that contradicts the prior still goes through the full 10-sample window — switching hands mid-session works as before.GripDetectorconstructor now takes an optionalpersistPreferenceflag (defaulttrue). Set it tofalsefor ephemeral detection without storage.GripDetector.start()is nowFuture<void>so it canawaitthe prior read. Existingunawaited(start())callers keep working.GripAdaptiveLayoutnow exposestransitionDuration(default 300ms) andtransitionCurve(defaulteaseOutCubic) so devs can tune the feel of the side-switch animation.
Tooling + dependencies #
- Added a minimal
example/so pana scores the package as having a runnable demo. - Bumped
battery_plus(4.x → 7.x),sensors_plus(4.x → 7.x),package_info_plus(>=4 <10 → ^10) to the latest majors. - Fixed dartdoc HTML interpretation warning on
Map<zoneId, orderIndex>.
0.1.1 #
- Renamed package from
morph_fluttertomorphuito align withmorphui.dev. - License switched to Apache 2.0.
- Internal API base URL is now resolved at compile time — clients no longer pass any URL; SDK maintainers can override via
--dart-define=CHAMELEON_API_BASE_URL=....
0.1.0 #
Initial public release.
Added #
MorphProvider— root widget that bootstraps theme detection, behavior storage, and zone scoring.MorphZone/ReorderableColumn— wrappers that track interactions and reorder children based on usage.- AI-adapted color schemes via the Morph SaaS (
/api/flutter/theme/generate) — automatic dark/light/high-contrast palettes generated from the host app's baseThemeData. - Behavior storage backed by Hive — visibility, taps, sequences, zoom events.
BuildContextextensions:context.morph,context.morphTheme,context.morphPalette,context.zoneOrder.- Origin-binding via
appIdpayload — backend rejects calls from packages not declared in the license'sallowed_packages. MorphAnalyticsConfig— opt-in behavior reporting with configurable upload interval and minimum-interaction floor.MorphFeatures.fintech()preset — interruption recovery, battery-aware UI, fatigue detection, GPS context (gated by license tier).