androiddartflutterdart-async

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;
  }
}

Solution

  • 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}';
        }
    }