a2ui-project / a2ui-project/a2ui

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

Offen
#1,877 1 Kommentar 0 Reaktionen 1 zugewiesene Person Beansprucht von @andrewkolos Auf GitHub ansehen
a: api-simplicity component: genui P2 sprint ready status: first-line-handled type: feature/enhancement
Vorherrschende Sprache
TypeScript
Sterne
16.4k
Forks
1.3k
Ø Merge
2 T. 13 Std.
Gemergte PRs (30 T.)
134

Beschreibung

_↴ Ported from [flutter/genui#811](https://github.com/flutter/genui/issues/811) — originally opened by [jacobsimionato](https://github.com/jacobsimionato) on 2026-03-18._
_Original labels: a: genui_lib, front-line-handled, quality, a: api-simplicity, sprint ready_
_Original assignees: [andrewkolos](https://github.com/andrewkolos)_

---

# 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 `SurfaceModel`s).
* **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.

---

### 10 comment(s) from the original issue

**[jacobsimionato](https://github.com/jacobsimionato)** commented on 2026-03-18:

Coding agent prompt:
Implementation of Phase 1: Agnostic Model Layer for GenUI Unified Architecture.

**Goal:** Establish a pure Dart state management layer for A2UI v0.9. This must be entirely decoupled from Flutter.

**Implementation Strategy:**
1. **Create `packages/genui_core`:** Initialize a new Dart package in `packages/genui_core`. Setup `pubspec.yaml` with no Flutter dependencies.
2. **Implement `DataPath` (RFC 6901):** Refine the existing `DataPath` logic to strictly follow JSON Pointer specs. Add robust segment parsing and joining.
3. **Implement `DataModel`:**
- Create an observable data store with "bubble and cascade" notification logic.
- Implement "Auto-vivification": Setting `/a/b/0/c` should create maps for `a` and `b`, and a list for `0`.
- Implement v0.9 [Type Coercion](https://github.com/google/A2UI/blob/main/specification/v0_9/docs/renderer_guide.md#data-model).
- `subscribe(path)` should return a ref-counted notifier providing synchronous initial values.
4. **Implement Agnostic Models:**
- `ComponentModel`: Observable state for a single component's JSON properties.
- `SurfaceComponentsModel`: A flat registry of `ComponentModel`s for a surface.
- `SurfaceModel`: Aggregates `DataModel`, `CatalogApi`, and `SurfaceComponentsModel`.
- `SurfaceGroupModel`: Root manager for multiple surfaces.
5. **Implement `MessageProcessor`:**
- A pure Dart controller that applies `A2uiMessage` mutations to the `SurfaceGroupModel`.
- Handle component recreation logic if the `type` changes for an existing `id`.

**Reference Code:**
- @A2UI/renderers/web_core/src/v0_9/state/data-model.ts
- @A2UI/renderers/web_core/src/v0_9/state/surface-model.ts
- @A2UI/renderers/web_core/src/v0_9/processing/message-processor.ts

**Context:**
- @genui/packages/genui/lib/src/model/data_model.dart
- @genui/packages/genui/lib/src/model/a2ui_message.dart
- @A2UI/specification/v0_9/docs/renderer_guide.md
- @A2UI/specification/v0_9/docs/a2ui_protocol.md

**Testing Requirements:**
- 100% unit test coverage for `DataModel` path resolution, auto-vivification, and notification triggers.
- Test `MessageProcessor` with various A2UI message streams (create, update, delete).

---

**[jacobsimionato](https://github.com/jacobsimionato)** commented on 2026-03-18:

Coding agent prompt:
Implementation of Phase 2: Context & Expression Layer for GenUI Unified Architecture.

**Goal:** Implement the expression parsing and scoping logic in `genui_core`.

**Implementation Strategy:**
1. **Refactor `DataContext`:**
- Implement `resolveDynamicValue(Object? value)` for one-time synchronous resolution.
- Implement `subscribeDynamicValue(Object? value, Function(V?) onChange)` for reactive binding to paths or function calls.
- Implement `nested(String relativePath)` for scope propagation.
2. **Implement `ExpressionParser`:**
- Port the TS expression parser to Dart.
- Support ${expression} syntax within strings.
- Handle tokenization of paths vs. function calls.
- Support nested interpolations (e.g., ${formatDate(value:${/date})}).
3. **Standard Functions:**
- Implement `formatString` as a core function in `genui_core`.
- Implement `and`, `or`, `not` logical functions.

**Reference Code:**
- @A2UI/renderers/web_core/src/v0_9/basic_catalog/expressions/expression_parser.ts
- @A2UI/renderers/web_core/src/v0_9/rendering/data-context.ts

**Context:**
- @genui/packages/genui/lib/src/functions/format_string.dart

**Testing Requirements:**
- Comprehensive tests for `ExpressionParser` handling escaping, recursion limits, and complex function arguments.
- Unit tests for `DataContext` relative path resolution within nested scopes.

---

**[jacobsimionato](https://github.com/jacobsimionato)** commented on 2026-03-18:

Coding agent prompt:
Implementation of Phase 3: Flutter Renderer Integration.

**Goal:** Refactor `packages/genui` to use the new `genui_core` models and provide a high-performance, granularly reactive renderer.

**Implementation Strategy:**
1. **Integrate `SurfaceGroupModel` into `SurfaceController`:** Refactor `SurfaceController` to delegate state management to `MessageProcessor` and `SurfaceGroupModel` from `genui_core`.
2. **`ComponentContext` Integration:** Introduce `ComponentContext` to pair `ComponentModel` with `DataContext`.
3. **Refactor `Surface` Widget:** Change `Surface` to be a `StatelessWidget` that builds the root component of a `SurfaceModel` recursively.
4. **Refactor `BasicCatalog`:**
- Update standard components (Text, Button, Row, Column, TextField) to use `ComponentContext`.
- Implement the `Checkable` trait for reactive validation feedback.

**Context:**
- @genui/packages/genui/lib/src/widgets/surface.dart
- @genui/packages/genui/lib/src/catalog/basic_catalog.dart
- @genui/packages/genui/lib/src/catalog/basic_catalog_widgets/

**Testing Requirements:**
- Flutter integration tests verifying that typing in a `TextField` reactively updates a dependent `Text` component.
- Visual regression tests for standard layout components.

---

**[polina-c](https://github.com/polina-c)** commented on 2026-03-18:

There is a lot of text here. Would it be more convenient to collaborate in a document?

---

**[yjbanov](https://github.com/yjbanov)** commented on 2026-03-18:

One motivator that's missing is support for server-side and non-Flutter client-side renderers.

---

**[yjbanov](https://github.com/yjbanov)** commented on 2026-03-18:

Oh, I see it's listed in "Key Benefits" as portability.

---

**[yjbanov](https://github.com/yjbanov)** commented on 2026-03-18:

> There is a lot of text here. Would it be more convenient to collaborate in a document?

Let's use the issue for sprint planning. Then actual work can be done using tools best suited for the job. A doc SGTM.

---

**[jacobsimionato](https://github.com/jacobsimionato)** commented on 2026-03-19:

I think the first step is to do 3.1 - try to get the model classes in, so find a way to put classes similar to https://github.com/google/A2UI/blob/main/renderers/web_core/src/v0_9/state/ between SurfaceController and Surface in the existing Gen UI SDK.

---

**[jacobsimionato](https://github.com/jacobsimionato)** commented on 2026-03-25:

One way to break this down is to build a core library in isolation first - see https://github.com/flutter/genui/issues/828

---

**[andrewkolos](https://github.com/andrewkolos)** commented on 2026-05-28:

Providing an update here which is mostly a recap of what I've shared on other channels:

My plan is still to migrate `package:genui` onto `package:a2ui_core`, so that so that the Flutter renderer follows the same unified architecture as the React/Lit renderers backed by `web_core`. Doing everything in one change would be a bit too large to review (>7000 lines), so I'm splitting it into smaller PRs.

First are some bug fixes: #938 (waiting on review) #940, and #936. My current plan for landing the rest:

The next PR will consist of swapping out the "runtime" stuff (data/surface model, message-processing) using temporary compatibility shims to avoid breaking and renaming a lot of the public API while we get the rest of the change reviewed and landed. I have this drafted up on a branch, https://github.com/andrewkolos/genui/tree/migrate-genui-to-a2ui-core, which I'm still reviewing/tidying up.

After that will come pretty much everything else: introducing `GenericBinder` and the typed props API, migrating all the catalog widget bodies, deleting all current API that doesn't match the unified architecture design and the shims between them and a2ui_core, and renaming types to the names chosen in the renderer guide.

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.