Unverified Commit be8a1eac authored by Qun Cheng's avatar Qun Cheng Committed by GitHub

Add scrollbar for menus (#140941)

Fixes #140162

This PR is to add a scrollbar for MenuAnchor and DropdownMenu for all platforms when height is limited. Previously, a scrollbar only shows on desktop platforms. This PR also disabled scrollbar's overscroll for MenuAnchor and DropdownMenu.

<img src="https://github.com/flutter/flutter/assets/36861262/9ca3d4d0-415f-43bf-9d2b-df96a42db620" width="250"/><img src="https://github.com/flutter/flutter/assets/36861262/18da8d02-586b-4aa4-b647-927691542429" width="350"/>
parent e256d491
...@@ -26,6 +26,7 @@ import 'menu_button_theme.dart'; ...@@ -26,6 +26,7 @@ import 'menu_button_theme.dart';
import 'menu_style.dart'; import 'menu_style.dart';
import 'menu_theme.dart'; import 'menu_theme.dart';
import 'radio.dart'; import 'radio.dart';
import 'scrollbar.dart';
import 'text_button.dart'; import 'text_button.dart';
import 'text_theme.dart'; import 'text_theme.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -3369,6 +3370,8 @@ class _MenuPanel extends StatefulWidget { ...@@ -3369,6 +3370,8 @@ class _MenuPanel extends StatefulWidget {
} }
class _MenuPanelState extends State<_MenuPanel> { class _MenuPanelState extends State<_MenuPanel> {
ScrollController scrollController = ScrollController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final MenuStyle? themeStyle; final MenuStyle? themeStyle;
...@@ -3454,7 +3457,18 @@ class _MenuPanelState extends State<_MenuPanel> { ...@@ -3454,7 +3457,18 @@ class _MenuPanelState extends State<_MenuPanel> {
clipBehavior: widget.clipBehavior, clipBehavior: widget.clipBehavior,
child: Padding( child: Padding(
padding: resolvedPadding, padding: resolvedPadding,
child: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(
scrollbars: false,
overscroll: false,
physics: const ClampingScrollPhysics(),
),
child: PrimaryScrollController(
controller: scrollController,
child: Scrollbar(
thumbVisibility: true,
child: SingleChildScrollView( child: SingleChildScrollView(
controller: scrollController,
scrollDirection: widget.orientation, scrollDirection: widget.orientation,
child: Flex( child: Flex(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
...@@ -3466,6 +3480,9 @@ class _MenuPanelState extends State<_MenuPanel> { ...@@ -3466,6 +3480,9 @@ class _MenuPanelState extends State<_MenuPanel> {
), ),
), ),
), ),
),
),
),
); );
if (widget.crossAxisUnconstrained) { if (widget.crossAxisUnconstrained) {
......
...@@ -1907,6 +1907,31 @@ void main() { ...@@ -1907,6 +1907,31 @@ void main() {
expect(selectionCount, 1); expect(selectionCount, 1);
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
}); });
testWidgets('Menu shows scrollbar when height is limited', (WidgetTester tester) async {
final List<DropdownMenuEntry<TestMenu>> menuItems = <DropdownMenuEntry<TestMenu>>[
DropdownMenuEntry<TestMenu>(
value: TestMenu.mainMenu0,
label: 'Item 0',
style: MenuItemButton.styleFrom(
minimumSize: const Size.fromHeight(1000),
)
),
];
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: DropdownMenu<TestMenu>(
dropdownMenuEntries: menuItems,
),
),
));
await tester.tap(find.byType(DropdownMenu<TestMenu>));
await tester.pumpAndSettle();
expect(find.byType(Scrollbar), findsOneWidget);
}, variant: TargetPlatformVariant.all());
} }
enum TestMenu { enum TestMenu {
......
...@@ -472,10 +472,9 @@ void main() { ...@@ -472,10 +472,9 @@ void main() {
await tester.tap(find.text('Main Menu')); await tester.tap(find.text('Main Menu'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(Scrollbar), findsOneWidget);
// Test Scrollbar thumb color. // Test Scrollbar thumb color.
expect( expect(
find.byType(Scrollbar), find.byType(Scrollbar).last,
paints..rrect(color: const Color(0xffff0000)), paints..rrect(color: const Color(0xffff0000)),
); );
...@@ -521,10 +520,9 @@ void main() { ...@@ -521,10 +520,9 @@ void main() {
await tester.tap(find.text('Main Menu')); await tester.tap(find.text('Main Menu'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(Scrollbar), findsOneWidget);
// Scrollbar thumb color should be updated. // Scrollbar thumb color should be updated.
expect( expect(
find.byType(Scrollbar), find.byType(Scrollbar).last,
paints..rrect(color: const Color(0xff00ff00)), paints..rrect(color: const Color(0xff00ff00)),
); );
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment