Flutter streambuilder returns wrong titles - flutter

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

Related

Flutter how to user setState()

One of my routes shows current data which is stored in firestore database. I am calling the function to retrieve the data from firestore in the initState method. The page will show all the data which are retrieved from firestore. It works fine i.e, when the user navigates to this page (quotesPage) it shows the data. But while navigating, for some fraction of seconds it shows error that the local variable which stores the retrieved data is null. It happens for only that fraction of seconds after which it receives the data and shows the data. So when the user navigates to that page, I want to show a progress indicator untill it receive the data. here is my code,
Map<String, dynamic> userInfo = {};
Future<void> getQoutes() async {
var data = await FirebaseFirestore.instance.collection('user').doc(auth.currentUser!.uid).get();
setState(() {
userInfo = data.data() as Map<String, dynamic>;
});
}
#override
void initState() {
getQoutes();
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Some Quotes',
),
backgroundColor: Colors.deepOrange,
),
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card_fun(userInfo['quotes'][index]);
}
)
)
],
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await popUpForm();
},
),
);
I am calling the function getQuotes() from initState() which will store the data to Map variable userInfo. So how to show a progress indicator untill the variable 'userInfo' gets data ?
Can anyone help ?
This is the updated code
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Some Quotes',
),
backgroundColor: Colors.deepOrange,
),
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FutureBuilder<void>(
future: getQoutes(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
if(snapshot.hasError) {
return Text('Error : ${snapshot.error}');
}
return Expanded(
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card_fun(userInfo['quotes'][index]);
}
)
);
default:
return const CircularProgressIndicator();
}
},
)
],
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await popUpForm();
},
),
);
You should try with Future builder or stream builder and here is the example with Future builder
FutureBuilder<String>(
future: getQoutes(), // async work
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return CircularProgressIndicator();
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return Expanded(
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card_fun(userInfo['quotes'][index]);
}
)
);
}
},
)
For more read this article
another approach that might be worth looking at is using a addPostFrameCallback method called from your initState in which you can await the necessary condition and take appropriate action and trigger a setState.

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

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

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

How to use Stack and listview together flutter

I have a ListView.builder inside of FutureBuilder where I get and display DB data to a screen.
Data are displayed one by one, but I'm wondering how to use the Stack widget in order to display them on each other.
https://github.com/geekruchika/FlutterCardSwipe that's how elements have to be positioned(scroll down)
SOLVED:
#override
Widget build(BuildContext context) {
var device = MediaQuery.of(context).size;
return FutureBuilder<List<Task>>(
future: DBHelper().getTasks(),
builder: (BuildContext context, AsyncSnapshot<List<Task>> snapshot) {
if (snapshot.hasData) {
var data = snapshot.data;
return snapshot.data.length > 0
? Stack(
children: data.map((task) {
return Positioned(
child: Dismissible(
key: UniqueKey(),
crossAxisEndOffset: -0.1,
onDismissed: (direction) {
DBHelper().delete(task.id);
},
child: Container(
height: device.height * 585 / 812,
child: AddTask(null, task.name,
'Meeting with directory', null, []),
),
),
);
}).toList(),
)
: Container(
height: device.height * 585 / 812,
child: NoTasksFound(),
);
} else {
return Center(child: CircularProgressIndicator());
}
},
);
}
What you tried is correct except for you had to convert Iterable to a List.
Like:
Widget build(BuildContext context) {
var device = MediaQuery.of(context).size;
return FutureBuilder<List<Task>>(
future: DBHelper().getTasks(),
builder: (BuildContext context, AsyncSnapshot<List<Task>> snapshot) {
if (snapshot.hasData) {
var data = snapshot.data;
return Stack(
children: snapshot.data.map((task) { // map the data
return Positioned(
child: Dismissible(
key: UniqueKey(),
crossAxisEndOffset: -0.2,
background: Container(color: Colors.red),
onDismissed: (direction) {
// DBHelper().delete(task.id);
},
child: Container(
// margin: EdgeInsets.only(bottom: 100, top: 100),
height: device.height * 585 / 812,
child: AddTask(null, 'Description',
'Meeting with directory', null, []),
),
),
);
}).toList(), // convert the mapped iterable to list
);
} else {
return Center(child: CircularProgressIndicator());
}
},
);
}
Hope that helps!