Skip to content

Commit 528a281

Browse files
authored
Update alwaysNeedsCompositing in RenderParagraph (flutter#135076)
fix flutter#111370 According to https://github.com/flutter/flutter/blob/5b47fef613e2d74ae44e1e22c2a3495294db180e/packages/flutter/lib/src/rendering/box.dart#L1259 : A [RenderBox] that uses methods on [PaintingContext] that introduce new /// layers should override the [alwaysNeedsCompositing] getter and set it to /// true. set [alwaysNeedsCompositing] to true when RenderParagraph introduces LeaderLayer for selection handles. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [Features we expect every widget to implement]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 45e4a0e commit 528a281

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

packages/flutter/lib/src/rendering/paragraph.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,9 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
388388
}
389389
_lastSelectableFragments ??= _getSelectableFragments();
390390
_lastSelectableFragments!.forEach(_registrar!.add);
391+
if (_lastSelectableFragments!.isNotEmpty) {
392+
markNeedsCompositingBitsUpdate();
393+
}
391394
}
392395

393396
void _removeSelectionRegistrarSubscription() {
@@ -425,6 +428,9 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
425428
_lastSelectableFragments = null;
426429
}
427430

431+
@override
432+
bool get alwaysNeedsCompositing => _lastSelectableFragments?.isNotEmpty ?? false;
433+
428434
@override
429435
void markNeedsLayout() {
430436
_lastSelectableFragments?.forEach((_SelectableFragment element) => element.didChangeParagraphLayout());

packages/flutter/test/material/selection_area_test.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,63 @@ void main() {
7070
expect(tester.takeException(), isNull);
7171
});
7272

73+
// Regression test for https://github.com/flutter/flutter/issues/111370
74+
testWidgetsWithLeakTracking('Handle is correctly transformed when the text is inside of a FittedBox ',(WidgetTester tester) async {
75+
final Key textKey = UniqueKey();
76+
await tester.pumpWidget(
77+
MaterialApp(
78+
color: const Color(0xFF2196F3),
79+
home: Scaffold(
80+
body: SelectionArea(
81+
child: SizedBox(
82+
height: 100,
83+
child: FittedBox(
84+
fit: BoxFit.fill,
85+
child: Text('test', key: textKey),
86+
),
87+
),
88+
),
89+
),
90+
),
91+
);
92+
93+
final TestGesture longpress = await tester.startGesture(const Offset(10, 10));
94+
addTearDown(longpress.removePointer);
95+
await tester.pump(const Duration(milliseconds: 500));
96+
await longpress.up();
97+
98+
// Text box is scaled by 5.
99+
final RenderBox textBox = tester.firstRenderObject(find.byKey(textKey));
100+
expect(textBox.size.height, 20.0);
101+
final Offset textPoint = textBox.localToGlobal(const Offset(0, 20));
102+
expect(textPoint, equals(const Offset(0, 100)));
103+
104+
// Find handles and verify their sizes.
105+
expect(find.byType(Overlay), findsOneWidget);
106+
expect(find.descendant(of: find.byType(Overlay),matching: find.byType(CustomPaint),),findsNWidgets(2));
107+
final Iterable<RenderBox> handles = tester.renderObjectList(find.descendant(
108+
of: find.byType(Overlay),
109+
matching: find.byType(CustomPaint),
110+
));
111+
112+
// The handle height is determined by the formula:
113+
// textLineHeight + _kSelectionHandleRadius * 2 - _kSelectionHandleOverlap .
114+
// The text line height will be the value of the fontSize.
115+
// The constant _kSelectionHandleRadius has the value of 6.
116+
// The constant _kSelectionHandleOverlap has the value of 1.5.
117+
// The handle height before scaling is 20.0 + 6 * 2 - 1.5 = 30.5.
118+
119+
final double handleHeightBeforeScaling = handles.first.size.height;
120+
expect(handleHeightBeforeScaling, 30.5);
121+
122+
final Offset handleHeightAfterScaling = handles.first.localToGlobal(const Offset(0, 30.5)) - handles.first.localToGlobal(Offset.zero);
123+
124+
// The handle height after scaling is 30.5 * 5 = 152.5
125+
expect(handleHeightAfterScaling, equals(const Offset(0.0, 152.5)));
126+
},
127+
skip: isBrowser, // [intended]
128+
variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.iOS}),
129+
);
73130

74131
testWidgetsWithLeakTracking('builds the default context menu by default', (WidgetTester tester) async {
75132
final FocusNode focusNode = FocusNode();

0 commit comments

Comments
 (0)