'GroupChat with Flutter and Firebase
I'm new on flutter and I built a chat app with Firebase. My app consist to let the user create a group chat and add other users in then start a chat. But what I'm not able to do is to make the name of the sender appear on his message. Here's my code
Hello, I'm new on flutter and I built a chat app with Firebase. My app consist to let the user create a group chat and add other users in then start a chat. But what I'm not able to do is to make the name of the sender appear on his message. Here's my code
if (type == 'text') {
return MessageTile(
senderName: snapshot.data.documents[index].data["sendBy"],
message: snapshot.data.documents[index].data["message"],
messageType: 'text',
sendByMe: Constants.myName ==
snapshot.data.documents[index].data["sendBy"],
);
}
import 'dart:io';
import 'dart:isolate';
import 'package:chatapp/models/connection.dart';
import 'package:chatapp/models/user_connection.dart';
import 'package:chatapp/helper/constants.dart';
import 'package:chatapp/helper/encoding_provider.dart';
import 'package:chatapp/helper/helperfunctions.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:path_provider/path_provider.dart';
/*// This function happens in the isolate.
void entryPoint(SendPort context) {
// Calling initialize from the entry point with the context is
// required if communication is desired. It returns a messenger which
// allows listening and sending information to the main isolate.
final messenger = HandledIsolate.initialize(context);
// Triggered every time data is received from the main isolate.
messenger.listen((msg) async {
// Use a plugin to get some new value to send back to the main isolate.
final dir = await getApplicationDocumentsDirectory();
messenger.send(msg + dir.path);
});
}
*/
class DatabaseMethods {
Future<void> addUserInfo(userData) async {
Firestore.instance.collection("users").add(userData).catchError((e) {
print(e.toString());
});
}
getUserInfoByEmail(String email) async {
QuerySnapshot snapshot = await Firestore.instance
.collection("users")
.where("userEmail", isEqualTo: email.trim())
.where("type", isEqualTo: "parentprofile")
.limit(1)
.getDocuments()
.catchError((e) {
print(e.toString());
});
return UserConnection.fromDocumentSnapshot(snapshot.documents[0]);
}
getUserInfoByRef(DocumentReference reference) async {
return Firestore.instance.document(reference.path).get().catchError((e) {
print(e.toString());
});
}
searchByName(String searchField) {
return Firestore.instance
.collection("users")
.where('userName', isEqualTo: searchField)
.getDocuments();
}
String getNewConversationId() {
return Firestore.instance.collection("conversations").document().documentID;
}
addChatRoom(chatRoom, chatRoomId) {
Firestore.instance
.collection("conversations")
.document(chatRoomId)
.setData(chatRoom)
.catchError((e) {
print(e);
}).then((_) {
return chatRoomId;
});
}
addUserToConversation({
chatRoomId,
userEmail,
adminName,
Map<String, bool> members,
}) async {
UserConnection user = await getUserInfoByEmail(userEmail);
print(members.entries.toString());
print("User Name: ${user.userName}");
members.addAll({"${user.userName}": true});
print(members.entries.toString());
Firestore.instance
.collection("conversations")
.document(chatRoomId)
.updateData({"users": members});
}
/*Future<UserConnection> getUserNameByEmail(String email) async {
QuerySnapshot userSnapshot = await Firestore.instance
.collection("users")
.where('userEmail', isEqualTo: email)
.where("type", isEqualTo: "parentprofile")
.limit(1)
.getDocuments();
return UserConnection.fromDocumentSnapshot(userSnapshot.documents[0]);
}*/
getChats(String chatRoomId) async {
return Firestore.instance
.collection("conversations")
.document(chatRoomId)
.collection("messages")
.orderBy('time')
.snapshots();
}
Future<void> addMessage(String chatRoomId, chatMessageData) {
Firestore.instance
.collection("conversations")
.document(chatRoomId)
.collection("messages")
.add(chatMessageData)
.catchError((e) {
print(e.toString());
});
}
Future<void> addImageMessage(
String chatRoomId, imageMessageData, filePath) async {
DocumentReference docRef = await Firestore.instance
.collection("conversations")
.document(chatRoomId)
.collection("messages")
.add(imageMessageData)
.catchError((e) {
print(e.toString());
});
final sfile = (await HelperFunctions.getLocalFile(
imageMessageData['fileName'],
type: imageMessageData['contentType']));
final path = sfile.path;
final file =
await HelperFunctions.compressAndGetFile(filePath, descPath: path);
final basename = HelperFunctions.getFileName(filePath);
final StorageReference ref = FirebaseStorage.instance
.ref()
.child('conversations/$chatRoomId')
.child(basename);
StorageUploadTask uploadTask = ref.putFile(file);
StorageTaskSnapshot snapshot = await uploadTask.onComplete;
String url = await snapshot.ref.getDownloadURL();
String accessToken = ((url.split('&'))[1].split('='))[1];
docRef.updateData({
'mediaURL': url,
'uploaded': true,
'localpath': '',
'accessToken': accessToken,
});
//return url;
}
Future<void> addDocumentMessage(String chatRoomId, messageData) async {
DocumentReference docRef = await Firestore.instance
.collection("conversations")
.document(chatRoomId)
.collection("messages")
.add(messageData)
.catchError((e) {
print(e.toString());
});
//final filePath = messageData['localpath'];
//final file = await HelperFunctions.compressAndGetFile(File(filePath));
//final basename = HelperFunctions.getFileName(filePath);
final StorageReference ref = FirebaseStorage.instance
.ref()
.child('conversations/$chatRoomId')
.child(messageData['fileName']);
print("Filename: ${messageData['fileName']}");
String fromPath =
await HelperFunctions.getTempPath(messageData['fileName']);
String filePath = await HelperFunctions.copyFile(
fromPath, messageData['fileName'], messageData['contentType']);
print("DocumentFilePath: $filePath");
StorageUploadTask uploadTask = ref.putFile(File(filePath));
StorageTaskSnapshot snapshot = await uploadTask.onComplete;
String url = await snapshot.ref.getDownloadURL();
print(((url.split('&'))[1].split('='))[1]);
String accessToken = ((url.split('&'))[1].split('='))[1];
docRef.updateData({
'docURL': url,
'accessToken': accessToken,
'uploaded': true,
});
//return url;
}
Future<void> addAudioMessage(String chatRoomId, messageData) async {
DocumentReference docRef = await Firestore.instance
.collection("conversations")
.document(chatRoomId)
.collection("messages")
.add(messageData)
.catchError((e) {
print(e.toString());
});
//final filePath = messageData['localpath'];
//final file = await HelperFunctions.compressAndGetFile(File(filePath));
//final basename = HelperFunctions.getFileName(filePath);
final StorageReference ref = FirebaseStorage.instance
.ref()
.child('conversations/$chatRoomId')
.child(messageData['fileName']);
StorageUploadTask uploadTask = ref.putFile(
await HelperFunctions.getLocalFile(messageData['fileName'],
type: messageData['contentType']));
StorageTaskSnapshot snapshot = await uploadTask.onComplete;
String url = await snapshot.ref.getDownloadURL();
print(((url.split('&'))[1].split('='))[1]);
String accessToken = ((url.split('&'))[1].split('='))[1];
docRef.updateData({
'mediaURL': url,
'accessToken': accessToken,
'uploaded': true,
});
//return url;
}
Future uploadVideoThumbnail(String chatRoomId, String videopath) async {
/*RegExp regExp = new RegExp(
r"/^[a-zA-Z0-9_-]+$/",
caseSensitive: false,
multiLine: false,
);
if (!regExp.hasMatch(videopath)) {
String tempDir = (await getTemporaryDirectory()).path;
String fileExtension = videopath.split('.').last;
String newPath =
tempDir + '/' + DateTime.now().millisecondsSinceEpoch.toString() + '.' + fileExtension;
File tempFile =
await File(videopath).copy(newPath);
// you can use this new file path for making the thumbnail without error
videopath = tempFile.path;
}*/
/*final sfile = (await HelperFunctions.getLocalFile(
'${DateTime.now().millisecondsSinceEpoch.toString()}.jpg',
type: 'video/thumbnail'));*/
final tmpFile = (await HelperFunctions.getLocalFile(
'${DateTime.now().millisecondsSinceEpoch.toString()}.jpg',
type: 'video/thumbnail'));
final data = <String>[videopath, tmpFile.path];
/*final thumbnailFile = await VideoThumbnail.thumbnailFile(
video: videopath,
quality: 10,
imageFormat: ImageFormat.JPEG,// default(-1)
thumbnailPath: tmpFile.path,
);*/
final thumbnailFile = await EncodingProvider.getThumbnail(data);
final sfilePath =
thumbnailFile; //await compute(EncodingProvider.getThumbnail, data);
print("Thumbnail String: $sfilePath");
/*final sfile = (await HelperFunctions.getLocalFile(
DateTime.now().toIso8601String(),
type: 'video/thumbnail'));*/
//await sfile.writeAsBytes(thumbnailFile.readAsBytesSync());
File sfile = File(sfilePath);
String fileName = HelperFunctions.getFileName(sfile.path);
final StorageReference ref = FirebaseStorage.instance
.ref()
.child('conversations/$chatRoomId')
.child(fileName);
StorageUploadTask uploadTask = ref.putFile(sfile);
StorageTaskSnapshot snapshot = await uploadTask.onComplete;
String url = await snapshot.ref.getDownloadURL();
print('Thumbnail Token: ${((url.split('&'))[1].split('='))[1]}');
//String accessToken = ((url.split('&'))[1].split('='))[1];
var thumbnailDetails = {'thumbnailURL': url, 'thumbnailName': fileName};
return thumbnailDetails;
}
Future<void> addVideoMessage(
String chatRoomId, Map<String, dynamic> messageData) async {
// final video = (await HelperFunctions.getLocalFile(messageData['fileName'],
// type: messageData['contentType'])).path;
final video =
await HelperFunctions.getTempPath('Trimmer/${messageData['fileName']}');
final thumbnailDetails = await uploadVideoThumbnail(chatRoomId, video);
messageData.addAll(thumbnailDetails);
DocumentReference docRef = await Firestore.instance
.collection("conversations")
.document(chatRoomId)
.collection("messages")
.add(messageData)
.catchError((e) {
print(e.toString());
});
//final filePath = messageData['localpath'];
//final file = await HelperFunctions.compressAndGetFile(File(filePath));
//final basename = HelperFunctions.getFileName(filePath);
String videoPath = video;
print('VideoPath: $videoPath');
//final _flutterVideoCompress = FlutterVideoCompress();
/*VideoCompress videoCompress = VideoCompress();
if(videoCompress.isCompressing){
print("Video was compressing. Cancelling previous compression");
await videoCompress.cancelCompression();
await videoCompress.deleteAllCache();
}*/
/*final info = await videoCompress.compressVideo(videoPath,
deleteOrigin: false, quality: VideoQuality.DefaultQuality);*/
num bitrate = EncodingProvider.getBitRate(
await EncodingProvider.getMediaInformation(videoPath));
List data = <String>[
videoPath,
"${bitrate}k",
];
final output = await EncodingProvider.compressVideo(data);
print(
"CompressedOuput: $output\nUncompressed Filename: ${messageData['fileName']}");
//final info = await compute(EncodingProvider.compressVideo, data);
//print('Compressed Video\nsize: ${info.filesize}\npath:${info.path}');
String newPath = await HelperFunctions.copyFile(
videoPath, messageData['fileName'], 'video');
final StorageReference ref = FirebaseStorage.instance
.ref()
.child('conversations/$chatRoomId')
.child(messageData['fileName']);
StorageUploadTask uploadTask = ref.putFile(File(newPath));
StorageTaskSnapshot snapshot = await uploadTask.onComplete;
String url = await snapshot.ref.getDownloadURL();
print(((url.split('&'))[1].split('='))[1]);
String accessToken = ((url.split('&'))[1].split('='))[1];
docRef.updateData({
'mediaURL': url,
'accessToken': accessToken,
'uploaded': true,
});
//return url;
}
getUserConversations(String itIsMyName) async {
return Firestore.instance
.collection("conversations")
.where('users.$itIsMyName', isEqualTo: true)
.snapshots();
}
/// 1.create a chatroom, send user to the chatroom, other userdetails
Connection createNewConversation({String userName, String title}) {
Map<String, bool> users;
if (userName != null) {
users = {"${Constants.myName.trim()}": true, "${userName.trim()}": true};
} else {
users = {"${Constants.myName.trim()}": true};
}
Map<String, String> role = {"${Constants.myName.trim()}": "admin"};
//String chatRoomId = getChatRoomId(Constants.myName.trim(),userName.trim());
String chatRoomId = getNewConversationId();
print("user: ${users[0]}\nchatRoomId: $chatRoomId\nTitile: $title");
Map<String, dynamic> chatRoom = {
"users": users,
"convId": chatRoomId,
"title": title,
"role": role,
};
addChatRoom(chatRoom, chatRoomId);
return Connection.fromJson(chatRoom);
}
}
import 'package:chatapp/models/connection.dart';
import 'package:chatapp/helper/authenticate.dart';
import 'package:chatapp/helper/constants.dart';
import 'package:chatapp/helper/helperfunctions.dart';
import 'package:chatapp/helper/theme.dart';
import 'package:chatapp/services/auth.dart';
import 'package:chatapp/services/database.dart';
import 'package:chatapp/views/chat.dart';
import 'package:chatapp/views/search.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class ChatRoom extends StatefulWidget {
@override
_ChatRoomState createState() => _ChatRoomState();
}
class _ChatRoomState extends State<ChatRoom> {
Stream chatRooms;
//List of all users connection
List<Connection> connectionList = List<Connection>();
var controller = TextEditingController();
Widget chatRoomsList() {
return StreamBuilder(
stream: chatRooms,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.documents.length,
shrinkWrap: true,
itemBuilder: (context, index) {
Connection connection = Connection.fromDocumentSnapshot(
snapshot.data.documents[index]);
connectionList.add(connection);
return ChatRoomsTile(
userName: snapshot.data.documents[index].data['title']
.toString()
.replaceAll("_", "")
.replaceAll(Constants.myName, ""),
// chatRoomId: snapshot.data.documents[index].data["convId"],
connectionInfo: connection,
);
})
: Container();
},
);
}
@override
void initState() {
getUserInfogetChats();
super.initState();
}
@override
void dispose() {
// Clean up the controller when the widget is disposed.
controller.dispose();
super.dispose();
}
getUserInfogetChats() async {
Constants.myName = await HelperFunctions.getUserNameSharedPreference();
DatabaseMethods().getUserConversations(Constants.myName).then((snapshots) {
setState(() {
chatRooms = snapshots;
print(
"we got the data + ${chatRooms.toString()} this is name ${Constants.myName}");
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"RealDiagnosis",
style: TextStyle(
color: Colors.white,
),
),
elevation: 0.0,
centerTitle: false,
actions: [
GestureDetector(
onTap: () async {
return await showMaterialModalBottomSheet(
expand: false,
context: context,
//backgroundColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20))),
bounce: true,
enableDrag: true,
builder: (context, scrollController) {
return Container(
width: MediaQuery.of(context).size.width,
height: (MediaQuery.of(context).size.height / 3) + 150,
child: Column(
children: [
Padding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 8),
child: Text(
"Add New Connection",
style: Theme.of(context).textTheme.headline6,
),
),
Divider(
height: 1,
),
Container(
height: 200,
margin:
EdgeInsets.symmetric(horizontal: 30, vertical: 8),
child: ListView(
padding: EdgeInsets.zero,
physics: ClampingScrollPhysics(),
children: [
TextFormField(
cursorColor: Colors.black,
controller: controller,
keyboardType: TextInputType.text,
decoration: new InputDecoration(
//border: InputBorder.none,
border: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.blue)),
icon: Icon(Icons.edit),
//disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.only(
left: 15,
bottom: 6,
top: 11,
right: 15,
),
hintText:
"Type connection title (subject) here...",
),
style: TextStyle(
fontSize: 24,
),
),
SizedBox(
height: 18,
),
RaisedButton(
onPressed: () async {
var _title = controller.text;
debugPrint(_title);
Connection connection =
DatabaseMethods().createNewConversation(
title: _title,
);
controller.text = "";
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
connectionInfo: connection,
)));
},
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(16))),
color: Theme.of(context).indicatorColor,
textColor: Colors.white,
child: Container(
color: Colors.transparent,
height: 58,
child: Center(
child: Text(
"Create Connection",
style: TextStyle(fontSize: 18),
),
),
),
)
],
),
)
],
),
);
},
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.add)),
),
GestureDetector(
onTap: () async {
AuthService().signOut();
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => Authenticate()));
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.exit_to_app)),
)
],
),
body: Container(
child: chatRoomsList(),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.search),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Search()));
},
),
);
}
}
class ChatRoomsTile extends StatelessWidget {
final String userName;
final Connection connectionInfo;
ChatRoomsTile({this.userName, this.connectionInfo});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
connectionInfo: connectionInfo,
)));
},
child: Container(
color: Colors.black26,
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 20),
child: Row(
children: [
Container(
height: 30,
width: 30,
decoration: BoxDecoration(
color: CustomTheme.colorAccent,
borderRadius: BorderRadius.circular(30)),
child: Text(userName.substring(0, 1),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontFamily: 'OverpassRegular',
fontWeight: FontWeight.w300)),
),
SizedBox(
width: 12,
),
Text(userName,
textAlign: TextAlign.start,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontFamily: 'OverpassRegular',
fontWeight: FontWeight.w300))
],
),
),
);
}
}
class Constants{
static String myName = "";
}
class User {
final String uid;
User({this.uid});
}
Solution 1:[1]
If all the users listen to the same Firestore Collection for the group chat, all users should be able to read the message sent. Checking the snippet you've provided, you need to provide how MessageTile are displayed. I suggest using a StreamBuilder - listening to the Firestore Collection with a Stream. The Widget should automatically rebuild if there's change detected on Stream, which should consequentially update its children. I see that you've already implemented a StreamBuilder for chatRooms, you may apply a similar approach for the chat room group.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Omatt |
