sdui_core 0.3.2
sdui_core: ^0.3.2 copied to clipboard
A high-performance Server-Driven UI (SDUI) engine for Flutter. Render dynamic, state-aware layouts from JSON payloads at runtime — no App Store review needed for UI changes.
0.3.2 #
Form widgets — createFormWidgets() #
9 new widget builders registered automatically by SduiScope: sdui:text_field, sdui:checkbox, sdui:checkbox_list_tile, sdui:radio, sdui:radio_list_tile, sdui:slider, sdui:range_slider, sdui:switch, sdui:dropdown. All dispatch via onChange / onSubmitted actions.
Error handling #
SduiErrorBoundary — StatefulWidget that catches Flutter build errors in a subtree and shows an inline error tile in debug mode (SizedBox.shrink in release). Accepts an onError callback for logging. Also available as sdui:error_boundary in JSON.
Renderer error protection — builder exceptions in SduiRenderer are now caught per-node. A broken node shows an error tile (debug) or collapses silently (release) — one bad node can no longer crash the whole screen.
JSON Schema & v2.0 #
- Added
schema/v1.jsonandschema/v2.json(JSON Schema draft-07) for server-side payload validation. SduiSchemaMigrator.migrate()— non-mutating, idempotent v1→v2 migration.- Parser and validator accept
sdui_version: "2.0". v2 requires explicitversionon every node (MISSING_NODE_VERSION). Rootmetadatafield is optional.
0.3.1 #
Bug fixes #
SduiWidgetRegistry defaults were empty
SduiWidgetRegistry.withDefaults()and.defaultsreturned empty registries because_loadDefaults()was a no-op.- Added
configureDefaultFactory(factory)static method.SduiScopewires it on first build;withDefaults()now calls the factory correctly. configureDefaultFactorymerges builders into an already-accesseddefaultsinstance, so custom widget registrations added beforeSduiScopeis built are preserved.
SduiScope only auto-loaded core widgets
_buildDefault()now registers core + Material 3 + Cupertino widgets viaconfigureDefaultFactory.- No manual
registerAll(createCoreWidgets())call is needed inmain().
sdui:bottom_sheet and sdui:dialog rendered inline
- Both were placeholder builders that rendered content inside the widget tree rather than as overlays.
- Added
SduiOverlayCallbacktypedef andwithOverlayCallback()method toSduiActionRegistry. SduiScreeninjects a renderer-aware overlay handler —show_bottom_sheettriggersshowModalBottomSheet,show_dialogtriggersshowDialog.dismiss_bottom_sheetaction now callsctx.navigator?.pop()(was a no-op).sdui:bottom_sheetwidget renders a drag-handle header + children insideSafeArea. Supportspaddingprop.sdui:dialogrenders anAlertDialogwith optionaltitle,confirmLabel, andcancelLabelprops.
Example app
- Removed redundant
SduiWidgetRegistry.defaults.registerAll(createCoreWidgets())call frommain(). - Refactored to use
SduiScope(registry: SduiWidgetRegistry.withDefaults()..register(...))— the canonical pattern.
0.3.0 #
Architecture: state management overhaul #
SduiController — new ChangeNotifier state machine
- Extracted the full fetch → cache → parse → diff lifecycle out of
_SduiScreenStateinto a standaloneSduiController extends ChangeNotifier. - Any state-management framework (Bloc, Provider, Riverpod, get_it, plain
setState) can now observe or drive the screen without any boilerplate wiring. - New
SduiScreen.controlled(controller: myController)named constructor for the controlled pattern; the standardSduiScreen(url: '...')constructor is unchanged. patchNode(nodeId, props)— apply optimistic prop overrides to any node instantly, without a network round-trip.clearPatch/clearAllPatchesrestore server values.refresh()— programmatic force-reload from anywhere.effectiveNode— the patched tree that the screen renders, safe to observe.
SduiBindings + SduiBindingsNotifier — live state injection
- New
InheritedNotifier-based widget that injects observable key/value state into the SDUI tree. visible_ifnow supports a"binding.X"expression form in addition to the existing"props.X"form.- Update bindings from your state layer; all dependent nodes rebuild automatically — no server round-trip:
SduiBindings( notifier: SduiBindingsNotifier({'user.isPremium': false}), child: SduiScope(child: ...), ) // JSON: { "visible_if": "binding.user.isPremium" }
SduiScope.navigatorKey — safe async navigation
- New
navigatorKey: GlobalKey<NavigatorState>?onSduiScope. - Flows through
SduiBuildContext.navigatorKey→SduiActionContext.navigatorKey. SduiActionContext.navigatorresolvesnavigatorKey.currentStatefirst, falling back toNavigator.maybeOf(flutterContext)only when the context is still mounted.- All three widget builder families (core, material, cupertino) now propagate the key.
BuildContext safety fixes
navigateaction now usesctx.navigator?.pushNamed(route)— safe across async middleware gaps, never crashes on unmounted contexts.- All built-in action handlers guard
ctx.flutterContext.mountedbefore accessingScaffoldMessengerorNavigator.
0.2.3 #
- WASM fix: replaced
dart:isolate/Isolate.run()inSduiParser.parseStringwith Flutter'scompute()fromflutter/foundation.dart.compute()is WASM-compatible and still offloads JSON parsing to a background isolate on native platforms. This resolves the pub.dev "Package not compatible with runtime wasm" score penalty. - README: added use-case diagrams (e-commerce, A/B testing, feature flags, white-labelling) and integration recipes for
bloc,provider,riverpod,go_router,get_it,dio,freezed, andfirebase_remote_config. - Diagrams: added 8 new Mermaid-generated PNG assets —
lifecycle,usecase_ecommerce,usecase_ab_testing,usecase_feature_flags,integration_bloc,integration_state_mgmt,integration_go_router,integration_get_it.
0.2.2 #
- Replaced
cached_network_imagewith Flutter's built-inImage.networkto achieve full WASM compatibility. Thesdui:imagebuilder preserves the same loading spinner and broken-image error icon. - Removed
cached_network_imagefrompubspec.yaml; no API changes for consumers.
0.2.1 #
- Documentation updates, branding improvements, and README overhaul for pub.dev structure.
0.2.0 #
Breaking changes #
SduiWidgetRegistryandSduiActionRegistryare no longer singletons. The.instanceaccessor has been removed. UseSduiWidgetRegistry()to create a fresh instance orSduiWidgetRegistry.defaultsfor the shared default. Pass registries viaSduiScoperather than relying on global state — this makes tests and multi-tenant apps trivially correct.SduiParser.parseAsyncrenamed toSduiParser.parseStringto reflect that the method accepts a raw JSON string (not aFuture). The background-isolate behaviour is unchanged.SduiWidgetRegistry.resolvenow requires the named parameternodePath:. Previously the tree path was positional and easy to omit accidentally.
New features #
Transport layer
- Abstract
SduiTransportinterface. Swap HTTP for WebSocket — or a custom gRPC/mock transport — with a single constructor argument. HttpSduiTransport— default HTTP transport with exponential back-off retry. ConfigurablemaxRetries,retryDelay,timeout, and customheaders.WebSocketSduiTransport— streams every server message as a fresh layout. Auto-reconnects with exponential back-off; configurablemaxReconnectAttemptsandpingIntervalto keep the connection alive.
Caching
SduiCache— stale-while-revalidate cache backed byshared_preferences. Serves the last-known layout instantly on app launch while a fresh fetch runs in the background. Enabled by default onSduiScreen; opt out withenableCache: false.SduiCacheException(codeSDUI_006) added to the sealed exception hierarchy for cache read/write failures.
Validation
SduiValidator— full-tree pre-parse validation with structured error codes. ReportsMISSING_VERSION,INVALID_VERSION,MISSING_TYPE,MISSING_ID,DUPLICATE_ID,MISSING_ACTION_TYPE,MISSING_ACTION_EVENT, and more. CallSduiParser.validate(map)before parsing to surface user-friendly errors without catching parse exceptions.
Diffing
SduiDiffer— incremental tree diff byid + version. Returns changed, added, removed, and moved node sets. Feed theupdatedTreeintosetStateto let Flutter reconcile only the changed subtrees.
Props
SduiProps— type-safe prop accessor wrappingMap<String, Object?>. Helpers:getString,getBool,getDouble,getInt,getColor,getColorOrNull,getEdgeInsets,getAlignment,getDoubleOrNull.
Conditional rendering
"visible_if"prop — evaluated by the renderer before any builder is called. Acceptsboolliterals,"props.X"expressions (resolved against the node's own props), and plain truthy strings. ReturnsSizedBox.shrink()when false. The single most-requested SDUI feature; enables A/B testing, feature flags, and role-based layout without a native release.
Theming
SduiTheme—InheritedWidgetholding aMap<String, TextStyle>named style registry. Thesdui:textbuilder checksSduiThemebefore the built-in MaterialTextThememappings, so the server can control brand typography ('promo','fine_print', etc.) without a native release.
Debug tooling
SduiDebugOverlay— wraps every rendered node in debug builds. Long-pressing any node opens a floating inspector panel showing the node id, type, version, tree path, prop count, action count, and child count. Enable withSduiDebugOverlay.enabled = true. No-op in release builds.
Screen machine
SduiScreenis now a 7-state machine:loading,loadingWithCache,success,refreshing,error,errorWithCache,empty.- New constructor parameters:
transport,pullToRefresh,onLoad,onError,onEvent,onRefresh,emptyBuilder.
Rendering
SduiWidget— renders a pre-parsedSduiNodedirectly without a network request. Embed dynamic sub-trees anywhere in an existing screen.
Action system
SduiActionRegistry.addMiddleware— middleware chain for logging, analytics, and action transformation. Each middleware receives the action and anextcallback.SduiActionRegistry.withEventInterceptor— convenience factory for wiringSduiScreen.onEventinto the registry without subclassing.SduiAction.debounceMs— built-in double-tap protection; debounces repeated triggers at the action definition level, not the call site.
Widget builders
- Namespace wildcard resolution:
register('myapp:*', builder)matches any type in themyapp:namespace that has no more-specific registration. createMaterialWidgets()— 12 Material 3 builders:sdui:list_tile,sdui:switch_tile,sdui:progress,sdui:fab,sdui:bottom_nav,sdui:nav_rail,sdui:drawer,sdui:app_bar,sdui:search_bar,sdui:tab_bar,sdui:bottom_sheet,sdui:dialog.createCupertinoWidgets()— 7 Cupertino builders:sdui:cupertino_button,sdui:cupertino_nav_bar,sdui:cupertino_list_tile,sdui:cupertino_switch,sdui:cupertino_slider,sdui:cupertino_activity,sdui:cupertino_dialog.- 10 additional core types:
sdui:safe_area,sdui:aspect_ratio,sdui:fitted_box,sdui:clip_r_rect,sdui:opacity,sdui:transform_scale,sdui:hero,sdui:badge,sdui:chip,sdui:placeholder.
Utilities
SduiLogger— category-based debug logging withnetwork,render,action,cache, andwarnchannels. Silenced in release builds.SduiIcons— 100+ icon name →IconDatamappings for use insdui:icon.SduiColorParsingextension —"#RRGGBB"/"#AARRGGBB"/ named color string →Colorparsing.SduiContextExtension— convenience methods onBuildContextfor accessingSduiScopedata inline.
Quality
- All node types are
@immutablewithcopyWith,==,hashCode, anddebugFillPropertiesfor the Flutter widget inspector. - 171 tests across unit, widget, and integration suites.
- GitHub Actions CI matrix (stable + beta channel, ubuntu/macOS/Windows).
- OIDC-based automated publish workflow on
v*.*.*tags.
0.1.0 #
Initial release of sdui_core.
Rendering engine #
- JSON-in, native-Flutter-out rendering engine. A JSON payload from any server
is parsed once and rendered as a real Flutter widget tree — no
eval, no WebView, no code generation. - Isolate-based JSON parsing via
SduiParser.parseAsync. The JSON decode and node tree construction run on a background isolate, keeping the UI thread free during heavy payloads. RepaintBoundaryat everySduiScreenroot. Per-subtree opt-in via"isolateRepaint": truein any node's props.SduiKeyManager— deterministicValueKeygeneration fromid + parentPath. Ensures Flutter's reconciler reuses element state across rebuilds rather than tearing down and re-mounting subtrees on every refresh.SduiScope—InheritedWidgetthat makes the widget registry and action registry available to every builder without prop-drilling.
Built-in widget types (18) #
sdui:text (Text with style mapping), sdui:image (Image.network with
fit/width/height), sdui:container (color, padding, border-radius),
sdui:column, sdui:row, sdui:stack (alignment props), sdui:button
(elevated / outlined / filled variants), sdui:icon (icon name → IconData),
sdui:divider / sdui:spacer, sdui:grid (GridView with columns, spacing,
aspectRatio), sdui:list (ListView / ListView.builder), sdui:card
(elevation, color), sdui:padding (directional), sdui:center,
sdui:expanded (flex prop), sdui:visibility (show/hide on visible bool),
sdui:inkwell (InkWell tap wrapper).
Actions (5 built-in types) #
navigate (Navigator.pushNamed with payload.route), open_url
(launchUrl with payload.url), show_snackbar (ScaffoldMessenger with
payload.message), copy_to_clipboard (Clipboard.setData with
payload.text), dispatch (calls a registered custom handler).
Registries (singletons in 0.1.0, replaced in 0.2.0) #
SduiWidgetRegistry.instance — global widget builder registry with register,
registerAll, resolve, and wildcard resolution.
SduiActionRegistry.instance — global action handler registry with register,
dispatch, and middleware support.
Exception hierarchy #
Typed, sealed exception hierarchy — every error includes a code, a human-readable message, and an actionable hint string:
SduiParseException(SDUI_001) — JSON parsing failed.SduiVersionException(SDUI_002) — unsupportedsdui_versionfield.SduiNetworkException(SDUI_003) — HTTP / network failure.SduiUnknownWidgetException(SDUI_004) — no builder registered for type.SduiActionException(SDUI_005) — no handler registered for event.
Auto-refresh #
SduiScreen(refreshInterval: Duration(minutes: 5)) — polls the server on a
timer and re-renders on change without any application code.