1

我编写了示例代码,如果用户单击登录按钮,当 TextFormField 内没有字符串时显示一条消息。它将在 TextFormField 下方显示一条消息,提醒用户输入电子邮件。我想要做的是在其他地方显示该消息,可能在登录按钮下方。

理想情况下,我有一个登录名和密码文本字段,而不是在每个文本字段下方显示消息,我想在登录按钮下方显示消息。任何帮助,将不胜感激!我查看了文本控制器,但不知道如何实现,以便如果验证器通过时没有消息,它会使小部件保持相同的高度,但仅在验证器失败时扩展高度显示消息。

import 'package:flutter/material.dart';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: TextFieldExample(),
        ),
      ),
    );
  }
}

class TextFieldExample extends StatefulWidget {
  @override
  _TextFieldExampleState createState() => _TextFieldExampleState();
}

class _TextFieldExampleState extends State<TextFieldExample> {
  final _formKey = GlobalKey<FormState>();
  String email = '';

  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.symmetric(vertical: 5),
      padding: EdgeInsets.symmetric(horizontal: 15),
      decoration: BoxDecoration(borderRadius: BorderRadius.circular(29)),
      child: Form(
        key: _formKey,
        child: Column(children: <Widget>[
          TextFormField(
            validator: (value) => value.isEmpty ? 'Enter an email' : null,
            onChanged: (value) {
              setState(() => email = value);
            },
            decoration:
                InputDecoration(icon: Icon(Icons.person), hintText: "Email"),
          ),
          Container(
              margin: EdgeInsets.symmetric(vertical: 5),
              child: ClipRRect(
                  borderRadius: BorderRadius.circular(29),
                  child: RaisedButton(
                      padding: EdgeInsets.symmetric(vertical: 10),
                      onPressed: () async {
                        if (_formKey.currentState.validate()) {
                          //sign in;
                        }
                      },
                      child: Text("Login")))),
        ]),
      ),
    );
  }
}

定位示例

4

2 回答 2

2

感谢 LOfG 的帮助(在 Flutter 中,我正在尝试基于文本控制器动态更新我的列小部件。我该怎么做?),我得到了答案的启发。

以下代码执行以下操作:

  • 使用 StreamBuilder 重建每个新事件
  • 仅当验证失败时在登录按钮下方显示消息,否则返回空容器
  • 当用户单击用户名或密码时,如果不为空,则消息消失
  • 使用 snapshot.data 登录用户(可以在此处使用 Provider 和 FirebaseAuth)

我想要这个功能的原因是在某个位置显示错误消息,而不是在文本字段中。

import 'package:flutter/material.dart';
import 'dart:async';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: TextFieldExample(),
        ),
      ),
    );
  }
}

class TextFieldExample extends StatefulWidget {
  @override
  _TextFieldExampleState createState() => _TextFieldExampleState();
}

class _TextFieldExampleState extends State<TextFieldExample> {
  @override
  void dispose() {
    _username.close();
    _password.close();
    super.dispose();
  }

  String validatorMessage;
  bool validate = false; //will be true if the user clicked in the    login button
  final _username = StreamController<String>(); //stream to   validate   the text
  final _password = StreamController<String>();

  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
          margin: EdgeInsets.symmetric(vertical: 5),
          padding: EdgeInsets.symmetric(horizontal: 15),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(29),
          ),
          //expose streambuilder to the column widget to use on multiple widgets
          child: StreamBuilder<String>(
              initialData: '',
              stream: _username.stream,
              builder: (context, usernameSnapshot) {
                return StreamBuilder<String>(
                    initialData: '',
                    stream: _password.stream,
                    builder: (context, passwordSnapshot) {
                      return Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          TextField(
                            onChanged: _username.add, //everytime the text changes a new value will be added to the stream
                            decoration: InputDecoration(
                              icon: Icon(Icons.person),
                              hintText: "Email",
                            ),
                          ),
                          TextField(
                            obscureText: true,
                            onChanged: _password.add, //everytime the text changes a new value will be added to the stream
                            decoration: InputDecoration(
                              icon: Icon(Icons.visibility),
                              hintText: "Password",
                            ),
                          ),
                          Container(
                            margin: EdgeInsets.symmetric(vertical: 5),
                            child: ClipRRect(
                              borderRadius: BorderRadius.circular(29),
                              child: RaisedButton(
                                padding: EdgeInsets.symmetric(vertical: 10),
                                //when user presses button, validate turns to true and we check snapshots to get the data for the entries
                                onPressed: () async {
                                  if (usernameSnapshot.data.isNotEmpty && passwordSnapshot.data.isNotEmpty) {
                                    try {
                                      //sign in with usernameSnapshot.data and passwordSnapshot.data
                                    } catch (e) {
                                      print(e);
                                    }
                                    validate = true;
                                  } else {
                                    setState(() {
                                      validate = true;
                                    });
                                  }
                                },
                                child: Text("Login"),
                              ),
                            ),
                          ),
                          if (usernameSnapshot.data.isEmpty &&validate == true) //checking the stream and if the user clicked in the button
                            Container(
                              alignment: Alignment.center,
                              child: Text(
                                'Check your e-mail and password.',
                                style: TextStyle(
                                  color: Colors.red,
                                ),
                              ),
                            )
                          else
                            Container()
                        ],
                      );
                    });
              })),
    );
  }
}

小部件

于 2020-08-31T07:32:53.017 回答
1

考虑使用 aRow而不是Column

import 'package:flutter/material.dart';
    
    final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            body: Center(
              child: TextFieldExample(),
            ),
          ),
        );
      }
    }
    
    class TextFieldExample extends StatefulWidget {
      @override
      _TextFieldExampleState createState() => _TextFieldExampleState();
    }
    
    class _TextFieldExampleState extends State<TextFieldExample> {
      final _formKey = GlobalKey<FormState>();
      String email = '';
    
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.symmetric(vertical: 5),
          padding: EdgeInsets.symmetric(horizontal: 15),
          decoration: BoxDecoration(borderRadius: BorderRadius.circular(29)),
          child: Form(
            key: _formKey,
            child: Row(children: <Widget>[
              Flexible(
    
                child: TextFormField(
                  validator: (value) => value.isEmpty ? 'Enter an email' : null,
                  onChanged: (value) {
                    setState(() => email = value);
                  },
                  decoration:
                  InputDecoration(icon: Icon(Icons.person), hintText: "Email"),
                ),
              ),
              Expanded(
                flex: 0,
                child: Container(
                  margin: EdgeInsets.symmetric(vertical: 5),
                  child: ClipRRect(
                      borderRadius: BorderRadius.circular(29),
                      child: RaisedButton(
                          padding: EdgeInsets.symmetric(vertical: 10),
                          onPressed: () async {
                            if (_formKey.currentState.validate()) {
                              //sign in;
                            }
                          },
                          child: Text("Login")))),)
    
            ]),
          ),
        );
      }
    }
于 2020-08-30T08:40:29.663 回答