pseudo_ui 0.2.3 copy "pseudo_ui: ^0.2.3" to clipboard
pseudo_ui: ^0.2.3 copied to clipboard

Server-Driven UI rendering engine for Flutter. Define UI with JSON Schema + View JSON, render natively with Material 3 widgets. 45+ components, expression engine, conditional visibility, LOV cascading [...]

pseudo_ui #

A Server-Driven UI (SDUI) rendering engine for Flutter. Define your UI once with JSON — render it natively with Material 3 widgets.

The SDK pairs a JSON Schema data contract with a View JSON component tree to produce fully interactive forms, summaries, and multi-step workflows without shipping new client code.

Features #

  • 45+ Material Design 3 components — TextField, Dropdown, DatePicker, TabView, Dialog, Slider, Avatar, Chip, and more
  • Delegate-driven architecture — the SDK never makes HTTP calls; your app provides data via a simple interface
  • Expression engine$form, $instance, $param, $ui, $lov, $lookup, $schema, $item, $context namespaces for dynamic value resolution
  • Conditional engineshowIf / hideIf / enableIf / disableIf with allOf, anyOf, not compound rules and 13 operators
  • Validation engine — JSON Schema validation (pattern, format, minLength, min/max) plus async custom validation via delegate
  • LOV & Lookup — List-of-Values dropdowns with cascade filtering and real-time data enrichment
  • Nested components — reusable sub-components with isolated contexts, input contracts (x-binding), and two-way data flow
  • $ui state — transient UI state (dialog visibility, active tab) that never pollutes form data
  • Overlay surfaces — Dialog, BottomSheet, SideSheet, NavigationDrawer open/close driven by $ui state
  • Multi-language — all labels, errors, and enum values support { "en": "...", "tr": "...", "ar": "..." }

Installation #

Add to your pubspec.yaml:

dependencies:
  pseudo_ui: ^0.1.1

Or from pub.dev:

flutter pub add pseudo_ui

Quick Start #

Two steps: first a minimal form, then we enrich it with a nested component and a dropdown.

Step 1: Minimal form #

Name, surname, birth date, and a Submit button.

Schema:

{
  "type": "object",
  "required": ["name", "surname"],
  "properties": {
    "name": { "type": "string", "minLength": 1, "x-labels": { "en": "First Name", "tr": "Ad" } },
    "surname": { "type": "string", "minLength": 1, "x-labels": { "en": "Surname", "tr": "Soyad" } },
    "birthDate": { "type": "string", "format": "date", "x-labels": { "en": "Date of Birth", "tr": "Doğum Tarihi" } }
  }
}

View:

{
  "view": {
    "type": "Column",
    "gap": "md",
    "children": [
      { "type": "TextField", "bind": "name" },
      { "type": "TextField", "bind": "surname" },
      { "type": "DatePicker", "bind": "birthDate" },
      { "type": "Button", "label": { "en": "Submit", "tr": "Gönder" }, "variant": "filled", "action": "submit" }
    ]
  }
}

Dart code:

import 'package:flutter/material.dart';
import 'package:pseudo_ui/pseudo_ui.dart';

class MyPage extends StatelessWidget {
  final DataSchema schema; // loaded from JSON
  final ViewDefinition view; // loaded from JSON

  const MyPage({super.key, required this.schema, required this.view});

  @override
  Widget build(BuildContext context) {
    return PseudoView(
      schema: schema,
      view: view,
      lang: 'en',
      delegate: MyDelegate(),
    );
  }
}

class MyDelegate extends PseudoViewDelegate {
  @override
  RequestDataFn get requestData => (ref, [params]) async {
    throw UnimplementedError('No LOV in this example');
  };

  @override
  Future<({DataSchema schema, ViewDefinition view})> loadComponent(String ref) async {
    throw UnimplementedError('No nested components');
  }

  @override
  Future<void> onAction(String action, Map<String, dynamic> formData, [String? command]) async {
    if (action == 'submit') debugPrint('Form submitted: $formData');
  }
}

Step 2: Add a dropdown with LOV #

Add a city field. The dropdown gets options via requestData.

Add to schema:

