cofounderella/lib/pages/chat_page.dart

216 lines
5.7 KiB
Dart
Raw Normal View History

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
2024-05-26 01:44:49 +02:00
import '../components/chat_bubble.dart';
import '../components/my_textfield.dart';
import '../constants.dart';
2024-05-26 01:44:49 +02:00
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;
2024-06-16 01:41:42 +02:00
final String? profileImageUrl;
const ChatPage({
super.key,
required this.receiverEmail,
required this.receiverID,
required this.chatTitle,
2024-06-16 01:41:42 +02:00
this.profileImageUrl,
});
@override
State<ChatPage> createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
final TextEditingController _messageController = TextEditingController();
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) {
2024-06-16 01:41:42 +02:00
Widget chatHeader = Text(widget.chatTitle);
Widget? userPic;
if (widget.profileImageUrl != null && widget.profileImageUrl!.isNotEmpty) {
2024-06-17 20:39:56 +02:00
userPic = CircleAvatar(
backgroundImage: NetworkImage(widget.profileImageUrl!),
);
2024-06-16 01:41:42 +02:00
}
if (userPic != null) {
chatHeader = Row(
children: [userPic, const SizedBox(width: 7), Text(widget.chatTitle)],
);
}
return Scaffold(
2024-06-16 01:41:42 +02:00
appBar: AppBar(title: chatHeader),
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;
2024-05-03 11:12:36 +02:00
List<String> msgDate =
(data[Constants.dbFieldMessageTimestamp] as Timestamp)
.toDate()
.toIso8601String()
.split("T");
2024-05-03 11:12:36 +02:00
return Container(
alignment: alignment,
child: Column(
crossAxisAlignment:
isCurrentUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
2024-05-03 11:12:36 +02:00
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
'${msgDate[0]} ${msgDate[1].substring(0, 5)}',
style: const TextStyle(color: Colors.grey, fontSize: 10),
2024-05-03 11:12:36 +02:00
),
),
ChatBubble(
message: data[Constants.dbFieldMessageText],
isCurrentUser: isCurrentUser,
),
],
),
);
}
Widget _buildUserInput() {
return Padding(
2024-04-30 22:33:01 +02:00
padding: const EdgeInsets.only(bottom: 10.0),
child: Row(
children: [
// text should take up most of space
Expanded(
child: MyTextField(
controller: _messageController,
2024-05-26 01:44:49 +02:00
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),
2024-04-30 22:33:01 +02:00
color: Colors.white,
),
)
],
),
);
}
}