如何在Flutter中使用Restful调用来验证textformfield中的值?

时间:2019-10-13 09:38:41

标签: flutter dart

我想检查以表格形式输入的邮政编码是否有效。我正在使用this进行验证。

如果用户输入的邮政编码不正确,我想向用户展示一下。

enter image description here

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;
}

2 个答案:

答案 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())),
              )
            ],
          ),
        ),
      ),
    );
  }
}