Consider this image. As you can see it has an appbar and the appbar has Tabbed buttons. Am trying to animate the appbar so that it hides on scrollup and leaves only the Tab Buttons showing and on scrollup the appbar apears. Please help me out. Sorry for bad english and not American neither am I English
Asked
Active
Viewed 8.3k times
6 Answers
95
If I understood you correctly, following code should make the app bar hide on scroll while TabBar remains visible:
Null safe code:
class _SomePageState extends State<SomePage> with SingleTickerProviderStateMixin {
late final TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
title: Text('Weight Tracker'),
pinned: true,
floating: true,
forceElevated: innerBoxIsScrolled,
bottom: TabBar(
tabs: <Tab>[
Tab(text: 'STATISTICS'),
Tab(text: 'HISTORY'),
],
controller: _tabController,
),
),
];
},
body: TabBarView(
controller: _tabController,
children: <Widget>[
StatisticsPage(),
HistoryPage(),
],
),
),
);
}
}
Example coming from this post.

CopsOnRoad
- 237,138
- 77
- 654
- 440

Marcin Szałek
- 4,609
- 5
- 30
- 43
-
-
4
-
22I use `adb shell screenrecord`, then `adb pull` then I convert mp4 to gif online using https://ezgif.com/video-to-gif – Marcin Szałek Feb 09 '19 at 01:46
-
-
2Hey @MarcinSzałek , when I scroll one page (eg Statistics) , the other page also gets scrolled. How do I avoid this? – bimsina Nov 09 '19 at 12:27
-
4
-
Do the scrollViewControllers interfere with each other, If i have another scrollview inside my actual view(childern of TabBarView)? I am not able to get floating property to work. – Soropromo Feb 29 '20 at 13:44
-
thanks for your answer, but with this way Refresh Indicator view starts from the top of App Bar how can I show Refresh Indicator starts from bottom of App Bar? is it possible? – Reza Esfandiari Apr 10 '20 at 09:30
-
Do you know how to make another container or a row below the tabbar to stay just like the tab bar when scrolling leaving the other list items to scroll? – The Keeper Jun 11 '20 at 12:54
9
Using DefaultTabController
DefaultTabController(
length: 2,
child: new Scaffold(
body: new NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
new SliverAppBar(
title: Text("Application"),
floating: true,
pinned: true,
snap: true,
bottom: new TabBar(
tabs: <Tab>[
new Tab(text: "T"),
new Tab(text: "B"),
], // <-- total of 2 tabs
),
),
];
},
body: new TabBarView(
children: <Widget>[
Center(
child: Text(
'T Tab',
style: TextStyle(fontSize: 30),
)),
Center(
child: Text(
'B Tab',
style: TextStyle(fontSize: 30),
)),
],
),
),
),
);
Output:

Jitesh Mohite
- 31,138
- 12
- 157
- 147
8
I suggest that you have to go through SliverAppBar and SliverList To achieve your layout. Following code may help you to understand that.
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
List buildTextViews(int count) {
List<Widget> strings = List();
for (int i = 0; i < count; i++) {
strings.add(new Padding(padding: new EdgeInsets.all(16.0),
child: new Text("Item number " + i.toString(),
style: new TextStyle(fontSize: 20.0))));
}
return strings;
}
return Scaffold(
body: new CustomScrollView(slivers: <Widget>[
const SliverAppBar(
title: const Text('Sliver App Bar'),
),
new SliverList(
delegate: new SliverChildListDelegate(buildTextViews(50)))
])
);
}
}

Viren V Varasadiya
- 25,492
- 9
- 45
- 61
6
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
bool _showAppbar = true;
ScrollController _scrollBottomBarController = new ScrollController();
bool isScrollingDown = false;
bool _show = true;
double bottomBarHeight = 75;
double _bottomBarOffset = 0;
@override
void initState() {
super.initState();
myScroll();
}
@override
void dispose() {
super.dispose();
_scrollBottomBarController.removeListener(() {});
super.dispose();
}
void showBottomBar() {
setState(() {
_show = true;
});
}
void hideBottomBar() {
setState(() {
_show = false;
});
}
void myScroll() async {
_scrollBottomBarController.addListener(() {
if (_scrollBottomBarController.position.userScrollDirection ==
ScrollDirection.reverse) {
if (!isScrollingDown) {
isScrollingDown = true;
_showAppbar = false;
hideBottomBar();
}
}
if (_scrollBottomBarController.position.userScrollDirection ==
ScrollDirection.forward) {
if (isScrollingDown) {
isScrollingDown = false;
_showAppbar = true;
showBottomBar();
}
}
});
}
Widget containterContent(){
return Container(
height: 50.0,
color: Colors.cyanAccent,
margin: EdgeInsets.all(8.0),
width: MediaQuery.of(context).size.width - 100,
child: Center(child: Text('Item 1',
style: TextStyle(
fontSize: 14.0,
),)),
);
}
Widget body() {
return ListView(
controller: _scrollBottomBarController,
children: <Widget>[
containterContent(),
containterContent(),
containterContent(),
containterContent(),
containterContent(),
containterContent(),
containterContent(),
containterContent(),
containterContent(),
containterContent(),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: _showAppbar
? AppBar(
title: Text('My Tasks'),
)
: PreferredSize(
child: Container(),
preferredSize: Size(0.0, 0.0),
),
bottomNavigationBar: Container(
height: bottomBarHeight,
width: MediaQuery.of(context).size.width,
child: _show
?BottomNavigationBar(
currentIndex: 0, // this will be set when a new tab is tapped
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.home),
title: new Text('Home'),
),
BottomNavigationBarItem(
icon: new Icon(Icons.mail),
title: new Text('Messages'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person), title: Text('Profile'))
],
)
: Container(
color: Colors.white,
width: MediaQuery.of(context).size.width,
),
),
body: body(
),
);
}
}

Snehal Masalkar
- 69
- 1
- 1
-
1Welcome to Stack Overflow! While this code snippet may be the solution, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Johan Jun 07 '19 at 11:07
-
1Thanks for the solution! this is the only one without using any sliver – Sohel Mahmud Nov 23 '21 at 18:01
5
I was able to make the floating appbar with tabbar similar to that of WhatsApp by using SliverAppbar with NestedScrollView.
Do add floatHeaderSlivers: true, in NestedScrollView and
pinned: true, floating: true, in SliverAppBar
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)],
);
},
),
);
}
}

Kapil Sahu
- 469
- 7
- 14
2
Screenshot:
Code:
As I mentioned here, you can use NestedScrollView
like this:
@override
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
length: 2,
child: NestedScrollView(
headerSliverBuilder: (context, value) {
return [
SliverAppBar(
title: Text('AppBar'),
pinned: true,
floating: true,
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.call), text: 'Call'),
Tab(icon: Icon(Icons.message), text: 'Message'),
],
),
),
];
},
body: TabBarView(
children: [
FlutterLogo(),
FlutterLogo(),
],
),
),
),
);
}

CopsOnRoad
- 237,138
- 77
- 654
- 440