Home > Net >  Bad state: Snapshot has neither data nor error in flutter when using StreamBuilder and Firestore
Bad state: Snapshot has neither data nor error in flutter when using StreamBuilder and Firestore

Time:03-08

I'm adding data from Firestore to a Stream from StreamBuilder, but I'm getting the following error:

Exception has occurred. StateError (Bad state: Snapshot has neither data nor error

My code.

class Home extends StatefulWidget {
  const Home({Key? key}) : super(key: key);

  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  AppState? estado;

  static String? userID = FirebaseAuth.instance.currentUser?.uid;
  static final userColeccion = FirebaseFirestore.instance.collection("users");

  var groupfav = ' ';
  Stream<QuerySnapshot>? taskGroup;

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

  void getGroupFavData() async {
    var groupFavData = await userColeccion.doc("$userID").get();
    var groupfav = groupFavData.data()!['groupfav'];

    taskGroup = FirebaseFirestore.instance
        .collection("groups")
        .doc(groupfav) // pass the obtained value
        .collection("task")
        .snapshots();
  }

  @override
  Widget build(BuildContext context) {
    estado = Provider.of<AppState>(context, listen: true);
    return Scaffold(
      appBar: AppBar(
        title: const Text("Home"),
        automaticallyImplyLeading: false,
      ),
      body: StreamBuilder(
        stream: taskGroup,
        builder: (
          BuildContext context,
          AsyncSnapshot<QuerySnapshot> snapshot,
        ) {
          if (snapshot.hasError) {
            return const Text("error");
          }
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Text("Loading");
          }
          var data = snapshot.requireData;

          return ListView.builder(
            itemCount: data.size,
            itemBuilder: (context, index) {
              return Card(
                child: ListTile(
                  title: Text("${data.docs[index]['titulo']}"),
                  subtitle: Text("${data.docs[index]['contenido']}"),
                  onTap: () {},
                  trailing: IconButton(
                    icon: const Icon(Icons.delete),
                    color: Colors.red[200],
                    onPressed: () {},
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

CodePudding user response:

Ok, looking at your issue, I see that 1) you need to get the data of the document BEFORE you start listening on that document, which is normal, so you want to do a call first to the collection, get the document, then listen on the document's collection called task, which makes sense. Your issue is still an asynchronous issue. The app is rebuilding on a stream that still hasn't arrived; you have to fix the sequence of things.

You then need to switch things up a bit and do the following:

Option #1: a) Use a FutureBuilder: this will allow you to make the async call to get the document name based on the user Id b) After you get the document associated to that user, you want to listen on the stream produced by the collection called tasks in that document. There is where then you can hook up the StreamBuilder.

Option #2: a) Keep things the way you have, but do a listen on the taskGroup snapshots; but keep rebuilding the list as the values arrive on that collection.

Those are my suggestions.

Here's some brief code on option 1:


// .. in your Scaffold's body:

Scaffold(
 body: FutureBuilder( // the future builder fetches the initial data
   future: userColeccion.doc("$userID").get(),
   builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {

     if (snapshot.hasData) {
       var groupfav = snapshot.data()!['groupfav'];

       // then once the 'groupfav' has arrived, 
       // start listening on the taskGroup

       taskGroup = FirebaseFirestore.instance
        .collection("groups")
        .doc(groupfav) // pass the obtained value
        .collection("task")
        .snapshots();

       return StreamBuilder(
           stream: taskGroup,
           builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
          // the rest of your code

       });
     }

     return CircularProgressIndicator();
   }
)

)

Option 2 would be something like:

List<Task> userTasks = [];

void getGroupFavData() async {
    var groupFavData = await userColeccion.doc("$userID").get();
    var groupfav = groupFavData.data()!['groupfav'];

    taskGroup = FirebaseFirestore.instance
        .collection("groups")
        .doc(groupfav) // pass the obtained value
        .collection("task")
        .snapshots().listen((snapshot) {
          // here populate a list of your tasks
          // and trigger a widget rebuild once you've grabbed the values
          // and display it as a list on the UI

          setState(() {

            userTasks = snapshot.docs.map((d) => Task.fromJson(d.data())).toList();
          });
 
       });
  }

And in your Scaffold, you can have a ListView just rendering the items on that task list, like:

ListView.builder(
  itemCount: userTasks.length,
  itemBuilder: (context, index) {
     // render your tasks here
  })

Here's a enter image description here

Hope those pointers take you somewhere.

  • Related