Why when i add data to firebase, the data keep looping? - flutter

I'm new to flutter and firebase and I do not know why when I upload any data to firebase the data will keep repeating the same thing but when I hot restart the upload item back to 1, this is my code:
Future getDocId() async {
await FirebaseFirestore.instance
.collection('users')
.orderBy('mobile', descending: true)
.get()
.then(
(snapshot) => snapshot.docs.forEach(
(document) {
print(document.reference);
docIDs.add(document.reference.id);
},
),
);
}
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: FutureBuilder(
future: getDocId(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: docIDs.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: ListTile(
title: ReadUser(documentId: docIDs[index]),
tileColor: Colors.purple[100],
),
);
},
);
},
),
),
This is my future builder
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 Text('Name:' +
'${data['name']}' +
"\n"
'Email:' +
'${data['email']}' +
"\n"
'Mobile Number:' +
'+' +
'${data['mobile']}' +
"");
}
return Text('Loading..');
}),
);

the way you are fetching your data is wrong, instead of pass the result into outside variable you need to return it like this, I assume docIDs is a list of string:
Future<List<String>> getDocId() async {
try {
var snapshot = await FirebaseFirestore.instance
.collection('users')
.orderBy('mobile', descending: true)
.get();
return snapshot.docs.map((document) => document.reference.id).toList();
} catch (e) {
return [];
}
}
then change your FutureBuilder to this:
FutureBuilder<List<String>>(
future: getDocId(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<String> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: ListTile(
title: ReadUser(documentId: data[index]),
tileColor: Colors.purple[100],
),
);
},
);
}
}
},
),

Related

convert this into streambuilder in flutter

I want to convert this function into Streambuilder, but somehow I could not figure out how I could do it. Any help would be greatly appreciated.
Future getReceiverChats() async {
var data = await FirebaseFirestore.instance
.collection("message")
.doc(widget.id)
.collection("nodes")
.orderBy("time", descending: false)
.get();
setState(() {
_msgReceiverList =
List.from(data.docs.map((doc) => Message.fromMap(doc)));
});
}
Try this:
Stream<List<Message>> getReceiverChats(String id) {
return FirebaseFirestore.instance
.collection("message")
.doc(id)
.collection("nodes")
.orderBy("time", descending: false)
.snapshots()
.map((QuerySnapshot query) {
List<Message> dataList = [];
query.docs.forEach((doc) {
dataList
.add(Message.fromMap(doc));
});
return dataList;
});
}
Then:
StreamBuilder<List>(
stream: getReceiverChats(widget.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
final List<Message>? dataList = snapshot.data;
if (dataList!.isEmpty) {
return Center(
child: Text('No results'),
);
}
return ListView.builder(
itemCount: dataList.length,
itemBuilder: (context, index) {
return MyWidget(dataList[index]);
});
}
if (snapshot.connectionState == ConnectionState.done) {
if (!snapshot.hasData) {
return Center(
child: Text('No results'),
);
}
}
return const Center(
child: CircularProgressIndicator(),
);
})
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("message")
.doc(widget.id)
.collection("nodes")
.orderBy("time", descending: false)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text("Loading...");
default:
return ListView(
children: snapshot.data.docs.map((doc) {
return Message.fromMap(doc);
}).toList(),
);
}
},
),

Fetching data from Firebase into listview in flutter

I'm trying to fetch data from firebase and I want that data to be printed in my listview.builder.
Here is my code:-
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("passwordProfile")
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
if (snapshot.data.documents.lenght == 0) {
return Text("no data");
}
return ListView.builder(
itemCount: snapshot.data.documents.lenght,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
child: snapshot.data.documents[index].data("url")),
title: snapshot.data.documents[index].data["url"],
);
});
}),
And below is a picture:-
From the picture you can observe that documents is not being recognized so help there.
Please specify SteamBuilder return type i-e Querysnapshot here's the example of your code
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("passwordProfile")
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
final userSnapshot = snapshot.data?.docs;
if (userSnapshot!.isEmpty) {
return const Text("no data");
}
return ListView.builder(
itemCount: userSnapshot.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
child: userSnapshot[index]["url"],
),
title: userSnapshot[index]["url"],
);
});
});
Once you check null, you can add ! like snapshot.data!
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
final data = snapshot.data as Map<String, dynamic>?;
if (data == null) {
return Text("no data");
}
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
// leading:
// CircleAvatar(child: data[index].data("url")),
title: Text("${data[index].data["url"]}"),
);
});
}),

