Saltar al contenido principal

RenderEditable necesita pasar por el layout antes del hit testing

El hit testing de RenderEditable requiere información adicional que solo está disponible después del layout.

Resumen

#

Las instancias de RenderEditable deben tener el layout hecho antes de procesar el hit testing. Intentar realizar un hit-test en un objeto RenderEditable antes del layout da como resultado una aserción como la siguiente:

Failed assertion: line 123 pos 45: '!debugNeedsLayout': is not true.

Contexto

#

Para soportar gesture recognizers en texto seleccionable, el RenderEditable requiere la información de layout de sus text spans para determinar qué text span recibe el evento de puntero. (Antes de este cambio, los objetos RenderEditable no tenían en cuenta su texto al evaluar los hit tests). Para implementar esto, el layout se convirtió en un requisito previo para realizar el hit testing en un objeto RenderEditable.

En la práctica, esto rara vez es un problema. La librería de widgets asegura que el layout se realice antes de cualquier hit test en todos los render objects. Es probable que este problema solo se vea en código que interactúa directamente con render objects, por ejemplo en pruebas de render objects personalizados.

Guía de migración

#

Si ves el error de aserción '!debugNeedsLayout': is not true al realizar un hit test a RenderEditable, realiza el layout del RenderEditable antes de hacerlo.

Código antes de la migración:

dart
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';

void main() {
  test('attach and detach correctly handle gesture', () {
    final RenderEditable editable = RenderEditable(
      textDirection: TextDirection.ltr,
      offset: ViewportOffset.zero(),
      textSelectionDelegate: FakeEditableTextState(),
      startHandleLayerLink: LayerLink(),
      endHandleLayerLink: LayerLink(),
    );
    final PipelineOwner owner = PipelineOwner(onNeedVisualUpdate: () {});
    editable.attach(owner);
    // This throws an assertion error because
    // the RenderEditable hasn't been laid out.
    editable.handleEvent(const PointerDownEvent(),
        BoxHitTestEntry(editable, const Offset(10, 10)));
    editable.detach();
  });
}

class FakeEditableTextState extends TextSelectionDelegate {
  @override
  TextEditingValue textEditingValue;
  @override
  void hideToolbar() {}
  @override
  void bringIntoView(TextPosition position) {}
}

Código después de la migración:

dart
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';

void main() {
  test('attach and detach correctly handle gesture', () {
    final RenderEditable editable = RenderEditable(
      textDirection: TextDirection.ltr,
      offset: ViewportOffset.zero(),
      textSelectionDelegate: FakeEditableTextState(),
      startHandleLayerLink: LayerLink(),
      endHandleLayerLink: LayerLink(),
    );
    // Lay out the RenderEditable first.
    editable.layout(BoxConstraints.loose(const Size(1000.0, 1000.0)));
    final PipelineOwner owner = PipelineOwner(onNeedVisualUpdate: () {});
    editable.attach(owner);
    editable.handleEvent(const PointerDownEvent(),
        BoxHitTestEntry(editable, const Offset(10, 10)));
    editable.detach();
  });
}

class FakeEditableTextState extends TextSelectionDelegate {
  @override
  TextEditingValue textEditingValue;
  @override
  void hideToolbar() {}
  @override
  void bringIntoView(TextPosition position) {}
}

Timeline

#

Introducido en la versión: 1.18.0
En la versión estable: 1.20

Referencias

#

Documentación de la API:

Problema relevante:

  • Issue 43494: SelectableText.rich usado junto con TapGestureRecognizer no funciona

PR relevante: