How to navigate to a specific route in a list full of the same widget? - firebase

I have a an app which the user is able to add in books and it upload to the firestore database, and then I use a streamBuilder() to recieve all the data and display it in a list of GestureDetector() with the card() as a child.
Here is the code for the
final bookWidget = GestureDetector(
key: ValueKey(loggedInUser.email),
onTap: () {
print(title);
},
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Stack(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 100,
height: 140,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(bookCoverURL)),
),
),
Container(
width: 230,
//color: Colors.red,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Text(
category.toString(),
style: TextStyle(
color: Colors.grey,
fontStyle: FontStyle.italic,
),
),
Container(
margin: EdgeInsets.only(top: 10),
child: Text(
title.toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
),
),
),
Container(
margin: EdgeInsets.only(top: 10),
child: Text(
(author),
style:
TextStyle(color: Colors.grey),
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: Colors.black,
size: 15,
)
],
),
],
),
//color: Colors.yellowAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 5,
margin: EdgeInsets.all(10),
),
);
bookWidgets.add(bookWidget);
And then I display all my code in the screen by :
Column(
children: bookWidgets,
)
But how can I make the app navigate to a new screen when the user pressed on a specific card()? Since all of them are the same widget and have no unique identifier?
Any help is greatly appreciated! Thank you!

GestureDetector(
key: ValueKey(loggedInUser.email),
onTap: () {
Navigator.pushNamed(
context,
'/routeNameHere',
arguments: id,
);
},
)
In parameter arguments: ID - you can change the id by the id of the card that you want to navigate to.
You must create a page for the display detail of the card, which just displays a card with same id in the argument. So on your new page, create a variable for receiving the argument, e.g.:
final cardId = ModalRoute.of(context).settings.arguments as String;
So the page can display the card depending on the cardId.

You will need to create an identifier to pass (in this case “book”). In Flutter, these are necessary when calling or passing an object.
However, in regards to using Named Routes , this can allow you to send the data without the identifier but is considered bad practice in coding as you would not have a reference to the call or be able to back trace through the data.
Keeping the above in mind, it is possible to use the OnTap() inside each child card container so that on handle clicks or tap gestures, the user is taken to a different screen using routes as it is part of the GestureDetector class.

Related

How to know which card is tapped in flutter

I am making my first flutter app (A coffee shop), in this I have accessed data from Firebase Cloudstore and displayed it in the form of card.
Now I have provided the user with the feature of customizing order
for that I have to know which item he is customizing and hence I need to know which card the user has tapped.
Here is my code snippet:
child: GestureDetector(
onTap: (){
return showModalBottomSheet<void>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(40.0),
topLeft: Radius.circular(40.0),
)
),
context: context,
builder: (BuildContext context) {
return Container(
child: CustomOrder(),
height: 400.0,
width: 350.0,
);
},
);
},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25.0),
),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(25.0),
child: Image(
image: AssetImage('assets/coff1.PNG'),
),
),
Align(
alignment: Alignment.topLeft,
child: Text(
'${itemslist[index]["item_name"]}',
style: TextStyle(
fontSize: 38.0,
fontFamily: 'GrandHotel',
fontWeight: FontWeight.bold,
color: Colors.brown[800]
),
// textAlign: TextAlign.left
),
),
Align(
alignment: Alignment.topLeft,
child: Text(
'${itemslist[index]["price"]}',
style: TextStyle(
fontSize: 22.0,
fontFamily: 'GrandHotel',
color: Colors.brown[800]
),
),
),
Align(
alignment: Alignment.topLeft,
child: Text(
'${itemslist[index]["desc"]}',
style: TextStyle(
fontSize: 22.0,
fontFamily: 'GrandHotel',
color: Colors.brown[800]
),
),
),
SizedBox(height: 10.0),
FlatButton.icon(
color: Colors.brown[400],
onPressed: (){},
icon: Icon(Icons.add),
label: Text(
"Add to cart",
style: TextStyle(
fontSize: 20.0,
color: Colors.brown[800]
),
),
),
]
),
),
),
),
It appears that you are using a listView.builder but didn't attach it to your code properly, but you are accessing this [index] in your code.
Therefore, in your onpressed button in the bottom,
onPressed: (){
print(itemslist[index]["item_name"]);} // should print your tapped item's name, and then you can do whatever you want if you have reached this far.
I would wrap each separate card with their own GestureDetector. If you need more instruction for implementing this, just let me know in the comments.