How to display list of data in application?

I have encountered an error where the data is printed out in terminal but do not display in the application.
Future<DocumentSnapshot> getTeacher() async {
var firebaseUser = await FirebaseAuth.instance.currentUser;
var docRef = FirebaseFirestore.instance.collection("User");
var query = docRef.where("type", isEqualTo: "teacher").limit(10);
query.get().then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
print(doc["name"]);
print(doc["email"]);
});
});
}
body: new FutureBuilder(
future: getTeacher(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return Row(
children: <Widget>[
Expanded(child: Text(snapshot.data["name"])),
Expanded(child: Text(snapshot.data["email"])),
Expanded(
child: IconButton(
icon: Icon(Icons.chat), onPressed: () {}))
],
);
});
}
return Container();
}),
this is the output in the terminal
Your getTeacher()-Function does not return your Future. Because of this
the Nullpointer-Exception ist thrown. You should return query.get() instead of listen to it.
You should also not call getTeacher() in the build-Function because it will be called at every build.
EDIT:
Your method:
Future<DocumentSnapshot> getTeacher() async {
var firebaseUser = await FirebaseAuth.instance.currentUser;
var docRef = FirebaseFirestore.instance.collection("User");
var query = docRef.where("type", isEqualTo: "teacher").limit(1);
return (await query.get()).docs[0];
}
Variable of your widget:
final Future<DocumentSnapshot> teacher = getTeacher();
Your FutureBuilder:
new FutureBuilder(
future: teacher,
builder: ...
)
You are not returning anything from future function. Please check below code
Future<List<DocumentSnapshot>> getTeacher() async {
var firebaseUser = await FirebaseAuth.instance.currentUser;
var docRef = FirebaseFirestore.instance.collection("users");
QuerySnapshot query =
await docRef.where("user_device_language", isEqualTo: "en").limit(10).get();
return query.docs;
}
And handle null value in list view like this
body: new FutureBuilder(
future: getTeacher(),
builder: (BuildContext context, AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Row(
children: <Widget>[
Expanded(child: Text(snapshot.data[index]["name"])),
Expanded(child: Text(snapshot.data[index]["email"])),
Expanded(child: IconButton(icon: Icon(Icons.chat), onPressed: () {}))
],
);
});
} else {
return Text('No Data');
}
}
return Container();
},
),

How to check if current user is an admin in Flutter using Firestore and FirebaseAuth

I want to check if my user is an admin and display a widget depending on that. My data is stored in Firestore such that there is a document in a 'users' collection with a user ID that contains an 'isAdmin' field. My current code looks like this:
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (ctx, futureSnapshot) {
if (futureSnapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final uid = futureSnapshot.data.uid;
return StreamBuilder(
stream: Firestore.instance
.collection('users')
.document(uid)
.snapshots(),
builder: (ctx, userSnapshot) {
if (userSnapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
var userDocument = userSnapshot.data;
if (userDocument['isAdmin'] == true) {
return FloatingActionButton(
onPressed: () {},
);
}
},
);
},
),
],
),
),
);
}
However, this code runs and gives me an error that my build function returned null. How can I check if my current user is an admin if that data is stored in Firestore
Update your code to this
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (ctx, futureSnapshot) {
if (futureSnapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final uid = futureSnapshot.data.uid;
return StreamBuilder(
stream: Firestore.instance
.collection('users')
.document(uid)
.snapshots(),
builder: (ctx, userSnapshot) {
if (userSnapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
var userDocument = userSnapshot.data.data();
if (userDocument['isAdmin'] == true) {
return FloatingActionButton(
onPressed: () {},
);
}
},
);
},
),
],
),
),
);
}
Change 'var userDocument = userSnapshot.data;' to var userDocument = userSnapshot.data.data();

Avoid StreamBuilder refreshing running SetState in Flutter

