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/reporton the dev'sconfig.uploadInterval. Runs only whenconfig.canUploadis 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
Positionupdates 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
AppColorsfile or any other palette they maintain by hand. Everything is optional — missing slots are filled in from the ambient ThemeData byColorExtractor. - 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 thecontext.morphextension. - Drop-in NavigatorObserver that feeds Morph's behavior store with every route transition. Plug it into any router that accepts a NavigatorObserver:
- 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 viaMorphFeatureson 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
Columnof MorphZones that rearranges itself based on the scorer's latest decisions. Children keep their widget state across reorders because each is built with a stableKey(zoneId). - 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.builderso the suggestion card has access to MaterialApp'sMediaQuery,Directionalityand 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…Contextextensions and restored byInterruptionRecoverywhen 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
_actionclosure 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_suggestionsHive 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
LicenseValidatorfrom 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 viacontext.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.