import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'dart:ui'; import '../theme/app_colors.dart'; import 'glassmorphism_card.dart'; class FloatingNavigationBar extends StatefulWidget { final int selectedIndex; final Function(int) onItemTapped; final bool isVisible; const FloatingNavigationBar({ super.key, required this.selectedIndex, required this.onItemTapped, this.isVisible = true, }); @override State createState() => _FloatingNavigationBarState(); } class _FloatingNavigationBarState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _animation = CurvedAnimation( parent: _controller, curve: Curves.easeInOut, ); if (widget.isVisible) { _controller.forward(); } } @override void didUpdateWidget(FloatingNavigationBar oldWidget) { super.didUpdateWidget(oldWidget); if (widget.isVisible != oldWidget.isVisible) { if (widget.isVisible) { _controller.forward(); } else { _controller.reverse(); } } } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; return AnimatedBuilder( animation: _animation, builder: (context, child) { return Positioned( bottom: 20, left: 20, right: 20, child: Transform.translate( offset: Offset(0, 100 * (1 - _animation.value)), child: Opacity( opacity: _animation.value, child: GlassmorphismCard( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), borderRadius: 24, blur: 10.0, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _NavigationItem( icon: Icons.home_rounded, label: '홈', isSelected: widget.selectedIndex == 0, onTap: () => _onItemTapped(0), ), _NavigationItem( icon: Icons.analytics_rounded, label: '분석', isSelected: widget.selectedIndex == 1, onTap: () => _onItemTapped(1), ), _AddButton( onTap: () => _onItemTapped(2), ), _NavigationItem( icon: Icons.qr_code_scanner_rounded, label: 'SMS', isSelected: widget.selectedIndex == 3, onTap: () => _onItemTapped(3), ), _NavigationItem( icon: Icons.settings_rounded, label: '설정', isSelected: widget.selectedIndex == 4, onTap: () => _onItemTapped(4), ), ], ), ), ), ), ); }, ); } void _onItemTapped(int index) { HapticFeedback.lightImpact(); widget.onItemTapped(index); } } class _NavigationItem extends StatelessWidget { final IconData icon; final String label; final bool isSelected; final VoidCallback onTap; const _NavigationItem({ required this.icon, required this.label, required this.isSelected, required this.onTap, }); @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: isSelected ? const Color(0xFF14B8A6).withValues(alpha: 0.1) : Colors.transparent, borderRadius: BorderRadius.circular(12), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ AnimatedContainer( duration: const Duration(milliseconds: 200), child: Icon( icon, color: isSelected ? const Color(0xFF14B8A6) : (isDarkMode ? Colors.white70 : AppColors.textSecondary), size: isSelected ? 26 : 24, ), ), const SizedBox(height: 4), AnimatedDefaultTextStyle( duration: const Duration(milliseconds: 200), style: TextStyle( fontSize: 11, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, color: isSelected ? const Color(0xFF14B8A6) : (isDarkMode ? Colors.white70 : AppColors.textSecondary), ), child: Text(label), ), ], ), ), ); } } class _AddButton extends StatefulWidget { final VoidCallback onTap; const _AddButton({required this.onTap}); @override State<_AddButton> createState() => _AddButtonState(); } class _AddButtonState extends State<_AddButton> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _scaleAnimation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 150), vsync: this, ); _scaleAnimation = Tween( begin: 1.0, end: 0.9, ).animate(CurvedAnimation( parent: _controller, curve: Curves.easeInOut, )); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return GestureDetector( onTapDown: (_) => _controller.forward(), onTapUp: (_) { _controller.reverse(); widget.onTap(); }, onTapCancel: () => _controller.reverse(), child: AnimatedBuilder( animation: _controller, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: Container( width: 56, height: 56, decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: AppColors.blueGradient, ), borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: AppColors.primaryColor.withValues(alpha: 0.3), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: const Icon( Icons.add_rounded, color: Colors.white, size: 28, ), ), ); }, ), ); } } // 스크롤 감지를 위한 유틸리티 클래스 class FloatingNavBarScrollController { final ScrollController scrollController; final VoidCallback onHide; final VoidCallback onShow; double _lastScrollPosition = 0; bool _isVisible = true; FloatingNavBarScrollController({ required this.scrollController, required this.onHide, required this.onShow, }) { scrollController.addListener(_handleScroll); } void _handleScroll() { final currentScroll = scrollController.position.pixels; if (currentScroll > _lastScrollPosition && currentScroll > 50) { // 스크롤 다운 if (_isVisible) { _isVisible = false; onHide(); } } else if (currentScroll < _lastScrollPosition - 5) { // 스크롤 업 if (!_isVisible) { _isVisible = true; onShow(); } } _lastScrollPosition = currentScroll; } void dispose() { scrollController.removeListener(_handleScroll); } }