How to create a bounded scrollable TabBarView

On top of Miguel Ruvio's answer replacing the ListView in the body with the TabBarView gets you almost all of the way per D.R.'s comment. I did get some overflow issues when one of my widgets in the was wrapped in a column. Replacing that with ListView per this example

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: Tabs()));
}

class Tabs extends StatefulWidget {
  @override
  _RoomTabsState createState() => _RoomTabsState();
}

class _RoomTabsState extends State<Tabs> with TickerProviderStateMixin {
  var _scrollViewController;
  var _tabController;

  @override
  void initState() {
    super.initState();
    _scrollViewController = ScrollController();
    _tabController = TabController(vsync: this, length: 2);
  }

  @override
  Widget build(BuildContext context) {
    return NestedScrollView(
      controller: _scrollViewController,
      headerSliverBuilder: (context, bool) => [
        SliverAppBar(
          bottom: TabBar(
            controller: _tabController,
            tabs: [
              Tab(text: "All"),
              Tab(text: "Living room"),
            ],
          ),
        ),
      ],
      body: TabBarView(
        controller: _tabController,
        children: [
          ListView(children: [
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
          ]),
          Text("test"),
        ],
      ),
    );
  }
}

comming from this github issue.


Please try this code below:

     NestedScrollView(
      headerSliverBuilder: (context, value) {
        return [
          SliverToBoxAdapter(
              child: Header()
          ),
          SliverToBoxAdapter(
            child: TabBar(
              controller: _controller,
              tabs: [
                Tab(icon: Icon(Icons.x)),
                Tab(icon: Icon(Icons.y)),
                Tab(icon: Icon(Icons.z)),
              ],
            ),
          ),
        ];
      },
    body: Container(
      child: TabBarView(
        controller: _controller,
        children: <Widget>[
          page1(),
          page2(),
          page3(),
        ],
      ),
    ),
  )


enter image description here

class SliverWithTabBar extends StatefulWidget {
  @override
  _SliverWithTabBarState createState() => _SliverWithTabBarState();
}

class _SliverWithTabBarState extends State<SliverWithTabBar> with SingleTickerProviderStateMixin {
  TabController controller;

  @override
  void initState() {
    super.initState();
    controller = TabController(length: 3, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return [
            SliverAppBar(
              pinned: false,
              backgroundColor: Colors.white,
              flexibleSpace: FlexibleSpaceBar(
                collapseMode: CollapseMode.pin,
                background: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Container(
                      height: 200.0,
                      width: double.infinity,
                      color: Colors.grey,
                      child: FlutterLogo(),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(10.0),
                      child: Text(
                        'Business Office',
                        style: TextStyle(fontSize: 25.0),
                        textAlign: TextAlign.left,
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(10.0),
                      child: Text(
                        'Open now\nStreet Address, 299\nCity, State',
                        style: TextStyle(fontSize: 15.0),
                        textAlign: TextAlign.left,
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(right: 10.0),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: <Widget>[
                          Icon(Icons.share),
                          Padding(
                            padding: const EdgeInsets.only(left: 10.0),
                            child: Icon(Icons.favorite),
                          ),
                        ],
                      ),
                    )
                  ],
                ),
              ),
              expandedHeight: 380.0,
              bottom: TabBar(
                indicatorColor: Colors.black,
                labelColor: Colors.black,
                tabs: [
                  Tab(text: 'POSTS'),
                  Tab(text: 'DETAILS'),
                  Tab(text: 'FOLLOWERS'),
                ],
                controller: controller,
              ),
            )
          ];
        },
        body: ListView.builder(
          itemCount: 100,
          itemBuilder: (BuildContext context, int index) {
            return Card(
              color: index % 2 == 0 ? Colors.blue : Colors.green,
              child: Container(
                alignment: Alignment.center,
                width: double.infinity,
                height: 100.0,
                child: Text(
                  'Flutter is awesome',
                  style: TextStyle(fontSize: 18.0),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

You should look for Sliver widgets to achieve NestedScrollView.

That gives you a headerSliverBuilder property where you can actually fit some headers that you might hide or pin on the top of the screen when the body widget is scrolled, in this particular example, a ListView.

You might want to take a look to the RenderSliver documentation.