如何使用StreamBuilder更新TextField的值?

12

我有一个textfield,并在我的应用程序中使用sqflite数据库。 sqflite具有一个值,我需要将其分配给我的textfield

这是我的textfield代码

 StreamBuilder<String>(
    stream: patientHealthFormBloc.doctorName,
    builder: (context, snapshot) {
      return TextFormField(
        initialValue: patientHealthFormBloc.doctorNameValue,
        onChanged: (value) {
          patientHealthFormBloc.doctorNameChanged(value);
        },
        ...

现在,在我的类的initstate方法中,我正在从数据库中获取值。由于它是一个异步操作,因此需要时间。

我的bloc类具有以下代码

Function(String) get doctorNameChanged => _doctorName.sink.add;

当我从数据库接收到值时,我立即调用以下函数

doctorNameChanged("valuefromdatabase");

但是我在文本字段中看不到值。同时我的数据库中存在一个值。 是否可能更新该值而不使用 TextEditingControllersetState。因为我的类被分成了许多块,而且过于复杂,无法使用上述任何一种方法,所以我试图避免它们。 我已经尝试使用相同的方法处理 RadioButtonCheckBox,它们似乎更新得很好。 值也在 _doctorName.stream.value 中更新,该值存在于数据库中,但是 textfield 不显示任何数据。我也尝试更改 textfield 的颜色,所以那里没有问题,因为我能够看到我输入的内容。

我做了一个小应用程序演示 https://github.com/PritishSawant/demo/tree/master/lib

我使用的是 shared preferences 而不是 sqflite,但问题仍然存在。


尝试移除 "initialValue: patientHealthFormBloc.doctorNameValue",并在 StreamBuilder 的 "initialData" 中启动它。 - Stel
@Stel 谢谢,但是不起作用。 - BraveEvidence
@ChinkySight 我正在使用Stream来避免使用TextEditingController。这个应用程序很复杂,使用TextEditingController是不可行的。 - BraveEvidence
如果您能解释一下您想要实现什么,那将会很有帮助。考虑到TextFormField是用户将要积极使用的小部件,为什么您想要实时更改它的值呢?您是否期望在用户输入时更改该值?这有点令人困惑,因为通常您会将现有值加载到TextField中,然后让用户编辑并保存它。 - J. S.
@JoãoSoares 当用户第一次进入文本字段时,他输入一些内容并单击保存,文本字段数据将保存在sqflite中。现在,当他在下一次应用程序重新打开时回到该文本字段时,我想显示他已经先前键入的值。我认为这个问题不难理解吧? - BraveEvidence
显示剩余4条评论
3个回答

4

好的,我终于找到了解决我的问题的方法。

以下是我的代码,我只是在下面的示例中使用了SharedPreferences,而不是sqflite。使用sqflite也可以做同样的事情。

class _MyHomePageState extends State<MyHomePage> {

  MyBloc myBloc = MyBloc();
  TextEditingController myController = TextEditingController();

  @override
  void dispose() {
    myBloc?.close();
    myController?.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    AppPreferences.setString("data", "this is my data");
    AppPreferences.getString("data").then((value){
      myBloc.dataChanged(value);
    });
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: StreamBuilder(
          stream: myBloc.data,
           builder: (context,snapshot){

            debugPrint(snapshot.data);
            myController.value = myController.value.copyWith(text: myBloc.dataValue);

            return TextFormField(
              controller: myController,
              onChanged: (value){
                myBloc.dataChanged(value);
              },
            );
           },
        ),
      ),
    );
  }
}

2
这是一个极度过度设计的解决方案。我希望你最初能够解释清楚你想要实现什么,而不是要求我们修复你的特定解决方案。正如我在你的问题中所评论的那样,你似乎想要对用户正在积极使用的TextField进行实时更新。这非常奇怪,很可能会导致糟糕的用户体验。 - J. S.
@JoãoSoares,你能发一下你的解决方案吗?如果它比我的更好,我很乐意授予悬赏并接受你的解决方案。我刚刚发布了一个更简单的解决方案,以便每个人都能理解,但实际上我的应用程序需要与sqflite和firestore同步,所以在你看来可能会显得过于复杂。 - BraveEvidence
你尝试过我的解决方案了吗? - J. S.
我建议您尝试@JoãoSoares的解决方案,并在其有效时接受该答案。因为它非常简单,现在可以使用TextEditController - Harshvardhan Joshi
不幸的是,尽管有几个用户提供帮助,但似乎该用户并不想归功于这个赏金。这很不幸。 - J. S.
显示剩余4条评论

3

请尝试以下方法:

StreamBuilder<String>(
  stream: patientHealthFormBloc.doctorName,
  builder: (context, snapshot) {
    String doctorName = patientHealthFormBloc.doctorNameValue;
    if(snapshot.hasData){
      doctorName = snapshot.data/*(your name string from the stream)*/;
    } 
    return TextFormField(
      initialValue: doctorName,
      onChanged: (value) {
        patientHealthFormBloc.doctorNameChanged(value);
      },
    ...

你在流中收到新的名称了吗? - Harshvardhan Joshi
你在 builder: (context, snapshot) 中是否收到相同的新名称? - Harshvardhan Joshi
@Stel 但它适用于单选按钮和复选框,为什么不适用于文本字段。我已经为文本字段添加了一个StreamBuilder,并在从数据库获取数据时更改其值,因此不需要另一个StreamBuilder来进行数据库操作。 - BraveEvidence
@HarshvardhanJoshi 我正在使用相同的 bloc 实例。 - BraveEvidence
@HarshvardhanJoshi 在 initstate 方法中。 - BraveEvidence
显示剩余7条评论

1
我的评论中所建议的是这样的:
TextEditingController _textEditingController = TextEditingController();

@override
void initState() {
  patientHealthFormBloc.doctorName.listen((snapshot){
    setState((){
      _textEditingController.text = snapshot;
    });
  });
  super.initState();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      children: <Widget>[
      TextFormField(
        controller: _textEditingController,
        onChanged: (value) {
          patientHealthFormBloc.doctorNameChanged(value);
        },
      ],
    ),
  );
}

我不想在不了解你为什么不想使用TextEditingController或setState的情况下写这个答案。但是,使用Bloc模式可以实现你想要的效果。


一开始我也有这个想法,但由于问题的引用和@Nudge坚持不使用TextEditController,所以我放弃了那个想法。仅通过使用控制器,解决方案变得简单且准确,非常棒。干得好! - Harshvardhan Joshi
2
谢谢,我很感激。不幸的是,用户似乎坚持要坚持自己的想法,而没有试图编写正确的解决方案,因此他们决定不归功于正确的回复或赏金。 - J. S.
当我使用这种方法时,我收到了这个警告:“另一个异常被抛出:在构建期间调用了setState()或markNeedsBuild()。”它可以工作,但我感到有些困扰。 - NothingBox
这应该与您正在使用的任何状态管理解决方案有关。 - J. S.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接