2

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'];
    }
  }
}

3 Answers 3

5

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Puf always! thanks much for that! Just checked your answer and this should work: Future<String?> _getUserData() async { final 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 = await datA?['name']; return name; } } return null; } but it says Instance of Future<String?> instead, why?
Ah good catch... that's because there's no return value if the user is null or is the snapshot doesn't exist. You can either add a return "Unknown user"; at the end, or change the return type indeed.
3

The error happens because of two main issues:

  1. Your function getUserData(String name) expects one argument, but you’re calling it with none ,getUserData().

  2. The function is async, so you can’t call it like a regular method inside build() and immediately use its value.

1. Remove the unnecessary parameter

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

In short: Change

Future<void> getUserData(String name)

to

Future<Map<String, dynamic>?> getUserData()

and fetch it using a FutureBuilder inside your StreamBuilder.

Comments

2

The getUserData() function expects to receive a name parameter, but the parameter was never passed to the function, so you can remove it.

Future<void> getUserData(String name);

I noticed you tried to store a void value in a string variable; you can change the function's return value to Map<String, dynamic>

Future<Map<String, dynamic>> getUserData();

snapShot.data(); returns a Map of user data, so you can have the getUserData() function return it directly and receive all the data on the client side:

/// The function's return value has been changed
/// from `void` to returns `Map<String, dynamic>`
Future<Map<String, dynamic>> 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) {
      Map<String, dynamic>? datA = snapShot.data(); 
      return datA ?? {}; // returns Map<String, dynamic>
    }
  }
}

Now that getUserData() returns a data Map, you can retrieve it on the client side like this:

  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();
          Map<String, dynamic>? datA = getUserData(); // retrieving the return value from getUserData()
          return AnApp(forUser1: eMail, forUser2: datA?['name']);
        } else {
          return const LogIn();
        }
      },
    );
  }
}

With these changes, your code will work fine.

2 Comments

Thanks @Vinicius Bruno, I tested your answer and keeps saying in Map<String, dynamic>? datA = getUserData();: A value of type 'Future<Map<String, dynamic>?>' can't be assigned to a variable of type 'Map<String, dynamic>?'., how can I cast that datA in that case?
I'm sorry, I forgot to specify the await to resolve the future, try changing that: Map<String, dynamic>? datA = await getUserData();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.