Flutter, Dart setState not reloading state

340

Your setState((){}) only rebuilds the build method of InputRow class. You need to rebuild the build method of MyHomePage to update all those fields. User Provider. Create a separate class to contains the value of those fields and notifyListeners() to update UI.

class Results extends ChangeNotifier{
//your logics

void calculate() {
  //your calculations
notifyListeners();
}

}
Share:
340
Jan
Author by

Jan

Hello there, I'm code enthusiast working on few projects at the moment

Updated on December 29, 2022

Comments

  • Jan
    Jan over 1 year

    Solved the issue, though there is still one bug, please check the edit section for more info on the bug!

    I have issue with setState in my app, basically I have Number input and Multiplier input TextField, and every time I input any of those, I would like app to show/update result/results in text widget/widgets.

    When value is provided in number TextField, only text widget behind it in the same row should update, and this works as expected.

    When value is provided/updated in Multiplier TextField all text widgets on the screen should be updated and this is not happening

    In both Multiplier and number TextField I'm using setState for purpose of updating Text widgets, which I would like to have updated every time I enter/update value in either Multiplier or number field, when value is provided in Multiplier field, all Text widgets that are behind non empty number field should update(not working). While when value is provided in number field, only text widget behind it should update (this is working as expected).

    Thank for your time

    enter image description here

    result doesn't update when entering or updating Multiplier field

        import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
    // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: new LayoutBuilder(
              builder: (BuildContext context, BoxConstraints constraints) {
            return Scaffold(
              body: new Column(
                children: <Widget>[
                  HeaderRow(),
                  InputRow(inputRowID: 0),
                  InputRow(inputRowID: 1),
                  InputRow(inputRowID: 2),
                  InputRow(inputRowID: 3),
                  InputRow(inputRowID: 4),
                  InputRow(inputRowID: 5),
                ],
              ),
            );
          }), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }
    
    class HeaderRow extends StatefulWidget {
      static var multiplier;
    
      @override
      _HeaderRowState createState() => _HeaderRowState();
    }
    
    class _HeaderRowState extends State<HeaderRow> {
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            Row(
              children: [
                Expanded(
                  flex: 1,
                  child: Container(
                    child: TextField(
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                        hintText: 'Multiplier',
                      ),
                      keyboardType: TextInputType.number,
                      inputFormatters: <TextInputFormatter>[
                        FilteringTextInputFormatter.digitsOnly
                      ], // Only numbers can be entered
                      onChanged: (String str) {
                        setState(() {
                          HeaderRow.multiplier = str;
                          calculate();
                        });
                      },
                    ),
                  ),
                ),
              ],
            ),
          ],
        );
      }
    }
    
    class InputRow extends StatefulWidget {
      final int inputRowID;
    
      InputRow({@required this.inputRowID});
    
      static List<String> number = ['0', '0', '0', '0', '0', '0'];
      static List<double> result = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
      static var resultToString = [
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
      ];
    
      @override
      _InputRowState createState() => _InputRowState();
    }
    
    class _InputRowState extends State<InputRow> {
      @override
      Widget build(BuildContext contex) {
        return Column(
          children: <Widget>[
            Row(
              children: [
                Expanded(
                  flex: 1,
                  child: Container(
                    child: TextField(
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                        hintText: 'number',
                      ),
                      keyboardType: TextInputType.number,
                      inputFormatters: <TextInputFormatter>[
                        FilteringTextInputFormatter.digitsOnly
                      ], // Only numbers can be entered
                      onChanged: (String str) {
                        setState(() {
                          InputRow.number[widget.inputRowID] = str;
                          calculate();
                          print(InputRow.resultToString[widget.inputRowID]);
                        });
                      },
                    ),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Container(
                    child: Text(
                      InputRow.resultToString[widget.inputRowID],
                      textAlign: TextAlign.center,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ),
                ),
              ],
            ),
          ],
        );
      }
    }
    
    void calculate() {
      for (int ctr = 0; ctr < InputRow.result.length; ctr++) {
        InputRow.result[ctr] =
            double.parse(InputRow.number[ctr]) * double.parse(HeaderRow.multiplier);
        InputRow.resultToString[ctr] = InputRow.result[ctr].toStringAsFixed(2);
      }
    }
    

    EDIT

    Was able to solve the issue by removing InputRow()'s widgets from MyHomePage class and adding them to the end of HeaderRow() class instead and now it works almost perfectly.

    However there is still one bug, whenever I delete all input in any of the number input fields, app will stop update results for all of the Text widgets, even though in other number fields and multiplier field there is input. Though if I enter numbers in all of the number fields, app works again as expected.

    If anyone has idea how to solve this issue, thank you for the help.

    Updated code

        import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
    // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: new LayoutBuilder(
              builder: (BuildContext context, BoxConstraints constraints) {
            return Scaffold(
              body: new Column(
                children: <Widget>[
                  HeaderRow(),
                ],
              ),
            );
          }), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }
    
    class HeaderRow extends StatefulWidget {
      static var multiplier;
    
      @override
      _HeaderRowState createState() => _HeaderRowState();
    }
    
    class _HeaderRowState extends State<HeaderRow> {
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            Row(
              children: [
                Expanded(
                  flex: 1,
                  child: Container(
                    child: TextField(
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                        hintText: 'Multiplier',
                      ),
                      keyboardType: TextInputType.number,
                      inputFormatters: <TextInputFormatter>[
                        FilteringTextInputFormatter.digitsOnly
                      ], // Only numbers can be entered
                      onChanged: (String str) {
                        setState(() {
                          HeaderRow.multiplier = str;
                          calculate();
                        });
                      },
                    ),
                  ),
                ),
              ],
            ),
            InputRow(inputRowID: 0),
            InputRow(inputRowID: 1),
            InputRow(inputRowID: 2),
            InputRow(inputRowID: 3),
            InputRow(inputRowID: 4),
            InputRow(inputRowID: 5),
          ],
        );
      }
    }
    
    class InputRow extends StatefulWidget {
      final int inputRowID;
    
      InputRow({@required this.inputRowID});
    
      static List<String> number = ['0', '0', '0', '0', '0', '0'];
      static List<double> result = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
      static var resultToString = [
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
        'awaiting input...',
      ];
    
      @override
      _InputRowState createState() => _InputRowState();
    }
    
    class _InputRowState extends State<InputRow> {
      @override
      Widget build(BuildContext contex) {
        return Column(
          children: <Widget>[
            Row(
              children: [
                Expanded(
                  flex: 1,
                  child: Container(
                    child: TextField(
                      decoration: InputDecoration(
                        border: OutlineInputBorder(),
                        hintText: 'number',
                      ),
                      keyboardType: TextInputType.number,
                      inputFormatters: <TextInputFormatter>[
                        FilteringTextInputFormatter.digitsOnly
                      ], // Only numbers can be entered
                      onChanged: (String str) {
                        setState(() {
                          InputRow.number[widget.inputRowID] = str;
                          calculate();
                          print(InputRow.resultToString[widget.inputRowID]);
                        });
                      },
                    ),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Container(
                    child: Text(
                      InputRow.resultToString[widget.inputRowID],
                      textAlign: TextAlign.center,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ),
                ),
              ],
            ),
          ],
        );
      }
    }
    
    void calculate() {
      for (int ctr = 0; ctr < InputRow.result.length; ctr++) {
        InputRow.result[ctr] =
            double.parse(InputRow.number[ctr]) * double.parse(HeaderRow.multiplier);
        InputRow.resultToString[ctr] = InputRow.result[ctr].toStringAsFixed(2);
      }
    }
    
  • Jan
    Jan about 3 years
    Thank you for your input, though I'm naab, can't really figure out what is meant to be logic in your example, was also unable to add provider package to my app, it says that "integration_test from sdk depends on collection 1.15.0-nullsafety.5 and provider >=5.0.0-nullsafety.4 depends on collection ^1.15.0, integration_test is incompatible with provider". However I've solved the issue by removing InputRows()s from MyHomePage and placed them instead to end of HeaderRow() and it works now. Thank you