@@ -61,6 +61,27 @@ class _RenderParagraph extends RenderBox
6161 String .fromCharCode (PlaceholderSpan .placeholderCodeUnit);
6262 final TextPainter _textPainter;
6363
64+ // Currently, computing min/max intrinsic width/height will destroy state
65+ // inside the painter. Instead of calling _layout again to get back the correct
66+ // state, use a separate TextPainter for intrinsics calculation.
67+ //
68+ // TODO(abarth): Make computing the min/max intrinsic width/height a
69+ // non-destructive operation.
70+ TextPainter ? _textIntrinsicsCache;
71+ TextPainter get _textIntrinsics {
72+ return (_textIntrinsicsCache ?? = TextPainter ())
73+ ..text = _textPainter.text
74+ ..textAlign = _textPainter.textAlign
75+ ..textDirection = _textPainter.textDirection
76+ ..textScaler = _textPainter.textScaler
77+ ..maxLines = _textPainter.maxLines
78+ ..ellipsis = _textPainter.ellipsis
79+ ..locale = _textPainter.locale
80+ ..strutStyle = _textPainter.strutStyle
81+ ..textWidthBasis = _textPainter.textWidthBasis
82+ ..textHeightBehavior = _textPainter.textHeightBehavior;
83+ }
84+
6485 List <AttributedString >? _cachedAttributedLabels;
6586
6687 List <InlineSpanSemanticsInformation >? _cachedCombinedSemanticsInfos;
@@ -162,10 +183,13 @@ class _RenderParagraph extends RenderBox
162183 if (end == - 1 ) {
163184 end = plainText.length;
164185 }
165- result.add (_SelectableFragment (
186+ result.add (
187+ _SelectableFragment (
166188 paragraph: this ,
167189 range: TextRange (start: start, end: end),
168- fullText: plainText));
190+ fullText: plainText,
191+ ),
192+ );
169193 start = end;
170194 }
171195 start += 1 ;
@@ -199,6 +223,7 @@ class _RenderParagraph extends RenderBox
199223 _removeSelectionRegistrarSubscription ();
200224 _disposeSelectableFragments ();
201225 _textPainter.dispose ();
226+ _textIntrinsicsCache? .dispose ();
202227 super .dispose ();
203228 }
204229
@@ -363,51 +388,54 @@ class _RenderParagraph extends RenderBox
363388
364389 Offset _getOffsetForPosition (TextPosition position) {
365390 return getOffsetForCaret (position, Rect .zero) +
366- Offset (0 , getFullHeightForCaret (position) ?? 0.0 );
367- }
368-
369- List <ui.LineMetrics > _computeLineMetrics () {
370- return _textPainter.computeLineMetrics ();
391+ Offset (0 , getFullHeightForCaret (position));
371392 }
372393
373394 @override
374395 double computeMinIntrinsicWidth (double height) {
375396 if (! _canComputeIntrinsics ()) {
376397 return 0.0 ;
377398 }
378- _textPainter.setPlaceholderDimensions (layoutInlineChildren (
399+ final List <PlaceholderDimensions > placeholderDimensions =
400+ layoutInlineChildren (
379401 double .infinity,
380402 (RenderBox child, BoxConstraints constraints) =>
381403 Size (child.getMinIntrinsicWidth (double .infinity), 0.0 ),
382- ));
383- _layoutText (); // layout with infinite width.
384- return _textPainter.minIntrinsicWidth;
404+ );
405+ return (_textIntrinsics
406+ ..setPlaceholderDimensions (placeholderDimensions)
407+ ..layout ())
408+ .minIntrinsicWidth;
385409 }
386410
387411 @override
388412 double computeMaxIntrinsicWidth (double height) {
389413 if (! _canComputeIntrinsics ()) {
390414 return 0.0 ;
391415 }
392- _textPainter.setPlaceholderDimensions (layoutInlineChildren (
416+ final List <PlaceholderDimensions > placeholderDimensions =
417+ layoutInlineChildren (
393418 double .infinity,
394419 // Height and baseline is irrelevant as all text will be laid
395420 // out in a single line. Therefore, using 0.0 as a dummy for the height.
396421 (RenderBox child, BoxConstraints constraints) =>
397422 Size (child.getMaxIntrinsicWidth (double .infinity), 0.0 ),
398- ));
399- _layoutText (); // layout with infinite width.
400- return _textPainter.maxIntrinsicWidth;
423+ );
424+ return (_textIntrinsics
425+ ..setPlaceholderDimensions (placeholderDimensions)
426+ ..layout ())
427+ .maxIntrinsicWidth;
401428 }
402429
403430 double _computeIntrinsicHeight (double width) {
404431 if (! _canComputeIntrinsics ()) {
405432 return 0.0 ;
406433 }
407- _textPainter.setPlaceholderDimensions (
408- layoutInlineChildren (width, ChildLayoutHelper .dryLayoutChild));
409- _layoutText (minWidth: width, maxWidth: width);
410- return _textPainter.height;
434+ return (_textIntrinsics
435+ ..setPlaceholderDimensions (
436+ layoutInlineChildren (width, ChildLayoutHelper .dryLayoutChild))
437+ ..layout (minWidth: width, maxWidth: _adjustMaxWidth (width)))
438+ .height;
411439 }
412440
413441 @override
@@ -507,14 +535,6 @@ class _RenderParagraph extends RenderBox
507535 @visibleForTesting
508536 bool get debugHasOverflowShader => _overflowShader != null ;
509537
510- void _layoutText ({double minWidth = 0.0 , double maxWidth = double .infinity}) {
511- final bool widthMatters = softWrap || overflow == TextOverflow .ellipsis;
512- _textPainter.layout (
513- minWidth: minWidth,
514- maxWidth: widthMatters ? maxWidth : double .infinity,
515- );
516- }
517-
518538 @override
519539 void systemFontsDidChange () {
520540 super .systemFontsDidChange ();
@@ -528,24 +548,38 @@ class _RenderParagraph extends RenderBox
528548 // restored to the original values before final layout and painting.
529549 List <PlaceholderDimensions >? _placeholderDimensions;
530550
551+ double _adjustMaxWidth (double maxWidth) {
552+ return softWrap || overflow == TextOverflow .ellipsis
553+ ? maxWidth
554+ : double .infinity;
555+ }
556+
531557 void _layoutTextWithConstraints (BoxConstraints constraints) {
532- _textPainter.setPlaceholderDimensions (_placeholderDimensions);
533- _layoutText (minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
558+ _textPainter
559+ ..setPlaceholderDimensions (_placeholderDimensions)
560+ ..layout (
561+ minWidth: constraints.minWidth,
562+ maxWidth: _adjustMaxWidth (constraints.maxWidth));
534563 }
535564
536565 @override
537- Size computeDryLayout (BoxConstraints constraints) {
566+ @protected
567+ Size computeDryLayout (covariant BoxConstraints constraints) {
538568 if (! _canComputeIntrinsics ()) {
539569 assert (debugCannotComputeDryLayout (
540570 reason:
541571 'Dry layout not available for alignments that require baseline.' ,
542572 ));
543573 return Size .zero;
544574 }
545- _textPainter.setPlaceholderDimensions (layoutInlineChildren (
546- constraints.maxWidth, ChildLayoutHelper .dryLayoutChild));
547- _layoutText (minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
548- return constraints.constrain (_textPainter.size);
575+ final Size size = (_textIntrinsics
576+ ..setPlaceholderDimensions (layoutInlineChildren (
577+ constraints.maxWidth, ChildLayoutHelper .dryLayoutChild))
578+ ..layout (
579+ minWidth: constraints.minWidth,
580+ maxWidth: _adjustMaxWidth (constraints.maxWidth)))
581+ .size;
582+ return constraints.constrain (size);
549583 }
550584
551585 @override
@@ -592,15 +626,13 @@ class _RenderParagraph extends RenderBox
592626 locale: locale,
593627 )..layout ();
594628 if (didOverflowWidth) {
595- double fadeEnd, fadeStart;
596- switch (textDirection) {
597- case TextDirection .rtl:
598- fadeEnd = 0.0 ;
599- fadeStart = fadeSizePainter.width;
600- case TextDirection .ltr:
601- fadeEnd = size.width;
602- fadeStart = fadeEnd - fadeSizePainter.width;
603- }
629+ final (double fadeStart, double fadeEnd) = switch (textDirection) {
630+ TextDirection .rtl => (fadeSizePainter.width, 0.0 ),
631+ TextDirection .ltr => (
632+ size.width - fadeSizePainter.width,
633+ size.width
634+ ),
635+ };
604636 _overflowShader = ui.Gradient .linear (
605637 Offset (fadeStart, 0.0 ),
606638 Offset (fadeEnd, 0.0 ),
@@ -628,24 +660,15 @@ class _RenderParagraph extends RenderBox
628660 defaultApplyPaintTransform (child, transform);
629661 }
630662
631- // zmtzawqlp
663+ // @override
632664 // void paint(PaintingContext context, Offset offset) {
633- // // Ideally we could compute the min/max intrinsic width/height with a
634- // // non-destructive operation. However, currently, computing these values
635- // // will destroy state inside the painter. If that happens, we need to get
636- // // back the correct state by calling _layout again.
637- // //
638- // // TODO(abarth): Make computing the min/max intrinsic width/height a
639- // // non-destructive operation.
640- // //
641- // // If you remove this call, make sure that changing the textAlign still
642- // // works properly.
665+ // // Text alignment only triggers repaint so it's possible the text layout has
666+ // // been invalidated but performLayout wasn't called at this point. Make sure
667+ // // the TextPainter has a valid layout.
643668 // _layoutTextWithConstraints(constraints);
644-
645669 // assert(() {
646670 // if (debugRepaintTextRainbowEnabled) {
647- // final Paint paint = Paint()
648- // ..color = debugCurrentRepaintColor.toColor();
671+ // final Paint paint = Paint()..color = debugCurrentRepaintColor.toColor();
649672 // context.canvas.drawRect(offset & size, paint);
650673 // }
651674 // return true;
@@ -697,7 +720,7 @@ class _RenderParagraph extends RenderBox
697720 /// {@macro flutter.painting.textPainter.getFullHeightForCaret}
698721 ///
699722 /// Valid only after [layout] .
700- double ? getFullHeightForCaret (TextPosition position) {
723+ double getFullHeightForCaret (TextPosition position) {
701724 assert (! debugNeedsLayout);
702725 _layoutTextWithConstraints (constraints);
703726 return _textPainter.getFullHeightForCaret (position, Rect .zero);
@@ -816,20 +839,20 @@ class _RenderParagraph extends RenderBox
816839 super .describeSemanticsConfiguration (config);
817840 _semanticsInfo = text.getSemanticsInformation ();
818841 bool needsAssembleSemanticsNode = false ;
819- bool needsChildConfigrationsDelegate = false ;
842+ bool needsChildConfigurationsDelegate = false ;
820843 for (final InlineSpanSemanticsInformation info in _semanticsInfo! ) {
821844 if (info.recognizer != null ) {
822845 needsAssembleSemanticsNode = true ;
823846 break ;
824847 }
825- needsChildConfigrationsDelegate =
826- needsChildConfigrationsDelegate || info.isPlaceholder;
848+ needsChildConfigurationsDelegate =
849+ needsChildConfigurationsDelegate || info.isPlaceholder;
827850 }
828851
829852 if (needsAssembleSemanticsNode) {
830853 config.explicitChildNodes = true ;
831854 config.isSemanticBoundary = true ;
832- } else if (needsChildConfigrationsDelegate ) {
855+ } else if (needsChildConfigurationsDelegate ) {
833856 config.childConfigurationsDelegate =
834857 _childSemanticsConfigurationsDelegate;
835858 } else {
@@ -1816,7 +1839,8 @@ class _SelectableFragment
18161839 TextPosition position,
18171840 {required double horizontalBaselineInParagraphCoordinates,
18181841 required bool below}) {
1819- final List <ui.LineMetrics > lines = paragraph._computeLineMetrics ();
1842+ final List <ui.LineMetrics > lines =
1843+ paragraph._textPainter.computeLineMetrics ();
18201844 final Offset offset = paragraph.getOffsetForCaret (position, Rect .zero);
18211845 int currentLine = lines.length - 1 ;
18221846 for (final ui.LineMetrics lineMetrics in lines) {
0 commit comments