How to display one particular data from Realtime Database? - firebase

In a firebase animated list, how do you put in a conditional statement, or anything else, so that only one set of data in Realtime Database will be displayed? I currently can display all of them in a ListTile but I only want to display a destination whose name is 'Spain' and its description instead of all the database that contains Spain, Italy, USA etc.
class _TestDestinationsState extends State<TestDestinations> {
final destdatabaseref = FirebaseDatabase.instance
.reference()
.child('Database')
.child('Destinations');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xFF4D71AC),
elevation: 0,
centerTitle: true,
title: Text('Eh',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.white)),
),
body: SafeArea(
child: FirebaseAnimatedList(
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 15),
Padding(
padding: const EdgeInsets.all(8),
child: ListTile(
title: Text(snapshot.value['name'],
style: TextStyle(fontSize: 20)),
subtitle: Text(snapshot.value['description'])),
),
],
),
);
},
query: destdatabaseref,
)),
);
}
}

If we need to only display specific data from FirebaseDatabase we can use the following logic:
Visibility(
visible: snapshot.value['name'] == 'Spain',
child: ...
),
The complete snippet can be found below:
class _TestDestinationsState extends State<TestDestinations> {
final destdatabaseref = FirebaseDatabase.instance
.reference()
.child('Database')
.child('Destinations');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xFF4D71AC),
elevation: 0,
centerTitle: true,
title: Text('Eh',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.white)),
),
body: SafeArea(
child: FirebaseAnimatedList(
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return Visibility(
visible: snapshot.value['name'] == 'Spain',
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 15),
Padding(
padding: const EdgeInsets.all(8),
child: ListTile(
title: Text(snapshot.value['name'],
style: TextStyle(fontSize: 20)),
subtitle: Text(snapshot.value['description'])),
),
],
),
),
);
},
query: destdatabaseref,
)),
);
}
}
However a much better solution, would be to retrieve only specific data based on the filter, which can be done by filtering our query to FirebaseDatabase as follows:
final destdatabaseref = FirebaseDatabase.instance
.reference()
.child('Database')
.child('Destinations')
.orderByChild('name')
.equalTo('Spain');

Related

How to put different images and redirect users to other pages in ListView Builder?

Writing a code in Flutter right now and I can display a database with ListView.
However, I want to put pictures of the destination according to its location so I was wondering how to put different images for each different item? The same goes for the onTap void callback function as well. I want each list item to go to different pages where further details of the destination is given.
Code:
class _DispDestState extends State<DispDest> {
List<AllDestinations> destinationsList = [];
#override
void initState() {
super.initState();
DatabaseReference referenceAllCourses = FirebaseDatabase.instance
.reference()
.child('Database')
.child('Destinations');
referenceAllCourses.once().then(((DataSnapshot dataSnapshot) {
destinationsList.clear();
var keys = dataSnapshot.value.keys;
var values = dataSnapshot.value;
for (var key in keys) {
AllDestinations allDestinations = new AllDestinations(
values[key]['name'],
values[key]['description'],
values[key]['category'],
);
if (allDestinations.category.toString() == 'Destination')
destinationsList.add(allDestinations);
}
setState(() {});
}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.fromLTRB(20, 5, 20, 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Come and Explore",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 14,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w500,
letterSpacing: 0.5,
),
),
SizedBox(height: 15),
Expanded(
child: SingleChildScrollView(
child: Column(children: <Widget>[
destinationsList.length == 0
? Center(
child: Text(
"Loading...",
style: TextStyle(fontSize: 15),
))
: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: destinationsList.length,
itemBuilder: (_, index) {
return DestinationCard(
title: destinationsList[index].destname,
onTap: () {},
img: 'assets/icons/temp.png');
})
]),
),
),
])));
}
}
class DestinationCard extends StatelessWidget {
final String title, img;
final VoidCallback onTap;
const DestinationCard({
Key? key,
required this.title,
required this.img,
required this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: 400,
height: 190,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.fromLTRB(15, 155, 0, 0),
width: 350,
height: 190,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
image: AssetImage(img), fit: BoxFit.cover),
),
child: Text(
title,
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold),
),
),
],
),
),
),
);
}
}
You should add a parameter named imagePath to AllDestinations class. So when you use DestinationCard in ListView.builder, you can add:
return DestinationCard(
title: destinationsList[index].destname,
onTap: () {},
img: destinationsList[index].imagePath,
);

