패키지, 저장소 및 앱 카테고리 검색

chat_bubbles 표지
패키지 상세v1.10.1

chat_bubbles

WhatsApp와 유사한 Flutter 채팅 버블 위젯 및 다양한 형태. 채팅 버블 사용 및 구현이 쉬움.

AndroidiOSWebmacOSWindowsLinux
30일 다운로드
2.3만
좋아요
577
Pub 점수
160

패키지 설치

flutter pub add chat_bubbles

패키지 개요

공개 정보를 바탕으로 생성한 선택 가이드입니다.

Chat Bubbles는 WhatsApp와 같은 인기 있는 메시징 앱을 영감으로 삼아 사용자 정의 가능한 채팅 버블 위젯을 제공하는 플러터 패키지입니다. 다양한 형태와 스타일을 지원하여 여러 플랫폼에서 현실적인 채팅 인터페이스를 쉽게 구현할 수 있습니다. 가볍고 간단하게 통합할 수 있으며, 최소한의 설정으로 시각적으로 매력적인 메시지 버블에 빠르게 접근할 수 있습니다. 동적이고 상호작용 가능한 채팅 UI가 필요한 앱에 적합합니다.

사용 사례

  • 즉시 메시징 앱
  • 고객 지원 채팅 인터페이스
  • 그룹 대화 기능
  • 실시간 채팅 프로토타입
  • 교육용 앱 토론 게시판

주요 기능

  • 여러 개의 버블 형태
  • 사용자 지정 가능한 색상 및 테두리
  • 다른 메시지 정렬 지원
  • 기존 UI와의 간편한 통합
  • 크로스 플랫폼 호환성

적합한 대상

  • 채팅 애플리케이션을 개발하는 개발자
  • 사전에 준비된 채팅 컴포넌트를 찾는 UI 디자이너
  • 반응형 메시지 버블이 필요한 프로젝트
  • Flutter UI 디자인을 배우는 초보자
  • 모바일 및 웹 플랫폼을 대상으로 한 앱

고려 사항

  • 기본 채팅 UI 요소에 한정
  • 내장된 메시지 전송 또는 저장 없음
  • 메시지 상태를 수동으로 처리해야 합니다
  • 대규모 메시지 목록에 최적화되지 않음
  • 실시간 기능을 위해 외부 논리에 의존합니다

원본 README

연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.

chat_bubbles plugin

Pub Version CI GitHub GitHub forks GitHub Repo stars GitHub last commit likes downloads pub points

Flutter chat bubble widgets, similar to the Whatsapp and more shapes. Audio and Image chat bubble widgets are also included. Easy to use and implement chat bubbles.

Getting Started

Add this to your package's pubspec.yaml file:

dependencies:
  chat_bubbles: ^1.10.1

Usage

Then you just have to import the package with

import 'package:chat_bubbles/chat_bubbles.dart'

Now you can use this plugin to implement various types of Chat Bubbles, Audio Chat Bubbles and Date chips.

Examples

iMessage's bubble example
BubbleSpecialThree(
  text: 'Added iMessage shape bubbles',
  color: Color(0xFF1B97F3),
  tail: false,
  textStyle: TextStyle(
      color: Colors.white,
      fontSize: 16
  ),
),
BubbleSpecialThree(
  text: 'Please try and give some feedback on it!',
  color: Color(0xFF1B97F3),
  tail: true,
  textStyle: TextStyle(
    color: Colors.white,
    fontSize: 16
  ),
),
BubbleSpecialThree(
  text: 'Sure',
  color: Color(0xFFE8E8EE),
  tail: false,
  isSender: false,
),
BubbleSpecialThree(
  text: "I tried. It's awesome!!!",
  color: Color(0xFFE8E8EE),
  tail: false,
  isSender: false,
),
BubbleSpecialThree(
  text: "Thanks",
  color: Color(0xFFE8E8EE),
  tail: true,
  isSender: false,
)
Single bubble example
  BubbleSpecialOne(
    text: 'Hi, How are you? ',
    isSender: false,
    color: Colors.purple.shade100,
    textStyle: TextStyle(
      fontSize: 20,
      color: Colors.purple,
      fontStyle: FontStyle.italic,
      fontWeight: FontWeight.bold,
    ),
  ),
