Flutter 개발

Flutter 메신저 업그레이드: 메시지 버블, 시간, 닉네임 추가 방법 (코드 변경점 자세히 설명)

withmilk 2025. 4. 27. 17:19

Flutter와 Firebase로 간단한 메신저 앱을 만들었다면, 이제 진짜 메신저처럼 멋지게 업그레이드해볼 차례입니다!
이번 글에서는 메시지 버블, 닉네임 표시, 보낸 시간 표시, 내 메시지와 다른 사람 메시지 구분까지,
코드를 어떻게 변경했는지 하나하나 자세히 알려드릴게요.


🧩 Flutter 메신저 업그레이드에서 변경된 부분

  1. 메시지를 보낼 때 닉네임과 시간도 함께 저장
  2. 메시지를 시간순으로 정렬해서 보여주기
  3. 내 메시지는 오른쪽, 다른 사람 메시지는 왼쪽 정렬
  4. 메시지를 버블 디자인으로 예쁘게 꾸미기
  5. 닉네임과 보낸 시간도 화면에 표시하기

업그레이드된 메신저 예제 코드

//chat_screen.dart
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:intl/intl.dart'; // 시간 포맷을 위해 필요해요.

class ChatScreen extends StatefulWidget {
  @override
  _ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  final TextEditingController _controller = TextEditingController();
  final DatabaseReference _messagesRef = FirebaseDatabase.instance.ref(
    'messages',
  );

