How to create Foreign key in firebase for chat app - firebase

I create chat app applicattion have many conversation which i can have conversation, my problem depend on receive messages by another account.
most work correctly but when i loggin to another user i receive the same messages as in the previous one user which didn't conduct a conversation.
i suppose i need foregin key
Column(children:
[
StreamBuilder(
//currentLoggedUserId - user which is logged right now
stream:FirebaseFirestore.instance.collection('users').doc(currentLoggedUserId).snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot)
{
return Center(child:
ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data['messages'].length,
itemBuilder: (BuildContext context, int index){
return ChatBubble(
clipper: ChatBubbleClipper1(type: BubbleType.sendBubble),
alignment: Alignment.topRight,
margin: EdgeInsets.only(top: 20),
backGroundColor: Colors.blue,
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.7,
),
child: Text(
snapshot.data['messages'][index],
style: TextStyle(color: Colors.white),
),
),
);
},),
);
}),
StreamBuilder(
//writer is DocumentId account which i have conversation
stream:FirebaseFirestore.instance.collection('users').doc(writer).snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot)
{
return Center(child:
ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data['messages']?.length ,
itemBuilder: (BuildContext context, int index){
return
ChatBubble(
clipper: ChatBubbleClipper1(type: BubbleType.receiverBubble),
backGroundColor: Color(0xffE7E7ED),
margin: EdgeInsets.only(top: 20),
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.7,
),
child: Text(
snapshot.data['messages'][index],
style: TextStyle(color: Colors.black),
),),);},),);}),],)

So one important thing to note is that Firebase "tables" aren't really like your RDBMS tables where you can have primary and foreign keys. You need to manage that manually. Simply store the ID of your primary key in every document that you create which you want to have a reference to the other "table".

Related

Retrieve stream value then call another stream based on the previous value using Flutter with firestore

