Skip to content

Making NavigationRail scrollable #89167

Description

@Yivan000

Use case

By default, when a NavigationRailDestination inside a NavigationRail goes out of the viewport (e.g. the window is resized to be less than the destinations), it will overflow and cause the infamous yellow-and-black-striped error to occur. One way to fix this is to make the NavigationRail itself scrollable. Currently, the only way to do this is rather janky.

Error-causing:

Row(
  children: [
    NavigationRail(...),
    ...
  ]
)

The "fix" that makes it scrollable:

Row(
  children: [
    LayoutBuilder(
      builder: (context, constraints) {
        return SingleChildScrollView(
          child: ConstrainedBox(
            constraints: BoxConstraints(
              minHeight: constraints.maxHeight,
            ),
            child: IntrinsicHeight(
              child: NavigationRail(...)
            )
          )
        );
      }
    ),
    ...
  ]
)

This fix is mentioned in this StackOverflow answer and is also used by Flutter Gallery. However, it is janky and cluttered. It even uses an IntrinsicHeight wherein the docs explicitly state that:

In the class constructor:

This class is relatively expensive. Avoid using it where possible.

In the class itself:

This class is relatively expensive, because it adds a speculative layout pass before the final layout phase. Avoid using it where possible. In the worst case, this widget can result in a layout that is O(N²) in the depth of the tree.

I want to avoid using widgets that the docs itself says not to use. However, I cannot find any other way to make it scrollable to avoid the infamous yellow-and-black-striped error.

Proposal

I propose a simple solution to this problem:

Row(
  children: [
    NavigationRail(
      scrollable: true,
      ...
    ),
    ...
  ]
)

Not only does this makes things easier and intuitive, but is also easily customizable and less clutter.

Activity

  1. TahaTesser commented on Aug 31, 2021

    @TahaTesser
    Contributor

    Hi @yivanscreations
    Thanks for filing the proposals.

    minimal code sample
    import 'package:flutter/material.dart';
    
    void main() => runApp(const MyApp());
    
    /// This is the main application widget.
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      static const String _title = 'Flutter Code Sample';
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          title: _title,
          home: MyStatefulWidget(),
        );
      }
    }
    
    /// This is the stateful widget that the main application instantiates.
    class MyStatefulWidget extends StatefulWidget {
      const MyStatefulWidget({Key? key}) : super(key: key);
    
      @override
      State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
    }
    
    /// This is the private State class that goes with MyStatefulWidget.
    class _MyStatefulWidgetState extends State<MyStatefulWidget> {
      int _selectedIndex = 0;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Row(
            children: <Widget>[
              NavigationRail(
                
                selectedIndex: _selectedIndex,
                onDestinationSelected: (int index) {
                  setState(() {
                    _selectedIndex = index;
                  });
                },
                labelType: NavigationRailLabelType.selected,
                destinations: const <NavigationRailDestination>[
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite_border),
                    selectedIcon: Icon(Icons.favorite),
                    label: Text('First'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.bookmark_border),
                    selectedIcon: Icon(Icons.book),
                    label: Text('Second'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.star_border),
                    selectedIcon: Icon(Icons.star),
                    label: Text('Third'),
                  ),
                ],
              ),
              const VerticalDivider(thickness: 1, width: 1),
              // This is the main content.
              Expanded(
                child: Center(
                  child: Text('selectedIndex: $_selectedIndex'),
                ),
              )
            ],
          ),
        );
      }
    }
  2. added
    p: material_uimaterial_ui package in flutter/packages
    frameworkflutter/packages/flutter repository. See also f: labels.
    c: proposalA detailed proposal for a change to Flutter
    and removed
    in triagePresently being triaged by the triage team
    on Aug 31, 2021
  3. maheshj01 commented on Aug 31, 2021

    @maheshj01
    Member

    Assigning this issue to myself will send a PR tonight

  4. self-assigned this
    on Aug 31, 2021
  5. removed their assignment
    on Jul 4, 2022
  6. DaedalusDelta commented on Feb 23, 2023

    @DaedalusDelta

    Hi, I'm also a web-developer. Just wondering if the proposed scrollable feature would be added any time soon.

  7. 4 remaining items

  8. adonisRodxander commented on Feb 27, 2025

    @adonisRodxander
  9. flutter-triage-bot commented on Feb 27, 2025

    @flutter-triage-bot

    This issue is missing a priority label. Please set a priority label when adding the triaged-design label.

  10. added
    P3Issues that are less important to the Flutter project
    triaged-designTriaged by Design Languages team
    on Feb 27, 2025
  11. pogojotz commented on Jun 2, 2025

    @pogojotz
    Contributor

    Hi. I recently added a PR for this issue. After some research into the previously merged and reverted PR #137415, I decided to reuse it with the "scrollable" feature being optional, so that existing code will not break. Users have to opt-in, by setting the flag.

    The PR adds two further flags, leadingAtTop and trailingAtBottom. For a justification, see my comment in the PR.

  12. added a commit that references this issue on Jun 27, 2025
    5b6b621
  13. github-actions commented on Jul 11, 2025

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  14. locked as resolved and limited conversation to collaborators on Jul 11, 2025
  15. added a commit that references this issue on Jul 21, 2025
    cd6b46e
  16. added a commit that references this issue on Nov 21, 2025
    715ec0f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectc: proposalA detailed proposal for a change to Flutterframeworkflutter/packages/flutter repository. See also f: labels.p: material_uimaterial_ui package in flutter/packagesr: fixedIssue is closed as already fixed in a newer versionteam-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions