import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; /// Custom bottom navigation bar implementing adaptive tab persistence /// with contextual badge indicators for the transit app class CustomBottomBar extends StatefulWidget { final int currentIndex; final Function(int) onTap; const CustomBottomBar({ super.key, required this.currentIndex, required this.onTap, }); @override State createState() => _CustomBottomBarState(); } class _CustomBottomBarState extends State { // Navigation items with routes and icons final List _navigationItems = [ const BottomNavigationBarItem( icon: Icon(Icons.map_outlined), activeIcon: Icon(Icons.map), label: 'Mapa', ), const BottomNavigationBarItem( icon: Icon(Icons.schedule_outlined), activeIcon: Icon(Icons.schedule), label: 'Horarios', ), const BottomNavigationBarItem( icon: Icon(Icons.local_offer_outlined), activeIcon: Icon(Icons.local_offer), label: 'Cupones', ), const BottomNavigationBarItem( icon: Icon(Icons.local_taxi_outlined), activeIcon: Icon(Icons.local_taxi), label: 'Taxi', ), ]; final List _routes = [ '/map-screen', '/schedules-screen', '/coupons-screen', '/taxi-screen', ]; void _handleTap(int index) { if (index != widget.currentIndex) { // Haptic feedback for tab selection HapticFeedback.lightImpact(); // Navigate to selected route Navigator.pushNamed(context, _routes[index]); // Call the onTap callback widget.onTap(index); } } Widget _buildNavigationItem(BottomNavigationBarItem item, int index) { final bool isSelected = index == widget.currentIndex; final theme = Theme.of(context); return Expanded( child: InkWell( onTap: () => _handleTap(index), borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric(vertical: 8), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Icon with micro-interaction confirmation AnimatedScale( scale: isSelected ? 1.1 : 1.0, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: isSelected ? theme.colorScheme.secondary.withValues(alpha: 0.1) : Colors.transparent, borderRadius: BorderRadius.circular(12), ), child: _buildIconWithBadge( isSelected ? item.activeIcon : item.icon, index, isSelected, ), ), ), const SizedBox(height: 4), // Label with smooth transition AnimatedDefaultTextStyle( duration: const Duration(milliseconds: 200), style: theme.textTheme.labelSmall!.copyWith( color: isSelected ? theme.colorScheme.primary : theme.colorScheme.onSurface.withValues(alpha: 0.6), fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, ), child: Text(item.label!), ), ], ), ), ), ); } Widget _buildIconWithBadge(Widget icon, int index, bool isSelected) { final theme = Theme.of(context); // Smart notification badges - contextual indicators bool showBadge = false; String? badgeText; switch (index) { case 0: // Map // Show badge when there are nearby bus updates showBadge = false; // Would be dynamic based on app state break; case 1: // Schedules // Show badge when there are schedule changes showBadge = false; // Would be dynamic based on app state break; case 2: // Coupons // Show badge when there are new coupons available showBadge = true; // Example: new coupons available badgeText = '3'; break; case 3: // Taxi // Show badge for new taxi services or favorites showBadge = false; // Would be dynamic based on app state break; } if (!showBadge) { return IconTheme( data: IconThemeData( color: isSelected ? theme.colorScheme.primary : theme.colorScheme.onSurface.withValues(alpha: 0.6), size: 24, ), child: icon, ); } return Badge( label: badgeText != null ? Text(badgeText) : null, backgroundColor: theme.colorScheme.error, textColor: theme.colorScheme.onError, smallSize: badgeText == null ? 8 : null, child: IconTheme( data: IconThemeData( color: isSelected ? theme.colorScheme.primary : theme.colorScheme.onSurface.withValues(alpha: 0.6), size: 24, ), child: icon, ), ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Container( decoration: BoxDecoration( color: theme.colorScheme.surface, boxShadow: [ BoxShadow( color: theme.shadowColor.withValues(alpha: 0.1), blurRadius: 8, offset: const Offset(0, -2), ), ], ), child: SafeArea( child: Container( height: 72, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: List.generate( _navigationItems.length, (index) => _buildNavigationItem(_navigationItems[index], index), ), ), ), ), ); } }