我想检查以表格形式输入的邮政编码是否有效。我正在使用this进行验证。
如果用户输入的邮政编码不正确,我想向用户展示一下。
Widget formUI() {
return new Column(
children: <Widget>[
new TextFormField(
decoration: new InputDecoration(hintText: 'Email ID'),
keyboardType: TextInputType.emailAddress,
maxLength: 32,
validator: Validation.validateEmail,
onSaved: (String val) {
email = val;
}),
new TextFormField(
decoration: new InputDecoration(hintText: 'Password'),
maxLength: 32,
validator: Validation.validateName,
onSaved: (String val) {
password = val;
print("name is now $password from $val");
},
),
//I WANT TO VALIDATE THE POSTCODE
new TextFormField(
decoration: new InputDecoration(hintText: 'Postcode'),
maxLength: 32,
validator: checkThePostCode(),
onSaved: (String val) {
postcode = val;
print("name is now $postcode from $val");
},
),
new SizedBox(height: 15.0),
new RaisedButton(
onPressed: () {
if (_sendToServer()) {
_showSnackBar("Successfully Signed-up!");
}
},
child: new Text('Sign up'),
),
],
);
}
而且,我有下面的代码可以进行API调用。我想在表格上显示错误响应,例如“无效的邮政编码”
Future<String> checkThePostCode() async {
final response =
await http.get('https://api.postcodes.io/postcodes/$postcode');
if (response.statusCode != 200) {
var map = ErrorMessage.fromJson(json.decode(response.body));
postcodeValidator = map.error;
return map.error;
}
return null;
}
答案 0 :(得分:0)
validator
属性所面临的问题是,如果邮政编码不正确,则必须同步返回String
,该TextEditingController
将显示。
根据您的情况,这是不可能的,因为您需要等待网络通话完成。
一种解决方法是将TextFormField
附加到您的class PostcodeValidator extends StatefulWidget {
@override
_PostcodeValidatorState createState() => _PostcodeValidatorState();
}
class _PostcodeValidatorState extends State<PostcodeValidator> {
String email;
String password;
String postcode;
bool hasValidPostcode = false;
bool get canSubmitForm => hasValidPostcode;
String postcodeErrorMessage;
final TextEditingController _postcodeController = TextEditingController();
void checkPostalCode(String postcode) async {
final response = await http.get('https://api.postcodes.io/postcodes/$postcode');
if (response.statusCode != 200) {
setState(() {
hasValidPostcode = false;
postcodeErrorMessage = json.decode(response.body)["error"];
});
}
if (response.statusCode == 200) {
setState(() {
hasValidPostcode = true;
});
}
return null;
}
@override
void initState() {
super.initState();
_postcodeController.addListener(() {
final postcodeInput = _postcodeController.value.text;
if (postcodeInput.isNotEmpty) {
checkPostalCode(postcodeInput);
}
});
}
@override
Widget build(BuildContext context) => Column(
children: <Widget>[
TextFormField(
controller: _postcodeController,
decoration: InputDecoration(hintText: 'Postcode'),
maxLength: 32,
autovalidate: true,
validator: (_) => hasValidPostcode ? null : postcodeErrorMessage,
onSaved: (String val) {
postcode = val;
print("name is now $postcode from $val");
},
),
RaisedButton(
onPressed: canSubmitForm ? _sendToServer : null,
child: new Text('Sign up'),
),
],
);
void _sendToServer() {
print("Submitting");
}
}
上。
每次输入值时,都会向API发出网络请求。 (这不理想,您可以在此处使用反跳功能,以免对API造成过多垃圾邮件)
只要所有字段均无效,提交按钮将被禁用。
概念证明代码:
{{1}}
答案 1 :(得分:0)
您可以使用flutter_form_bloc,它支持异步验证器,并且除了提供其他优点之外,还可以将反跳时间设置为不对API进行垃圾邮件处理。
因此,当您在TextFieldBloc
内创建SignUpFormBloc
时,可以设置去抖动时间:
class SignUpFormBloc extends FormBloc<String, String> {
...
final postcodeField = TextFieldBloc(
asyncValidatorDebounceTime: Duration(milliseconds: 1000),
);
...
}
然后在构造函数中,您可以添加异步验证器:
...
SignUpFormBloc() {
postcodeField.addAsyncValidators([_isValidPostcode]);
}
...
如果您不想在键入时进行自动验证,而仅在提交表单时,可以在autoValitade
构造函数中将false
设置为super
:
...
SignUpFormBloc() : super(autoValidate: false) {
postcodeField.addAsyncValidators([_isValidPostcode]);
}
...
或者,如果您要自动验证并且仅在提交表单时检查异步验证器,则可以检查以下答案:https://stackoverflow.com/a/58295440/11768068
这是您可以运行的示例:
dependencies:
form_bloc: ^0.5.2
flutter_form_bloc: ^0.4.4
http: ^0.12.0+2
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:form_bloc/form_bloc.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MaterialApp(home: SignUpScreen()));
class SignUpFormBloc extends FormBloc<String, String> {
final emailField = TextFieldBloc(
validators: [Validators.email],
);
final passwordField = TextFieldBloc(
validators: [Validators.passwordMin6Chars],
);
final postcodeField = TextFieldBloc(
asyncValidatorDebounceTime: Duration(milliseconds: 500),
);
@override
List<FieldBloc> get fieldBlocs => [emailField, passwordField, postcodeField];
SignUpFormBloc() {
postcodeField.addAsyncValidators([_isValidPostcode]);
}
Future<String> _isValidPostcode(String postcode) async {
try {
final response =
await http.get('https://api.postcodes.io/postcodes/$postcode');
print(response.body);
if (response.statusCode != 200) {
return json.decode(response.body)['error'];
}
} catch (e) {
return 'There is no Internet connection';
}
return null;
}
@override
Stream<FormBlocState<String, String>> onSubmitting() async* {
// Form logic...
try {
// Get the fields values:
print(postcodeField.value);
print(emailField.value);
print(postcodeField.value);
await Future<void>.delayed(Duration(seconds: 2));
yield currentState.toSuccess();
} catch (e) {
yield currentState.toFailure(
'Fake error, please continue testing the async validation.');
}
}
}
class SignUpScreen extends StatefulWidget {
SignUpScreen({Key key}) : super(key: key);
_SignUpScreenState createState() => _SignUpScreenState();
}
class _SignUpScreenState extends State<SignUpScreen> {
SignUpFormBloc _simpleFormBloc;
@override
void initState() {
super.initState();
_simpleFormBloc = SignUpFormBloc();
}
@override
void dispose() {
_simpleFormBloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Sign up')),
body: FormBlocListener(
formBloc: _simpleFormBloc,
onSubmitting: (context, state) {
// Show the progress dialog
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => WillPopScope(
onWillPop: () async => false,
child: Center(
child: Card(
child: Container(
width: 80,
height: 80,
padding: EdgeInsets.all(12.0),
child: CircularProgressIndicator(),
),
),
),
),
);
},
onSuccess: (context, state) {
// Hide the progress dialog
Navigator.of(context).pop();
// Navigate to success screen
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => SuccessScreen()));
},
onFailure: (context, state) {
// Hide the progress dialog
Navigator.of(context).pop();
// Show snackbar with the error
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(state.failureResponse),
backgroundColor: Colors.red[300],
),
);
},
child: ListView(
children: <Widget>[
TextFieldBlocBuilder(
textFieldBloc: _simpleFormBloc.emailField,
decoration: InputDecoration(labelText: 'Email ID'),
maxLength: 32,
keyboardType: TextInputType.emailAddress,
),
TextFieldBlocBuilder(
textFieldBloc: _simpleFormBloc.passwordField,
decoration: InputDecoration(labelText: 'Password'),
maxLength: 32,
),
TextFieldBlocBuilder(
textFieldBloc: _simpleFormBloc.postcodeField,
suffixButton: SuffixButton.circularIndicatorWhenIsAsyncValidating,
decoration: InputDecoration(labelText: 'Postcode'),
maxLength: 32,
errorBuilder: (context, error) {
// Here you can map your error codes
// if you want to use Localizations
switch (error) {
case ValidatorsError.requiredTextFieldBloc:
return 'Please enter a postcode.';
break;
default:
return error;
}
},
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: _simpleFormBloc.submit,
child: Center(child: Text('SIGN UP')),
),
),
],
),
),
);
}
}
class SuccessScreen extends StatelessWidget {
const SuccessScreen({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.green[300],
body: Center(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Icon(
Icons.sentiment_satisfied,
size: 100,
),
RaisedButton(
color: Colors.green[100],
child: Text('Go to home'),
onPressed: () => Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => SignUpScreen())),
)
],
),
),
),
);
}
}