  final String myNickname = '나'; // 내 닉네임 (테스트용)

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Flutter 메신저')),
      body: Column(
        children: [
          Expanded(
            child: StreamBuilder(
              stream: _messagesRef.orderByChild('timestamp').onValue,
              builder: (context, snapshot) {
                if (snapshot.hasData && snapshot.data!.snapshot.value != null) {
                  Map data = snapshot.data!.snapshot.value as Map;
                  List items = data.entries.map((e) => e.value).toList();
                  items.sort(
                    (a, b) => a['timestamp'].compareTo(b['timestamp']),
                  ); // 시간순 정렬

                  return ListView.builder(
                    itemCount: items.length,
                    itemBuilder: (context, index) {
                      bool isMe = items[index]['sender'] == myNickname;
                      return _buildMessageBubble(
                        text: items[index]['text'],
                        sender: items[index]['sender'],
                        timestamp: items[index]['timestamp'],
                        isMe: isMe,
                      );
                    },
                  );
                } else {
                  return Center(child: Text('아직 메시지가 없어요.'));
                }
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: InputDecoration(labelText: '메시지 입력'),
                  ),
                ),
                IconButton(icon: Icon(Icons.send), onPressed: _sendMessage),
              ],
            ),
          ),
        ],
      ),
    );
  }

  void _sendMessage() {
    if (_controller.text.isNotEmpty) {
      _messagesRef.push().set({
        'text': _controller.text,
        'sender': myNickname,
        'timestamp': DateTime.now().millisecondsSinceEpoch,
      });
      _controller.clear();
    }
  }

  Widget _buildMessageBubble({
    required String text,
    required String sender,
    required int timestamp,
    required bool isMe,
  }) {
    String time = DateFormat(
      'HH:mm',
    ).format(DateTime.fromMillisecondsSinceEpoch(timestamp));

    return Align(
      alignment: isMe ? Alignment.centerRight : Alignment.centerLeft,
      child: Container(
        width: MediaQuery.of(context).size.width * 0.7,
        margin: EdgeInsets.symmetric(vertical: 4, horizontal: 8),
        padding: EdgeInsets.all(12),
        decoration: BoxDecoration(
          color: isMe ? Colors.blueAccent : Colors.grey[300],
          borderRadius: BorderRadius.circular(16),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(sender, style: TextStyle(fontSize: 12, color: Colors.black54)),
            SizedBox(height: 4),
            Text(
              text,
              style: TextStyle(
                fontSize: 16,
                color: isMe ? Colors.white : Colors.black,
              ),
            ),
            SizedBox(height: 4),
            Align(
              alignment: Alignment.bottomRight,
              child: Text(
                time,
                style: TextStyle(fontSize: 10, color: Colors.black45),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

0. 의존성 추가

pubspec.yaml에 데이터 포맷팅을 위한 intl 패키지를 추가해주세요.

dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^3.13.0
  firebase_database: ^11.3.5
  intl: ^0.20.2 # 의존성 추가

1. 메시지 저장할 때 데이터 구조 변경

🔥 변경 전

_messagesRef.push().set({'text': _controller.text});

메시지 텍스트만 저장했어요.

✨ 변경 후

_messagesRef.push().set({
  'text': _controller.text,
  'sender': myNickname,
  'timestamp': DateTime.now().millisecondsSinceEpoch,
});

텍스트, 닉네임(sender), 보낸 시간(timestamp)까지 함께 저장합니다.

변경된 데이터 형태

✅ 왜 바꿨을까요?

  • 닉네임과 시간을 표시하려면 저장할 때부터 함께 기록해야 해요.
  • 나중에 누가 보냈고, 언제 보냈는지 쉽게 알 수 있어요.

2. 메시지 불러올 때 시간순 정렬 추가

🔥 변경 전

stream: _messagesRef.onValue,

👉 그냥 가져와서 순서가 랜덤일 수 있었어요.

✨ 변경 후

stream: _messagesRef.orderByChild('timestamp').onValue,

👉 시간순(timestamp)으로 정렬해서 가져와요.

메세지 버블을 그릴 때 출력한 시간. 순서대로 그린다.

✅ 왜 바꿨을까요?

  • 대화 흐름이 자연스럽게 이어지게 만들려고요.
  • 최근 메시지가 맨 밑에 나오도록 정렬하기 위해서예요.

3. 내 메시지와 다른 사람 메시지 정렬 다르게 만들기

🔥 변경 전

  • 모든 메시지가 왼쪽에 똑같이 정렬.

✨ 변경 후

bool isMe = items[index]['sender'] == myNickname; ... alignment: isMe ? Alignment.centerRight : Alignment.centerLeft,
  • 내 메시지는 오른쪽, 다른 사람 메시지는 왼쪽 정렬!

버블의 정렬을 다르게 한 모습

✅ 왜 바꿨을까요?

  • 누구의 메시지인지 한눈에 알아보기 쉽게 하기 위해서예요.
  • 카카오톡처럼 자연스럽게 구분되게 만들었어요.

4. 메시지를 버블 형태로 디자인 변경

🔥 변경 전

ListTile(
  title: Text(message),
);

👉 그냥 텍스트만 덜렁 나왔어요.

✨ 변경 후

Container(
  padding: EdgeInsets.all(12),
  decoration: BoxDecoration(
    color: isMe ? Colors.blueAccent : Colors.grey[300],
    borderRadius: BorderRadius.circular(16),
  ),
  child: ...
)
 

👉 말풍선(버블) 모양으로 예쁘게 만들었어요!

내 메세지 버블의 모습

✅ 왜 바꿨을까요?

  • 채팅이 훨씬 보기 좋아지고, 프로 느낌이 납니다.
  • 사용자가 대화를 읽을 때 더 편하게 볼 수 있어요.

5. 닉네임과 보낸 시간 표시

🔥 변경 전

  • 메시지 내용만 보여줬어요.

✨ 변경 후

Text(sender),
...
Text(time),
  • 위에는 닉네임, 아래에는 시간을 작게 표시했어요.

✅ 왜 바꿨을까요?

  • 대화 상대가 누구인지, 언제 보냈는지 쉽게 알 수 있어야 해요.
  • 친구랑 채팅할 때 누가 썼는지 몰라서 헷갈리면 안 되겠죠?

완벽 업그레이드한 메신저의 UI. 버블이 생기고 닉네임과 시간이 표시된다.


🧠 Flutter 메신저 업그레이드 요약 표

변경 내용 이유
닉네임과 시간 저장 보낸 사람과 시간을 표시하려고
시간순 정렬 대화 흐름을 자연스럽게 하려고
내 메시지/남 메시지 정렬 구분 누가 보냈는지 쉽게 구분하려고
메시지 버블 디자인 보기 좋고 읽기 쉽게 하려고
닉네임과 시간 화면 표시 혼동 없이 대화하려고

마무리하며

이번 업그레이드를 통해
우리 Flutter 메신저는 훨씬 진짜 메신저앱처럼 변했어요!

앞으로는 이 위에

같은 것도 해볼 거예요!
조금씩 추가하면서, 진짜 카카오톡 같은 멋진 앱을 같이 만들어 봐요! 🚀