cofounderella/lib/pages/chat_page.dart

204 lines
5.3 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import '../components/chat_bubble.dart';
import '../components/my_textfield.dart';
import '../constants.dart';
import '../services/auth/auth_service.dart';
import '../services/chat/chat_service.dart';
class ChatPage extends StatefulWidget {
final String receiverEmail;
final String receiverID;
final String chatTitle;
const ChatPage({
super.key,
required this.receiverEmail,
required this.receiverID,
required this.chatTitle,
});
@override
State<ChatPage> createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
// text controller
final TextEditingController _messageController = TextEditingController();
// chat and auth services
final ChatService _chatService = ChatService();
final AuthService _authService = AuthService();
// for textfield focus
FocusNode myFocusNode = FocusNode();
@override
void initState() {
super.initState();
// add listener to focus node
myFocusNode.addListener(() {
if (myFocusNode.hasFocus) {
// cause a delay so that the keyboard has time to show up
// then the amount of remaining space will be calculated
// then scroll down
Future.delayed(
const Duration(milliseconds: 500),
() => scrollDown(),
);
}
});
// wait a bit for listview to be built, then scroll to bottom
Future.delayed(
const Duration(milliseconds: 500),
() => scrollDown(),
);
}
@override
void dispose() {
myFocusNode.dispose();
_messageController.dispose();
super.dispose();
}
// scroll controller
final ScrollController _scrollController = ScrollController();
void scrollDown() {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
);
}
void sendMessage() async {
// send message if not empty
if (_messageController.text.isNotEmpty) {
await _chatService.sendMessage(
widget.receiverID, _messageController.text);
// clear text controller
_messageController.clear();
}
scrollDown();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.chatTitle)),
body: Column(
children: [
// display all messages
Expanded(
child: _buildMessageList(),
),
// user input
_buildUserInput(),
],
),
);
}
Widget _buildMessageList() {
String senderID = _authService.getCurrentUser()!.uid;
return StreamBuilder(
stream: _chatService.getMessages(senderID, widget.receiverID),
builder: (context, snapshot) {
// errors
if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
}
// loading
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading..");
}
// return list view
return ListView(
controller: _scrollController,
children:
snapshot.data!.docs.map((doc) => _buildMessageItem(doc)).toList(),
);
},
);
}
Widget _buildMessageItem(DocumentSnapshot doc) {
Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
// align message to the right if sender is current user, otherwise left
bool isCurrentUser = data[Constants.dbFieldMessageSenderId] ==
_authService.getCurrentUser()!.uid;
var alignment =
isCurrentUser ? Alignment.centerRight : Alignment.centerLeft;
List<String> msgDate =
(data[Constants.dbFieldMessageTimestamp] as Timestamp)
.toDate()
.toIso8601String()
.split("T");
return Container(
alignment: alignment,
child: Column(
crossAxisAlignment:
isCurrentUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
"${msgDate[0]} ${msgDate[1].substring(0, 8)}",
style: TextStyle(color: Colors.grey.shade600),
),
),
ChatBubble(
message: data[Constants.dbFieldMessageText],
isCurrentUser: isCurrentUser,
),
],
),
);
}
Widget _buildUserInput() {
return Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Row(
children: [
// text should take up most of space
Expanded(
child: MyTextField(
controller: _messageController,
hintText: 'Type a message',
obscureText: false,
focusNode: myFocusNode,
),
),
// send button
Container(
decoration: const BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
),
margin: const EdgeInsets.only(right: 25),
child: IconButton(
onPressed: sendMessage,
icon: const Icon(Icons.send),
color: Colors.white,
),
)
],
),
);
}
}