AnimatedBottomNavigationBar is a customizable widget inspired by dribble shot.
With AnimatedBottomNavigationBar.builder
you are able to customize tab view however you need. In this case you are responsible to handle an active(inactive) state of tabs.
Getting Started
To get started, place your AnimatedBottomNavigationBar
in the bottomNavigationBar slot of a Scaffold
. The AnimatedBottomNavigationBar
respects FloatingActionButton
location. For example:
Scaffold(
body: Container(), //destination screen
floatingActionButton: FloatingActionButton(
//params
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: iconList,
activeIndex: _bottomNavIndex,
gapLocation: GapLocation.center,
notchSmoothness: NotchSmoothness.verySmoothEdge,
leftCornerRadius: 32,
rightCornerRadius: 32,
onTap: (index) => setState(() => _bottomNavIndex = index),
//other params
),
);
Customization
AnimatedBottomNavigationBar is customizable and works with 2, 3, 4, or 5 navigation elements.
Scaffold(
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: iconList,
activeIndex: _bottomNavIndex,
onTap: (index) => setState(() => _bottomNavIndex = index),
//other params
),
);
Scaffold(
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: iconList,
activeIndex: _bottomNavIndex,
leftCornerRadius: 32,
rightCornerRadius: 32,
onTap: (index) => setState(() => _bottomNavIndex = index),
//other params
),
);
Scaffold(
floatingActionButton: FloatingActionButton(
//params
),
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: iconList,
activeIndex: _bottomNavIndex,
gapLocation: GapLocation.end,
notchSmoothness: NotchSmoothness.defaultEdge,
onTap: (index) => setState(() => _bottomNavIndex = index),
//other params
),
);
Scaffold(
floatingActionButton: FloatingActionButton(
//params
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: iconList,
activeIndex: _bottomNavIndex,
gapLocation: GapLocation.center,
notchSmoothness: NotchSmoothness.defaultEdge,
onTap: (index) => setState(() => _bottomNavIndex = index),
//other params
),
);
Scaffold(
floatingActionButton: FloatingActionButton(
//params
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: iconList,
activeIndex: _bottomNavIndex,
gapLocation: GapLocation.center,
notchSmoothness: NotchSmoothness.softEdge,
onTap: (index) => setState(() => _bottomNavIndex = index),
//other params
),
);
Scaffold(
floatingActionButton: FloatingActionButton(
//params
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: iconList,
activeIndex: _bottomNavIndex,
gapLocation: GapLocation.center,
notchSmoothness: NotchSmoothness.smoothEdge,
onTap: (index) => setState(() => _bottomNavIndex = index),
//other params
),
);
Scaffold(
floatingActionButton: FloatingActionButton(
//params
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: iconList,
activeIndex: _bottomNavIndex,
gapLocation: GapLocation.center,
notchSmoothness: NotchSmoothness.verySmoothEdge,
onTap: (index) => setState(() => _bottomNavIndex = index),
//other params
),
);
Driving Navigation Bar Changes
You have to change the active navigation bar tab programmatically by passing a new activeIndex to the AnimatedBottomNavigationBar widget.
class _MyAppState extends State<MyApp> {
int activeIndex;
/// Handler for when you want to programmatically change
/// the active index. Calling `setState()` here causes
/// Flutter to re-render the tree, which `AnimatedBottomNavigationBar`
/// responds to by running its normal animation.
void _onTap(int index) {
setState((){
activeIndex = index;
});
}
Widget build(BuildContext context) {
return AnimatedBottomNavigationBar(
activeIndex: activeIndex,
onTap: _onTap,
//other params
);
}
}
Run this command:
With Flutter:
$ flutter pub add animated_bottom_navigation_bar
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
animated_bottom_navigation_bar: ^1.2.0
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:animated_bottom_navigation_bar/animated_bottom_navigation_bar.dart';
import 'dart:async';
import 'package:animated_bottom_navigation_bar/animated_bottom_navigation_bar.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:circular_reveal_animation/circular_reveal_animation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:lanarsnavbarflutter/theme/app_theme.dart';
import 'package:lanarsnavbarflutter/theme/custom_colors_theme.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: AppTheme.get(isLight: true),
darkTheme: AppTheme.get(isLight: false),
home: MyHomePage(title: 'Animated Navigation Bottom Bar'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
final autoSizeGroup = AutoSizeGroup();
var _bottomNavIndex = 0; //default index of a first screen
late AnimationController _fabAnimationController;
late AnimationController _borderRadiusAnimationController;
late Animation<double> fabAnimation;
late Animation<double> borderRadiusAnimation;
late CurvedAnimation fabCurve;
late CurvedAnimation borderRadiusCurve;
late AnimationController _hideBottomBarAnimationController;
final iconList = <IconData>[
Icons.brightness_5,
Icons.brightness_4,
Icons.brightness_6,
Icons.brightness_7,
];
@override
void initState() {
super.initState();
_fabAnimationController = AnimationController(
duration: Duration(milliseconds: 500),
vsync: this,
);
_borderRadiusAnimationController = AnimationController(
duration: Duration(milliseconds: 500),
vsync: this,
);
fabCurve = CurvedAnimation(
parent: _fabAnimationController,
curve: Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
);
borderRadiusCurve = CurvedAnimation(
parent: _borderRadiusAnimationController,
curve: Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
);
fabAnimation = Tween<double>(begin: 0, end: 1).animate(fabCurve);
borderRadiusAnimation = Tween<double>(begin: 0, end: 1).animate(
borderRadiusCurve,
);
_hideBottomBarAnimationController = AnimationController(
duration: Duration(milliseconds: 200),
vsync: this,
);
Future.delayed(
Duration(seconds: 1),
() => _fabAnimationController.forward(),
);
Future.delayed(
Duration(seconds: 1),
() => _borderRadiusAnimationController.forward(),
);
}
bool onScrollNotification(ScrollNotification notification) {
if (notification is UserScrollNotification &&
notification.metrics.axis == Axis.vertical) {
switch (notification.direction) {
case ScrollDirection.forward:
_hideBottomBarAnimationController.reverse();
_fabAnimationController.forward(from: 0);
break;
case ScrollDirection.reverse:
_hideBottomBarAnimationController.forward();
_fabAnimationController.reverse(from: 1);
break;
case ScrollDirection.idle:
break;
}
}
return false;
}
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).extension<CustomColorsTheme>()!;
return Scaffold(
extendBody: true,
appBar: AppBar(
title: Text(
widget.title,
style: TextStyle(color: Colors.white),
),
),
body: NotificationListener<ScrollNotification>(
onNotification: onScrollNotification,
child: NavigationScreen(iconList[_bottomNavIndex]),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.brightness_3,
color: AppTheme.colorGray,
),
onPressed: () {
_fabAnimationController.reset();
_borderRadiusAnimationController.reset();
_borderRadiusAnimationController.forward();
_fabAnimationController.forward();
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: AnimatedBottomNavigationBar.builder(
itemCount: iconList.length,
tabBuilder: (int index, bool isActive) {
final color = isActive ? colors.activeNavigationBarColor : colors.notActiveNavigationBarColor;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
iconList[index],
size: 24,
color: color,
),
const SizedBox(height: 4),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: AutoSizeText(
"brightness $index",
maxLines: 1,
style: TextStyle(color: color),
group: autoSizeGroup,
),
)
],
);
},
backgroundColor: colors.bottomNavigationBarBackgroundColor,
activeIndex: _bottomNavIndex,
splashColor: colors.activeNavigationBarColor,
notchAndCornersAnimation: borderRadiusAnimation,
splashSpeedInMilliseconds: 300,
notchSmoothness: NotchSmoothness.defaultEdge,
gapLocation: GapLocation.center,
leftCornerRadius: 32,
rightCornerRadius: 32,
onTap: (index) => setState(() => _bottomNavIndex = index),
hideAnimationController: _hideBottomBarAnimationController,
shadow: BoxShadow(
offset: Offset(0, 1),
blurRadius: 12,
spreadRadius: 0.5,
color: colors.activeNavigationBarColor,
),
),
);
}
}
class NavigationScreen extends StatefulWidget {
final IconData iconData;
NavigationScreen(this.iconData) : super();
@override
_NavigationScreenState createState() => _NavigationScreenState();
}
class _NavigationScreenState extends State<NavigationScreen>
with TickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> animation;
@override
void didUpdateWidget(NavigationScreen oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.iconData != widget.iconData) {
_startAnimation();
}
}
@override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 1000),
);
animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeIn,
);
_controller.forward();
super.initState();
}
_startAnimation() {
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 1000),
);
animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeIn,
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).extension<CustomColorsTheme>()!;
return Container(
color: Theme.of(context).colorScheme.background,
child: ListView(
children: [
SizedBox(height: 64),
Center(
child: CircularRevealAnimation(
animation: animation,
centerOffset: Offset(80, 80),
maxRadius: MediaQuery.of(context).size.longestSide * 1.1,
child: Icon(
widget.iconData,
color: colors.activeNavigationBarColor,
size: 160,
),
),
),
],
),
);
}
}
Download details:
Author: lanars.com
Source: https://github.com/LanarsInc/animated-bottom-navigation-bar-flutter