describeSemanticsConfiguration method

  1. @override
void describeSemanticsConfiguration(
  1. SemanticsConfiguration config
)
override

Report the semantics of this node, for example for accessibility purposes.

This method should be overridden by subclasses that have interesting semantic information.

The given SemanticsConfiguration object is mutable and should be annotated in a manner that describes the current state. No reference should be kept to that object; mutating it outside of the context of the describeSemanticsConfiguration call (for example as a result of asynchronous computation) will at best have no useful effect and at worse will cause crashes as the data will be in an inconsistent state.

The following snippet will describe the node as a button that responds to tap actions.
link
abstract class SemanticButtonRenderObject extends RenderObject {
  @override
  void describeSemanticsConfiguration(SemanticsConfiguration config) {
    super.describeSemanticsConfiguration(config);
    config
      ..onTap = _handleTap
      ..label = 'I am a button'
      ..isButton = true;
  }

  void _handleTap() {
    // Do something.
  }
}

Implementation

@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
  super.describeSemanticsConfiguration(config);
  _semanticsInfo = _textPainter.text!.getSemanticsInformation();
  // TODO(chunhtai): the macOS does not provide a public API to support text
  // selections across multiple semantics nodes. Remove this platform check
  // once we can support it.
  // https://github.com/flutter/flutter/issues/77957
  if (_semanticsInfo!.any((InlineSpanSemanticsInformation info) => info.recognizer != null) &&
      defaultTargetPlatform != TargetPlatform.macOS) {
    assert(readOnly && !obscureText);
    // For Selectable rich text with recognizer, we need to create a semantics
    // node for each text fragment.
    config
      ..isSemanticBoundary = true
      ..explicitChildNodes = true;
    return;
  }
  if (_cachedAttributedValue == null) {
    if (obscureText) {
      _cachedAttributedValue = AttributedString(obscuringCharacter * plainText.length);
    } else {
      final StringBuffer buffer = StringBuffer();
      int offset = 0;
      final List<StringAttribute> attributes = <StringAttribute>[];
      for (final InlineSpanSemanticsInformation info in _semanticsInfo!) {
        final String label = info.semanticsLabel ?? info.text;
        for (final StringAttribute infoAttribute in info.stringAttributes) {
          final TextRange originalRange = infoAttribute.range;
          attributes.add(
            infoAttribute.copy(
              range: TextRange(start: offset + originalRange.start, end: offset + originalRange.end),
            ),
          );
        }
        buffer.write(label);
        offset += label.length;
      }
      _cachedAttributedValue = AttributedString(buffer.toString(), attributes: attributes);
    }
  }
  config
    ..attributedValue = _cachedAttributedValue!
    ..isObscured = obscureText
    ..isMultiline = _isMultiline
    ..textDirection = textDirection
    ..isFocused = hasFocus
    ..isTextField = true
    ..isReadOnly = readOnly;

  if (hasFocus && selectionEnabled) {
    config.onSetSelection = _handleSetSelection;
  }

  if (hasFocus && !readOnly) {
    config.onSetText = _handleSetText;
  }

  if (selectionEnabled && (selection?.isValid ?? false)) {
    config.textSelection = selection;
    if (_textPainter.getOffsetBefore(selection!.extentOffset) != null) {
      config
        ..onMoveCursorBackwardByWord = _handleMoveCursorBackwardByWord
        ..onMoveCursorBackwardByCharacter = _handleMoveCursorBackwardByCharacter;
    }
    if (_textPainter.getOffsetAfter(selection!.extentOffset) != null) {
      config
        ..onMoveCursorForwardByWord = _handleMoveCursorForwardByWord
        ..onMoveCursorForwardByCharacter = _handleMoveCursorForwardByCharacter;
    }
  }
}