I am trying to get several details from the collection "users" (eg, name, surname, nickname) stored in Firestore with the function getUserData() to pass them to other classes through the constructor. The parenthesis in getUserData() is highlighted, pointing to this error message:
1 positional argument expected by 'getUserData', but 0 found.
If I put name as getUserData(name), it says Undefined name 'name'.
What am I missing here?
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData && snapshot.data != null) {
String eMail = snapshot.data!.email.toString();
String Name = getUserData().toString(); // π PROBLEM HERE
return AnApp(forUser1: eMail, forUser2: Name);
} else {
return const LogIn();
}
},
);
}
}
Future<void> getUserData(String name) async {
User? user = FirebaseAuth.instance.currentUser;
if (user != null) {
DocumentSnapshot<Map<String, dynamic>> snapShot = await FirebaseFirestore.instance.collection('users').doc(user.uid).get();
if (snapShot.exists) {
var datA = snapShot.data();
String name = datA?['name'];
String surname = datA?['surname'];
String nickname = datA?['nickname'];
}
}
}
Your getUserData function expects a name parameter, but you're not passing anything in when you call it here:
String Name = getUserData().toString();
So that's what the error message tells you: it expects you to pass a parameter when you call getUserData(), but you are not meeting that expectation.
It looks like getUserData does not actually use the name parameter though, so you can just remove it:
Future<void> getUserData() async {
...
That will solve the first problem in your code, and the initial error will disappear.
Currently your `getUserData` doesn't actually return any value, as you declare it as Future<void>. More likely, you want to actually return the user name from getUserData, which means it'll look like this:
// π Indicates that we return a string value at some point in the future
Future<String> getUserData() async {
User? user = FirebaseAuth.instance.currentUser;
if (user != null) {
DocumentSnapshot<Map<String, dynamic>> snapShot = await FirebaseFirestore.instance.collection('users').doc(user.uid).get();
if (snapShot.exists) {
var datA = snapShot.data();
String name = datA?['name'];
String surname = datA?['surname'];
String nickname = datA?['nickname'];
return nickname; // π For now, let's return just the nickname
}
}
return "Unknown user"; // π default value if no user is signed in or no document exists for them
}
If you want to return more than just a single value, you can stuff the names into a map, or create a custom type for the user data.
Note again that this returns a Future<String>, not just a String. This means that your getUserData().toString() still won't give you the value you want, as it may not be available yet.
To learn how to deal with async values like this in Flutter, I recommend reading Asynchronous programming: futures, async, await. The simplest solution here is to use a FutureBuilder.
The error happens because of two main issues:
Your function getUserData(String name) expects one argument, but youβre calling it with none ,getUserData().
The function is async, so you canβt call it like a regular method inside build() and immediately use its value.
You donβt need to pass anything to getUserData(), since it already reads the current user from Firebase.
Change the function like this:
Future<Map<String, dynamic>?> getUserData() async {
User? user = FirebaseAuth.instance.currentUser;
if (user != null) {
final snapShot = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
if (snapShot.exists) {
return snapShot.data(); // returns {'name': ..., 'surname': ..., 'nickname': ...}
}
}
return null;
}
Now getUserData() returns a Future<Map<String, dynamic>?> that you can await.
2. Use FutureBuilder to wait for Firestore data
Because getUserData() is async, you canβt just call it inside build() β you need to wait for the data to load first.
You can do that with a FutureBuilder:
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasData && snapshot.data != null) {
final eMail = snapshot.data!.email!;
// Fetch Firestore user data
return FutureBuilder<Map<String, dynamic>?>(
future: getUserData(),
builder: (context, userSnapshot) {
if (userSnapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (userSnapshot.hasData && userSnapshot.data != null) {
final data = userSnapshot.data!;
final name = data['name'];
final surname = data['surname'];
final nickname = data['nickname'];
return AnApp(
forUser1: eMail,
forUser2: name,
forUser3: surname,
forUser4: nickname,
);
} else {
return const Center(child: Text('User data not found.'));
}
},
);
} else {
return const LogIn();
}
},
);
}
}
Future<void> getUserData(String name)
to
Future<Map<String, dynamic>?> getUserData()
and fetch it using a FutureBuilder inside your StreamBuilder.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With