ib_conversations 0.0.6-alpha copy "ib_conversations: ^0.0.6-alpha" to clipboard
ib_conversations: ^0.0.6-alpha copied to clipboard

AI Conversations Flutter widget library

InnoBoutique AI Conversations library #

ib_conversations is a pure Flutter library for embedding interactive widgets directly within conversational user interfaces, designed to enhance AI chatbot experiences.

Overview #

The ib_conversations library provides a flexible framework for transforming static AI text responses into dynamic, interactive experiences. By leveraging a custom Markdown syntax and a pluggable widget registry, you can embed custom Flutter widgets (like buttons, forms, charts, or task cards) directly within the AI's message bubbles. This is particularly useful for building sophisticated conversational AI applications in domains like healthcare, education, or task management, where structured interaction and data exchange are crucial.

The library is designed to be pure Flutter, maximizing reusability, while providing clear integration points for consuming applications, including low-code platforms like FlutterFlow.

Features #

  • Custom Markdown Syntax: Define special fenced code blocks (```widget:<label>) to indicate where embedded widgets should be rendered.
  • JSON Parameter Passing: Embed JSON data within widget blocks to configure individual widget instances dynamically.
  • Pluggable Widget Registry: Register different types of embedded widgets and their metadata in a central registry.
  • Modular Widget Libraries: Organize embedded widgets into logical libraries for better code management.
  • Command Sending: Embedded widgets can send structured commands back to the parent application logic via a dedicated callback.
  • Pure Flutter Core: The core library is written in pure Flutter, ensuring reusability outside of specific platforms like FlutterFlow.
  • Integration with Chat UIs: Designed to integrate with existing chat UI components (like SfAIAssistView) by providing custom message content builders.

Installation #

To use this library in your Flutter project (including FlutterFlow):

  1. Add the dependency to your pubspec.yaml file:
    dependencies:
    flutter:
    sdk: flutter

    ib_conversations:
    path: /path/to/your/ib_conversations/library # For local development
    # OR
    # ib_conversations: ^1.0.0 # If published to pub.dev or a private repository

    # Ensure you have the necessary peer dependencies:
    flutter_markdown: ^latest_version
    markdown: ^latest_version
    provider: ^latest_version
    image_picker: ^latest_version # If using widgets that need image picking
    http: ^latest_version # If using widgets that make HTTP calls
    flutter_sound: ^latest_version # If using audio recording features
    permission_handler: ^latest_version # For permissions
    path_provider: ^latest_version # For temporary files
    web_socket_channel: ^latest_version # If using WebSocket streaming

    # ... rest of your pubspec.yaml

  2. Run flutter pub get in your project's terminal.

Usage #

Using the ib_conversations library involves setting up the widget registry and integrating the main ConversationWidget into your chat UI.

1. Registering Embedded Widgets #

You need to instantiate the EmbeddedWidgetRegistry and register the widget libraries you want to make available. This should be done once, high up in your application's widget tree (e.g., in your main.dart or a top-level widget in FlutterFlow).

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:ib_conversations/ib_conversations.dart' as ib_conversations;

// Instantiate and populate the registry once
final ib_conversations.EmbeddedWidgetRegistry globalWidgetRegistry = ib_conversations.EmbeddedWidgetRegistry();

void main() {
// Register all widget libraries during app startup
ib_conversations.registerAllEmbeddedWidgets(globalWidgetRegistry);

// Optional: You can also register individual widgets or libraries here
// ib_conversations.registerCoreWidgets(globalWidgetRegistry);
// ib_conversations.registerHealthWidgets(globalWidgetRegistry);
// ib_conversations.registerEduWidgets(globalWidgetRegistry);

runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Provide the registry instance at the top of your app using Provider
return Provider<ib_conversations.EmbeddedWidgetRegistry>.value(
value: globalWidgetRegistry,
child: MaterialApp(
title: 'My AI Chat App',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ChatPage(), // Your page containing the ConversationWidget
),
);
}
}

2. Integrating the ConversationWidget #

Place the ConversationWidget in your chat UI where you want the conversation messages to be displayed. This widget requires callbacks for sending user input to the backend and handling commands from embedded widgets.

import 'package:flutter/material.dart';
import 'package:ib_conversations/ib_conversations.dart' as ib_conversations;
// Assuming you have SfAIAssistView imported or available
import 'package:syncfusion_flutter_chat/assist_view.dart';
// Assuming your backend API calls are available (e.g., FlutterFlow generated)
// import '../../backend/api_requests/api_calls.dart';

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> {
// Manage the list of messages for SfAIAssistView
List<AssistMessage> _messages = [];

// --- Implement the SendChatDataToBackend callback ---
// This function sends the main chat input (text/files) to your backend AI service.
Future<String?> _sendChatDataToBackend({
required String message,
String? flavor,
List<PlatformFile>? files,
Map<String, dynamic>? additionalArgs,
}) async {
// TODO: Implement your actual backend API call logic here.
// Use FlutterFlow's generated API calls or your custom backend client.
print('Sending to backend: Message="$message", Flavor="$flavor", Files=${files?.length ?? 0}');

try {  
  // Example: Using a FlutterFlow generated API call (requires FF code)  
  /\*  
  final apiResult \= await ColabHealthAIApiGroup.chatCall.call(  
    message: message,  
    flavor: flavor,  
    filesList: files, // Pass PlatformFile list  
    // Pass additional args like userId, conversationId  
    // additionalArgs: {'userId': widget.userId},  
  );

  if (apiResult.succeeded && apiResult.jsonBody \!= null) {  
    final aiResponse \= ColabHealthAIApiGroup.chatCall.response(apiResult.jsonBody);  
    return aiResponse?.toString(); // Return the AI response string  
  } else {  
    print('API call failed: ${apiResult.statusCode}');  
    return 'System: Error contacting AI.'; // Return an error message  
  }  
  \*/

  // Example: Placeholder simulation  
  await Future.delayed(Duration(seconds: 1));  
  return "AI received: '$message'. This is a simulated response with a \[\`\`\`widget:action\_button\\n{\\"label\\": \\"More Info\\", \\"commandType\\": \\"get\_info\\", \\"args\\": {\\"query\\": \\"$message\\"}}\\n\`\`\`\].";

} catch (e) {  
  print('Error sending to backend: $e');  
  return 'System: An error occurred.'; // Return an error message  
}  

}

// --- Implement the SendCommandCallback for embedded widgets ---
// This function handles commands triggered by embedded widgets within messages.
void _handleEmbeddedWidgetCommand(String commandType, Map<String, dynamic> args) {
// TODO: Implement logic to interpret commands and trigger actions.
// This is where you define what happens when an embedded widget is interacted with.
print('Received command from embedded widget: Type="$commandType", Args=$args');

// Example: Trigger a specific action based on the command type  
if (commandType \== 'submit\_rating') {  
  final questionId \= args\['questionId'\];  
  final answer \= args\['answer'\];  
  print('User submitted rating for $questionId: $answer');  
  // TODO: Send this data to your backend or update state  
  // \_sendRatingToBackend(questionId, answer);  
} else if (commandType \== 'confirm\_exercise\_completion') {  
   final exerciseId \= args\['exerciseId'\];  
   print('User confirmed completion for exercise $exerciseId');  
   // TODO: Mark task as complete in backend  
   // \_markTaskComplete(exerciseId);  
} else if (commandType \== 'show\_canvas\_widget') {  
   // Example: Update state to show a widget in a side canvas  
   // This requires your page layout to have a canvas area  
   // setState(() {  
   //    \_showCanvas \= true;  
   //    \_canvasWidgetLabel \= args\['widgetLabel'\];  
   //    \_canvasWidgetParams \= args\['params'\];  
   // });  
}  
// Add more command handling logic here...  

}

@override
Widget build(BuildContext context) {
// Access theme data from Flutter's Theme (or FlutterFlowTheme in FF project)
final theme = Theme.of(context);
// In a FlutterFlow Custom Widget, you would get FF theme like this:
// final FlutterFlowTheme ffTheme = FlutterFlowTheme.of(context);

return Scaffold(  
  appBar: AppBar(title: Text('AI Chat')),  
  body: Column(  
    children: \[  
      Expanded(  
        // Use the ConversationWidget from your 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 context/FFTheme  
          primaryColor: theme.primaryColor,  
          primaryText: theme.textTheme.bodyMedium\!.color, // Example mapping  
          secondaryText: theme.textTheme.bodySmall\!.color, // Example mapping  
          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  
      // 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)  
      //   ),  
    \],  
  ),  
);  

}
}

3. Implementing Embedded Widgets and Libraries #

Create the .dart files for your individual embedded widgets (e.g., _questionnaire_widget.dart, _monitoring_task_widget.dart, _log_meal_task_widget.dart) and their corresponding WidgetEntry definitions within the appropriate subdirectories (health/questionnaire/, health/monitoring/, health/meals/, edu/, etc.).

Create the registration files for each library (e.g., questionnaire_widgets_registration.dart, monitoring_widgets_registration.dart, meals_widgets_registration.dart, edu_widgets_registration.dart) that import the WidgetEntry definitions from their respective widget files and call registry.registerWidget().

Create the global_widgets_register.dart file that imports and calls the registration functions for each top-level library.

4. Exporting from lib/ib_conversations.dart #

Ensure your main library export file (lib/ib_conversations.dart) exports all the necessary public components:

// lib/ib_conversations.dart

// Export the main chat widget.
export 'src/widgets/conversation_widget.dart';

// Export the widget used to render individual conversation messages with embedded widgets.
export 'src/widgets/conversation_message.dart';

// Export the global function to register all embedded widgets with the registry.
export 'src/widgets/global_widgets_register.dart';

// Export the typedefs defining the interfaces for backend communication.
export 'src/backend/chat_backend_interface.dart';

// Export the main embedded widget registry class.
export 'src/registry/embedded_widget_registry.dart';

// Export the embedded widget metadata class.
export 'src/registry/embedded_widget_metadata.dart';

// Export utility typedefs (like WidgetBuildFunction)
export 'src/util/typedefs.dart';

// Export the Markdown parsing classes as they are needed for custom syntax setup
export 'src/markdown/widget_block_syntax.dart';
export 'src/markdown/widget_element_builder.dart';

// Optionally export individual public embedded widget classes if you intend to use them directly
// export 'src/widgets/health/questionnaire/questionnaire_widget.dart';
// export 'src/widgets/health/monitoring/monitoring_task_widget.dart';
// export 'src/widgets/health/meals/log_meal_task_widget.dart';
// export 'src/widgets/core/_action_button_widget.dart'; // If public
// ... export other public widget classes

By following these steps and using this structure, your ib_conversations library will be well-organized, reusable, and ready to be consumed by your FlutterFlow project.pure Flutter library for embedding interactive widgets directly within conversational user interfaces, designed to enhance AI chatbot experiences.