value property

  1. @override
TextEditingValue get value
inherited

The current value stored in this notifier.

When the value is replaced with something that is not equal to the old value as evaluated by the equality operator ==, this class notifies its listeners.

Implementation

@override
T get value => _value;
  1. @override
set value (TextEditingValue newValue)
override

Implementation

@override
set value(TextEditingValue newValue) {
  if (newValue == value) {
    return;
  }

  /// Truncates the text to the maximum length.
  if (newValue.text.characters.take(_length).string case final truncated when truncated != newValue.text) {
    _focused = (truncated.length - 1).clamp(0, _length - 1);
    super.value = TextEditingValue(
      text: truncated,
      selection: .collapsed(offset: truncated.length),
    );
    return;
  }

  /// Clamps selection offsets that exceed text length. This happens when dragging selection handles across empty
  /// WidgetSpan items — the framework reports offsets based on span positions, not text length.
  final length = newValue.text.length;
  if (length < newValue.selection.start || length < newValue.selection.end) {
    super.value = newValue.copyWith(
      selection: TextSelection(
        baseOffset: newValue.selection.baseOffset.clamp(0, length),
        extentOffset: newValue.selection.extentOffset.clamp(0, length),
      ),
    );
    _focused = length;
    return;
  }

  /// Calculates the focused index and caret position. Arrow key events are intercepted and routed via `traverse`
  /// to avoid conflicts.
  ///
  /// WidgetSpan offset/affinity calculations are fucked. See https://github.com/flutter/flutter/issues/107432.
  /// Tapping the right half of the first item produces <offset: 1, affinity: upstream> while other items produce
  /// <offset: N, affinity: downstream>.
  _focused = newValue.selection.baseOffset.clamp(0, _length - 1);
  if (newValue.selection.isCollapsed) {
    /// Corrects the focused index when tapping the right half of the first item.
    if (newValue.selection.baseOffset == 1 && newValue.selection.affinity == .upstream) {
      _focused = 0;
      super.value = newValue;
      return;
    }

    /// Handles replacement/deletion of middle items.
    if (newValue.text.length != newValue.selection.baseOffset) {
      /// Selects the previous item on deletion.
      if (newValue.text.length != text.length) {
        super.value = newValue.copyWith(
          selection: TextSelection(
            baseOffset: max(newValue.selection.baseOffset - 1, 0),
            extentOffset: newValue.selection.baseOffset,
          ),
        );
        return;
      }

      /// Selects the middle item at the caret so that backspace deletes it and typing replaces it.
      super.value = newValue.copyWith(
        selection: TextSelection(
          baseOffset: newValue.selection.baseOffset,
          extentOffset: newValue.selection.baseOffset + 1,
        ),
      );
      return;
    }
  }

  super.value = newValue;
}