1. 채팅 메세지 전송 시 추가 사항
메세지 전송 버튼을 클릭해 firestore에 저장할 때, Timestamp를 활용해 시간을 기록
await _firestore
.collection('chatting').doc('messages')
.collection('data').doc(widget.chattingRoomId)
.collection('chat').add({
'uid' : _authentication.currentUser!.uid,
'message' : _sendedMessage,
'time' : Timestamp.now(),
});
2. StreamBuilder 및 FutureBuilder 사용
2.0. Stream과 Future의 차이
2.1. 전체 코드
Widget _buildProfileBody() {
return Column(
children: [
Expanded(
// StreamBuilder를 통해 firestore에 chat 정보가 입력되면 실시간으로 가져온다
child: StreamBuilder(
stream: _firestore
.collection('chatting').doc('messages').collection('data')
.doc(widget.chattingInfo.cid).collection('chat')
.orderBy('time', descending: true).snapshots(), // 첫 로딩 시 가장 마지막 message로 가기 위해 내림차순으로 정렬
builder: (context, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return _messageData;
}
ChattingMessageHandler handler = ChattingMessageHandler(widget.chattingInfo.cid);
final chatDocs = snapshot.data!.docs;
// chat bubble을 만들기 위해서는 async 함수를 호출해야 한다.
// 이때 StreamBuilder의 builder는 async 함수화가 되지 않는다.
// 따라서 FutureBuilder를 이용해 async 함수를 호출
return FutureBuilder(
future: handler.getChattingMessages(chatDocs),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
_messageData = ListView.builder(
controller: _scrollController,
reverse: true, // 첫 로딩 시 가장 마지막 message로 가기 위해 사용
itemCount: chatDocs.length,
itemBuilder: (context, index) {
return makeChattingCommentWidget(snapshot.data![index]);
},
);
}
return _messageData;
},
);
},
),
),
MessageSendBar(chattingRoomId: widget.chattingInfo.cid, scrollController: _scrollController),
],
);
}
StreamBuilder의 builder 내부에서 Future값을 리턴받아야 하는데 builder는 async 키워드를 사용할 수 없음으로, FutureBuilder를 통해 Future값을 리턴받게 해주었다.
2.2. StreamBuilder의 stream
stream: _firestore
.collection('chatting').doc('messages').collection('data')
.doc(widget.chattingInfo.cid).collection('chat')
.orderBy('time', descending: true).snapshots(), // 첫 로딩 시 가장 마지막 message로 가기 위해 내림차순으로 정렬
stream에 들어오는 정보를 통해 StreamBuilder는 정보에 변화가 있을 때 마다 정보를 가져온다.
firestore에서는 snapshots을 통해 stream을 제공해주고 있다.
위 코드에서는 /chatting/messages/data/0/chat/ 아래 문서에 추가, 삭제, 변경 등 변화가 있을 경우 stream을 통해 그 변경된 값이 들어온다.
이때 채팅을 시간 순으로 출력해주기 위해 orderBy를 사용해 시간 순으로 정렬해 준다.
snapshots의 return type
Stream<QuerySnapshot<Map<String, dynamic>>> Function({bool includeMetadataChanges})
2.3. StreamBuilder의 builder
builder: (context, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return _messageData;
}
ChattingMessageHandler handler = ChattingMessageHandler(widget.chattingInfo.cid);
final chatDocs = snapshot.data!.docs;
builder 내에서는 내가 원하는 Widget을 만들어서 return해주면 된다.
이때 snapshot을 통해 변경된 데이터가 들어오게 된다. 따라서 snapshot의 state를 보고 새롭게 Widget을 만들어서 return할지, 이전 Widget 또는 로딩 화면을 return할지 판단한다.
snapshot의 데이터를 활용하려면 'snapshot.data!.docs' 구문을 통해 문서를 모두 가져온 후 사용하면 된다.
2.4. FutureBuilder의 future
future: handler.getChattingMessages(chatDocs),
FutureBuilder의 future에는 async 함수를 지정해줄 수 있다.
여기서 설정된 future return 값은 아래 builder 함수의 snapshot에 저장이 된다.
2.5. FutureBuilder의 builder
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
_messageData = ListView.builder(
controller: _scrollController,
reverse: true, // 첫 로딩 시 가장 마지막 message로 가기 위해 사용
itemCount: chatDocs.length,
itemBuilder: (context, index) {
return makeChattingCommentWidget(snapshot.data![index]);
},
);
}
return _messageData;
},
FutureBuilder의 builder도 StreamBuilder의 builder와 똑같이 내가 원하는 Widget를 return해주면 된다.
이때 snapshot의 state를 똑같이 확인을 해주어야 한다.
'플러터(Flutter)' 카테고리의 다른 글
github를 통해 flutter webapp 자동 배포하기 (0) | 2023.07.11 |
---|---|
firebase auth에 계정 등록하는 방법 (0) | 2023.07.04 |
Firestore에 데이터 저장하는 방법 (0) | 2023.06.15 |
Firestore db 권한 설정 (0) | 2023.06.15 |
Flutter Firestore 연동 (0) | 2023.06.12 |