flutter how to use future async method in init state
The initState
method is synchronous by design. Rather than creating the FutureBuilder
widget in the initState
method, you could return the FutureBuilder
widget in the overridden build
method. Feel free to take a look at the documentation for the FutureBuilder
widget here for an example of how this could work.
It looks like there is also an issue with the code in the setState
callback function. You probably want to replace imgList = widget.imageList as List<String>;
with imgList = snapshot.data as List<String>;
in order to use the data from the completed future.
It looks like there is also an issue with the build
method not returning a widget in all the cases. I have taken your updated code sample and modified it as below:
import 'package:flutter/material.dart';
class BottomSheetWidget extends StatefulWidget {
BottomSheetWidget({Key key, this.name, this.imageList}) : super(key: key);
final String name;
final Future<List<String>> imageList;
@override
_BottomSheetWidgetState createState() => _BottomSheetWidgetState();
}
class _BottomSheetWidgetState extends State<BottomSheetWidget> {
int _current = 0;
@override
Widget build(BuildContext context) {
return FutureBuilder<List<String>>(
future: widget.imageList,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final imgList = snapshot.data;
return Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(25),
),
height: MediaQuery.of(context).size.height * 0.85,
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
CarouselSlider(
items: imgList.map(
(item) {
return Container(
child: Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius:
BorderRadius.all(Radius.circular(5.0)),
child: Stack(
children: <Widget>[
Image.network(
item,
fit: BoxFit.cover,
height: 1000.0,
width: 1000.0,
),
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(200, 0, 0, 0),
Color.fromARGB(0, 0, 0, 0)
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
),
),
],
),
),
),
);
},
).toList(),
options: CarouselOptions(
enlargeCenterPage: true,
enlargeStrategy: CenterPageEnlargeStrategy.height,
height: MediaQuery.of(context).size.height - 500,
aspectRatio: 2.0,
onPageChanged: (index, reason) {
setState(() {
_current = index;
});
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: imgList.map((url) {
return Container(
width: 8.0,
height: 8.0,
margin: EdgeInsets.symmetric(
vertical: 10.0, horizontal: 2.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _current == imgList.indexOf(url)
? Color.fromRGBO(0, 0, 0, 0.9)
: Color.fromRGBO(0, 0, 0, 0.4),
),
);
}).toList(),
),
Text(
widget.name,
style: TextStyle(
fontSize: 30,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
Text(
"United Kingdom",
style: TextStyle(
fontSize: 18,
color: Colors.white,
),
),
],
),
)
],
),
);
} else {
return CircularProgressIndicator();
}
},
);
}
}
iKreateCode
Updated on December 22, 2022Comments
-
iKreateCode over 1 year
When the screen loads i want the init state to fill the List with Future<List> that is passed through the stateful widget. However since its a future, my method will crash and not work.
My code
@override void initState() async { FutureBuilder( future: widget.imageList, builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.none: print('NONE'); break; case ConnectionState.active: case ConnectionState.waiting: print("WAITING"); break; case ConnectionState.done: print("DONE"); setState(() { imgList = widget.imageList as List<String>; }); break; } }, ); super.initState(); }
Error Produced
════════ Exception caught by widgets library ═══════════════════════════════════ _BottomSheetWidgetState.initState() returned a Future. The relevant error-causing widget was MaterialApp
Stateful widget where the list is passed through
List<String> imgList = List<String>(); class BottomSheetWidget extends StatefulWidget { BottomSheetWidget({Key key, this.name, this.imageList}) : super(key: key); String name; Future<List<String>> imageList;
Whole edited code:
List<String> imgList = List<String>(); class BottomSheetWidget extends StatefulWidget { BottomSheetWidget({Key key, this.name, this.imageList}) : super(key: key); String name; Future<List<String>> imageList; @override _BottomSheetWidgetState createState() => _BottomSheetWidgetState(); } class _BottomSheetWidgetState extends State<BottomSheetWidget> { int _current = 0; final List<Widget> imageSliders = imgList .map((item) => Container( child: Container( margin: EdgeInsets.all(5.0), child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(5.0)), child: Stack( children: <Widget>[ Image.network(item, fit: BoxFit.cover, height: 1000.0, width: 1000.0), Positioned( bottom: 0.0, left: 0.0, right: 0.0, child: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [ Color.fromARGB(200, 0, 0, 0), Color.fromARGB(0, 0, 0, 0) ], begin: Alignment.bottomCenter, end: Alignment.topCenter, ), ), ), ), ], )), ), )) .toList(); @override Widget build(BuildContext context) { return FutureBuilder( future: widget.imageList, builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.none: print('NONE'); break; case ConnectionState.active: case ConnectionState.waiting: print("WAITING"); break; case ConnectionState.done: print("DONNE"); setState(() async { imgList = snapshot.data as List<String>; }); return Container( decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(25), ), height: MediaQuery.of(context).size.height * 0.85, width: MediaQuery.of(context).size.width, child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: <Widget>[ Padding( padding: EdgeInsets.symmetric(horizontal: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ CarouselSlider( items: imageSliders, options: CarouselOptions( enlargeCenterPage: true, enlargeStrategy: CenterPageEnlargeStrategy.height, height: MediaQuery.of(context).size.height - 500, aspectRatio: 2.0, onPageChanged: (index, reason) { setState(() { _current = index; }); }), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: imgList.map((url) { int index = imgList.indexOf(url); return Container( width: 8.0, height: 8.0, margin: EdgeInsets.symmetric( vertical: 10.0, horizontal: 2.0), decoration: BoxDecoration( shape: BoxShape.circle, color: _current == index ? Color.fromRGBO(0, 0, 0, 0.9) : Color.fromRGBO(0, 0, 0, 0.4), ), ); }).toList(), ), Text(widget.name, style: TextStyle( fontSize: 30, color: Colors.white, fontWeight: FontWeight.bold)), Text("United Kingdom", style: TextStyle( fontSize: 18, color: Colors.white, )), ], ), ) ], ), ); break; } }, ); } }
New error from console:
flutter: WAITING ════════ Exception caught by widgets library ═══════════════════════════════════ A build function returned null. The relevant error-causing widget was FutureBuilder<List<String>> lib/common_widget/bottom_sheet.dart:54 ════════════════════════════════════════════════════════════════════════════════ flutter: DONNE ════════ Exception caught by widgets library ═══════════════════════════════════ setState() callback argument returned a Future. The relevant error-causing widget was FutureBuilder<List<String>> lib/common_widget/bottom_sheet.dart:54 ════════════════════════════════════════════════════════════════════════════════
-
Jitesh Mohite almost 4 yearsthis should be done inside build method, not in initt
-
iKreateCode almost 4 yearsI am still learning flutter and classed as a beginner. The only reason i put it inside init state was because i want that to run first before anything else. So if i were to include it inside the build would it still run first?
-
tnc1997 almost 4 yearsThe
FutureBuilder
widget provides you with a snapshot which contains details of whether the future has completed amongst others. You could use this to check if the future has completed before rendering other widgets that depend on the result of the future. Any widgets returned from within thebuilder
callback function of theFutureBuilder
will be created after theFutureBuilder
has been created.
-
-
iKreateCode almost 4 yearsI think using that i have managed to get it somewhat working before i can accept your answer i am getting this error within the done case. type 'Future<List<String>>' is not a subtype of type 'List<String>' in type cast. I believe this happens in the set state
-
tnc1997 almost 4 yearsWould you be able to post an updated code sample of the
BottomSheetWidget
and the_BottomSheetWidgetState
? -
tnc1997 almost 4 yearsPerfect, I have updated my answer based on the updated code sample.
-
iKreateCode almost 4 yearsHmmm, having tried that i still get an error, i updated my code above
-
tnc1997 almost 4 yearsApologies, I missed another issue which is your
build
method not returning a widget in all the cases.