How to add a date header for my chat module in flutter with firebase?

I want to add a sticky date header for my chat module just like how WhatsApp has. I tried sticky headers dependency, but it didn't work out.
Chat Page Code:
Column(
crossAxisAlignment:
snapshot['senderUid'] == _senderuid
? CrossAxisAlignment.end
: CrossAxisAlignment.end,
children: [
new Text(
snapshot['message'],
style: TextStyle(
color: Colors.white, fontSize: 15.0),
),
Padding(
padding: const EdgeInsets.only(top: 5),
child: new Text(
snapshot['timestamp']
.toDate()
.toString()
.substring(10, 16),
style: TextStyle(
color: Colors.white.withOpacity(0.5),
fontSize: 9.0),
),
),
],
),
You can try with this package
https://pub.dev/packages/flutter_sticky_header
Or with Slivers and SliverPersistentHeaderDelegate

How can I get a subcollection on Firebase without specifying it?

Hey Guys I have these subcollections on my Firebase database under the "vocabulary" collection, but I would need to call them without specifying it, as they are more than one and they are called from the same page. Instead of the collection "colors", is it possible to not specify it as the "documentID" but for the collection?
class CategoryScreen extends StatelessWidget {
final DocumentSnapshot vocabulary;
CategoryScreen(this.vocabulary);
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<QuerySnapshot>(
future: Firestore.instance.collection('vocabulary').document(vocabulary.documentID)
.collection('colors').getDocuments(),
builder: (context, snapshot){
if(!snapshot.hasData)
return Center(
child: CircularProgressIndicator(),);
else
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: Card(
elevation: 7.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50)
),
child: Column(
children: <Widget>[
Container(
height: 350.0,
width: 350.0,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(vocabulary.data["image"]
),
fit: BoxFit.fill),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50)))
),
Container(
height: 70.0,
width: 300.0,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Center(
child: AutoSizeText(vocabulary.data["name"],
style: TextStyle(
fontFamily: 'Twiddlestix',
fontSize: 25,
fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
minFontSize: 15,
),
)
),
),
],
),
),
),
],
);
},
),
);
}
}
There are no wildcards in Cloud Firestore references to documents. So in order to create a query, you need to know all collection names. If you need to get only some documents within a collection based on a specific property, you'll have to do a query using some field value as a filter.
Instead of the collection "colors", is it possible to not specify it as the "documentID" but for the collection?
No, it's not possible. You cannot substitute that.

Global Stream with multiple listeners

