【问题标题】:Flutter not showing BLoC stream value on TextField widgetFlutter 未在 TextField 小部件上显示 BLoC 流值
【发布时间】:2020-06-26 10:09:55
【问题描述】:

我正在尝试制作一个屏幕,用户可以在其中添加一个人的生日,但在另一个时刻,同一屏幕应该从 Firebase 加载数据并在TextFields 中显示加载的值。我正在使用BLoC 和流来执行此操作。我可以正确地从 Firebase 加载数据,并在每个TextField 中使用StreamBuilders 将其传递到屏幕,但是即使流包含正确的值,这些值也没有显示。在同一页面中,我使用一些DropdownButtons 来获取可选数据,它们接收并显示值就好了。我不确定我做错了什么。

这是每个TextField的代码:

class InputField extends StatelessWidget {

  final IconData icon;
  final String hint;
  final bool obscure;
  final TextInputType type;
  final Stream<String> stream;
  final Function(String) onChanged;

  InputField({this.icon, this.hint, this.obscure, this.type, this.stream, this.onChanged});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<String>(
      stream: stream,
      builder: (context, snapshot) {
        return TextField(
          onChanged: onChanged,
          keyboardType: type != null ? type : null,
          decoration: InputDecoration(
            icon: icon == null ? null : Icon(icon, color: Colors.white,),
            hintText: hint,
            hintStyle: TextStyle(color: Colors.white),
            focusedBorder: UnderlineInputBorder(
              borderSide: BorderSide(color: Theme.of(context).primaryColor)
            ),
            contentPadding: EdgeInsets.only(
              left: 5,
              right: 30,
              bottom: 30,
              top: 30
            ),
            errorText: snapshot.hasError ? snapshot.error : null
          ),
          style: TextStyle(color: Colors.white),
          obscureText: obscure,
        );
      }
    );
  }
}

