I am building a flutter app and using cloud-firestore,
this is how my database looks like
I want a function that retrieves all documents in the collection called "Driver List" in an array of strings
that what I had already used but it gets them back in a listview in a new screen
class DriverList extends StatelessWidget {#overrideWidget build(BuildContext context) {
return new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('DriverList').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['name']),
subtitle: new Text(document['phone']),
);
}).toList(),
);
},
);
}
}
This has some additional logic to remove potentially duplicate records, but you can use something like the following to retrieve data from Firestore.
We get access to a collection reference, then list the results of the query, then create local model objects for the data returned by Firestore, and then we return the a list of those model objects.
static Future<List<AustinFeedsMeEvent>> _getEventsFromFirestore() async {
CollectionReference ref = Firestore.instance.collection('events');
QuerySnapshot eventsQuery = await ref
.where("time", isGreaterThan: new DateTime.now().millisecondsSinceEpoch)
.where("food", isEqualTo: true)
.getDocuments();
HashMap<String, AustinFeedsMeEvent> eventsHashMap = new HashMap<String, AustinFeedsMeEvent>();
eventsQuery.documents.forEach((document) {
eventsHashMap.putIfAbsent(document['id'], () => new AustinFeedsMeEvent(
name: document['name'],
time: document['time'],
description: document['description'],
url: document['event_url'],
photoUrl: _getEventPhotoUrl(document['group']),
latLng: _getLatLng(document)));
});
return eventsHashMap.values.toList();
}
Source: https://github.com/dazza5000/austin-feeds-me-flutter/blob/master/lib/data/events_repository.dart#L33
Getting one time data:
var collection = FirebaseFirestore.instance.collection('DriverList');
var querySnapshot = await collection.get();
for (var queryDocumentSnapshot in querySnapshot.docs) {
Map<String, dynamic> data = queryDocumentSnapshot.data();
var name = data['name'];
var phone = data['phone'];
}
Getting data each time it changes, using a StreamBuilder:
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance.collection('DriverList').snapshots(),
builder: (_, snapshot) {
if (snapshot.hasError) return Text('Error = ${snapshot.error}');
if (snapshot.hasData) {
final docs = snapshot.data!.docs;
return ListView.builder(
itemCount: docs.length,
itemBuilder: (_, i) {
final data = docs[i].data();
return ListTile(
title: Text(data['name']),
subtitle: Text(data['phone']),
);
},
);
}
return Center(child: CircularProgressIndicator());
},
)
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class LoadDataFromFirestore extends StatefulWidget {
#override
_LoadDataFromFirestoreState createState() => _LoadDataFromFirestoreState();
}
class _LoadDataFromFirestoreState extends State<LoadDataFromFirestore> {
#override
void initState() {
super.initState();
getDriversList().then((results) {
setState(() {
querySnapshot = results;
});
});
}
QuerySnapshot querySnapshot;
#override
Widget build(BuildContext context) {
return Scaffold(
body: _showDrivers(),
);
}
//build widget as prefered
//i'll be using a listview.builder
Widget _showDrivers() {
//check if querysnapshot is null
if (querySnapshot != null) {
return ListView.builder(
primary: false,
itemCount: querySnapshot.documents.length,
padding: EdgeInsets.all(12),
itemBuilder: (context, i) {
return Column(
children: <Widget>[
//load data into widgets
Text("${querySnapshot.documents[i].data['activation']}"),
Text("${querySnapshot.documents[i].data['car1']}"),
Text("${querySnapshot.documents[i].data['car2']}"),
Text("${querySnapshot.documents[i].data['car5']}"),
Text("${querySnapshot.documents[i].data['name']}"),
Text("${querySnapshot.documents[i].data['phone']}"),
],
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
//get firestore instance
getDriversList() async {
return await Firestore.instance.collection('DriversList').getDocuments();
}
}
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
StreamBuilder(
stream:
FirebaseFirestore.instance.collection('messages').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
}
if (snapshot.hasData) {
final messages = snapshot.data!.docs;
List<Text> messageWigdets = [];
for (var message in messages) {
final messageText = message['text'];
final messageSender = message['sender'];
final messageWigdet =
Text('$messageText from $messageSender');
messageWigdets.add(messageWigdet);
}
return Expanded(
child: ListView(
children: [...messageWigdets],
),
);
}
return const CircularProgressIndicator.adaptive();
},
),
Related
im trying to return two futures and getting the above error when trying to access the data within the returned snapshot.Specifically this line:
${snapshot.data!["firstName"]}
Any ideas as to the correct way to access this data, can't see any documentation anywhere defining this.
Code is as follows:
Widget build(BuildContext context) {
return FutureBuilder(
future: Future.wait([getData(), getMood()]),
builder: (_, snapshot) {
if (snapshot.hasData) {
return Scaffold(
appBar: AppBar(
title: const Text('Display the Picture'),
backgroundColor: kPrimaryColor,
),
// The image is stored as a file on the device. Use the `Image.file`
// constructor with the given path to display the image.
body: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 20.0, 8.0, 8.0),
child: Column(
children: [
Center(
child: Text(
"${snapshot.data!["firstName"]} \n\n "
"We have predicted your mood as:\n\n "
"${snapshot.data!.data()!["mood"]}\n\n"
"Please select a reason associated to your mood",
style: const TextStyle(
color: Colors.black, fontSize: 15),
textAlign: TextAlign.center,
),
),
],
),
),
],
),
),
);
} else {
return CircularProgressIndicator();
}
},
);
}
Future getData() async {
var currentUser = FirebaseAuth.instance.currentUser;
DocumentSnapshot q1 = await FirebaseFirestore.instance
.collection('USER_TABLE')
.doc(currentUser!.uid)
.get();
return q1;
}
Future getMood() async {
var currentUser = FirebaseAuth.instance.currentUser;
QuerySnapshot q2 = await FirebaseFirestore.instance
.collection('userMood')
.where('userId' == currentUser!.uid)
.orderBy('createdAt',descending: true)
.limit(1)
.get();
return q2;
}
}
Thanks
EDIT
future: Future.wait([getData(), getMood()]),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (snapshot.hasData) {
var a = snapshot.data![0]["firstName"] as String;
var b = snapshot.data![1]['Prediction'] as String;
Future <String> getData() async {
var currentUser = FirebaseAuth.instance.currentUser;
DocumentSnapshot q1 = await FirebaseFirestore.instance
.collection('USER_TABLE')
.doc(currentUser!.uid)
.get();
return q1.toString();
}
Future <String> getMood() async {
var currentUser = FirebaseAuth.instance.currentUser;
QuerySnapshot q2 = await FirebaseFirestore.instance
.collection('userMood')
.where('userId' == currentUser!.uid)
.orderBy('createdAt',descending: true)
.limit(1)
.get();
return q2.toString();
}
Let's simplify the task:
Future getA() async {
await Future.delayed(Duration(seconds: 1));
return 12.34;
}
Future getB() async {
await Future.delayed(Duration(seconds: 1));
return 'abc';
}
... FutureBuilder(
future: Future.wait([getA(), getB()]),
builder: (context, snapshot) {
if (snapshot.hasData) {
// how to get a and b from snapshot?
} else {
return CircularProgressIndicator();
}
},
)
You're trying to do snapshot.data!['a'] and snapshot.data!['b']. It doesn't work because snapshot.data isn't Map<String, something>.
Future.wait returns Future<List<T>>. So snapshot.data will be List<T>, and you can get individual items like snapshot.data![0] and snapshot.data![1].
Another problem is that Future.wait doesn't support different item types, so the items type will be Object and you'll need to cast it manually.
Future<double> getA() async {
await Future.delayed(Duration(seconds: 1));
return 12.34;
}
Future<String> getB() async {
await Future.delayed(Duration(seconds: 1));
return 'abc';
}
... FutureBuilder<List<Object>>(
future: Future.wait([getA(), getB()]),
builder: (context, snapshot) {
if (snapshot.hasData) {
var a = snapshot.data![0] as double;
var b = snapshot.data![1] as String;
return Text('$a $b');
} else {
return CircularProgressIndicator();
}
},
)
I am trying to get data (In real-time) from my database using QuerySnapshot.
and I need to update my data as well. This is my method and I don't know how to get my documentID in this method.
class LoadData extends StatefulWidget {
const LoadData({Key? key}) : super(key: key);
#override
_LoadDataState createState() => _LoadDataState();
}
class _LoadDataState extends State<LoadData> {
//Read Data in realTime Snapshot
final Stream<QuerySnapshot> _cupCakeStream = FirebaseFirestore.instance
.collection('cupcake')
.snapshots(includeMetadataChanges: true);
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _cupCakeStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
shrinkWrap: true,
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
print(data); //**Print collection only..I need to get documentID with each collection**
return ListTile(
title: Text(data['cupcake_name']),
subtitle: Text(data['description']),
);
}).toList(),
);
},
);
}
}
document is a DocumentSnapshot and has an id property.
So, the following should do the trick:
return ListView(
shrinkWrap: true,
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
print(data);
print(document.id); // <= Should print the doc id
return ListTile(
title: Text(data['cupcake_name']),
subtitle: Text(data['description']),
);
}).toList(),
);
I have a regular List View that fetches some data from Firestore, here is the code for it:
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('orders').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(
child:
CircularProgressIndicator(),
);
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot ds = snapshot.data.docs[index];
return Text(ds['name']);
Now if I wanted to create a delete button somewhere in it, I would do something like this:
FirebaseFirestore.instance.collection('orders').doc('ID SHOULD BE HERE').delete();
The issue I have is how do I find the document ID of the list tile on here in order to delete it automatically from the database? ID SHOULD BE HERE in the code should be some command to find the id of it like in the screenshot below:
DocumentSnapshot contains a property called id which will return the document's given ID for this snapshot. Therefore you can do the following:
itemBuilder: (context, index) {
DocumentSnapshot ds = snapshot.data.docs[index];
print(ds.id);
return Text(ds['name']);
And then to delete it according to the id, do the following:
FirebaseFirestore.instance.collection('orders').doc(ds.id).delete();
By saving the reference id from doc in local model and using the reference id on your required set of operation
I have provide the realtime example of removing the data.
class Employee {
Employee(this.employeeID, this.employeeName, this.branch, this.designation, this.location,
this.salary,
{this.reference});
double employeeID;
String employeeName;
String designation;
String branch;
String location;
double salary;
DocumentReference reference;
factory Employee.fromSnapshot(DocumentSnapshot snapshot) {
Employee newEmployee = Employee.fromJson(snapshot.data());
newEmployee.reference = snapshot.reference;
return newEmployee;
}
factory Employee.fromJson(Map<String, dynamic> json) =>
_employeeFromJson(json);
Map<String, dynamic> toJson() => _employeeToJson(this);
#override
String toString() => 'employeeName ${employeeName}';
}
Employee _employeeFromJson(Map<String, dynamic> data) {
return Employee(
data['employeeID'],
data['employeeName'],
data['branch'],
data['designation'],
data['location'],
data['salary'],
);
}
Map<String, dynamic> _employeeToJson(Employee instance) {
return {
'employeeID' : instance.employeeID,
'employeeName': instance.employeeName,
'branch': instance.branch,
'designation': instance.designation,
'location': instance.location,
'salary': instance.salary,
};
}
Repository
class EmployeeRepository{
List<Employee> employees = [];
final CollectionReference collection =
FirebaseFirestore.instance.collection('employees');
Stream<QuerySnapshot> getStream() {
return collection.snapshots();
}
Future<DocumentReference> add(Employee employee) {
var documentReference = collection.add(employee.toJson());
return documentReference;
}
update(Employee employee) async {
collection.doc(employee.reference.id).update(employee.toJson());
}
delete(Employee employee) async {
collection.doc(employee.reference.id).delete();
}
fromSnapShot(DocumentSnapshot snapshot) => Employee.fromSnapshot(snapshot);
Future<void> buildData(AsyncSnapshot snapshot) async {
if (snapshot.data.documents.length == 0) {
employees = [];
}
employees = await snapshot.data.documents.map<Employee>((doc) {
return Employee.fromSnapshot(doc);
}).toList(growable: false);
}
}
Listview builder
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Employees List'),
),
body: Column(children: [
StreamBuilder<QuerySnapshot>(
stream: employeeRepository.getStream(),
builder: (context, snapShot) {
if (!snapShot.hasData ||
snapShot.hasError ||
snapShot.connectionState == ConnectionState.waiting) {
return Container(
child: Center(child: CircularProgressIndicator()),
);
}
employeeRepository.buildData(snapShot);
return ListView.builder(
itemBuilder: (context, index) {
final employee = employeeRepository.employees[index];
return ListTile(
title: Text(employee.employeeName),
onLongPress: () {
showDialog<AlertDialog>(
context: context,
builder: (context) {
return AlertDialog(
actions: [
FlatButton(
onPressed: () {},
child: Text('Edit')),
FlatButton(
onPressed: () {
setState(() {
employeeRepository
.delete(employee);
Navigator.pop(context);
});
},
child: Text('Delete')),
],
content: Text(employee.employeeName),
);
});
},
);
},
);
}),
]),
);
}
CRUD Operation in firestore
what I am trying to achieve is load data from a specific collection(teacher) of my database. So I am using a function called isTeacher(). which checks if the current user's uid belongs in that collection. if not then it is a student. it stores the value in a string called value. so when I am using stream builder to load data available in their specific collection or documents, my stream builder shows circular progress and after that, it doesn't load the data. Any help is appreciated.
Thank you
`class MyClasses extends StatefulWidget {
#override
_MyClasses createState() => _MyClasses();
}
String value;
String classPassword;
List<dynamic> catchUserDetails = [];
class _MyClasses extends State<MyClasses> {
Future isTeacher() {
return FirebaseFirestore.instance
.collection('teacher')
.doc(FirebaseAuth.instance.currentUser.uid)
.get()
.then((DocumentSnapshot doc) {
value = doc.exists.toString();
print(doc.data());
print(value);
print('isteacher called in method');
});
}
#override
Widget build(BuildContext context) {
isTeacher();
return Scaffold(
body: SafeArea(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection(value)
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('class')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
} else {
final messages = snapshot.data.documents.reversed;
List<GenerateClass> messageBubbles = [];
for (var message in messages) {
final messageText = message.data()['className'];
final messageBubble = GenerateClass(
classID: messageText,
//nnouncementID: i,
);
messageBubbles.add(messageBubble);
}
return ListView(
//itemExtent: 100,
children: messageBubbles,
);
}
},
),
),
);`
Solved it by using a FutureBuilder
FutureBuilder(
future: isTeacher(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return StreamBuilder();
I want to fetch currently logged in user data. There is a field in fire_store 'useremail'. When a user logs in, I get his ID and using 'where class' I fetch the animal's data against his ID shown below:
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('booking_tbl').where("useremail", isEqualTo: _firebaseUser.email.toString()).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Although it is working but it shows error on as well. I need some help to fix this issue or an alternate suggestion for this (Any suggestion or kind help would be highly appreciated):
════════ Exception caught by widgets library
══════════════════════
NoSuchMethodError was thrown building UserBookingHistoryModel(dirty,
state: _UserBookingHistoryModelState#2d8c2):
The getter 'email' was called on null.
Receiver: null
Tried calling: email
Probably the problem is caused by this snippet in Firebase Auth:
void initState() {
super.initState();
widget.auth.getCurrentUser().then((firebaseUserId) {
setState(() {
authStatus = firebaseUserId == null
? AuthStatus.notSignedIn
: AuthStatus.signedIn;
});
});
}
The full code of bookings.dart is here:
class _UserBookingHistoryModelState extends State<UserBookingHistoryModel> {
FirebaseAuth _auth;
FirebaseUser _firebaseUser;
#override
void initState() {
super.initState();
_auth = FirebaseAuth.instance;
_getCurrentUser();
}
_getCurrentUser () async {
_firebaseUser = await FirebaseAuth.instance.currentUser();
setState(() {
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('booking_tbl').where("useremail", isEqualTo: _firebaseUser.email.toString()).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 5.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
return Padding(
key: ValueKey(record.animal),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
),
child: new ListTile(
title: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(
"${record.animal} Slaughtering",
style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.black),
),
],
),
)
),
);
}
}
class Record {
final String animal;
final String user;
final DocumentReference reference;
Record.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['animal'] != null),
assert(map['user'] != null),
animal = map['animal'],
user = map['user'];
Record.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Record<$animal:$user>]";
}
You need to do the following:
Stream<QuerySnapshot> getData() async*{
FirebaseUser firebaseUser = await FirebaseAuth.instance.currentUser();
yield* Firestore.instance.collection('booking_tbl').where("useremail", isEqualTo: firebaseUser.email.toString()).snapshots();
}
Then inside the StreamBuilder use getData():
return StreamBuilder<QuerySnapshot>(
stream: getData(),
builder: (context, snapshot) {
//....
The getData() method is asynchronous, since you are using a StreamBuilder then you need to return a Stream therefore you use the async* keyword, and you emit the result using yield*