flutter_ai_chat_ui

pub.dev Flutter License: MIT

A complete, production-ready AI Chat UI Kit for Flutter.
Works with any AI backend — OpenAI, Anthropic Claude, Google Gemini, Ollama, or your own API.

Streaming support — real-time chunk-by-chunk text rendering
Built-in Markdown — bold, italic, code blocks, lists, headings, blockquotes
Typing indicator — animated 3-dot bouncing indicator
Message actions — copy, retry, star (long press to reveal)
Emoji reactions — tap to react, counts shown on bubbles
Multiple themes — clean, dark, WhatsApp-style, or fully custom
Avatar support — initials fallback with custom colors
Image messages — display images in chat
System messages — centered info pills
Scroll to bottom — floating button when scrolled up
Zero external dependencies


Installation

dependencies:
  flutter_ai_chat_ui: ^1.0.0

Quick Start

import 'package:flutter_ai_chat_ui/flutter_ai_chat_ui.dart';

class ChatPage extends StatefulWidget { ... }

class _ChatPageState extends State<ChatPage> {
  final List<ChatMessage> _messages = [];

  @override
  Widget build(BuildContext context) {
    return AiChatView(
      messages: _messages,
      onSend: _handleSend,
    );
  }

  Future<void> _handleSend(String text) async {
    // 1. Add user message
    setState(() => _messages.add(ChatMessage.user(text)));

    // 2. Create streaming AI message
    final aiMsg = ChatMessage.aiStreaming();
    setState(() => _messages.add(aiMsg));

    // 3. Stream chunks from your AI
    await for (final chunk in myAiService.stream(text)) {
      aiMsg.appendChunk(chunk);  // UI updates automatically!
    }

    // 4. Mark complete
    aiMsg.finishStreaming();
  }
}

With OpenAI

import 'package:dart_openai/dart_openai.dart';

Future<void> _handleSend(String text) async {
  setState(() => _messages.add(ChatMessage.user(text)));

  final aiMsg = ChatMessage.aiStreaming();
  setState(() => _messages.add(aiMsg));

  final stream = OpenAI.instance.chat.createStream(
    model: 'gpt-4o',
    messages: [OpenAIChatCompletionChoiceMessageModel(
      role: OpenAIChatMessageRole.user,
      content: [OpenAIChatCompletionChoiceMessageContentItemModel.text(text)],
    )],
  );

  await for (final chunk in stream) {
    final delta = chunk.choices.first.delta.content?.first?.text ?? '';
    aiMsg.appendChunk(delta);
  }
  aiMsg.finishStreaming();
}

With Anthropic Claude

Future<void> _handleSend(String text) async {
  setState(() => _messages.add(ChatMessage.user(text)));

  final aiMsg = ChatMessage.aiStreaming();
  setState(() => _messages.add(aiMsg));

  final request = http.Request('POST', Uri.parse('https://api.anthropic.com/v1/messages'));
  request.headers.addAll({
    'x-api-key': 'YOUR_KEY',
    'anthropic-version': '2023-06-01',
    'content-type': 'application/json',
  });
  request.body = jsonEncode({
    'model': 'claude-sonnet-4-20250514',
    'max_tokens': 1024,
    'stream': true,
    'messages': [{'role': 'user', 'content': text}],
  });

  final response = await http.Client().send(request);
  await for (final line in response.stream.transform(utf8.decoder).transform(const LineSplitter())) {
    if (line.startsWith('data: ')) {
      final data = jsonDecode(line.substring(6));
      if (data['type'] == 'content_block_delta') {
        aiMsg.appendChunk(data['delta']['text'] ?? '');
      }
    }
  }
  aiMsg.finishStreaming();
}

Message Types

ChatMessage.user('Hello!')                  // User text message
ChatMessage.ai('Here is my response')       // AI text message (complete)
ChatMessage.aiStreaming()                    // AI message ready for streaming
ChatMessage.error('Something went wrong')   // Error state bubble
ChatMessage.system('Chat started')          // Centered info pill
ChatMessage.image(imageUrl: url, user: user) // Image message
ChatMessage.code(code: '...', language: 'dart') // Code block message

Theming

// Built-in presets
AiChatView(theme: AiChatTheme.clean())
AiChatView(theme: AiChatTheme.dark())
AiChatView(theme: AiChatTheme.whatsapp())

// Custom
AiChatView(
  theme: AiChatTheme(
    userBubbleColor: Colors.indigo,
    userTextColor: Colors.white,
    aiBubbleColor: Color(0xFFF0F0F0),
    bubbleBorderRadius: 20,
    showTimestamps: true,
    showAiAvatar: true,
    typingText: 'AI is thinking...',
    reactionEmojis: ['👍', '❤️', '🔥', '😂'],
    inputHint: 'Ask me anything...',
  ),
)

Custom Users / Avatars

final myAi = ChatUser.ai(
  name: 'Aria',
  avatarUrl: 'https://example.com/avatar.png',
  avatarColor: Colors.deepPurple,
);

final me = ChatUser.human(
  name: 'Rahul',
  avatarColor: Colors.teal,
);

// Pass to messages
ChatMessage.user('Hi!', user: me)
ChatMessage.aiStreaming(user: myAi)

Reactions & Actions

Long-press any message bubble to reveal actions:

  • Copy — copies text to clipboard
  • Star — bookmark the message
  • Retry — re-generate the AI response
  • React — pick an emoji reaction
AiChatView(
  onRetry: (msg) { /* regenerate response */ },
  onStar: (msg) { /* save to favorites */ },
  onReact: (msg, emoji) { /* handle reaction */ },
)

AiChatView Parameters

Parameter Type Description
messages List<ChatMessage> The message list to display
onSend Future<void> Function(String) Called when user sends a message
theme AiChatTheme Visual customization
isLoading bool Shows typing indicator
currentUserId String Your user's ID (for reactions)
appBar PreferredSizeWidget? Optional AppBar
showInputBar bool Toggle input bar
onRetry void Function(ChatMessage)? Retry callback
onStar void Function(ChatMessage)? Star callback
onReact void Function(ChatMessage, String)? Reaction callback
header Widget? Widget shown above messages
emptyState Widget? Custom empty state widget
autoScroll bool Auto-scroll on new messages

License

MIT © 2026

Libraries

flutter_ai_chat_ui
FlutterAiChatUi — Complete AI Chat UI Kit