Reputation: 1360
I have a list of blog posts in the body and bottom navigation bar. I want to hide bottom navigation bar with a slide down animation when the posts list is scrolled down and visible with a slide up animation when scrolled up. How to do it?
Upvotes: 19
Views: 50144
Reputation: 2423
My team developed a package called: hidable. Which basically makes it easy to add scroll-to-hide functionality to any static-located widget.
Depend on it:
dependencies:
hidable: ^1.0.3
Create a scroll controller, inside your widget state:
final ScrollController scrollController = ScrollController();
Pass that scrollController
to your scrollable widget - (ListView, SingleChildScrollView and etc.)
ListView(
controller: scrollController,
...
)
Then wrap your BottomNavigationBar
with the Hidable
widget:
Hidable(
controller: scrollController,
wOpacity: true, // As default it's true.
size: 56, // As default it's 56.
child: BottomNavigationBar(...),
)
For more information refer to the GitHub page -> https://github.com/insolite-dev/hidable
Upvotes: 7
Reputation: 1599
I was able to do it in my application using Provider. This is how I proceeded:
1- Create your provider class, that will have to method to switch the visibility of your bottom bar and notify the listeners:
class BottomBarVisibilityProvider extends ChangeNotifier {
bool isVisible = true;
void show() {
if (!isVisible) {
isVisible = true;
notifyListeners();
}
}
void hide() {
if (isVisible) {
isVisible = false;
notifyListeners();
}
}
}
2- add a consumer of this class on top of your bottom bar, the AnimatedContainer will consume the visibility status from your provider and set if you want to display the bottomBar or not depending on the scroll direction of your scrollable widget:
bottomNavigationBar: Consumer<BottomBarVisibilityProvider>(
builder: (context, bottomBarVisibilityProvider, child) =>
AnimatedContainer(
duration: const Duration(milliseconds: 200),
child: bottomBarVisibilityProvider.isVisible
? Wrap(
children: const [BottomBar()],
)
: Wrap(),
),
),
3- Another consumer now, but for the scrollable widget: and note that in this widget your need to add a listener for your scrollController so your could change the visibility status in your provider depending on the user scroll direction:
return Consumer<BottomBarVisibilityProvider>(
builder: (context, bottomBarVisibilityProvider, child) {
scrollController.addListener(() {
final direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
if (!bottomBarVisibilityProvider.isVisible) {
bottomBarVisibilityProvider.show();
}
} else if (direction == ScrollDirection.reverse) {
if (bottomBarVisibilityProvider.isVisible) {
bottomBarVisibilityProvider.hide();
}
}
});
return ListView.builder(
key: eventListKey,
controller: scrollController,
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
return TravelCard(events.elementAt(index));
},
);
},
);
Happy fluttering!
Upvotes: 3
Reputation: 267364
The widget used is StatelessWidget
, so don't have to worry about multiple setState
calls.
Create this class:
class ScrollListener extends ChangeNotifier {
double bottom = 0;
double _last = 0;
ScrollListener.initialise(ScrollController controller, [double height = 56]) {
controller.addListener(() {
final current = controller.offset;
bottom += _last - current;
if (bottom <= -height) bottom = -height;
if (bottom >= 0) bottom = 0;
_last = current;
if (bottom <= 0 && bottom >= -height) notifyListeners();
});
}
}
Usage:
class MyPage extends StatelessWidget {
final ScrollController _controller = ScrollController();
final double _bottomNavBarHeight = 56;
late final ScrollListener _model;
MyPage() {
_model = ScrollListener.initialise(_controller);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: _model,
builder: (context, child) {
return Stack(
children: [
ListView.builder(
controller: _controller,
itemCount: 20,
itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
),
Positioned(
left: 0,
right: 0,
bottom: _model.bottom,
child: _bottomNavBar,
),
],
);
},
),
);
}
Widget get _bottomNavBar {
return SizedBox(
height: _bottomNavBarHeight,
child: BottomNavigationBar(
backgroundColor: Colors.amber[800],
items: [
BottomNavigationBarItem(icon: Icon(Icons.call), label: 'Call'),
BottomNavigationBarItem(icon: Icon(Icons.message), label: 'Message'),
],
),
);
}
}
Upvotes: 4
Reputation: 1157
Check out this answer, => Bottom Navigation Scrolling it uses animation to hide the bottom navigation bar. You can also customize the animation.
Upvotes: 0
Reputation: 12673
You need a ScrollController to monitor/observe the scroll direction of the Listview. The controller will be initialized in the initState and a listener should be added to it....The listener should toggle a boolean depending on the Scroll direction....Then the Bottom Naigatio Bar should be wrapped with an OffStage
This code should help
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@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> {
bool _isVisible = true;
ScrollController controller;
@override
void initState() {
super.initState();
controller = ScrollController();
controller.addListener(() {
setState(() {
_isVisible = controller.position.userScrollDirection == ScrollDirection.forward;
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
controller: controller,
children: List.generate(200, (index) => Text(("$index"))),
),
bottomNavigationBar: Offstage(
offstage: !_isVisible,
child: BottomNavigationBar(
items: [
BottomNavigationBarItem(
title: Text("Hello"),
icon: Icon(Icons.style)
),
BottomNavigationBarItem(
title: Text("Hi"),
icon: Icon(Icons.style)
),
BottomNavigationBarItem(
title: Text("Hey"),
icon: Icon(Icons.style)
)
],
),
),
);
}
}
Upvotes: 2
Reputation: 663
This solution is just a workaround to this problem. There may be some detrimental changes.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ScrollController _hideButtonController;
var _isVisible;
@override
initState() {
super.initState();
_isVisible = true;
_hideButtonController = new ScrollController();
_hideButtonController.addListener(() {
if (_hideButtonController.position.userScrollDirection ==
ScrollDirection.reverse) {
if(_isVisible)
setState(() {
_isVisible = false;
print("**** $_isVisible up");
});
}
if (_hideButtonController.position.userScrollDirection ==
ScrollDirection.forward) {
if(!_isVisible)
setState(() {
_isVisible = true;
print("**** $_isVisible down");
});
}
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new CustomScrollView(
controller: _hideButtonController,
shrinkWrap: true,
slivers: <Widget>[
new SliverPadding(
padding: const EdgeInsets.all(20.0),
sliver: new SliverList(
delegate: new SliverChildListDelegate(
<Widget>[
const Text('I\'m dedicating every day to you'),
const Text('Domestic life was never quite my style'),
const Text('When you smile, you knock me out, I fall apart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('I realize I am crazy'),
],
),
),
),
],
)),
bottomNavigationBar: AnimatedContainer(
duration: Duration(milliseconds: 500),
height: _isVisible ? 60.0 : 0.0,
child: _isVisible
? new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.card_giftcard),
title: Text('Offers'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.account_box),
title: Text('Account'),
),
],
currentIndex: 0,
)
: Container(
color: Colors.white,
width: MediaQuery.of(context).size.width,
),
),
);
}
}
Also you can use sliver bar with this :-
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ScrollController _hideButtonController;
var _isVisible;
@override
initState() {
super.initState();
_isVisible = true;
_hideButtonController = new ScrollController();
_hideButtonController.addListener(() {
if (_hideButtonController.position.userScrollDirection ==
ScrollDirection.reverse) {
setState(() {
_isVisible = false;
print("**** $_isVisible up");
});
}
if (_hideButtonController.position.userScrollDirection ==
ScrollDirection.forward) {
setState(() {
_isVisible = true;
print("**** $_isVisible down");
});
}
});
}
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
TextEditingController searchController = new TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
resizeToAvoidBottomPadding: true,
drawer: Container(),
key: scaffoldKey,
body: NestedScrollView(
controller: _hideButtonController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
title: Container(
child: Card(
elevation: 3.0,
margin: EdgeInsets.only(top: 10.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2.0))),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
GestureDetector(
child: Icon(
Icons.sort,
color: Colors.black54,
),
onTap: () {
scaffoldKey.currentState.openDrawer();
},
),
SizedBox(
width: 10.0,
),
Expanded(
child: TextField(
controller: searchController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: "What are you looking for?"),
),
),
GestureDetector(
onTap: () {
searchController.clear();
},
child: Icon(
Icons.clear,
color: Colors.black54,
),
),
],
),
),
),
),
elevation: 10.0,
automaticallyImplyLeading: false,
expandedHeight: 70,
floating: true,
snap: true,
)
];
},
body: new ListView(
children: <Widget>[
const Text('I\'m dedicating every day to you'),
const Text('Domestic life was never quite my style'),
const Text('When you smile, you knock me out, I fall apart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('I realize I am crazy'),
const Text('I\'m dedicating every day to you'),
const Text('Domestic life was never quite my style'),
const Text('When you smile, you knock me out, I fall apart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('And I thought I was so smart'),
const Text('I realize I am crazy'),
],
),
),
bottomNavigationBar: AnimatedContainer(
duration: Duration(seconds: 1),
height: _isVisible ? 60.0 : 0.0,
child: _isVisible
? AnimatedContainer(
duration: Duration(milliseconds: 200),
height: _isVisible ? 60.0 : 0.0,
child: _isVisible
? new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.card_giftcard),
title: Text('Offers'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.account_box),
title: Text('Account'),
),
],
currentIndex: 0,
)
: Container(
color: Colors.white,
width: MediaQuery.of(context).size.width,
),
)
: Container(
color: Theme.of(context).primaryColor,
width: MediaQuery.of(context).size.width,
),
),
// _isVisible
// ? bottomNavigationBar()
// : Container(
// height: 0.0,
// width: MediaQuery.of(context).size.width,
// ),
);
}
}
Kindly comment your view on this codes so we can improve this code.
this was taken from this answer :- Flutter - Hiding FloatingActionButton
Upvotes: 10
Reputation: 21081
Try to wrap the ListView as the child of a NotificationListener and listen to scrolling events https://docs.flutter.io/flutter/widgets/OverscrollNotification-class.html
other approach is using ScrollUpdateNotification https://docs.flutter.io/flutter/widgets/ScrollUpdateNotification-class.html
Upvotes: 5