ib_conversations 0.0.7-alpha
ib_conversations: ^0.0.7-alpha copied to clipboard
AI Conversations Flutter widget library
example/lib/main.dart
// example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; // Required for Provider
import 'package:file_picker/file_picker.dart'; // Required for PlatformFile
// Import the entire ib_conversations library using its main export file
import 'package:ib_conversations/ib_conversations.dart' as ib_conversations;
// Instantiate and populate the registry once, globally for this example app.
// In a real application, you might manage the lifecycle of this registry
// based on your state management strategy.
final ib_conversations.EmbeddedWidgetRegistry globalWidgetRegistry = ib_conversations.EmbeddedWidgetRegistry();
void main() {
// Register all embedded widget libraries during application startup.
// This makes all defined embedded widgets available to the registry.
ib_conversations.registerAllEmbeddedWidgets(globalWidgetRegistry);
// Optional: Print metadata for debugging or backend reference
print('--- Registered Widget Metadata (from example main.dart) ---');
globalWidgetRegistry.getAllMetadata().forEach((label, metadata) {
print('Label: $label');
print(' Description: ${metadata.description}');
print(' Parameters: ${metadata.parameterSchema}');
});
print('----------------------------------------------------------');
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Provide the EmbeddedWidgetRegistry instance high up in the widget tree
// using Provider. This allows ConversationMessage and its builders
// to access the registry via Provider.of(context).
return Provider<ib_conversations.EmbeddedWidgetRegistry>.value(
value: globalWidgetRegistry, // Provide the instantiated registry
child: MaterialApp(
title: 'ib_conversations Example App',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
// The home page is a simple ChatPage that will use the ConversationWidget
home: ChatPage(userId: 'example_user_123'),
),
);
}
}
/// A simple example page that hosts the ConversationWidget.
class ChatPage extends StatefulWidget {
final String userId; // Example: User ID for the chat
const ChatPage({Key? key, required this.userId}) : super(key: key);
@override
_ChatPageState createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
// --- Implement the SendChatDataToBackend callback ---
// This function sends the main chat input (text/files) to your backend AI service.
// In a real application, this would contain your actual API call logic.
// It receives PlatformFile list directly from the ConversationWidget.
Future<String?> _sendChatDataToBackend({
required String message,
String? flavor,
List<PlatformFile>? files, // Receive PlatformFile list
Map<String, dynamic>? additionalArgs,
}) async {
print('Example App: Sending chat data to backend...');
print(' Message: "$message"');
print(' Flavor: "$flavor"');
print(' Files attached: ${files?.length ?? 0}');
if (files != null) {
for (var file in files) {
print(' - File: ${file.name} (${file.size} bytes)');
// In a real app, you would handle file upload here (e.g., convert to bytes, send via API)
}
}
print(' Additional Args: $additionalArgs');
// TODO: Replace with your actual backend API call logic.
// This is where you would use packages like 'http' or 'dio'
// to send data to your Spring backend or call FlutterFlow generated APIs.
// If using FF APIs, you would convert PlatformFile to FFUploadedFile here.
// Example: Simulate a backend response after a delay
await Future.delayed(Duration(seconds: 2));
final simulatedResponse = "AI received: '$message'.\n\nHere are some options for you:\n```widget:action_button\n{\"label\": \"Tell Me More\", \"commandType\": \"tell_more\", \"args\": {\"topic\": \"response\"}}\n```\n```widget:questionnaire\n{\"questionText\": \"Was this helpful?\", \"selectionType\": \"single\", \"options\": [{\"value\": \"yes\", \"text\": \"Yes\"}, {\"value\": \"no\", \"text\": \"No\"}], \"commandType\": \"feedback\", \"questionId\": \"helpful_001\"}\n```\n```widget:data_display\n{\"text\": \"Current time is ${DateTime.now().toLocal().toShortString()}\"}\n```\n";
print('Example App: Received simulated AI response.');
return simulatedResponse; // Return the simulated AI response string
}
// --- Implement the SendCommandCallback for embedded widgets ---
// This function handles commands triggered by embedded widgets within messages.
// This is where you define what happens when an embedded widget is interacted with.
void _handleEmbeddedWidgetCommand(String commandType, Map<String, dynamic> args) {
print('Example App: Received command from embedded widget:');
print(' Command Type: "$commandType"');
print(' Arguments: $args');
// TODO: Implement logic to interpret commands and trigger actions.
// - Trigger a specific backend API call based on commandType and args.
// - Update application state (using Provider, setState, etc.).
// - Trigger navigation.
// - Show a different widget in a side canvas/modal (if you implement that UI).
// Example: Show a SnackBar based on the command
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Command received: $commandType with args $args'),
duration: Duration(seconds: 3),
),
);
// Example: If a command is meant to trigger a backend action based on embedded widget interaction
// _sendEmbeddedWidgetCommandToBackend(commandType, args);
}
@override
Widget build(BuildContext context) {
// Access theme data from Flutter's Theme
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(title: Text('AI Chat Example')),
body: Column(
children: [
Expanded(
// Use the ConversationWidget from your ib_conversations library
child: ib_conversations.ConversationWidget(
width: double.infinity, // Match parent width
height: double.infinity, // Match parent height
user: widget.userId,
showTextInput: true, // Or pass from page parameters
flavor: 'DEFAULT', // Or pass from page parameters
// Pass theme parameters from the current Flutter Theme
primaryColor: theme.primaryColor,
primaryText: theme.textTheme.bodyMedium?.color, // Use null-safe access
secondaryText: theme.textTheme.bodySmall?.color, // Use null-safe access
alternateColor: theme.dividerColor, // Example mapping
primaryBackground: theme.scaffoldBackgroundColor, // Example mapping
secondaryBackground: theme.cardColor, // Example mapping
errorColor: theme.colorScheme.error, // Example mapping
bodyMediumStyle: theme.textTheme.bodyMedium,
labelMediumStyle: theme.inputDecorationTheme.hintStyle, // Example mapping
bodySmallStyle: theme.textTheme.bodySmall,
// Pass the implemented backend interaction callbacks
sendChatDataToBackend: _sendChatDataToBackend,
handleEmbeddedWidgetCommand: _handleEmbeddedWidgetCommand,
),
),
// Optional: Add a canvas/side panel area here, managed by _ChatPageState state
// This area would display widgets triggered by 'show_canvas_widget' commands
// if (_showCanvas)
// Container(
// width: 300, // Example width
// color: Colors.grey[200],
// child: Center(child: Text('Canvas Area')), // Replace with dynamic widget loading
// // You would dynamically build the widget here based on _canvasWidgetLabel and _canvasWidgetParams
// // using your registry:
// // final registry = Provider.of<ib_conversations.EmbeddedWidgetRegistry>(context);
// // final builder = registry.getBuilder(_canvasWidgetLabel!);
// // if (builder != null) builder(context, _canvasWidgetParams ?? {}, _handleEmbeddedWidgetCommand)
// ),
],
),
);
}
}
// Helper extension for DateTime to match the simulated data display widget
extension on DateTime {
String toShortString() {
return '${this.year}-${this.month.toString().padLeft(2, '0')}-${this.day.toString().padLeft(2, '0')} ${this.hour.toString().padLeft(2, '0')}:${this.minute.toString().padLeft(2, '0')}';
}
}