how to get document id Firestore - firebase

I am new to Firestore. I am stuck at getting the document ID from Firestore.
Here I create the user with name and age parameters.
When I click Submit, it submits to Cloud Firestore. Everything is Ok up to now.
class _MyHomePageState extends State<MyHomePage> {
late final TextEditingController name = TextEditingController();
late final TextEditingController age = TextEditingController();
final DBHelper _helper = DBHelper();
late final User _user=User();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: name,
decoration: const InputDecoration(hintText: "Name"),
),
TextField(
controller: age,
decoration: const InputDecoration(hintText: "Age"),
),
ElevatedButton(onPressed:(){
_user.setAge(age.text);
_user.setName(name.text);
print("Name: ${_user.name}");
_helper.addUser(_user);
Navigator.push(context, MaterialPageRoute(builder: (builder)=>MainPage( ,))); //here i need to push the document id
}, child: const Text("Submit"))
],
),
),
);
}
This is my custom User model
class User{
late final String userId;
final String name;
final String age;
User({required this.name, required this.age});
}
This is my service class for Firestore methods
class DBHelper{
// final User user = User();
final CollectionReference _reference= FirebaseFirestore.instance.collection("users"); // database path
//Adding user to Firestore
Future<void> addUser(User user){
return _reference.add({
'name':user.name,
'age':user.age,
}).then((value) => print("User added"))
.catchError((onError)=> print("Failed to add the user: $onError"));
}
}
Here is what I want: When I click on the submit button in the previous page. It will navigate to the following page. In this page, I want to show the "name" and "age" information. The only thing that is missing is the documentId
return FutureBuilder<DocumentSnapshot>(
future: _ref.doc(documentId).get(), //which document doesn't know
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text(
"Bir hata oluştu.",
textScaleFactor: 3,
));
} else if (snapshot.hasData && !snapshot.data!.exists) {
return const Center(
child: Text(
"Döküman yok",
textScaleFactor: 3,
));
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Name: ${data['name']}",
textScaleFactor: 3,
),
Text(
"Age:${data['age']}",
textScaleFactor: 3,
),
],
)),
);
} else {
return const Center(child: CircularProgressIndicator());
}
});

you can get the id on the metodh you used to submit infos
instead of return void,return a String(the id) then go to the next screen
like that:
Future<String> addUser(User user)async {
return await _reference.add({
'name':user.name,
'age':user.age,
}).then((value) => var documentId=value.id)
and the button function should be:
ElevatedButton(onPressed:(){
_user.setAge(age.text);
_user.setName(name.text);
print("Name: ${_user.name}");
var id=await _helper.addUser(_user);
Navigator.push(Get.context!, MaterialPageRoute(builder:(builder)=>MainPage(documentI: id)));
}, child: const Text("Submit"))
],
),

Related

'Future is not a subtype of type 'Widget' - error only present when using Future.wait

I have a string error which I can't seem to debug I think its related to how I'm trying to retrieve data when using Future.wait I get the above error. If I only build a single future and don't use Future.wait and retrieve data using snapshot.data!.data()!["prediction"] I don't get any errors.
Code below for my future.wait
Any help appreciated!
class CorrectMood extends StatefulWidget {
const CorrectMood({Key? key}) : super(key: key);
#override
_CorrectMoodState createState() => _CorrectMoodState();
}
class _CorrectMoodState extends State<CorrectMood> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Future.wait([getData(), getLatestMood()]),
builder: (context, AsyncSnapshot<List> snapshot) {
snapshot.data![0]; //getLatestMood
snapshot.data![1]; //getData
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![0]["firstName"]}"
// "${snapshot.data![0].prediction}"
//"${snapshot.data!.data()!["firstName"]} \n\n "
"We have predicted your mood as:\n\n "
//"${DatabaseService.getMood()}\n\n"
"Please select a reason associated to your mood",
style: const TextStyle(
color: Colors.black, fontSize: 15),
textAlign: TextAlign.center,
),
),
countDocuments(),
],
),
),
],
),
),
);
}else {
return CircularProgressIndicator();
}
},
);
}
}
Future<DocumentSnapshot<Map<String, dynamic>>> getData() async {
var currentUser = FirebaseAuth.instance.currentUser;
return await FirebaseFirestore.instance
.collection('USER_TABLE')
.doc(currentUser!.uid)
.get();
}
countDocuments() async {
var currentUser = FirebaseAuth.instance.currentUser;
QuerySnapshot _myDoc = await FirebaseFirestore.instance
.collection('userMoods')
.doc('useerID')
.collection(currentUser!.uid)
.get();
List<DocumentSnapshot> _myDocCount = _myDoc.docs;
return (_myDocCount.length);
}
Future<DocumentSnapshot<Map<String, dynamic>>> getLatestMood() async {
var currentUser = FirebaseAuth.instance.currentUser;
var latestMoodDoc = countDocuments();
return await FirebaseFirestore.instance
.collection('userMoods')
.doc('useerID') // make this the userID
.collection('UbcNaFtJwXWoId9J5RLuVBVPhpN2') // make this increment every time
.doc('2') //this can be system generated name don't care about it
.get();
}

