Flutter : Class 'Future<List<String>>' has no instance getter 'length' - flutter

I have a List of String saved on SharedPreference in Flutter app, I want to call it inside Provider and using it in a widget.
Provider :
get myTeam => getMyTeam();
Future<List<String>> getMyTeam() async {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
final SharedPreferences prefs = await _prefs;
return prefs.getStringList('team');
}
I used it in future builder :
Widget build(BuildContext context) {
return Consumer<GeneralProvider>(
builder: (context, generalProvider, child) {
var items = generalProvider.myTeam;
return FutureBuilder(
future: items,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index].club}'),
);
});
} else {
return Text('bad');
}
});
});
}
I get error : Class 'Future<List<String>>' has no instance getter 'length'
I tied with many solutions in questions here like :
Class 'Future<dynamic>' has no instance getter 'length'
but it wasn't solved

Change itemCount: items.length into itemCount: snapshot.length,
And Future builder to FutureBuilder<List<String>(...etc).
Will look like this in the end:
Widget build(BuildContext context) {
return Consumer<GeneralProvider>(
builder: (context, generalProvider, child) {
var items = generalProvider.myTeam;
return FutureBuilder<List<String>>(
future: items,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.length,
itemBuilder: (context, index) {
return ListTile(
// title: Text('${items[index].club}'),//This will likely throw an error also, because items is a List<String>, there is no method called "club" for Lists.
//Replace it with this to validate:
title: Text(snapshot[index]),//This
);
});
} else {
return Text('bad');
}
});
});
}

Related

Error trying to build a ListView in a Flutter FutureBuilder

I am new to Flutter and building a small app to record my expenses and learn a bit.
I am using Hive to store data. Now I am building a page which targets to show all the previously saved entries. I do this by creating a List with all the data and then trying to use a FutureBuilder to show the data in a ListView.
This is the code so far:
class LogScreen extends StatefulWidget {
const LogScreen({Key? key}) : super(key: key);
#override
_LogScreenState createState() => _LogScreenState();
}
class _LogScreenState extends State<LogScreen> {
get futureEntries => getEntries();
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<Widget>(
future: futureEntries,
builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
if (snapshot.hasData) {
return Container(
child: ListView.builder(
itemCount: futureEntries.length,
itemBuilder: (context, index) {
Entry currentEntry = Hive.box<Entry>('entriesBox').getAt(index);
return ListTile(
title: Text('${currentEntry.description}'),
);
},
),
);
} else {
return CircularProgressIndicator();
}
}
);
}
Future<List> getEntries() async {
List listEntries = await DbHelper().getListEntries();
print(listEntries);
return listEntries;
}
}
I am getting the following error though:
The following _TypeError was thrown building LogScreen(dirty, state: _LogScreenState#75644):
type 'Future<List<dynamic>>' is not a subtype of type 'Future<Widget>?'
The relevant error-causing widget was:
LogScreen file:///home/javier/StudioProjects/finanzas/lib/main.dart:55:14
When the exception was thrown, this was the stack:
#0 _LogScreenState.build (package:finanzas/log_screen.dart:29:17)
Could someone please tell me what I am doing wrong and suggest a solution? I come from Python and am having a though time with all these types :-P
Thanks in advance.
The generic type of FutureBuilder<T>() should correspond to the data type your Future will return, not what the builder is building. In your case you have FutureBuilder<Widget> so it expects a Future<Widget>, but your getEntries returns a Future<List<dynamic>>. So this is what the error is hinting at. Your code should probably look like this:
return FutureBuilder<List<Entry>>(
future: futureEntries,
builder: (BuildContext context, AsyncSnapshot<List<Entry>> snapshot) {
if (snapshot.hasData) {
return Container(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Entry currentEntry = snapshot.data[index];
return ListTile(
title: Text('${currentEntry.description}'),
);
},
),
);
} else {
return CircularProgressIndicator();
}
}
);
Also note that i replaced the references in your ListView.builder from directly referencing your future to using the data inside the snapshot
Alright. After some research, here's the code that got to work:
Widget build(BuildContext context) {
return FutureBuilder<List>(
future: futureEntries,
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (snapshot.hasData) {
return Container(
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
Entry currentEntry = snapshot.data![index];
return ListTile(
title: Text('${currentEntry.description}'),
);
},
),
);
} else {
return CircularProgressIndicator();
}
}
);
}
Future<List> getEntries() async {
List listEntries = await DbHelper().getListEntries();
print(listEntries);
return listEntries;
}
I don't know yet exactly what the exclamation marks after 'data' do, but they did the trick.

