Async Functions: Future<Query Snapshot> to Bool in Flutter? - firebase

I have this piece of code right here: if the user exists in the database and it's followed by the CurrentUser it builds an "Unfollow" custom button, otherwise it builds a follow button
CustomButton follow = CustomButton("Follow", Colors.red, Colors.red, Colors.white, user);
CustomButton unfollow = CustomButton("Unfollow", Colors.black, Colors.black, Colors.white, user);
AvatarHeader(user.username, " ", user.photoURL, checkIfFollowing(user.id)== true ? unfollow : follow)
the checkIfFollowing() function is always evaluated false probably because I have a problem in this asyncronus function which does not return a boolean value
checkIfFollowing(String fetchedUser) async{
DocumentSnapshot doc = await FOLLOWERS
.document(fetchedUser)
.collection('userFollowers')
.document(CURRENTUSER.id)
.get()
return doc.exists;
}
How can i fix this?
EDIT
search(String query) {
Future<QuerySnapshot> users = USERS
.where("username", isGreaterThanOrEqualTo: query)
.getDocuments();
print(query);
print("");
onStringChange(users);
}
FutureBuilder buildResults(){
return FutureBuilder (
future: results,
builder: (context, snapshot) {
if (!snapshot.hasData) {
print("i dont have data");
return circularProgress();
}
List<AvatarHeader> searchResults = [];
snapshot.data.documents.forEach((doc) async {
User user = User.fromDocument(doc);
if (user.photoURL != null) {
print(user.username);
bool check = await checkIfFollowing(user.id);
CustomButton follow = CustomButton("Follow", Colors.red, Colors.red, Colors.white, user);
CustomButton unfollow = CustomButton("Unfollow", Colors.black, Colors.black, Colors.white, user);
searchResults.add(AvatarHeader(user.username, " ", user.photoURL, check == true ? unfollow : follow));
}
});
return ListView(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
children: searchResults,
);
},
);
}

You need to return a Future in async functions, in your particular case you need a Future<bool>
Future<bool> checkIfFollowing(String fetchedUser) async{
DocumentSnapshot doc = await FOLLOWERS
.document(fetchedUser)
.collection('userFollowers')
.document(CURRENTUSER.id)
.get()
return doc.exists;
}
I recommend you to take a look to Asynchronous programming: futures, async, await
A future (lower case “f”) is an instance of the Future (capitalized “F”) class. A future represents the result of an asynchronous operation, and can have two states: uncompleted or completed.
EDIT
Without more details is very hard to be sure how to solve your specific issue. If I understand correctly, you need to have something in line with this
FutureBuilder buildResults() {
return FutureBuilder(
future: results,
builder: (context, snapshot) {
if (!snapshot.hasData) {
print("i dont have data");
return circularProgress();
}
// get documents where user.photoURL != null
var docsWhereUserPhotoIsNotNull = getDocumentsWithUserPhotoNotNull(snapshot.data.documents);
// build a list of FutureBuilders
return ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: docsWhereUserPhotoIsNotNull.length,
itemBuilder: (context, index) {
var doc = docsWhereUserPhotoIsNotNull[index];
var user = User.fromDocument(doc);
return FutureBuilder(
future: checkIfFollowing(user.id),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
bool check = snapshot.data;
CustomButton follow = CustomButton("Follow", Colors.red, Colors.red, Colors.white, user);
CustomButton unfollow = CustomButton(
"Unfollow", Colors.black, Colors.black, Colors.white, user);
return AvatarHeader(user.username, " ", user.photoURL, check == true ? unfollow : follow);
});
});
}
);
}
Where
List<DocumentSnapshot> getDocumentsWithUserPhotoNotNull(List<DocumentSnapshot> documents) {
var documentsWithUserPhotoNotNull = List<DocumentSnapshot>();
documents.forEach((doc) async {
User user = User.fromDocument(doc);
if (user.photoURL != null) {
documentsWithUserPhotoNotNull.add(doc);
}
});
return documentsWithUserPhotoNotNull;
}

Related

The operator '[]' isn't defined for the type 'Object' when trying to access data from futureBuilder

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();
}
},
)

How to compare multiple fields in a query from Firestore?

I'm trying to find a way to compare the location of all users and show nearby people as a result. In this question, Frank explains how it should be done. But I have no idea how to do the third step.
That's what I've achieved so far:
double _userLatitude;
double _userLongitude;
_getUserLocation() async {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
_userLatitude = position.latitude;
_userLongitude = position.longitude;
final firebaseUser = await FirebaseAuth.instance.currentUser();
if (firebaseUser != null)
await usersRef.document(firebaseUser.uid).updateData({
"location": "active",
"latitude": _userLatitude,
"longitude": _userLongitude,
});
}
_getNearbyUsers() {
usersRef.where('location', isEqualTo: "active").getDocuments();
Geolocator.distanceBetween(
_userLatitude, _userLongitude, endLatitude, endLongitude);
}
StreamBuilder(
stream: _getNearbyUsers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Text(
"Loading...",
),
);
} else if (snapshot.data.documents.length == 1) {
return Center(
child: Text(
":/",
),
);
}
return ListView.builder(
padding: EdgeInsets.only(top: 10),
scrollDirection: Axis.vertical,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
_buildListUsers(context, snapshot.data.documents[index]));
},
),
Widget _buildListUsers(BuildContext context, DocumentSnapshot document) {
List<Users> usersList = [];
var data = document.data;
if (data["id"] != _userId) {
Users user = Users();
user.id = document["id"];
usersList.add(user);
return ListTile(
onTap: () {
Navigator.pushNamed(context, "/users", arguments: user);
},
title: Text(
document['id'],
),
);
}
return SizedBox();
}
I have no idea what to do next in _getNearbyUsers().
The Geolocator.distanceBetween function returns the distance in meters. With the distance you should apply the logic for your application, like performing an action if the distance is smaller than 500m, for example.
Something like this:
double distanceInMeters = Geolocator.distanceBetween(_userLatitude, _userLongitude, endLatitude, endLongitude);
if (distanceInMeters < 500) {
// Your custom logic goes here
}

