omni_chat 1.0.3 copy "omni_chat: ^1.0.3" to clipboard
omni_chat: ^1.0.3 copied to clipboard

PlatformAndroid

A professional, feature-rich Flutter chat UI package. Supports 17+ message types including text, image, video, audio waveform, sticker, GIF, document, location, contact, link preview, poll, profile ca [...]

example/lib/main.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:omni_chat/omni_chat.dart';

void main() => runApp(const OmniChatExampleApp());

class OmniChatExampleApp extends StatelessWidget {
  const OmniChatExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'OmniChat Demo',
      theme: ThemeData.dark(useMaterial3: true),
      home: const ChatScreen(),
      debugShowCheckedModeBanner: false,
    );
  }
}

// ── Mock data ─────────────────────────────────────────────────────────────────

const _me = 'user_me';
const _other = 'user_them';

int _msgId = 0;
String _nextId() => '${++_msgId}';

final List<ChatMessage> _initialMessages = [
  SystemMessage(
    id: _nextId(),
    senderId: 'system',
    timestamp: DateTime.now().subtract(const Duration(hours: 2)),
    text: 'Sohbet başladı • Uçtan uca şifreli',
  ),
  TextMessage(
    id: _nextId(),
    senderId: _other,
    timestamp: DateTime.now().subtract(const Duration(hours: 1, minutes: 50)),
    text: 'Merhaba! Bugün nasılsın?',
    status: MessageStatus.read,
  ),
  TextMessage(
    id: _nextId(),
    senderId: _me,
    timestamp: DateTime.now().subtract(const Duration(hours: 1, minutes: 48)),
    text: 'İyiyim, teşekkürler! Sen nasılsın? 😊',
    status: MessageStatus.read,
    reactions: [
      MessageReaction(emoji: '❤️', userIds: [_other]),
    ],
  ),
  AudioMessage(
    id: _nextId(),
    senderId: _other,
    timestamp: DateTime.now().subtract(const Duration(hours: 1, minutes: 40)),
    audioUrl: 'https://example.com/audio/voice.m4a',
    duration: 305,
    waveformData: List.generate(
      40,
      (i) => 0.2 + 0.7 * (sin(i * 0.4).abs()),
    ),
    source: MediaSource.microphone,
    status: MessageStatus.read,
  ),
  ImageMessage(
    id: _nextId(),
    senderId: _other,
    timestamp: DateTime.now().subtract(const Duration(hours: 1, minutes: 30)),
    imageUrl:
        'https://images.unsplash.com/photo-1541961017774-22349e4a1262?w=600',
    caption: 'Bugün çektiğim fotoğraf 📷',
    source: MediaSource.camera,
    status: MessageStatus.read,
  ),
  ImageMessage(
    id: _nextId(),
    senderId: _me,
    timestamp: DateTime.now().subtract(const Duration(hours: 1, minutes: 20)),
    imageUrl:
        'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600',
    caption: 'Benim de dünden bir fotoğrafım',
    source: MediaSource.gallery,
    status: MessageStatus.read,
  ),
  LinkPreviewMessage(
    id: _nextId(),
    senderId: _other,
    timestamp: DateTime.now().subtract(const Duration(hours: 1, minutes: 10)),
    url: 'https://dribbble.com/shots/21436621',
    title: 'Amazing UI Design Shots',
    description: 'Explore the best UI designs on Dribbble.',
    siteName: 'dribbble.com',
    imageUrl:
        'https://images.unsplash.com/photo-1558655146-9f40138edfeb?w=600',
    text: 'Bunu gördün mü? Harika tasarımlar!',
    status: MessageStatus.read,
  ),
  ProfileCardMessage(
    id: _nextId(),
    senderId: _other,
    timestamp: DateTime.now().subtract(const Duration(minutes: 50)),
    userId: _other,
    username: 'odama_studio',
    displayName: 'Odama Studio',
    avatarUrl:
        'https://images.unsplash.com/photo-1611162617474-5b21e879e113?w=200',
    isVerified: true,
    bio: 'Professional UI/UX design studio',
    status: MessageStatus.read,
  ),
  PollMessage(
    id: _nextId(),
    senderId: _me,
    timestamp: DateTime.now().subtract(const Duration(minutes: 40)),
    question: 'Bu hafta sonu buluşalım mı?',
    options: [
      PollOption(id: 'a', text: 'Cumartesi 🎉', voterIds: [_other]),
      PollOption(id: 'b', text: 'Pazar ☀️', voterIds: []),
      PollOption(id: 'c', text: 'Uygun değilim ❌', voterIds: []),
    ],
    status: MessageStatus.read,
  ),
  EphemeralImageMessage(
    id: _nextId(),
    senderId: _other,
    timestamp: DateTime.now().subtract(const Duration(minutes: 30)),
    imageUrl:
        'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=600',
    hasBeenViewed: false,
    status: MessageStatus.delivered,
  ),
  DocumentMessage(
    id: _nextId(),
    senderId: _me,
    timestamp: DateTime.now().subtract(const Duration(minutes: 20)),
    fileUrl: 'https://example.com/docs/report.pdf',
    fileName: 'Q4_Report_2024.pdf',
    fileSize: 2_450_000,
    mimeType: 'application/pdf',
    status: MessageStatus.sent,
  ),
  TextMessage(
    id: _nextId(),
    senderId: _other,
    timestamp: DateTime.now().subtract(const Duration(minutes: 5)),
    text: 'Harika! Dokümanı aldım, teşekkürler 👍',
    status: MessageStatus.read,
  ),
];