"city": {
  "type": "string",
  "x-labels": { "en": "City", "tr": "Şehir" },
  "x-lov": {
    "source": "get-cities",
    "valueField": "$.response.data.code",
    "displayField": "$.response.data.name"
  }
}

Add to view children (before Button):

{ "type": "Dropdown", "bind": "city" }

Implement requestData:

@override
RequestDataFn get requestData => (ref, [params]) async {
  if (ref == 'get-cities') {
    return {
      'response': {
        'data': [
          {'code': '06', 'name': 'Ankara'},
          {'code': '34', 'name': 'Istanbul'},
        ]
      }
    };
  }
  throw Exception('Unknown source: $ref');
};

Initial data (optional) #

You can pass initial values when the view first renders:

Parameter Purpose
instanceData Backend/persisted data (e.g. read-only display, lookup filters). Used by $instance expressions and summary views.
params Parent-bound parameters for nested components. Used by $param expressions.
PseudoView(
  schema: schema,
  view: view,
  instanceData: {'status': 'active', 'createdAt': '2024-01-01'},
  params: {'branchCode': '001'},
  lang: 'en',
  delegate: delegate,
)

instanceData is for backend state that drives display and lookups. Form data is managed internally by PseudoView based on schema defaults and user input — both instanceData and params are optional.


Lookups (enrichment) #

When a schema property has x-lookup, the SDK fetches enrichment data via requestData. You must activate the lookup by listing it in the view's lookups array — otherwise it won't run.

Schema (defines the lookup):

{
  "branchDetail": {
    "type": "object",
    "x-lookup": {
      "source": "get-branch-details",
      "resultField": "$.response.data",
      "filter": [{ "param": "branchCode", "value": "$param.selectedBranchCode", "required": true }]
    }
  }
}

View (activates it):

{
  "dataSchema": "urn:amorphie:res:schema:shared:branch-info",
  "lookups": ["branchDetail"],
  "view": { "type": "Column", "children": [
    { "type": "Text", "content": "$lookup.branchDetail.address" }
  ]}
}

Then use $lookup.branchDetail.address, $lookup.branchDetail.phone, etc. in Text or other components. The SDK calls requestData(source, filterParams) when the view mounts; the delegate returns the enrichment payload.


Architecture #

┌─────────────────────────────────────────────────────┐
│                   Your Application                   │
│                                                      │
│  ┌──────────────┐   implements   ┌────────────────┐ │
│  │  MyPage      │ ──────────────▶│  Delegate      │ │
│  │              │                │  - requestData │ │
│  │  PseudoView( │                │  - loadComponent│ │
│  │   schema,    │                │  - onAction    │ │
│  │   view,      │                │  - onLog       │ │
│  │   lang,      │                └────────────────┘ │
│  │   delegate)  │                        ▲          │
│  └──────┬───────┘                        │          │
├─────────┼────────────────────────────────┼──────────┤
│  SDK    │                                │          │
│         ▼                                │          │
│  ┌─────────────────┐  ┌──────────────┐   │          │
│  │ DynamicRenderer  │  │ Expression   │   │          │
│  │ (recursive)      │  │ Resolver     │   │          │
│  │                  │  ├──────────────┤   │          │
│  │ 45+ MD3 widgets  │  │ Conditional  │   │          │
│  │ built-in Flutter │  │ Engine       │   │          │
│  │                  │  ├──────────────┤   │          │
│  │ Overlay surfaces │  │ Schema       │   │          │
│  │ (Dialog, Sheet)  │  │ Resolver     │◀──┘          │
│  │                  │  │ (validation) │              │
│  └─────────────────┘  └──────────────┘              │
└─────────────────────────────────────────────────────┘

Data Model (MVVM) #

Layer File Purpose
ViewModel schema.json Data contract — field types, validation, LOV sources, conditionals, multi-lang labels
View view.json UI component tree — layout, binding, actions, transient UI state
Model Backend Persisted data, served via delegate's requestData

Delegate Interface #

abstract class PseudoViewDelegate {
  /// Fetch data from backend (LOV items, lookup enrichment).
  RequestDataFn get requestData;

  /// Load a nested component's schema + view by reference.
  Future<({DataSchema schema, ViewDefinition view})> loadComponent(String ref);

