How to Get Array Field Values From Firestore - firebase

I have a Flutter app that uses Firestore to store user data. I need help with retrieving the values stored in the 'friends' array. The image below shows the structure of my Firestore. As you can see, the 'friends' field is an array with two values: '123456' and '789123'.
I want to store these values in my variable called friendsList and I try to do this in getFriendsList(). To test and see if the 'friends' array values were stored in the friendsList variable, I use a print statement at the end of getFriendsList() to print the value of friendsList. But when I check my Console, Instance of 'Future<dynamic>' is printed and not the values of the 'friends' field.
How can I assign the values of the 'friends' array field from Firestore into my friendsList variable?
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:mood/components/nav_drawer.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
FirebaseAuth auth = FirebaseAuth.instance;
User currentUser;
String currentUserUID;
Future<dynamic> friendsList;
class LandingScreen extends StatefulWidget {
static const String id = 'landing_screen';
#override
_LandingScreenState createState() => _LandingScreenState();
}
class _LandingScreenState extends State<LandingScreen> {
final _auth = FirebaseAuth.instance;
#override
void initState() {
super.initState();
getUserData();
}
void getUserData() {
getCurrentUser();
getCurrentUserUID();
getFriendsList();
}
void getCurrentUser() {
final currentUser = _auth.currentUser;
}
void getCurrentUserUID() {
currentUserUID = auth.currentUser.uid;
}
void getFriendsList() {
friendsList = FirebaseFirestore.instance
.collection("Users")
.doc(currentUserUID)
.get()
.then((value) {
return value.data()["friends"];
});
print(friendsList);
}

In the then callback, just assign your list to friendsList and change your friendsList to List<dynamic> type
FirebaseFirestore.instance
.collection("Users")
.doc(currentUserUID)
.get()
.then((value) {
friendsList = value.data()["friends"];
print(friendsList);
});
According to your comment for async await syntax,
final value = await FirebaseFirestore.instance
.collection("Users")
.doc(currentUserUID)
.get();
friendsList = value.data()["friends"];

Related

firebase flutter - Receive null till reload emulator works fine

Is there anyway to avoid Streambuilder or Futurebuilder for query data from firebase. I have tried to use Future async but error said received null till I reload the emulator after that it works fine.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class infoStudentCard extends StatefulWidget {
infoStudentCard();
#override
_infoStudentCardState createState() => _infoStudentCardState();
}
class _infoStudentCardState extends State<infoStudentCard> {
String userId = (FirebaseAuth.instance.currentUser).uid;
String _sname;
String _slastname;
String _semail;
String _sid;
String _sclass;
Future data;
#override
void initState() {
super.initState();
data = getDataFromDb();
}
Future<void> getDataFromDb() async{
FirebaseFirestore.instance
.collection("student")
.doc(userId)
.get()
.then((DocumentSnapshot documentSnapshot) {
this._sname = documentSnapshot.get('sname');
});
}
#override
Widget build(BuildContext context) {
return Text(_sname);
}
}
received null till I reload the emulator after that it works fine.
Everytime you change something, call setState to see the result:
Future<void> getDataFromDb() async{
FirebaseFirestore.instance
.collection("student")
.doc(userId)
.get()
.then((DocumentSnapshot documentSnapshot) {
setState(() {
this._sname = documentSnapshot.get('sname');
});
});
}
Other, inside the initState, you are calling data = getDataFromDb but, your function return nothing. Just add:
#override
void initState() {
super.initState();
getDataFromDb();
}
or add a value to your data returning a value from function
late Future<DocumentSnapshot> data;
#override
void initState() {
super.initState();
data = getDataFromDb();
}
Future<DocumentSnapshot > getDataFromDb() async{
return FirebaseFirestore.instance
.collection("student")
.doc(userId)
.get()
.then((DocumentSnapshot documentSnapshot) {
setState(() {
this._sname = documentSnapshot.get('sname');
});
});
}

Flutter Firestore doc get returning null

I am trying to get a document from a Firestore collection using the following code:
firebase_service.dart:
class FirebaseService {
final firestoreInstance = FirebaseFirestore.instance;
final FirebaseAuth auth = FirebaseAuth.instance;
Map<String, dynamic> getProfile(String uid) {
firestoreInstance.collection("Artists").doc(uid).get().then((value) {
return (value.data());
});
}
}
home_view.dart:
Map<String, dynamic> profile =
firebaseService.getProfile(auth.currentUser.uid);
When stepping through the code the profile variable is null in home_view.dart, but value.data() in firebase_service.dart contains a map. Is there a reason why this value isn't being returned in home_view.dart?
Your code needs a few edits, as the getProfile function is async.
class FirebaseService {
final firestoreInstance = FirebaseFirestore.instance;
final FirebaseAuth auth = FirebaseAuth.instance;
// set the return type to Future<Map<String, dynamic>>
Future<Map<String, dynamic>> getProfile(String uid) async { // insert async here
/// insert a return and await here
return await firestoreInstance.collection("Artists").doc(uid).get().then((value) =>
return value.data(); // the brackets here aren't needed, so you can remove them
});
}
}
Then finally in home_view.dart
// insert await here:
Map<String, dynamic> profile = await
firebaseService.getProfile(auth.currentUser.uid);
If you plan to use the getProfile function I suggest you to use a FutureBuilder.
In you home_view.dart's build function write this:
return FutureBuilder(
future: firebaseService.getProfile(auth.currentUser.uid),
builder: (context, snapshot){
if (!snapshot.hasData){
return Center(child: CircularProgressIndicator(),);
}
final Map<String, dynamic> profile = snapshot.data.data();
return YourWidgets();
});
And now you don't need to write:
Map<String, dynamic> profile = await
firebaseService.getProfile(auth.currentUser.uid);
This is an async operation and you have to await for its value.
For reference, you can take a look here at documentation of how propper authentication and CRUD operations made in Firebase with flutter.

