Flutter: How to show a message if snapshot has no data - flutter

I have a simple problem in flutter but I cant quite figure out how to solve it. So here it is. I'm trying to show a message in my app if the snapshot that I'm calling has no data in my firebase database.
I have this code:
return Scaffold(
body: Container (
child: new LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return Column(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.height * 0.020,
),
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
child: Column(
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: db.collection('CONFIRMED HELP BENEFICIARY').where('Respondents_ID', isEqualTo: '${widget.UidUser}').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data.documents
.map((doc) => buildItem(doc))
.toList());
}
else {
return Container(
color: Colors.red,
height: 200,
width: 200,
child: Text("No Data"));
)
],
);
},
),
),
);
Inside my singlescrollview, I have a streambuilder in it. Also an if else. So if the"snapshot.hasdata" I'm showing a list of data and it successfully shows that. But the problem is in the "else". I've been trying to show a container that has a color:red and a text that contains "No Data" but I quite cant figure out how to ## It shows the container for milliseconds then it disappear ##. Please help.

if(!snapshot.hasData){
// still waiting for data to come
return circularProgress();
}
else if(snapshot.hasData && snapshot.data.isEmpty) {
// got data from snapshot but it is empty
return Text("no data");
}
else {
// got data and it is not empty
return ListView(
children: snapshot.data,
);
}
},

This works for me on Flutter 2.10.3 with null safety.
if (!snapshot.hasData) {
// waiting for data
return CircularProgressIndicator();
} else if (snapshot.data?.size == 0) {
// collection has no data
return _noDataWidget();
} else {
return ...;
}

There was a few brackets missing. That caused the problem. I fixed the code for you.
return Scaffold(
body: Container(
child: new LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return Column(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.height * 0.020,
),
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
child: Column(
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: db.collection('CONFIRMED HELP BENEFICIARY')
.where('Respondents_ID', isEqualTo: '${widget.UidUser}')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data.documents
.map((doc) => buildItem(doc))
.toList());
}
else {
return Container(
color: Colors.red,
height: 200,
width: 200,
child: Text("No Data"));
}
}
)
],
),
),
),
],
);
},
),
),
);

if(!snapshot.hasData){
return circularProgress();
}
else if(snapshot.data.docs.isEmpty){
return Text("There is no data here");
//Or you can show any widget you want
}else{
return ListView()
}

Related

Flutter streambuilder returns wrong titles

I recieve ’title’ from firebase in a streambuilder but it gets all titles from all documents. I just want titles from the selected document.
StreamBuilder(
stream: Firestore.instance
.collection(widget.user.uid)
.orderBy('date', descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
return ListView(
shrinkWrap: true,
children: snapshot.data.documents.map((document) {
final current = document.data;
final activities = current["activities"] as List;
List titles =
activities.map((e) => e["title"]).toList();
return Center(
child: Container(
width:
MediaQuery.of(context).size.width / 1.2,
height:
MediaQuery.of(context).size.height / 6,
child: Text("Title: $titles"),
),
);
}).toList(),
);
}
return const Text("no data");
},
)
What am I doing wrong? I just want to display title: lkjn and title:99okkj in a listview.
Just wrap widget creation inside of activitites.map(...) operation and make your widget list flat
Here is the updated version of the build method:
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
shrinkWrap: true,
children: snapshot.data.documents.map((document) {
final current = document.data;
final activities = current["activities"] as List;
return activities.map((e) => Center(
child: Container(
width:
MediaQuery.of(context).size.width / 1.2,
height:
MediaQuery.of(context).size.height / 6,
child: Text("Title: ${e["title"]}"),
),
)
);
}).expand((element) => element).toList(),
));
}

Why StreamBuilder always has no data before hot reload?

I use firestore and streambuilder to read data in a list, when i run the application for the first time i get a message "Unexpected null value" and I realized that "snapshot.hasData" is always false and snapshot.ConnectionState.waiting is always true. But when i restart application with hot reload i can retrieve data.
This is my stream:
Stream<QuerySnapshot> _branchStream = FirebaseFirestore.instance.collection('Companies').doc(company_id).collection("Branch Offices").snapshots();
This is my StreamBuilder
StreamBuilder<QuerySnapshot>(
stream: _branchStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
/* if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}*/
return ListView(
children: snapshot.data!.docs
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Padding(
padding: const EdgeInsets.all(22.0),
child: Card(
elevation: 8,
shadowColor: Colors.blueGrey,
shape: cardShape,
child: Row(
children: [
Expanded(
flex: 2,
child: Padding(
padding: const EdgeInsets.all(22.0),
child: CircleAvatar(
radius: 50,
backgroundImage:
NetworkImage(data['branch_image'],scale: 60),
),
)),
Expanded(
flex: 4,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(22.0),
child: Text(data['branch_name'], style: textBlackTitle, textAlign: TextAlign.center,),
),
Padding(
padding: const EdgeInsets.all(22.0),
child: Text("Ubicación: "+data['branch_address'], style: textGraySubTitle, textAlign: TextAlign.center,),
),
],
)),
Expanded(
flex: 2,
child: IconButton(
// focusColor: Color(color1),
// color: Color(color1),
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => Home(branch_id : data['branch_id'], company_id : company_id, branch_name : data['branch_name'], branch_image : data['branch_image'])));
}, icon: Image.asset("assets/enter.png", fit: BoxFit.contain, height: 100,)))
],
),
),
);
})
.toList()
.cast(),
);
},
)
This is data that I want to get
This is what I get at the first time
This is what I get after hot reload (That I should have from the beginning).
Because your data is null at the beginning, it takes some time to load the data.
You actually already included a check, but commented it out again. Undo it and try again.
/* if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}*/
It takes some time to load snapshot data. For better UX return specific widgets for each state of the snapshot.
Make sure you're using StreamBuilder inside StatefulWidget.
StreamBuilder<QuerySnapshot>(
stream: _branchStream,
builder: (BuildContext context, snapshot) {
if (snapshot.hasError) {
return //error widget
} else {
switch (snapshot.connectionState) {
case ConnectionState.none:
return //some widget
case ConnectionState.waiting:
return CircularProgressIndicator(),
case ConnectionState.active:
return ListView()
case ConnectionState.done:
return //some widget
}
}
);

How to extract snapshot data and save as a global variable in flutter

I don't know whether this is a dumb question or not. Pardon me if it is.
I created a streamBuilder and now I want to extract it's AsyncSnapshot and save as a global variable. Currently I only have access to that snapshot is, inside the streamBuilder widget. But I want that snapshot data to update some widgets outside that streamBuilder widget. I have added a comment to below code:
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: _crudStorage.all(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.active:
case ConnectionState.waiting:
if (snapshot.data == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
final tasksList = snapshot.data as List<Task>; //I want to extract this
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
tasksList.isEmpty
? const RiveBird()
: Expanded(
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: tasksList.length + 1,
itemBuilder: (context, index) {
if (index == tasksList.length) {
return const SizedBox(
height: 85.0,
width: double.infinity,
);
}
final task = tasksList[index];
return Dismissible();
},
),
),
],
);
default:
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}

