i have created a chat app and everything works fine except two things
1- in fire base store i get too much reads even if i do nothing, for example i just used my chat app for 5 minuets i got around 6000 reads
2- when i chat to any person some messages don't get received, this is an example
the other person(wiliam) did not receive three messages( yeah, k , now).
///////////
this is the stream builder code to display messages
class Streambuild extends StatelessWidget {
final String roomid;
Streambuild({#required this.roomid});
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _store
.collection('FCHATROOM')
.doc(roomid)
.collection("CHAT")
.orderBy('time', descending: true)
.snapshots(),
builder: (context, snap) {
if (!snap.hasData) {
return Center(
child: Text('No Messages'),
);
}
return Expanded(
child: ListView.builder(
itemCount: snap.data.docs.length,
reverse: true,
shrinkWrap: true,
itemBuilder: (context, index) {
return Bubblemsg(
txt: snap.data.docs[index].data()['message'],
sender: snap.data.docs[index].data()['sender'],
realtime: snap.data.docs[index].data()['realtime'],
isMe: (snap.data.docs[index].data()['sender'] ==
_auth.currentUser.email),
);
}),
);
});
}
}
bubble message code:
class Bubblemsg extends StatelessWidget {
Bubblemsg({this.sender, this.txt, this.isMe, this.realtime});
final String txt;
final String sender;
final bool isMe;
final String realtime;
#override
Widget build(BuildContext context) {
var myprov = Provider.of<Help>(context, listen: false);
return Container(
width: double.infinity,
child: Bubble(
elevation: 5,
padding: BubbleEdges.all(10),
margin: isMe
? BubbleEdges.only(top: 10, right: 5, left: 30)
: BubbleEdges.only(top: 10, left: 5, right: 30),
alignment: isMe ? Alignment.topRight : Alignment.topLeft,
nip: isMe ? BubbleNip.rightTop : BubbleNip.leftTop,
color: isMe ? myprov.mid : Colors.grey[900],
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
txt != null
? txt.startsWith("https://firebasestorage.googleapis.com/v0")
? Imagecontainer(
url: txt,
)
: SelectableText(
txt != null ? txt : "",
style: TextStyle(
color: myprov.word,
fontSize: 20,
),
)
: Text(""),
Padding(
padding: EdgeInsets.only(top: 10.0),
child: Text(
realtime != null ? realtime : "",
style: TextStyle(fontSize: 15, color: myprov.word),
textAlign: TextAlign.end,
),
)
],
),
),
);
}
}
Related
I am getting data from this stream builder and showing it inside a stateful widget.
Widget chatRoomsLists() {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('chatrooms').orderBy("lastMessageSendTs", descending: true)
.where("users", arrayContains:myUserName)
.snapshots(),
builder: (context, snapshot){
return snapshot.hasData ? ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context,index){
DocumentSnapshot ds = snapshot.data!.docs[index];
return ChatRoomListTile(ds['lastMessage'], ds.id,myUserName! );
}) : Center(child: CircularProgressIndicator());
}) ;
}
and showing it here
class ChatRoomListTile extends StatefulWidget {
final String lastMessage , chatRoomId, myUsername;
ChatRoomListTile(this.lastMessage, this.chatRoomId, this.myUsername,);
#override
_ChatRoomListTileState createState() => _ChatRoomListTileState();
}
class _ChatRoomListTileState extends State<ChatRoomListTile> {
String profilePicUrl = 'https://miro.medium.com/max/875/0*H3jZONKqRuAAeHnG.jpg' , name = '' ,username = "";
getThisUserInfo() async {
print('userinfo called');
username = widget.chatRoomId.replaceAll(widget.myUsername, "").replaceAll("_", "");
QuerySnapshot querySnapshot = await DatabaseMethods().getUserInfo(username);
// print("something bla bla ${querySnapshot.docs[0].id} ${querySnapshot.docs[0]["name"]} ${querySnapshot.docs[0]["profileURL"]}");
name = "${querySnapshot.docs[0]["name"]}";
profilePicUrl = "${querySnapshot.docs[0]["profileURL"]}";
setState(() {});
}
#override
void initState() {
getThisUserInfo();
super.initState();
}
#override
Widget build(BuildContext context) {
print('BUILDING IS CALLED');
return GestureDetector(
onTap: () {
print('name is $username and ${widget.myUsername}');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chatting(username, widget.myUsername)));
},
child: Column(
children: [
Container(
margin: EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(30),
child: Image.network(profilePicUrl,
height: 40,
width: 40,
),
),
SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: TextStyle(fontSize: 16),
),
SizedBox(height: 3),
SizedBox(width: 220,
child: Text(widget.lastMessage,overflow: TextOverflow.ellipsis)),
],
)
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Divider(thickness: 0.5 ,),
)
],
),
);
}
}
I want to call get this future getThisUserInfo every time there is change in widget.lastmessage . How can i do that ?
When i am calling this future inside the build , It is getting called again and again non stop .So how can i only call this when there is a new message only ?
I would suggest you to review the construction of your builder and check the snapshot().listen() method, you can refer to this similar issue for that.
To your follow-up question, it is hard to say what is causing this duplicated update, as it can be a number of things and nothing in your code indicates that, so that being said, I would think of some logic that allows you to check if the update you want to make already happened, perhaps spending a read, which is less expensive than a write.
Hi i have created a simple Chat module with FireStore everything is working fine both incoming and outgoing messages are being sent. But here's a little bit issue with the flow of List of appearing messages. when message is sent from 1st Emulator it appears randomly on the other Emulator and vise versa.
I tried to reversed the ListView and also the List coming from the SnapShot.
here's the code.
final messages = snapshot.data.docs.reversed;
List<BubbleText> messageBubbles = [];
for (var message in messages) {
final messageText = message.get('text');
final messageSender = message.get('sender');
final currentUser = loggedInUser.email;
final messageWidget = BubbleText(
text: messageText,
sender: messageSender,
isMe: currentUser == messageSender,
);
messageBubbles.add(messageWidget);
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
children: messageBubbles,
),
);
This is my ChatScreen.dart file
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flash_chat/constants.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
User loggedInUser;
final fireStore = FirebaseFirestore.instance;
class ChatScreen extends StatefulWidget {
static String chatID = 'chat_screen';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final messageTextController = TextEditingController();
final auth = FirebaseAuth.instance;
String messageText;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = auth.currentUser;
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
auth.signOut();
Navigator.pop(context);
//Implement logout functionality
}),
],
title: Text('⚡️Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: fireStore.collection('messages').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.blueAccent,
),
);
}
final messages = snapshot.data.docs.reversed;
List<BubbleText> messageBubbles = [];
for (var message in messages) {
final messageText = message.get('text');
final messageSender = message.get('sender');
final currentUser = loggedInUser.email;
final messageWidget = BubbleText(
text: messageText,
sender: messageSender,
isMe: currentUser == messageSender,
);
messageBubbles.add(messageWidget);
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
children: messageBubbles,
),
);
},
),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextController,
onChanged: (value) {
//Do something with the user input.
messageText = value;
},
decoration: kMessageTextFieldDecoration,
),
),
TextButton(
onPressed: () {
messageTextController.clear();
//Implement send functionality.
fireStore.collection('messages').add(
{'text': messageText, 'sender': loggedInUser.email});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
class BubbleText extends StatelessWidget {
BubbleText({this.text, this.sender, this.isMe});
final String text, sender;
final bool isMe;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment:
isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
Text(
sender,
style: TextStyle(
color: Colors.black54,
fontSize: 12.0,
),
),
Material(
elevation: 5,
borderRadius: isMe
? BorderRadius.only(
topLeft: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30))
: BorderRadius.only(
topRight: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30)),
color: isMe ? Colors.blueAccent : Colors.white,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
child: Text(
text,
style: TextStyle(
color: isMe ? Colors.white : Colors.black,
fontSize: 15,
),
),
),
),
],
),
);
}
}
i spent over 3 hrs on it but couldn't find a proper solution.
you can add a timesTamp field in the messages document, So every single message should have a timestamp and when you read the data try to sort it by that timesTamp, it is worked with me, and DON'T forget to remove the revered property from the List Widget
here is my suggestion :
final messages = snapshot.data.docs;
if(messages != null){
messages = messages..sort((a,b)=>a.timesTamp>b.timestamp);
}
List<BubbleText> messageBubbles = [];
for (var message in messages) {
final messageText = message.get('text');
final messageSender = message.get('sender');
final currentUser = loggedInUser.email;
final messageWidget = BubbleText(
text: messageText,
sender: messageSender,
isMe: currentUser == messageSender,
);
messageBubbles.add(messageWidget);
}
return Expanded(
child: ListView(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
children: messageBubbles,
),
);
Just reverse your list like this when you define your list
final chatList = data.reversed.toList();
There's a problem which I'm trying to solve, it is displaying data by recently added to Firestore, through Flutter. What can be done in my case?
In React I would achieve this with useState hook, how can this be achieved in Flutter?
I read about .sort(); method, is that a right way of doing this?
Code:
Form.dart
class FormText extends StatelessWidget {
final String _labelText = 'Enter your weight..';
final String _buttonText = 'Save';
final _controller = TextEditingController();
final dateFormat = new DateFormat.yMMMMd().add_jm();
final _collection =
FirebaseFirestore.instance.collection('weightMeasurement');
void saveItemToList() {
final weight = _controller.text;
if (weight.isNotEmpty) {
_collection.add({
'weight': weight,
'time': dateFormat.format(DateTime.now()),
});
} else {
return null;
}
_controller.clear();
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: TextField(
keyboardType: TextInputType.number,
controller: _controller,
decoration: InputDecoration(
labelText: _labelText,
),
),
),
FlatButton(
color: Colors.blue,
onPressed: saveItemToList,
child: Text(
_buttonText,
style: TextStyle(
color: Colors.white,
),
),
),
],
);
}
}
Measurements.dart
class RecentMeasurement {
Widget buildList(QuerySnapshot snapshot) {
return ListView.builder(
reverse: false,
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
final doc = snapshot.docs[index];
return Dismissible(
background: Container(color: Colors.red),
key: Key(doc.id),
onDismissed: (direction) {
FirebaseFirestore.instance
.collection('weightMeasurement')
.doc(doc.id)
.delete();
},
child: ListTile(
title: Expanded(
child: Card(
margin: EdgeInsets.all(30.0),
child: Column(
children: <Widget>[
Text('Current Weight: ' + doc['weight'] + 'kg'),
Text('Time added: ' + doc['time'].toString()),
],
),
),
),
),
);
},
);
}
}
Layout.dart
class Layout extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
children: <Widget>[
FormText(),
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('weightMeasurement')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return Expanded(
child: RecentMeasurement().buildList(snapshot.data),
);
}),
],
),
);
}
}
You can try order by . Here is an example
firestoreDb.collection("weightMeasurement")
.orderBy("date", Query.Direction.ASCENDING)
You have to use "orderBy" on your collection, but previously You have to store something called timestamp. Make sure when You upload Your items to Firebase to also upload DateTime.now() along with Your items so You can order them by time. Do not forget to use Ascending direction since it will show you Your items ordered correctly.
In my program, I'm pulling values from cloud firestore as an initial value and want them to react on user input in case of the slider. However, the aforementioned error gets thrown. I haven't posted the entire class as it didn't let me. Just kept it to the essentials. Do let me know if you require further information. Thanks in advance!
Here's my code:
class _ListPlusScreenState extends State<ListPlusScreen> {
#override
double _progress;
String _name;
Widget _BuildListItem(BuildContext context, DocumentSnapshot doc) {
_progress = doc['Progress'];
final _name = doc['Name'].toString();
final _due = doc['Due'];
final _priority = doc['Priority'];
DocumentReference _listRef =
Firestore.instance.collection("List Items").document();
return Container(
padding: EdgeInsets.all(15),
width: 200,
height: 600,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Color(0xffacb3b8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
_name,
style: TextStyle(fontSize: 20),
),
Checkbox(value: false, onChanged: null)
],
),
Slider(
value: _progress,
min: 0.0,
max: 100.0,
divisions: 10,
label: _progress.round().toString(),
onChanged: (value) {
setState(
() {
_progress = value;
_listRef.updateData({'Progress': value});
},
);
},
),
],
),
);
}
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: const Color(0xFF6EC6CA),
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(20.0),
),
child: StreamBuilder(
stream: Firestore.instance.collection('List Items').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text('Loading..');
return ListView.builder(
itemExtent: 150,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
_BuildListItem(context, snapshot.data.documents[index]),
);
}),
);
}
}
```
double _progress =0.0;
label: _progress?.round().toString()??"0.0",
Change two lines of code in your code
I have a favorite Icon and the number of likes next to it, when I press the button it changes the Icon but it doesn't increment the number, it only increments when I reload the page, how can I change it?
class LanchonetesContact extends StatefulWidget {
final DocumentSnapshot lanchonetes;
LanchonetesContact(this.lanchonetes);
#override
_LanchonetesContactState createState() => _LanchonetesContactState();
}
class _LanchonetesContactState extends State<LanchonetesContact> {
bool liked = false;
void _pressed() {
setState(() {
liked = !liked;
});
}
void _changeLikes() {
setState(() {
if (liked) {
Firestore.instance
.collection('lanchonetes')
.document(widget.lanchonetes.documentID)
.updateData({'likes': FieldValue.increment(1)});
} else {
Firestore.instance
.collection('lanchonetes')
.document(widget.lanchonetes.documentID)
.updateData({'likes': FieldValue.increment(-1)});
}
});
}
#override
Widget Padding(
padding: EdgeInsets.only(top: 0.0),
child: Card(
elevation: 1.0,
child: GestureDetector(
child: Container(
height: 70.0,
width: 390.0,
color: Colors.white,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: 15.0, horizontal: 15.0),
child: Row(
children: <Widget>[
Padding(
padding:
const EdgeInsets.only(left: 100.0, bottom: 30),
child: Icon(
liked ? Icons.favorite : Icons.favorite_border,
color: Colors.black,
size: 50.0,
),
),
Text(
widget.lanchonetes.data["likes"].toString(),
style: TextStyle(fontSize: 40.0),
),
],
),
),
),
onTap: () {
_pressed();
_changeLikes();
},
)),
),
I also notice that it doesn't maintain the state, if I navigate to another page and after return the Icon would not be liked anymore. Any Idea on how to deal with these situations?
Function updateData is async so setState is updated before the DocumentSnapshot data is changed. To fix the issue you could do:
void _changeLikes() {
Firestore.instance
.collection('lanchonetes')
.document(widget.lanchonetes.documentID)
.updateData({'likes': FieldValue.increment(liked ? 1 : -1)
.then((_) => setState(() => {}))
}
While this method should work, there is better ways to work with firestore:
FIREBASE WITH STREAMBUILDER
Wrap the content with a streambuilder with the snapshot of the document:
StreamBuilder(
stream: Firestore.instance
.collection('lanchonetes')
.document(widget.lanchonetes.documentID).snapshots(),
builder: (context, snapshot) => Text(
snapshot.data.data["likes"].toString(),
style: TextStyle(fontSize: 40.0),
) //DocumentSnapshot is in snapshot.data,
);
and
void _changeLikes() {
Firestore.instance
.collection('lanchonetes')
.document(widget.lanchonetes.documentID)
.updateData({'likes': FieldValue.increment(liked ? 1 : -1);
}