I am trying to display items in a ListView using ListView.builder inside a FutureBuilder. My future function for FutureBuilder looks like this:
_fetchListItems() async {
wait() async {
number = await db.getNumber(userId); }
await wait();
List rawFavouriteList = await db.getList(number);
setState((){
rawFavouriteList.forEach((item){
_faouriteList.add(Model.map(item));
}});
return _faouriteList;
}
My FutureBuilder looks like this:
FutureBuilder(
future: _fetchListItems(),
builder:(context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {Container( child: ListView.builder(
itemCount: _faouriteList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text(
'${_faouriteList[index].title}');
}));}})
he following assertion was thrown building FutureBuilder(dirty, state:
I/flutter (24728): _FutureBuilderState#f12a3):
I/flutter (24728): A build function returned null.
I/flutter (24728): The offending widget is: FutureBuilder
I/flutter (24728): Build functions must never return null
Another exception was thrown: A build function returned null.
Note:
I tried to call _fetchListItems() from initState and not use FutureBuilder and that didn't work for me as well.
Here is a link to that case: (Flutter/Dart) Two async methods in initState not working
Please let me know if I should use FutureBuilder or initState to wait for List to load it's data. And how to make it work since none of the methods seem to work for me :(
Your fetch _fetchListItems method is not the problem as far as I can tell.
Your error message is very clear, " a build function returned null". In this case, the method that returns null is the anonymous function passed as the builder argument to the FutureBuilder. You're just missing the return keyword inside the else case because you're just creating the instance of Container but not returning it.
Something like this:
FutureBuilder(
future: _fetchListItems(),
builder:(context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return Container(
child: ListView.builder(
itemCount: _faouriteList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text('${_faouriteList[index].title}');
}
)
);
}
}
)
I don't know if there are any other problems with your code but this should solve the particular error message you are asking about.
It is not necessary to use setState for your case, try this:
Fetch async function
_fetchListItems() async {
number = await db.getNumber(userId);
List rawFavouriteList = await db.getList(number);
List _faouriteList = rawFavouriteList.map((item)=>Model.map(item)).toList();
return _faouriteList;
}
FutureBuilder
FutureBuilder(
future: _fetchListItems(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
Container(
child: ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text('${snapshot.data[index].title}');
}));
}
});
Related
I have a streamBuilder where the "snapshots()" change from Stream<QuerySnapshot<Map<String, dynamic>>> to AsyncSnapshot<Object?>.
This is weird because I have another app, with identical code and works fine.
The problem is, I cannot access "snapshot.data.docs" and all firestore datas.
Why is happening?
my code:
body: StreamBuilder(
//Stream<QuerySnapshot<Map<String, dynamic>>>
stream: firestore.collection('Books').orderBy('name').snapshots(),
//AsyncSnapshot<Object?>
builder: (context, snapshot ) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
{
final document = snapshot.data?; //error here
return ListView.builder(
itemCount: document?.length,
itemBuilder: (context, index) {
return Column(
Declare your StreamBuilder Widget response type as a dynamic, and you should use
snapshot.data.docs without any problem.
body: StreamBuilder<dynamic>(
stream: FirebaseFirestore.instance.collection('Books').orderBy('name').snapshots(),
builder: (context, snapshots) {
if(snapshots.hasData){
print(snapshots.data.docs.toString());
}});
I wrote a code like this:
List ProductsList;
//...
void initState() {
super.initState();
_firestore.collection("Products").snapshots().listen((event) {
setState(() {
ProductsList = event.docs;
});
});
}
//...
StreamBuilder(
stream: ProductsList.where((x) => x["Name"] == SearchText).toList().first, // Filter
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(snapshot.data.docs[index].data()["Name"]),
),
);
},
);
} else {
return Text("No data");
}
}
),
My aim is to fetch the Name value from the Arrays in the list. But when I run the code I get an error like this:
I tested it and the results are in the list. How can I to solve the problem? Thanks for help.
Your ProductsList variable is declared, but not defined. You set the ProductList in the listener in initState, but it only gets set if a change is made in your Products Collection. This is why it can't call the .where on ProductList, because it's null after the initState.
You either give ProductsList a default Value
List ProductList = [];
or you pass the stream into the StreamBuilder and change your logic inside the builder
StreamBuilder(
stream: _firestore.collection("Products").snapshots(),
I have a problem about using FutureBuilder in Flutter.
With FutureBuilder, the page is continuously rebuilt.
I've omitted the detailed code to write the question. If you want to see additional code, please leave a comment.
To stop this, What can I do?
Future<bool> initLikes() async {
var list = await ApiProvider().post('/RoomSalesInfo/Select/Like', jsonEncode(
{
"userID" : GlobalProfile.loggedInUser.userID,
}
));
return true;
} else {
return false;
}
}
//This is the Code that I use in Widget build
FutureBuilder(
future: initLikes(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
//해당 부분은 data를 아직 받아 오지 못했을때 실행되는 부분을 의미한다.
if (snapshot.hasData == false) {
return SizedBox();
}
//error가 발생하게 될 경우 반환하게 되는 부분
// 데이터를 정상적으로 받아오게 되면 다음 부분을 실행하게 되는 것이다.
else {
return Expanded(
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
controller: _scrollController,
itemCount: GlobalProfile.listForMe.length +1,
itemBuilder: (BuildContext context, int index) {
if(index == GlobalProfile.listForMe.length){
return CupertinoActivityIndicator();
}
else
return Column();
}
),
);
}
})
future: initLikes(),
Don't recomputing this. The new invocation will overwrite the old one. Instead use an initState() to compute it just once into a variable that you reference from "future:..".
Scaffold(body: FutureBuilder(
future: fetchTracks(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.hasData)
{
ListView.builder(
scrollDirection: Axis.vertical,
itemExtent: 130.0,
physics: AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: trackes.length,
itemBuilder: (BuildContext context, int index) {
print("test");
return makeCard(snapshot.data[index]);
},
).build(context);
}
else
{
return Center(child: new CircularProgressIndicator());
}
} ));
When i call this Scaffold Future build will call my future function fetchTracks() and get the data in snapshot but it is not entering into itemBuilder function. So futurebuilder return NULL.
Please help me to solve .and Thank you in advance
You're missing a return before ListView.builder. If you don't return it, it won't build it.
FutureBuilder has different snapshot connectionstates which you must handle. Data on the stream is not available until ConnectionState equals done and hasData equals true.
_loadData(context)
{
showModalBottomSheet(
context: context,
builder: (BuildContext bc){
return FutureBuilder(
future: fetchTracks(),
builder: (BuildContext context, AsyncSnapshot<List<MyClass>> snapshot){
if (snapshot.connectionState!=ConnectionState.done)
{
return PleaseWaitWidget();
}
else if(snapshot.hasError)
{
DialogCaller.showErrorDialog(context,"future builder has an error").then((value){});
}
else if (snapshot.connectionState==ConnectionState.done)
{
if(snapshot.hasData){
List<Widget> list = snapshot.data.map((MyClass myClass){
return Card(
child:Wrap(children:<Widget>[
Row(children: [Text(myClass.field1)],),
]));}).toList();
return list;
}
}
});
});
}
DatabaseService databaseService = new DatabaseService();
Stream questionsSnapshot;
so Im using a stream and and a database service to retrieve data (questions and answers of a quiz) to my listView builder
the called function from database service is
getAquizData(String quizId) async{
return await Firestore.instance
.collection("quiz")
.document(quizId)
.collection("questionReponses")
.snapshots();
}
the init state function
#override
void initState() {
databaseService.getAquizData(widget.quizId).then((value){
questionsSnapshot = value;
setState(() {});
});
super.initState();
}
my listViewBuilder
#override
Widget build(BuildContext context) {
return Scaffold(
// an appbar
) ,
body: Container(
child: Column(children: <Widget>[
StreamBuilder(
stream: questionsSnapshot,
builder: (context, snapshot) {
return snapshot.data == null
? Container(child: Text("empty"),) : ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: snapshot.data.documents.length,
itemBuilder: (context,index){
return QuizPlayTile(
questionModel: getQuestionModelFromDatasnapshot(
snapshot.data.documents[index]),
index: index,
);
});
},
)
],)
)
);
}
}
when running it just show the word empty for a second and then it shows questions without answers
[1]: https://i.stack.imgur.com/E4CS4.jpg
We can use stream builder without calling it in initState method. Following code works for me. user quizStreamer in build method.
quizStreamer(){
return StreamBuilder(
stream: Firestore.instance
.collection('quiz')
.document(quizId)
.collection('questionReponses')
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
//loadWidgets method takes snapshot and render defined widgets
return loadWidgets(snapshot);
},
);
}