Theodorus Agum Gumilang
Theodorus Agum Gumilang

Reputation: 1486

How to add Tabbar in SliverAppBar on Flutter?

Can we add TabBar in our SliverAppBar?

Since SliverAppBar has the bottom property, I thought we can add Tabbar in our SliverAppBar, but the problem is TabBar needs DefaultTabbarController and DefaultTabbarController only works in Material Widget and SliverAppbar only works in Scaffold Body, not in my appbar, but I need my scaffold body to have TabView. What is the explanation?

Upvotes: 4

Views: 10000

Answers (3)

Kapil Sahu
Kapil Sahu

Reputation: 599

Yes, you can add Tabbar inside SliverAppBar using NestedScrollView. You can also customise the appbar similar to that of whatsapp i.e floating appbar. Do make sure to add floatHeaderSlivers: true, in NestedScrollView for this. Link to working code

import 'dart:math';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: CustomSliverAppbar(),
    );
  }
}

class CustomSliverAppbar extends StatefulWidget {
  @override
  _CustomSliverAppbarState createState() => _CustomSliverAppbarState();
}

class _CustomSliverAppbarState extends State<CustomSliverAppbar>
    with SingleTickerProviderStateMixin {
  TabController _tabController;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        floatHeaderSlivers: true,
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverAppBar(
              title: Text(
                "WhatsApp type sliver appbar",
              ),
              centerTitle: true,
              pinned: true,
              floating: true,
              bottom: TabBar(
                  indicatorColor: Colors.black,
                  labelPadding: const EdgeInsets.only(
                    bottom: 16,
                  ),
                  controller: _tabController,
                  tabs: [
                    Text("TAB A"),
                    Text("TAB B"),
                  ]),
            ),
          ];
        },
        body: TabBarView(
          controller: _tabController,
          children: [
            TabA(),
            const Center(
              child: Text('Display Tab 2',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }
}

class TabA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scrollbar(
      child: ListView.separated(
        separatorBuilder: (context, child) => Divider(
          height: 1,
        ),
        padding: EdgeInsets.all(0.0),
        itemCount: 30,
        itemBuilder: (context, i) {
          return Container(
            height: 100,
            width: double.infinity,
            color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
          );
        },
      ),
    );
  }
}

enter image description here

Upvotes: 10

Dhiraj Sharma
Dhiraj Sharma

Reputation: 4859

You can wrap SilverAppBar(which has TabBar as bottom) & SilverFillRemaining(wraps TabBarView) with CustomScrollView. Then set CustomScrollView as body of Scaffold. For this you need to create a TabController.

Full example here:

import 'package:flutter/material.dart';

class SilverAppBarWithTabBarScreen extends StatefulWidget {
  @override
  _SilverAppBarWithTabBarState createState() => _SilverAppBarWithTabBarState();
}

class _SilverAppBarWithTabBarState extends State<SilverAppBarWithTabBarScreen>
    with SingleTickerProviderStateMixin {
  TabController controller;

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

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new CustomScrollView(
        slivers: <Widget>[
          new SliverAppBar(
            title: Text("Silver AppBar With ToolBar"),
            pinned: true,
            expandedHeight: 160.0,
            bottom: new TabBar(
              tabs: [
                new Tab(text: 'Tab 1'),
                new Tab(text: 'Tab 2'),
                new Tab(text: 'Tab 3'),
              ],
              controller: controller,
            ),
          ),
          new SliverFillRemaining(
            child: TabBarView(
              controller: controller,
              children: <Widget>[
                Text("Tab 1"),
                Text("Tab 2"),
                Text("Tab 3"),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Upvotes: 4

ap14
ap14

Reputation: 4721

I was able to achieve what you was asking. But I have only one problem, i.e., when I add a scrolling widget in TabView it doesn't produce the required result.

I have open an issue on GitHub.

Here is my code:

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => HomePageState();
}

class HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  TabController tabController;
  @override
  void initState() {
    super.initState();
    tabController = TabController(length: 2, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    Color tabColor = Theme.of(context).primaryColorDark;
    TextStyle tabStyle = TextStyle(color: tabColor);
    return SafeArea(
      child: Scaffold(
        body: CustomScrollView(
          slivers: <Widget>[
            SliverAppBar(
              title: Text("AppBar"),
              floating: true,
              primary: true,
              pinned: false,
            ),
            SliverFillRemaining(
              child: Scaffold(
                appBar: TabBar(
                controller: tabController,
                tabs: <Widget>[
                  Tab(
                    child: Text(
                      'Tab1',
                     style: tabStyle,
                    ),
                  ),
                  Tab(
                    child: Text(
                      'Tab2',
                    style: tabStyle,
                    ),
                  ),
                ],
              ),
                body: TabBarView(
                  controller: tabController,
                  children: <Widget>[
                    Scaffold(
                      body: Text('Tab One'),
                    ),
                    Scaffold(
                      body: Text('Tab Two'),
                    ),
                  ],
                ),
              ),
              ),
          ],
        ),
      ),
    );
  }
}

Emulator Screen

Upvotes: 1

Related Questions