Show this text when a document field is empty - flutter

I am having trouble in the documentSnapshot. I want to show "No data" when the document field is empty. And show "has data" when the document field has something inside. The problem is that either when the document field has something inside or not it’s always showing "no data"
class _TestState extends State<Test> {
String userUid = FirebaseAuth.instance.currentUser!.uid;
#override
Widget build(BuildContext context) {
return StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection("users")
.doc(userUid)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.data?.data()?.containsValue('groupId') ==null) {
return const Text("no data");
} else {
//It’s always show this
return const Text('has data');
}
});
}
}

you are checking here if the document field is null or not but you need to check whether the field is empty string("") or not since thats how picture shows.
change this line
if (snapshot.data?.data()?.containsValue('groupId') == null)
containsValue is checking if the object from firebase which is something like key and value pair, contains the given [value], in this case 'groupId'. but groupId is key not value from picture shown in your case
try this
String userUid = FirebaseAuth.instance.currentUser!.uid;
bool checkEmpty = false;
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: firestore
.collection('users')
.doc("userUid")
.snapshots(),
builder: (context, snapshot) {
snapshot.data!.data()?.forEach((key, value) {
if (key == 'groupId') {
checkEmpty = value == '';
}
});
return checkEmpty?
const Text('no data'):
const Text('has data')

Related

how to perform query to Firestore and order result Randomly in Flutter

I am retrieving specific documents from firestore collection using flutter stream builder.
the issue is I would like to display the results every single time in a different order (Randomely).
the stream is the below:
stream: FirebaseFirestore.instance
.collection('BusinessProfilesCollection')
.where('Profile_direct_category',
isEqualTo: selecteddirectcategory)
.where('Profile_status', isEqualTo: "Active")
.where('Profile_visibility', isEqualTo: "Yes")
.where('Profile_city',
isEqualTo: globaluserdefaultcity)
.where('Profile_pinning_status',
isEqualTo: "No")
.snapshots(),
the problem is everytime the user do the query the data is returned in the same order. I would like to shuffle it somehow so I remove any advantage from any profile. (document)
I assume you have a list somewhere, where you display your documents? If so, you can use the .shuffle() operator on it! Example:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
String selecteddirectcategory = 'selecteddirectcategory';
String globaluserdefaultcity = 'globaluserdefaultcity';
class RandomResultsScreen extends StatefulWidget {
#override
_RandomResultsScreenState createState() {
return _RandomResultsScreenState();
}
}
class _RandomResultsScreenState extends State<RandomResultsScreen> {
Stream<QuerySnapshot> myStream = FirebaseFirestore.instance
.collection('BusinessProfilesCollection')
.where('Profile_direct_category', isEqualTo: selecteddirectcategory)
.where('Profile_status', isEqualTo: "Active")
.where('Profile_visibility', isEqualTo: "Yes")
.where('Profile_city', isEqualTo: globaluserdefaultcity)
.where('Profile_pinning_status', isEqualTo: "No")
.snapshots();
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: myStream,
builder: (context, asyncSnapshot) {
List<Widget> docs = [];
QuerySnapshot? foundResults = asyncSnapshot.data;
if (foundResults == null) {
//It always wants to be null at first, and then you get errors for calling on null.
return Center(child: CircularProgressIndicator());
} else {
for (QueryDocumentSnapshot doc in foundResults.docs) {
Map<String, dynamic> docData = doc.data() as Map<String, dynamic>;
docs.add(
MyWidget(docData) // Some Widget that you use to display your data
);
}
docs.shuffle(); // <- Where the magic randomization happens!
return ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index) {
return docs[index];
},
);
}
},
),
);
}
}

Realtime Update of UI with StreamBuilder and bool -ERROR Expected a value of type 'Map<dynamic, dynamic>', but got one of type '_JsonDocumentSnapshot'

In the title I explained what I want to do. I have a bool value named
'turnInvitingPlayer' stored somewhere in a document field in Firestore. The location of the document I know exactly from the instance Variables of GameTable.
This is what i tried:
class GameTable extends StatefulWidget {
GameTable({Key? key,
required this.player,
required this.invitationID,
required this.invitationIdPlayerInvited,
required this.invitationIdPlayerInviting})
: super(key: key);
final Player? player;
final String invitationIdPlayerInvited;
final String invitationIdPlayerInviting;
/// the invitation ID is the doc name of the gambling Table
final String invitationID;
#override
State<GameTable> createState() => _GameTableState();
}
class _GameTableState extends State<GameTable> {
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('GameTables')
.doc(widget.invitationID)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var dataGameTable = snapshot.data! as Map;
var turnInvitingPlayer =
dataGameTable['turnInvitingPlayer'] as bool;
if (turnInvitingPlayer == true) {
return Container(color: Colors.blue);
} else {
return Container(color: Colors.red);
}
} else if (!snapshot.hasData) {
return Container(
child: Text('There is no data'),
);
}
return CircularProgressIndicator();
});
}
}
I am getting the following error when I run the App
Expected a value of type 'Map<dynamic, dynamic>', but got one of type '_JsonDocumentSnapshot'
Can somebody show me a way how I can simple access the bool value of the stream and use it in if Clauses?
Thank's to everybody who will help.
Modify your stream builder as follows:
return StreamBuilder<Map<dynamic,dynamic>>(
stream: FirebaseFirestore.instance
.collection('GameTables')
.doc(widget.invitationID)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var dataGameTable = snapshot.data! as Map;
var turnInvitingPlayer =
dataGameTable['turnInvitingPlayer'] as bool;
if (turnInvitingPlayer == true) {
return Container(color: Colors.blue);
} else {
return Container(color: Colors.red);
}
} else if (!snapshot.hasData) {
return Container(
child: Text('There is no data'),
);
}
return CircularProgressIndicator();
});
I found the solution i have done following changes:
var dataGameTable = snapshot.data!; // remove as Map
var turnInvitingPlayer = dataGameTable['turnInvitingPlayer'] as bool; // remove this line
Now I have access to the boolean value with simple dataGameTable['turnInvitingPlayer'].

Firestore how to fetch specific data with specific user id in Flutter

I have stream builder, and I fetch all the users. After that, using bloc (or any state management) I filter them. After filtering, I create a Set which has filtered user ids (I mean there is a set, and it has user ids).
Now, using with these uids I want to fetch filtered user datas. I did with FirebaseFirestore.instance.collection(...).doc(userId).get(), after that it gives Future<String?>. What should I do?
here is the codes:
class HomePageBody extends StatelessWidget {
HomePageBody({
Key? key,
required this.mapsState,
}) : super(key: key);
final MapsState mapsState;
final Set users = {};
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: firestoreStream,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting || snapshot.connectionState == ConnectionState.none) {
return const CustomProgressIndicator(
progressIndicatorColor: blackColor,
);
} else if (!snapshot.hasData) {
return const CustomProgressIndicator(
progressIndicatorColor: blackColor,
);
} else if (snapshot.hasData) {
final usersDatas = snapshot.data.docs;
for (var userDatas in usersDatas) {
if (userDatas["latitude"] == null || userDatas["longitude"] == null) {
} else {
users.add(userDatas);
}
}
context.read<MapsCubit>().filterUsersWithRespectToDistance(users: users);
final usersWithInTenKilometers = mapsState.usersWithInTenKilometers;
**// HERE WE HAVE FILTERED USERS, AND THIS SET HAS USER IDS.**
return ListView.builder(
padding: const EdgeInsets.only(top: 75),
itemCount: usersWithInTenKilometers.length,
itemBuilder: (context, index) {
final userId = usersWithInTenKilometers.elementAt(index);
final usersDatas = FirebaseFirestore.instance
.collection("users")
.doc(userId)
.get();
// I did like this, but it does not work.
return CustomListTile(
userImageUrl: "https://picsum.photos/200/300",
userStatus: "userStatus",
userName: "userName",
);
},
);
}
return const CustomProgressIndicator(
progressIndicatorColor: blackColor,
);
},
);
}
}
Consequently, I have a Set (or you can think like List), and it has user ids. Using these user ids, fetch user datas basically from the Firestore (email: ..., password: ... etc)
final userId = usersWithInTenKilometers.elementAt(index);
final users = FirebaseFirestore.instance
.collection("users")
.doc(userId)
.get()
.then((value) => value)
.then((value) => value.data());
return FutureBuilder(
future: users,
builder: (context, snapshot) {
if (snapshot.hasData) {
final convertUserDataToMap =
Map<String, dynamic>.from(snapshot.data as Map<dynamic, dynamic>);
final List userDataList = convertUserDataToMap.values.toList();
final userId = userDataList[0];
final userLong = userDataList[1];
....
I solved like this
Since you get back a Future<String?>, I'd typically first consider using a FutureBuilder to render that value.
If you have multiple values that each is loaded asynchronously separately (like is the case here with your multiple get() calls), I'd start with using a separate FutureBuilder for each Future. Only if I'd run into practical problems with that, would I start considering more complex options, such as Future.wait() to wait for all of them to complete before rendering any result.

Correcting Null Safety issue with StreamBuilder in Flutter app

I have an null safety issue with my StreamBuilder in my flutter app.
On the open bracket "{" of the builder: property I am getting this error
The body might complete normally, causing 'null' to be returned, but the return type is a potentially non-nullable type.
Here is the code for the StreamBuilder.
StreamBuilder (
stream: _db.collection('agency').doc(globals.agencyId).
collection('trxns').doc(globals.currentTrxnId).snapshots(),
builder: (BuildContext context, AsyncSnapshot trxnSnapshot) {
if (trxnSnapshot.hasData) {
var outPut = (trxnSnapshot.data() as QueryDocumentSnapshot);
clientFNameController.text = trxnSnapshot.data.data['clientFName'] ?? "";
}
),
I tried to add a type like this: StreamBuilder (
but I get this error: The argument type 'Stream<DocumentSnapshot<Map<String, dynamic>>>' can't be assigned to the parameter type 'Stream<QuerySnapshot<Object?>>?'.
Now I change the type to match the statement above and now I am back at the original error message. Here is what I changed the type to.
StreamBuilder <DocumentSnapshot<Map<String, dynamic>>>(
stream: _db.collection('agency').doc(globals.agencyId).
collection('trxns').doc(globals.currentTrxnId).snapshots(),
builder: (BuildContext context, AsyncSnapshot trxnSnapshot) {
if (trxnSnapshot.hasData) {
var outPut = (trxnSnapshot.data() as QueryDocumentSnapshot);
clientFNameController.text = trxnSnapshot.data.data['clientFName'] ?? "";
}
),
I don't know what is wrong or how to fix it. I know I need either this "!" or this "?" but I don't know which one or where to put it.
I would really appreciate some help here.
A StreamBuilder must return a Widget in its builder parameter. If you don't need to show any Widget (just do some background update), you can use a StreamSubscription instead:
class _MyWidgetState extends State<MyWidget> {
late final StreamSubscription<DocumentSnapshot> _subscription;
#override
void initState() {
super.initState();
final Stream<DocumentSnapshot> stream = _db
.collection('agency')
.doc(globals.agencyId)
.collection('trxns')
.doc(globals.currentTrxnId)
.snapshots();
_subscription = stream.listen((data) {
if (data == null) return;
setState(() => clientFNameController.text = data['clientFName'] ?? "");
});
}
#override
void dispose() {
_subscription.cancel();
super.dispose();
}
}
However, if you want to keep using StreamBuilder, you can
just return an empty Widget (not really a good practice):
StreamBuilder(
stream: _db
.collection('agency')
.doc(globals.agencyId)
.collection('trxns')
.doc(globals.currentTrxnId)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot trxnSnapshot) {
if (trxnSnapshot.hasData) {
var outPut = (trxnSnapshot.data as QueryDocumentSnapshot);
clientFNameController.text = outPut.data['clientFName'] ?? "";
}
return SizedBox.shrink();
},
),
return a meaningful Widget based on each operation:
StreamBuilder(
stream: _db
.collection('agency')
.doc(globals.agencyId)
.collection('trxns')
.doc(globals.currentTrxnId)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot trxnSnapshot) {
if (trxnSnapshot.hasData) {
var outPut = (trxnSnapshot.data as QueryDocumentSnapshot);
clientFNameController.text = outPut.data['clientFName'] ?? "";
return Text("client name updated");
}
return Text("client name not updated");
},
),

Flutter: Future<String> returns null but if printing it has value

username is null but if I'm printing 'value' it contains some string, how can I get 'value'?
class HomeWrapper extends StatelessWidget {
final DataBaseServices _db = DataBaseServices();
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
String username;
_db.getUsername(user).then((value) => username = value);
print(username);
if(username != null){
return Home();
}else{
_db.createBlankUser(user);
return EditProfile();
}
}
.then() is called when the value of the Future is returned. So the value of value is always non null, whereas username is null when you print it.
Try the difference by replacing .then(...) with:
.then((value){
username = value;
print(username);
});
Additionally, you can have a look at how to handle Asynchronous data in Flutter
I'm guessing _db.getUsername is returning a Future?
In that case you should look into using FutureBuilder
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
return FutureBuilder(
builder: (context, snap) {
//snap.data will be the username
if(snap.hasData) {
return Home();
} else {
//you need to wait for another Future here I guess?
return FutureBuilder(
builder: (context, snap2){
if(snap2.connectionState == ConnectionState.done){
return EditProfile();
} else {
//return some sort of circular loader icon.
}
},
future: _db.createBlankUser(user)
);
}
},
future: _db.getUsername(user),
);