ai_chat_assistant 0.1.0
ai_chat_assistant: ^0.1.0 copied to clipboard
A lightweight, plug-and-play Flutter library that provides AI-powered chat assistance with automatic platform detection and intelligent service selection.
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:ai_chat_assistant/ai_chat_assistant.dart';
import 'examples_page.dart';
import 'customization_page.dart';
import 'error_scenarios_page.dart';
import 'simple_test.dart'; // Import simple test
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Load environment variables
try {
await dotenv.load(fileName: '.env');
} catch (e) {
debugPrint('Warning: Could not load .env file: $e');
}
// Run simple test instead
runApp(const SimpleTestApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AI Chat Assistant Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const ExampleHomePage(),
);
}
}
class ExampleHomePage extends StatefulWidget {
const ExampleHomePage({super.key});
@override
State<ExampleHomePage> createState() => _ExampleHomePageState();
}
class _ExampleHomePageState extends State<ExampleHomePage> {
bool _isInitialized = false;
String? _initError;
final List<ChatMessage> _messages = [];
final ScrollController _scrollController = ScrollController();
// Sample chat data for demonstration
final List<ChatMessage> _sampleHistory = [
ChatMessage(
text: 'Hello! I need help with my recent order.',
author: 'customer',
timestamp: DateTime.now().subtract(const Duration(minutes: 5)),
),
ChatMessage(
text:
'Hi there! I\'d be happy to help you with your order. Could you please provide your order number?',
author: 'agent',
timestamp: DateTime.now().subtract(const Duration(minutes: 4)),
),
ChatMessage(
text: 'Sure, it\'s ORDER-12345',
author: 'customer',
timestamp: DateTime.now().subtract(const Duration(minutes: 3)),
),
];
@override
void initState() {
super.initState();
_initializeAssistant();
}
Future<void> _initializeAssistant() async {
try {
// Get API key from environment or use a placeholder
final apiKey = dotenv.env['GEMINI_API_KEY'] ?? '';
if (apiKey.isEmpty || apiKey == 'your_api_key_here') {
setState(() {
_initError = 'Please set GEMINI_API_KEY in .env file';
_isInitialized = false;
});
return;
}
// Initialize the library with sample history
await AIChatAssistant.initialize(
apiKey: apiKey,
initialHistory: _sampleHistory,
autoGenerateSuggestions: true,
onError: (error) {
print('π₯ AI Error: $error'); // Debug log
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: $error'),
backgroundColor: Colors.red,
),
);
}
},
);
print('π― AI Chat Assistant initialized successfully'); // Debug log
print(
'π― Sample history has ${_sampleHistory.length} messages'); // Debug log
setState(() {
_isInitialized = true;
_messages.addAll(_sampleHistory);
});
} catch (e) {
setState(() {
_initError = e.toString();
_isInitialized = false;
});
}
}
Future<void> _handleSendMessage(String text) async {
if (text.trim().isEmpty) return;
final message = ChatMessage(
text: text,
author: 'agent',
timestamp: DateTime.now(),
);
setState(() {
_messages.add(message);
});
// Scroll to bottom
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
}
});
// Add message to assistant
await AIChatAssistant.addMessage(message);
}
void _handleSuggestionTap(String suggestion) {
print('π― Suggestion tapped: $suggestion'); // Debug log
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Selected: $suggestion'),
duration: const Duration(seconds: 2),
),
);
}
@override
void dispose() {
_scrollController.dispose();
AIChatAssistant.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AI Chat Assistant Example'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
actions: [
PopupMenuButton<String>(
onSelected: (value) {
Widget page;
switch (value) {
case 'basic':
page = const BasicUsageExample();
break;
case 'custom':
page = const CustomizationExample();
break;
case 'errors':
page = const ErrorScenariosExample();
break;
default:
return;
}
Navigator.push(
context,
MaterialPageRoute(builder: (context) => page),
);
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'basic',
child: Text('Basic Usage'),
),
const PopupMenuItem(
value: 'custom',
child: Text('Customization'),
),
const PopupMenuItem(
value: 'errors',
child: Text('Error Scenarios'),
),
],
),
],
),
body: _buildBody(),
);
}
Widget _buildBody() {
if (_initError != null) {
return Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
size: 64,
color: Colors.red,
),
const SizedBox(height: 16),
const Text(
'Initialization Error',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
_initError!,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.red),
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () {
setState(() {
_initError = null;
});
_initializeAssistant();
},
icon: const Icon(Icons.refresh),
label: const Text('Retry'),
),
],
),
),
);
}
if (!_isInitialized) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Initializing AI Chat Assistant...'),
],
),
);
}
return Column(
children: [
// Chat messages
Expanded(
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.all(16),
itemCount: _messages.length,
itemBuilder: (context, index) {
final message = _messages[index];
final isAgent = message.author == 'agent';
return Align(
alignment:
isAgent ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 10,
),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.7,
),
decoration: BoxDecoration(
color: isAgent ? Colors.blue[100] : Colors.grey[300],
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
message.author,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.grey[700],
),
),
const SizedBox(height: 4),
Text(
message.text,
style: const TextStyle(fontSize: 16),
),
],
),
),
);
},
),
),
// AI Suggestions
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey[100],
border: Border(
top: BorderSide(color: Colors.grey[300]!),
),
),
child: Column(
children: [
// Manual trigger button for testing
ElevatedButton(
onPressed: () async {
print('π― Manual suggestion generation triggered');
try {
final suggestions =
await AIChatAssistant.generateSuggestions();
print(
'π― Generated ${suggestions.length} suggestions: $suggestions');
} catch (e) {
print('π₯ Manual generation error: $e');
}
},
child: const Text('Generate Suggestions'),
),
const SizedBox(height: 8),
AISuggestionWidget(
onSuggestionTap: _handleSuggestionTap,
maxSuggestions: 5,
),
],
),
),
// Text Input
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 4,
offset: const Offset(0, -2),
),
],
),
child: AITextInputWidget(
onSend: _handleSendMessage,
decoration: InputDecoration(
hintText: 'Type your message...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
),
),
),
],
);
}
}