这是我初始化输入类型的地方:

                  return Padding(
                    padding: EdgeInsets.all(16.0),
                    child: SingleChildScrollView(
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          InputField(
                            icon: Icons.person_outline,
                            hint: "Nome do aniversariante",
                            obscure: false,
                            type: TextInputType.text,
                            stream: _birthdayBloc.outName,
                            onChanged: _birthdayBloc.changeName,
                          ),
                          InputField(
                            icon: Icons.phone,
                            hint: "Telefone",
                            obscure: false,
                            type: TextInputType.text,
                            stream: _birthdayBloc.outPhone,
                            onChanged: _birthdayBloc.changePhone,
                          ),
                          InputField(
                            icon: Icons.email,
                            hint: "E-mail",
                            obscure: false,
                            type: TextInputType.text,
                            stream: _birthdayBloc.outEmail,
                            onChanged: _birthdayBloc.changeEmail,
                          ),
                          DropdownWidget(
                            isCenter: false,
                            icon: Icons.list,
                            arrowIcon: Icons.keyboard_arrow_down,
                            hint: "Selecione uma categoria",
                            items: _birthdayBloc.getCategoryList(),
                            stream: _birthdayBloc.outCategory,
                            onChanged: _birthdayBloc.changeCategory,
                          ),
                          DropdownWidget(
                            isCenter: false,
                            icon: Icons.notifications,
                            arrowIcon: Icons.keyboard_arrow_down,
                            hint: "Selecione a notificação",
                            items: _birthdayBloc.getNotificationList(),
                            stream: _birthdayBloc.outNotification,
                            onChanged: _birthdayBloc.changeNotification,
                          ),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            crossAxisAlignment: CrossAxisAlignment.center,
                            children: <Widget>[
                              Expanded(
                                flex: 5,
                                child: DropdownWidget(
                                  isCenter: false,
                                  label: "Mês",
                                  icon: Icons.calendar_today,
                                  arrowIcon: Icons.keyboard_arrow_down,
                                  hint: "Selecione o mês",
                                  items: _birthdayBloc.getMonthsList(),
                                  stream: _birthdayBloc.outMonth,
                                  onChanged: _birthdayBloc.changeMonth,
                                ),
                              ),
                              Expanded(
                                flex: 5,
                                child: DropdownWidget(
                                  isCenter: false,
                                  label: "Dia",
                                  arrowIcon: Icons.keyboard_arrow_down,
                                  hint: "Selecione o dia",
                                  items: _birthdayBloc.getDaysList(),
                                  stream: _birthdayBloc.outDay,
                                  onChanged: _birthdayBloc.changeDay,
                                ),
                              ),
                            ],
                          ),
                          DropdownWidget(
                              isCenter: false,
                              label: "Ano de nascimento",
                              icon: Icons.perm_contact_calendar,
                              arrowIcon: Icons.keyboard_arrow_down,
                              hint: "Selecione o ano",
                              items: _birthdayBloc.getYearsList(),
                              stream: _birthdayBloc.outYear,
                              onChanged: _birthdayBloc.changeYear,
                              valueController: 'Não sei'
                          ),

这是在屏幕加载时从Firebase检索数据的代码:


  Future<void> _loadSelectedBirthday(personId) async {
    String _userUid = await _getCurrentUserUid();

    await _firestore.collection('birthdays').document(_userUid).collection('birthdays').document(personId).get()
      .then((document) {
        _nameController.add(document.data["name"]);
        _phoneController.add(document.data["phone"]);
        _emailController.add(document.data["email"]);
        _categoryController.add(document.data["category"]);
        _notificationController.add(document.data["notification"]);
        _monthController.add(document.data["month"]);
        _dayController.add(document.data["day"]);
        _yearController.add(document.data["year"]);

        print(_nameController.value);
        print(_phoneController.value);
        print(_emailController.value);
        print(_categoryController.value);
        print(_notificationController.value);
        print(_monthController.value);
        print(_dayController.value);
        print(_yearController.value);

        _stateController.add(BirthdayState.READY);
      }).catchError((error) {
        print('ERROR => $error');
      });
  }

我从上面的打印中得到的输出是:

I/flutter (10907): Person Name
I/flutter (10907): 62999991234
I/flutter (10907): someemail@gmail.com
I/flutter (10907): Familiar
I/flutter (10907): No dia
I/flutter (10907): 3
I/flutter (10907): 25
I/flutter (10907): 1996

这是来自应用程序屏幕的打印:

【问题讨论】:

    标签: flutter dart bloc


    【解决方案1】:

    看起来您错过了带有值的 TextField 初始化,这就是字段缺少数据的原因。 TextField 可以通过它的 controller 属性来初始化:

    ...
              return TextField(
                controller: TextEditingController(text: snapshot.data), // <--
                keyboardType: type != null ? type : null,
                onChanged: onChanged,
    ...
    

    因此,在 StreamBuilder 中以这种方式初始化文本字段可能不是最好的方法。根据您的要求,最好:

    1. 使 InputField 小部件有状态
    2. 在 InputField 的 State 类中声明 TextEditingController 字段
    3. 订阅 State 中的流并在每次流发出数据时更新控制器的 valuetext 属性。

    【讨论】:

    • 所以让 InputField 小部件有状态是最好的方法,对吧?我想在以编辑模式打开时将所有 InputFields 设为只读,如果用户单击“编辑”按钮,它将启用编辑,但是如果用户决定取消更改,则在更改某些值之后,我该如何返回值而不将它们存储在其他变量中?
    • 是的,我认为将字段转换为 StatefulWidget 是个好主意。它使您在实现所需要求方面具有更大的灵活性:例如恢复更改。要提出正确的建议,需要更好地了解您的要求:例如用户如何取消更改?简单的解决方案是将数据副本存储在另一个 State 字段中,然后在用户取消请求时使用它来恢复。
    • 所以要存储这个副本,我需要使用另一组流来存储更改的值,如果用户取消编辑,我可以恢复到存储原始值的第一组流吗?抱歉,如果我不太明白你说的话,我对 Flutter 开发有点陌生。
    • 我尝试将 InputField 更改为 Stateful 但它不起作用:/ 使用 TextEditingController 确实有效
    猜你喜欢
    • 2019-12-10
    • 1970-01-01
    • 2020-10-17
    • 1970-01-01
    • 1970-01-01
    • 2021-02-10
    • 2020-09-26
    • 2020-09-09
    • 1970-01-01
    相关资源
    最近更新 更多