Implementing comment section for each post in flutter with cloud firestore

I'm creating instagram app clone in flutter with firebase at backend. It's a beginner level project so the coding and structure is basic.
I am stuck at adding comment section under each post. I'm using streambuilder to display data and trying to create a function in which with every image is on the feed screen would have a comment box which is connected to the current-image document in cloud-firestore.
Below is my code and images of database:
class FeedScreen extends StatefulWidget {
const FeedScreen({Key? key}) : super(key: key);
#override
_FeedScreenState createState() => _FeedScreenState();
}
class _FeedScreenState extends State<FeedScreen> {
User? user = FirebaseAuth.instance.currentUser;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
bottomNavigationBar: BottomNavigation(),
appBar: AppBar(
backgroundColor: Colors.black,
automaticallyImplyLeading: false,
title: Text(
"Platform",
style: TextStyle(
color: Colors.white,
fontSize: 32.96,
fontWeight: FontWeight.w500,
fontFamily: 'Yaldevi',
),
),
),
body: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('users').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot){
if(snapshot.hasData){
final List<DocumentSnapshot> documents = snapshot.data!.docs;
return ListView(
children: documents.map((doc) => SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
leading: doc['profileImage'] == null ?
CircleAvatar(
radius: 16.6,
backgroundColor: Colors.white24,
) :
CircleAvatar(
radius: 16.6,
backgroundImage: NetworkImage(
doc['profileImage']
)
),
title: Text(
doc['displayName'],
style: TextStyle(
color: Colors.white,
fontSize: 16.5,
)
),
subtitle: doc['title'] !=null ?
Text(
doc['title'],
style: TextStyle(
color: Colors.white,
fontSize: 12.5,
),
) :
Text(
"Some Title",
style: TextStyle(
color: Colors.white,
)
),
),
if(doc['photoURL'] != null) ... [
Container(
height: 400,
width: 400,
child: Image(
image: NetworkImage(
doc['photoURL'],
),
fit: BoxFit.contain,
)
),
IconButton(
icon: Icon(
Icons.mode_comment_outlined,
color: Colors.white,
),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) =>
CommentSection(),
));
},
)
] else if(doc['photoURL'] == null) ...[
Container(
height: 400,
width: 400,
child: Image(
image: AssetImage(
"assets/images/placeholder.png"
),
fit: BoxFit.contain,
)
)
],
ListTile(
leading: Padding(
padding: EdgeInsets.only(bottom: 13.5 ),
child: Text( "# " +
doc['displayName'],
style: TextStyle(
color: Colors.white,
),
),
),
subtitle: Padding(
padding: EdgeInsets.only(bottom: 13.5),
child: doc['decsription'] != null ?
Text( ":" +
doc['decsription'],
style: TextStyle(
color: Colors.white,
)
) :
Text(
"Some Descritiption",
style: TextStyle(
color: Colors.white,
)
)
)
),
]
),
)).toList(),
);
} else {
return CircularProgressIndicator();
}
}
)
);
}
}
and here`s the comment screen code
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
class CommentSection extends StatefulWidget {
// const CommentSection({Key? key}) : super(key: key);
#override
_CommentSectionState createState() => _CommentSectionState();
}
class _CommentSectionState extends State<CommentSection> {
var username = ' ';
List photoURL = [];
User? user = FirebaseAuth.instance.currentUser;
CollectionReference userRef = FirebaseFirestore.instance.collection('users');
final _formKey = GlobalKey<FormState>();
late String comments = ' ';
sendComment() async {
final isValid = _formKey.currentState!.validate();
final name = user!.displayName;
var res = await userRef.where('userid', isEqualTo: user!.uid).get();
_formKey.currentState!.save();
var doc = userRef.doc('photoURL');
doc.set({
'comment' : comments,
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: 50),
child: TextFormField(
style: TextStyle(
color: Colors.white,
),
decoration: InputDecoration(
labelText: "Leave a Comment..",
labelStyle: TextStyle(
color: Colors.white,
)
),
onSaved: (value) {
comments = value!;
}
),
)
],
)
),
ElevatedButton.icon(
onPressed: sendComment,
icon: Icon(Icons.send,
color: Colors.white,
),
label: Text(
"Send"
))
],
)
),
);
}
}
That's a really broad use-case, and honestly a bit too broad to answer on Stack Overflow. I recommend focusing on a more concrete, specific problem.
For example, you mention in your comment:
I tried creating a separate collection for comment but the comment would be fetched for every image in database and I'm trying to save and retrieve the comment data for particular image
To allow reading only comments on a specific image, you'll need to associate each comment in the database with an image. The two most common approaches for this are:
Create a document for each image in a top-level collection, and then create a subcollection under that document for the comments on that specific image.
Create a single top-level comments collection, and store the ID of the image in each comment document.

Error is not showing after scanning a item thats not on the firestore database

I did this personal project of mine where a barcode scanner would scan for data inside firestore database. I have this problem when I scanned a barcode thats not on the database it wont show the error message is just shows a empty scan item container which I made. Let me know if someone can figure why. I tried everything still couldnt fix it.
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("products")
.where("barcode", isEqualTo: '$barcodeScanRes')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Dialog(
child: Container(
height: 300,
child: Text('Product Not Found'),
),
);
} else {
return Dialog(
child: Container(
height: 350,
child: Column(children: [
Container(
height: 350,
width: 165,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot products =
snapshot.data!.docs[index];
return ScanCard(products: products);
},
)),
]),
),
);
#Scan Card
class ScanCard extends StatelessWidget {
const ScanCard({
Key? key,
required this.products,
}) : super(key: key);
final DocumentSnapshot products;
#override
Widget build(BuildContext context) {
final user = FirebaseAuth.instance.currentUser;
String _userId = user!.uid;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(10.0),
height: 180,
width: 160,
decoration: BoxDecoration(
color: Colors.blueAccent,
borderRadius: BorderRadius.circular(16)),
child: Image.network(products['img']),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0 / 4),
child: Text(
products['name'],
style: TextStyle(
color: Colors.blueGrey,
fontSize: 18,
),
),
),
Column(
children: [
Text(
"Size: " + products['size'],
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Colors.brown),
),
SizedBox(
width: 30,
),
],
),
Row(
children: [
Text(
"\tRs. " + products['price'],
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
SizedBox(
width: 40,
),
Icon(
Icons.add_shopping_cart,
color: Colors.black,
size: 25,
),
],
),
SizedBox(
width: 10,
),
SizedBox(
child: Padding(
padding: const EdgeInsets.all(10),
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
color: Colors.red,
child: Text(
"Add to cart",
style: TextStyle(color: Colors.white),
),
onPressed: () {
DocumentReference documentReference = FirebaseFirestore.instance
.collection('userData')
.doc(_userId)
.collection('cartData')
.doc();
documentReference.set({
'uid': FirebaseAuth.instance.currentUser!.uid,
'barcode': products['barcode'],
'img': products['img'],
'name': products['name'],
'size': products['size'],
'price': products['price'],
'id': documentReference.id
}).then((result) {
addToCartMessage(context).then((value) => {
Navigator.pop(context)
});
}).catchError((e) {
print(e);
});
},
),
),
)
],
);
}
}
The thing is you are showing Product not found based on the condition:- !snapshot.hasData but this conditon means that data is being fetched so at this time rather show a progress indicator.
And to handle when data is not present in backend then add another condition:- if(snapshot.data.docs.isEmpty) and here show your dialogbox of Product not found...
Final Code Snippet will look like:-
if (!snapshot.hasData)
return Center(child:CircularProgressIndicator));//or return a black container if you don't want to show anything while fetching data from firestore
else if (snapshot.data.docs.isEmpty) {
return Dialog(
child: Container(
height: 300,
child: Text('Product Not Found'),
),
);
} else {
return Dialog(
child: Container(
height: 350,
child: Column(children: [
Container(
height: 350,
width: 165,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot products =
snapshot.data!.docs[index];
return ScanCard(products: products);
},
)),
]),
),
);
}

How to order documents by a field content in firestore flutter

I have a collection of rooms for a hotel. When i add a new one or show the rooms list, they don't show from 1 to 5. Is there a way to order the rooms by their number?
The documents in room collection have their id generated automatically, and every room have a field called room number (ex : 1,2,3 ..)
final rooms = Provider.of<List<Room>>(context) ?? [];
return StreamProvider<List<Room>>.value(
initialData: [],
value: DatabaseService().singleRoom,
child: Scaffold(
appBar: AppBar(
title: Text("Liste des chambre single"),
backgroundColor: Colors.blue,
elevation: 2.0,
leading: IconButton(icon: Icon(Icons.chevron_left),onPressed: ()=> Navigator.pushReplacementNamed(context, '/room_type_screen'),),
actions: [
ElevatedButton.icon(
icon: Icon(Icons.home),
label: Text(""),
onPressed: ()=> Navigator.pushReplacementNamed(context, '/admin_home'),
),
ElevatedButton.icon(
icon: Icon(Icons.add),
label: Text(""),
onPressed: ()=> Navigator.pushNamed(context, '/add_room'),
),
],
),
body: ListView.builder(
itemCount: rooms.length,
itemBuilder: (context,index){
return RoomTile(room : rooms[index]);
},
),
),
Widget build(BuildContext context) {
void deleteRoomFromDatabase(String id){
final CollectionReference singleRoomCollection = FirebaseFirestore.instance.collection("rooms/single/room");
singleRoomCollection.doc(id).delete();
}
return Padding(
padding: EdgeInsets.only(top: 10.0),
child: Card(
margin: EdgeInsets.fromLTRB(20, 6, 20, 0),
child: GridTileBar(
backgroundColor: Color(0xFFBDBDBD),
leading: CircleAvatar(
radius: 30.0,
backgroundColor: Colors.orange,
// backgroundImage: AssetImage('assets/6074.jpg'),
child: Text("${room.numChambre.toString().substring(1)}",
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
fontWeight: FontWeight.bold
),
),
),
title: room.reserver?Text("Chambre réservé"):Text('Chambre disponible',style: TextStyle(fontSize: 20),),
subtitle: Text("Prix : "+room.prix.toString()),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: (){
deleteRoomFromDatabase(room.roomId);
},
),
),
),
);
}
if you want to order your result from:
FirebaseFirestore.instance.collection("rooms/single/room")
use the .orderBy() function.
FirebaseFirestore.instance.collection("rooms/single/room").orderBy("FieldInFirestoreToOrderBy", descending: true/false)
If you wanna filter the result you can also use the .where function

Flutter Gridview button functionality to new screen with Firebase

I've made a Gridview using Firebase and Streambuilder and Gridview.builder. This grid displays album titles, the album cover art for each album, and the artists that make each album. I'd like for each grid tile to be able to be pressed and navigate to a separate page with its specific album details. The plan was on press, the app would be able to identify the entire document the grid tile was referring to, move to a new page, and display the document in full to unveil the album details. The thing is, I don't know how to do that. Since snapshot.data.documents[index]['Title'] worked when iterating though all the documents to create the gridview, I thought that typing snapshot.data.documents[index] would work, but it just displays Instance of 'DocumentSnapshot' in the debug console. I'm out of ideas on how to tackle this, so any suggestions are welcome
My code is shown below
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final Services services = Services();
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: bgcolour,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: Icon(Icons.menu),
title: Text("Home"),
actions: <Widget>[
Padding(padding: EdgeInsets.all(10), child: Icon(Icons.more_vert))
],
),
body: StreamBuilder(
stream: Firestore.instance.collection('music').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text("Loading...");
return GridView.builder(
itemCount: snapshot.data.documents.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, childAspectRatio: 0.655172413),
//cacheExtent: 1000.0,
itemBuilder: (BuildContext context, int index) {
var url = snapshot.data.documents[index]['Cover Art'];
return GestureDetector(
child: Container(
width: 190.0,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(32)),
color: hexToColor(
snapshot.data.documents[index]['Palette'][0]),
elevation: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(height: 12),
ClipRRect(
borderRadius: BorderRadius.circular(21.0),
child: Image.network(url,
height: 180.0, width: 180)),
SizedBox(height: 10),
Text(
snapshot.data.documents[index]['Artist']
.join(', '),
textAlign: TextAlign.center,
style: GoogleFonts.montserrat(
textStyle: TextStyle(color: Colors.white),
fontSize: 14,
fontWeight: FontWeight.w300)),
SizedBox(height: 10),
Text(snapshot.data.documents[index]['Title'],
style: GoogleFonts.montserrat(
textStyle: TextStyle(color: Colors.white),
fontSize: 16,
fontWeight: FontWeight.w600),
textAlign: TextAlign.center),
],
),
),
),
onTap: () {
print("Tapped ${snapshot.data.documents[index]}");
},
);
},
);
}
),
);
}
}
Is there an ID for your snapshot.data.documents[index]? If yes, add it to the end.
onTap: () {
print("Tapped ${snapshot.data.documents[index]['the property you want']}");
},

Resources