Flutter, Dart setState not reloading state
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();
}
}
Jan
Hello there, I'm code enthusiast working on few projects at the moment
Updated on December 29, 2022Comments
-
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
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 about 3 yearsThank 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