Pratik
Pratik

Reputation: 755

Firestore ConnectionState is in Waiting state forever in flutter app

I am trying to fetch records from the Firestore database and show it in the Flutter app.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: Text('Hello'),
          actions: <Widget>[
            IconButton(icon: Icon(Icons.list), onPressed: _pushSaved)
          ]),
      drawer: _buildDrawer(),
      body: _buildCarpoolsList(),
    );
  }

Widget _buildCarpoolsList() {
    return StreamBuilder<QuerySnapshot>(
        stream: Firestore.instance.collection("carpools").snapshots(),
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return CircularProgressIndicator();
          } else {
            var _carpools = snapshot.data.documents;
            return ListView(
                padding: const EdgeInsets.all(16.0),
                children: _deserializeCarpools(_carpools));
          }
        });
  }

In the _buildCarpoolsList() function the snapshot.ConnectionState is always ConnectionState.waiting, and the function returns CircularProgressBar(). ConnectionState.waiting

I have got a collection carpools in the FireStore database with a few records in it.

I have set the firestore database rules to allow all.

rules_version = '2';
service cloud.firestore {
  match /** {
      allow read, write: if true;
  }
}

Edit: My Firebase authentication works. (If that helps)

What could be missing here?

Upvotes: 11

Views: 3735

Answers (5)

James Yoo
James Yoo

Reputation: 503

If you reviewed the Cloud Firestore on FlutterFire, and there was no issue with the security rules, check whether you use .map() or .forEach() correctly.

For example, the following is working well:

builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {

        List<String> urls = [];
        if (snapshot.hasError) {
          return ...
        }

        if (snapshot.connectionState == ConnectionState.waiting) {
          return ...
        }
        
        **snapshot.data.docs.forEach((QueryDocumentSnapshot document) {
          print(document);
          urls.add(...);
        });**

        return Container( 
          child: GridView.count(
            crossAxisCount: ...,
            childAspectRatio: (...),
            children: List.generate(urls.length, (index) {

However, the below example does nothing, if your widget was supposed to show some images using an item in the urls:

builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {

        List<String> urls = [];
        if (snapshot.hasError) {
          return ...
        }

        if (snapshot.connectionState == ConnectionState.waiting) {
          return ...
        }
        
        
        **snapshot.data.docs.map((QueryDocumentSnapshot document) {
          print(document);
          urls.add(...);
        });**

        return Container( 
          child: GridView.count(
            crossAxisCount: ...,
            childAspectRatio: (...),
            children: List.generate(urls.length, (index) {

While document is printed in both examples, it does not mean that they do the same thing. It is also worth noting that the condition snapshot.connectionState == ConnectionState.waiting is satisfied at least one time during the loading, and it makes you think that you are waiting permanently.

Upvotes: 0

anoop4real
anoop4real

Reputation: 7718

I also had the same issue, I was following the guides from Flutter In focus. The state was always connecting and no data was returned and also no error. This was in iOS and then I ran the app in Android and fortunately in the LogCat, I saw a message permission to listen to query was denied by default the security rules seem to deny everything, you. have to allow it as @MSaudi mentioned. I added this and it worked

   match /{collectionName}/{documentId} {
       allow read, write : if collectionName == "products";
   }

To play with security rules, the below link will help

Firebase security rules

Upvotes: 0

Noldy Nayoan
Noldy Nayoan

Reputation: 127

I came across the same situation. Using cloud_firestore plugin and StreamBuilder :

class DealerWorkPage extends StatelessWidget {
final String wheelId;
DealerWorkPage(this.wheelId);
@override
Widget build(BuildContext context) {
return StreamBuilder(
    stream: Firestore.instance.collection('wheels').document(this.wheelId).snapshots(),

    builder: (context, asyncSnapshot) {
        if(asyncSnapshot.hasError) return Text('Error: ${asyncSnapshot.error}');

        switch (asyncSnapshot.connectionState) {
          case ConnectionState.none: return Center(child: Text('No data'));
          case ConnectionState.waiting: return Center(child: CircularProgressIndicator());
          case ConnectionState.active:
            return Center(child: Text(asyncSnapshot["description"]))
          break;
        }
        return null;
    });
 }
}

This code runs with no error, but only showing CircularProgressIndicator() forever!

I tried another flutter project that has an add clause like :

await _firestore.collection('messages').add({
    'text': messageController.text,
    'from': widget.user.email,
    'date': DateTime.now().toIso8601String().toString(),
  });

And it throws : PlatformException(Error performing setData, PERMISSION_DENIED: Missing or insufficient permission

Now, just like @MSaudi said, this error has something todo with firebase security rules. I then check the database security rules in Firebase Console. Yes I forget to 'extend' the date for database access :

rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {

// This rule allows anyone on the internet to view, edit, and delete
// all data in your Firestore database. It is useful for getting
// started, but it is configured to expire after 30 days because it
// leaves your app open to attackers. At that time, all client
// requests to your Firestore database will be denied.
//
// Make sure to write security rules for your app before that time, or else
// your app will lose access to your Firestore database
match /{document=**} {
  allow read, write: if request.time < timestamp.date(2020, 1, 27);
  }
 }
}

AFTER changing the request.time timestamp.date(2020, 1, 27) to later date, VOILA! my StreamBuilder behaves as expected!. No more waiting forever! I hope this information helps.

Upvotes: 0

MSaudi
MSaudi

Reputation: 4652

You may revise security settings in Firebase console. If you created the database in production mode (not testing), it comes with security rules that might block any connection until you allow so.

Open the Rules tab in the database screen, (JUST TO TEST -> Make allow read, write; as below ) If it works, just consider reading about the security rules and set it appropriately as per your needs.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

Upvotes: 1

Did you add GoogleService-Info.plist in your Runner.xcworkspace , you must open it with Xcode. I always forget to add it in new project :

Upvotes: 0

Related Questions