MoodCanvas/lib/widgets/custom_bottom_nav_bar.dart
fengshengxiong 91b7eebbf2 接入TopON
2026-01-22 16:34:55 +08:00

261 lines
8.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:animated_bottom_navigation_bar/animated_bottom_navigation_bar.dart';
import 'package:aesthetica_wallpaper/widgets/animated_background.dart';
class CustomBottomNavBar extends StatefulWidget {
final int currentIndex;
final Function(int) onTap;
final VoidCallback? onFabPressed;
const CustomBottomNavBar({
super.key,
required this.currentIndex,
required this.onTap,
this.onFabPressed,
});
@override
State<CustomBottomNavBar> createState() => _CustomBottomNavBarState();
}
class _CustomBottomNavBarState extends State<CustomBottomNavBar>
with TickerProviderStateMixin {
late AnimationController _fabAnimationController;
late AnimationController _borderRadiusAnimationController;
late Animation<double> fabAnimation;
late Animation<double> borderRadiusAnimation;
late CurvedAnimation fabCurve;
late CurvedAnimation borderRadiusCurve;
late AnimationController _hideBottomBarAnimationController;
final List<IconData> _iconList = [
Icons.dashboard_rounded,
Icons.favorite_rounded,
Icons.chat_bubble_rounded,
Icons.settings_rounded,
];
final List<String> _labelList = ['Home', 'Favorites', 'Messages', 'Settings'];
@override
void initState() {
super.initState();
_fabAnimationController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_borderRadiusAnimationController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
fabCurve = CurvedAnimation(
parent: _fabAnimationController,
curve: const Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
);
borderRadiusCurve = CurvedAnimation(
parent: _borderRadiusAnimationController,
curve: const 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: const Duration(milliseconds: 200),
vsync: this,
);
Future.delayed(
const Duration(milliseconds: 300),
() => _fabAnimationController.forward(),
);
Future.delayed(
const Duration(milliseconds: 300),
() => _borderRadiusAnimationController.forward(),
);
}
@override
void dispose() {
_fabAnimationController.dispose();
_borderRadiusAnimationController.dispose();
_hideBottomBarAnimationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GlowingBottomNavBar(
child: AnimatedBottomNavigationBar.builder(
itemCount: _iconList.length,
tabBuilder: (int index, bool isActive) {
final color = isActive ? Colors.pinkAccent : Colors.grey[400];
return SizedBox(
height: 56,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 图标容器,带有动画效果
AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: isActive
? Colors.pinkAccent.withValues(alpha: 0.15)
: Colors.transparent,
borderRadius: BorderRadius.circular(8),
border: isActive
? Border.all(
color: Colors.pinkAccent.withValues(alpha: 0.3),
width: 1,
)
: null,
),
child: AnimatedScale(
scale: isActive ? 1.05 : 1.0,
duration: const Duration(milliseconds: 200),
child: Icon(_iconList[index], size: 20, color: color),
),
),
const SizedBox(height: 2),
// 标签文字,带有动画效果
AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 200),
style: TextStyle(
color: color,
fontSize: isActive ? 10 : 8,
fontWeight: isActive ? FontWeight.w600 : FontWeight.w400,
letterSpacing: isActive ? 0.2 : 0,
),
child: Text(_labelList[index]),
),
// 活跃指示器
AnimatedContainer(
duration: const Duration(milliseconds: 200),
margin: const EdgeInsets.only(top: 1),
height: 2,
width: isActive ? 12 : 0,
decoration: BoxDecoration(
color: Colors.pinkAccent,
borderRadius: BorderRadius.circular(1),
),
),
],
),
);
},
backgroundColor: Colors.black,
activeIndex: widget.currentIndex,
splashColor: Colors.pinkAccent.withValues(alpha: 0.3),
notchAndCornersAnimation: borderRadiusAnimation,
splashSpeedInMilliseconds: 300,
notchSmoothness: NotchSmoothness.verySmoothEdge,
gapLocation: GapLocation.center,
gapWidth: 60,
leftCornerRadius: 20,
rightCornerRadius: 20,
onTap: widget.onTap,
hideAnimationController: _hideBottomBarAnimationController,
shadow: BoxShadow(
offset: const Offset(0, -3),
blurRadius: 15,
spreadRadius: 1,
color: Colors.black.withValues(alpha: 0.4),
),
),
);
}
}
class CustomFloatingActionButton extends StatefulWidget {
final VoidCallback? onPressed;
const CustomFloatingActionButton({super.key, this.onPressed});
@override
State<CustomFloatingActionButton> createState() =>
_CustomFloatingActionButtonState();
}
class _CustomFloatingActionButtonState extends State<CustomFloatingActionButton>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _scaleAnimation;
late Animation<double> _rotationAnimation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
);
_scaleAnimation = Tween<double>(begin: 1.0, end: 0.95).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
_rotationAnimation = Tween<double>(begin: 0.0, end: 0.1).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: Transform.rotate(
angle: _rotationAnimation.value,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
colors: [Colors.pinkAccent, Colors.purpleAccent],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Colors.pinkAccent.withValues(alpha: 0.4),
blurRadius: 12,
spreadRadius: 2,
offset: const Offset(0, 4),
),
],
),
child: FloatingActionButton(
onPressed: () {
_animationController.forward().then((_) {
_animationController.reverse();
});
widget.onPressed?.call();
},
backgroundColor: Colors.transparent,
elevation: 0,
child: const Icon(
Icons.palette_rounded,
color: Colors.white,
size: 28,
),
),
),
),
);
},
);
}
}