I have a stream connected to a Firebase document and I am constantly listening to any changes of the document.
I want to update some parts of my app when document is changed so I made the stream globally available and I use StreamBuilders on different screens so the latest data will be available ob the screen.
Will there be a problem with the global stream and multiple StreamBuilders? How does the app work when the StreamBuilders are created on multiple screens?
This is my global stream:
Stream userDocGlobalStream =
Firestore.instance.collection("user").document(CurrentUserDetails.id).snapshots();
This is the build method of one of my screens widget (I change the color of a button depending on the stream data):
#override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return StreamBuilder(
stream: userDocGlobalStream,
builder: (context, snapShot) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
side: BorderSide(color: theme.primaryColor)),
elevation: 30,
child: Container(
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment
.stretch, //need to use it to strech the items horizontally
children: <Widget>[
Container(
//to think, it is used for the background color of the picture. can undo it later
decoration: BoxDecoration(
color: theme.primaryColor.withAlpha(10),
borderRadius: BorderRadius.circular(10),
),
padding: EdgeInsets.all(10),
child: GestureDetector(
onTap: () {}, //go to user profile when pressed.
child: CircleAvatar(
radius: 70,
backgroundImage: NetworkImage(userImageUrl),
),
),
),
],
),
Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: FittedBox(
child: Text(
"Username : $username ",
),
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: FittedBox(
child: Text(
"interests : ${interests.toString().replaceAll("[", "").replaceAll("]", "")} ",
),
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: FittedBox(
child: Text("Distance from you : $distanceFromUser KM"),
),
), //to do address and km from you should go here
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
color: theme.primaryColor,
icon: Icon(Icons.chat),
onPressed: () {}, //send a message to the user
),
IconButton(
color: Colors.cyan,
icon: CurrentUserDetails.friendRequestsSent.contains(
userId) //to do=> THE ICON MUST CHANGE WITH EVERY CHANGE OF THE DATA
? Icon(
Icons.person,
color: Colors.black,
)
: CurrentUserDetails.friends.contains(userId)
? Icon(
Icons.person,
color: Colors.green,
)
: Icon(Icons.person_add),
onPressed: () {
try {
//to do
CurrentUserDetails.friendRequestsSent
.contains(userId)
? DoNothingAction()
//Cancel the sent request:
: CurrentUserDetails.friendRequestsReceived
.contains(userId)
? DoNothingAction()
//accept friend request:
: CurrentUserDetails.friends
.contains(userId)
? DoNothingAction()
//delete the friend:
: DatabaseManagement().sendFriendRequest(
CurrentUserDetails.id,
userId); //userId is the id of the user we are showing the widget for
} catch (e) {
showDialog(
context: context,
builder: (ctx) => DialogueWidget(
titleText: "Error occured",
contentText:
e.toString(), //to do+> change the er
),
);
}
} //send friend request when this button is pressed
),
IconButton(
color: Colors.red[300],
icon: Icon(Icons.location_on),
onPressed:
() {}, //show a map with a users location details on it.
)
],
)
],
),
),
);
},
);
}
}
StreamBuilder automatically starts and ends listening of provided stream:, so there will be no problem when using one broadcast stream in multiple places around the app. Even nested listening to one stream is not an issue.
Here are some helpful links if you want to dig deeper:
https://dart.dev/tutorials/language/streams
https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html

How do I generate a list of widgets from firebase firestore database?

I'm trying to figure out how to iterate through documents in firestore and create a text widget for each one.
I've figured out how to access those elements. I used debugPrint() and got the results I'm expecting, but I can't get it to display below my other widgets. I get a red screen with a ton of errors (on phone).Below is my code for what I've tried so far.
QuerySnapshot querySnapshot = await
Firestore.instance.collection("users").document(user.uid).collection("trails").getDocuments();
var list = querySnapshot.documents;
list.forEach((doc) => debugPrint(doc.data["Trail Name"].toString()));//works
final topContentText = Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
//these widgets are generating correctly
Text(
"First Name: $firstName",
style: TextStyle(color: Colors.white, ),
),
Text(
"Last Name: $lastName",
textAlign: TextAlign.left,
style: TextStyle(color: Colors.white,),
),
Text(
"Email: $email",
style: TextStyle(color: Colors.white, ),
),
//these ones are causing my errors.
list.forEach((doc) => Text(
"Trail Name: ${doc.data["Trail Name"].toString()}",
style: TextStyle(color: Colors.white, ),
),)
],
);
final topContent = Stack(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.40,
padding: EdgeInsets.all(40.0),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, .9)),
child: Center(
child: topContentText,
),
),
Positioned(
left: 8.0,
top: 60.0,
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, color: Colors.white),
),
)
],
);
return Scaffold(
body: Column(
children: <Widget>[topContent, bottomContent],
),
);
The screen on my device lights up red with errors on creating child widgets, when I'm expecting it to display the Trail Name(s) below the other info. I only included necessary code, but could include more (such as the widget's build method) if needed. Thanks for any assistance. I'm new to flutter.
Try using the map function:
List<Widget> _widgets = list.map((doc) => Text(
"Trail Name: ${doc.data["Trail Name"].toString()}",
style: TextStyle(color: Colors.white, ),
),).toList();
And then for the children of the column, just add that list to the list you specify, like:
children: [ ... ] + _widgets;

Resources