admin管理员组

文章数量:1406052

I am using a custom form text field widget. Its validation is working fine until i submit the form. but once i submit the form and if there is validation error in the field. then if i update and correct the field data, it remain shows error message and red borders. I have spend a lot of time but I am not able to find a solution please see it. And if someone knows how to fix it then let me know. Thank you

import 'package:flutter/material.dart';

class CustomTextField extends StatefulWidget {
  final String label;
  final TextEditingController? controller;
  final String? initialValue;
  final String? Function(String?)? validator;
  final void Function(String)? onChanged;
  final String inputType; // 'text', 'email', 'phone', etc.
  final bool isMandatory;

  const CustomTextField({
    super.key,
    required this.label,
    this.controller,
    this.initialValue,
    this.validator,
    this.onChanged,
    this.inputType = 'text',
    this.isMandatory = false,
  });

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

class _CustomTextFieldState extends State<CustomTextField> {
  bool _isValid = true;
  bool _isTouched = false;
  late FocusNode _focusNode;

  @override
  void initState() {
    super.initState();
    _focusNode = FocusNode();
    _focusNode.addListener(_onFocusChange); // Listen for focus change
  }

  @override
  void dispose() {
    _focusNode.removeListener(_onFocusChange); // Remove the listener
    _focusNode.dispose();
    super.dispose();
  }

  void _onFocusChange() {
    if (_focusNode.hasFocus) {
      // Reset validation when focus is gained
      setState(() {
        _isTouched = false;
        _isValid = true;
      });
    } else {
      // Validate when focus is lost (field is unfocused)
      _validate(widget.controller?.text ?? '');
    }
  }

  void _validate(String value) {
    setState(() {
      _isTouched = true;
      if (widget.isMandatory) {
        if (widget.inputType == 'email') {
          _isValid = _isValidEmail(value);
        } else if (widget.inputType == 'phone') {
          _isValid = _isValidPhone(value);
        } else if (widget.inputType == 'number') {
          _isValid = _isValidNumber(value);
        } else {
          _isValid = value.isNotEmpty;
        }
      } else {
        _isValid = true;
      }
    });
  }

  bool _isValidEmail(String email) {
    final regex = RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$');
    return regex.hasMatch(email);
  }

  bool _isValidPhone(String phone) {
    final regex = RegExp(r'^\+?[0-9]{10,15}$');
    return regex.hasMatch(phone);
  }

  bool _isValidNumber(String number) {
    final regex = RegExp(r'^\d+(\.\d{1,2})?$');
    return regex.hasMatch(number);
  }

  // Expose the validation state
  bool isValid() {
    return !_isTouched || _isValid;
  }

  String? _internalValidator(String? value) {
    _validate(value ?? '');
    if (!_isValid) {
      // Return custom error messages based on input type
      if (widget.inputType == 'email') {
        return 'Inserisci un indirizzo email valido.';
      } else if (widget.inputType == 'phone') {
        return 'Inserisci un numero di telefono valido.';
      } else if (widget.inputType == 'number') {
        return 'Inserisci un numero valido.';
      } else {
        return 'Il campo è obbligatorio.';
      }
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        TextFormField(
          controller: widget.controller,
          focusNode: _focusNode,
          onChanged: (value) {
            // Call _validate to update validation state as the user types
            _validate(value);
            if (widget.onChanged != null) widget.onChanged!(value); // Call the external onChanged if provided
          },
          keyboardType: _getKeyboardType(widget.inputType),
          decoration: InputDecoration(
            labelText: widget.label,
            labelStyle: const TextStyle(color: Colors.black),
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide(color: Colors.grey.shade300),
            ),
            enabledBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide(
                color: (widget.isMandatory && !_isValid && _isTouched)
                    ? Colors.red
                    : Colors.grey.shade300,
              ),
            ),
            focusedBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide(
                color: (widget.isMandatory && !_isValid && _isTouched)
                    ? Colors.red
                    : Colors.blue,
                width: 2,
              ),
            ),
            contentPadding:
                const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
          ),
          // Use the internal validator, and allow external override
          validator: (value) {
            if (widget.validator != null) {
              // If a custom validator is provided, use it
              return widget.validator!(value);
            }

            // Otherwise, use the internal validation logic
            return _internalValidator(value);
          },
        ),
        const SizedBox(height: 4),
      ],
    );
  }

  TextInputType _getKeyboardType(String type) {
    switch (type) {
      case 'email':
        return TextInputType.emailAddress;
      case 'phone':
        return TextInputType.phone;
      case 'number':
        return TextInputType.number;
      default:
        return TextInputType.text;
    }
  }
}

I am using a custom form text field widget. Its validation is working fine until i submit the form. but once i submit the form and if there is validation error in the field. then if i update and correct the field data, it remain shows error message and red borders. I have spend a lot of time but I am not able to find a solution please see it. And if someone knows how to fix it then let me know. Thank you

import 'package:flutter/material.dart';

