Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter - setState not updating inner Stateful Widget

Basically I am trying to make an app whose content will be updated with an async function that takes information from a website, but when I do try to set the new state, it doesn't reload the new content. If I debug the app, it shows that the current content is the new one, but after "rebuilding" the whole widget, it doesn't show the new info.

Edit: loadData ( ) method, basically read a URL with http package, the URL contains a JSON file whose content changes every 5 minutes with new news. For example a .json file with sports real-time scoreboards whose scores are always changing, so the content should always change with new results.

class mainWidget extends StatefulWidget {       State<StatefulWidget> createState() => new mainWidgetState(); }  class mainWidgetState extends State<mainWidget> {    List<Widget> _data;   Timer timer;    Widget build(BuildContext context) {      return new ListView(               children: _data);   }    @override   void initState() {     super.initState();     timer = new Timer.periodic(new Duration(seconds: 2), (Timer timer) async {       String s = await loadData();       this.setState(() {         _data = <Widget> [new childWidget(s)];       });       });   } }  class childWidget extends StatefulWidget {   childWidget(String s){     _title = s;   }    Widget _title;    createState() => new childState(); }  class childState extends State<gameCardS> {    Widget _title;    @override   Widget build(BuildContext context) {     return new GestureDetector(onTap: foo(),        child: new Card(child: new Text(_title));    }    initState()   {     super.initState();     _title = widget._title;   } } 
like image 368
Jesús Martín Avatar asked Feb 17 '18 18:02

Jesús Martín


People also ask

Why setState is not updating immediately Flutter?

Answer That is because you are only updating your PageOne with this setState((){}) call. It doesn't rebuild the parent widget.

How do I use setState in widget Flutter?

setState method Null safety Notify the framework that the internal state of this object has changed. Whenever you change the internal state of a State object, make the change in a function that you pass to setState: setState(() { _myState = newValue; }); The provided callback is immediately called synchronously.

Does setState rebuild widget?

When you run the application, the setState() will fail to rebuild the widget. In this example, the initState() is called only once. This means the results of clicking + or – icons won't get displayed on the screen. However, this clicking will always execute the setState() and pass the new value to the widget tree.


2 Answers

This should sort your problem out. Basically you always want your Widgets created in your build method hierarchy.

import 'dart:async';  import 'package:flutter/material.dart';  void main() => runApp(new MaterialApp(home: new Scaffold(body: new MainWidget())));  class MainWidget extends StatefulWidget {     @override     State createState() => new MainWidgetState(); }  class MainWidgetState extends State<MainWidget> {      List<ItemData> _data = new List();     Timer timer;      Widget build(BuildContext context) {         return new ListView(children: _data.map((item) => new ChildWidget(item)).toList());     }      @override     void initState() {         super.initState();         timer = new Timer.periodic(new Duration(seconds: 2), (Timer timer) async {             ItemData data = await loadData();             this.setState(() {                 _data = <ItemData>[data];             });         });     }       @override     void dispose() {         super.dispose();         timer.cancel();     }      static int testCount = 0;      Future<ItemData> loadData() async {         testCount++;         return new ItemData("Testing #$testCount");     } }  class ChildWidget extends StatefulWidget {      ItemData _data;      ChildWidget(ItemData data) {         _data = data;     }      @override     State<ChildWidget> createState() => new ChildState(); }  class ChildState extends State<ChildWidget> {      @override     Widget build(BuildContext context) {         return new GestureDetector(onTap: () => foo(),             child: new Padding(                 padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),                 child: new Card(                     child: new Container(                         padding: const EdgeInsets.all(8.0),                         child: new Text(widget._data.title),                     ),                 ),             )         );     }      foo() {         print("Card Tapped: " + widget._data.toString());     } }  class ItemData {     final String title;      ItemData(this.title);      @override     String toString() {         return 'ItemData{title: $title}';     } } 
like image 104
Simon Avatar answered Sep 20 '22 19:09

Simon


This was really giving me headache and no Google results were working. What finally worked was so simple. In your child build() assign the value to the local variable before you return. Once I did this everything worked with subsequent data loads. I even took out the initState() code.

Many thanks to @Simon. Your answer somehow inspired me to try this.

In your childState:

@override Widget build(BuildContext context) { _title = widget._title; // <<< ADDING THIS HERE IS THE FIX return new GestureDetector(onTap: foo(),    child: new Card(child: new Text(_title));  } 

Hopefully this works in your code. For me, I use a Map for the entire JSON record passed in, rather than a single String, but that should still work.

like image 37
gregthegeek Avatar answered Sep 18 '22 19:09

gregthegeek