// ── Chat screen ───────────────────────────────────────────────────────────────

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

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

class _ChatScreenState extends State<ChatScreen> {
  final List<ChatMessage> _messages = List.from(_initialMessages);
  ChatMessage? _replyTo;
  ChatMessage? _editMessage;
  bool _isDark = true;

  void _addMessage(ChatMessage msg) {
    setState(() => _messages.add(msg));
  }

  void _sendText(String text) {
    _addMessage(TextMessage(
      id: _nextId(),
      senderId: _me,
      timestamp: DateTime.now(),
      text: text,
      replyTo: _replyTo,
      status: MessageStatus.sending,
    ));
    setState(() => _replyTo = null);
  }

  void _sendAudio(String path, int duration, List<double> wave) {
    _addMessage(AudioMessage(
      id: _nextId(),
      senderId: _me,
      timestamp: DateTime.now(),
      audioUrl: path,
      duration: duration,
      waveformData: wave,
      source: MediaSource.microphone,
      status: MessageStatus.sending,
    ));
  }

  @override
  Widget build(BuildContext context) {
    final theme = _isDark ? OmniChatTheme.dark() : OmniChatTheme.light();

    return OmniChatThemeScope(
      theme: theme,
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: _isDark ? const Color(0xFF1F2C34) : Colors.white,
          leading: IconButton(
            icon: Icon(Icons.arrow_back,
                color: _isDark ? Colors.white : Colors.black87),
            onPressed: () {},
          ),
          title: Row(
            children: [
              const CircleAvatar(
                radius: 18,
                backgroundColor: Color(0xFF00A884),
                child: Text('OS',
                    style: TextStyle(
                        color: Colors.white,
                        fontSize: 12,
                        fontWeight: FontWeight.bold)),
              ),
              const SizedBox(width: 10),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Odama Studio',
                    style: TextStyle(
                      color: _isDark ? Colors.white : Colors.black87,
                      fontSize: 16,
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                  Text(
                    'Çevrimiçi',
                    style: TextStyle(
                      color: _isDark ? Colors.white60 : Colors.grey,
                      fontSize: 12,
                    ),
                  ),
                ],
              ),
            ],
          ),
          actions: [
            IconButton(
              icon: Icon(
                _isDark ? Icons.light_mode : Icons.dark_mode,
                color: _isDark ? Colors.white : Colors.black87,
              ),
              onPressed: () => setState(() => _isDark = !_isDark),
              tooltip: 'Temayı değiştir',
            ),
            IconButton(
              icon: Icon(Icons.more_vert,
                  color: _isDark ? Colors.white : Colors.black87),
              onPressed: () {},
            ),
          ],
        ),
        body: Column(
          children: [
            Expanded(
              child: OmniChatList(
                messages: _messages,
                currentUserId: _me,
                isEncrypted: true,
                screenshotProtection: false, // set true in production
                getSenderName: (uid) =>
                    uid == _me ? 'Sen' : 'Odama Studio',
                onReply: (msg) => setState(() => _replyTo = msg),
                onEdit: (msg) => setState(() => _editMessage = msg),
                onDelete: (msg, forAll) {
                  setState(() {
                    final i = _messages.indexWhere((m) => m.id == msg.id);
                    if (i != -1) {
                      _messages[i] = msg.copyWith(isDeleted: true);
                    }
                  });
                },
                onCopy: (_) {},
                onForward: (_) {},
                onPin: (_) {},
                onReact: (msg, emoji) {
                  setState(() {
                    final i = _messages.indexWhere((m) => m.id == msg.id);
                    if (i == -1) return;
                    final existing = _messages[i].reactions;
                    final reactionIdx =
                        existing.indexWhere((r) => r.emoji == emoji);
                    List<MessageReaction> updated;
                    if (reactionIdx != -1) {
                      final r = existing[reactionIdx];
                      if (r.userIds.contains(_me)) {
                        updated = [
                          ...existing.sublist(0, reactionIdx),
                          r.copyWith(
                              userIds: r.userIds
                                  .where((id) => id != _me)
                                  .toList()),
                          ...existing.sublist(reactionIdx + 1),
                        ];
                      } else {
                        updated = [
                          ...existing.sublist(0, reactionIdx),
                          r.copyWith(userIds: [...r.userIds, _me]),
                          ...existing.sublist(reactionIdx + 1),
                        ];
                      }
                    } else {
                      updated = [
                        ...existing,
                        MessageReaction(emoji: emoji, userIds: [_me]),
                      ];
                    }
                    updated.removeWhere((r) => r.userIds.isEmpty);
                    _messages[i] = _messages[i].copyWith(reactions: updated);
                  });
                },
                onProfileTap: (userId) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('Profil: $userId')),
                  );
                },
                onPollVote: (msg, optId) {
                  setState(() {
                    final i = _messages.indexWhere((m) => m.id == msg.id);
                    if (i == -1 || _messages[i] is! PollMessage) return;
                    final poll = _messages[i] as PollMessage;
                    final opts = poll.options.map((o) {
                      if (o.id == optId && !o.voterIds.contains(_me)) {
                        return o.copyWith(voterIds: [...o.voterIds, _me]);
                      }
                      return o;
                    }).toList();
                    _messages[i] = poll.copyWith(options: opts);
                  });
                },
              ),
            ),
            OmniChatInput(
              replyMessage: _replyTo,
              replyMessageSenderName: _replyTo != null
                  ? (_replyTo!.senderId == _me ? 'Sen' : 'Odama Studio')
                  : null,
              onCancelReply: () => setState(() => _replyTo = null),
              editMessage: _editMessage,
              onCancelEdit: () => setState(() => _editMessage = null),
              onSendEdit: (text) {
                setState(() {
                  if (_editMessage == null) return;
                  final i = _messages.indexWhere((m) => m.id == _editMessage!.id);
                  if (i != -1) {
                    _messages[i] = _editMessage!.copyWith(isEdited: true);
                  }
                  _editMessage = null;
                });
              },
              onSendText: _sendText,
              onSendAudio: _sendAudio,
              onAttachment: (type) {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Ek seçildi: ${type.name}')),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}
0
likes
120
points
18
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A professional, feature-rich Flutter chat UI package. Supports 17+ message types including text, image, video, audio waveform, sticker, GIF, document, location, contact, link preview, poll, profile card, ephemeral media, and custom messages. Includes swipe-to-reply, emoji reactions, message editing, built-in media viewers, screenshot protection, full theme customisation, and built-in localisation via OmniChatStrings.

Repository (GitHub)
View/report issues

Topics

#chat #messaging #ui #widget #media

License

MIT (license)

Dependencies

audio_waveforms, cached_network_image, chewie, emoji_picker_flutter, file_picker, flutter, flutter_linkify, flutter_windowmanager, google_fonts, html, http, image_picker, intl, linkify, lottie, path_provider, photo_view, url_launcher, video_player

More

Packages that depend on omni_chat