Audio chat bubble example
  Duration duration = new Duration();
  Duration position = new Duration();
  bool isPlaying = false;
  bool isLoading = false;
  bool isPause = false;

  BubbleNormalAudio(
    color: Color(0xFFE8E8EE),
    duration: duration.inSeconds.toDouble(),
    position: position.inSeconds.toDouble(),
    isPlaying: isPlaying,
    isLoading: isLoading,
    isPause: isPause,
    onSeekChanged: _changeSeek,
    onPlayPauseButtonClick: _playAudio,
    sent: true,
  ),
Image chat bubble example
  BubbleNormalImage(
    id: 'id001',
    image: _image(),
    color: Colors.purpleAccent,
    tail: true,
    delivered: true,
  ),
Date Chip example
  DateChip(
    date: new DateTime(2021, 5, 7),
    color: Color(0x558AD3D5),
  ),
Message bar example
MessageBar(
    onSend: (_) => print(_),
    actions: [
      InkWell(
        child: Icon(
          Icons.add,
          color: Colors.black,
          size: 24,
        ),
        onTap: () {},
      ),
      Padding(
        padding: EdgeInsets.only(left: 8, right: 8),
        child: InkWell(
          child: Icon(
            Icons.camera_alt,
            color: Colors.green,
            size: 24,
          ),
          onTap: () {},
        ),
      ),
    ],
  ),

New in v1.8.0

Reply/Quote Bubble example
BubbleReply(
  repliedMessage: 'This is the original message',
  repliedMessageSender: 'John Doe',
  text: 'This is my reply!',
  isSender: false,
  color: Color(0xFF1B97F3),
  replyBorderColor: Colors.white,
  textStyle: TextStyle(color: Colors.white, fontSize: 16),
  sent: true,
),
Typing Indicator example
TypingIndicator(
  showIndicator: true,
  bubbleColor: Color(0xFFE8E8EE),
  dotColor: Colors.black54,
),

// Alternative wave animation style
TypingIndicatorWave(
  showIndicator: true,
  bubbleColor: Color(0xFFE8E8EE),
  dotColor: Colors.black54,
),
Link Preview Bubble example
BubbleLinkPreview(
  url: 'https://flutter.dev',
  title: 'Flutter - Build apps for any screen',
  description: 'Flutter transforms the app development process.',
  imageUrl: 'https://storage.googleapis.com/cms-storage-bucket/70760bf1e88b184bb1bc.png',
  text: 'Check out this awesome framework!',
  isSender: false,
  color: Color(0xFF1B97F3),
  textStyle: TextStyle(color: Colors.white, fontSize: 16),
),
Bubble Reactions example
Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    BubbleNormal(
      text: 'Great work! 🎉',
      isSender: false,
      color: Color(0xFF1B97F3),
    ),
    BubbleReaction(
      reactions: [
        Reaction(emoji: '👍', count: 3, isUserReacted: true),
        Reaction(emoji: '❤️', count: 2),
        Reaction(emoji: '🎉', count: 1),
      ],
      onReactionTap: (reaction) {
        print('Tapped on ${reaction.emoji}');
      },
      onAddReactionTap: () {
        print('Add reaction tapped');
      },
    ),
  ],
),
Main example (Chat View)

Checkout the plugin example to figure out more.

Duration duration = new Duration();
Duration position = new Duration();
bool isPlaying = false;
bool isLoading = false;
bool isPause = false;

