chatting-firebase
카테고리: Project
아래는 플러터와 파이어베이스를 이용해 채팅방 기능, 로그인, 말풍선 UI 및 이미지/파일 전송 기능을 포함한 전체적인 코드입니다. 이 코드는 프로젝트 구조를 구성하고, 로그인 화면부터 채팅방까지의 흐름을 완성한 예시입니다.
pubspec.yaml
의존성 설정
먼저 필요한 패키지들을 pubspec.yaml
파일에 추가합니다.
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.10.0
firebase_auth: ^4.4.0
cloud_firestore: ^4.8.3
firebase_storage: ^11.0.16
image_picker: ^1.0.0
file_picker: ^5.3.0
main.dart
- Firebase 초기화 및 라우팅
main.dart
파일은 Firebase를 초기화하고 로그인 및 채팅 화면을 연결합니다.
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'chat_screen.dart';
import 'login_screen.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chat App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (ctx, snapshot) {
if (snapshot.hasData) {
return ChatScreen();
}
return LoginScreen();
},
),
routes: {
'/chat': (context) => ChatScreen(),
},
);
}
}
login_screen.dart
- 로그인 화면
이 파일에서는 이메일/비밀번호로 Firebase 인증을 사용하여 로그인 기능을 구현합니다.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class LoginScreen extends StatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
Future<void> _login() async {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
);
Navigator.pushReplacementNamed(context, '/chat');
} catch (e) {
print('Login Failed: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
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'),
),
],
),
),
);
}
}
chat_screen.dart
- 채팅 화면
사용자가 로그인 후 진입하는 채팅 화면입니다. Firestore에서 채팅 메시지를 받아오고 말풍선 UI로 표시합니다.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:image_picker/image_picker.dart';
import 'package:file_picker/file_picker.dart';
import 'dart:io';
import 'package:skeletons/skeletons.dart'; // Skeletons 패키지 임포트
import 'chat_bubble.dart';
class ChatScreen extends StatefulWidget {
@override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _messageController = TextEditingController();
Future<void> _sendMessage() async {
final user = FirebaseAuth.instance.currentUser;
if (_messageController.text.isNotEmpty) {
await FirebaseFirestore.instance.collection('chat').add({
'text': _messageController.text,
'createdAt': Timestamp.now(),
'userId': user?.uid,
'username': user?.email,
});
_messageController.clear();
}
}
Future<void> _sendImage() async {
final picker = ImagePicker();
final pickedImage = await picker.getImage(source: ImageSource.gallery);
if (pickedImage == null) return;
final ref = FirebaseStorage.instance
.ref()
.child('chat_images')
.child('${DateTime.now().toIso8601String()}.jpg');
await ref.putFile(File(pickedImage.path));
final url = await ref.getDownloadURL();
FirebaseFirestore.instance.collection('chat').add({
'imageUrl': url,
'createdAt': Timestamp.now(),
'userId': FirebaseAuth.instance.currentUser?.uid,
'username': FirebaseAuth.instance.currentUser?.email,
});
}
Future<void> _sendFile() async {
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result == null) return;
final file = File(result.files.single.path!);
final ref = FirebaseStorage.instance
.ref()
.child('chat_files')
.child('${DateTime.now().toIso8601String()}_${result.files.single.name}');
await ref.putFile(file);
final fileUrl = await ref.getDownloadURL();
FirebaseFirestore.instance.collection('chat').add({
'fileUrl': fileUrl,
'fileName': result.files.single.name,
'createdAt': Timestamp.now(),
'userId': FirebaseAuth.instance.currentUser?.uid,
'username': FirebaseAuth.instance.currentUser?.email,
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Chat Room'),
actions: [
IconButton(
icon: Icon(Icons.logout),
onPressed: () {
FirebaseAuth.instance.signOut();
},
),
],
),
body: Column(
children: [
Expanded(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('chat')
.orderBy('createdAt', descending: true)
.snapshots(),
builder: (ctx, AsyncSnapshot<QuerySnapshot> chatSnapshot) {
if (chatSnapshot.connectionState == ConnectionState.waiting) {
return SkeletonListView();
}
final chatDocs = chatSnapshot.data!.docs;
return ListView.builder(
reverse: true,
itemCount: chatDocs.length,
itemBuilder: (ctx, index) => ChatBubble(
chatDocs[index]['text'] ?? '',
chatDocs[index]['userId'] == FirebaseAuth.instance.currentUser!.uid,
),
);
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _messageController,
decoration: InputDecoration(labelText: 'Send a message...'),
),
),
IconButton(
icon: Icon(Icons.send),
onPressed: _sendMessage,
),
IconButton(
icon: Icon(Icons.image),
onPressed: _sendImage,
),
IconButton(
icon: Icon(Icons.attach_file),
onPressed: _sendFile,
),
],
),
),
],
),
);
}
}
chat_bubble.dart
- 말풍선 UI
채팅 메시지를 말풍선으로 표현하는 위젯입니다.
import 'package:flutter/material.dart';
class ChatBubble extends StatelessWidget {
final String message;
final bool isMe;
ChatBubble(this.message, this.isMe);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: isMe ? MainAxisAlignment.end : MainAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
color: isMe ? Colors.grey[300] : Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(12),
),
width: 140,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16),
margin: EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: Text(
message,
style: TextStyle(
color: isMe ? Colors.black : Colors.white,
),
),
),
],
);
}
}
보안 규칙 설정
Firebase Firestore 및 Storage의 보안 규칙을 적절하게 설정하여 인증된 사용자만 데이터에 접근할 수 있게 해야 합니다.
결론
이 코드는 Firebase와 Flutter를 사용해 로그인, 채팅, 이미지 및 파일 전송이 가능한 간단한 채팅 앱을 구성한 것입니다. 필요한 경우 디자인이나 기능을 확장할 수 있습니다.
댓글 남기기