Flutter - setState not updating inner Stateful Widget Flutter - setState not updating inner Stateful Widget dart dart

Flutter - setState not updating inner Stateful Widget


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:

@overrideWidget build(BuildContext context) {_title = widget._title; // <<< ADDING THIS HERE IS THE FIXreturn 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.


I'm unsure why this happens when calling setState(...) in an async function, but one simple solution is to use:

WidgetsBinding.instance.addPostFrameCallback((_) => setState(...));

instead of just setState(...)