flutter: how can I retrieve current user data from firebase firestore - flutter

am trying to retrieve current user data but it showing all the users data from firestore in app
here is my code
Widget build(BuildContext context){
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder: ((context, snapshot){
if(snapshot.connectionState == ConnectionState.done){
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Container(
child: Column(
children: [
Text('Email : '"${data['Email name'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
Text('Contact : '"${data['age'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
Text('Name : '"${data['firstname'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
Text('Last Name : '"${data['lastname'].toString()}",
style: const TextStyle(fontWeight: FontWeight.bold,wordSpacing: 10),),
const SizedBox(
width: 10,
),
],
),
);
}
return const Text('Loading');
}),
here is documentId code
List<String> docIDs = [];
Future getDocId() async{
await FirebaseFirestore.instance.collection('users').get().then(
(snapShot) => snapShot.docs.forEach(
(documant){
print(documant.reference);
docIDs.add(documant.reference.id);
}));
}
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// ignore: prefer_const_constructors
Text('SignUp as ${user!.email!}',style: TextStyle(
fontSize: 20,
),),
Expanded(child: FutureBuilder(
future: getDocId(),
builder: (context, snapshot){
return ListView.builder(
itemCount: docIDs.length,
itemBuilder: (context, index){
return ListTile(
title: GetuserData(documentId: docIDs[index]),
);
},
);
},
))
when ever user post the form it submitted successfully and generate a post like summited form history but it showing to others users to but i wont it for only current user how to do that please any one can help me

You can use firestore query to return the current user using where()
on getDocId():
await FirebaseFirestore.instance.collection('users').get()
change to
await FirebaseFirestore.instance.collection('users')
.where('Email name', isEqualTo: 'myEmail#mail.test').get()
you can replace the email with your or consider using parameter to pass user email dynamically.

Related

Display Firestore data as list without using a stream

I am trying to run a query on my Firebase Firestore data and display the results as a list. Typically, when I do this, I use a streambuilder, which updates the results whenever any data is changed. I was wondering how to display the same data only once, without subscribing to a stream. This is my code for the streambuilder, which updates on change.
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('announcements')
.orderBy('date', descending: true)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshots) {
if (snapshots.connectionState == ConnectionState.active &&
snapshots.hasData) {
return ListView(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
children: snapshots.data!.size == 0
? [
const Padding(
padding: EdgeInsets.only(top: 10),
child: Text(
"You have no announcements",
style: TextStyle(
height: 1.0,
fontFamily: "urwmedium",
color: Color(0xffD5D6D7),
fontSize: 20,
),
),
),
]
: snapshots.data!.docs.map(
(DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Text(
data['announcementTitle'],
);
},
).toList(),
);
}
return Center(
child: CircularProgressIndicator(
color: CustomColors.textColor,
),
);
},
),
The above code works, but again, it loads information realtime. What would the code be if I wanted it to only load once at opening?
as the StreamBuilder is helpful in order to get stream of your collection snapshots
to get that data only once, use FutureBuilder instead:
FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection('announcements')
.orderBy('date', descending: true)
.get(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshots) {
if (snapshots.connectionState == ConnectionState.done &&
snapshots.hasData) {
return ListView(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
children: snapshots.data!.size == 0
? [
const Padding(
padding: EdgeInsets.only(top: 10),
child: Text(
"You have no announcements",
style: TextStyle(
height: 1.0,
fontFamily: "urwmedium",
color: Color(0xffD5D6D7),
fontSize: 20,
),
),
),
]
: snapshots.data!.docs.map(
(DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Text(
data['announcementTitle'],
);
},
).toList(),
);
}
return Center(
child: CircularProgressIndicator(
color: CustomColors.textColor,
),
);
},
),

Flutter - Nested Streambuilder and FutureBuilder

I have categories collection with category name and id fields. In foods collection, the category is a reference field. I need to display like this:click to see expected output
where title is coming from categories collection and foods are coming from foods collection.
I tried using nested streambuilder: streambuilder 1: fetch categories in a listview streambuilder 2: fetch foods in a list. Inside streambuilder 2, i have used a futurebuilder to decode the category data. If category name in food and category name from streambuilder 1 is same, the food will be displayed under that category.+
class RestaurantDetails extends StatefulWidget {
final String id;
RestaurantDetails({required this.id, super.key});
#override
State<RestaurantDetails> createState() => _RestaurantDetailsState();
}
class _RestaurantDetailsState extends State<RestaurantDetails> {
List<FoodCategory> categories = [];
List<Food> foods = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: StreamBuilder(
stream: getCategories(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
categories = snapshot.data!.docs
.map((item) => FoodCategory.fromMap(item))
.toList();
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: categories.length,
itemBuilder: ((context, cateindex) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
child: Container(
height: 30,
width: MediaQuery.of(context).size.width * 1,
color: Colors.white,
child: Text(
categories[cateindex].name.toString(),
style: Theme.of(context)
.textTheme
.headline4!
.copyWith(fontSize: 20.0),
),
),
),
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('foods')
.doc(widget.id)
.collection('all')
.snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
foods = snapshot.data!.docs
.map((item) => Food.fromMap(item))
.toList();
return ListView.builder(
itemCount: foods.length,
physics:
const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: ((context, foodindex) {
var catepath = foods[foodindex].cid!.path;
String cateDocumentName = catepath
.substring(11, catepath.length);
return Column(
children: [
FutureBuilder(
future: FirebaseFirestore.instance
.collection('categories')
.doc(cateDocumentName)
.get(),
builder: ((context,
AsyncSnapshot<
DocumentSnapshot>
snapshot) {
if (snapshot.hasData) {
Map<String, dynamic> data =
snapshot.data!.data()
as Map<String,
dynamic>;
if (data['name'] ==
categories[cateindex]
.name) {
return Padding(
padding: const EdgeInsets
.symmetric(
vertical: 10,
horizontal: 10),
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius
.circular(
20),
color:
Colors.white),
height: 100,
width: MediaQuery.of(
context)
.size
.width *
1,
child: Row(
children: [
Image.network(
foods[foodindex]
.cover
.toString(),
height: 100,
width: 100,
errorBuilder:
((context,
error,
stackTrace) {
return Image
.asset(
'assets/images/food1.jpg',
height: 100,
width: 100,
);
}),
),
UIHelper
.horizontalSpaceMedium(),
Column(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
UIHelper
.verticalSpaceSmall(),
Text(
foods[foodindex]
.name
.toString(),
style: Theme.of(
context)
.textTheme
.bodyText1,
),
UIHelper
.verticalSpaceSmall(),
Text(
'₹${foods[foodindex].price}',
style: Theme.of(
context)
.textTheme
.bodyText1!
.copyWith(
fontSize:
14.0),
),
UIHelper
.verticalSpaceMedium(),
],
)
],
)),
);
} else {
return const SizedBox();
}
} else {
return const CircularProgressIndicator
.adaptive();
}
}))
],
);
}));
} else {
return const CircularProgressIndicator
.adaptive();
}
},
)
],
);
}));
} else {
return const CircularProgressIndicator.adaptive();
}
}),
),
);
}
getCategories() {
return FirebaseFirestore.instance
.collection('categories')
.where('uid', isEqualTo: widget.id)
.snapshots();
}
}
categories data
click to see categories
food data
click to see food data
I get the output.see my output here but when data is large (i.e large number of foods inside a category) the app hangs. Is there anyother way we can achieve this? the data should load seamlessly regardless of data size.