Flutter Null Safety Migration - StreamBuilder snapshot - Cannot access length or data through index

i'm trying to migrate to null safety.
I'm currently stuck with StreamBuilder and ListView.builder.
I'm unsure how to adjust the code, that the itemCount and an element from the snapshot data can be safely accessed.
Is there a way to cast Object to int?
Errors
The argument type 'Object?' can't be assigned to the parameter type 'int?'.
The method '[]' can't be unconditionally invoked because the receiver can be 'null'.
The code is a minimal version, where I want to display List elements
class _ListState extends State<List> {
#override
Widget build(BuildContext context) {
final database = Provider.of<AppDatabase>(context);
return StreamBuilder(
stream: database.XYZ(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (_, index) {
return ListItem(snapshot.data[index]);
}
);
} else {
return Text("No data");
}
},
);
}
}
Possible Solution - Specify StreamBuilder with Class
class _ListState extends State<List> {
#override
Widget build(BuildContext context) {
final database = Provider.of<AppDatabase>(context);
return StreamBuilder<List<Element>>(
stream: database.XYZ(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (_, index) {
return ListItem(snapshot.data[index]);
}
);
} else {
return Text("No data");
}
},
);
}
}
You can solve it in two ways:
Provide a specific type to your StreamBuilder
StreamBuilder<List> (...)
Use as to downcast.
builder: (context, snapshot) {
if (snapshot.data != null) {
final list = snapshot.data as List; // <-- Downcasting
return ListView.builder(
itemCount: list.length, // Works now
);
} else {
return Text("No data");
}
}

flutter data not retrieved from firestore when using stream

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

get firebase data without streambuilder

This is how I get data using stream builder from firebase
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('profile').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if (!snapshot.hasData) return const Text('Loading...');
final int messageCount = snapshot.data.documents.length;
return ListView.builder(
shrinkWrap: true,
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document = snapshot.data.documents[index];
return Container();}
);
},
),
My question is, How to get a collection list data to a list inside initstate() maybe using a function
List data=new List();
#override
void initState() {
super.initState();
//here
}
Just do as follow inside your stateful class to fetch the data,
bool isFetching=false;
List<String> dataList=[];
#override
void initState() {
super.initState();
getGroupsData();
}
getGroupsData() {
setState(() {
isFetching= true;
});
databaseReference
.collection("profile")
.getDocuments()
.then((QuerySnapshot snapshot) {
snapshot.documents.forEach((f) => dataList.add[f.data["name"]));
setState(() {
isFetching= false;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child : isFetching ?
CircularProgressIndictaor()
: dataList!=null && dataList.length >0
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Text(dataList[index]);
}
)
: Text("No Data"),
)
);
}

How to fix 'NoSuchMethodError: The getter 'length' was called on null'

How i can fix this problem
i use function to get data from api, but i see error 'NoSuchMethodError: The getter 'length' was called on null'
My code:
Future getData() async{
http.Response response = await http.get('https://myappres.000webhostapp.com/pubg/api.php?action=getskin');
debugPrint(response.body);
_data = json.decode(response.body);
_list = _data['categorys'];
return _list;
}
and
Center(
child: _list.length != null? ListView.builder(
itemCount: _list.length,
padding: const EdgeInsets.all(15.9),
itemBuilder: (BuildContext context, int position){
final index = position;
return ListTile(
title: Text('${_list[index]['name']}'),
subtitle: Image.network('${_list[index]['image']}',width: 200,)
);
}
):Container()
)
this is result Error:
Try using FutureBuilder to wait for the Future:
FutureBuilder(
future: getData(),
builder: (BuildContext context,AsyncSnapshot<List> snapshot){
if(snapshot.hasData){
return Center(child: snapshot.length)
} else return Container();
},
//you can use too:
getData().then((listData){
Center(child: listData)...
});