**This posts collection and following collection are child nodes of user collection.Using below code snippets I can retrieve users posts directly.
But I want to retrieve only followed users posts. If particular user followed another user. That followed users id saved under followings collection
**
Container(
height: 225.0,
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("users")
.doc(userProfileID)
.collection("posts")
.orderBy('Post Date', descending: true)
.snapshots(),
builder: (context, snapshot) {
return !snapshot.hasData
? ShimmerPlaceHolder().homePosts(true)
: snapshot.data.docs.length.toString() == "0"
? Container(
height: 100.0,
width: 200.0,
child: Column(
children: [
SizedBox(
height: 30.0,
),
Text(
"You have no posts yet",
style: TextStyle(
fontSize: AppTheme.AppLightTheme.fontLarge,
fontFamily: 'AirbnbCereal'),
),
Image.asset(
'assets/images/home_picture.png',
height: 100.0,
width: 100.0,
),
],
),
)
: Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
height: 200,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
Post postModel = Post.fromDocument(
snapshot.data.docs[index],
snapshot.data.docs.length);
return Container(
width: MediaQuery.of(context).size.width * 0.35,
child: Card(
//TODO: Add all images radius
color: AppTheme.AppLightTheme.unSelectColor,
child: snapshot.data.docs.length == null
? ShimmerPlaceHolder().homePosts(true)

Listview builder scrolling in Flutter not smooth when combining futurebuilder and streambuilder

I have a comment page for each post in my app that I use streambuilder to fetch the comments from Firebase database. For each comment, I am displaying user's image and their comment.
To get the user's image, I need to use a futurebuilder to find the user in the userData document and then grab the image url and display it (users can change their image profile, name, etc. at any time and I have to grab the updated one from the userData document every time I want to display their name or image anywhere). Here is the code I use:
StreamBuilder(
stream: Firestore.instance
.collection('posts')
.document(postId)
.collection('postComments')
.orderBy(
'createdAt',
descending: true,
)
.snapshots(),
builder: (ctx, chatSnapshot) {
if (chatSnapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final chatDocs = chatSnapshot.data.documents;
return ListView.builder(
shrinkWrap: true,
reverse: true,
itemCount: chatDocs.length,
itemBuilder: (ctx, index) {
return FutureBuilder(
future: Firestore.instance
.collection('userData')
.document(userId)
.get(),
builder: (context, userSnapshot) {
if (userSnapshot.connectionState == ConnectionState.waiting) {
return Container(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(Color.fromRGBO(0, 0, 0, 0)),
),
);
}
final userData = userSnapshot.data;
User commentor = User.fromDocument(userData);
return Padding(
padding: const EdgeInsets.all(20.0),
child: Row(
children: [
CircleAvatar(
backgroundImage: NetworkImage(
commentor.userImage,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: MediaQuery.of(context).size.width * 0.5,
padding: EdgeInsets.all(10),
color: Colors.grey,
child: Text(
chatDocs[index]['comment'],
style: TextStyle(
color: Colors.black,
),
textAlign: TextAlign.start,
),
),
),
],
),
);
},
);
},
);
},
);
When I scroll from bottom to top (most recent to older comments), the scrolling is very smooth with no problem, but when I get to the end of the list (oldest comment) and start scrolling back down, there is a weird jump between the comments and the scrolling is not smooth at least for the first few scrolls.
I have This screencapture here which shows the weird scrolling behavior. Why does this happen?
Thanks!

Retrieve Map Data in Firebase to Flutter

I have this data structure
Database Structure
I need to call all the item in the FavDestination collection and here is my code in Flutter
child: StreamBuilder(
stream: Firestore.instance.collection('FavDestination').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData) return Text('Loading Data... Please Wait');
return
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.documents.length, //snapshot.data.document.length,
itemBuilder: (BuildContext context, int index) {
DocumentSnapshot destination = snapshot.data.documents[index];
//DocumentSnapshot destination = snapshot.data.document[index];
return GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => DestinationScreen(
destination: destination,
),
),
),
It retrieves the data successfully, and if the user taps on the document, it will go to Destination Screen and I need to retrieve all the data in activities, here is my code in the Destination Screen
Expanded(
child: ListView.builder(
padding: EdgeInsets.only(top: 10.0, bottom: 15.0),
itemCount: widget.destination['activities'].length,
itemBuilder: (BuildContext context, int index) {
DocumentSnapshot activity = widget.destination['activities'][index];
return Stack(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: 120.0,
child: Text(
activity['name'],
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w600,
),
and here is the Error I got
Error Message
any idea how to solve it? Thank you
It seems that your activities field is nested map. On first level you have field Bali1 which is map as well. And it the code activity is trying to get name on first level.
I suppose you should work more on destination object structure. You can print it out to analyze and try to map object like Map<dynamic, dynamic> and Bali1 as well.
I don't have playground to do it fast, but I found similar approach here:
How do I get specific values from a datasnapshot in flutter?
I hope it will help!

Cant get StreamBuilder to display data from cloud firestore

I know I have a connection to the database and no errors are appearing so I'm pretty confused. The title and code should summarize the problem fairly well. Think I'm missing something?
here is the main code that should be displaying cards with titles from firebase
mainList() {
StreamBuilder(
stream: Firestore.instance.collection('Events').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading');
} else {
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot userPost = snapshot.data.documents[index];
return Container(
width: MediaQuery.of(context).size.width,
height: 350.0,
child: Padding(
padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
child: Material(
elevation: 14.0,
shadowColor: Color(0x802196F3),
child: Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: 200.0,
child: Text(
'${userPost['title']}',
))
],
),
),
))),
);
},
);
}
});
}
and here is where the function is called:
lass MyAppmain extends State<MyApp> {
#override
Widget build(BuildContext context) {
var listView = ListView.builder(
itemCount: local.length,
itemBuilder: (BuildContext cnxt, int index) {
return new Text(local[index]);
});
return MaterialApp(
home: PageView(
controller: controller,
children: <Widget>[
//home page---------------------------
Scaffold(
appBar: AppBar(
title: Text(
'Events',
),
elevation: 20,
),
//main list view for the cards
//think I use streambuilder for this so google before starting
body: mainList(),//RIGHT HERE
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.push(context, NewEventTransition());
},
mini: true,
),
),
//Profile Page-------------------------------
Scaffold(
appBar: AppBar(
title: Text(
'Profile',
),
elevation: 20,
),
),
],
));
}
}
Want a listview of cards holding the titles from firebase (will soon be more than titles but want to get this working first)
This is a common problem.
return ListView.builder(
itemCount: snapshot.data.documents.length, // this line is the culprit!
itemBuilder: (context, index) {
print(snapshot.data.documents.length); // it will print null
.......
}
See, It takes some time to fetch data from firebase. When ListView.builder is called the value of snapshot.data.documents.length is actually null. Tho after few seconds it gets data but till then ListView had built the UI and that's why it's blank. To check the value, you can add a Print statement like shown above.
Now there are few ways to solve this problem:
Make an int variable say totalLength, make a function say setTotalLength which makes a call to Firebase/Firestore database and use setState to assign this value to totalLength and then change that code to this:
itemCount: totalLength,
You should Call setTotalLength in your initState method.
Or, you can change your code to this, But I'm NOT 100% sure that this will work:
itemCount: snapshot.data.documents.length ?? 0 // returns 0 if the value is null, may reload UI again when data comes

How to load image to the Card from data retrieved from async task in flutter?

I'm new to flutter development. I need to load images into a card depending on data loaded via async task.
I have an async task which returns Future> user data quired from the sqlite local database. With retrieved data, I build a ListView to show users using Card. But inside the card, I'm trying to show an image which will be downloaded from Firebase Storage depending on the data retrieved from the local database. But the image URL is null.
Widget build(BuildContext context) {
var allCards = DBProvider.db.getAllCards();
return FutureBuilder<List<User>>(
future: DBProvider.db.getAllCards(),
builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
User user = snapshot.data[index];
return Card(
elevation: 8.0,
margin:
new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Container(
child: Image(
image: CachedNetworkImageProvider(FirebaseStorage().ref().child('employer_logo').child('00001').child('google-banner.jpg').getDownloadURL().toString()),
fit: BoxFit.cover,
),
),
Positioned(
bottom: 0,
left: 0,
child: Container(
padding: EdgeInsets.all(10),
child: Text(
'Google Incorperation',
style: TextStyle(
fontSize: 20, color: Colors.white),
),
),
)
],
),
Container(
decoration: BoxDecoration(
color: Colors.white10,
),
child: ListTile(
title: Text(user.fname + " " + user.lname,
style: TextStyle(
color: Colors.blue[400], fontSize: 20)),
subtitle: Text(user.designation,
style: TextStyle(
color: Colors.blue[300], fontSize: 16)),
onTap: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Profile(
user.fname,
user.lname,
user.uid,
user.designation,
user.mobile,
user.employerId)))
},
),
)
],
),
);
},
);
}
},
);
}
I expect to show images downloaded from firebase storage
This would be my first answer, and there are probably many ways to improve my answer here. But I will give it a go: Actually, you will have to look up a lot on Futuresand Streams, because it is quite a big part in many a app. If your app needs any content on the web, it will need Futures, or it's bigger counterpart Stream. In this case, where you want to set up a Listview with probably multiple images, I would go for a Stream. Also, I would save all my database logic in a seperate file. However, if you don't want to modify your code too much now, I would use a FutureBuilder.
I've seen you already use one of them in your code. But in this case, use:
...
int maxsize = 10e6.round(); // This is needed for getData. 10e^6 is usually big enough.
return new Card (
FutureBuilder<UInt8List> ( // I also think getting Data, instead of a DownloadUrl is more practical here. It keeps the data more secure, instead of generating a DownloadUrl which is accesible for everyone who knows it.
future: FirebaseStorage().ref().child('entire/path/can/go/here')
.getData(maxsize),
builder: (BuildContext context, AsyncSnapshot<UInt8List> snapshot) {
// When this builder is called, the Future is already resolved into snapshot.data
// So snapshot.data contains the not-yet-correctly formatted Image.
return Image.memory(data, fit: BoxFit.Cover);
},
),
Widget build(BuildContext context) {
var allCards = DBProvider.db.getAllCards();
return FutureBuilder<List<User>>(
future: DBProvider.db.getAllCards(),
builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
User user = snapshot.data[index];
int maxsize = 10e6.round();
return Card(
elevation: 8.0,
margin:
new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Container(
child: FutureBuilder<dynamic>(
future: FirebaseStorage()
.ref()
.child('employer_logo')
.child('00001')
.child('google-banner.jpg')
.getDownloadURL(),
builder: (BuildContext context,
AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState !=
ConnectionState.waiting) {
return Image(
image: CachedNetworkImageProvider(
snapshot.data.toString()),
fit: BoxFit.cover,
);
}
else {
return Text('Loading image....');
}
},
),
),
Positioned(
bottom: 0,
left: 0,
child: Container(
padding: EdgeInsets.all(10),
child: Text(
'Google Incorperation',
style: TextStyle(
fontSize: 20, color: Colors.white),
),
),
)
],
),
Container(
decoration: BoxDecoration(
color: Colors.white10,
),
child: ListTile(
title: Text(user.fname + " " + user.lname,
style: TextStyle(
color: Colors.blue[400], fontSize: 20)),
subtitle: Text(user.designation,
style: TextStyle(
color: Colors.blue[300], fontSize: 16)),
onTap: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Profile(
user.fname,
user.lname,
user.uid,
user.designation,
user.mobile,
user.employerId)))
},
),
)
],
),
);
},
);
}
},
);
}

Resources