跳转至正文

输入与事件

GenUI 应用中如何处理输入与事件。

本指南说明 GenUI package 中如何处理用户交互,从最初 widget 交互到 AI 智能体接收事件。

概览

#

在 GenUI 架构中,UI 由 AI 驱动,但用户交互(如点击按钮或提交表单)必须传回 AI 智能体,以便智能体根据用户输入更新 UI 或执行操作。

事件流程如下:

  1. 交互 (Interaction):用户与 widget 交互;例如点击按钮。

  2. 捕获 (Capture):widget 实现派发 UiEvent

  3. 处理 (Processing):框架添加上下文(如 surfaceId 或数据模型值)并转发事件。

  4. 传输 (Transmission):Flutter widget 生成事件、添加上下文,经 ContentGenerator 路由到 AI,再转发给 AI 智能体。

定义事件

#

协议层

#

A2UI 协议定义用于上报事件的 action 消息。 action 包含:

  • name:操作名称(由 AI 在生成 widget 时定义)。

  • surfaceId:事件发生所在 UI surface 的 ID。

  • sourceComponentId:触发事件的 widget ID。

  • context:包含与事件相关数据的 JSON 对象。

  • timestamp:事件发生时间。

Dart 实现

#

package:genui 中,用户事件由 UiEvent extension type 及其实现 UserActionEvent 表示。

以下结构定义在 lib/src/model/ui_models.dart

lib/src/model/ui_models.dart
dart
/// A data object that represents a user interaction event in the UI.
extension type UiEvent.fromMap(JsonMap _json) { ... }

/// A UI event that represents a user action.
extension type UserActionEvent.fromMap(JsonMap _json) implements UiEvent {
  UserActionEvent({
    String? surfaceId,
    required String name,
    required String sourceComponentId,
    JsonMap? context,
    // ...
  }) : ...
}
lib/src/model/ui_models.dart
dart
/// 表示 UI 中用户交互事件的数据对象。
extension type UiEvent.fromMap(JsonMap _json) { ... }

/// 表示用户操作的 UI 事件。
extension type UserActionEvent.fromMap(JsonMap _json) implements UiEvent {
  UserActionEvent({
    String? surfaceId,
    required String name,
    required String sourceComponentId,
    JsonMap? context,
    // ...
  }) : ...
}

在 widget 中捕获事件

#

GenUI 中的 widget 在 Catalog 中定义,其中包含 widget 可向 AI 发送哪些事件的信息。 AI 随后可发送如何回传这些事件的说明。实现自定义 widget(或使用标准 widget)时,在 CatalogItemContext 中使用 dispatchEvent 派发事件。

示例:Button 实现

#

以下示例展示 Button widget 通常如何捕获点击并派发事件:从属性获取 AI 提供的 action 定义,解析上下文中的数据绑定,并发送事件。

dart
// Inside a CatalogItem widgetBuilder:
widgetBuilder: (itemContext) {
  // 1. Extract action data from the component properties.
  final buttonData = _ButtonData.fromMap(itemContext.data as JsonMap);
  final JsonMap actionData = buttonData.action;
  final actionName = actionData['name'] as String;

  // 2. Extract context definition (which data to send back).
  final List<Object?> contextDefinition =
      (actionData['context'] as List<Object?>?) ?? <Object?>[];

  return ElevatedButton(
    onPressed: () {
      // 3. Resolve the context values from the data model.
      final JsonMap resolvedContext = resolveContext(
        itemContext.dataContext,
        contextDefinition,
      );

      // 4. Dispatch the event.
      itemContext.dispatchEvent(
        UserActionEvent(
          name: actionName,
          sourceComponentId: itemContext.id,
          context: resolvedContext,
        ),
      );
    },
    child: /* ... */
  );
},

事件处理流水线

#

调用 dispatchEvent 后,事件流经 GenUI 核心层。

Surface

#

Surface widget(位于 lib/src/core/surface.dart)包装已渲染 widget,提供 dispatchEvent 回调实现。

调用 _dispatchEvent 时:

  1. 自动将 surfaceId 注入事件,确保 AI 知道交互来自哪个 surface。

  2. 将处理委托给 SurfaceHost(由 SurfaceController 实现)。

dart
// Surface implementation details
void _dispatchEvent(UiEvent event) {
  // ...
  final Map<String, Object?> eventMap = {
    ...event.toMap(),
    surfaceIdKey: widget.surfaceId, // Inject surfaceId
  };
  final UiEvent newEvent = UserActionEvent.fromMap(eventMap);
  widget.host.handleUiEvent(newEvent);
}

SurfaceController

#

SurfaceController(位于 lib/src/core/surface_controller.dart)是管理 UI 状态的中央枢纽。

调用 handleUiEvent 时,它会:

  1. 验证事件类型。

  2. 用协议要求的 action JSON 信封包装事件。

  3. 在其 onSubmit 流上发出 UserUiInteractionMessage

dart
// SurfaceController implementation details
@override
void handleUiEvent(UiEvent event) {
  if (event is! UserActionEvent) return;

  // Wrap in protocol 'action' envelope
  final String eventJsonString = jsonEncode({'action': event.toMap()});

  // Emit for listeners (like Conversation)
  _onSubmit.add(UserUiInteractionMessage.text(eventJsonString));
}

传输到 AI

#

最后一步将事件发送给 AI 智能体,通常由 Conversation(位于 lib/src/facade/conversation.dart)处理。 Conversation 监听消息处理器的 onSubmit 流。

dart
// Conversation constructor
_userEventSubscription = surfaceController.onSubmit.listen(sendRequest);

收到事件时,sendRequest 方法会:

  1. UserUiInteractionMessage 包装回开发者客户端代码。

  2. 自定义集成或预定义传输适配器将消息转发到 LLM 智能体网络传输。

AI 智能体接收该 JSON 消息,处理用户操作,并可能流式返回新的 surfaceUpdatedataModelUpdate 消息以修改 UI,或其他操作,完成完整交互循环。