flutter_ai_chat_ui 1.0.1
flutter_ai_chat_ui: ^1.0.1 copied to clipboard
A complete, production-ready AI chat UI kit for Flutter. Supports streaming responses, markdown rendering, typing indicators, message reactions, copy/retry actions, image messages, and beautiful bubbl [...]
example/lib/main.dart
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_ai_chat_ui/flutter_ai_chat_ui.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AI Chat UI Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.deepPurple,
useMaterial3: true,
),
darkTheme: ThemeData(
colorSchemeSeed: Colors.deepPurple,
brightness: Brightness.dark,
useMaterial3: true,
),
home: const ChatDemoPage(),
);
}
}
class ChatDemoPage extends StatefulWidget {
const ChatDemoPage({super.key});
@override
State<ChatDemoPage> createState() => _ChatDemoPageState();
}
class _ChatDemoPageState extends State<ChatDemoPage> {
final List<ChatMessage> _messages = [];
bool _isLoading = false;
AiChatTheme _theme = const AiChatTheme();
// Simulated AI user
final _aiUser = const ChatUser.ai(name: 'Claude', id: 'ai');
final _humanUser = const ChatUser.human(name: 'You', id: 'user');
@override
void initState() {
super.initState();
// Welcome message
_messages.add(ChatMessage.system('Chat started'));
_messages.add(ChatMessage.ai(
'Hello! I\'m your AI assistant. Try asking me something!\n\n'
'I can render **markdown**, `inline code`, and even:\n'
'```dart\nvoid main() => runApp(MyApp());\n```',
user: _aiUser,
));
}
Future<void> _handleSend(String text) async {
setState(() {
_messages.add(ChatMessage.user(text, user: _humanUser));
_isLoading = true;
});
// Simulate network delay before streaming starts
await Future.delayed(const Duration(milliseconds: 800));
// Create streaming AI message
final aiMsg = ChatMessage.aiStreaming(user: _aiUser);
setState(() {
_messages.add(aiMsg);
_isLoading = false;
});
// Simulate streaming response
final response = _generateFakeResponse(text);
final chunks = _chunkText(response);
for (final chunk in chunks) {
await Future.delayed(Duration(milliseconds: 20 + Random().nextInt(60)));
aiMsg.appendChunk(chunk);
}
aiMsg.finishStreaming();
}
String _generateFakeResponse(String input) {
final lower = input.toLowerCase();
if (lower.contains('code') || lower.contains('dart') || lower.contains('flutter')) {
return '''Here\'s a simple Flutter widget example:
```dart
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
child: Text(\'Hello, Flutter!\'),
);
}
}
```
This is a basic **StatelessWidget**. For state management, consider using:
- `StatefulWidget` for local state
- **Riverpod** for scalable state
- **Bloc** for enterprise apps''';
}
if (lower.contains('hello') || lower.contains('hi') || lower.contains('helo')) {
return 'Hello! 👋 Great to meet you. I\'m powered by **flutter_ai_chat_ui** — a beautiful, feature-rich chat UI package for Flutter.\n\nWhat would you like to talk about?';
}
if (lower.contains('markdown') || lower.contains('format')) {
return '''I support full **Markdown** rendering!\n\n# Heading 1\n## Heading 2\n### Heading 3\n\n**Bold text** and *italic text* work great.\n\n> Blockquotes look like this.\n\nAnd lists:\n- Item one\n- Item two\n- Item three\n\nInline `code` and full code blocks too:\n```python\nprint("Hello from Python!")\n```''';
}
return 'Thanks for your message! This is a simulated streaming response from **flutter_ai_chat_ui**.\n\nIn a real app, you\'d connect this to:\n- OpenAI GPT-4\n- Anthropic Claude\n- Google Gemini\n- Any streaming AI API\n\nThe package handles *all* the UI — you just pipe in the chunks! 🚀';
}
List<String> _chunkText(String text) {
final chunks = <String>[];
for (int i = 0; i < text.length; i += 3) {
chunks.add(text.substring(i, min(i + 3, text.length)));
}
return chunks;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
children: [
CircleAvatar(
radius: 16,
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
child: Icon(
Icons.auto_awesome,
size: 18,
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
const SizedBox(width: 10),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Claude', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
Text('AI Assistant', style: TextStyle(fontSize: 11)),
],
),
],
),
actions: [
PopupMenuButton<String>(
icon: const Icon(Icons.palette_outlined),
tooltip: 'Change theme',
onSelected: (v) => setState(() {
switch (v) {
case 'clean':
_theme = const AiChatTheme.clean();
case 'dark':
_theme = const AiChatTheme.dark();
case 'whatsapp':
_theme = const AiChatTheme.whatsapp();
}
}),
itemBuilder: (_) => const [
PopupMenuItem(value: 'clean', child: Text('🎨 Clean (Default)')),
PopupMenuItem(value: 'dark', child: Text('🌙 Dark')),
PopupMenuItem(value: 'whatsapp', child: Text('💬 WhatsApp')),
],
),
IconButton(
icon: const Icon(Icons.delete_outline),
tooltip: 'Clear chat',
onPressed: () => setState(() {
_messages.clear();
_messages.add(ChatMessage.system('Chat cleared'));
}),
),
],
),
body: AiChatView(
messages: _messages,
theme: _theme,
isLoading: _isLoading,
currentUserId: 'user',
onSend: _handleSend,
onRetry: (msg) {
// Find the message before this AI message (the user prompt)
final idx = _messages.indexOf(msg);
if (idx > 0 && _messages[idx - 1].isAi == false) {
final userText = _messages[idx - 1].text;
setState(() => _messages.removeRange(idx - 1, _messages.length));
_handleSend(userText);
}
},
onStar: (msg) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(msg.starred ? '⭐ Message starred' : 'Star removed'),
duration: const Duration(seconds: 1),
),
);
},
emptyState: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.chat_bubble_outline, size: 64, color: Colors.grey),
const SizedBox(height: 12),
Text(
'Start a conversation!',
style: Theme.of(context).textTheme.titleMedium,
),
],
),
),
),
);
}
}