Display sub-collection in flutter Firebase

I want to display the data of a sub-collection named "Profile". I get it that we need to query it differently, and I tried it, but it is not working out for me. First, I displayed the information from the documents of the mother collection "mentors", using StreamBuilder. Then passed it's data to a Widget I created. Then on the Widget I created, I performed another streamBuilder query for the subcollection of each document of the Mother Collection "mentors".
This is the code I used to display the documents on "mentors" collection, and is working fine.
final mentors = Expanded(
child: Container(
height: 250,
margin: const EdgeInsets.only(left: 20, right: 20),
child: StreamBuilder<QuerySnapshot>(
stream: db_mentors,
builder: (
BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot,
) {
if (snapshot.hasError) {
Fluttertoast.showToast(msg: "An Error Occured");
}
if (snapshot.connectionState == ConnectionState.waiting) {
Fluttertoast.showToast(msg: "Loading");
}
final data = snapshot.requireData;
return ListView.builder(
itemCount: data.size,
itemBuilder: ((context, index) {
return mentorsWidget(
"${data.docs[index]["uid"]}",
"${data.docs[index]['name']}",
"${data.docs[index]['specialty']}",
);
}),
);
}),
),
);
This here is the code I used to display the data from the subcollection of each document named "Profile". Which is also the widget I created.
Widget mentorsWidget(String uid, String name, String specialty) {
return Container(
margin: const EdgeInsets.all(5),
width: size.width,
decoration: const BoxDecoration(
color: Color.fromARGB(255, 3, 42, 134),
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
bottomRight: Radius.circular(20))),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("mentors")
.doc(uid)
.collection("Profile")
.snapshots(),
builder: (
BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot,
) {
if (!snapshot.hasData) {
return SizedBox(
width: 80,
child: Image.asset("assets/Navigatu-icon.ico"),
);
} else {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: ((context, index) {
String url = snapshot.data!.docs[index]['downloadURL'];
return SizedBox(
width: 80,
child: Image.network(url),
);
}),
);
}
}),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(top: 10, left: 5),
child: Text(
name,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontFamily: 'Roboto',
fontWeight: FontWeight.w500),
),
),
Container(
margin: const EdgeInsets.only(top: 15, bottom: 15, left: 5),
child: Text(
specialty,
style: const TextStyle(
color: Colors.white,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
fontSize: 12,
),
),
),
],
)
],
),
);
}
Here is the Collection Tree in my firebase:
Firebase Collection Tree
Here is the display I want to achieve. The boat picture here supposedly must be a Image.network, with the url that is in the sub-collection, named "Profile".
Mentor Field
As you can see in the code, I performed first the "final mentors", then performing streambuilder inside of it. So that I can get the datas of each document from the mother collection. Now I passed those data to the "mentorwidget" to display them in a proper way, but then I wanna use a Image.network, containing the data inside the sub-collection of each document in the mother collection. That's why I performed another streambuilder inside the mentorwidget to display the picture, or get the data of the sub-collection which is the url of the said picture.
If the data doesn't get frequently updated or if you don't need to display the constant changes of it's value to the users then use FutureBuilder instead of StreamBuilder to query the value you want only once.
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection('mentors')
.doc('b23lt...[your desired document ID]')
.collection('Profile')
.get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return ListView.builder(
itemCount: , // lenght of snapshot data,
itemBuilder: (context, index) {
//Here you can retrieve the data of each document
},
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
Update: I found the answer! thanks to Slender's answer, I managed to get the answer, here is the code.
FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection("mentors")
.doc(uid)
.collection("profile")
.get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data!.docs.isEmpty) {
return SizedBox(
width: 80,
child: Image.asset("assets/Navigatu-icon.ico"),
);
} else if (snapshot.hasData) {
// print(snapshot);
return SizedBox(
width: 80,
child: Image.network(
"${snapshot.data!.docs[0]['downloadURL']}"),
);
}
}
// print(snapshot.data!.docs[0]['downloadURL']);
return const SizedBox(
width: 80,
child: Center(
child: CircularProgressIndicator(),
),
);
},
),

