ib_conversations 0.0.14-alpha copy "ib_conversations: ^0.0.14-alpha" to clipboard
ib_conversations: ^0.0.14-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';
// import 'package:file_picker/file_picker.dart'; // No longer directly used by ChatPage for sending
// import 'package:http/http.dart' as http; // No longer directly used by ChatPage
// import 'dart:io'; // No longer directly used by ChatPage
// import 'package:path/path.dart' as path; // No longer directly used by ChatPage
// import 'dart:convert'; // No longer directly used by ChatPage
// import 'package:intl/intl.dart'; // No longer directly used by ChatPage

import 'package:flutter_dotenv/flutter_dotenv.dart';

import 'package:ib_conversations/ib_conversations.dart' as ib_conversations;

final ib_conversations.EmbeddedWidgetRegistry globalWidgetRegistry =
ib_conversations.EmbeddedWidgetRegistry();

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await dotenv.load(fileName: ".env");

  ib_conversations.registerAllEmbeddedWidgets(globalWidgetRegistry);

  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(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // The Provider for EmbeddedWidgetRegistry is still needed if ConversationMessage
    // or other deep widgets access it via Provider.of<...>(context).
    // ConversationWidget itself creates its own registry and provides it down its tree.
    // If globalWidgetRegistry is intended for other parts of a larger example app,
    // it can stay. Otherwise, if only ConversationWidget uses a registry, this
    // top-level provider might become redundant depending on how ConversationWidget
    // exposes its internal registry or if ConversationMessage can get it from there.
    // For now, assuming ConversationWidget will provide its own registry internally.
    // Thus, this top-level Provider<EmbeddedWidgetRegistry> might not be strictly necessary
    // for ConversationWidget itself, but it doesn't hurt.
    return Provider<ib_conversations.EmbeddedWidgetRegistry>.value(
      value: globalWidgetRegistry, // This registry is registered but might not be the one ConversationWidget uses.
      child: MaterialApp(
        title: 'ib_conversations Example App',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
          brightness: Brightness.light, // Example: Light theme
          // Add other theme properties if needed by ConversationWidget's theme params
        ),
        darkTheme: ThemeData(
          brightness: Brightness.dark,
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
          // ... define dark theme properties
        ),
        themeMode: ThemeMode.system, // Or ThemeMode.light / ThemeMode.dark
        home: const ChatPage(),
      ),
    );
  }
}

/// A simple example page that hosts the ConversationWidget.
class ChatPage extends StatefulWidget {
  const ChatPage({Key? key}) : super(key: key);

  @override
  _ChatPageState createState() => _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
  final String _aiRestBaseUrl =
      dotenv.env['API_URL'] ?? 'http://localhost:8080/api/v1/ai'; // More specific example

  final String _aiWsBaseUrl =
      dotenv.env['WEBSOCKET_URL'] ?? 'ws://localhost:8080'; // Base WS URL

  // Placeholder for the auth token. In a real app, get this from your auth provider.
  final String _authToken = dotenv.env['AUTH_TOKEN'] ?? "YOUR_AUTH_TOKEN_HERE_OR_FROM_DOTENV";
  String _currentConversationId = ""; // Store the active conversation ID

  @override
  void initState() {
    super.initState();
    if (_authToken == "YOUR_AUTH_TOKEN_HERE_OR_FROM_DOTENV") {
      print("************************************************************************************");
      print("WARNING: Using a placeholder auth token. Ensure your .env file is set up with a valid AUTH_TOKEN.");
      print("************************************************************************************");
    }
    print("ChatPage using AI REST Base URL: $_aiRestBaseUrl");
    print("ChatPage using AI WebSocket Base URL: $_aiWsBaseUrl");
  }

  void _handleEmbeddedWidgetCommand(
      String commandType,
      Map<String, dynamic> args,
      ) {
    print('--- Example App: Received command from embedded widget ---');
    print(' Command Type: "$commandType"');
    print(' Arguments: $args');
    print('--- End Example App: Received command from embedded widget ---');

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Command received: $commandType with args $args'),
        duration: const Duration(seconds: 3),
      ),
    );
  }

  void _onNewConversationIdGenerated(String newConversationId) {
    print("Example App: New Conversation ID generated/confirmed: $newConversationId");
    if (mounted) {
      setState(() {
        _currentConversationId = newConversationId;
      });
    }
  }

  void _onErrorOccurred(String error) {
    print("Example App: Error occurred in ConversationWidget: $error");
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Error: $error', style: TextStyle(color: Theme.of(context).colorScheme.onError)),
          backgroundColor: Theme.of(context).colorScheme.errorContainer,
          duration: const Duration(seconds: 5),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final isDarkMode = theme.brightness == Brightness.dark;

    // Example of deriving text colors based on theme brightness
    final primaryTextColor = isDarkMode ? Colors.white70 : Colors.black87;
    final secondaryTextColor = isDarkMode ? Colors.white60 : Colors.black54;

    return Scaffold(
      appBar: AppBar(title: const Text('AI Chat Example')),
      body: Column(
        children: [
          if (_currentConversationId.isNotEmpty)
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text("Conversation ID: $_currentConversationId", style: theme.textTheme.labelSmall),
            ),
          Expanded(
            child: ib_conversations.ConversationWidget(
              // Key parameters for the new API
              token: _authToken,
              // flavor: 'DEFAULT', // Or any flavor your backend supports
              flavor: 'DOCTOR_VISIT_STANDARD', // Or any flavor your backend supports
              aiRestBaseUrl: _aiRestBaseUrl,
              aiWsBaseUrl: _aiWsBaseUrl,
              initialConversationId: null, // Start fresh or pass a saved one
              onNewConversationIdGenerated: _onNewConversationIdGenerated,
              onErrorOccurred: _onErrorOccurred,
              handleEmbeddedWidgetCommand: _handleEmbeddedWidgetCommand,

              // UI parameters
              width: double.infinity,
              height: double.infinity,
              showTextInput: true,

              // Theming (passed directly from current theme)
              primaryColor: theme.primaryColor,
              primaryText: primaryTextColor, // Use derived color
              secondaryText: secondaryTextColor, // Use derived color
              alternateColor: theme.dividerColor,
              primaryBackground: theme.scaffoldBackgroundColor,
              secondaryBackground: theme.cardColor,
              errorColor: theme.colorScheme.error,
              bodyMediumStyle: theme.textTheme.bodyMedium?.copyWith(color: primaryTextColor),
              labelMediumStyle: theme.inputDecorationTheme.hintStyle ?? theme.textTheme.labelMedium?.copyWith(color: secondaryTextColor),
              bodySmallStyle: theme.textTheme.bodySmall?.copyWith(color: secondaryTextColor),
            ),
          ),
        ],
      ),
    );
  }
}