@override
Widget build(BuildContext context) {
  final now = new DateTime.now();
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: Stack(
      children: [
        SingleChildScrollView(
          child: Column(
            children: <Widget>[
              BubbleNormalImage(
                  id: 'id001',
                  image: _image(),
                  color: Colors.purpleAccent,
                  tail: true,
                  delivered: true,
              ),
              BubbleNormalAudio(
                color: Color(0xFFE8E8EE),
                duration: duration.inSeconds.toDouble(),
                position: position.inSeconds.toDouble(),
                isPlaying: isPlaying,
                isLoading: isLoading,
                isPause: isPause,
                onSeekChanged: _changeSeek,
                onPlayPauseButtonClick: _playAudio,
                sent: true,
              ),
              BubbleNormal(
                text: 'bubble normal with tail',
                isSender: false,
                color: Color(0xFF1B97F3),
                tail: true,
                textStyle: TextStyle(
                  fontSize: 20,
                  color: Colors.white,
                ),
              ),
              BubbleNormal(
                text: 'bubble normal with tail',
                isSender: true,
                color: Color(0xFFE8E8EE),
                tail: true,
                sent: true,
              ),
              DateChip(
                date: new DateTime(now.year, now.month, now.day - 2),
              ),
              BubbleNormal(
                text: 'bubble normal without tail',
                isSender: false,
                color: Color(0xFF1B97F3),
                tail: false,
                textStyle: TextStyle(
                  fontSize: 20,
                  color: Colors.white,
                ),
              ),
              BubbleNormal(
                text: 'bubble normal without tail',
                color: Color(0xFFE8E8EE),
                tail: false,
                sent: true,
                seen: true,
                delivered: true,
              ),
              BubbleSpecialOne(
                text: 'bubble special one with tail',
                isSender: false,
                color: Color(0xFF1B97F3),
                textStyle: TextStyle(
                  fontSize: 20,
                  color: Colors.white,
                ),
              ),
              DateChip(
                date: new DateTime(now.year, now.month, now.day - 1),
              ),
              BubbleSpecialOne(
                text: 'bubble special one with tail',
                color: Color(0xFFE8E8EE),
                seen: true,
              ),
              BubbleSpecialOne(
                text: 'bubble special one without tail',
                isSender: false,
                tail: false,
                color: Color(0xFF1B97F3),
                textStyle: TextStyle(
                  fontSize: 20,
                  color: Colors.black,
                ),
              ),
              BubbleSpecialOne(
                text: 'bubble special one without tail',
                tail: false,
                color: Color(0xFFE8E8EE),
                sent: true,
              ),
              BubbleSpecialTwo(
                text: 'bubble special tow with tail',
                isSender: false,
                color: Color(0xFF1B97F3),
                textStyle: TextStyle(
                  fontSize: 20,
                  color: Colors.black,
                ),
              ),
              DateChip(
                date: now,
              ),
              BubbleSpecialTwo(
                text: 'bubble special tow with tail',
                isSender: true,
                color: Color(0xFFE8E8EE),
                sent: true,
              ),
              BubbleSpecialTwo(
                text: 'bubble special tow without tail',
                isSender: false,
                tail: false,
                color: Color(0xFF1B97F3),
                textStyle: TextStyle(
                  fontSize: 20,
                  color: Colors.black,
                ),
              ),
              BubbleSpecialTwo(
                text: 'bubble special tow without tail',
                tail: false,
                color: Color(0xFFE8E8EE),
                delivered: true,
              ),
              BubbleSpecialThree(
                text: 'bubble special three without tail',
                color: Color(0xFF1B97F3),
                tail: false,
                textStyle: TextStyle(color: Colors.white, fontSize: 16),
              ),
              BubbleSpecialThree(
                text: 'bubble special three with tail',
                color: Color(0xFF1B97F3),
                tail: true,
                textStyle: TextStyle(color: Colors.white, fontSize: 16),
              ),
              BubbleSpecialThree(
                text: "bubble special three without tail",
                color: Color(0xFFE8E8EE),
                tail: false,
                isSender: false,
              ),
              BubbleSpecialThree(
                text: "bubble special three with tail",
                color: Color(0xFFE8E8EE),
                tail: true,
                isSender: false,
              ),
              SizedBox(
                height: 100,
              )
            ],
          ),
        ),
        MessageBar(
          onSend: (_) => print(_),
          actions: [
            InkWell(
              child: Icon(
                Icons.add,
                color: Colors.black,
                size: 24,
              ),
              onTap: () {},
            ),
            Padding(
              padding: EdgeInsets.only(left: 8, right: 8),
              child: InkWell(
                child: Icon(
                  Icons.camera_alt,
                  color: Colors.green,
                  size: 24,
                ),
                onTap: () {},
              ),
            ),
          ],
        ),
      ],
    ),
    // This trailing comma makes auto-formatting nicer for build methods.
  );
}

