概述

#

RenderEditable 的例項在處理命中測試之前必須先進行佈局。嘗試在佈局之前命中測試 RenderEditable 物件會導致斷言失敗,如下所示:

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

背景

#

為了在可選文字中支援手勢識別器,RenderEditable 需要其文字跨度的佈局資訊,以確定哪個文字跨度接收指標事件。(在此更改之前,RenderEditable 物件在評估命中測試時不會考慮其文字。)為了實現這一點,佈局被設為了在 RenderEditable 物件上執行命中測試的先決條件。

實際上,這很少成為問題。小部件庫確保在對所有渲染物件進行任何命中測試之前都會執行佈局。此問題僅可能在直接與渲染物件互動的程式碼中看到,例如自定義渲染物件的測試中。

遷移指南

#

如果您在命中測試 RenderEditable 時看到 '!debugNeedsLayout': is not true 斷言錯誤,請先佈局 RenderEditable,然後再進行命中測試。

遷移前的程式碼

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) {}
}

遷移後的程式碼

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) {}
}

時間線

#

釋出版本:1.18.0
穩定版本中:1.20

參考資料

#

API 文件

相關議題

  • 問題 43494:SelectableText.rich 與 TapGestureRecognizer 一起使用時不起作用

相關 PR