简体   繁体   中英

Flutter, Dart setState not reloading state

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

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM