TextField上的TextController不会在编辑后听取更改&& OnChanged一次不再起作用

问题描述 投票:0回答:1

我希望能够编辑TextField,并在填充完所有文本后,计算一个值。每次用户更改值时,都必须重新计算该值。

我试过Streams和Controller。流正在做得很好,即使价值的更新是用1个变化的数据完成的。即heightweightage都必须是!= null,当我们在第一个{height: 2, weight:3, age:2}时,它没有计算,在其中一个被更改之后,结果计算但是使用上述数据。相反,控制器似乎根本不听变量变化。

在这里,我在另一个类中初始化控制器

class Bsmi extends StatelessWidget {
  StreamController<void> valueBoxStream = StreamController<void>.broadcast();

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new DefaultAppBar(context: context, title: Text(" ")),
      body: new ListView(
        children: <Widget>[
          BsmiResult(valueBoxStream, {}),
          new Container(
            alignment: Alignment.topLeft,
            padding: EdgeInsets.all(20),
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(15.0),
            ),
            child: ListView(
              shrinkWrap: true,
              physics: ClampingScrollPhysics(),
              padding: EdgeInsets.all(20.0),
              itemExtent: 80.0,
              children: <Widget>[
                _BsmiResultState().indices("height", valueBoxStream),
                _BsmiResultState().indices("weight", valueBoxStream),
                _BsmiResultState().indices("age", valueBoxStream),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

这是重要的代码

class BsmiResult extends StatefulWidget {
  final StreamController<void> valueBoxStream;

  const BsmiResult(this.valueBoxStream, this.data);

  final Map data;

  @override
  _BsmiResultState createState() =>
      _BsmiResultState(valueBoxStream: this.valueBoxStream, data: this.data);
}

class _BsmiResultState extends State<BsmiResult> {
  _BsmiResultState({this.valueBoxStream, this.data});
  StreamController<void> valueBoxStream;

  final Map data;

  final textFieldController = new TextEditingController();

  int weight;
  int age;
  int height;
  String _result = "0.0";



  _printLatestValue() {
    print("Second text field: ${textFieldController.text}");
  }

  void setData(data) {
    if (data['type'] == 'height') {
      print("I'm inside height\n");
      height = int.parse(data['value']);
    } else if (data['type'] == 'weight') {
      print("I'm inside weight\n");
      weight = int.parse(data['value']);
    } else {
      print("I'm inside age\n");
      age = int.parse(data['value']);
    }
  }

  @override
  void initState() {
    super.initState();
    textFieldController.addListener(_printLatestValue);
    valueBoxStream.stream.listen((_){
      _calculateResult();
    });
  }

  @override
  void dispose() {
    // Clean up the controller when the Widget is removed from the Widget tree
    textFieldController.dispose();
    super.dispose();
  }

  void _calculateResult() {
      if ((height != null) && (height != 0) && (weight != null) && (weight != 0) && (age != null) && (age != 0)) {
        print("height: " +
            height.toString() +
            "" +
            " weight: " +
            weight.toString() +
            " age: " +
            age.toString());
        _result = ((height + weight) / age).toString();
        print(_result + "\n");
      } else {
        _result = "0.0";
      }
  }

  @override
  Widget build(BuildContext context) {
    return new Column(
      children: <Widget>[
        new Text("Risultato"),
        new Container(
          decoration: new BoxDecoration(
            borderRadius: BorderRadius.circular(15.0),
          ), //For the controller i don't use the StreamBuilder but a normal Text("$_result"),
          child: StreamBuilder(  
            stream: valueBoxStream.stream,
            builder: (context, snapshot) {
              if (snapshot.hasData || _result !=  "0.0") {
                print(snapshot.data);
                setData(snapshot.data);
                return new Text(
                  "$_result",
                  textAlign: TextAlign.end,
                );
              } else {
                return new Text(
                  "0.0",
                  textAlign: TextAlign.end,
                );
              }
            },
          ),
        ),
      ],
    );
  }

  Row indices(String text, StreamController<void> valueBoxStream) {
    return new Row(
      children: <Widget>[
        leftSection(text),
        rightSection(text, valueBoxStream),
      ],
    );
  }

  Widget leftSection(text) {
    return new Expanded(
      child: new Container(
        child: new Text(
          text,
          style: TextStyle(fontSize: 18),
        ),
      ),
    );
  }


  Container rightSection(text, valueBoxStream) {
    return new Container(
      child: new Flexible(
        flex: 1,
        child: new TextField(
          controller: textFieldController,
          keyboardType: TextInputType.number,
          textAlign: TextAlign.end,
          inputFormatters: <TextInputFormatter>[
            WhitelistingTextInputFormatter(new RegExp("[0-9.]")),
            LengthLimitingTextInputFormatter(4),
          ],
          decoration: new InputDecoration(
            enabledBorder: new OutlineInputBorder(
              borderRadius: new BorderRadius.circular(15),
              borderSide: new BorderSide(width: 1.2),
            ),
            border: new OutlineInputBorder(
              borderRadius: new BorderRadius.circular(15),
              borderSide: new BorderSide(color: Colors.lightBlueAccent),
            ),
            hintText: '0.0',
          ),
          onChanged: (val) {
            valueBoxStream.add({'type': text, 'value': val});
          },
        ),
      ),
    );
  }
}

我应该能够在这一点上得到Result: x.y,但是我只在我改变textField一次并且在同一个'session'之后才得到它

在此先感谢谁能真正向我解释为什么这不起作用以及我正在犯的错误。

dart flutter
1个回答
0
投票

我真的很抱歉..但是代码出现了很多问题,我甚至不确定从哪里开始。看起来你已经阅读了有关如何处理flutter状态的所有版本,并设法将它们组合在一个简单的用例中。这是一个工作示例修复:https://gitlab.com/hpoul/clinimetric/commit/413d32d4041f2e6adb7e30af1126af4b05e72271

我不知道如何回答这个“问题”,以便其他人可以从中受益。但..

  1. 如果您创建流控制器或文本视图控制器..您有状态..因此,创建一个有状态小部件(否则您不能例如关闭流和订阅)
  2. 了解哪个小部件负责哪个状态。并且只将有用的值传递给子视图。一种可能的架构是使用ViewController并使用Sink作为变更事件的输入,使用Streams来处理这些事件。因此,如果您创建流控制器,如果要通知事件,则仅使用streamController.sink;如果窗口小部件需要侦听事件,则使用streamController.stream
  3. 构建方法不是处理事件的正确位置。我重构了你的代码,事件的处理和事件的计算是在流的监听器中完成的。如果你想使用StreamBuilder,你可以有一个StreamController,你的生产者直接将Result推入接收器......所以StreamBuilder的builder方法只需要做Text('$ {snapshot.data.value}')

我可能在这个列表中遗漏了很多东西。但这应该让你开始。看看我的变更集。它并不完美,但它确实有效。您应该尝试了解自己在做什么并降低复杂性,而不是将您找到的所有内容叠加在一起。

© www.soinside.com 2019 - 2024. All rights reserved.