Add OnEmpty Widget to ListView.builder() inside FutureBuilder using flutter?

I am using Flutter to develop small application with floor for the database.
I am getting the data from the database using Future then listing all items in UI using FutureBuild.
This is my code
Getting the data from database:
#Query('SELECT * FROM Doctor')
Future<List<Doctor>> findAllDoctor();
Getting data to UI
Future<List<Doctor>> findAllDoctor() async {
return await database.doctorDao.findAllDoctor();
}
Setting data into FutureBuilder:
return FutureBuilder(
future: findAllDoctor(),
builder: (BuildContext context, AsyncSnapshot<List<Doctor>> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: ListTile(
contentPadding: const EdgeInsets.all(8.0),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${snapshot.data![index].firstName} ${snapshot
.data![index].lastName}"),
Text(
snapshot.data![index].phone,
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
],
),
subtitle: Text(
"${snapshot.data![index].address} ${snapshot.data![index]
.nameOfTheClinic}"),
),
);
},
);
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
I want to add new widget that tells me no data if there is no data in the table.

flutter: StreamBuilder doesn't accept merged stream

i have a problem with merged stream and StreamBuilder.
im trying to merge multiple streams from firestore that each one represents a grocery list.
my result should be a ListView that combines all list in a some group.
from some reason,my StreamBuilder shows a single stream list in one tab but doesnt show it in another.
group list app photo
personal list app photo
code:
Stream<QuerySnapshot<Map<String, dynamic>>> getPesonalStream<T>() {
final userGroceries =
_fireStore.collection("users").doc(email).collection("groceries");
return userGroceries.snapshots();
}
Stream<QuerySnapshot<Map<String, dynamic>>> getGroupStream<T>() {
List<Stream<QuerySnapshot<Map<String, dynamic>>>> list = [];
_fireStore
.collection("groups")
.doc(gid)
.snapshots()
.forEach(((docSnapshot) {
List<dynamic> members = docSnapshot.data()!["members"];
list = members
.map((member) => _fireStore
.collection("users")
.doc(member)
.collection("groceries")
.snapshots())
.toList();
}));
return CombineLatestStream(list,
(values) => values.last as QuerySnapshot<Map<String, dynamic>>)
.asBroadcastStream();
// return StreamGroup.merge(list).asBroadcastStream();
}
as you can see iv tried a few ways to combine my streams and non workes
body:
body: TabBarView(children: [
_buildContent(
context,
db.getGroupStream(),
),
_buildContent(
context,
db.getPesonalStream(),
),
]),
my builder:
Widget _buildContent(BuildContext context, Stream<QuerySnapshot> stream) {
return StreamBuilder<QuerySnapshot>(
stream: stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final docs = snapshot.data!.docs;
if (docs.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"The list is empty",
style: TextStyle(fontSize: 32, color: Colors.black54),
),
Text(
"Add a new item to get started",
style: TextStyle(fontSize: 16, color: Colors.black54),
),
],
));
}
int index = -1;
final cards = docs
.map((doc) => CrossableListTile(
doc, _showSetGroceryButtomSheetForm, index++))
.toList();
return Container(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
child: ListView(
children: cards,
),
);
} else if (snapshot.hasError) {
return Center(
child: Column(
children: [Text("An error has occured while loading you'r list")],
),
);
}
return Center(
child: CircularProgressIndicator(
color: Colors.black,
));
},
);
}