New in v1.9.0

Message Status Enhancements

All bubble widgets now accept timestamp, isEdited, isForwarded, and messageId parameters.

BubbleNormal(
  text: 'This message was edited',
  isSender: true,
  color: Color(0xFFE8E8EE),
  tail: true,
  seen: true,
  timestamp: '12:34 PM',
  isEdited: true,
),

BubbleNormal(
  text: 'Forwarded from another chat',
  isSender: false,
  color: Color(0xFF1B97F3),
  tail: true,
  textStyle: TextStyle(color: Colors.white, fontSize: 16),
  isForwarded: true,
  timestamp: '12:35 PM',
),
Swipe Actions

Wrap any bubble with SwipeableBubble to add swipe-to-reply and swipe-to-delete gestures.

SwipeableBubble(
  onSwipeRight: () => print('Reply triggered'),
  onSwipeLeft: () => print('Delete triggered'),
  swipeThreshold: 64.0,
  enableHaptics: true,
  child: BubbleNormal(
    text: 'Swipe me!',
    isSender: false,
    color: Color(0xFF1B97F3),
    tail: true,
    textStyle: TextStyle(color: Colors.white, fontSize: 16),
  ),
),
Parameter Type Default Description
child Widget required The bubble to wrap
onSwipeRight VoidCallback? null Called on swipe-right (reply)
onSwipeLeft VoidCallback? null Called on swipe-left (delete)
swipeThreshold double 64.0 Drag distance to trigger
enableHaptics bool true Haptic feedback on threshold
rightActionColor Color Colors.blue Background for right swipe
leftActionColor Color Colors.red Background for left swipe
rightActionIcon Icon Icons.reply Icon for right swipe
leftActionIcon Icon Icons.delete Icon for left swipe
Message Groups / Clustering

Use BubbleGroupBuilder to automatically group consecutive messages from the same sender. Only the last message in each group shows a tail.

final messages = ['Hello!', 'How are you?', 'Good, thanks!'];
final senders  = ['alice', 'alice', 'bob'];

BubbleGroupBuilder(
  itemCount: messages.length,
  senderIdOf: (i) => senders[i],
  itemBuilder: (context, i, info) => BubbleNormal(
    text: messages[i],
    isSender: senders[i] == 'bob',
    tail: info.showTail,
    color: senders[i] == 'bob'
        ? const Color(0xFFE8E8EE)
        : const Color(0xFF1B97F3),
  ),
)

For advanced control, use MessageGroupHelper.compute() directly to get GroupInfo for each message:

final List<GroupInfo> groups = MessageGroupHelper.compute(
  senderIds: senders,
  timestamps: timestamps,  // optional
  threshold: const Duration(minutes: 2),
);

// groups[i].showTail  — whether to show the tail
// groups[i].showAvatar — whether to show the avatar
// groups[i].isGroupStart — first message in group (add extra top spacing)
Voice Message Waveform

BubbleNormalAudio now supports waveform visualization and playback speed control.

BubbleNormalAudio(
  color: Color(0xFFE8E8EE),
  duration: duration.inSeconds.toDouble(),
  position: position.inSeconds.toDouble(),
  isPlaying: isPlaying,
  isLoading: isLoading,
  isPause: isPause,
  onSeekChanged: _changeSeek,
  onPlayPauseButtonClick: _playAudio,
  sent: true,
  // Waveform visualization
  waveformData: [0.2, 0.8, 0.5, 0.9, 0.4, 0.7, 0.6, 0.3, 0.8, 0.5],
  waveformActiveColor: Color(0xFF1B97F3),
  waveformInactiveColor: Colors.grey,
  // Playback speed
  showPlaybackSpeed: true,
  playbackSpeed: _playbackSpeed,
  onPlaybackSpeedChanged: (speed) => setState(() => _playbackSpeed = speed),
),

waveformData is a List<double> of normalized bar heights (0.0–1.0). Tapping or dragging on the waveform seeks to that position. The speed button cycles 1x → 1.5x → 2x → 1x.

Issues

Please feel free to let me know any issue regarding to this plugin.

카테고리

의존성

의존성 목록 1 개

소스 저장소

이 패키지를 사용하는 프로젝트