animated_bottom_navigation_bar 1.2.0 animated_bottom_navigation_bar: ^1.2.0 copied to clipboard
Animated Bottom Navigation Bar Widget implementation inspired by https://dribbble.com/shots/7134849-Simple-Tab-Bar-Animation
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,
),
),
),
],
),
);
}
}