genui_button 0.0.5
genui_button: ^0.0.5 copied to clipboard
A GenUI compatible button widget package. Provides a GenUIButton catalog item for GenUI agents to produce interactive UI.
import 'package:flutter/material.dart';
import 'package:genui/genui.dart';
import 'package:genui_button/genui_button.dart';
// 1. Initialize the GenUI Message Processor equipped with our custom button:
final a2uiMessageProcessor = A2uiMessageProcessor(
catalogs: [
CoreCatalogItems.asCatalog().copyWith([genUiButton]),
],
);
// 2. Set up the AI Content Generator (e.g., Firebase, Gemini, etc.)
// Provide it with the tools/system instructions to tell the AI how to use it.
/*
final contentGenerator = FirebaseAiContentGenerator(
systemInstruction: 'Use GenUIButton to provide user actions with custom styling.',
additionalTools: a2uiMessageProcessor.getTools(),
);
*/
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GenUIButton Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const ExamplePage(),
);
}
}
class ExamplePage extends StatelessWidget {
const ExamplePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('GenUIButton Example')),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Here are some custom UI components from the catalog:',
),
const SizedBox(height: 20),
// Button 1: Custom Colored Button
Builder(
builder: (ctx) {
return genUiButton.widgetBuilder(
_MockContext(
context: ctx,
data: {
'label': 'Custom Colored Button',
'type': 'filled',
'backgroundColor': '#FF5733',
'foregroundColor': '#FFFFFF',
'borderRadius': 8.0,
'elevation': 4.0,
'padding': 16.0,
'fontSize': 18.0,
'tooltip': 'I am a custom colored button',
},
id: 'mock_btn_1',
),
);
},
),
const SizedBox(height: 20),
// Button 2: Theme Fallback Button (No custom colors)
Builder(
builder: (ctx) {
return genUiButton.widgetBuilder(
_MockContext(
context: ctx,
data: {
'label': 'Theme Fallback Button',
'type': 'filled',
'borderRadius': 8.0,
'elevation': 2.0,
'padding': 16.0,
'fontSize': 16.0,
'tooltip': 'I use the app theme primary color',
},
id: 'mock_btn_2',
),
);
},
),
const SizedBox(height: 20),
// Button 3: Theme Fallback Outlined Button
Builder(
builder: (ctx) {
return genUiButton.widgetBuilder(
_MockContext(
context: ctx,
data: {
'label': 'Outlined Theme Button',
'type': 'outlined',
'borderRadius': 12.0,
'padding': 16.0,
'fontSize': 16.0,
'tooltip': 'I use the app theme outlne color',
},
id: 'mock_btn_3',
),
);
},
),
const SizedBox(height: 20),
// Button 4: Full Width Theme Fallback Button
Builder(
builder: (ctx) {
return genUiButton.widgetBuilder(
_MockContext(
context: ctx,
data: {
'label': 'Full Width Button',
'type': 'elevated',
'borderRadius': 16.0,
'elevation': 4.0,
'padding': 20.0,
'fontSize': 18.0,
'fullWidth': true,
'tooltip': 'I stretch to fill available width',
},
id: 'mock_btn_4',
),
);
},
),
const SizedBox(height: 20),
// Button 5: Realistic Action with Confirmation
Builder(
builder: (ctx) {
return genUiButton.widgetBuilder(
_MockContext(
context: ctx,
data: {
'label': 'Destructive Action',
'type': 'filled',
'backgroundColor': '#D32F2F',
'action': 'delete_item',
'confirmation': {
'title': 'Delete Item?',
'message': 'This action cannot be undone.',
'confirmText': 'Delete',
'cancelText': 'Keep',
},
},
id: 'mock_btn_5',
),
);
},
),
const SizedBox(height: 20),
// Button 6: Context Mapping Demo
Builder(
builder: (ctx) {
return genUiButton.widgetBuilder(
_MockContext(
context: ctx,
data: {
'label': 'Send with Context',
'type': 'outlined',
'action': 'submit_form',
'contextMapping': {
'user_email': 'session/email',
'form_id': 'current_form_id',
},
'data': {
'source': 'example_app',
},
},
id: 'mock_btn_6',
),
);
},
),
const SizedBox(height: 20),
// Button 7: Advanced Confirmation Logic Demo
// This is a new feature in v0.0.5 allowing developers to:
// 1. Conditionally show dialogs (isEnabled)
// 2. Trigger different actions for Confirm vs Cancel
Builder(
builder: (ctx) {
return genUiButton.widgetBuilder(
_MockContext(
context: ctx,
data: {
'label': 'Sophisticated Delete',
'type': 'filled',
'backgroundColor': '#212121',
'action': 'standard_delete', // Primary action
'data': {'userId': 'user_99'},
'confirmation': {
'isEnabled': true, // Native dialog will show
'title': 'Critical Operation',
'message': 'Are you absolutely sure you want to wipe this?',
'confirmText': 'Yes, Wipe Everything',
'cancelText': 'No, Save Me!',
// Custom action string specifically for the "Confirm" click
'onConfirmAction': 'ACTION_WIPE_CONFIRMED',
// Custom action string specifically for the "Cancel" click
'onCancelAction': 'ACTION_ABORTED_BY_USER',
},
},
id: 'mock_btn_7',
),
);
},
),
const SizedBox(height: 40),
const Text(
'Note: Click buttons to see Action Events in the Snackbar!',
style: TextStyle(fontStyle: FontStyle.italic, color: Colors.grey),
),
],
),
),
),
);
}
}
class _MockContext implements CatalogItemContext {
@override
final Object data;
@override
final String id;
final BuildContext context;
_MockContext({required this.context, required this.data, required this.id});
@override
DispatchEventCallback get dispatchEvent => (UiEvent event) {
if (event is UserActionEvent) {
// IN A REAL APP:
// You would handle these actions in your GenUiSurface onEvent handler.
// This allows the AI to trigger native Flutter logic.
final String message = 'Action: ${event.name}\nData: ${event.context}';
debugPrint('GenUI Event Triggered: $message');
// Show a snackbar to prove the event was caught and handled
ScaffoldMessenger.of(buildContext).clearSnackBars();
ScaffoldMessenger.of(buildContext).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: event.name.contains('CONFIRMED')
? Colors.green
: event.name.contains('ABORTED')
? Colors.orange
: Colors.deepPurple,
),
);
}
};
@override
ChildBuilderCallback get buildChild =>
(String id, [DataContext? dataContext]) => const SizedBox.shrink();
@override
BuildContext get buildContext => context;
@override
late final DataContext dataContext = _MockDataContext();
@override
GetComponentCallback get getComponent =>
(String id) => null;
@override
String get surfaceId => 'mock_surface';
}
class _MockDataContext extends DataContext {
_MockDataContext() : super(DataModel(), '') {
update(DataPath('session/email'), 'user@example.com');
update(DataPath('current_form_id'), 'form_123');
}
}