Skip to content

Unified Architecture: Restructure GenUI to align with A2UI v0.9 (Decoupled Core + Flutter Renderer) #811

@jacobsimionato

Description

@jacobsimionato

Design Document: A2UI Flutter Unified Architecture (v0.9)

1. Introduction & Motivation

Currently, the Flutter GenUI library processes A2UI streams and manages state using a monolithic, immutable data structure (SurfaceDefinition). Whenever an updateComponents or updateDataModel message arrives, the SurfaceController applies the changes and pushes a completely new SurfaceDefinition. The Surface widget listens to this global change and rebuilds the entire component tree.

As UI complexity grows, this approach has several drawbacks:

  1. Performance (Lack of Granular Reactivity): Typing in a TextField updates the Data Model, which triggers a full surface rebuild.
  2. Coupling: A2UI message parsing, JSON Pointer resolution, and Flutter widget building are tightly entangled.
  3. Cross-Platform Inconsistency: The A2UI Web Renderers (React, Angular, Lit) all share a unified, framework-agnostic state engine (@a2ui/web_core). Flutter's implementation is entirely bespoke, making it harder to maintain parity and share catalog logic.

The Solution: Implement the A2UI Unified Architecture. We will split the library into a Platform-Agnostic State Engine (genui_core, pure Dart) and a Framework-Specific Renderer (genui, Flutter).

2. The Great Decoupling (Package Structure)

We will introduce a new package into the monorepo:

  • packages/genui_core (Pure Dart): Has absolutely zero dependency on package:flutter. It is responsible for parsing A2UI messages, maintaining the live state tree, resolving JSON pointers, and evaluating logic/expressions.
  • packages/genui (Flutter): Depends on genui_core and package:flutter. It contains the Flutter Surface widget, the BasicCatalog Flutter widgets (Material/Cupertino), and layout delegates.

3. Architectural Changes: Old vs. New

This section maps the current Flutter-centric classes to their new, decoupled counterparts.

3.1. State Management: From Static to Live Models

Currently, SurfaceDefinition acts as a static snapshot of the UI.

  • Deprecated: SurfaceDefinition
  • New (genui_core): SurfaceModel, SurfaceComponentsModel, and ComponentModel.
    • Instead of a static map, SurfaceComponentsModel acts as a live registry.
    • Each component is backed by a ComponentModel which holds its properties and exposes an onUpdated event stream.
    • Why? This allows a Flutter widget to subscribe only to its specific ComponentModel. If a message updates the Button's label, only the Button rebuilds, not the whole surface.

3.2. Data Binding & JSON Pointers

Currently, InMemoryDataModel handles basic path resolution but lacks strict RFC 6901 compliance and advanced array manipulation (auto-vivification).

  • Restructured (genui_core): DataModel
    • Will be moved to genui_core.
    • Must implement Auto-vivification: Setting /a/b/0/c automatically creates nested maps and lists.
    • Must implement the v0.9 Bubble & Cascade Notification Strategy: A change to /user/name notifies listeners of /user/name, /user, /, and /user/name/first (if it existed).

3.3. Message Processing

Currently, SurfaceController.handleMessage manually applies changes to SurfaceDefinition and the DataModel.

  • New (genui_core): MessageProcessor
    • A pure Dart class. It takes a stream of A2uiMessage objects and mutates the SurfaceGroupModel (which holds all SurfaceModels).
  • Restructured (genui): SurfaceController becomes a thin Flutter wrapper around the MessageProcessor, bridging the pure Dart engine to Flutter's lifecycle.

3.4. Widget Rendering & Context

Currently, CatalogItem.widgetBuilder receives a CatalogItemContext containing raw JSON data. Widgets manually extract paths and set up data model listeners.

  • Deprecated: CatalogItemContext
  • New (genui_core): ComponentContext & DataContext
    • ComponentContext pairs a ComponentModel (the UI config) with a DataContext (the scoped data state).
    • DataContext handles evaluating expressions (e.g., ${/user/name} or ${formatDate(...)}) and resolving relative paths natively in Dart.
  • New (genui): Flutter widgets will now receive a ComponentContext. They will use utility builders (like a generic binder or updated BoundString) to listen to the exact resolved values coming from the DataContext.

3.5. Recursive Surface Rendering

Currently, the Surface widget wraps itself in a ValueListenableBuilder watching the entire SurfaceDefinition.

  • Restructured: Surface will take a SurfaceModel. It will simply render the component with ID root. The recursive buildChild pipeline will construct the Flutter widget tree. Each widget connects to its own ComponentModel and DataContext, achieving O(1) rebuilds for data/property changes.

4. Key Benefits

  1. Granular Reactivity: Dramatically improves rendering performance. Changes to data or properties only trigger rebuilds for the specific Flutter widgets listening to them.
  2. Strict Separation of Concerns: Core A2UI logic (JSON handling, pointers, expression parsing) is completely isolated and can be rigorously unit tested without booting a Flutter engine.
  3. Portability: The genui_core package can be used in non-Flutter environments. For example, a pure Dart CLI tool could ingest A2UI streams and print terminal UI, or a backend Dart server could validate A2UI states.
  4. Ecosystem Consistency: This architecture mirrors the @a2ui/web_core implementation. As A2UI evolves (and as future iOS/Android native renderers are built), having a shared mental model and architecture across platforms makes maintaining the specification and catalogs exponentially easier.

5. Implementation Phases

The detailed coding agent prompts for these phases are included in the comments below.

  1. Phase 1: Agnostic Model Layer: Build genui_core, DataModel, MessageProcessor, and the live component models.
  2. Phase 2: Context & Expression Layer: Implement ExpressionParser, DataContext scoping, and core functions (e.g., formatString).
  3. Phase 3: Flutter Renderer Integration: Refactor genui to use genui_core, updating the Surface widget and BasicCatalog components for granular reactivity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    a: ergonomicsEfficiency and simplicity of the SDK adoption.a: genui_libfront-line-handledCan wait until the second-line triage. The front-line triage already checked if it's a P0.qualityAn issue which affects the quality of the Gen UI productsprint readyA task that should be included in sprint planning

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions