반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 스프링공부
- 스프링 공부
- nestjs
- Kafka
- JPA 공부
- 자료구조공부
- 스프링부트
- 스프링부트공부
- 플러터 공부
- K8S
- 카프카
- 스프링
- nestjs스터디
- 알고리즘공부
- Axon framework
- JPA
- JPA예제
- 코테준비
- 기술면접공부
- 자바공부
- 플러터 개발
- 코테공부
- 프로그래머스
- nestjs공부
- JPA스터디
- 기술공부
- Flutter
- DDD
- JPA공부
- querydsl
Archives
- Today
- Total
DevBoi
[Flutter] Chatting 모듈 정리 본문
반응형
사용 의존성
firebase_core: ^2.16.0
cloud_firestore: ^4.9.2
chat_bubbles: ^1.5.0
flutter_chat_bubble: ^2.0.2
chat-list
import 'package:get/get.dart';
import 'package:goodshot/global-provider.dart';
import 'package:goodshot/view/chat/screen/chat-list-widget.dart';
import 'package:goodshot/view/group/dto/MyGroupItem.dart';
import 'package:flutter/material.dart';
import 'package:goodshot/view/group/popup/create-group-popup.dart';
import 'package:goodshot/view/group/widget/my-group-widget.dart';
import '../../group/http/group-my-api.dart';
class ChatListScreen extends StatefulWidget {
final GlobalProvider globalProvider = Get.find();
@override
_ChatListState createState() => _ChatListState(groupList: getMyGroupListApi(globalProvider.getUserId()));
}
class _ChatListState extends State<ChatListScreen> {
final GlobalProvider globalProvider = Get.find();
Future<List<MyGroupItem>> groupList;
_ChatListState({required this.groupList});
@override
void initState() {
super.initState();
groupList = getMyGroupListApi(globalProvider.getUserId());
}
@override
Widget build(BuildContext context) {
_fetchData() async {
setState((){
groupList = getMyGroupListApi(globalProvider.getUserId());
});
}
return Scaffold(
appBar: AppBar(
centerTitle: true,
actions:[
//드롭 다운 지역 검색 가능하도록?
]
),
body: Container(
child: FutureBuilder(
future: groupList,
builder: (context,AsyncSnapshot snapshot){
if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
MyGroupItem item = snapshot.data[index];
return MyChatWidget(myGroupItem: item, refreshListFunc: _fetchData);
},);
}
else
{
return Container(
child: Center(
child: Text("Loading..."),
),
);
}
}
),
),
);
}
}
chat-list-widget
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:goodshot/util/const.dart';
import 'package:goodshot/view/chat/screen/chat-detail.dart';
import 'package:goodshot/view/group/dto/MyGroupItem.dart';
import 'package:goodshot/view/group/http/group-join-api.dart';
import 'package:goodshot/view/group/popup/my-group-popup.dart';
import 'package:goodshot/view/group/util/chip-builder.dart';
class MyChatWidget extends StatefulWidget {
MyGroupItem myGroupItem;
final Function() refreshListFunc;
MyChatWidget({Key? key,required this.myGroupItem,required this.refreshListFunc});
@override
_MyChatWidgetState createState() => _MyChatWidgetState(myGroupItem: myGroupItem);
}
class _MyChatWidgetState extends State<MyChatWidget> {
MyGroupItem myGroupItem;
_MyChatWidgetState({required this.myGroupItem});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 1.1),
child: InkWell(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
ListTile(
leading: CircleAvatar(
radius: 25,
backgroundImage: NetworkImage("${myGroupItem.thumbnailImageUrl}"),
),
contentPadding: EdgeInsets.all(0),
title: Text("${myGroupItem.title}",),
subtitle: Text("${myGroupItem.nickname}",),
isThreeLine: false,
trailing:
ElevatedButton(
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Constants.appColor)),
child: const Text('입장하기'),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) => ChatScreen(groupNo: myGroupItem.groupNo))
);
},
),
),
Divider(thickness: 1,),
],
),
onTap: () {
showDialog(
barrierDismissible: true,
context: context, builder: (BuildContext context){
return MyGroupDetailPopup(myGroupItem: myGroupItem);
}
).then((value) => setState((){
widget.refreshListFunc.call();
}));
},
),
);
}
}
chat-bubbles
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:flutter_chat_bubble/chat_bubble.dart';
class ChatBubbles extends StatelessWidget {
const ChatBubbles(this.message,this.isMe,this.nickname,this.imgUrl,{Key? key}) : super(key: key);
final String message;
final bool isMe;
final String nickname;
final String imgUrl;
@override
Widget build(BuildContext context) {
return Stack(
children:[
Row(
mainAxisAlignment: isMe ? MainAxisAlignment.end : MainAxisAlignment.start,
children: [
if(isMe)
Padding(
padding: const EdgeInsets.fromLTRB(0,10,45,0),
child: ChatBubble(
clipper: ChatBubbleClipper8(type: BubbleType.sendBubble),
backGroundColor: Colors.green,
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.4,
),
child: Column(
children: [
Text(nickname),
Text(
message,
style: TextStyle(color: Colors.white),
),
],
),
),
),
),
if(!isMe)
Padding(
padding: const EdgeInsets.fromLTRB(45,10,0,0),
child: ChatBubble(
clipper: ChatBubbleClipper8(type: BubbleType.receiverBubble),
backGroundColor: Colors.grey,
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.4,
),
child: Column(
children: [
Text(nickname),
Text(
message,
style: TextStyle(color: Colors.black),
),
],
),
),
),
)
],
),
Positioned(
top: 0,
left: isMe ? null : 5,
right: isMe ? 5 : null,
child: CircleAvatar(
backgroundImage: NetworkImage(imgUrl),
),
)
]
);
}
}
message
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:get/get.dart';
import 'package:goodshot/global-provider.dart';
import 'package:goodshot/view/chat/widget/chat-bubble.dart';
final GlobalProvider globalProvider = Get.find();
class Messages extends StatelessWidget {
const Messages(this.groupNo,{Key? key}) : super(key: key);
final int groupNo;
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseFirestore.instance.collection(groupNo.toString())
.orderBy('time',descending: true)
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot<Map<String,dynamic>>> snapshot) {
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: CircularProgressIndicator(),
);
}
final chatDoc = snapshot.data!.docs;
return ListView.builder(
reverse: true,
itemCount: chatDoc.length,
itemBuilder: (context,index){
return ChatBubbles(
chatDoc[index]['content'],
chatDoc[index]['userId'] == globalProvider.getUserId(),
chatDoc[index]['nickname'],
chatDoc[index]['imgUrl']
);
});
}
);
}
}
new-message
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class NewMessages extends StatefulWidget {
final String userId;
final String nickname;
final String imgUrl;
const NewMessages({required this.userId,required this.nickname,required this.imgUrl});
@override
_NewMessagesState createState() => _NewMessagesState(userId: userId,nickname: nickname,imgUrl: imgUrl);
}
class _NewMessagesState extends State<NewMessages> {
final String userId;
final String nickname;
final String imgUrl;
_NewMessagesState({required this.userId,required this.nickname, required this.imgUrl});
var inputMessage = '';
final _controller = TextEditingController();
void sendMessage(){
FocusScope.of(context).unfocus();
FirebaseFirestore.instance.collection('1').add({
"content":inputMessage,
"userId":userId,
"imgUrl":imgUrl,
"nickname":nickname,
"time":Timestamp.now()
});
_controller.clear();
}
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 8),
padding: EdgeInsets.all(8),
child: Row(
children: [
Expanded(
child: TextField(
maxLines: null,
controller: _controller,
decoration: InputDecoration(
labelText: 'Send a message....'),
onChanged: (value) {
setState(() {
inputMessage = value;
});
},
)
),
IconButton(
onPressed: inputMessage.trim().isEmpty ? null : sendMessage,
icon: Icon(Icons.send),
color: Colors.blue
)
],
),
);
}
}
chat-detail.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:goodshot/global-provider.dart';
import 'package:goodshot/view/chat/widget/message.dart';
import 'package:goodshot/view/chat/widget/new-message.dart';
final GlobalProvider globalProvider = Get.find();
class ChatScreen extends StatefulWidget {
const ChatScreen({super.key, required this.groupNo});
final int groupNo;
@override
ChatScreenState createState() => ChatScreenState(groupNo: groupNo);
}
class ChatScreenState extends State<ChatScreen> {
final GlobalProvider globalProvider = Get.find();
late String userId;
final int groupNo;
ChatScreenState({required this.groupNo});
@override
void dispose() {
super.dispose();
}
@override
void initState() {
userId = globalProvider.getUserId();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
title: const Text(
'Good Shot',
),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_sharp,
color: Colors.black54),
onPressed: () {
Navigator.pop(context);
},
),
),
backgroundColor: Colors.white,
extendBodyBehindAppBar: false,
body: Container(
padding: EdgeInsets.all(50),
child: Column(
children: [
Expanded(child: Messages(groupNo)),
NewMessages(userId: globalProvider.getUserId(),nickname: globalProvider.getNickName(),imgUrl: globalProvider.getImgUrl(),)
],
),
),
);
}
}
chat-list, chat-list-widget은 채팅 리스트화면을 구성하는 소스이다.
chat-bubble은 메시지 관련 말풍선 소스이다.
new-message 는 새로운메시지로직이다. 새로운 그룹에 데이터를 넣는 소스이다.
message는 시간을 역순으로 정렬하거나 isMe를 세팅해서 나라면 오른쪽 타인이라면 왼쪽으로 세팅하는 소스들이 있다.
해당 관련된 소스를 개발하면 아래와 같은 모듈을 완성시킬수 있다. 사람의 이미지나 닉네임, 메시지를 정의할수있다.
전체적인 패키지는 아래와 같이 정리했다.
이렇게 하면 파이어베이스를 활용한 채팅 모듈을 개발할 수 있다.
반응형
'[Mobile] > [Flutter]' 카테고리의 다른 글
[Flutter] Firebase Storage, Database (1) | 2023.10.22 |
---|---|
[Flutter] ios 빌드 구성 차이 (0) | 2023.10.12 |
[Flutter] IOS관련 리빌드 (0) | 2023.10.07 |
[Flutter] FireStore를 활용해 채팅 모듈 구현 [1] (0) | 2023.09.24 |
[Flutter] 오류 조치 사항 (0) | 2023.09.23 |