Skip to main content

bloc_signals_genui 1.0.0 copy "bloc_signals_genui: ^1.0.0" to clipboard
bloc_signals_genui: ^1.0.0 copied to clipboard

Pure-Dart A2UI state machine and Generative UI adapter for the BlocSignal ecosystem.

example/example.dart

// Example CLI script prints streaming A2UI state updates to stdout.
// ignore_for_file: avoid_print

import 'dart:async';

import 'package:bloc_signals_genui/bloc_signals_genui.dart';

void main() async {
  const basicCatalogId =
      'https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json';

  // 1. Instantiate the A2UI surface state machine with bounded retention
  late final A2uiSurfaceBloc bloc;
  bloc = A2uiSurfaceBloc(
    maxSurfaces: 5,
    maxResponseHistory: 50,
    outboundListener: (message) {
      // Loopback mock responder for outbound callAgentFunction RPC envelopes
      if (message is CallAgentFunctionMessage) {
        bloc.add(
          ProcessMessage(
            AgentFunctionResponseMessage(
              version: 'v1.0',
              response: A2uiFunctionResponse.value(
                message.functionCallId,
                'Verified Developer Badge',
              ),
            ),
          ),
        );
      }
    },
  );

  // 2. Subscribe to action responses for agent tool execution
  final responseCompleter = Completer<void>();
  final responseSub = bloc.actionResponses.listen((response) {
    print('Agent tool payload: ${response.toToolResult()}');
    if (!responseCompleter.isCompleted) {
      responseCompleter.complete();
    }
  });

  // 3. Observe state transitions and signal when SurfaceReady is reached
  final readyCompleter = Completer<void>();
  final disposeStateListener = bloc.state.subscribe((state) {
    if (state is SurfaceReady && !readyCompleter.isCompleted) {
      readyCompleter.complete();
    }
  });

  // 4. Ingest a streaming sequence of A2UI v0.9 BasicCatalog wire messages
  // with ChildList template expansion and CheckRule validation
  final stream = Stream<Map<String, dynamic>>.fromIterable([
    {
      'version': 'v0.9',
      'createSurface': {
        'surfaceId': 'profile_editor',
        'catalogId': basicCatalogId,
      },
    },
    {
      'version': 'v0.9',
      'updateDataModel': {
        'surfaceId': 'profile_editor',
        'path': '/',
        'value': {
          'user': {
            'name': '',
            'role': ['Maintainer'],
            'notifications': true,
          },
          'skills': [
            {'title': 'Dart 3.5+'},
            {'title': 'BlocSignal'},
          ],
        },
      },
    },
    {
      'version': 'v0.9',
      'updateComponents': {
        'surfaceId': 'profile_editor',
        'components': [
          {
            'id': 'root',
            'component': 'Column',
            'children': [
              'header_txt',
              'name_input',
              'role_picker',
              'notify_check',
              'skills_list',
              'save_btn',
            ],
          },
          {
            'id': 'header_txt',
            'component': 'Text',
            'variant': 'h2',
            'text': 'Edit Developer Profile',
          },
          {
            'id': 'name_input',
            'component': 'TextField',
            'label': 'Display Name',
            'value': {'path': '/user/name'},
            'checks': [
              {
                'condition': {
                  'call': 'required',
                  'args': {
                    'value': {'path': '/user/name'},
                  },
                },
                'message': 'Display name is required',
              },
            ],
          },
          {
            'id': 'role_picker',
            'component': 'ChoicePicker',
            'label': 'Team Role',
            'variant': 'mutuallyExclusive',
            'options': [
              {'label': 'Contributor', 'value': 'Contributor'},
              {'label': 'Maintainer', 'value': 'Maintainer'},
            ],
            'value': {'path': '/user/role'},
          },
          {
            'id': 'notify_check',
            'component': 'CheckBox',
            'label': 'Enable Release Notifications',
            'value': {'path': '/user/notifications'},
          },
          {
            'id': 'skills_list',
            'component': 'List',
            'children': {
              'componentId': 'skill_item',
              'path': '/skills',
            },
          },
          {
            'id': 'skill_item',
            'component': 'Text',
            'text': {'path': 'title'},
          },
          {
            'id': 'save_btn',
            'component': 'Button',
            'child': 'save_label',
            'checks': [
              {
                'condition': {
                  'call': 'required',
                  'args': {
                    'value': {'path': '/user/name'},
                  },
                },
                'message': 'Display name is required',
              },
            ],
            'action': {
              'event': {'name': 'save_profile'},
            },
          },
          {
            'id': 'save_label',
            'component': 'Text',
            'text': 'Save',
          },
        ],
      },
    },
  ]);

  bloc.add(IngestStream(stream));

  // Wait for the stream to finish and transition to SurfaceReady
  await readyCompleter.future;
  disposeStateListener();
  print('Active surface: ${bloc.activeSurfaceIdValue}');

  // 5. Inspect NodeResolver template expansion over /skills
  final ready = bloc.value as SurfaceReady;
  final resolver = NodeResolver<ComponentApi>(ready.surface);
  print('Resolved root node state: ${resolver.rootNode.value?.state}');
  resolver.dispose();

  // 6. Mutate form state synchronously in 0ms
  bloc.add(
    const UpdateFormField(
      path: '/user/name',
      value: 'Randal L. Schwartz',
    ),
  );

  // 7. Invoke bidirectional RPC on the agent
  final badge = await bloc.callAgentFunction(
    'profile_editor',
    FunctionCall(
      call: 'verifyDeveloperBadge',
      args: const {'handle': '@RandalSchwartz'},
    ),
  );
  print('Agent RPC result: $badge');

  // 8. Submit an interactive user action back to the agent
  bloc.add(
    const SubmitAction(
      actionName: 'save_profile',
      sourceComponentId: 'save_btn',
      context: {'source': 'cli_example'},
    ),
  );
  await responseCompleter.future;

  // 9. Complete the submission lifecycle
  bloc.add(const CompleteAction(surfaceId: 'profile_editor'));

  await responseSub.cancel();
  await bloc.close();
}
0
likes
160
points
0
downloads

Documentation

API reference

Publisher

verified publisherblocsignal.dev

Weekly Downloads

Pure-Dart A2UI state machine and Generative UI adapter for the BlocSignal ecosystem.

Repository (GitHub)
View/report issues
Contributing

Topics

#bloc #signals #genui #a2ui #state-management

License

MIT (license)

Dependencies

a2ui_core, bloc_signals, json_schema_builder, meta

More

Packages that depend on bloc_signals_genui