Skip to content

Instantly share code, notes, and snippets.

@TasyaShark
Last active May 13, 2024 11:58
Show Gist options
  • Save TasyaShark/ec1db6a029eddc89bd88b7c57bd4b175 to your computer and use it in GitHub Desktop.
Save TasyaShark/ec1db6a029eddc89bd88b7c57bd4b175 to your computer and use it in GitHub Desktop.
ChatScreen
class ChatScreen extends StatelessWidget {
final CubeUser _cubeUser;
final CubeDialog _cubeDialog;
const ChatScreen(this._cubeUser, this._cubeDialog, {super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_cubeDialog.name ?? ''),
),
body: ChatScreenBody(_cubeUser, _cubeDialog),
);
}
}
class ChatScreenBody extends StatefulWidget {
final CubeUser currentUser;
final CubeDialog cubeDialog;
const ChatScreenBody(this.currentUser, this.cubeDialog, {super.key});
@override
State createState() => ChatScreenBodyState();
}
class ChatScreenBodyState extends State<ChatScreenBody> {
final Map<int?, CubeUser?> _occupants = {};
late bool isLoading;
List<CubeMessage> listMessage = [];
String userStatus = '';
final TextEditingController textEditingController = TextEditingController();
final ScrollController listScrollController = ScrollController();
StreamSubscription<CubeMessage>? msgSubscription;
StreamSubscription<MessageStatus>? deliveredSubscription;
StreamSubscription<MessageStatus>? readSubscription;
@override
void initState() {
super.initState();
_initCubeChat();
isLoading = false;
}
@override
void dispose() {
msgSubscription?.cancel();
deliveredSubscription?.cancel();
readSubscription?.cancel();
textEditingController.dispose();
super.dispose();
}
void onReceiveMessage(CubeMessage message) {
if (message.dialogId != widget.cubeDialog.dialogId) return;
addMessageToListView(message);
}
void onDeliveredMessage(MessageStatus status) {
updateReadDeliveredStatusMessage(status, false);
}
void onReadMessage(MessageStatus status) {
updateReadDeliveredStatusMessage(status, true);
}
void onSendChatMessage(String content) {
if (content.trim() != '') {
final message = createCubeMsg();
message.body = content.trim();
onSendMessage(message);
} else {
print('Nothing to send');
}
}
CubeMessage createCubeMsg() {
var message = CubeMessage();
message.dateSent = DateTime.now().millisecondsSinceEpoch ~/ 1000;
message.markable = true;
message.saveToHistory = true;
return message;
}
void onSendMessage(CubeMessage message) async {
textEditingController.clear();
await widget.cubeDialog.sendMessage(message);
message.senderId = widget.currentUser.id;
addMessageToListView(message);
listScrollController.animateTo(0.0,
duration: const Duration(milliseconds: 300), curve: Curves.easeOut);
}
updateReadDeliveredStatusMessage(MessageStatus status, bool isRead) {
setState(() {
CubeMessage? msg = listMessage
.where((msg) => msg.messageId == status.messageId)
.firstOrNull;
if (msg == null) return;
if (isRead) {
msg.readIds == null
? msg.readIds = [status.userId]
: msg.readIds?.add(status.userId);
} else {
msg.deliveredIds == null
? msg.deliveredIds = [status.userId]
: msg.deliveredIds?.add(status.userId);
}
});
}
addMessageToListView(CubeMessage message) {
setState(() {
isLoading = false;
int existMessageIndex = listMessage.indexWhere((cubeMessage) {
return cubeMessage.messageId == message.messageId;
});
if (existMessageIndex != -1) {
listMessage[existMessageIndex] = message;
} else {
listMessage.insert(0, message);
}
});
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
buildListMessage(),
buildInput(),
],
),
buildLoading(),
],
),
);
}
Widget buildItem(int index, CubeMessage message) {
markAsReadIfNeed() {
var isOpponentMsgRead = message.readIds != null &&
message.readIds!.contains(widget.currentUser.id);
if (message.senderId != widget.currentUser.id && !isOpponentMsgRead) {
if (message.readIds == null) {
message.readIds = [widget.currentUser.id!];
} else {
message.readIds!.add(widget.currentUser.id!);
}
if (CubeChatConnection.instance.chatConnectionState ==
CubeChatConnectionState.Ready) {
widget.cubeDialog.readMessage(message);
}
}
}
Widget getReadDeliveredWidget() {
bool messageIsRead() {
if (widget.cubeDialog.type == CubeDialogType.PRIVATE) {
return message.readIds != null &&
(message.recipientId == null ||
message.readIds!.contains(message.recipientId));
}
return message.readIds != null &&
message.readIds!.any((int id) =>
id != widget.currentUser.id && _occupants.keys.contains(id));
}
bool messageIsDelivered() {
if (widget.cubeDialog.type == CubeDialogType.PRIVATE) {
return message.deliveredIds != null &&
(message.recipientId == null ||
message.deliveredIds!.contains(message.recipientId));
}
return message.deliveredIds != null &&
message.deliveredIds!.any((int id) =>
id != widget.currentUser.id && _occupants.keys.contains(id));
}
if (messageIsRead()) {
return getMessageStateWidget(MessageState.read);
} else if (messageIsDelivered()) {
return getMessageStateWidget(MessageState.delivered);
} else {
return getMessageStateWidget(MessageState.sent);
}
}
Widget getDateWidget() {
return Text(
DateFormat('HH:mm').format(
DateTime.fromMillisecondsSinceEpoch(message.dateSent! * 1000)),
style: const TextStyle(
color: Colors.grey, fontSize: 12.0, fontStyle: FontStyle.italic),
);
}
Widget getMessageBubble(bool isOwnMessage) {
if (!isOwnMessage) {
markAsReadIfNeed();
}
return Column(
key: Key('${message.messageId}'),
children: <Widget>[
Row(
mainAxisAlignment:
isOwnMessage ? MainAxisAlignment.end : MainAxisAlignment.start,
children: <Widget>[
Flexible(
child: Container(
constraints: const BoxConstraints(maxWidth: 480),
padding: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 4.0),
decoration: BoxDecoration(
color: isOwnMessage
? Colors.grey.shade200
: Colors.blueGrey.shade800,
borderRadius: BorderRadius.circular(8.0)),
margin: EdgeInsets.only(
bottom: isOwnMessage ? 20.0 : 10.0,
right: isOwnMessage ? 10.0 : 0,
left: isOwnMessage ? 0 : 10.0),
child: Column(
crossAxisAlignment: isOwnMessage
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
Text(
message.body!,
style: TextStyle(
color: isOwnMessage
? Colors.blueGrey.shade800
: Colors.grey.shade200),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
getDateWidget(),
if (isOwnMessage) getReadDeliveredWidget(),
],
),
]),
),
)
],
),
],
);
}
return getMessageBubble(message.senderId == widget.currentUser.id);
}
Widget buildLoading() {
return Positioned(
child: isLoading
? Container(
color: Colors.white.withOpacity(0.8),
child: const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.green),
),
))
: const SizedBox.shrink());
}
Widget buildInput() {
return Container(
width: double.infinity,
height: 50.0,
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.blueGrey.shade800, width: 0.5)),
color: Colors.white),
child: Row(
children: <Widget>[
Flexible(
child: TextField(
autofocus: true,
keyboardType: TextInputType.multiline,
maxLines: null,
style: TextStyle(color: Colors.blueGrey.shade800, fontSize: 15.0),
controller: textEditingController,
decoration: const InputDecoration.collapsed(
hintText: 'Type your message...',
hintStyle: TextStyle(color: Colors.grey),
),
),
),
// Button send message
Material(
color: Colors.white,
child: Container(
margin: const EdgeInsets.only(left: 4, right: 2.0),
child: IconButton(
icon: const Icon(Icons.send),
onPressed: () => onSendChatMessage(textEditingController.text),
color: Colors.blueGrey.shade800,
),
),
),
],
),
);
}
Widget buildListMessage() {
getWidgetMessages(listMessage) {
return ListView.builder(
padding: const EdgeInsets.all(10.0),
itemBuilder: (context, index) => buildItem(index, listMessage[index]),
itemCount: listMessage.length,
reverse: true,
controller: listScrollController,
);
}
return Flexible(
child: StreamBuilder<List<CubeMessage>>(
stream: getMessagesList().asStream(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.green)));
} else {
listMessage = snapshot.data ?? [];
return getWidgetMessages(listMessage);
}
},
),
);
}
Future<List<CubeMessage>> getMessagesList() {
if (listMessage.isNotEmpty) return Future.value(listMessage);
var params = GetMessagesParameters();
params.sorter = RequestSorter('desc', '', 'date_sent');
return getMessages(
widget.cubeDialog.dialogId!, params.getRequestParameters())
.then((getMessagesResult) {
return getAllUsersByIds(widget.cubeDialog.occupantsIds!.toSet())
.then((getUsersResult) {
_occupants
.addAll({for (var item in getUsersResult!.items) item.id: item});
return getMessagesResult?.items ?? [];
});
});
}
_initCubeChat() {
msgSubscription = CubeChatConnection
.instance.chatMessagesManager!.chatMessagesStream
.listen(onReceiveMessage);
deliveredSubscription = CubeChatConnection
.instance.messagesStatusesManager!.deliveredStream
.listen(onDeliveredMessage);
readSubscription = CubeChatConnection
.instance.messagesStatusesManager!.readStream
.listen(onReadMessage);
}
}
Widget getMessageStateWidget(MessageState? state) {
Widget result;
switch (state) {
case MessageState.read:
result = const Stack(children: <Widget>[
Icon(
Icons.check_rounded,
size: 15.0,
color: Colors.green,
),
Padding(
padding: EdgeInsets.only(
left: 4,
),
child: Icon(
Icons.check_rounded,
size: 15.0,
color: Colors.green,
),
)
]);
break;
case MessageState.delivered:
result = Stack(children: <Widget>[
Icon(
Icons.check_rounded,
size: 15.0,
color: Colors.grey.shade700,
),
Padding(
padding: const EdgeInsets.only(left: 4),
child: Icon(
Icons.check_rounded,
size: 15.0,
color: Colors.grey.shade700,
),
)
]);
break;
case MessageState.sent:
result = Icon(
Icons.check_rounded,
size: 15.0,
color: Colors.grey.shade700,
);
break;
default:
result = const SizedBox.shrink();
break;
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment