Flutter를 사용하여 실시간 채팅 애플리케이션을 개발하는 것은 매우 흥미로운 작업입니다. Flutter의 강력한 위젯과 Firebase를 활용하면 복잡한 기능도 쉽게 구현할 수 있습니다. 이번 글에서는 Flutter와 Firebase를 사용하여 기본적인 채팅 애플리케이션을 만드는 방법을 단계별로 설명하겠습니다.
1. Firebase 설정
Firebase는 실시간 데이터베이스, 인증, 호스팅 등 다양한 백엔드 서비스를 제공합니다. 채팅 애플리케이션에서는 Firebase의 Firestore와 Authentication을 사용합니다.
Firebase 프로젝트 생성
- Firebase 콘솔에 로그인하고 새 프로젝트를 만듭니다.
- 프로젝트 설정에서 Firebase SDK를 추가합니다.
- Android: google-services.json 파일을 다운로드하여 android/app 폴더에 추가합니다.
- iOS: GoogleService-Info.plist 파일을 다운로드하여 ios/Runner 폴더에 추가합니다.
 
Firebase 플러그인 추가
pubspec.yaml 파일에 Firebase 플러그인을 추가합니다.
dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^1.10.0
  firebase_auth: ^3.3.0
  cloud_firestore: ^3.1.5
