[Solved] How require in a textformfield that the string has uppercase, lowercase and numbers and validate it?

Question

Asked by santiagobueras on December 30, 2021 (source).

I'm benninger in flutter and I need in my TextFormField a filter, if don't have at least one uppercase or lowercase letter, or a number, show error in a text in red with: "Should be have a number", for example.

This is my form (a part, with the relevant parts: textformfields and the voidinitState()):

class _RegisterState extends State<Register> {
  bool showError = false;
  bool isButtonActive = true;
  String message = '';
  String messagcell = '';

  @override
  void initState() {
    super.initState();

    correo.addListener(() {
      final isButtonActive = correo.text.isNotEmpty;

      setState(() => this.isButtonActive = isButtonActive);
    });

    celular.addListener(() {
      final isButtonActive = correo.text.isNotEmpty;

      setState(() => this.isButtonActive = isButtonActive);
    });

    passwd2.addListener(() {
      setState(() {
        showError = passwd2.text.isEmpty
            ? false
            : passwd.text.trim() != passwd2.text.trim() ||
                passwd2.text.length < 8;
      });
      final isButtonActive = correo.text.isNotEmpty;
      setState(() => this.isButtonActive = isButtonActive);
    });
    passwd.addListener(() {
      setState(() {
        showError = passwd.text.isEmpty
            ? false
            : passwd.text.trim() != passwd2.text.trim() ||
                passwd.text.length < 8;
      });
      final isButtonActive = correo.text.isNotEmpty;
      setState(() => this.isButtonActive = isButtonActive);
    });
  }

  void validateEmail(String enteredEmail) {
    if (EmailValidator.validate(enteredEmail)) {
      setState(() {
        message = '';
      });
    } else {
      setState(() {
        message = ('Por favor ingrese un correo válido');
      });
    }
  }

  void validateCell(String enteredCell) {
    if (enteredCell.length >= 9 && enteredCell.length <= 15) {
      setState(() {
        messagcell = '';
      });
    } else if (enteredCell.length < 9) {
      setState(() {
        messagcell = ('Por favor ingrese un número válido');
      });
    }
  }

  TextEditingController correo = TextEditingController();
  TextEditingController celular = TextEditingController();
  TextEditingController passwd = TextEditingController();
  TextEditingController passwd2 = TextEditingController();

  @override
  void dispose() {
    correo.dispose();
    passwd.dispose();
    celular.dispose();
    passwd2.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        height: 900,
        child: Card(
          color: Colors.blue[200],
          child: Column(
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(
                  'Register',
                  style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: TextFormField(
                  inputFormatters: <TextInputFormatter>[
                    FilteringTextInputFormatter.allow(
                        RegExp("[[email protected]_0-9a-zA-Z]")),
                    new LengthLimitingTextInputFormatter(36),
                  ],
                  decoration: InputDecoration(
                      labelText: 'Correo',
                      prefixIcon: Icon(Icons.mail),
                      focusedBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(8),
                        borderSide: BorderSide(color: Colors.blue, width: 2.0),
                      ),
                      enabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(8),
                        borderSide:
                            BorderSide(color: Colors.blue[900], width: 2.0),
                      )),
                  onChanged: (enteredEmail) => validateEmail(enteredEmail),
                  controller: correo,
                ),
              ),
              Text(
                message,
                style: TextStyle(color: Colors.red),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: TextFormField(
                  // maxLength: 12,
                  inputFormatters: <TextInputFormatter>[
                    FilteringTextInputFormatter.digitsOnly,
                    new LengthLimitingTextInputFormatter(15),
                  ],
                  keyboardType: TextInputType.number,
                  validator: (value) {
                    final intNumber = int.tryParse(value);
                    if (intNumber != null && intNumber <= 9) {
                      return null;
                    }
                    return 'Ingrese un número válido';
                  },
                  decoration: InputDecoration(
                    labelText: 'Celular',
                    prefixIcon: Icon(Icons.person),
                    focusedBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide: BorderSide(color: Colors.blue, width: 2.0),
                    ),
                    enabledBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide:
                          BorderSide(color: Colors.blue[900], width: 2.0),
                    ),
                  ),
                  onChanged: (enteredCell) => validateCell(enteredCell),
                  controller: celular,
                  textInputAction: TextInputAction.done,
                ),
              ),
              Text(
                messagcell,
                style: TextStyle(color: Colors.red),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: TextFormField(
                  obscureText: true,
                  inputFormatters: <TextInputFormatter>[
                    FilteringTextInputFormatter.allow(RegExp("[-0-9a-zA-Z]")),
                    new LengthLimitingTextInputFormatter(16),
                  ],
                  decoration: InputDecoration(
                    labelText: 'Contraseña',
                    prefixIcon: Icon(Icons.lock),
                    focusedBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide: BorderSide(color: Colors.blue, width: 2.0),
                    ),
                    enabledBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide:
                          BorderSide(color: Colors.blue[900], width: 2.0),
                    ),
                  ),
                  controller: passwd,
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: TextFormField(
                  obscureText: true,
                  inputFormatters: <TextInputFormatter>[
                    FilteringTextInputFormatter.allow(RegExp("[-0-9a-zA-Z]")),
                    new LengthLimitingTextInputFormatter(16),
                  ],
                  decoration: InputDecoration(
                    labelText: 'Repita contraseña',
                    prefixIcon: Icon(Icons.lock),
                    focusedBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide: BorderSide(color: Colors.blue, width: 2.0),
                    ),
                    enabledBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide:
                          BorderSide(color: Colors.blue[900], width: 2.0),
                    ),
                  ),
                  controller: passwd2,
                ),
              ),
              if (showError)
                const Text(
                  "Las contraseñas no son válidas",
                  style: TextStyle(color: Colors.red),
                ),
              Row(
                children: <Widget>[
                  Expanded(
                    child: ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        onSurface: Colors.blue,
                      ),
                      child: Text('Register',
                          style: TextStyle(
                              fontSize: 20,
                              fontWeight: FontWeight.bold,
                              color: Colors.white)),
                      onPressed: () => [
                        if (isButtonActive)
                          {
                            setState(() => isButtonActive = false),
                          }
                        else
                          {null},
                        register(), //REGISTER FUNCTION
                        setState(() {})
                      ],
                    ),
                  ),
                  //LOGIN BUTTON
                  Expanded(
                    child: ElevatedButton(
                      // color: Colors.amber[100],
                      child: Text('Login',
                          style: TextStyle(
                              fontSize: 20,
                              fontWeight: FontWeight.bold,
                              color: Colors.black)),
                      onPressed: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => MyHomePage(),
                          ),
                        );
                      },
                    ),
                  ),
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

In my TextFormFields have inputFormatters, for example:

inputFormatters: <TextInputFormatter>[
                    FilteringTextInputFormatter.allow(
                        RegExp("[[email protected]_0-9a-zA-Z]")),
                    new LengthLimitingTextInputFormatter(36),
                  ],

I need a function for detect in realtime the absence of any letter in uppercase, lowercase or numeric character. I don't know how make it. Please help me, thanks.

Answer

Question answered by mmcdon20 (source).

You can use the validator parameter on TextFormField. Your TextFormField will need to be inside a Form widget for this to work. Pass a key to your Form widget and call formKey.currentState?.validate() in the onChanged callback of your TextFormField.

Try out this code below:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(),
      home: const Scaffold(
        body: Center(
          child: CustomForm(),
        ),
      ),
    );
  }
}

class CustomForm extends StatefulWidget {
  const CustomForm({Key? key}) : super(key: key);

  @override
  _CustomFormState createState() => _CustomFormState();
}

class _CustomFormState extends State<CustomForm> {
  final formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Form(
      key: formKey,
      child: Padding(
        padding: const EdgeInsets.all(50),
        child: TextFormField(
          onChanged: (v) => formKey.currentState?.validate(),
          validator: (v) {
            String? message;
            if (!RegExp(".*[0-9].*").hasMatch(v ?? '')) {
              message ??= '';
              message += 'Input should contain a numeric value 1-9. ';
            }
            if (!RegExp('.*[a-z].*').hasMatch(v ?? '')) {
              message ??= '';
              message += 'Input should contain a lowercase letter a-z. ';
            }
            if (!RegExp('.*[A-Z].*').hasMatch(v ?? '')) {
              message ??= '';
              message += 'Input should contain an uppercase letter A-Z. ';
            }
            return message;
          },
        ),
      ),
    );
  }
}

Edit: You can also use a key directly on the TextFormField, if you want to only validate that particular field.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(),
      home: const Scaffold(
        body: Center(
          child: CustomForm(),
        ),
      ),
    );
  }
}

class CustomForm extends StatefulWidget {
  const CustomForm({Key? key}) : super(key: key);

  @override
  _CustomFormState createState() => _CustomFormState();
}

class _CustomFormState extends State<CustomForm> {
  final formKey = GlobalKey<FormState>();
  final passwordFieldKey = GlobalKey<FormFieldState>();

  @override
  Widget build(BuildContext context) {
    return Form(
      key: formKey,
      child: Padding(
        padding: const EdgeInsets.all(50),
        child: TextFormField(
          key: passwordFieldKey,
          onChanged: (v) => passwordFieldKey.currentState?.validate(),
          validator: (v) {
            String? message;
            if (!RegExp(".*[0-9].*").hasMatch(v ?? '')) {
              message ??= '';
              message += 'Input should contain a numeric value 1-9. ';
            }
            if (!RegExp('.*[a-z].*').hasMatch(v ?? '')) {
              message ??= '';
              message += 'Input should contain a lowercase letter a-z. ';
            }
            if (!RegExp('.*[A-Z].*').hasMatch(v ?? '')) {
              message ??= '';
              message += 'Input should contain an uppercase letter A-Z. ';
            }
            return message;
          },
        ),
      ),
    );
  }
}
DART FLUTTER
SHARE: