英文:
How to validate username TextFormField when incorrect in Flutter
问题
我正在尝试在登录按钮按下时验证我的表单。当所有字段都为空时,在客户端上可以正常工作,但是使用了我的开关/案例后似乎不起作用。我认为这可能与我的验证函数有关,但我无法确定。我认为布尔值在 MySOAPRequest
中设置正确,因为当我按下登录按钮然后更新公司或 PIN 字段时,错误消息显示在用户名字段上,但应该在登录按钮按下时显示。
登录页面带有文本字段和登录按钮
CustomTextField(
key: _customTextFieldKey,
companyController: _companyController,
usernameController: _usernameController,
pinController: _pinController,
),
const SizedBox(height: 20),
OutlinedButton(
onPressed: () {
FocusManager.instance.primaryFocus?.unfocus();
String lcomp = Globals().lcomp = _companyController.text.trim();
String luser = Globals().luser = _usernameController.text.trim();
String lpin = Globals().lpin = _pinController.text.trim();
if (lcomp.isNotEmpty &&
luser.isNotEmpty &&
lpin.isNotEmpty &&
!_customTextFieldKey.currentState!.hasError()) {
MySOAPRequest(
lcomp: lcomp,
luser: luser,
lpin: lpin,
onLoginSuccess: _saveCredentials,
).makeSOAPRequest(context);
} else {
_customTextFieldKey.currentState?.validate();
}
},
child: const Text('登录'),
),
MySOAPRequest 页面代码
final String lcomp;
final String luser;
final String lpin;
final Function(bool) onLoginSuccess; // 定义 onLoginSuccess 参数
MySOAPRequest({
required this.lcomp,
required this.luser,
required this.lpin,
required this.onLoginSuccess,
});
switch (logonState) {
case '1':
// 如果登录状态为 1,则导航到 Landing 页面
loginFlushbar(context);
navigateToSubPages(context);
onLoginSuccess(true);
break;
case '3':
flushbar(context, '不正确的用户名', '请再试一次或联系您的管理员,如果问题仍然存在。');
onLoginSuccess(false);
break;
default:
flushbar(context, '未知错误', '发生未知错误。请再试一次或联系您的管理员。');
Globals().usernameHasError = true;
Globals().errorText = '不正确的用户名';
Globals().customTextFieldKey.currentState?.validate();
onLoginSuccess(false);
break;
}
用户名字段文本
class CustomTextFieldState extends State<CustomTextField> {
final _formKey = GlobalKey<FormState>();
//final _usernameKey = Globals().customTextFieldKey;
String _company = '';
String _username = '';
String _pin = '';
bool _companyHasError = false;
bool _usernameHasError = false;
bool _pinHasError = false;
bool hasError() {
return _usernameHasError || _companyHasError || _pinHasError;
}
void validate() {
setState(() {
if (_formKey.currentState != null) {
_companyHasError =
!_formKey.currentState!.validate() || _company.isEmpty;
_usernameHasError =
!_formKey.currentState!.validate() || _username.isEmpty;
_pinHasError = !_formKey.currentState!.validate() || _pin.isEmpty;
}
});
}
@override
Widget build(BuildContext context) {
String lcomp = widget.companyController.text.trim();
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.disabled,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
// 公司文本字段
child: TextFormField(
controller: widget.companyController,
decoration: InputDecoration(
// ...
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入您的公司名称!';
}
return null;
},
onChanged: (value) {
setState(() {
_company = value;
_companyHasError = false;
});
},
onFieldSubmitted: (value) {
setState(() {
_companyHasError = !_formKey.currentState!.validate();
});
},
),
),
// 用户名文本字段
TextFormField(
controller: widget.usernameController,
//key: _usernameKey,
decoration: InputDecoration(
// ...
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入您的用户名!';
}
return null;
},
onChanged: (value) {
setState(() {
_username = value;
_usernameHasError = false;
Globals().usernameHasError = false;
});
},
onFieldSubmitted: (value) {
setState(() {
_usernameHasError = !_formKey.currentState!.validate();
});
},
),
// ...
],
),
);
}
}
以上是您提供的代码的翻译。如果您有其他问题或需要进一步的帮助,请告诉我。
英文:
I am trying to validate my form on login button press. It works perfectly fine on client side when all fields are empty however with my switch/case it does not appear to work. I believe it may be related to my validation function but I cannot pinpoint it. I believe the booleans are being set correctly in MySOAPRequest since when I press the login button then update the company or pin field the error message shows on the username field but it should show on login button press.
Login page with text fields and login button
CustomTextField(
key: _customTextFieldKey,
companyController: _companyController,
usernameController: _usernameController,
pinController: _pinController,
),
const SizedBox(height: 20),
OutlinedButton(
onPressed: () {
FocusManager.instance.primaryFocus?.unfocus();
String lcomp = Globals().lcomp =
_companyController.text.trim();
String luser = Globals().luser =
_usernameController.text.trim();
String lpin =
Globals().lpin = _pinController.text.trim();
if (lcomp.isNotEmpty &&
luser.isNotEmpty &&
lpin.isNotEmpty &&
!_customTextFieldKey.currentState!
.hasError()) {
MySOAPRequest(
lcomp: lcomp,
luser: luser,
lpin: lpin,
onLoginSuccess: _saveCredentials,
).makeSOAPRequest(context);
} else {
_customTextFieldKey.currentState?.validate();
}
},
child: const Text('Login'),
),
MySOAPRequest page code
final String lcomp;
final String luser;
final String lpin;
final Function(bool) onLoginSuccess; // define the onLoginSuccess parameter
MySOAPRequest({
required this.lcomp,
required this.luser,
required this.lpin,
required this.onLoginSuccess,
});
switch (logonState) {
case '1':
// Navigate to the Landing page if the logon state is 1
// ignore: use_build_context_synchronously
loginFlushbar(context);
// ignore: use_build_context_synchronously
navigateToSubPages(context);
onLoginSuccess(true);
break;
case '3':
// ignore: use_build_context_synchronously
flushbar(context, 'Incorrect User Name',
'Please try again or contact your administrator if this issue persists.');
onLoginSuccess(false);
break;
default:
// ignore: use_build_context_synchronously
flushbar(context, 'Unknown Error',
'An unknown error occurred. Please try again or contact your administrator.');
Globals().usernameHasError = true;
Globals().errorText = 'Incorrect User Name';
Globals().customTextFieldKey.currentState?.validate();
onLoginSuccess(false);
break;
}
Username field text
class CustomTextFieldState extends State<CustomTextField> {
final _formKey = GlobalKey<FormState>();
//final _usernameKey = Globals().customTextFieldKey;
String _company = '';
String _username = '';
String _pin = '';
bool _companyHasError = false;
bool _usernameHasError = false;
bool _pinHasError = false;
bool hasError() {
return _usernameHasError || _companyHasError || _pinHasError;
}
void validate() {
setState(() {
if (_formKey.currentState != null) {
_companyHasError =
!_formKey.currentState!.validate() || _company.isEmpty;
_usernameHasError =
!_formKey.currentState!.validate() || _username.isEmpty;
_pinHasError = !_formKey.currentState!.validate() || _pin.isEmpty;
}
});
}
@override
Widget build(BuildContext context) {
String lcomp = widget.companyController.text.trim();
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.disabled,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
//!--- COMPANY TEXT FIELD --- //
child: TextFormField(
controller: widget.companyController,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(left: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: ThemeClass.primaryColor,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Color(0x00232323)),
),
suffixIcon: const Icon(Icons.corporate_fare_outlined,
color: ThemeClass.primaryColor),
labelText: "Company Name",
labelStyle: const TextStyle(
fontSize: 14.0,
decoration: TextDecoration.none,
),
hintText: 'Enter Company Name',
hintStyle: const TextStyle(
fontSize: 14.0,
),
errorText: _companyHasError
? 'Please enter a valid company name!'
: ((_company.isNotEmpty &&
!Globals().resultList.contains(lcomp))
? 'Please enter a valid company name!'
: null),
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your company name!';
}
return null;
},
onChanged: (value) {
setState(() {
_company = value;
_companyHasError = false;
});
},
onFieldSubmitted: (value) {
setState(() {
_companyHasError = !_formKey.currentState!.validate();
});
},
),
),
//!--- END COMPANY TEXT FIELD --- //
//!--- USERNAME TEXT FIELD --- //
TextFormField(
controller: widget.usernameController,
//key: _usernameKey,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(left: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: ThemeClass.primaryColor,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Color(0x00232323)),
),
suffixIcon: const Icon(Icons.person_outlined,
color: ThemeClass.primaryColor),
labelText: "Username",
labelStyle: const TextStyle(
fontSize: 14.0,
decoration: TextDecoration.none,
),
hintText: 'Enter username',
hintStyle: const TextStyle(
fontSize: 14.0,
),
errorText: _usernameHasError
? 'Please enter a valid username!'
: Globals().usernameHasError
? Globals().errorText
: null,
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your username!';
}
return null;
},
onChanged: (value) {
setState(() {
_username = value;
_usernameHasError = false;
Globals().usernameHasError = false;
});
},
onFieldSubmitted: (value) {
setState(() {
_usernameHasError = !_formKey.currentState!.validate();
});
},
),
//!--- END USERNAME TEXT FIELD --- //
const SizedBox(height: 20),
//!--- PIN TEXT FIELD --- //
TextFormField(
controller: widget.pinController,
inputFormatters: [
LengthLimitingTextInputFormatter(6),
FilteringTextInputFormatter.digitsOnly
],
obscureText: true,
keyboardType: TextInputType.number,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(left: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: ThemeClass.primaryColor,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Color(0x00232323)),
),
suffixIcon: const Icon(Icons.pin_outlined,
color: ThemeClass.primaryColor),
labelText: "PIN",
labelStyle: const TextStyle(
fontSize: 14.0,
decoration: TextDecoration.none,
),
hintText: 'Enter PIN',
hintStyle: const TextStyle(
fontSize: 14.0,
),
errorText: _pinHasError
? 'Please enter a valid PIN!'
: _pin.length < 4 && _pin.isNotEmpty
? 'PIN is too short!'
: null,
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your PIN!';
}
return null;
},
onChanged: (value) {
setState(() {
_pin = value;
_pinHasError = false;
});
},
onFieldSubmitted: (value) {
setState(() {
_pinHasError = !_formKey.currentState!.validate();
});
},
),
//!--- END PIN TEXT FIELD --- //
],
),
);
}
}
</details>
# 答案1
**得分**: 1
I just saw something bad you are doing on the switch case,
ignore: use_build_context_synchronously
there is a reason why you should not call the build context asynchronously, it may have been unmounted since you passed it around
if you are using a stateful widget, which appears to be so, you can define your context-dependent functions outside the build widget like this
StatefulWidget {
void _loginFlushBar() => loginFlushbar(context);
void _navigateToSubPages => navigateToSubPages(context);
Widget build(BuildContext context) {
// Your widgets
}
}
<details>
<summary>英文:</summary>
i just saw something bad you are doing on the switch case,
ignore: use_build_context_synchronously
there is a reason why you should not call the build context asynchronously, it may have been unmounted since you passed it aroud
if you are using a stateful widget, which appears to be so, you can define your context dependendant functions outside the build widget like this
StatefulWidget{
void _loginFlushBar() => loginFlushbar(context);
void _navigateToSubPages => navigateToSubPages(context);
Widget build(Buildcontext context){
//Your widgets
}
}
</details>
# 答案2
**得分**: 1
将您的 `TextFormField` 包裹在一个 `Form` 小部件中,并为 `Form` 提供一个 `GlobalKey<FormState>` 作为其键。然后您可以使用 `_formKey.currentState!.validate()` 进行验证。
<details>
<summary>英文:</summary>
Wrap your `TextFormField` in a `Form` widget and provide a `GlobalKey<FormState>` to the `Form` as its key. Then you can validate it with `_formKey.currentState!.validate()`.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论