2. Firebase 초기화
Flutter 프로젝트에서 Firebase를 초기화합니다. main.dart 파일을 수정하여 Firebase 초기화를 추가합니다.
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ChatScreen(),
    );
  }
}
3. 사용자 인증
채팅 애플리케이션에서는 사용자를 인증하기 위해 Firebase Authentication을 사용합니다. 간단한 이메일 로그인 기능을 구현합니다.
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class LoginScreen extends StatefulWidget {
  @override
  _LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  Future<void> _login() async {
    try {
      await _auth.signInWithEmailAndPassword(
        email: _emailController.text,
        password: _passwordController.text,
      );
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (context) => ChatScreen()),
      );
    } catch (e) {
      print(e);
    }
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Login')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _emailController,
              decoration: InputDecoration(labelText: 'Email'),
            ),
            TextField(
              controller: _passwordController,
              decoration: InputDecoration(labelText: 'Password'),
              obscureText: true,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _login,
              child: Text('Login'),
            ),
          ],
        ),
      ),
    );
  }
}
4. Firestore 설정 및 메시지 전송
Firestore에 채팅 메시지를 저장하고 실시간으로 데이터를 업데이트합니다. 메시지를 전송하고, Firestore에서 데이터를 가져와 화면에 표시하는 기능을 구현합니다.
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class ChatScreen extends StatefulWidget {
  @override
  _ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  final TextEditingController _messageController = TextEditingController();
  Future<void> _sendMessage() async {
    if (_messageController.text.isNotEmpty) {
      await _firestore.collection('messages').add({
        'text': _messageController.text,
        'sender': _auth.currentUser?.email,
        'timestamp': FieldValue.serverTimestamp(),
      });
      _messageController.clear();
    }
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Chat'),
        actions: [
          IconButton(
            icon: Icon(Icons.logout),
            onPressed: () async {
              await _auth.signOut();
              Navigator.pushReplacement(
                context,
                MaterialPageRoute(builder: (context) => LoginScreen()),
              );
            },
          ),
        ],
      ),
      body: Column(
        children: [
          Expanded(
            child: StreamBuilder(
              stream: _firestore.collection('messages').orderBy('timestamp').snapshots(),
              builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
                if (!snapshot.hasData) {
                  return Center(child: CircularProgressIndicator());
                }
                final messages = snapshot.data!.docs;
                return ListView.builder(
                  itemCount: messages.length,
                  itemBuilder: (context, index) {
                    final message = messages[index];
                    return ListTile(
                      title: Text(message['text']),
                      subtitle: Text(message['sender']),
                    );
                  },
                );
              },
            ),
          ),
          Padding(
            padding: EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _messageController,
                    decoration: InputDecoration(hintText: 'Enter a message...'),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.send),
                  onPressed: _sendMessage,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
5. 사용자 인터페이스 개선
채팅 애플리케이션의 사용자 인터페이스(UI)를 개선하여 더 나은 사용자 경험(UX)을 제공할 수 있습니다. 메시지 버블, 보낸 사람과 받은 사람의 메시지 스타일을 구분하는 등의 기능을 추가할 수 있습니다.
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class ChatScreen extends StatefulWidget {
  @override
  _ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  final TextEditingController _messageController = TextEditingController();
  Future<void> _sendMessage() async {
    if (_messageController.text.isNotEmpty) {
      await _firestore.collection('messages').add({
        'text': _messageController.text,
        'sender': _auth.currentUser?.email,
        'timestamp': FieldValue.serverTimestamp(),
      });
      _messageController.clear();
    }
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Chat'),
        actions: [
          IconButton(
            icon: Icon(Icons.logout),
            onPressed: () async {
              await _auth.signOut();
              Navigator.pushReplacement(
                context,
                MaterialPageRoute(builder: (context) => LoginScreen()),
              );
            },
          ),
        ],
      ),
      body: Column(
        children: [
          Expanded(
            child: StreamBuilder(
              stream: _firestore.collection('messages').orderBy('timestamp').snapshots(),
              builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
                if (!snapshot.hasData) {
                  return Center(child: CircularProgressIndicator());
                }
                final messages = snapshot.data!.docs;
                return ListView.builder(
                  itemCount: messages.length,
                  itemBuilder: (context, index) {
                    final message = messages[index];
                    final currentUser = _auth.currentUser?.email;
                    final isMe = message['sender'] == currentUser;
                    return Container(
                      margin: isMe
                          ? EdgeInsets.only(left: 50, top: 8, bottom: 8, right: 8)
                          : EdgeInsets.only(left: 8, top: 8, bottom: 8, right: 50),
                      padding: EdgeInsets.all(10),
                      decoration: BoxDecoration(
                        color: isMe ? Colors.blue[100] : Colors.grey[200],
                        borderRadius: BorderRadius.circular(12),
                      ),
                      child: Column(
                        crossAxisAlignment:
                            isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
                        children: [
                          Text(
                            message['text'],
                            style: TextStyle(fontSize: 16),
                          ),
                          Text(
                            message['sender'],
                            style: TextStyle(fontSize: 12, color: Colors.grey[600]),
                          ),
                        ],
                      ),
                    );
                  },
                );
              },
            ),
          ),
          Padding(
            padding: EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _messageController,
                    decoration: InputDecoration(hintText: 'Enter a message...'),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.send),
                  onPressed: _sendMessage,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
결론
Flutter와 Firebase를 사용하여 실시간 채팅 애플리케이션을 쉽게 구현할 수 있습니다. Firebase의 강력한 기능을 활용하면 실시간 데이터베이스와 사용자 인증을 간편하게 설정할 수 있으며, Flutter의 다양한 위젯을 사용하여 사용자 인터페이스를 개선할 수 있습니다. 이번 글에서 소개한 방법들을 활용하여 Flutter 애플리케이션에서 매력적이고 기능적인 채팅 애플리케이션을 구현해보세요. 채팅 애플리케이션을 통해 사용자에게 실시간 소통의 기쁨을 제공할 수 있습니다.
'Flutter' 카테고리의 다른 글
| Flutter의 푸시 알림(Push Notifications) 설정 (0) | 2025.01.20 | 
|---|---|
| Flutter의 알림(Notification) 구현하기 (0) | 2025.01.19 | 
| Flutter의 사용자 목록(User List) 화면 만들기 (0) | 2025.01.17 | 
| Flutter의 설정 화면(Settings Screen) 만들기 (0) | 2025.01.17 | 
| Flutter의 프로필 화면(Profile Screen) 만들기 (0) | 2025.01.17 |