floating_bottom_nav_bar 1.1.4
floating_bottom_nav_bar: ^1.1.4 copied to clipboard
A customizable floating bottom navigation bar for Flutter with smooth animations, modern UI styling, SVG icon support, and badge indicators.
example/main.dart
import 'package:floating_frosted_bottom_bar/app/frosted_bottom_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Floating Bottom Nav Bar Demo',
theme: ThemeData(
primarySwatch: Colors.purple,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MainScreen(),
debugShowCheckedModeBanner: false,
);
}
}
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> with TickerProviderStateMixin {
final BaseController controller = Get.put(BaseController());
late TabController _tabController;
final List<Widget> tabPages = [
HomePage(),
BrowsePage(),
MapsPage(),
FavoritePage(),
MorePage(),
];
@override
void initState() {
_tabController = TabController(length: 5, vsync: this);
_tabController.animation!.addListener(() {
final value = _tabController.animation!.value.round();
if (value != controller.currentPage.value && mounted) {
controller.setPage(value);
}
});
super.initState();
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: SafeArea(
bottom: false,
child: Obx(() => IndexedStack(
index: controller.currentPage.value,
children: tabPages,
)),
),
bottomNavigationBar: FrostedBottomBar(
opacity: 1.0,
sigmaX: 5,
sigmaY: 5,
borderRadius: BorderRadius.circular(16),
duration: const Duration(milliseconds: 800),
hideOnScroll: true,
body: (context, scrollController) => TabBarView(
controller: _tabController,
physics: const BouncingScrollPhysics(),
children: tabPages,
),
child: Container(
width: double.infinity,
color: const Color(0xFFb22222),
child: TabBar(
controller: _tabController,
onTap: (index) {
controller.setPage(index);
},
indicatorPadding: const EdgeInsets.fromLTRB(6, 60, 6, 3),
indicator: CurvedUnderlineTabIndicator(
color: Colors.white,
height: 10,
width: 60,
radius: 30,
),
tabs: [
TabsIconWithLabel(
assetPath: "assets/icons/home.svg",
label: "Home",
color: controller.currentPage.value == 0
? Colors.yellow.shade50
: Colors.yellow.shade50,
),
TabsIconWithLabel(
assetPath: "assets/icons/browse.svg",
label: "Browse",
color: controller.currentPage.value == 1
? Colors.yellow.shade50
: Colors.yellow.shade50,
),
TabsIconWithLabel(
assetPath: "assets/icons/maps.svg",
label: "Maps",
color: controller.currentPage.value == 2
? Colors.yellow.shade50
: Colors.yellow.shade50,
),
TabsIconWithLabel(
assetPath: "assets/icons/favorite.svg",
label: "Favorites",
color: controller.currentPage.value == 3
? Colors.yellow.shade50
: Colors.yellow.shade50,
),
TabsIconWithLabel(
assetPath: "assets/icons/more.svg",
label: "More",
color: controller.currentPage.value == 4
? Colors.yellow.shade50
: Colors.yellow.shade50,
),
],
),
),
),
);
}
}
// Controller for managing page state
class BaseController extends GetxController {
var currentPage = 0.obs;
void setPage(int page) {
currentPage.value = page;
}
}
// Tab icon widget
class TabsIconWithLabel extends StatelessWidget {
final Color color;
final double iconSize;
final String assetPath;
final String label;
const TabsIconWithLabel({
Key? key,
this.color = Colors.white,
this.iconSize = 34,
required this.assetPath,
required this.label,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Tab(
iconMargin: const EdgeInsets.only(top: 18.0, bottom: 5),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
assetPath,
color: color,
width: iconSize,
height: iconSize,
),
const SizedBox(height: 5),
Text(
label,
style: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.normal,
color: color,
fontSize: 7,
),
textAlign: TextAlign.center,
),
],
),
);
}
}
// Custom tab indicator
class CurvedUnderlineTabIndicator extends Decoration {
final Color color;
final double height;
final double width;
final double radius;
const CurvedUnderlineTabIndicator({
required this.color,
required this.height,
required this.width,
required this.radius,
});
@override
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
return _CurvedUnderlinePainter(color, height, width, radius);
}
}
class _CurvedUnderlinePainter extends BoxPainter {
final Color color;
final double height;
final double width;
final double radius;
_CurvedUnderlinePainter(this.color, this.height, this.width, this.radius);
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill;
final rect = Rect.fromLTWH(
offset.dx + (configuration.size!.width - width) / 2,
configuration.size!.height - height,
width,
height,
);
final rRect = RRect.fromRectAndRadius(rect, Radius.circular(radius));
canvas.drawRRect(rRect, paint);
}
}
// Example pages
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: const Center(
child: Text(
'Home Page',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
);
}
}
class BrowsePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: const Center(
child: Text(
'Browse Page',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
);
}
}
class MapsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: const Center(
child: Text(
'Maps Page',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
);
}
}
class FavoritePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: const Center(
child: Text(
'Favorites Page',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
);
}
}
class MorePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: const Center(
child: Text(
'More Page',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
);
}
}