Flutter未填文本框时SnackBar不显示的问题求助
问题解决:表单验证不触发提示SnackBar的问题
测试时故意留空部分文本框,未出现任何错误提示,尤其是SnackBar完全不显示,调整代码后仍无法解决,相关代码片段如下:
class _SignUpPageState extends State<SignUpPage> { // Focus nodes for email, password, and retyping passwords final FocusNode emailFocus = FocusNode(); final FocusNode passwordFocus = FocusNode(); final FocusNode retypePasswordFocus = FocusNode(); // Bool values for obscuring text bool _obscureTextPassword = true; bool _obscureTextConfirmPassword = true; // Manages the state of the form final _formKey = GlobalKey<FormState>(); // Refers to the name of the TextEditingController class TextEditingController _emailController = TextEditingController(); TextEditingController _passwordController = TextEditingController(); TextEditingController _retypePasswordController = TextEditingController(); @override void dispose() { // Clean up the controller when the widget is disposed. emailFocus.dispose(); passwordFocus.dispose(); retypePasswordFocus.dispose(); super.dispose(); } // Initializing these TextEditingController objects @override void initState() { super.initState(); _emailController = TextEditingController(); _passwordController = TextEditingController(); _retypePasswordController = TextEditingController(); } void _toggleSignUp() { setState(() { _obscureTextPassword = !_obscureTextPassword; }); } void _toggleSignUpConfirm() { setState(() { _obscureTextConfirmPassword = !_obscureTextConfirmPassword; }); } @override Widget build(BuildContext context) { return Scaffold( body: Form( key: _formKey, child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color.fromRGBO(73, 77, 235, 1.0), Color.fromRGBO(75, 0, 130, 0.8) ] ), ), child: Padding( padding: const EdgeInsets.all(10.0), child: ListView( children: [ SizedBox(height: 60,), Center( child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Sign Up", style: TextStyle(fontSize: 20, decoration: TextDecoration.none, color: Colors.white),), ), ), SizedBox(height: 40,), Column( children: [ Container( height: 45, width: 350, padding: EdgeInsets.fromLTRB(30, 0, 30, 0), child: TextFormField( keyboardType: TextInputType.emailAddress, focusNode: emailFocus, autofocus: true, textInputAction: TextInputAction.next, decoration: InputDecoration( hintText: "Enter Your Email Address", border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), ), contentPadding: EdgeInsets.all(30), ), controller: _emailController, validator: (String? value) { if (value == null || value.isEmpty) { return "Please enter some text"; } return null; }, ), ), SizedBox(height: 40,), Container( height: 45, width: 350, padding: EdgeInsets.fromLTRB(30, 0, 30, 0), child: TextFormField( keyboardType: TextInputType.visiblePassword, focusNode: passwordFocus, obscureText: _obscureTextPassword, textInputAction: TextInputAction.next, decoration: InputDecoration( hintText: "Password", border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), ), contentPadding: EdgeInsets.all(30), ), controller: _passwordController, ), ), SizedBox(height: 40,), Container( height: 45, width: 350, padding: EdgeInsets.fromLTRB(30, 0, 30, 0), child: TextFormField( keyboardType: TextInputType.visiblePassword, obscureText: _obscureTextConfirmPassword, textInputAction: TextInputAction.done, decoration: InputDecoration( hintText: "Retype Password", border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), ), contentPadding: EdgeInsets.all(30), ), controller: _retypePasswordController, ), ), ], ), SizedBox(height: 60,), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 110, vertical: 25), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20) ) ), child: const Text( 'Next', style: TextStyle(fontSize: 12), ), onPressed: () { if (_formKey.currentState!.validate()) { final snackBar = SnackBar( /// need to set following properties for best effect of awesome_snackbar_content elevation: 5, behavior: SnackBarBehavior.floating, backgroundColor: Colors.transparent, content: AwesomeSnackbarContent( title: 'On Snap!', message: 'This is an example error message that will be shown in the body of snackbar!', /// change contentType to ContentType.success, ContentType.warning or ContentType.help for variants contentType: ContentType.failure, ), ); } else { Navigator.push( context, MaterialPageRoute( builder: (context) => const TabNav()), ); } }, ), ], ), ), ), ), ); } }
问题根源分析
- 表单验证逻辑颠倒:
validate()返回true表示验证通过,此时应该执行页面跳转;而验证不通过(返回false)时才需要触发错误提示SnackBar,原代码逻辑完全相反 - SnackBar创建后未调用显示方法:仅创建
SnackBar实例但未执行ScaffoldMessenger.of(context).showSnackBar(snackBar),导致SnackBar从未被渲染 - 密码与确认密码输入框缺失校验逻辑:这两个
TextFormField未配置validator,留空时不会触发表单验证,导致表单整体验证结果不符合预期
修复后的完整代码
class _SignUpPageState extends State<SignUpPage> { // Focus nodes for email, password, and retyping passwords final FocusNode emailFocus = FocusNode(); final FocusNode passwordFocus = FocusNode(); final FocusNode retypePasswordFocus = FocusNode(); // Bool values for obscuring text bool _obscureTextPassword = true; bool _obscureTextConfirmPassword = true; // Manages the state of the form final _formKey = GlobalKey<FormState>(); // TextEditingController instances late TextEditingController _emailController; late TextEditingController _passwordController; late TextEditingController _retypePasswordController; @override void dispose() { // Clean up all resources emailFocus.dispose(); passwordFocus.dispose(); retypePasswordFocus.dispose(); _emailController.dispose(); _passwordController.dispose(); _retypePasswordController.dispose(); super.dispose(); } @override void initState() { super.initState(); _emailController = TextEditingController(); _passwordController = TextEditingController(); _retypePasswordController = TextEditingController(); } void _toggleSignUp() { setState(() { _obscureTextPassword = !_obscureTextPassword; }); } void _toggleSignUpConfirm() { setState(() { _obscureTextConfirmPassword = !_obscureTextConfirmPassword; }); } @override Widget build(BuildContext context) { return Scaffold( body: Form( key: _formKey, child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color.fromRGBO(73, 77, 235, 1.0), Color.fromRGBO(75, 0, 130, 0.8) ] ), ), child: Padding( padding: const EdgeInsets.all(10.0), child: ListView( children: [ SizedBox(height: 60,), Center( child: Padding( padding: const EdgeInsets.all(8.0), child: Text("Sign Up", style: TextStyle(fontSize: 20, decoration: TextDecoration.none, color: Colors.white),), ), ), SizedBox(height: 40,), Column( children: [ Container( height: 45, width: 350, padding: EdgeInsets.fromLTRB(30, 0, 30, 0), child: TextFormField( keyboardType: TextInputType.emailAddress, focusNode: emailFocus, autofocus: true, textInputAction: TextInputAction.next, decoration: InputDecoration( hintText: "Enter Your Email Address", border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), ), contentPadding: EdgeInsets.all(30), ), controller: _emailController, validator: (String? value) { if (value == null || value.isEmpty) { return "Please enter your email"; } // 可添加邮箱格式校验 final emailRegExp = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); if (!emailRegExp.hasMatch(value)) { return "Please enter a valid email"; } return null; }, ), ), SizedBox(height: 40,), Container( height: 45, width: 350, padding: EdgeInsets.fromLTRB(30, 0, 30, 0), child: TextFormField( keyboardType: TextInputType.visiblePassword, focusNode: passwordFocus, obscureText: _obscureTextPassword, textInputAction: TextInputAction.next, decoration: InputDecoration( hintText: "Password", border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), ), contentPadding: EdgeInsets.all(30), ), controller: _passwordController, validator: (String? value) { if (value == null || value.isEmpty) { return "Please enter your password"; } if (value.length < 6) { return "Password must be at least 6 characters"; } return null; }, ), ), SizedBox(height: 40,), Container( height: 45, width: 350, padding: EdgeInsets.fromLTRB(30, 0, 30, 0), child: TextFormField( keyboardType: TextInputType.visiblePassword, obscureText: _obscureTextConfirmPassword, textInputAction: TextInputAction.done, decoration: InputDecoration( hintText: "Retype Password", border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), ), contentPadding: EdgeInsets.all(30), ), controller: _retypePasswordController, validator: (String? value) { if (value == null || value.isEmpty) { return "Please retype your password"; } if (value != _passwordController.text) { return "Passwords do not match"; } return null; }, ), ), ], ), SizedBox(height: 60,), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 110, vertical: 25), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20) ) ), child: const Text( 'Next', style: TextStyle(fontSize: 12), ), onPressed: () { if (_formKey.currentState!.validate()) { // 验证通过,执行页面跳转 Navigator.push( context, MaterialPageRoute(builder: (context) => const TabNav()), ); } else { // 验证不通过,显示错误提示SnackBar final snackBar = SnackBar( elevation: 5, behavior: SnackBarBehavior.floating, backgroundColor: Colors.transparent, content: AwesomeSnackbarContent( title: 'Error!', message: 'Please fill in all required fields correctly', contentType: ContentType.failure, ), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); } }, ), ], ), ), ), ), ); } }
内容的提问来源于stack exchange,提问作者Jazil Zaim
相关产品推荐
相关产品推荐

