Update alwaysNeedsCompositing in RenderParagraph (#135076)
fix #111370
According to
5b47fef613/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
This commit is contained in:
parent
45e4a0e525
commit
528a281a5c
@ -388,6 +388,9 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
|
||||
}
|
||||
_lastSelectableFragments ??= _getSelectableFragments();
|
||||
_lastSelectableFragments!.forEach(_registrar!.add);
|
||||
if (_lastSelectableFragments!.isNotEmpty) {
|
||||
markNeedsCompositingBitsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void _removeSelectionRegistrarSubscription() {
|
||||
@ -425,6 +428,9 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
|
||||
_lastSelectableFragments = null;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get alwaysNeedsCompositing => _lastSelectableFragments?.isNotEmpty ?? false;
|
||||
|
||||
@override
|
||||
void markNeedsLayout() {
|
||||
_lastSelectableFragments?.forEach((_SelectableFragment element) => element.didChangeParagraphLayout());
|
||||
|
@ -70,6 +70,63 @@ void main() {
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/111370
|
||||
testWidgetsWithLeakTracking('Handle is correctly transformed when the text is inside of a FittedBox ',(WidgetTester tester) async {
|
||||
final Key textKey = UniqueKey();
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
color: const Color(0xFF2196F3),
|
||||
home: Scaffold(
|
||||
body: SelectionArea(
|
||||
child: SizedBox(
|
||||
height: 100,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.fill,
|
||||
child: Text('test', key: textKey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final TestGesture longpress = await tester.startGesture(const Offset(10, 10));
|
||||
addTearDown(longpress.removePointer);
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
await longpress.up();
|
||||
|
||||
// Text box is scaled by 5.
|
||||
final RenderBox textBox = tester.firstRenderObject(find.byKey(textKey));
|
||||
expect(textBox.size.height, 20.0);
|
||||
final Offset textPoint = textBox.localToGlobal(const Offset(0, 20));
|
||||
expect(textPoint, equals(const Offset(0, 100)));
|
||||
|
||||
// Find handles and verify their sizes.
|
||||
expect(find.byType(Overlay), findsOneWidget);
|
||||
expect(find.descendant(of: find.byType(Overlay),matching: find.byType(CustomPaint),),findsNWidgets(2));
|
||||
final Iterable<RenderBox> handles = tester.renderObjectList(find.descendant(
|
||||
of: find.byType(Overlay),
|
||||
matching: find.byType(CustomPaint),
|
||||
));
|
||||
|
||||
// The handle height is determined by the formula:
|
||||
// textLineHeight + _kSelectionHandleRadius * 2 - _kSelectionHandleOverlap .
|
||||
// The text line height will be the value of the fontSize.
|
||||
// The constant _kSelectionHandleRadius has the value of 6.
|
||||
// The constant _kSelectionHandleOverlap has the value of 1.5.
|
||||
// The handle height before scaling is 20.0 + 6 * 2 - 1.5 = 30.5.
|
||||
|
||||
final double handleHeightBeforeScaling = handles.first.size.height;
|
||||
expect(handleHeightBeforeScaling, 30.5);
|
||||
|
||||
final Offset handleHeightAfterScaling = handles.first.localToGlobal(const Offset(0, 30.5)) - handles.first.localToGlobal(Offset.zero);
|
||||
|
||||
// The handle height after scaling is 30.5 * 5 = 152.5
|
||||
expect(handleHeightAfterScaling, equals(const Offset(0.0, 152.5)));
|
||||
},
|
||||
skip: isBrowser, // [intended]
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.iOS}),
|
||||
);
|
||||
|
||||
testWidgetsWithLeakTracking('builds the default context menu by default', (WidgetTester tester) async {
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
Loading…
x
Reference in New Issue
Block a user