Flutter Firebase return a Querysnapshot and DocumentSnapshot in the same widget

I've spent weeks trying to sort this issue and can't seem to sort it.
I have a database with two collections userTable and userMoods
I have a future builder which is returning the name, however I am querying the userMood table to return the last created document.
I cannot seem to find a way to get this data back out.
Picture of data I am trying to retrieve:
Code is as follows:
class CorrectMood extends StatefulWidget {
const CorrectMood({Key? key}) : super(key: key);
#override
_CorrectMoodState createState() => _CorrectMoodState();
}
class _CorrectMoodState extends State<CorrectMood> {
Future<DocumentSnapshot<Map<String, dynamic>>>? _fetchedData;
#override
void initState() {
super.initState();
_fetchedData = getData();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _fetchedData,
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot<Map<String, dynamic>>> 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!.data()!["firstName"]} \n\n "
"We have predicted your mood as:\n\n "
//"${DatabaseService.getMood()}\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<DocumentSnapshot<Map<String, dynamic>>> getData() async {
var currentUser = FirebaseAuth.instance.currentUser;
return await FirebaseFirestore.instance
.collection('USER_TABLE')
.doc(currentUser!.uid)
.get();
}
Future<QuerySnapshot<Map<String, dynamic>>> getMood() async {
var currentUser = FirebaseAuth.instance.currentUser;
return await FirebaseFirestore.instance
.collection('userMood')
.where('userId' == currentUser!.uid)
.orderBy('createdAt', descending: true)
.limit(1)
.get();
Any help is greatly appreciated!
you can use getData() directly to your FutureBuilder.
by the way I cannot where you are calling getMood() function.

Futurebuilder snapshot has no data

CollectionReference users = FirebaseFirestore.instance.collection('Users');
FirebaseAuth auth = FirebaseAuth.instance;
String uid = FirebaseAuth.instance.currentUser!.uid.toString();
var userData;
var dbFuture;
#override
void initState() {
dbFuture = getData();
super.initState();
}
Future getData() async {
final String uid = FirebaseAuth.instance.currentUser!.uid.toString();
final DocumentSnapshot doc = await users.doc(uid).get();
users.doc(uid).get().then((DocumentSnapshot doc) {
userData = doc.data();
print(doc.data());
});
}
#override
Widget build(BuildContext context) => Scaffold(
body: FutureBuilder(
future: dbFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Container(
child: Text('waiting'),
);
}
if (!snapshot.hasData) {
return Container(
child: Text('error'),
);
}
final data = snapshot.data;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(userData['displayName']),
ElevatedButton(
onPressed: FirebaseAuth.instance.signOut,
child: Text("Log out"))
],
),
);
}),
);
I'm new in Flutter and trying to make an application for managing an academy.
I successfully saved the data at Firestore Cloud, and I can read them with
print(doc.data());
Now I want to build Profile page with those data, so I used Futurebuilder.
But snapshot always has no data.
I read documents as well, but still have no idea.
To get your data from Firebase and display them in your widgets, you have two ways, but you have to choose only one according to your needs.
With FutureBuilder()
This code will call your database and load the info you request at each build and at each setState() (responsible for updating your interface content). It could be useful for some data types, but in your case your redundant Firebase calls could cost you.
CollectionReference users = FirebaseFirestore.instance.collection('Users');
final auth = FirebaseAuth.instance;
late final uid = auth.currentUser!.uid;
#override
Widget build(BuildContext context) => Scaffold(
body: FutureBuilder(
future: users.doc(uid).get(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Container(
child: Text('waiting'),
);
}
if (!snapshot.hasData) {
return Container(
child: Text('error'),
);
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(snapshot.data!['some_data']),
Text(auth.currentUser?.displayName ?? 'user have no name'),
ElevatedButton(
onPressed: auth.signOut,
child: Text("Log out"))
],
),
);
}),
);
In the initState()
When using initState(), the code inside is called only once. To refresh the content, you will have to call getData() manually (in a setState() for example)
CollectionReference users = FirebaseFirestore.instance.collection('Users');
final auth = FirebaseAuth.instance;
late final uid = auth.currentUser!.uid;
String? someData;
#override
void initState() {
getData();
super.initState();
}
Future<void> getData() async {
users.doc(uid).get().then((doc) {
someData = doc['some_data'];
});
}
#override
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(someData ?? 'no data'),
Text(auth.currentUser?.displayName ?? 'user have no name'),
ElevatedButton(
onPressed: auth.signOut,
child: Text("Log out"))
],
),
),
);
Finally, if your users authenticate, you can use auth.currentUser?.displayName and auth.currentUser!.updateDisplayName('new name') to simply get and change your users' names.

onTap method for Flutter to open longitude and latitude stored in Firestore

I am trying to create a search engine for electoral sections, once it finds the electoral
section by clicking on the item it should send me to a longitude and latitude that I have stored
in firestore and display it on Google maps as markers with flutter, but I cannot create the
method, what will be the most efficient way to do this?
class SearchPage extends StatefulWidget {
#override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
TextEditingController textEditingController = TextEditingController();
final database = Firestore.instance;
String searchString;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(30.0),
child: Container(
child: TextField(
onChanged: (val) {
setState(() {
searchString = val.toLowerCase();
});
},
controller: textEditingController,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: () => textEditingController.clear()),
hintText: 'Buscar seccion',
hintStyle: TextStyle(
fontFamily: 'Antra', color: Colors.blueGrey)),
),
),
),
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: (searchString == null || searchString.trim() == ' ')
? Firestore.instance.collection('secciones').snapshots()
: Firestore.instance
.collection('secciones')
.where('searchIndex', arrayContains: searchString)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('We got an error ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Cargando');
case ConnectionState.none:
return Text('Error de conexion');
case ConnectionState.done:
return Text('We are done!');
default:
return new ListView(
children: snapshot.data.documents
.map((DocumentSnapshot document) {
return new ListTile(
title: Text(document['estado']),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) {
return MapsScreen(
);
}),
);
});
}).toList());
}
},
),
)
],
),
)
],
));
}
}
This is the screen where you should send the position stored in firestore,
but I can't find out how to do it and I took the method from a video
tutorial in which they taught you how to show and store your current
location in Google maps.
class MapsScreen extends StatefulWidget{
final String partyNumber;
final String userId;
const MapsScreen({Key key, this.userId, this.partyNumber}) : super(key: key);
#override
_MapsScreenState createState() => _MapsScreenState();
}
class _MapsScreenState extends State<MapsScreen>{
GoogleMapController _mapController;
Location _location = Location();
StreamSubscription<LocationData> subscription;
#override
void initState(){
super.initState();
_initLocation();
}
_initLocation() async{
var _serviceEnabled = await _location.serviceEnabled();
if(!_serviceEnabled) {
_serviceEnabled = await _location.requestService();
if(!_serviceEnabled){
return;
}
}
var _permissionGranted = await _location.hasPermission();
if(_permissionGranted == PermissionStatus.DENIED){
_permissionGranted = await _location.requestPermission();
if(_permissionGranted != PermissionStatus.GRANTED){
print("Sin permisos de GPS");
return;
}
}
subscription = _location.onLocationChanged().listen((LocationData event) {
if(_mapController != null){
_mapController.animateCamera(
CameraUpdate.newLatLng(
LatLng(event.latitude, event.longitude),
),
);
}
Firestore.instance
.collection('seccion')
.document(widget.partyNumber)
.collection('people')
.document(widget.userId)
.setData({
'lat': event.latitude,
'lng': event.longitude,
});
print("${event.latitude}, ${event.longitude}");
});
}
#override
void dispose(){
if(subscription != null){
subscription.cancel();
}
Firestore.instance
.collection('seccion')
.document(widget.partyNumber)
.collection('people')
.document(widget.userId)
.delete();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Instituto Nacional Electoral"),
),
body: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(16.879860202903764, -99.9013661857768),
zoom: 15,
),
zoomGesturesEnabled: true,
myLocationEnabled: true,
myLocationButtonEnabled: true,
onMapCreated: (controller) => _mapController = controller,
),
);
}
}
I am not quite sure what exactly you are trying to accomplish.
I initially thought you had latitudes and longitudes stored somewhere in Firebase and wanted to display the marker in those locations.
I you wanted to do that, you would need to get the location data from Firebase and pass it into the GoogleMap. I am not familiar with the widget itself, but from the documentation as you can see here: https://github.com/flutter/plugins/blob/f3024731b090659edaa92d01416549c690f65678/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart#L112
the widget accepts a Set of Markers.
If you did a little in the repository you can see how to build a Marker. And then you can construct one or more from the location data in Firebase and pass them to the GoogleMap widget.
If that is what you want to accomplish. The code you posted saves the current user location to Firebase, so I am unsure what exactly your goal is.

How to fix issue with firestore collection snapshot

I'm trying to order my messages collection from Firebase from newest to oldest. But my messages display randomly once after the last and once before the last.
Despite my final who order my collection by reverse
I've tried to find any subject to orders by realtime but I can't find anything about this on Flutter and Firestore. (Maybe I'm blind or I'm not looking well)
class MessagesStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _fireStore.collection('messages').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final messages = snapshot.data.documents.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in messages) {
final messageSender = message.data['sender'];
final messageText = message.data['text'];
final currentUser = loggedInUser.email;
final messageBubble = MessageBubble(
sender: messageSender,
text: messageText,
isMe: currentUser == messageSender,
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
children: messageBubbles,
),
);
},
);
}
}
I add my data into this widget :
FlatButton(
onPressed: () {
messageTextController.clear();
_fireStore.collection('messages').add(
{'text': messageText, 'sender': loggedInUser.email});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
while adding data you can add a timestamp /server timestamp to your message doc and then use it to arrange your data example "timestamp": Timestamp.now(), then query your data .orderby('timestamp' ,descending :true)

Resources