Skip to content

Commit fd7f45a

Browse files
authored
Add SingleChildScrollView for NavigationRail (#137415)
## Description Add `SingleChildScrollView` to `NavigationRail` for scrolling. Closes: #89167
1 parent f727948 commit fd7f45a

File tree

2 files changed

+117
-56
lines changed

2 files changed

+117
-56
lines changed

packages/flutter/lib/src/material/navigation_rail.dart

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -439,39 +439,41 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
439439
Expanded(
440440
child: Align(
441441
alignment: Alignment(0, groupAlignment),
442-
child: Column(
443-
mainAxisSize: MainAxisSize.min,
444-
children: <Widget>[
445-
for (int i = 0; i < widget.destinations.length; i += 1)
446-
_RailDestination(
447-
minWidth: minWidth,
448-
minExtendedWidth: minExtendedWidth,
449-
extendedTransitionAnimation: _extendedAnimation,
450-
selected: widget.selectedIndex == i,
451-
icon: widget.selectedIndex == i ? widget.destinations[i].selectedIcon : widget.destinations[i].icon,
452-
label: widget.destinations[i].label,
453-
destinationAnimation: _destinationAnimations[i],
454-
labelType: labelType,
455-
iconTheme: widget.selectedIndex == i ? selectedIconTheme : effectiveUnselectedIconTheme,
456-
labelTextStyle: widget.selectedIndex == i ? selectedLabelTextStyle : unselectedLabelTextStyle,
457-
padding: widget.destinations[i].padding,
458-
useIndicator: useIndicator,
459-
indicatorColor: useIndicator ? indicatorColor : null,
460-
indicatorShape: useIndicator ? indicatorShape : null,
461-
onTap: () {
462-
if (widget.onDestinationSelected != null) {
463-
widget.onDestinationSelected!(i);
464-
}
465-
},
466-
indexLabel: localizations.tabLabel(
467-
tabIndex: i + 1,
468-
tabCount: widget.destinations.length,
442+
child: SingleChildScrollView(
443+
child: Column(
444+
mainAxisSize: MainAxisSize.min,
445+
children: <Widget>[
446+
for (int i = 0; i < widget.destinations.length; i += 1)
447+
_RailDestination(
448+
minWidth: minWidth,
449+
minExtendedWidth: minExtendedWidth,
450+
extendedTransitionAnimation: _extendedAnimation,
451+
selected: widget.selectedIndex == i,
452+
icon: widget.selectedIndex == i ? widget.destinations[i].selectedIcon : widget.destinations[i].icon,
453+
label: widget.destinations[i].label,
454+
destinationAnimation: _destinationAnimations[i],
455+
labelType: labelType,
456+
iconTheme: widget.selectedIndex == i ? selectedIconTheme : effectiveUnselectedIconTheme,
457+
labelTextStyle: widget.selectedIndex == i ? selectedLabelTextStyle : unselectedLabelTextStyle,
458+
padding: widget.destinations[i].padding,
459+
useIndicator: useIndicator,
460+
indicatorColor: useIndicator ? indicatorColor : null,
461+
indicatorShape: useIndicator ? indicatorShape : null,
462+
onTap: () {
463+
if (widget.onDestinationSelected != null) {
464+
widget.onDestinationSelected!(i);
465+
}
466+
},
467+
indexLabel: localizations.tabLabel(
468+
tabIndex: i + 1,
469+
tabCount: widget.destinations.length,
470+
),
471+
disabled: widget.destinations[i].disabled,
469472
),
470-
disabled: widget.destinations[i].disabled,
471-
),
472-
if (widget.trailing != null)
473-
widget.trailing!,
474-
],
473+
if (widget.trailing != null)
474+
widget.trailing!,
475+
],
476+
),
475477
),
476478
),
477479
),

packages/flutter/test/material/navigation_rail_test.dart

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3605,6 +3605,62 @@ void main() {
36053605
expect(inkFeatures, paints..circle(color: Colors.transparent));
36063606
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
36073607

3608+
testWidgets('NavigationRail can scroll in low height', (WidgetTester tester) async {
3609+
// This is a regression test for https://github.com/flutter/flutter/issues/89167.
3610+
await tester.pumpWidget(
3611+
MaterialApp(
3612+
theme: ThemeData(
3613+
useMaterial3: true,
3614+
),
3615+
home: Builder(
3616+
builder: (BuildContext context) {
3617+
return MediaQuery(
3618+
// Set Screen height with 300
3619+
data: MediaQuery.of(context).copyWith(size: const Size(800, 300)),
3620+
child: Scaffold(
3621+
body: Row(
3622+
children: <Widget>[
3623+
// Set NavigationRail height with 100
3624+
SizedBox(
3625+
height: 100,
3626+
child: NavigationRail(
3627+
selectedIndex: 0,
3628+
destinations: const <NavigationRailDestination>[
3629+
NavigationRailDestination(
3630+
icon: Icon(Icons.favorite_border),
3631+
selectedIcon: Icon(Icons.favorite),
3632+
label: Text('Abc'),
3633+
),
3634+
NavigationRailDestination(
3635+
icon: Icon(Icons.bookmark_border),
3636+
selectedIcon: Icon(Icons.bookmark),
3637+
label: Text('Def'),
3638+
),
3639+
NavigationRailDestination(
3640+
icon: Icon(Icons.star_border),
3641+
selectedIcon: Icon(Icons.star),
3642+
label: Text('Ghi'),
3643+
),
3644+
],
3645+
),
3646+
),
3647+
const Expanded(
3648+
child: Text('body'),
3649+
),
3650+
],
3651+
),
3652+
),
3653+
);
3654+
},
3655+
),
3656+
),
3657+
);
3658+
3659+
final ScrollableState scrollable = tester.state(find.byType(Scrollable));
3660+
scrollable.position.jumpTo(500.0);
3661+
expect(scrollable.position.pixels, equals(500.0));
3662+
});
3663+
36083664
group('Material 2', () {
36093665
// These tests are only relevant for Material 2. Once Material 2
36103666
// support is deprecated and the APIs are removed, these tests
@@ -5433,31 +5489,34 @@ TestSemantics _expectedSemantics() {
54335489
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
54345490
children: <TestSemantics>[
54355491
TestSemantics(
5436-
flags: <SemanticsFlag>[
5437-
SemanticsFlag.isSelected,
5438-
SemanticsFlag.isFocusable,
5492+
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
5493+
children: <TestSemantics>[
5494+
TestSemantics(
5495+
flags: <SemanticsFlag>[SemanticsFlag.isSelected,
5496+
SemanticsFlag.isFocusable],
5497+
actions: <SemanticsAction>[SemanticsAction.tap],
5498+
label: 'Abc\nTab 1 of 4',
5499+
textDirection: TextDirection.ltr,
5500+
),
5501+
TestSemantics(
5502+
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
5503+
actions: <SemanticsAction>[SemanticsAction.tap],
5504+
label: 'Def\nTab 2 of 4',
5505+
textDirection: TextDirection.ltr,
5506+
),
5507+
TestSemantics(
5508+
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
5509+
actions: <SemanticsAction>[SemanticsAction.tap],
5510+
label: 'Ghi\nTab 3 of 4',
5511+
textDirection: TextDirection.ltr,
5512+
),
5513+
TestSemantics(
5514+
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
5515+
actions: <SemanticsAction>[SemanticsAction.tap],
5516+
label: 'Jkl\nTab 4 of 4',
5517+
textDirection: TextDirection.ltr,
5518+
),
54395519
],
5440-
actions: <SemanticsAction>[SemanticsAction.tap],
5441-
label: 'Abc\nTab 1 of 4',
5442-
textDirection: TextDirection.ltr,
5443-
),
5444-
TestSemantics(
5445-
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
5446-
actions: <SemanticsAction>[SemanticsAction.tap],
5447-
label: 'Def\nTab 2 of 4',
5448-
textDirection: TextDirection.ltr,
5449-
),
5450-
TestSemantics(
5451-
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
5452-
actions: <SemanticsAction>[SemanticsAction.tap],
5453-
label: 'Ghi\nTab 3 of 4',
5454-
textDirection: TextDirection.ltr,
5455-
),
5456-
TestSemantics(
5457-
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
5458-
actions: <SemanticsAction>[SemanticsAction.tap],
5459-
label: 'Jkl\nTab 4 of 4',
5460-
textDirection: TextDirection.ltr,
54615520
),
54625521
TestSemantics(
54635522
label: 'body',

0 commit comments

Comments
 (0)