genui_a2ui 0.6.0
genui_a2ui: ^0.6.0 copied to clipboard
Integration package for genui and A2UI Streaming UI Protocol.
genui_a2ui #
An integration package for genui and the A2UI Streaming UI Protocol. This package allows Flutter applications to connect to an A2A (Agent-to-Agent) server and render dynamic user interfaces generated by an AI agent using the genui framework.
Features #
- A2A Server Connection: Establishes and manages a WebSocket connection to any server implementing the A2A protocol.
- A2UI Message Processing: Receives and parses A2UI messages (like
SurfaceUpdate,DataModelUpdate,BeginRendering) from the A2A stream. - Dynamic UI Rendering: Integrates seamlessly with
genui'sGenUiSurfaceto render UIs based on the received A2UI messages. - Content Generator Implementation: Provides
A2uiContentGenerator, a specializedContentGeneratorforgenui'sGenUiConversationto handle the A2A communication flow. - Event Handling: Captures UI events from
genuiand sends them back to the A2A server as A2A messages. - Stateful Conversation: Maintains the conversation context (
taskId,contextId) with the A2A server.
Getting Started #
Prerequisites #
- A Flutter project.
- An A2A server endpoint that implements the A2UI Streaming UI Protocol.
Installation #
Add the following to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
genui: ^0.5.1 # Or the latest version
genui_a2ui: ^0.5.1 # Or the latest version
a2a: ^3.1.0 # Or the latest version
Then run flutter pub get.
Basic Usage #
- Initialize
A2uiMessageProcessor: Set upA2uiMessageProcessorwith your widgetCatalog. - Create
A2uiContentGenerator: InstantiateA2uiContentGenerator, providing the A2A serverUri. - Create
GenUiConversation: Pass theA2uiContentGeneratorto theGenUiConversation. - Render with
GenUiSurface: UseGenUiSurfacewidgets in your UI to display the agent-generated content. - Send Messages: Use
GenUiConversation.sendRequestto send user input to the agent.
import 'package:flutter/material.dart';
import 'package:genui/genui.dart';
import 'package:genui_a2ui/genui_a2ui.dart';
import 'package:logging/logging.dart';
void main() {
// Setup logging
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
if (record.error != null) {
print(record.error);
}
if (record.stackTrace != null) {
print(record.stackTrace);
}
});
runApp(const GenUIExampleApp());
}
class GenUIExampleApp extends StatelessWidget {
const GenUIExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'A2UI Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const ChatScreen(),
);
}
}
class ChatScreen extends StatefulWidget {
const ChatScreen({super.key});
@override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final TextEditingController _textController = TextEditingController();
final A2uiMessageProcessor _a2uiMessageProcessor =
A2uiMessageProcessor(catalog: CoreCatalogItems.asCatalog());
late final A2uiContentGenerator _contentGenerator;
late final GenUiConversation _uiAgent;
final List<ChatMessage> _messages = [];
@override
void initState() {
super.initState();
_contentGenerator = A2uiContentGenerator(
serverUrl: Uri.parse('http://localhost:8080'), // Replace with your A2A server URL
);
_uiAgent = GenUiConversation(
contentGenerator: _contentGenerator,
a2uiMessageProcessor: _a2uiMessageProcessor,
);
// Listen for text responses from the agent
_contentGenerator.textResponseStream.listen((String text) {
setState(() {
_messages.insert(0, AgentMessage.text(text));
});
});
// Listen for errors
_contentGenerator.errorStream.listen((ContentGeneratorError error) {
print('Error from ContentGenerator: ${error.error}');
// Optionally show error to the user
});
}
@override
void dispose() {
_textController.dispose();
_uiAgent.dispose();
_a2uiMessageProcessor.dispose();
_contentGenerator.dispose();
super.dispose();
}
void _handleSubmitted(String text) {
if (text.isEmpty) return;
_textController.clear();
final message = UserMessage.text(text);
setState(() {
_messages.insert(0, message);
});
_uiAgent.sendRequest(message);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('A2UI Example'),
),
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, int index) =>
_buildMessage(_messages[index]),
itemCount: _messages.length,
),
),
const Divider(height: 1.0),
Container(
decoration: BoxDecoration(color: Theme.of(context).cardColor),
child: _buildTextComposer(),
),
// Surface for the main AI-generated UI
SizedBox(
height: 300,
child: GenUiSurface(
host: _a2uiMessageProcessor,
surfaceId: 'main_surface',
)),
],
),
);
}
Widget _buildMessage(ChatMessage message) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: const EdgeInsets.only(right: 16.0),
child: CircleAvatar(child: Text(message is UserMessage ? 'U' : 'A')),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(message is UserMessage ? 'User' : 'Agent',
style: const TextStyle(fontWeight: FontWeight.bold)),
Container(
margin: const EdgeInsets.only(top: 5.0),
child: Text(message.parts.whereType<TextPart>().map((e) => e.text).join('\n')),
),
],
),
),
],
),
);
}
Widget _buildTextComposer() {
return IconTheme(
data: IconThemeData(color: Theme.of(context).colorScheme.secondary),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
children: <Widget>[
Flexible(
child: TextField(
controller: _textController,
onSubmitted: _handleSubmitted,
decoration:
const InputDecoration.collapsed(hintText: 'Send a message'),
),
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 4.0),
child: IconButton(
icon: const Icon(Icons.send),
onPressed: () => _handleSubmitted(_textController.text)),
),
],
),
),
);
}
}
Key Components #
A2uiContentGenerator: ImplementsContentGenerator. Manages the connection to the A2A server and processes incoming A2UI messages, updating theA2uiMessageProcessor.A2uiAgentConnector: Handles the low-level WebSocket communication with the A2A server, including sending messages and parsing stream events.AgentCard: A data class holding metadata about the connected AI agent.
Example App #
See the example/ directory for a more complete application demonstrating the usage of this package.