morphui library

Morph for Flutter — automatic UI adaptation.

import 'package:morphui/morphui.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MorphProvider(
    licenseKey: 'cha-pro-xxx',
    child: MyApp(),
  ));
}

Classes

AnalyticsReporter
Pushes anonymized aggregates to /api/flutter/behavior/report on the dev's config.uploadInterval. Runs only when config.canUpload is true — otherwise stays dormant.
BatteryAdapter
Read-only battery telemetry. Polls the OS for the level, listens to charging-state changes, emits a coarse BatteryMode every time the bucket flips. Morph never controls power — only reacts to it.
BatteryAwareTheme
Reactively swaps in a battery-adapted ThemeData. Animates the change with AnimatedTheme so the swap is gentle.
BatteryAwareWidget
Picks one of four widget variants depending on the active BatteryMode. Lighter variants are used at lower levels — typically fewer animated regions and skeletons instead of skeletons + heroes.
BatterySessionSnapshot
Telemetry for a single foreground battery session.
BatterySessionStats
Aggregated stats over a window of BatterySessionSnapshots, returned by BatteryAdapter.getSessionStats. All fields are derived — no raw storage. Safe to expose in dev dashboards.
BehaviorDB
Hive-backed behavioral store — the Flutter equivalent of BehaviorDB.js (which uses IndexedDB on the web). Six boxes: clicks, time, sequences, zooms, sessions, scroll. Auto-cleanup of entries older than retentionDays (capped at 30) runs at init() and then every 24h.
ChargePatternPredictor
Lightweight clustering of past charge-start events to predict the user's next typical charge window. No ML — a histogram over the 24 hours of the day with day-of-week weighting, smoothed across a recency-decayed window. Goal: reduce false positives, not chase state-of-the-art.
DeviceCapabilities
Best-effort hardware capability detection.
FatigueAdaptiveForm
Form scaffold whose field set, scale, and banner adapt to the detected fatigue.
FatigueBaseline
Per-user baseline derived from the first few completed sessions.
FatigueBaselineLimits
Constants — split out so FatigueBaseline.isLocked can reference them without a circular import on FatigueDetector.
FatigueDetector
Estimates user cognitive fatigue from aggregate behavioral signals. Operates on PATTERNS — never on content. The score is a 0..1 blend of weighted signals:
GeneratedTheme
Full Material3 ColorScheme returned by /api/flutter/theme/generate. All slots are stored as hex strings (#RRGGBB) to stay JSON-serializable; use toColorScheme to convert to Flutter's ColorScheme.
GpsAdaptiveScaffold
Scaffold replacement that scales typography and reinforces contrast when the user is moving. Drop in instead of Scaffold inside any fintech screen that should follow the user from desk to commute.
GpsContextAdapter
Receives location updates from the host app's existing GPS pipeline and exposes a coarse MovementContext stream. Morph never requests location permission itself — the dev pipes their own Position updates in via onLocationUpdate.
GripAdaptiveLayout
Wraps a screen and floats primaryAction over it on the side that matches the user's detected grip — left edge for left-handed grip, right for right, centered when both/unknown.
GripDetector
Reads the accelerometer to infer left/right grip and an approximate posture (portrait, landscape, moving). Subscribers (typically GripAdaptiveLayout) react to handStream to reposition CTAs on the dominant side.
InterruptionRecovery
Detects significant app interruptions and produces a recovery suggestion to surface on resume. Sits on top of the existing suggestion system — the engine pulls a pending recovery via consumeSuggestion before running its other checks.
Morph
Static entry points for Morph that don't need a BuildContext.
MorphAdaptedColors
Semantic color palette produced by Morph after adapting the app's base colors to the current OS settings (dark mode, high contrast, etc.).
MorphAnalyticsConfig
Dev-facing configuration for Morph analytics. Pass this to MorphProvider to opt into anonymized usage reporting. Privacy by default: when this object is omitted, no data ever leaves the device.
MorphColors
What a dev declares when they have a dedicated AppColors file or any other palette they maintain by hand. Everything is optional — missing slots are filled in from the ambient ThemeData by ColorExtractor.
MorphConfig
Configuration passed to MorphProvider. All fields optional.
MorphFeatures
Opt-in flag bag passed to MorphProvider. Each commercial feature is dormant unless the dev explicitly enables it — only interruptionRecovery is on by default because it has zero runtime cost and requires no permissions.
MorphInheritedWidget
InheritedWidget that lets any descendant read Morph state. Reach it with MorphInheritedWidget.of(context) or use the context.morph extension.
MorphNavigatorObserver
Drop-in NavigatorObserver that feeds Morph's behavior store with every route transition. Plug it into any router that accepts a NavigatorObserver:
MorphNavItem
A single item fed to MorphReorderableNav.
MorphPaletteStops
A 12-stop color scale (s25 → s950) that can flip to its mirror in dark mode. Designed to let apps with a tiered palette (like the Untitled UI scale used here) keep semantic access to every stop while letting Morph follow system brightness.
MorphPlanFeatures
What a given MorphPlan is allowed to do. The runtime gate is always wants AND allows: • wants = the dev's opt-in via MorphFeatures on the provider • allows = an instance of this class derived from the resolved plan
MorphProvider
Root widget — wraps the app and coordinates storage, scoring, and theme detection. Use exactly once, typically in main.dart.
MorphReorderableColumn
A Column of MorphZones that rearranges itself based on the scorer's latest decisions. Children keep their widget state across reorders because each is built with a stable Key(zoneId).
MorphReorderableNav
Bottom navigation that reorders its items based on the scorer. The app keeps passing the ORIGINAL index via currentIndex / onTap; this widget handles the mapping to the reordered set transparently, so the rest of your routing logic doesn't need to know a reorder happened.
MorphState
The piece of state exposed to descendants via MorphInheritedWidget. Everything in here is derived from the system or from the scorer — never from user app state.
MorphSuggestion
A single, fully-formed suggestion ready to render in the MorphSuggestionCard. Built by SuggestionEngine — devs don't instantiate these directly.
MorphSuggestionCard
Themed card that surfaces a single MorphSuggestion at the bottom of the app. EVERY color, radius and text style is derived from the host's Theme / ColorScheme — Morph never injects its own branding. The card looks like the dev's app.
MorphSuggestionOverlay
Place this inside your MaterialApp.builder so the suggestion card has access to MaterialApp's MediaQuery, Directionality and Material theming. It listens for scrolls / text input / keyboard above its subtree and only surfaces a card when the user is genuinely idle.
MorphSuggestionScope
Internal inherited carrier so MorphProvider can publish the engine and history store down the tree without forcing every dev to pass them through their MaterialApp manually.
MorphSystemSettings
Raw system-level accessibility + appearance state read from MediaQuery. This is the snapshot the ThemeAdapter consumes to produce an adapted ThemeData — see MorphTheme for the higher-level wrapper that also carries the AI-generated palette and locale.
MorphTheme
Snapshot of the system-detected theme + accessibility state. Immutable — rebuilt by ThemeAdapter.detect on every platform change.
MorphThemeExtension
Carries "extra" semantic colors that Material's built-in ColorScheme doesn't name — success, warning, text tiers, KYC-style tier accents.
MorphUpgradeDialog
Morph-branded upgrade dialog — shown ONLY when YOU (the SDK customer) explicitly call it via showDialog. Never auto-rendered by the SDK. Use it on YOUR admin screens, billing-recovery flows, or wherever an end-user-facing prompt is already part of YOUR own product narrative.
MorphUpsellCard
Morph-branded upsell card — visually pitches the customer to upgrade THEIR Morph subscription. Never auto-rendered by the SDK. Pass it explicitly via PlanGate.fallback when YOU (the SDK customer) want it shown — typically on YOUR own admin / settings / dev-tools surface, NOT on end-user screens.
MorphZone
Wraps a section of the UI so Morph can track clicks + time-spent and reorder it based on learned behavior.
PlanGate
Wraps a feature so it only renders when the resolved plan satisfies requiredPlan. Otherwise renders fallback, or — when fallback is null — nothing visible (a SizedBox.shrink()).
RecoverySnapshot
Snapshot of where the user was when the app was paused. Built by the host app via the morphSet…Context extensions and restored by InterruptionRecovery when the user comes back from a pause that crosses the configured threshold.
SuggestionEngine
Reads BehaviorDB + the navigator observer to produce contextual suggestions. The engine NEVER triggers UI changes by itself — every returned MorphSuggestion carries an _action closure that only runs after the user taps the action button in the card.
SuggestionHistory
Per-suggestion outcome record. One row per suggestion id in the cml_suggestions Hive box.
SuggestionHistoryStore
Hive-backed store enforcing the cooldown rules from the spec: • accepted → never show again • 3+ refusals → never show again • last refused → wait 7 days • last ignored → wait 3 days
TapEvent
A single tap event. Public so consumers can feed taps from custom gesture detectors (in addition to the auto-instrumentation).
ThemeAdapter
Reads the current system theme + a11y preferences and, when a licenseKey is present, fetches a WCAG-verified AI-generated palette from the backend.
ThemeGenerator
Orchestrates the "generate the opposite of what the dev gave us" flow.
ZoneReorder
Applies a reorder map to the visible widget tree via ChangeNotifier. Widgets listen through MorphInheritedWidget — this class itself is only the canonical source of truth.
ZoneScorer
Computes per-zone scores from BehaviorDB stats and decides whether a reorder is warranted. Mirrors the weighting used by the web SDK's ScorerEngine.js.

Enums

BatteryAdaptiveMode
How aggressively BatteryAwareTheme applies its adaptations.
BatteryMode
Bucketed battery state. Drives BatteryAwareWidget and BatteryAwareTheme without exposing raw percentages to consumers (they don't need them — they just want to render less when there's less juice).
FatigueAdaptation
How FatigueAdaptiveForm reacts to the live fatigue signal.
FatigueLevel
Coarse fatigue bucket. Kept for backward compatibility — new code should prefer FatigueDetector.scoreStream for a continuous 0..100 signal that allows progressive UI adaptations instead of three discrete steps.
GripHand
Inferred dominant hand of the current grip.
GripPosture
Coarse posture inference — used as a guard against repositioning while the device is moving (walking, in-pocket).
InterruptionBucket
Coarse classification of how disruptive a pause was. The recovery engine adapts message tone, confidence and the auto-restore vs confirm path based on the bucket.
MorphPlan
The four subscription tiers Morph licenses can hold. Resolved at boot by LicenseValidator from the backend response (or from the 24-hour Hive cache, or from the demo-key shortcut).
MorphZoneType
Coarse semantic type of a zone — used by the scorer to decide how to interpret interactions (a click on a video zone ≠ a click on a button).
MovementContext
Coarse movement classification — derived from speed + GPS accuracy + (optionally) an accelerometer fallback when the satellite signal is degraded.
RecoveryOutcome
Outcome the dev signals back via InterruptionRecovery.recordOutcome after the user has interacted with (or ignored) a recovery card. Feeds the local learning loop in F.1.
RecoveryStrategy
How the recovery should be presented to the user when the snapshot is restored. Each snapshot declares its own strategy so the suggestion engine can adapt — a saved address can come back silently, a half-typed transfer amount must be confirmed.
SuggestionResponse
What the user did with a suggestion. Persisted in SuggestionHistoryStore to drive the cooldown rules.
SuggestionType
All suggestion categories Morph can produce. The engine fills the ones it has data for; new categories can be added without breaking existing consumers (the card widget renders a fallback icon).

Extensions

MorphAnalyticsContext on BuildContext
Privacy-screen helpers — read the live consent state and trigger the data-erase flow from any descendant widget.
MorphContext on BuildContext
Flutter-native equivalent of the useMorph() hook. Reach the Morph state from any descendant via context.morph.
MorphPlanContext on BuildContext
License-plan helpers — read the resolved tier and gate features at the call site without dragging a PlanGate around. Useful for imperative flows (e.g. opening an Agency-only screen via a button).
MorphRecoveryContext on BuildContext
Recovery-context declarations. Devs call these from a screen's didChangeDependencies (or anywhere with a context) so Morph knows what message to surface if the user is interrupted on this page.
MorphThemeExtensionContext on BuildContext
context.morphExt — null when the dev hasn't attached the extension.

Constants

kMorphSdkVersion → const String
SDK version sent in the validate payload so the backend can deprecate older clients gracefully. Bump this in lock-step with pubspec.yaml.
kRecoveryDefaultTtl → const Map<String, Duration>
Default time-to-live per declared context. Once a snapshot is older than its TTL, recovery refuses to surface it — a transfer amount from 30 minutes ago is more likely to mislead than help.

Functions

recoveryTtlFor(String context) Duration
Resolves the effective TTL for context, falling back to the "basic" entry when no specific match is configured.

Typedefs

DarkModeRequestCallback = void Function(BuildContext context)
Type of the dev-provided callback that switches the host app to dark mode. Wired via MorphProvider.onDarkModeRequested. The dev's implementation typically toggles their own ThemeMode controller (e.g. a Riverpod provider).
ResumePositionCallback = void Function(String route, double depth)
Type of the dev-provided callback that scrolls a screen back to the position the user left it at. The depth is a percentage (0..100). Wired via MorphProvider.onResumePosition.