Flutter: How to re-render the view without making an API call?
Here is how I solved my problem, but firstly I'll try to explain what was the cause.
The problem
The problem was that I had my getData()
method that is calling the API in my build
method, therefore, every time you'd make a change it calls the build
method which then executes the getData
method respectively.
...
future: getData()
...
The fix
The getData()
method must be executed in the initState()
to prevent it from executing multiple times. First, I defined a variable dataFetched
which will only be set when the getData()
method finishes. Once the it is set, the future builder will continue executing and run the code within it.
...
var dataFetched;
@override
void initState(){
super.initState();
dataFetched = getData();
}
...
FutureBuilder(
future: dataFetched,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if(snapshot.connectionState == ConnectionState.done){...}
}
}
)
...
I am not sure that this is the best way of doing it, but at least it fixed my problem.
Please read this thread for more detailed explanation.
How to deal with unwanted widget build?
Credits: Rémi Rousselet
selected
Updated on December 14, 2022Comments
-
selected over 1 year
So I have this code that fetches routes from one place to another. Please see the picture attached below.
I need to add details for each journey which will be hidden unless you click on 'Details' button, I want to show the data in a ExpansionTile/ExpansionPanel - basically a collapsing element.
The problem is that when I use the
setState() {...}
on the button, it will show the data indeed but it will also make the API call again.The method
getData()
gets triggered in the build widget which is why (I suppose) this is happening but I don't know how to solve this.Is there a better way of doing it?
Any help/improvements will be highly appreciated as I am new to Flutter =)
... class _PlanJourney extends State<PlanJourney> { String data; final from; final to; final type; final time; static var date = new DateTime.now(); final String formattedDate = new DateFormat('yyyy-MM-dd').format(date); double _animatedHeight = 100.0; Map<String,dynamic> journeyData; _PlanJourney(this.from, this.to, this.type, this.time); Future<String> getData() async { http.Response response = await http.get(url); data = response.body; journeyData = json.decode(data); debugPrint(journeyData.toString()); } @override Widget build(BuildContext context){ return Scaffold( appBar: AppBar( title: Text('Plan a journey'.toUpperCase(), style: TextStyle( color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold ) ), iconTheme: IconThemeData( color: Color(0xFF0984e3), ), backgroundColor: Colors.white, ), resizeToAvoidBottomPadding: false, body: SingleChildScrollView( child: ConstrainedBox( constraints: BoxConstraints(), child: FutureBuilder( future: getData(), builder: (BuildContext context, AsyncSnapshot<String> snapshot) { if(snapshot.connectionState == ConnectionState.done){ List<Widget> elements = new List<Widget>(); for(var i = 0; i < journeyData['routes'].length; i++){ List<Widget> iconList = new List<Widget>(); String duration; String start = journeyData['routes'][i]['departure_time']; String end; String stress = '2 min'; for(var j = 0; j < journeyData['routes'][i]['route_parts'].length; j++){ if(journeyData['routes'][i]['route_parts'][j]['mode'] == 'foot'){ iconList.add(IconTheme( data: IconThemeData( color: Color(0xFFbfcdd5) ), child: Icon(Icons.directions_walk), ) ); } else if(journeyData['routes'][i]['route_parts'][j]['mode'] == 'tube'){ iconList.add(IconTheme( data: IconThemeData( color: Color(0xFFbfcdd5) ), child: Icon(Icons.train), ) ); } else if(journeyData['routes'][i]['route_parts'][j]['mode'] == 'bus'){ iconList.add(IconTheme( data: IconThemeData( color: Color(0xFFbfcdd5) ), child: Icon(Icons.directions_bus), ) ); } duration = journeyData['routes'][i]['duration']; end = journeyData['routes'][i]['arrival_time']; } elements.add(Container( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Container( alignment: Alignment.topLeft, margin: EdgeInsets.only(left: 15, right: 15), padding: EdgeInsets.all(10), child: new Wrap( direction: Axis.horizontal, crossAxisAlignment: WrapCrossAlignment.start, spacing: 5, runSpacing: 5, children: iconList ), ), Container( alignment: Alignment.topLeft, margin: EdgeInsets.only(left: 15, right: 15), padding: EdgeInsets.all(10), child: new Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget> [ Column( children: <Widget>[ Text('Duration', style: TextStyle( fontFamily: "Gotham Pro", fontWeight: FontWeight.w300, fontSize: 14 ) ), Padding( padding: EdgeInsets.only(top: 5), ), Text(duration) ], ), Column( children: <Widget>[ Text('Start', style: TextStyle( fontFamily: "Gotham Pro", fontWeight: FontWeight.w300, fontSize: 14 ) ), Padding( padding: EdgeInsets.only(top: 5), ), Text(start), ], ), Column( children: <Widget>[ Text('End', style: TextStyle( fontFamily: "Gotham Pro", fontWeight: FontWeight.w300, fontSize: 14 ) ), Padding( padding: EdgeInsets.only(top: 5), ), Text(end), ], ), Column( children: <Widget>[ Text('Stress', style: TextStyle( fontFamily: "Gotham Pro", fontWeight: FontWeight.w300, fontSize: 14 ) ), Padding( padding: EdgeInsets.only(top: 5), ), Text(stress) ], ) ] ), ), Container( alignment: Alignment.topLeft, margin: EdgeInsets.only(left: 15, right: 15), child: Row( children: <Widget>[ OutlineButton( child: Text('Details', style: TextStyle( color: Color(0xFF0c85e4), fontFamily: "Gotham Pro", fontWeight: FontWeight.w700 ) ), borderSide: BorderSide( color: Color(0xFF0c85e4), //Color of the border style: BorderStyle.solid, //Style of the border width: 2, //width of the border ), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), onPressed: ()=>setState((){ _animatedHeight!=0.0 _animatedHeight=0.0:_animatedHeight=100.0; } ), ), Padding( padding: EdgeInsets.only(left: 15, top: 15), ), RaisedButton( child: Text('Save', style: TextStyle( color: Colors.white, fontFamily: "Gotham Pro", fontWeight: FontWeight.w700, ) ), color: Color(0xFF08b894), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), onPressed: (){}, ) ], ) ), AnimatedContainer(duration: const Duration(milliseconds: 120), child: new Text("Journey data will go here"), height: _animatedHeight, color: Colors.tealAccent, width: 200.0, ), Divider() ], ), )); } return new Column(children: elements); }else if(snapshot.connectionState == ConnectionState.waiting){ return Text("loading ..."); } }, ), ) ) ); } }
-
Rémi Rousselet over 4 years
-
-
Randal Schwartz over 4 yearsYou should act as if your build() method might be called 60 times per second, so it should be fast and idempotent (no side effects like API calls). In practice, the framework optimizes most of those away.