How put a future inside initState?

Viewed 484

I created a list but I put it my future in a stream builder and it rebuilds when I scroll so I'm trying to create a future method outside in initstate that I'll call in my code.

The problem is I used in the docs(), informations from streamBuilder so I can't access to these, when my code is outside. How can I do ?

This is my code

Future getNewUid() async {
    CollectionReference Users = FirebaseFirestore.instance.collection('Users');
    var response = await Users
        .doc(snapshot.data.docs[index]['uid']) //it doesn't take informations and it's red underlined
        .get();
        return response.data;
  }

  @override
  void initState() {
    var newUid = getNewUid();
    print(newUid);
    super.initState();
  }

And the rest

body: StreamBuilder(
        stream: BusiPosts.orderBy('time', descending: true).snapshots(),
        builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
          if (snapshot.hasError) {
            return Text('Something went wrong');
          }
          if (snapshot.connectionState == ConnectionState.waiting)
          {
            return Center(
                child: CircularProgressIndicator());
          }
              return ListView.builder(
                itemCount: snapshot.data.docs.length,
                itemBuilder: (context, index) {
                  return Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        ListTile(
                            title: FutureBuilder<DocumentSnapshot>(
                              future: newUid,
                                  builder: (BuildContext context,
                                  AsyncSnapshot<DocumentSnapshot> snapshot) {
                                if (snapshot.hasError) {
                                  return Text("Something went wrong");
                                }
                                if (snapshot.hasData &&
                                    !snapshot.data!.exists) {
                                  return Text("Document does not exist");
                                }
                                if (snapshot.connectionState ==
                                    ConnectionState.done) {
                                  Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>;
                                  return GestureDetector(
                                    onTap: () =>
                                        navigateToDetail(data[index]),

I tried to edit with no success :/

    CollectionReference Users = FirebaseFirestore.instance.collection('Users');
    var futureResult = await Users
        .doc(snapshot.data.docs[index]['uid']) //it doesn't take informations and it's red underlined
        .get();
    return futureResult;
  }
  
  void initState() async{
    var futureResult = await myFutureFunction();
    super.initState();
  }
2 Answers

You could just do this:

Future getNewUid() async {
    CollectionReference Users = FirebaseFirestore.instance.collection('Users');
    var response = await Users
        .doc(snapshot.data.docs[index]['uid']) //it doesn't take informations and it's red underlined
        .get();
    newUid = response.data;
  }

@override
  void initState() {
   getNewUid();
   super.initState();
 }

You might want to check how to use asynchronous calls inside the InitState method. Here is a good example on how to use the Future class, like this:

@override
void initState() {
  //you are not allowed to add async modifier to initState
  Future.delayed(Duration.zero,() async {
        //your async 'await' codes goes here
  });
  super.initState();
}

I would also check if this is the correct way to make the query.

var response = await Users
        .doc(snapshot.data.docs[index]['uid']) //it doesn't take informations and it's red underlined
        .get();

In a related question on how to get the user ID, they explain how to properly obtain the user id, and I think you can implement it to your stream builder:

final FirebaseAuth auth = FirebaseAuth.instance;

void inputData() async {
    final FirebaseUser user = await auth.currentUser();
    final uid = user.uid;
    // here you write the codes to input the data into firestore
  }

Also, in the FlutterFire documentation there’s a good example on how to read data in realtime using a StreamBuilder that could help you as well:

Realtime changes

FlutterFire provides support for dealing with realtime changes to collections and documents. A new event is provided on the initial request, and any subsequent changes to collection/document whenever a change occurs (modification, deleted or added).

Both the CollectionReference & DocumentReference provide a snapshots() method which returns a Stream:

Stream collectionStream = FirebaseFirestore.instance.collection('users').snapshots();
Stream documentStream = FirebaseFirestore.instance.collection('users').doc('ABC123').snapshots();

Once returned, you can subscribe to updates via the listen() method. The below example uses a StreamBuilder which helps automatically manage the streams state and disposal of the stream when it's no longer used within your app:

class UserInformation extends StatefulWidget {
  @override
    _UserInformationState createState() => _UserInformationState();
}

class _UserInformationState extends State<UserInformation> {
  final Stream<QuerySnapshot> _usersStream = FirebaseFirestore.instance.collection('users').snapshots();

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
      stream: _usersStream,
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        if (snapshot.hasError) {
          return Text('Something went wrong');
        }

        if (snapshot.connectionState == ConnectionState.waiting) {
          return Text("Loading");
        }

        return ListView(
          children: snapshot.data!.docs.map((DocumentSnapshot document) {
          Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
            return ListTile(
              title: Text(data['full_name']),
              subtitle: Text(data['company']),
            );
          }).toList(),
        );
      },
    );
  }
}
Related