  /// Handle user actions (submit, cancel, back, custom commands).
  Future<void> onAction(String action, Map<String, dynamic> formData, [String? command]);

  /// Optional: custom async validation after built-in checks pass.
  Future<String?> onValidationRequest(String field, dynamic value, Map<String, dynamic> formData);

  /// Optional: capture SDK logs.
  void onLog(String level, String message, [dynamic error, Map<String, dynamic>? context]);

  /// Optional: receive nested component lookup data (for debug panels).
  void onNestedLookupData(String componentRef, Map<String, Map<String, dynamic>> lookupData);
}

Supported Components #

Layout #

Column · Row · ScrollView · Grid · Expanded · Center · Wrap · Divider

Input #

TextField · TextArea · NumberField · Dropdown · Checkbox · RadioGroup · DatePicker · TimePicker · Switch · Slider · SegmentedButton · SearchField · AutoComplete

Display #

Text · Icon · Image · Avatar · Chip · Badge · ListTile · RichText · ProgressIndicator · LoadingIndicator

Surface & Overlay #

Card · Dialog · BottomSheet · SideSheet · Snackbar · Tooltip

TabView · AppBar · NavigationBar · NavigationDrawer

Container #

ExpansionPanel

Action #

Button · IconButton · FAB · Menu · Toolbar

Control #

ForEach · Component (nested)

Expression Namespaces #

Namespace Source Example
$form.field User input data $form.firstName
$instance.field Backend persisted data $instance.status
$param.field Parent-bound data (nested components) $param.cityCode
$ui.key Transient UI state (not submitted) $ui.showDialog
$schema.field.label Schema label for current language $schema.city.label
$lov.field LOV items array $lov.city
$lov.field.display Localized display name for current value $lov.city.display
$lookup.prop.field Enrichment data $lookup.branch.address
$item.field ForEach iteration item $item.name
$context.lang Runtime context $context.lang

Conditional Operators #

equals · notEquals · in · notIn · greaterThan · lessThan · greaterThanOrEquals · lessThanOrEquals · contains · startsWith · endsWith · isEmpty · isNotEmpty

Compound rules: allOf (AND), anyOf (OR), not (negate) — recursive nesting supported.

Validation Formats #

Built-in format validators: email · uri / url · date · date-time · time · phone / tel · iban

Vocabularies #

The repository includes shared JSON Schema vocabulary definitions for IDE auto-complete and tooling (in the repo root vocabularies/ folder):

  • View Vocabulary (view-vocabulary.json) — defines all valid component types, properties, and their constraints
  • ViewModel Vocabulary (view-model-vocabulary.json) — defines all x-* extensions (x-labels, x-lov, x-conditional, etc.)

Both TS and Dart SDKs share the same vocabulary definitions, ensuring consistent component behavior across platforms.

Testing #

cd core/dart-pseudo-ui
flutter test

101 tests covering expression resolver, schema resolver, conditional engine, bind resolver, data client, widget rendering, overlay surfaces, and LOV cascading.

Theming #

The pseudo-ui Flutter adapter integrates with Material 3 theming. Form fields respect InputDecorationTheme, text uses TextTheme variants, and colors follow ColorScheme roles. For semantic colors (success, warning, info), use the PseudoUiThemeExt theme extension.

See the Theming Guide for comprehensive theming documentation.

Cross-Platform #

This is the Dart/Flutter implementation. A companion TypeScript package (@burgan-tech/pseudo-ui on npm) renders the same JSON schemas and views using PrimeVue, PrimeReact or Angular Material components. Both share the same vocabulary definitions and expression engine, ensuring consistent behavior across platforms.

License #

MIT

0
likes
140
points
11
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Server-Driven UI rendering engine for Flutter. Define UI with JSON Schema + View JSON, render natively with Material 3 widgets. 45+ components, expression engine, conditional visibility, LOV cascading, nested components.

Homepage
Repository (GitHub)
View/report issues

Topics

#sdui #server-driven-ui #json-schema #material-design #dynamic-forms

License

MIT (license)

Dependencies

flutter, intl

More

Packages that depend on pseudo_ui