Horizontally expand text selection toolbar buttons in overflow menu (#144391)

## Description

This PR expands the items displayed in the overflow menu of a `TextSelectionToolbar` making buttons clickable in the blank area.

| Before | After |
|--------|--------|
| Each item has its own width | All items expand horizontally |
|  ![Capture d’écran 2024-02-29 à 14 43 57](https://github.com/flutter/flutter/assets/840911/f7379eef-9185-4cc4-bf14-e4c916c432b1) | ![Capture d’écran 2024-02-29 à 14 40 47](https://github.com/flutter/flutter/assets/840911/bff272cd-9fe2-4f07-adaf-61edef03d26e) | 

## Related Issue

Fixes https://github.com/flutter/flutter/issues/144089.

## Tests

Adds 1 tests.
This commit is contained in:
Bruno Leroux 2024-03-01 07:40:47 +01:00 committed by GitHub
parent 263ffe5f04
commit 8a312cd01a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 97 additions and 0 deletions

View file

@ -262,6 +262,7 @@ class AdaptiveTextSelectionToolbar extends StatelessWidget {
buttons.add(TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(i, buttonItems.length),
onPressed: buttonItem.onPressed,
alignment: AlignmentDirectional.centerStart,
child: Text(getButtonLabel(context, buttonItem)),
));
}

View file

@ -267,6 +267,7 @@ class _TextSelectionControlsToolbarState extends State<_TextSelectionControlsToo
children: itemDatas.asMap().entries.map((MapEntry<int, _TextSelectionToolbarItemData> entry) {
return TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(entry.key, itemDatas.length),
alignment: AlignmentDirectional.centerStart,
onPressed: entry.value.onPressed,
child: Text(entry.value.label),
);

View file

@ -569,6 +569,40 @@ class _RenderTextSelectionToolbarItemsLayout extends RenderBox with ContainerRen
size = nextSize;
}
// Horizontally expand the children when the menu overflows so they can react to
// pointer events into their whole area.
void _resizeChildrenWhenOverflow() {
if (!overflowOpen) {
return;
}
final RenderBox navButton = firstChild!;
int i = -1;
visitChildren((RenderObject renderObjectChild) {
final RenderBox child = renderObjectChild as RenderBox;
final ToolbarItemsParentData childParentData = child.parentData! as ToolbarItemsParentData;
i++;
// Ignore the navigation button.
if (renderObjectChild == navButton) {
return;
}
// There is no need to update children that won't be painted.
if (!_shouldPaintChild(renderObjectChild, i)) {
childParentData.shouldPaint = false;
return;
}
child.layout(
BoxConstraints.tightFor(width: size.width),
parentUsesSize: true,
);
});
}
@override
void performLayout() {
_lastIndexThatFits = -1;
@ -579,6 +613,7 @@ class _RenderTextSelectionToolbarItemsLayout extends RenderBox with ContainerRen
_layoutChildren();
_placeChildren();
_resizeChildrenWhenOverflow();
}
@override

View file

@ -291,4 +291,64 @@ void main() {
);
});
}
testWidgets('Overflowed menu expands children horizontally', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/144089.
late StateSetter setState;
final List<Widget> children = List<Widget>.generate(7, (int i) => const TestBox());
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: StatefulBuilder(
builder: (BuildContext context, StateSetter setter) {
setState = setter;
return TextSelectionToolbar(
anchorAbove: const Offset(50.0, 100.0),
anchorBelow: const Offset(50.0, 200.0),
children: children,
);
},
),
),
),
);
// All children fit on the screen, so they are all rendered.
expect(find.byType(TestBox), findsNWidgets(children.length));
expect(findOverflowButton(), findsNothing);
const String short = 'Short';
const String medium = 'Medium length';
const String long = 'Long label in the overflow menu';
// Adding several children makes the menu overflow.
setState(() {
children.addAll(const <Text>[
Text(short),
Text(medium),
Text(long),
]);
});
await tester.pumpAndSettle();
expect(findOverflowButton(), findsOneWidget);
// Tap the overflow button to show the overflow menu.
await tester.tap(findOverflowButton());
await tester.pumpAndSettle();
expect(find.byType(TestBox), findsNothing);
expect(find.byType(Text), findsNWidgets(3));
expect(findOverflowButton(), findsOneWidget);
Finder findToolbarContainer() {
return find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_TextSelectionToolbarContainer');
}
expect(findToolbarContainer(), findsAtLeastNWidgets(1));
// Buttons have their width set to the container width.
final double overflowMenuWidth = tester.getRect(findToolbarContainer()).width;
expect(tester.getRect(find.text(long)).width, overflowMenuWidth);
expect(tester.getRect(find.text(medium)).width, overflowMenuWidth);
expect(tester.getRect(find.text(short)).width, overflowMenuWidth);
});
}