I am having an issue with my code.
Essentially, what I am trying to do is dynamically create cards that display file information, after pulling the data from a sqflite database.
The issue I have is that my ListView
has a return type of <List<Widget>>
and my query from the sqflite DB returns with a <Future<List<Widget>>>
. I know this problem requires a FutureBuilder
, but I am not sure where to implement it.
Here is the error message:
The argument type Future<List< Widget >> can't be assigned to the parameter type List< Widget >
This is the code that pulls the data from the DB, and stores the resulting card widget in the list itemsList
Future<List<Widget>> getRecordedItems() async {
List<Widget> itemsList = [];
final dbHelper helper = dbHelper();
var recordings = await helper.getRecordings();
var len = recordings.length;
for (var i = 0; i < len; i ) {
var id = recordings[i]["id"];
var filepath = recordings[i]["filepath"];
var filelength = recordings[i]["length"];
var loopCode = Card1(filepath, i, len, filelength);
itemsList.add(loopCode);
}
return itemsList;
}
This is my current return
statement:
var items = getRecordedItems();
return ListView(children: items);
This is the code that is receiving the ListView
object in another file:
Widget build(BuildContext context) {
final AudioPlayerController audioController =
Provider.of<AudioPlayerController>(context);
return Scaffold(
backgroundColor: AppColor.AppBackgroundColor,
appBar: const PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight), child: Titlebar()),
body: Container(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(children: [
const PastRecordingsHeader(name: "$name"),
const Spacer(
flex: 1,
),
SizedBox(
height: MediaQuery.of(context).size.height /1.67,
child: RecordingCard(name: '$name')
)
]),
)),
bottomNavigationBar: PrevRecordingsNavBar();
RecordingCard()
is where the <List<Widget>>
is called.
Can someone please help me understand where to place the FutureBuilder?
Tried implementing the FutureBuilder
outside the ListView
, and inside the ListView
to no avail.
CodePudding user response:
To fetch your data right, you need to put a (FutureBuilder) Widget above (ListView) Widget that you already have, and in this case, the list will not be implemented until the data comes, so to check if the data comes or not you need to make (if statement) as you see below to show other Widget until the data comes, and after the function completed then your list will show the fetched data from the function.
FutureBuilder<List<Widget>>(
future: getRecordedItems(),
builder: (context, data) {
if (data.hasData) {
List<Widget> dataList = data.data!;
return ListView(children: dataList);
} else {
Text(
'There is no data'); // put any widget you want in case there is no data
}
}),
CodePudding user response:
Instead of returning the ListView
you have to return the FutureBuilder
so
Future<List<Widget>> items = [];
@override
void initState() {
items = getRecordedItems(); // future is created just once, here!
super.initState();
}
...
// somewhere further down in your build method:
return FutureBuilder<List<Widget>>(
future: items, // do not create the future here!
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
List<Widget> items = snapshot.data ?? [];
return ListView(children: items);
} else { // if not yet done, return a progress indicator!
return CircularProgressIndicator();
}
}),
Take special care of not creating the future
in future:
parameter, otherwise it will be created everytime the widget rebuilds!
For more on this see the wonderful Widget of the week chapter on FutureBuilder