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; } }
Answer That is because you are only updating your PageOne with this setState((){}) call. It doesn't rebuild the parent widget.
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.
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.
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}'; } }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With