a2ui-project / a2ui-project/a2ui
Unified Architecture: Restructure GenUI to align with A2UI v0.9 (Decoupled Core + Flutter Renderer)
- 主要言語
- TypeScript
- スター
- 16.4k
- フォーク
- 1.3k
- 平均マージ
- 2日 13時間
- マージ済み PR(30日)
- 134
説明
_↴ 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.
コントリビューションガイド
評価
この issue はまだ評価されていません。