'Flutter future builder to list
I have some troubles in understanding how future builder works in flutter. I want to pass a list of String from my future call and I want to display them in a SingleChildScrollView.
The problem is that when I access the snapshot.data
I can not access the element of the list. Because in my SingleChildScrollView I have container and in each container I want to display one String from the list.
This is the Future getData method with which I retrieve the data.
Future<List<String>> getData () async {
List<String> data = [];
data.add("A");
data.add("B");
data.add("C");
// DEBUG
await Future.delayed(const Duration(seconds: 2), (){});
return data;
}
And this is my future builder in which I want to display the data in each container. In the loading I added a shimmer effect.
FutureBuilder(
builder: (context, snapshot) {
List<Widget> children;
if (snapshot.hasData) {
children = <Widget>[
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
],
),
),
];
} else if (snapshot.hasError) {
children = <Widget>[
const Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
)
];
} else {
children = <Widget>[
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
Shimmer.fromColors(
baseColor: Colors.grey.shade200,
highlightColor: Colors.grey.shade300,
child: Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
),
Shimmer.fromColors(
baseColor: Colors.grey.shade200,
highlightColor: Colors.grey.shade300,
child: Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
),
Shimmer.fromColors(
baseColor: Colors.grey.shade200,
highlightColor: Colors.grey.shade300,
child: Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
),
],
),
),
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
);
},
future: getData(),
),
So in this way I can I access the elements of my list of Strings?
Solution 1:[1]
As @piskink mentioned, using ListView.builder
is more efficient.
body: FutureBuilder<List<String>?>(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Text(snapshot.data?[index] ?? "got null");
},
);
}
/// handles others as you did on question
else {
return CircularProgressIndicator();
}
},
If you still wish to use SingleChildScrollView
, you can generate items like
return Column(
children: List.generate(
snapshot.data!.length,
(index) => Text(snapshot.data?[index] ?? "got null"),
),
);
Check more about async-await and Future.
Solution 2:[2]
When you declare a FutureBuilder
you have also to pass it it's data type. In this case it will be:
FutureBuilder<List<String>>(
future: getData(),
builder: (context,snapshot){
return ...;
}
)
If you don't declare its datatype, your snapshot
will be considered as an AsyncDataSnapshot<dynamic>
instead of AsyncDataSnapshot<List<String>>
.
Solution 3:[3]
This is a full example for FutureBuilder with Null Safty
if you got these errors:
( The getter 'length' isn't defined for the type 'Object'. )
( The operator '[]' isn't defined for the type 'Object'. )
This is the Fix ?????
Fix & Important Note:
You have to select datatype of the Future Data that will be received.
Right: FutureBuilder<List>()
Wrong: FutureBuilder()
Full Simple Example:
import 'package:flutter/material.dart';
class FutureExample extends StatelessWidget {
const FutureExample({Key? key}) : super(key: key);
Future<List<String>> getData() async{
await Future.delayed(
const Duration(seconds:2)
);
return ["I'm Ramy","I'm Yasser", "I'm Ahmed", "I'm Yossif",];
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<List<String>>(
future: getData(),
builder: (context, snapshot) {
return snapshot.connectionState == ConnectionState.waiting
? const CircularProgressIndicator()
: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: List.generate(snapshot.data!.length,
(index) {
return Text(snapshot.data?[index] ?? "null") ;
},
),
);
},
),
),
);
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Yeasin Sheikh |
Solution 2 | smaso |
Solution 3 | Ramy Wahid |