I have a page which displays 2 elements, both of them are different StreamBuilder but the second one depends on the first one.
To make it more clear I display this:
Firebase documents (list)
Firebase user
If we sign out both StreamBuilder disappear. That's fine, but my problem comes when I need to select a document from the list:
return ListTile(
leading: FlutterLogo(size: 40.0),
title: Text(set["title"]),
selected: _selected[index],
trailing: Badge(
badgeColor: Colors.grey,
shape: BadgeShape.circle,
toAnimate: true,
onTap: () => setState(() => _selected[index] = !_selected[index]),
);
Everytime I do the SetState() I refresh the first StreamBuilder (not sure why) and with this the second one.
This is the list widget:
Widget _mySetsLists(BuildContext context) {
List<bool> _selected = List.generate(20, (i) => false);
return StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (context, snapshot) {
FirebaseUser user = snapshot.data;
if (snapshot.hasData) {
return StreamBuilder(
stream: Firestore.instance
.collection('users')
.document(user.uid)
.collection('sets')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return new ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot set = snapshot.data.documents[index];
return ListTile(
leading: FlutterLogo(size: 40.0),
title: Text(set["title"]),
selected: _selected[index],
onTap: () => setState(() => _selected[index] = !_selected[index]),
);
},
);
} else {
return Center(
child: new CircularProgressIndicator(),
);
}
},
);
} else {
return Text("loadin");
}
},
);
}
}
And this is the user profile:
class UserProfileState extends State<UserProfile> {
#override
Widget build(BuildContext context) {
return SliverList(
delegate: SliverChildListDelegate(
[
_mySetsLists(context),
Divider(),
StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
FirebaseUser user = snapshot.data;
if (user == null) {
return Text('not logged in');
}
return ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(
user.photoUrl,
),
),
title: Text(user.displayName),
subtitle: Text(user.email),
trailing: new IconButton(
icon: new Icon(Icons.exit_to_app),
highlightColor: Colors.pink,
onPressed: () {
authService.signOut();
}),
);
} else {
return Text("loading profile"); // <---- THIS IS WHAT I SEE
}
},
),
],
),
);
}
I also went through the same difficulty, but this is the trick i used
var itemsData = List<dynamic>();
var _documents = List<DocumentSnapshot>();
#override
void initState() {
// TODO: implement initState
super.initState();
getData();
}
getData(){
Firestore.instance
.collection('users')
.document(currentUser.uid)
.collection('set')
.getDocuments()
.then((value) {
value.documents.forEach((result) {
setState(() {
_documents.add(result);
itemsData.add(result.data);
});
});
});
}
replace your listview builder will be like this
ListView.builder(
shrinkWrap: true,
itemCount: _documents.length,
itemBuilder: (context, index) {
return ListTile(
title:Text(itemsData[index]['name'])
)
})
Hope it helps!!
If you pretend to use setstat a lot using the stream you can download the data locally. So every reload will not download data again, but just show the local data.
First step: declare the variable that will store data locally.
QuerySnapshot? querySnapshotGlobal;
Then where you read the streamData, first check if the local data you just declared is empty:
//check if its empty
if(querySnapshotGlobal==null)
//as its empty, we will download it from firestore
StreamBuilder<QuerySnapshot>(
stream: _queryAlunos.snapshots(),
builder: (context, stream){
if (stream.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
else if (stream.hasError) {
return Center(child: Text(stream.error.toString()));
}
else if(stream.connectionState == ConnectionState.active){
//QuerySnapshot? querySnapshot = stream.data;
//instead of save data here, lets save it in the variable we declared
querySnapshotGlobal = stream.data;
return querySnapshotGlobal!.size == 0
? Center(child: Text('Sem alunos nesta turma'),)
: Expanded(
child: ListView.builder(
itemCount: querySnapshotGlobal!.size,
itemBuilder: (context, index){
Map<String, dynamic> map = querySnapshotGlobal!.docs[index].data();
//let it build
return _listDeAlunoswid(map, querySnapshotGlobal!.docs[index].id);
},
),
);
}
return CircularProgressIndicator();
},
)
else
//now, if you call setstate, as the variable with the data is not empty, will call it from here e instead of download it again from firestore, will load the local data
Expanded(
child: ListView.builder(
itemCount: querySnapshotGlobal!.size,
itemBuilder: (context, index){
Map<String, dynamic> map = querySnapshotGlobal!.docs[index].data();
return _listDeAlunoswid(map, querySnapshotGlobal!.docs[index].id);
},
),
),
Hope it helps you save some money!