attach method

FutureOr<ValidationResult?> attach(
  1. BuildContext context,
  2. FormFieldHandle handle,
  3. Object? value,
  4. Validator? validator, [
  5. bool forceRevalidate = false,
])

Attaches a form field to this controller.

Registers the field and runs initial validation if a validator is provided. Manages field lifecycle transitions (initial → changed) and coordinates revalidation of dependent fields.

Parameters:

  • context (BuildContext, required): The build context.
  • handle (FormFieldHandle, required): The field handle to attach.
  • value (Object?, required): Current field value.
  • validator (Validator?, optional): Validation function.
  • forceRevalidate (bool, default: false): Force revalidation even if unchanged.

Returns: FutureOr<ValidationResult?> — validation result if applicable.

Implementation

FutureOr<ValidationResult?> attach(BuildContext context,
    FormFieldHandle handle, Object? value, Validator? validator,
    [bool forceRevalidate = false]) {
  final key = handle.formKey;
  final oldState = _attachedInputs[key];
  var state = FormValueState(value: value, validator: validator);
  if (oldState == state && !forceRevalidate) {
    return _validity[key]?.result;
  }
  var lifecycle = oldState == null
      ? FormValidationMode.initial
      : FormValidationMode.changed;
  _attachedInputs[key] = state;
  // validate
  var future = validator?.validate(context, value, lifecycle);
  if (future is Future<ValidationResult?>) {
    _validity[key] = _ValidatorResultStash(future, lifecycle);
    future.then((value) {
      // resolve the future and store synchronous value
      if (_validity[key]?.result == future) {
        _validity[key] = _ValidatorResultStash(value?.attach(key), lifecycle);
        WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
          if (_disposed) {
            return;
          }
          notifyListeners();
        });
      }
    });
  } else {
    _validity[key] = _ValidatorResultStash(future, lifecycle);
  }
  // check for revalidation
  Map<FormKey, FutureOr<ValidationResult?>> revalidate = {};
  for (var entry in _attachedInputs.entries) {
    var k = entry.key;
    var value = entry.value;
    if (key == k) {
      continue;
    }
    if (value.validator != null && value.validator!.shouldRevalidate(key)) {
      var revalidateResult =
          value.validator!.validate(context, value.value, lifecycle);
      revalidate[k] = revalidateResult;
    }
  }
  for (var entry in revalidate.entries) {
    var k = entry.key;
    var future = entry.value;
    var attachedInput = _attachedInputs[k]!;
    attachedInput = FormValueState(
        value: attachedInput.value, validator: attachedInput.validator);
    _attachedInputs[k] = attachedInput;
    if (future is Future<ValidationResult?>) {
      _validity[k] = _ValidatorResultStash(future, lifecycle);
      future.then((value) {
        if (_validity[k]?.result == future) {
          _validity[k] = _ValidatorResultStash(value?.attach(k), lifecycle);
          WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
            if (_disposed) {
              return;
            }
            notifyListeners();
          });
        }
      });
    } else {
      _validity[k] = _ValidatorResultStash(future, lifecycle);
    }
  }
  WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
    if (_disposed) {
      return;
    }
    notifyListeners();
  });
  return _validity[key]?.result;
}