chat_ui_flex 0.2.1 copy "chat_ui_flex: ^0.2.1" to clipboard
chat_ui_flex: ^0.2.1 copied to clipboard

A flexible and customizable chat UI toolkit for Flutter.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:chat_ui_flex/chat_ui_flex.dart';

void main() {
  runApp(const ChatDemoApp());
}

class ChatDemoApp extends StatefulWidget {
  const ChatDemoApp({super.key});

  @override
  State<ChatDemoApp> createState() => _ChatDemoAppState();
}

class _ChatDemoAppState extends State<ChatDemoApp> {
  bool _isDarkMode = false;

  void _toggleTheme() {
    setState(() {
      _isDarkMode = !_isDarkMode;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Chat UI Flex Demo',
      debugShowCheckedModeBanner: false,
      theme: AppTheme.lightTheme,
      darkTheme: AppTheme.darkTheme,
      themeMode: _isDarkMode ? ThemeMode.dark : ThemeMode.light,
      home: HomeScreen(onThemeToggle: _toggleTheme),
    );
  }
}

class HomeScreen extends StatelessWidget {
  final VoidCallback onThemeToggle;

  const HomeScreen({super.key, required this.onThemeToggle});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Chat UI Flex Examples'),
        actions: [
          IconButton(
            icon: Icon(
              Theme.of(context).brightness == Brightness.dark
                  ? LucideIcons.sun
                  : LucideIcons.moon,
            ),
            onPressed: onThemeToggle,
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () => Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (_) => ChatScreen(onThemeToggle: onThemeToggle),
                ),
              ),
              child: const Text('Standard Chat'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () => Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (_) =>
                      CustomChatScreen(onThemeToggle: onThemeToggle),
                ),
              ),
              child: const Text('Advanced Customization'),
            ),
          ],
        ),
      ),
    );
  }
}

// -----------------------------------------------------------------------------
// STANDARD CHAT IMPLEMENTATION
// -----------------------------------------------------------------------------

class ChatScreen extends StatefulWidget {
  final VoidCallback onThemeToggle;

  const ChatScreen({super.key, required this.onThemeToggle});

  @override
  State<ChatScreen> createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  final List<ChatMessage> _messages = [
    ChatMessage(
      id: '1',
      text: 'Hey there! Checking out the new UI?',
      senderName: 'Jane',
      timestamp: DateTime.now().subtract(const Duration(minutes: 5)),
      isSentByMe: false,
    ),
    ChatMessage(
      id: '2',
      text: 'Yes! The glassmorphism looks amazing.',
      senderName: 'John',
      timestamp: DateTime.now().subtract(const Duration(minutes: 4)),
      isSentByMe: true,
    ),
  ];

  bool _isTyping = false;

  void _handleSend(String text) {
    setState(() {
      _messages.insert(
        0,
        ChatMessage(
          id: DateTime.now().toString(),
          text: text,
          senderName: 'John',
          timestamp: DateTime.now(),
          isSentByMe: true,
        ),
      );
      _isTyping = true;
    });

    Future<void>.delayed(const Duration(seconds: 2), () {
      if (mounted) {
        setState(() {
          _isTyping = false;
          _messages.insert(
            0,
            ChatMessage(
              id: DateTime.now().toString(),
              text: "Cannot wait to see what you build with this!",
              senderName: 'Jane',
              timestamp: DateTime.now(),
              isSentByMe: false,
            ),
          );
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      body: Column(
        children: [
          ChatHeader(onThemeToggle: widget.onThemeToggle),
          Expanded(
            child: MessageList(messages: _messages, isTyping: _isTyping),
          ),
          InputField(onSend: _handleSend),
        ],
      ),
    );
  }
}

// -----------------------------------------------------------------------------
// CUSTOM CHAT IMPLEMENTATION
// -----------------------------------------------------------------------------

class CustomChatScreen extends StatefulWidget {
  final VoidCallback onThemeToggle;

  const CustomChatScreen({super.key, required this.onThemeToggle});

  @override
  State<CustomChatScreen> createState() => _CustomChatScreenState();
}

class _CustomChatScreenState extends State<CustomChatScreen> {
  final List<ChatMessage> _messages = [
    ChatMessage(
      id: '1',
      text: 'This is a custom message bubble.',
      senderName: 'Bot',
      timestamp: DateTime.now().subtract(const Duration(hours: 2)),
      isSentByMe: false,
    ),
    ChatMessage(
      id: '2',
      text: 'And a nice date separator above!',
      senderName: 'User',
      timestamp: DateTime.now(),
      isSentByMe: true,
    ),
  ];

  void _handleSend(String text) {
    setState(() {
      _messages.insert(
        0,
        ChatMessage(
          id: DateTime.now().toString(),
          text: text,
          senderName: 'User',
          timestamp: DateTime.now(),
          isSentByMe: true,
        ),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // Gradient Background
      backgroundColor: Colors.indigo.shade50,
      body: SafeArea(
        child: Column(
          children: [
            // Custom Header
            ChatHeader(
              onThemeToggle: widget.onThemeToggle,
              backgroundColor: Colors.indigo,
              padding: const EdgeInsets.all(16),
              elevation: 4,
              leading: const CircleAvatar(
                backgroundColor: Colors.white,
                child: Icon(LucideIcons.bot, color: Colors.indigo),
              ),
              title: const Text(
                'Support Bot',
                style: TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                  fontSize: 18,
                ),
              ),
              subtitle: const Text(
                'Always here to help',
                style: TextStyle(color: Colors.white70, fontSize: 12),
              ),
              actions: [
                IconButton(
                  icon: const Icon(LucideIcons.settings, color: Colors.white),
                  onPressed: () {},
                ),
              ],
            ),

            // Custom Message List
            Expanded(
              child: MessageList(
                messages: _messages,
                reverse: true, // Keep standard chat reverse
                padding: const EdgeInsets.symmetric(
                  horizontal: 16,
                  vertical: 20,
                ),
                messageSpacing: 16,
                dateSeparatorBuilder: (context, date) => Center(
                  child: Container(
                    margin: const EdgeInsets.symmetric(vertical: 12),
                    padding: const EdgeInsets.symmetric(
                      horizontal: 12,
                      vertical: 4,
                    ),
                    decoration: BoxDecoration(
                      color: Colors.grey.withValues(alpha: 0.2),
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: Text(
                      "${date.day}/${date.month}/${date.year}",
                      style: const TextStyle(
                        fontSize: 10,
                        color: Colors.black54,
                      ),
                    ),
                  ),
                ),
                messageBuilder: (context, message) {
                  // Custom Bubble logic
                  final isMe = message.isSentByMe;
                  return Align(
                    alignment: isMe
                        ? Alignment.centerRight
                        : Alignment.centerLeft,
                    child: Container(
                      constraints: BoxConstraints(
                        maxWidth: MediaQuery.of(context).size.width * 0.75,
                      ),
                      padding: const EdgeInsets.all(12),
                      decoration: BoxDecoration(
                        color: isMe ? Colors.indigo : Colors.white,
                        borderRadius: BorderRadius.only(
                          topLeft: const Radius.circular(16),
                          topRight: const Radius.circular(16),
                          bottomLeft: isMe
                              ? const Radius.circular(16)
                              : Radius.zero,
                          bottomRight: isMe
                              ? Radius.zero
                              : const Radius.circular(16),
                        ),
                        boxShadow: [
                          BoxShadow(
                            color: Colors.black.withValues(alpha: 0.05),
                            blurRadius: 5,
                            offset: const Offset(0, 2),
                          ),
                        ],
                      ),
                      child: Text(
                        message.text,
                        style: TextStyle(
                          color: isMe ? Colors.white : Colors.black87,
                        ),
                      ),
                    ),
                  );
                },
              ),
            ),

            // Custom Input Field
            InputField(
              onSend: _handleSend,
              backgroundColor: Colors.white,
              borderRadius: BorderRadius.circular(0), // Rectangular strip
              padding: const EdgeInsets.all(0),
              hintText: 'Ask something...',
              leading: IconButton(
                icon: const Icon(LucideIcons.plus, color: Colors.indigo),
                onPressed: () {},
              ),
              sendButton: Container(
                padding: const EdgeInsets.all(8),
                decoration: const BoxDecoration(
                  color: Colors.indigo,
                  shape: BoxShape.circle,
                ),
                child: const Icon(
                  LucideIcons.arrowUp,
                  color: Colors.white,
                  size: 20,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
0
likes
135
points
36
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A flexible and customizable chat UI toolkit for Flutter.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

cupertino_icons, flutter, google_fonts, lucide_icons

More

Packages that depend on chat_ui_flex