Search code examples
flutterdartlayoutflutter-layoutoverflow

TabBarView within Scrollable Page


I am having real trouble getting a layout to work within Flutter.

The layout I am trying to create:

  • A ListView that contains a:
    • A Container.
    • A TabBar.
    • A TabBarView, where each TabBarView contains a Column.
  • And I want the whole page to be scrollable.

Here is the schematic for the layout:

Example Code

Here is a minimum example code (with exact widget definitions removed):

return DefaultTabController(
      length: 2,
      child: ListView(
        children: [
          // TOP CONTAINER //
          Container(height: 30),

          // TAB BAR //

          const TabBar(tabs: [
            Tab(child: Text("Tab 1")),
            Tab(child: Text("Tab 2")),
          ]),

          // TAB BAR VIEWS //
          SizedBox(
            height: MediaQuery.of(context).size.height,
            child: TabBarView(
              children: [
                Container(height: 5000),
                Container(height: 5000),
              ],
            ),
          )
        ],
      ),
    );

The Problem:

When the height of the window gets smaller, I get an overflow error at the bottom:

What I have Done:

  • I first tried converting the inner Column into a ListView, which fixed the overflow, but resulted in two separate scrollable areas (the individual tab views and the whole page), which is not what I want - I want a single scrollable area. Setting the physics property of this ListView to NeverScrollablePhysics() doesnt fix this and results in some weird behaviour.
  • I tried using a NestedScrollView with Silvers (from How to create a bounded scrollable TabBarView). But this results in the following exception when navigating through the tabs: The provided ScrollController is currently attached to more than one ScrollPosition., and produces some dodgy scroll mechanics.
  • I tried using a CustomScrollView but that didnt work.

Similar Questions that Didnt provide a working solution:

I am very confused as to why it is not working as I feel this is a very simple thing. Essentially, it is the same layout used in the Instragram app (among others) when viewing your personal profile (see: https://unblast.com/wp-content/uploads/2020/01/Instagram-UI-Profile-1.jpg).


Solution

  • From the comments you can wrap your page in a singlechildscrollview, disable scroll physics for the listview as the parent is already scrollable.

    return SingleChildScrollView(
      child: DefaultTabController(
        length: 2,
        child: ListView(
          physics: NeverScrollableScrollPhysics(),
            children: [
              // TOP CONTAINER //
              Container(height: 30),
    
              // TAB BAR //
              const TabBar(tabs: [
                Tab(child: Text("Tab 1")),
                Tab(child: Text("Tab 2")),
              ]),
    
              // TAB BAR VIEWS //
              SizedBox(
                height: MediaQuery.of(context).size.height,
                child: TabBarView(
                  children: [
                    Container(height: 5000),
                    Container(height: 5000),
                  ],
                ),
              )
            ],
          ),
        ));
    

    ** Option2 **

    you can use a customScrollView or a nestedScrollView

    DefaultTabController(
          length: 2,
          child:
    CustomScrollView(
                  slivers: [
    
    SlivertoboxAdapter(child:   // TOP CONTAINER //
              Container(height: 30),
    
    SlivertoboxAdapter(child:   // TAB BAR //
    
              const TabBar(tabs: [
                Tab(child: Text("Tab 1")),
                Tab(child: Text("Tab 2")),
              ]),
    
    //... Sliverfillremaining/slivettoboxadapter for your tabbarview
    
    SlivertoboxAdapter(child:TabBarView(
                  children: [
                    Container(height: 5000),
                    Container(height: 5000),
                  ],
                ),