Using Flutter ChangeNotifierProvider for authentication

I am using ChangeNotifierProvider to handle app state for my flutter app.
My main.dart file
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_app/services/auth.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<AuthService>(
create: (context) => AuthService(), // initializes auth.dart file here
child: MaterialApp(
initialRoute: '/',
onGenerateRoute: RouteGenerator.generateRoute,
debugShowCheckedModeBanner: false,
title: '...',
home: WelcomeScreen(),
));
}
}
I am trying to change the value of the uid field here in auth.dart
auth.dart file
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class AuthService with ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
String uid = ""; //initial value of uid
UserM _userFromFirebaseUser(User user) {
return user != null ? UserM(uid: user.uid) : null;
}
Stream<UserM> get user {
return null;
}
Future signInWithEmail(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
User user = result.user;
uid = user.uid; //trying to change uid here
print('user id: $uid'); //new value is printed here
notifyListeners();
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
uid changes but then when i try to get the new value in another file, i still get the old value which is the empty string declared on top.
This is how i am trying to access it
final auth = Provider.of<AuthService>(context, listen: true).uid;
print(auth);
What am I doing wrong please?
I don't get why there is a need to use Provider to get the uid of a user in Firebase. You can get the uid synchronously by doing currentUser.uid.
Here is an example:
print(FirebaseAuth.instance.currentUser.uid);

Can't read List from firebase, always returning null on snapshot.data

I am trying to read a list from the db and use it on a listview.builder() but before I can do that I need to have data. But the thing is that I DO have data. I have tried this with a String and Int and it works perfectly.
I am reading and setting the users' data as a list to the db. You will notice that I do have a list in the home called 'userDoc', well I needed a way to insert the client info as a list so I thought of doing it like this(if there is a better way please share), but the thing is that it does save it the db, but whenever I refresh the app all the content is gone off-screen but still saved in the db, and when I actually enter new data after refresh it deletes all the old data and replaces it with a new list in the db, which is not what I want(it's suppose to add on to the list with the already existing data).
I am trying to create a simple to-do list by the way. In short, I am having difficulties reading and writing a list to the db, and for some reason, it's returning null when I actually have data in the db. Please tell me if you need more precise information. Thank you.
Home
import "package:flutter/material.dart";
import "package:innovative_world/services/auth_service.dart";
import "package:innovative_world/models/list_model.dart";
import 'package:innovative_world/services/database_service.dart';
import "package:innovative_world/shared/decoration.dart";
import 'package:innovative_world/shared/loading.dart';
import 'package:provider/provider.dart';
import "package:innovative_world/models/user_model.dart";
import "package:innovative_world/models/list_model.dart";
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
String _text;
final _formKey = GlobalKey<FormState>();
List<String> userDoc = [];
#override
Widget build(BuildContext context) {
final user = Provider.of<UserId>(context);
return StreamBuilder<UserList>(
stream: DatabaseService(uid: user.uid).userListStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserList userList = snapshot.data;
return Scaffold(
// App Bar
body: // I cut off some irrelevant code
RaisedButton( // Submit button
onPressed: () async {
if (_formKey.currentState.validate()) {
userDoc.add(_text);
_formKey.currentState.reset();
await DatabaseService(uid: user.uid)
.setUserData(userDoc);
}
},
ListView.builder(
shrinkWrap: true,
itemCount: userDoc.length, // snapshot.data.list.length will give Error null
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(userDoc[index]),
),
);
},
),
],
),
),
);
} else {
return Loading();
}
});
}
}
Database service
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import "package:innovative_world/models/list_model.dart";
class DatabaseService {
// Collection Reference
CollectionReference listCollection = Firestore.instance.collection("list");
// Get current users id
final String uid;
DatabaseService({this.uid});
// Set data to firestore db
Future setUserData(List<String> list) async {
return await listCollection.document(uid).setData({
"list": list
});
}
// UserList snapshot
UserList _userListFromSnapshot(DocumentSnapshot snapshot) {
return UserList(
uid: uid,
list: snapshot.data["list"]
);
}
// Stream for user's to do list
Stream<UserList> get userListStream {
return listCollection.document(uid).snapshots()
.map(_userListFromSnapshot);
}
}
Auth Service
// Register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
await DatabaseService(uid: user.uid).setUserData(["Test"]); // Set initial Data as snapshot
return _userFromFirebaseUser(user);
}
catch(e) {
print(e.toString());
return null;
}
}
list model
class UserList {
final List<String> list;
final String uid;
UserList({ this.list, this.uid });
}
As far as I've understood, I think you're having a similar problem as I had. First off try seeing what the error is with a small if condition inside your stream builder:
if (snapshot.hasError){
print(snapshot.error.toString);
}
If you get an error that says
type 'List<dynamic>' is not a subtype of type 'List<String>'
then you have to convert the list to String list. To do that, edit your code in _userListFromSnapshot like this:
snapshot.data["list"].cast<String>().toList()
Even if you don't get that exact error you're at least one step forward towards fixing it.

get value from Current User´s Id to display in feed with flutter and firestore

I am new to flutter so bear with me please, I am trying to display cards fill with information that users create. I store that info in firestore and I would like to retrieve it.
My firebase cloud store is organized like this:
posts/currentUserId/usersPost/INFO
but i dont know how to add value in the currentUsersId when interacting with the cloud. so it says currentUserId next to document but I can´t find how to add value.
import 'package:flutter/material.dart';
import 'package:expandable_card/expandable_card.dart';
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'post_details.dart';
class FeedScreen extends StatefulWidget {
static final String id = 'feed_screen';
final String userId;
FeedScreen({this.userId});
#override
_FeedScreenState createState() => _FeedScreenState();
}
class _FeedScreenState extends State<FeedScreen> {
StreamSubscription<QuerySnapshot> subscription;
List<DocumentSnapshot> snapshot;
CollectionReference collectionReference =
Firestore.instance.collection('posts').document(**CurrentUserId**).collection('usersPosts');
passData(DocumentSnapshot snap) {
Navigator.of(context).push(new MaterialPageRoute(
builder: (context) => PostDetails(
snapshot: snap,
)));
}
#override
void initState() {
super.initState();
subscription = collectionReference.snapshots().listen((datasnapshot) {
setState(() {
snapshot = datasnapshot.documents;
});
});
}
to get currentuser Id in firebase you need firebase_auth and then you can get the value of currentuserId as follows
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
final CurrentUserId = user.uid;
//then
Firestore.instance.collection('posts').document(CurrentUserId).collection('usersPosts');

Resources