Skip to content

Instantly share code, notes, and snippets.

@neganngdev
Created December 17, 2024 01:20
Show Gist options
  • Select an option

  • Save neganngdev/9340f4c45d3c9e505cc7278b21706d3b to your computer and use it in GitHub Desktop.

Select an option

Save neganngdev/9340f4c45d3c9e505cc7278b21706d3b to your computer and use it in GitHub Desktop.
Passant Navbar - Animated Floating Action Menu
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter UI Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen>
with SingleTickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
duration: const Duration(milliseconds: 400),
vsync: this,
);
late final Animation<double> _rotationAngle =
Tween<double>(begin: 0, end: 0.5).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
late final Animation<Color?> _centerIconBackgroundColor = ColorTween(
begin: const Color(0xFFFF3841),
end: const Color(0xFFEAEAEA),
).animate(_controller);
late final Animation<Color?> _centerIconForegroundColor = ColorTween(
begin: Colors.white,
end: Colors.black,
).animate(_controller);
late final Animation<double?> _bottomBarActionMenuTopPosition = Tween<double>(
begin: 800,
end: 550,
).animate(_controller);
void _onCenterIconTap() {
_controller.isCompleted ? _controller.reverse() : _controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: SizedBox.expand(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Stack(
children: [
child!,
BottomBarActionMenuRow(
topPosition: _bottomBarActionMenuTopPosition.value,
controller: _controller,
),
Positioned(
bottom: 0,
height: kBottomNavigationBarHeight,
left: 0,
right: 0,
child: PBottomAppBar(
onCenterIconTap: _onCenterIconTap,
rotationAngle: _rotationAngle.value,
centerIconBackgroundColor:
_centerIconBackgroundColor.value,
centerIconForegroundColor:
_centerIconForegroundColor.value,
),
),
],
);
},
child: Container(
width: 200.0,
height: 200.0,
color: Colors.green,
child: const Center(child: Text('BODY!')),
),
),
),
),
);
}
}
class MenuItem {
final String text;
final IconData icon;
const MenuItem({required this.text, required this.icon});
}
const List<MenuItem> dataSet = [
MenuItem(text: 'Add Place', icon: Icons.location_on),
MenuItem(text: 'Create List', icon: Icons.view_list_rounded),
MenuItem(text: 'Add Friend', icon: Icons.person_add_alt_1_rounded)
];
class BottomBarActionMenuRow extends StatelessWidget {
final double? topPosition;
final AnimationController controller;
const BottomBarActionMenuRow(
{super.key, this.topPosition, required this.controller});
@override
Widget build(BuildContext context) {
return Positioned(
height: 180,
top: topPosition,
left: 0,
right: 0,
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.white,
Colors.white.withOpacity(0.8),
Colors.white.withOpacity(0.5),
Colors.white.withOpacity(0.35),
Colors.white.withOpacity(0.005),
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
stops: [0.55, 0.65, 0.75, 0.85, 0.99],
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: dataSet.map((item) {
int index = dataSet.indexOf(item);
return SlideTransition(
position: Tween<Offset>(
begin: Offset(0, 1.0 + (2 * index)),
end: Offset.zero,
).animate(controller),
child: PIconButton(item: item),
);
}).toList(),
),
),
);
}
}
class PIconButton extends StatelessWidget {
final MenuItem item;
const PIconButton({super.key, required this.item});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 50,
height: 50,
decoration: const BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
child: Center(
child: Icon(item.icon, color: Colors.white, size: 24),
),
),
const SizedBox(height: 4),
Text(
item.text.toUpperCase(),
style: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.black,
),
textAlign: TextAlign.center,
),
],
);
}
}
class PBottomAppBar extends StatelessWidget {
final VoidCallback onCenterIconTap;
final double rotationAngle;
final Color? centerIconBackgroundColor, centerIconForegroundColor;
const PBottomAppBar({
super.key,
required this.onCenterIconTap,
required this.rotationAngle,
this.centerIconBackgroundColor,
this.centerIconForegroundColor,
});
@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).bottomAppBarTheme.color ?? Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Icon(Icons.assistant_navigation,
color: Colors.black, size: 30.0),
const Icon(Icons.map_outlined, color: Colors.black, size: 30.0),
Align(
alignment: const Alignment(0, 2),
child: InkWell(
onTap: onCenterIconTap,
child: Transform.rotate(
angle: rotationAngle,
child: Container(
width: 50.0,
height: 50.0,
decoration: BoxDecoration(
color: centerIconBackgroundColor,
shape: BoxShape.circle,
),
child: Icon(Icons.add,
color: centerIconForegroundColor, size: 30.0),
),
),
),
),
const Icon(Icons.notifications_outlined,
color: Colors.black, size: 30.0),
const Icon(Icons.person_outline_rounded,
color: Colors.black, size: 30.0),
],
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment