I am trying to asynchronously pull data from firestore in a streambuilder (all within a list view). The problem with the approach I have now is that nothing displays on the screen. I tried setting state (after i check if the screen is mounted, but to no avail).
I know the correct data is being pulled because it prints to the screen, but for some reason, it just does not display on the screen.
here's a sample of my list view below:
Widget _showFriends() {
return Expanded(
child: StreamBuilder<List<Future<AppUser>>>(
stream: db.friendStream(),
builder: (context, snapshot) {
if(snapshot.hasData) {
print("in here");
final friends = snapshot.data!;
var friend;
String username = "";
String firstName = "";
String photoUrl = "";
return ListView.builder(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
friends[index].then((value) {
friend = value;
username = value.username;
firstName = value.firstName;
//photoUrl = value.photoUrl;
print("friend username -- " + username);
});
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context)
=> IndividualProfile(
sourceType: SourceType.friends,
name: friend.firstName,
userName: friend.username,
aboutMe: friend.aboutMe,
photoUrl: friend.photoUrl,
individualUid: friend.uid,
)
));
},
child: ListTile(
title: Text(username),
subtitle: Text(firstName),
/*leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(photoUrl),
),*/
),
);
},
);
}
else if(snapshot.hasError) {
print(snapshot.error.toString());
return const Center(
child: Text("An error occurred please try again later"),
);
}
else {
return const Center(
child: CircularProgressIndicator(),
);
}
}
)
);
}
Pleas let me know any suggestions. I thoroughly appreciate it!
The problem is that friends[index] is a future. You did not await it before returning GestureDetector.
return GestureDetector(...) would be called before friends[index].then callback. Variable friend would still be null when return GestureDetector(...) is called.
The correct way to wait for it would be with a FutureBuilder.
Try this:
Widget _showFriends() {
return Expanded(
child: StreamBuilder<List<Future<AppUser>>>(
stream: db.friendStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final friends = snapshot.data!;
return ListView.builder(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
// here I am awaiting friends[index]'s future
return FutureBuilder<AppUser>(
future: friends[index],
builder:
(BuildContext context, AsyncSnapshot<AppUser> snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text(
'An error occurred. Please try again later',
),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
final friend = snapshot.data!;
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => IndividualProfile(
sourceType: SourceType.friends,
name: friend.firstName,
userName: friend.username,
aboutMe: friend.aboutMe,
photoUrl: friend.photoUrl,
individualUid: friend.uid,
),
),
);
},
child: ListTile(
title: Text(friend.username),
subtitle: Text(friend.firstName),
// leading: CircleAvatar(
// backgroundImage:
// CachedNetworkImageProvider(friend.photoUrl),
// ),
),
);
},
);
},
);
}
if (snapshot.hasError) {
print(snapshot.error.toString());
return const Center(
child: Text('An error occurred please try again later'),
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
Related
hello I have the list of categories in my firestore i want to fetch them in to a listview it only shows CircularProgressIndicator then after it disapear nothing comes on screen where did get wrong?
Expanded(
child: _appwidget.customeTextFormField(
hint: "Select category",
icon: InkWell(
onTap: () {
showFancyCustomDialog(context,
height: 500,
width: 500,
title: "Add / Select / Delete Category",
widget: FutureBuilder<DocumentSnapshot>(
future: _services.category
.doc("ProductCategories")
.get(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot>
snapshot) {
if (snapshot.hasError) {
return Text('error');
}
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child:
Center(child: CircularProgressIndicator()),
);
}
return ListView.builder(
itemBuilder:
(BuildContext context,
int index) {
return ListTile(
title: Text(snapshot.data!["Categories"]
[index]["name"]),
);
},
itemCount: snapshot.data!['Categories'] ==
null
? 0
: snapshot.data!['Categories'].length,
);
}),
addButton: Container());
},
child: Icon(Icons.list)))),
You forgot to tackle the condition in which you have data.
Just add if(snapshot.hasData) & return your ListView.builder(), because in other conditions you are not sure whether you have received the data or not.
return Expanded(
child: _appwidget.customeTextFormField(
hint: "Select category",
icon: InkWell(
onTap: () {
showFancyCustomDialog(context,
height: 500,
width: 500,
title: "Add / Select / Delete Category",
widget: FutureBuilder<DocumentSnapshot>(
future: _services.category
.doc("ProductCategories")
.get(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot>
snapshot) {
if (snapshot.hasError) {
return Text('error');
}
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child:
Center(child: CircularProgressIndicator()),
);
}
if(snapshot.hasData){
return ListView.builder(
itemBuilder:
(BuildContext context,
int index) {
return ListTile(
title: Text(snapshot.data!["Categories"]
[index]["name"]),
);
},
itemCount: snapshot.data!['Categories'] ==
null
? 0
: snapshot.data!['Categories'].length,
);
}
}),
addButton: Container());
},
child: Icon(Icons.list))));
I want build the layout that provided in the image and I had done that but I want Play all button also able to scroll as the Future list view builder. But I am unable to acheive that. I had used NEstedScrollView() wrap around DefaultTabController Widget
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 10, left: 10, right: 10),
child: Row(
children: [
AddButton(
icon: Icons.play_circle_outline_rounded,
buttonText: 'Play all',
onPressed: () {})
],
),
),
Expanded(
child: FutureBuilder(
future: GetPermissionAndSongs().getAvailableSongs(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
Widget leading;
Widget artworkAvailableWidget(dynamic snapshot) {
return CircleAvatar(
backgroundImage: MemoryImage(snapshot),
);
}
Widget artworkUnavailableWidget(dynamic snapshot) {
return CircleAvatar(
backgroundImage: AssetImage('images/happy_music.png'),
backgroundColor: Colors.grey.shade100,
);
}
Widget artworkLoadingWidget() {
return CircleAvatar(
child: CircularProgressIndicator(),
);
}
FlutterAudioQuery audioQuery = new FlutterAudioQuery();
if (snapshot.data == null) {
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return SongPlayCard(
imagePath: snapshot.data[index].songArtwork,
songPath: snapshot.data[index].songPath,
songName: snapshot.data[index].songName,
onPress: () {
SharedPreferences.getInstance().then((value) {
value.setStringList('last_Played', [
snapshot.data[index].songName,
snapshot.data[index].songPath,
snapshot.data[index].artistName,
snapshot.data[index].songId,
]);
});
changeName(
snapshot.data[index].songName,
snapshot.data[index].artistName,
snapshot.data[index].songPath,
snapshot.data[index].songId,
_leadingArtwork(leading),
);
},
artistName: snapshot.data[index].artistName,
leading: (snapshot.data[index].songArtwork == null)
? FutureBuilder<dynamic>(
future: audioQuery.getArtwork(
type: ResourceType.SONG,
id: snapshot.data[index].songId,
),
builder: (_, snapshot) {
if (snapshot.data == null) {
leading = artworkLoadingWidget();
return leading;
} else {
if (snapshot.data.isEmpty == false) {
print(snapshot.data);
leading = artworkAvailableWidget(
snapshot.data);
return leading;
} else {
leading = artworkUnavailableWidget(
snapshot.data);
return leading;
}
}
},
)
: null,
);
});
}
}),
),
FutureBuilder(
future: getLastPlayed(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
FlutterAudioQuery audioQuery = new FlutterAudioQuery();
if (snapshot.data == null) {
return FloatingPlayer(
songName: '',
songArtist: "",
);
} else {
if (snapshot.data.isEmpty == false) {
String songName =
(updatedName == "") ? snapshot.data[0] : updatedName;
String artistName = (updatedArtistName == "")
? snapshot.data[2]
: updatedArtistName;
String songPath = (updatedSongPath == "")
? snapshot.data[1]
: updatedSongPath;
return FutureBuilder(
future: GetPermissionAndSongs()
.getLastPlayedArtworks(snapshot.data[0]),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return FloatingPlayer(
songName: songName,
iconData: iconData,
songArtist: artistName,
imagePath: snapshot.data[0].songArtwork,
onPlayOrPause: () {
playPause(songPath, null);
},
leading: (updatedLeadingArtwork == null)
? (leadingArtwork == null)
? (snapshot.data[0].songArtwork == null)
? FutureBuilder(
future: audioQuery.getArtwork(
type: ResourceType.SONG,
id: snapshot.data[0].songId),
builder: (_, snapshot) {
if (snapshot.data == null) {
leadingArtwork = CircleAvatar(
child:
CircularProgressIndicator(),
);
return leadingArtwork;
} else {
if (snapshot.data.isEmpty ==
false) {
leadingArtwork = CircleAvatar(
backgroundImage: MemoryImage(
snapshot.data),
);
return leadingArtwork;
} else {
leadingArtwork = CircleAvatar(
backgroundImage: AssetImage(
'images/happy_music.png'),
backgroundColor:
Colors.teal.shade600);
return leadingArtwork;
}
}
},
)
: null
: leadingArtwork
: updatedLeadingArtwork,
);
}
},
);
} else {
return FloatingPlayer(
songName: 'Last played unavailable',
songArtist: "",
);
}
}
},
),
],
),
);
}
Above code doesn't help me. It expand the Future list view builder throughout the whole screen. How I make that scrollable too
One way of solving this would be to use a SingleChildScrollView and to set the physics of the ListView to ScrollPhysics()
SingleChildScrollView(
child: Column(
children: [
PlayAllButton(),
ListView(
//My List View Properties
physics: ScrollPhysics(),
),
),
),
)
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;
}
I am trying to build a search list, the list is working fine but if the result is empty i need to show no data. I tried the following code but the widget holds for a second and then disappear
FutureBuilder(
future: getSearchedProducts(widget.searchString),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: Container(
child: Text('No Data Found.'),
),
);
} else {
return ListView.builder(
shrinkWrap: true,
itemCount: searchResult.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Image.network(searchResult[index].proThumbnail),
title: Text(searchResult[index].proName),
onTap: () {
print(searchResult[index].proName);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ProductPage(prodid: searchResult[index].proId);
}));
},
),
);
});
}
})
Can anyone help me with this.
Thank you in advance.
I just write the code as below and it works.
FutureBuilder(
future: getSearchedProducts(widget.searchString),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print('length of list ${searchResult.length}');
if (searchResult.length==0) {
return Center(
child: Text('No data'),
);
}
else if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
else {
return ListView.builder(
shrinkWrap: true,
itemCount: searchResult.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Image.network(searchResult[index].proThumbnail),
title: Text(searchResult[index].proName),
onTap: () {
print(searchResult[index].proName);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ProductPage(prodid: searchResult[index].proId);
}));
},
),
);
});
}
}),
In your Code in Else part before - return ListView.builder- add the following code.
...
else {
if (searchResult.length == 0)
{
return Center(child: const Text('No Date'));
}
return ListView.builder .....
I want to display values from my API in a PopupMenuItem in PopupMenuButton. I manage to display it but I want it to be dynamic. Currently, I hard-coded the index of each item because it seems that I cannot do looping inside PopupMenuButton.
`Widget _simplePopup4() => PopupMenuButton<int>(
child: Icon(Icons.arrow_drop_down, color: Colors.orangeAccent),
offset: Offset(0, 100),
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[1].title); //index 1
}
}
return CircularProgressIndicator();
})),
),
PopupMenuDivider(),
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[2].title); //index 2
}
}
return CircularProgressIndicator();
})),
),
PopupMenuDivider(),
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[3].title); //index 3
}
}
return CircularProgressIndicator();
})),
),
],
);`
//First attempt which gives error: RenderShrinkWrappingViewport does not support returning intrinsic dimensions.
Widget _simplePopup5() => PopupMenuButton(
itemBuilder: (context) {
var list = List<PopupMenuEntry<Object>>();
list.add(
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.setTitle.length,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data.setTitle[index].title);
});
}
}
return CircularProgressIndicator();
})),
),
);
list.add(
PopupMenuDivider(
height: 10,
),
);
return list;
},
icon: Icon(
Icons.settings,
size: 50,
color: Colors.white,
),
);
//Second attempt which gives error: Another exception was thrown: A RenderFlex overflowed by 85 pixels on the bottom.
Widget _simplePopup5() => PopupMenuButton(
itemBuilder: (context) {
var list = List<PopupMenuEntry<Object>>();
list.add(
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final listTitle = <Widget>[];
for (var i = 0;
i < snapshot.data.setTitle.length;
i++) {
listTitle.add(SingleChildScrollView(
scrollDirection: Axis.vertical,
child: InkWell(
child:
Text(snapshot.data.setTitle[i].title),
)));
}
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Column(children: listTitle);
}
}
return CircularProgressIndicator();
})),
),
);
list.add(
PopupMenuDivider(
height: 10,
),
);
return list;
},
icon: Icon(
Icons.settings,
size: 50,
color: Colors.white,
),
);
From the screenshot, only one item are clearly displayed which is "MR" while the other item (before item "MR") are displayed in half. Meanwhile, the rest of the item (after item "MR") being replaced with error message.
The screenshot of the second attempt error
The cause of the RenderFlex error is because the child Widget expands beyond the parent Widget. What you can do here is fetch the List of PopupMenu items prior to rendering the PopupMenuButton. With this approach, the List items is ready prior to clicking the PopupMenuButton.