I’m trying to make this chat app where i want to add an unsend option (allowing the users to delete the messages they sent). I succeeded in getting the unsend option selected but now I cant figure out how to do these few things.
- delete message (somehow get document id of selected message and delete it)
- see if the message to be deleted was sent by the logged in user (this i can figure out on my own ig)
I cant seem to get hang of how to figure out the document id.
Heres the chat screen’s code:
import 'package:flutter/material.dart';
import 'package:messenger/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
final _fireStore = FirebaseFirestore.instance;
late User loggedInUser;
class ChatScreen extends StatefulWidget {
const ChatScreen({super.key});
static String id = '/chat';
@override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final messageTextController = TextEditingController();
final _auth = FirebaseAuth.instance;
late String messageText;
void getCurrentUser() async {
try {
final user = await _auth.currentUser;
if (user != null) {
loggedInUser = user;
}
} catch (e) {
print(e);
}
}
@override
void initState() {
super.initState();
getCurrentUser();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: const Icon(Icons.close),
onPressed: () {
_auth.signOut();
Navigator.pop(context);
}),
],
title: const Text('⚡️Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
const MessagesStream(),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextController,
onChanged: (value) {
messageText = value;
},
decoration: kTextFieldDecoration,
),
),
TextButton(
onPressed: () {
messageTextController.clear();
_fireStore.collection('messages').add({
'text': messageText,
'sender': loggedInUser.email,
'timestamp': FieldValue.serverTimestamp(),
});
},
child: const Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
class MessagesStream extends StatelessWidget {
const MessagesStream({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream:
_fireStore.collection('messages').orderBy('timestamp').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final messages = snapshot.data!.docs.reversed;
List<Messagebubble> messageBubbles = [];
for (var message in messages) {
final messageText = message.get('text');
final messageSender = message.get('sender');
final currentUser = loggedInUser.email;
final messageBubble = Messagebubble(
sender: messageSender,
text: messageText,
isMe: currentUser == messageSender,
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 20.0,
),
children: messageBubbles,
),
);
},
);
}
}
class Messagebubble extends StatefulWidget {
const Messagebubble(
{required this.sender,
required this.text,
required this.isMe,
super.key});
final String sender;
final String text;
final bool isMe;
@override
State<Messagebubble> createState() => _MessagebubbleState();
}
class _MessagebubbleState extends State<Messagebubble> {
late Offset tapXY;
late RenderBox overlay;
// void _getTapPosition(TapDownDetails details) {
// final RenderBox referenceBox = context.findRenderObject() as RenderBox;
// setState(() {
// tapXY = referenceBox.globalToLocal(details.globalPosition);
// });
// }
void _getTapPosition(TapDownDetails detail) {
tapXY = detail.globalPosition;
}
void _showContextMenu(BuildContext context) async {
final RenderObject? overlay =
Overlay.of(context)?.context.findRenderObject();
final result = await showMenu(
context: context,
// Show the context menu at the tap location
position: RelativeRect.fromRect(
Rect.fromLTWH(tapXY.dx, tapXY.dy, 30, 30),
Rect.fromLTWH(0, 0, overlay!.paintBounds.size.width,
overlay.paintBounds.size.height)),
// set a list of choices for the context menu
items: [
const PopupMenuItem(
value: 'unsend',
child: Text('Unsend Message'),
),
]);
// Implement the logic for each choice here
switch (result) {
case 'unsend':
debugPrint('Add To Favorites');
break;
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment:
widget.isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
Text(
widget.sender,
style: const TextStyle(
fontSize: 12.0,
color: Colors.black,
),
),
GestureDetector(
onTapDown: (details) => _getTapPosition(details),
onLongPress: () => _showContextMenu(context),
child: Material(
elevation: 5.0,
borderRadius: BorderRadius.only(
topLeft: widget.isMe
? const Radius.circular(30)
: const Radius.circular(0),
topRight: widget.isMe
? const Radius.circular(0)
: const Radius.circular(30),
bottomLeft: const Radius.circular(30),
bottomRight: const Radius.circular(30),
),
color: widget.isMe ? Colors.lightBlueAccent : Colors.white,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 10.0),
child: Text(
widget.text,
style: const TextStyle(
fontSize: 15.0,
),
),
),
),
),
],
),
);
}
}
2
Answers
You need the ID of the message you want to delete.
See also the docs for deleting Firestore data and the references.
first, let’s say you have a
DocuemntQuery
callesdoc_ref
, and we gor the snapshot from it :you can get the
documentId
from the DocumentSnapshot like this :then you can delete that document with that id :