Why is there an error with snapshot.data.length? - flutter

I am trying to parse data from an API. For that, I am using FutureBuilder to list all the parsed data in a ListView.
I've performed a check for nullity of snapshot.data but I keep on getting this error in the segment snapshot.data.length, it says, The property 'length' can't be unconditionally accessed because the receiver can be 'null'. Try making the access conditional (using '?.') or adding a null check to the target ('!').
I've a similar error in the snapshot.data[i] section, which says The method '[]' can't be unconditionally invoked because the receiver can be 'null'. Try making the call conditional (using '?.') or adding a null check to the target ('!').
Here is my code's section of the same:
body: Container(
child: FutureBuilder(
future: getData('hello'),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Text("Loading"),
);
}else{
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, i) {
return ListTile(
title: snapshot.data[i].partOfSpeech,
);
});
}
},
),
),
Here's getData(String s):
Future<List> getData(String s) async {
var response = await http
.get(Uri.https('api.dictionaryapi.dev', 'api/v2/entries/en_US/' + s));
var jsonData = jsonDecode(response.body)[0];
List<Data> data = [];
for (var x in jsonData["meanings"]) {
String definition = x["definitions"][0]["definition"];
Data d = Data(x["partOfSpeech"], definition);
data.add(d);
}
return data;
}

if u are using a new version of flutter (2.2.0 or above). first try adding a null check to the target ('!'). because of the null safety feature.
body: Container(
child: FutureBuilder(
future: getData('hello'),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Text("Loading"),
);
}else{
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, i) {
return ListTile(
title: snapshot.data[i].partOfSpeech,
);
});
}
},
),
),
then try specifying the FutureBuilder type to a List of Data type
body: Container(
child: FutureBuilder<List<Data>>(
future: getData('hello'),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Text("Loading"),
);
}else{
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, i) {
return ListTile(
title: snapshot.data[i].partOfSpeech,
);
});
}
},
),
),

In continuation to this answer,
I found the solution to my problem. Apparently getData was not returning a List as intended. Instead, it was returning an Object.
Typecasting the Object to List solved the problem.
Here's the corrected code:
body: Container(
child: FutureBuilder(
future: getData('hello'),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Text("Loading"),
);
}else{
//typecasting Object to List
var data = (snapshot.data as List<Data>).toList();
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, i) {
return ListTile(
title: data[i].partOfSpeech,
);
});
}
},
),
),

Put 'AsyncSnapshot' before snapshot in the builder parameter.
builder: (context, AsyncSnapshot snapshot)

Since you are checking that snapshot.data is not null you can do the following to fix it.
body: Container(
child: FutureBuilder(
future: getData('hello'),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Text("Loading"),
);
} else{
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, i) {
return ListTile(
title: snapshot.data[i]!.partOfSpeech,
);
});
}
},
),
),

What you need to look at is the result of getData('hello')
Apparently, it does not return something that has a length property.

Related

Fetch data from Firestore Document using StreamBuilder

In my Flutter app, I'm trying to get the data from a Document in Firestore. Here's the data I want to get :
Firestore document's data
I need to fetch that url from the Document. So far, when I needed to fetch data from a collection, I used a Streambuilder. But here I need to fetch data from a document, so I get this error message :
late Stream<DocumentSnapshot<Map<String, dynamic>>>? personnalData =
FirebaseFirestore.instance
.collection('Decembre')
.doc(uid)
.collection('Docs')
.doc('test')
.snapshots();
StreamBuilder<QuerySnapshot>(
stream: personnalData, // Error: The argument type 'Stream<DocumentSnapshot<Map<String, dynamic>>>?' can't be assigned to the parameter type 'Stream<QuerySnapshot<Map<String, dynamic>>>?'.
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
return Stack(
children: snapshot.data!.docs
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return PageView.builder(
controller: _controller,
itemCount: 3,
itemBuilder: (context, index) {
return Container(
child: InteractiveViewer(
minScale: 0.1,
maxScale: 4.0,
child: Image.network(
// FETCH URL FROM DOCUMENT 'TEST'
width:
MediaQuery.of(context)
.size
.width,
fit: BoxFit.cover,
loadingBuilder: (context,
child,
loadingProgress) {
if (loadingProgress ==
null) {
return child;
} else {
return Center(
child:
CircularProgressIndicator(),
);
),
);
}),
),
],
),
),
);
},
child: Text('Open'));
})
.toList()
.cast(),
);
},
),
Any suggestions ?
I found the solution !
The problem was that I was trying to fetch data from a DocumentSnapshot using a StreamBuilder<QuerySnapshot> instead of StreamBuilder<DocumentSnapshot>
Here's how I solved it :
late Stream<DocumentSnapshot<Map<String, dynamic>>> personnalData =
FirebaseFirestore.instance
.collection('Decembre')
.doc(uid)
.collection('Docs')
.doc('test')
.snapshots();
StreamBuilder<DocumentSnapshot>(
stream: personnalData,
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!['url']);
}
return CircularProgressIndicator();
}),
This code is working well for me. I hope that will help somebody !
Maybe what you need is just to specify the type of the QuerySnapshot:
late Stream<DocumentSnapshot<Map<String, dynamic>>> personnalData = // like this FirebaseFirestore.instance
.collection('Decembre')
.doc(uid)
.collection('Docs')
.doc('test')
.snapshots();
because the snapshots() is a method that returns Stream<DocumentSnapshot<Map<String, dynamic>>>, and setting only Stream<DocumentSnapshot> will be considered as a different type, which throws the error.
maybe you can try
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: personnalData,
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
return Stack(
children: snapshot.data!.docs
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return PageView.builder(
controller: _controller,
itemCount: 3,
itemBuilder: (context, index) {
return Container(
child: InteractiveViewer(
minScale: 0.1,
maxScale: 4.0,
child: Image.network(
// FETCH URL FROM DOCUMENT 'TEST'
width:
MediaQuery.of(context)
.size
.width,
fit: BoxFit.cover,
loadingBuilder: (context,
child,
loadingProgress) {
if (loadingProgress ==
null) {
return child;
} else {
return Center(
child:
CircularProgressIndicator(),
);
),
);
}),
),
],
),
),
);
},
child: Text('Open'));
})
.toList()
.cast(),
);
},
),
in streamBuilder you can add <DocumentSnapshot<Map<String, dynamic>>> same as you create stream.
Refer the following example:
StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance.collection('Decembre').doc(uid).collection('Docs').doc('test').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot doc = snapshot.data!.docs[index];
return Column(
children:[
Text(doc['url'])
);
});
} else {
return Text("No data");
}
},
)

Is there any way to make it -> snapshot.data[index].variable in flutter?

In flutter if i wanted to print the values using FutureBuilder
I have to write this 3 line individually
snapshot.data[index].courseName ,
snapshot.data[index].coursePrice,
snapshot.data[index].aboutCourse
So, Is there any way to make a list like this -> fieldItem = ['courseName' , 'coursePrice' , 'aboutCourse'] and then pass fieldItems after the .data[index].{our varibles}
**snapshot.data[index].fieldItems**
varibles will be passed through the fieldItem.
Sorry for my bad english
body: FutureBuilder(
future: _loadData(),
builder: (BuildContext ctx, AsyncSnapshot<List> snapshot) {
if(snapshot.hasData) {
// You can make variable here
// final _data = snapshot.data;
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, index) {
// Or as in your case
final _data = snapshot.data![index];
return Card(
margin: const EdgeInsets.all(10),
// render list item
child: ListTile(
contentPadding: const EdgeInsets.all(10),
title: Text(_data['title']),
subtitle: Text(snapshot.data![index]['body']),
),
),
}
);
} else {
return const Center(child: CircularProgressIndicator());
}
}
);
Refer 11th line, if it helps, upvote.

Error: the operation '[]' is not defined for the type 'object'

I am using Null -Safety then I keep getting this error, anywhere I user snapshot in my code
here is the error
here is my code
StreamBuilder(
stream: firestore
.collection('interest')
.doc('${auth.currentUser?.uid}')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!['Interest'].length ,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(top: 12.0),
child: bottomCardList(
'assets/1 (6).jpeg',
snapshot.data!['Interest'][index]
.toString(),
),
);
});
}),
Thanks
i solve this by using the type
StreamBuilder<DocumentSnapshot<Map>>(
stream: firestore
.collection('interest')
.doc('${auth.currentUser?.uid}')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!['Interest'].length ,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(top: 12.0),
child: bottomCardList(
'assets/1 (6).jpeg',
snapshot.data!['Interest'][index]
.toString(),
),
);
});
}),
There are a few solutions:
Provide a type to your StreamBuilder:
StreamBuilder<DocumentSnapshot<Map>> (...)
Provide a type to the second parameter of your builder:
builder: (context, AsyncSnapshot<Map> snapshot)
Use as to downcast the Object to Map
(snapshot.data as Map)['key']

Flutter query (where + order by) from firebase firestore returns error

My queries from flutter app return error. any ideas why?
Query user = FirebaseFirestore.instance.collection('users').where('approvedStatus', isEqualTo: true).orderBy('name');
How i show data from query
Container(
child: StreamBuilder(
stream: user.snapshots(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasError) {
return Text('Snapshot return error');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading ..."); // CircularProgressIndicator();
}
return Container(
height: MediaQuery.of(context).size.height,
child: ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, i) {
return Card(
child: ListTile(
title: Text(snapshot.data.docs[i]['name'].toString()),
),
);
},
),
);
},
),
),
The result i got is snapshot return error
Nevermind guys. I found the problem after printing snapshot.error
[cloud_firestore/failed-precondition] The query requires an index.
I just follow the link provided and it solve the problem . Thanks

Flutter: When the snapshot is empty the widget disappears

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 .....