💬

【Flutter】Flutter Chat UI(flutter_chat_ui)を使ってチャットアプリを最速で組み立てる方法【v2対応】

に公開

はじめに

Flutterでチャット機能を実装しようとすると、UIの構築やメッセージの管理が意外と手間に感じることがあります。
そんなときに便利なのが、flutter_chat_uiパッケージです。

公式パッケージページ(pub.dev)
https://pub.dev/packages/flutter_chat_ui

2024年にリリースされたv2では、従来の使い方から大きな変更が加えられました。
この記事では、現在開発中のプロジェクトに導入している実装をベースに、flutter_chat_ui v2 の基本的な使い方と、カスタマイズの一例をご紹介します。


パッケージの導入

以下のコマンドで必要なパッケージを導入します。

flutter pub add flutter_chat_core
flutter pub add flutter_chat_ui

チャット画面の基本構成

ChatControllerを使ったメッセージ管理

v2からはChatControllerを通じてメッセージを管理するスタイルに変更されました。

final _chatController = InMemoryChatController();

Chatウィジェットのセットアップ

以下が、flutter_chat_uiのチャット画面構築の基本的なコードです。
メッセージ送信や添付ファイル選択、ユーザー情報の解決、カスタムUIの定義など、すべてChatウィジェットのプロパティとして指定できます。

Chat(
  chatController: _chatController,
  currentUserId: _myUserId,
  onMessageSend: (text) {
    final message = TextMessage(
      id: uuid.v4(),
      authorId: _myUserId,
      createdAt: DateTime.now(),
      text: text,
    );
    _chatController.insertMessage(message);
  },
  onAttachmentTap: () => _showAttachmentOptions(),
  onMessageTap: (message) => _handleMessageTap(message),
  resolveUser: (userId) async {
    return User(id: userId, name: userId == _myUserId ? '自分' : '相手');
  },
  builders: Builders(...),
)

メッセージの送信方法

テキストメッセージの送信

final message = TextMessage(
  id: uuid.v4(),
  authorId: _myUserId,
  createdAt: DateTime.now(),
  text: 'こんにちは!',
);
_chatController.insertMessage(message);

画像やファイルメッセージの送信

画像の選択にはimage_picker、ファイルの選択にはfile_pickerを使用しています。

final image = await ImagePicker().pickImage(source: ImageSource.gallery);
if (image != null) {
  final imageMessage = ImageMessage(
    id: uuid.v4(),
    authorId: _myUserId,
    createdAt: DateTime.now(),
    source: image.path,
  );
  _chatController.insertMessage(imageMessage);
}
final result = await FilePicker.platform.pickFiles();
if (result != null && result.files.single.path != null) {
  final path = result.files.single.path!;
  final fileMessage = FileMessage(
    id: uuid.v4(),
    authorId: _myUserId,
    createdAt: DateTime.now(),
    name: p.basename(path),
    size: await File(path).length(),
    source: path,
  );
  _chatController.insertMessage(fileMessage);
}

表示のカスタマイズ(Builders)

flutter_chat_uiはデフォルトでも十分使えるUIを提供していますが、プロダクトに合わせて自由にカスタマイズすることもできます。
ここでは、実際に行ったビルダーのカスタマイズ例をいくつかご紹介します。

テキストメッセージのバブル色を変更する

送信メッセージと受信メッセージで色を分けたい場合は、textMessageBuilderを使います。

textMessageBuilder: (context, message, index) {
  return SimpleTextMessage(
    message: message,
    index: index,
    showStatus: false,
    showTime: false,
    sentTextStyle: const TextStyle(color: Colors.black),
    receivedTextStyle: const TextStyle(color: Colors.black),
    sentBackgroundColor: Colors.lightBlueAccent.shade100,
    receivedBackgroundColor: Colors.grey.shade200,
  );
},

画像メッセージの表示をカスタムする

画像の角丸処理やエラー時の表示なども細かく調整できます。

imageMessageBuilder: (context, message, index) {
  final isNetwork = message.source.startsWith('http');
  final imageWidget = isNetwork
      ? Image.network(message.source, fit: BoxFit.cover, width: 160, height: 160)
      : Image.file(File(message.source), fit: BoxFit.cover, width: 160, height: 160);

  return Container(
    padding: const EdgeInsets.all(4),
    decoration: BoxDecoration(
      color: Colors.grey.shade100,
      borderRadius: BorderRadius.circular(12),
    ),
    child: ClipRRect(
      borderRadius: BorderRadius.circular(8),
      child: imageWidget,
    ),
  );
},

ファイルメッセージの表示とタップ処理

ファイルメッセージの見た目や、タップ時の動作(保存など)も制御できます。

fileMessageBuilder: (context, message, index) {
  return GestureDetector(
    onTap: () => saveFile(message.source, message.name),
    child: Container(
      padding: const EdgeInsets.all(8),
      decoration: BoxDecoration(
        color: Colors.grey.shade200,
        borderRadius: BorderRadius.circular(12),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Icon(Icons.insert_drive_file, size: 24, color: Colors.blueGrey),
          const SizedBox(width: 8),
          Flexible(
            child: Text(
              message.name,
              style: const TextStyle(fontSize: 14),
              overflow: TextOverflow.ellipsis,
            ),
          ),
        ],
      ),
    ),
  );
},

アバター・既読表示のカスタマイズ

メッセージの送信者に応じてアバターを出し分けたり、既読・未読表示も追加可能です。
以下は chatMessageBuilderの一例です:

chatMessageBuilder: (context, message, index, animation, child, {groupStatus, isRemoved}) {
  final isSelf = message.authorId == _myUserId;
  return Column(
    crossAxisAlignment: isSelf ? CrossAxisAlignment.end : CrossAxisAlignment.start,
    children: [
      // 吹き出し
      ChatMessage(
        message: message,
        index: index,
        animation: animation,
        groupStatus: groupStatus,
        child: child,
      ),
      // 既読・時間などの補助情報
      Padding(
        padding: const EdgeInsets.only(left: 4.0, right: 4.0, bottom: 4.0),
        child: Row(
          mainAxisAlignment: isSelf ? MainAxisAlignment.end : MainAxisAlignment.start,
          children: [
            if (isSelf)
              Text(
                message.seenAt != null ? '既読' : '未読',
                style: const TextStyle(fontSize: 12, color: Colors.grey),
              ),
            const SizedBox(width: 6),
            Text(
              _formatTime(message.createdAt),
              style: const TextStyle(fontSize: 11, color: Colors.grey),
            ),
          ],
        ),
      ),
    ],
  );
},

v1からv2への変更点まとめ

項目 v1 v2
メッセージ管理 messagesに直接渡す ChatController経由で操作
イベントハンドラ onSendPressed onMessageSend に統一
カスタマイズ方法 theme, customMessageBuilder など Builders に集約
型の扱い やや曖昧(dynamicに近い) TextMessage, ImageMessage など強化された型定義

おわりに

flutter_chat_uiは、比較的少ないコード量でモダンなチャットUIを構築できる便利なパッケージです。
特にv2では、ChatController を中心とした明確な設計になったことで、より柔軟で実用的なチャット画面が作りやすくなったと感じています。

本記事では、実際のプロジェクトで導入している構成をもとに、基本的な使い方と表示カスタマイズについてご紹介しました。
これからチャット機能を実装する方の参考になれば幸いです。

Discussion