The getter 'docs' isn't defined for the type 'AsyncSnapshot<Object?>'

After migrating to null safety getting error The getter 'docs' isn't defined for the type 'AsyncSnapshot<Object?>'.
Try importing the library that defines 'docs', correcting the name to the name of an existing getter, or defining a getter or field named 'docs'.
Code snippet where error is
return FutureBuilder(
future: searchResultsFuture,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
List<UserResult> searchResults = [];
snapshot.docs.forEach((doc) { //have error here
User user = User.fromDocument(doc);
UserResult searchResult = UserResult(user);
searchResults.add(searchResult);
});
return ListView(
children: searchResults,
);
},
);
}
searchResultsFuture
handleSearch(String query) {
Future<QuerySnapshot> users =
usersRef.where("displayName", isGreaterThanOrEqualTo: query).get();
setState(() {
searchResultsFuture = users;
});
}
clearSearch() {
searchController.clear();
}
The snapshot in your code is an AsyncSnapshot, which indeed doesn't have a docs child. To get the docs from Firestore, you need to use:
snapshot.data.docs
Also see the FlutterFire documentation on listening for realtime data, which contains an example showing this - and my answer here explaining all snapshot types: What is the difference between existing types of snapshots in Firebase?
change like this:
return FutureBuilder(
future: searchResultsFuture,
builder: (context, **AsyncSnapshot** snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
List<UserResult> searchResults = [];
**snapshot.data!.docs.forEach((doc) {**
User user = User.fromDocument(doc);
UserResult searchResult = UserResult(user);
searchResults.add(searchResult);
});
return ListView(
children: searchResults,
);
},
);
}
usually, it takes a few ms for data to retrieve so I tried this to
make sure my operations are performed after data is retrieved
return StreamBuilder<QuerySnapshot>(
stream: Collectionreference
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> activitySnapshot) {
if (activitySnapshot.hasError) {
return Center(
child: Text('Something went wrong'),
);
}
if (activitySnapshot.connectionState == ConnectionState.waiting) {
return Center(
child: SpinKitWave(
color: constants.AppMainColor,
itemCount: 5,
size: 40.0,
)));
}
if (!activitySnapshot.hasData || activitySnapshot.data.docs.isEmpty) {
return Center(
child: Text('Nothing to Display here'),
);
}
if (activitySnapshot.hasData) {
activitySnapshot.data.docs.forEach(doc=>{
print(doc);
})
}
}
});

'Future<QuerySnapshot>' is not a subtype of type 'Stream<dynamic>'?

Im struggling trying to use a streambuider. Im getting an error that says :
════════ Exception caught by widgets library ═══════════════════════════════════
The following _TypeError was thrown building StreamBuilder<UserData>(dirty, state: _StreamBuilderBaseState<UserData, AsyncSnapshot<UserData>>#e2c02):
type 'Future<QuerySnapshot>' is not a subtype of type 'Stream<dynamic>' of 'function result'
heres my code
Stream myVideos;
getalldata() async {
//get videos as future
myVideos = FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: widget.userid)
.snapshots();
var documents = await FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: widget.userid)
.get();
if (documents.docs.isNotEmpty) {
for (var item in documents.docs) {
likes = item.data()['likes'].length + likes;
}
} else {
setState(() {
picturesdontexists = true;
});
}
setState(() {
dataisthere = true;
});
}
#override
Widget build(BuildContext context) {
final user = Provider.of<Userforid>(context);
return dataisthere == false
? Scaffold(body: Center(child: CircularProgressIndicator()))
: StreamBuilder<UserData>(
stream: DatbaseService(uid: user?.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
- - - - - - - - - -
),
),
StreamBuilder(
stream: myVideos,
builder: ( context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if(videos>0){
print(snapshot.data);
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video =
snapshot.data.docs[index];
return InkWell(
onTap: () {
Navigator.of(context)
.pushNamed(ChatFirstpage.route);
},
child: Card(
elevation: 0.0,
What I think is that the error is thrown in the stream because before im using the stream myVideos im getting no error and all works fine .
Maye anyone can help Thank!. if you need more Information leave a comment .
Heres mine DatbaseService
class DatbaseService {
static DatbaseService instance = DatbaseService();
final String uid;
String _messages = "messages";
String _images = "images";
DatbaseService({this.uid});
//userData from snapshot
UserData userDataFromSnapshot(DocumentSnapshot snapshot) {
return UserData(
uid: uid,
email: snapshot.data()['email'],
fullname: snapshot.data()['fullname'],
password: snapshot.data()['password'],
url: snapshot.data()['url'],
username: snapshot.data()['username'],
);
}
//get user doc stream
Stream<UserData> get userData {
return myprofilsettings.doc(uid).snapshots().map(userDataFromSnapshot);
}
``
This usually arises when you are passing a Future where Stream should have gone or where you have defined the type of the variable as Stream but you are putting that variable equal to a future value.
I think instead of creating a variable like Stream myVideos you can directly put
FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: widget.userid)
.snapshots()
inside the stream builder.
Also please provide the whole code (DatbaseService).

how to get data from firebase in flutter

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();
},
),

Resources