Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
2b9fdada
Unverified
Commit
2b9fdada
authored
Jun 24, 2021
by
LongCatIsLooong
Committed by
GitHub
Jun 24, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[RenderEditable] fix crash & remove `TextPainter.layout` short-circuiting (#85008)
parent
9a981b91
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
137 additions
and
13 deletions
+137
-13
editable.dart
packages/flutter/lib/src/rendering/editable.dart
+36
-13
editable_test.dart
packages/flutter/test/rendering/editable_test.dart
+31
-0
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+70
-0
No files found.
packages/flutter/lib/src/rendering/editable.dart
View file @
2b9fdada
...
...
@@ -301,6 +301,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_foregroundRenderObject
=
null
;
_backgroundRenderObject
?.
dispose
();
_backgroundRenderObject
=
null
;
_cachedBuiltInForegroundPainters
?.
dispose
();
_cachedBuiltInPainters
?.
dispose
();
super
.
dispose
();
}
...
...
@@ -3162,8 +3164,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
/// * [getLocalRectForCaret], which is the equivalent but for
/// a [TextPosition] rather than a [TextSelection].
List
<
TextSelectionPoint
>
getEndpointsForSelection
(
TextSelection
selection
)
{
assert
(
constraints
!=
null
);
_layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
_computeTextMetricsIfNeeded
();
final
Offset
paintOffset
=
_paintOffset
;
...
...
@@ -3193,10 +3194,9 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
/// Returns null if [TextRange.isValid] is false for the given `range`, or the
/// given `range` is collapsed.
Rect
?
getRectForComposingRange
(
TextRange
range
)
{
assert
(
constraints
!=
null
);
if
(!
range
.
isValid
||
range
.
isCollapsed
)
return
null
;
_
layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
_
computeTextMetricsIfNeeded
(
);
final
List
<
ui
.
TextBox
>
boxes
=
_textPainter
.
getBoxesForSelection
(
TextSelection
(
baseOffset:
range
.
start
,
extentOffset:
range
.
end
),
...
...
@@ -3217,7 +3217,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
/// * [TextPainter.getPositionForOffset], which is the equivalent method
/// for a [TextPainter] object.
TextPosition
getPositionForPoint
(
Offset
globalPosition
)
{
_
layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
_
computeTextMetricsIfNeeded
(
);
globalPosition
+=
-
_paintOffset
;
return
_textPainter
.
getPositionForOffset
(
globalToLocal
(
globalPosition
));
}
...
...
@@ -3234,7 +3234,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
/// * [TextPainter.getOffsetForCaret], the equivalent method for a
/// [TextPainter] object.
Rect
getLocalRectForCaret
(
TextPosition
caretPosition
)
{
_
layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
_
computeTextMetricsIfNeeded
(
);
final
Offset
caretOffset
=
_textPainter
.
getOffsetForCaret
(
caretPosition
,
_caretPrototype
);
// This rect is the same as _caretPrototype but without the vertical padding.
final
Rect
rect
=
Rect
.
fromLTWH
(
0.0
,
0.0
,
cursorWidth
,
cursorHeight
).
shift
(
caretOffset
+
_paintOffset
+
cursorOffset
);
...
...
@@ -3306,7 +3306,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
@override
double
computeDistanceToActualBaseline
(
TextBaseline
baseline
)
{
_
layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
_
computeTextMetricsIfNeeded
(
);
return
_textPainter
.
computeDistanceToActualBaseline
(
baseline
);
}
...
...
@@ -3500,7 +3500,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
void
selectWordsInRange
({
required
Offset
from
,
Offset
?
to
,
required
SelectionChangedCause
cause
})
{
assert
(
cause
!=
null
);
assert
(
from
!=
null
);
_
layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
_
computeTextMetricsIfNeeded
(
);
final
TextPosition
firstPosition
=
_textPainter
.
getPositionForOffset
(
globalToLocal
(
from
-
_paintOffset
));
final
TextSelection
firstWord
=
_getWordAtOffset
(
firstPosition
);
final
TextSelection
lastWord
=
to
==
null
?
...
...
@@ -3521,7 +3521,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
/// {@macro flutter.rendering.RenderEditable.selectPosition}
void
selectWordEdge
({
required
SelectionChangedCause
cause
})
{
assert
(
cause
!=
null
);
_
layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
_
computeTextMetricsIfNeeded
(
);
assert
(
_lastTapDownPosition
!=
null
);
final
TextPosition
position
=
_textPainter
.
getPositionForOffset
(
globalToLocal
(
_lastTapDownPosition
!
-
_paintOffset
));
final
TextRange
word
=
_textPainter
.
getWordBoundary
(
position
);
...
...
@@ -3693,8 +3693,6 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
void
_layoutText
({
double
minWidth
=
0.0
,
double
maxWidth
=
double
.
infinity
})
{
assert
(
maxWidth
!=
null
&&
minWidth
!=
null
);
if
(
_textLayoutLastMaxWidth
==
maxWidth
&&
_textLayoutLastMinWidth
==
minWidth
)
return
;
final
double
availableMaxWidth
=
math
.
max
(
0.0
,
maxWidth
-
_caretMargin
);
final
double
availableMinWidth
=
math
.
min
(
minWidth
,
availableMaxWidth
);
final
double
textMaxWidth
=
_isMultiline
?
availableMaxWidth
:
double
.
infinity
;
...
...
@@ -3707,6 +3705,30 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_textLayoutLastMaxWidth
=
maxWidth
;
}
// Computes the text metrics if `_textPainter`'s layout information was marked
// as dirty.
//
// This method must be called in `RenderEditable`'s public methods that expose
// `_textPainter`'s metrics. For instance, `systemFontsDidChange` sets
// _textPainter._paragraph to null, so accessing _textPainter's metrics
// immediately after `systemFontsDidChange` without first calling this method
// may crash.
//
// This method is also called in various paint methods (`RenderEditable.paint`
// as well as its foreground/background painters' `paint`). It's needed
// because invisible render objects kept in the tree by `KeepAlive` may not
// get a chance to do layout but can still paint.
// See https://github.com/flutter/flutter/issues/84896.
//
// This method only re-computes layout if the underlying `_textPainter`'s
// layout cache is invalidated (by calling `TextPainter.markNeedsLayout`), or
// the constraints used to layout the `_textPainter` is different. See
// `TextPainter.layout`.
void
_computeTextMetricsIfNeeded
()
{
assert
(
constraints
!=
null
);
_layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
}
late
Rect
_caretPrototype
;
// TODO(garyq): This is no longer producing the highest-fidelity caret
...
...
@@ -3790,7 +3812,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
final
BoxConstraints
constraints
=
this
.
constraints
;
_placeholderDimensions
=
_layoutChildren
(
constraints
);
_textPainter
.
setPlaceholderDimensions
(
_placeholderDimensions
);
_
layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
_
computeTextMetricsIfNeeded
(
);
_setParentData
();
_computeCaretPrototype
();
// We grab _textPainter.size here because assigning to `size` on the next
...
...
@@ -3983,7 +4005,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
_
layoutText
(
minWidth:
constraints
.
minWidth
,
maxWidth:
constraints
.
maxWidth
);
_
computeTextMetricsIfNeeded
(
);
if
(
_hasVisualOverflow
&&
clipBehavior
!=
Clip
.
none
)
{
_clipRectLayer
=
context
.
pushClipRect
(
needsCompositing
,
...
...
@@ -4071,6 +4093,7 @@ class _RenderEditableCustomPaint extends RenderBox {
assert
(
parent
!=
null
);
final
RenderEditablePainter
?
painter
=
this
.
painter
;
if
(
painter
!=
null
&&
parent
!=
null
)
{
parent
.
_computeTextMetricsIfNeeded
();
painter
.
paint
(
context
.
canvas
,
size
,
parent
);
}
}
...
...
packages/flutter/test/rendering/editable_test.dart
View file @
2b9fdada
...
...
@@ -3928,6 +3928,37 @@ void main() {
expect
(
result
.
path
,
hasLength
(
0
));
},
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/61020
});
test
(
'does not skip TextPainter.layout because of invalid cache'
,
()
{
// Regression test for https://github.com/flutter/flutter/issues/84896.
final
TextSelectionDelegate
delegate
=
FakeEditableTextState
();
const
BoxConstraints
constraints
=
BoxConstraints
(
minWidth:
100
,
maxWidth:
500
);
final
RenderEditable
editable
=
RenderEditable
(
text:
const
TextSpan
(
style:
TextStyle
(
height:
1.0
,
fontSize:
10.0
,
fontFamily:
'Ahem'
),
text:
'A'
,
),
startHandleLayerLink:
LayerLink
(),
endHandleLayerLink:
LayerLink
(),
textAlign:
TextAlign
.
start
,
textDirection:
TextDirection
.
ltr
,
locale:
const
Locale
(
'en'
,
'US'
),
forceLine:
true
,
offset:
ViewportOffset
.
fixed
(
10.0
),
textSelectionDelegate:
delegate
,
selection:
const
TextSelection
.
collapsed
(
offset:
0
),
cursorColor:
const
Color
(
0xFFFFFFFF
),
showCursor:
ValueNotifier
<
bool
>(
true
),
);
layout
(
editable
,
constraints:
constraints
);
final
double
initialWidth
=
editable
.
computeDryLayout
(
constraints
).
width
;
expect
(
initialWidth
,
500
);
// Turn off forceLine. Now the width should be significantly smaller.
editable
.
forceLine
=
false
;
expect
(
editable
.
computeDryLayout
(
constraints
).
width
,
lessThan
(
initialWidth
));
});
}
class
_TestRenderEditable
extends
RenderEditable
{
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
2b9fdada
...
...
@@ -1142,6 +1142,76 @@ void main() {
expect
(
state
.
wantKeepAlive
,
true
);
});
testWidgets
(
'kept-alive EditableText does not crash when layout is skipped'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/84896.
EditableText
.
debugDeterministicCursor
=
true
;
const
Key
key
=
ValueKey
<
String
>(
'EditableText'
);
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
ListView
(
children:
<
Widget
>[
EditableText
(
key:
key
,
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
autofocus:
true
,
maxLines:
null
,
keyboardType:
TextInputType
.
text
,
style:
textStyle
,
textAlign:
TextAlign
.
left
,
cursorColor:
cursorColor
,
showCursor:
false
,
),
],
),
),
),
);
// Wait for autofocus.
await
tester
.
pump
();
expect
(
focusNode
.
hasFocus
,
isTrue
);
// Prepend an additional item to make EditableText invisible. It's still
// kept in the tree via the keepalive mechanism. Change the text alignment
// and showCursor. The RenderEditable now needs to relayout and repaint.
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
ListView
(
children:
<
Widget
>[
const
SizedBox
(
height:
6000
),
EditableText
(
key:
key
,
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
autofocus:
true
,
maxLines:
null
,
keyboardType:
TextInputType
.
text
,
style:
textStyle
,
textAlign:
TextAlign
.
right
,
cursorColor:
cursorColor
,
showCursor:
true
,
),
],
),
),
),
);
EditableText
.
debugDeterministicCursor
=
false
;
expect
(
tester
.
takeException
(),
isNull
);
});
/// Toolbar is not used in Flutter Web. Skip this check.
///
/// Web is using native DOM elements (it is also used as platform input)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment