1

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).

cekpowell
  • 27
  • 1
  • 5
  • Wrap the SingleChildScrollView with ``Expanded`` – M.A. Apr 06 '22 at 15:31
  • @pasha i am not using a SingleChildScrollView - where should I place it? – cekpowell Apr 06 '22 at 15:36
  • 1
    Expanded-> SingleChildScrollView -> ListView( make shrinkWrap: true and physics: NeverScrollableScrollPhysics() for ListView) – M.A. Apr 06 '22 at 15:43
  • No that hasnt done it - I get an error when using the Expanded widget and the SingleChildScrollView gives me two scrollable areas still. – cekpowell Apr 06 '22 at 15:53
  • @pasha would you be able to produce a code example for it? – cekpowell Apr 06 '22 at 16:31
  • https://stackoverflow.com/questions/71769800/implement-tabbar-under-appbar/71769862?noredirect=1#comment126833382_71769862 i just answered this here it is very similar – flutterloop Apr 06 '22 at 16:47

2 Answers2

1

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),
              ],
            ),




Erisan Olasheni
  • 2,395
  • 17
  • 20
griffins
  • 7,079
  • 4
  • 29
  • 54
  • sorry Im on mobile ,I'll fomart the answer once on a desktop – griffins Apr 06 '22 at 16:49
  • Yeah using `NestedScrollView` kind of works but it produces an exception (`The provided ScrollController is currently attached to more than one ScrollPosition`). – cekpowell Apr 06 '22 at 17:00
  • also @griffins - this works but it produces two scroll areas - the scroll area for the `NestedScrollView` or `CustomScrollView` and the scroll area for the `ListView` that I have in `TabView`. – cekpowell Apr 06 '22 at 18:05
  • let the parent widget be the only scrollable widget set physcis on other scrollable components and remove the scroll controller from them i.e `physics:NeverScrollablePhysics(),` – griffins Apr 07 '22 at 03:47
  • what about tabbarview inside has listview ? – Nicholas Jela Aug 02 '23 at 14:07
1

I have scratched my head around TabBarView many times with a question 'How to put dynamic-height content inside TabBarView while it is inside a scrollable container?'

Unfortunately, it is broken for such use cases as the scroll and does not work correctly and the content overflows.

Hence, my solution answers the OP question by answering how to achieve the whole page scrollable. It replaces TabBarView with different mechanism.

My solution is to skip TabBarView and use TabBar and TabController to get a Widget from the Widget list.

Code:

// STATEFUL WIDGET CODE

late TabController tabController; // USED FOR NAVIGATION

@override
void initState() {
    tabController = TabController(
      initialIndex: 0,
      length: 3, // adjust your length
      vsync: this,
    );
    super.initState();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
               appBar: // some app bar
               body: SingleChildScrollView(
                    Column(
                        children: [
                            TabBar(controller: tabController),
                            _getTabAtIndex(tabContoller.index),
                        ]
                    )
                )

    );
}

Widget _getTabAtIndex(int index) {
   var list = [
     Container(), // FIRST ITEM
     Container(), // SECOND ITEM
     Container(), // THIRD ITEM
   ];
   return list[index];
}
Michał Dobi Dobrzański
  • 1,449
  • 1
  • 20
  • 19