class CustomTextField extends StatefulWidget {
  final String label;
  final TextEditingController? controller;
  final String? initialValue;
  final String? Function(String?)? validator;
  final void Function(String)? onChanged;
  final String inputType; // 'text', 'email', 'phone', etc.
  final bool isMandatory;

  const CustomTextField({
    super.key,
    required this.label,
    this.controller,
    this.initialValue,
    this.validator,
    this.onChanged,
    this.inputType = 'text',
    this.isMandatory = false,
  });

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

class _CustomTextFieldState extends State<CustomTextField> {
  bool _isValid = true;
  bool _isTouched = false;
  late FocusNode _focusNode;

  @override
  void initState() {
    super.initState();
    _focusNode = FocusNode();
    _focusNode.addListener(_onFocusChange); // Listen for focus change
  }

  @override
  void dispose() {
    _focusNode.removeListener(_onFocusChange); // Remove the listener
    _focusNode.dispose();
    super.dispose();
  }

  void _onFocusChange() {
    if (_focusNode.hasFocus) {
      // Reset validation when focus is gained
      setState(() {
        _isTouched = false;
        _isValid = true;
      });
    } else {
      // Validate when focus is lost (field is unfocused)
      _validate(widget.controller?.text ?? '');
    }
  }

  void _validate(String value) {
    setState(() {
      _isTouched = true;
      if (widget.isMandatory) {
        if (widget.inputType == 'email') {
          _isValid = _isValidEmail(value);
        } else if (widget.inputType == 'phone') {
          _isValid = _isValidPhone(value);
        } else if (widget.inputType == 'number') {
          _isValid = _isValidNumber(value);
        } else {
          _isValid = value.isNotEmpty;
        }
      } else {
        _isValid = true;
      }
    });
  }

  bool _isValidEmail(String email) {
    final regex = RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$');
    return regex.hasMatch(email);
  }

  bool _isValidPhone(String phone) {
    final regex = RegExp(r'^\+?[0-9]{10,15}$');
    return regex.hasMatch(phone);
  }

  bool _isValidNumber(String number) {
    final regex = RegExp(r'^\d+(\.\d{1,2})?$');
    return regex.hasMatch(number);
  }

  // Expose the validation state
  bool isValid() {
    return !_isTouched || _isValid;
  }

  String? _internalValidator(String? value) {
    _validate(value ?? '');
    if (!_isValid) {
      // Return custom error messages based on input type
      if (widget.inputType == 'email') {
        return 'Inserisci un indirizzo email valido.';
      } else if (widget.inputType == 'phone') {
        return 'Inserisci un numero di telefono valido.';
      } else if (widget.inputType == 'number') {
        return 'Inserisci un numero valido.';
      } else {
        return 'Il campo è obbligatorio.';
      }
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        TextFormField(
          controller: widget.controller,
          focusNode: _focusNode,
          onChanged: (value) {
            // Call _validate to update validation state as the user types
            _validate(value);
            if (widget.onChanged != null) widget.onChanged!(value); // Call the external onChanged if provided
          },
          keyboardType: _getKeyboardType(widget.inputType),
          decoration: InputDecoration(
            labelText: widget.label,
            labelStyle: const TextStyle(color: Colors.black),
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide(color: Colors.grey.shade300),
            ),
            enabledBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide(
                color: (widget.isMandatory && !_isValid && _isTouched)
                    ? Colors.red
                    : Colors.grey.shade300,
              ),
            ),
            focusedBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide(
                color: (widget.isMandatory && !_isValid && _isTouched)
                    ? Colors.red
                    : Colors.blue,
                width: 2,
              ),
            ),
            contentPadding:
                const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
          ),
          // Use the internal validator, and allow external override
          validator: (value) {
            if (widget.validator != null) {
              // If a custom validator is provided, use it
              return widget.validator!(value);
            }

            // Otherwise, use the internal validation logic
            return _internalValidator(value);
          },
        ),
        const SizedBox(height: 4),
      ],
    );
  }

  TextInputType _getKeyboardType(String type) {
    switch (type) {
      case 'email':
        return TextInputType.emailAddress;
      case 'phone':
        return TextInputType.phone;
      case 'number':
        return TextInputType.number;
      default:
        return TextInputType.text;
    }
  }
}
Share Improve this question asked Mar 6 at 17:11 saisai 535 bronze badges 2
  • Why don't you use form widget to rap the whole list of text form field and call validate() method – Nnamani Daniel Commented Mar 7 at 2:20
  • @NnamaniDaniel I am doing it – sai Commented Mar 7 at 8:47
Add a comment  | 

1 Answer 1

Reset to default 2
onChanged: (value) {
  _validate(value);
  if (widget.onChanged != null) {
    widget.onChanged!(value);
  }

  
  FormFieldState? fieldState = Form.of(context)?.fields[widget.key];
  fieldState?.validate();
},

if above solution not works then use below

onChanged: (value) {
  _validate(value);
  if (widget.onChanged != null) {
    widget.onChanged!(value);
  }
  
  // Trigger form validation
  Form.of(context)?.validate();
},

Modify your onChanged method inside TextFormField:

本文标签: form validation is not updating once the form is submitted in FlutterStack Overflow