How do I solve type 'MappedListIterable<DocumentSnapshot, dynamic>' is not a subtype of type 'List<Widget>' errors

I'm using FutureBuilder to Query Firestore for the documentID in one collection then pass the documentID to another collection.The relevant error-causing widget wasFutureBuilder
What could be the issue? I've checked the code I'm not sure maybe its the format.
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
The first Future builder retrieves the documentID in Cart
FutureBuilder( // <<=========FutureBuilder
future: usersref
.document().
collection("Cart")
.getDocuments(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
CircularProgressIndicator();
}
{
return ListView( // <<=== ListView
padding: EdgeInsets.only(
top: 108.0,
bottom: 12.0,
),
children: snapshot
.data.
documents.map((document) { // <<===snapshot map
return FutureBuilder(
future: productsRef
.document(widget.shopname)
.collection("products")
Then It's passed below to another collection reference
.document(document.documentID) //<<===documentID
.get(),
// ignore: missing_return
builder: (context, snapshot) {
if (snapshot.hasError) {
return Scaffold(
body: Center(
child: Text("Error: ${snapshot.error}"),
),
);
}
if (snapshot.connectionState ==
ConnectionState.waiting) {
Center(
child: Text("Waiting...'"),
);
}
if (snapshot.connectionState == ConnectionState.done) {
Map _productMap = document.data.documents(); // <<<======== Map
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 90,
height: 90,
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
From the collection reference, Im querying the data.
child: Image.network(
"${_productMap['images'][0]}",
fit: BoxFit.cover,
),
),
),
],
);
}},
);
}),
);
}
})
],
));
}
}
So i see multiple things that can go wrong. I'm not sure why you're nesting multiple FutureBuilders in a parent FutureBuilder. Scaffold should be on top level, now you have multiple of them.
The error you get is in the ListViews children. It expects widgets but you're mapping it to another FutureBuilder.
snapshot.data.documents.map((document) {
return FutureBuilder(
future: productsRef
.document(widget.shopname)
.collection("products").document(document.documentID)
.get(),
the snapshot has all the info you need so try this:
ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (ctx, i) => Text(snapshot.data.documents[i].documentID) // <-docID
Also, i generally use futurebuilder to check the state of the user, to get the documents, use StreamBuilder

UnimplementedError error in FutureBuilder while displaying inserted data from database

I'm trying to create a Futurebuilder function to call and display all data that inserted in database unfortunately I got this error 'UnimplementedError' and im pretty stock on this any suggestion will be appreciated.
Here in my full code for implementation to display data in been trying to fix my error 'UnimplementedError' in which I'm trying to do is to display inserted in list view not in web view any suggestion will be appreciated.
body: Center(
child: Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<ContactsDao>(
future: _calltheStream(),
builder: (BuildContext context,
AsyncSnapshot<ContactsDao> snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.none) {
return Container(
child: CircularProgressIndicator(),
);
} else {
return StreamBuilder<List<ContactObject>>(
stream: snapshot.data.findallContactsById(),
builder: (context, snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState ==
ConnectionState.none) {
return Container(
child: CircularProgressIndicator(),
);
} else {
if(widget.Contactlist.length != snapshot.data.length){
widget.Contactlist = snapshot.data;
}
if(snapshot.data.length == 0){
return Center(
child: Text('No Data Found'),
);
}
return Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int index) {
return Card(
child: ListTile(
leading: Checkbox(
value: widget.Contactlist[index].isSelect,
onChanged: (bool value) {
setState(() {
widget.Contactlist[index].isSelect = value;
});
},
),
trailing: GestureDetector(
onTap: () {
_selectedDetele(snapshot.data[index].id);
},
child: Icon(Icons.delete),
),
title: Text('${snapshot.data[index].task}',maxLines: 1,),
subtitle: Text('${snapshot.data[index].time}',style: TextStyle(fontSize: 10),),
));
}),
);
}
}); //DATA
} //DATA
}), // DATA
], // DATA
), // DATA
),//DATA
),
Future<ContactsDao> _calltheStream() async { //GET ALL DATA HERE
ContactDatabase contactDatabase = await widget.database;
widget._contactsdao = contactDatabase.contactsDao;
return contactDatabase.contactsDao;
}