/
siblun
/
ReadFlow
Обзор
Документация
Войти
/
siblun
/
ReadFlow
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
lib/presentation/widgets/app_bottom_nav.dart
127 строк
4 KB
siblun
Add docs
21 апр 2026, 16:13
21 апр 2026, 16:13
c40d018
Код
Авторство
О чём код?
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:go_router/go_router.dart'; import '../theme/app_colors.dart'; import '../theme/app_typography.dart'; /// Кастомная нижняя панель навигации. /// /// Особенности: /// - **Haptic Feedback**: Использует [HapticFeedback.selectionClick] для тактильного отклика. /// - **Адаптивность**: Автоматически учитывает "безопасную зону" (Safe Area) для устройств без кнопок (iOS/Android). /// - **Анимация**: Плавное переключение состояний (цвет, размер, фон) через [AnimatedContainer] и [AnimatedDefaultTextStyle]. class AppBottomNav extends StatelessWidget { final int activeIndex; const AppBottomNav({super.key, required this.activeIndex}); @override Widget build(BuildContext context) { final bottomPadding = MediaQuery.of(context).padding.bottom; return Container( padding: EdgeInsets.only( top: 12.h, left: 16.w, right: 16.w, bottom: bottomPadding > 0 ? bottomPadding : 16.h, ), decoration: BoxDecoration( color: AppColors.background, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.02), blurRadius: 20, offset: const Offset(0, -5), ), ], border: Border( top: BorderSide( color: AppColors.surface.withValues(alpha: 0.5), width: 0.5.w, ), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildNavItem( context: context, label: 'Главная', icon: Icons.home_rounded, isActive: activeIndex == 0, onTap: () => _handleNavigation(context, '/home'), ), _buildNavItem( context: context, label: 'Библиотека', icon: Icons.menu_book_rounded, isActive: activeIndex == 1, onTap: () => _handleNavigation(context, '/library'), ), _buildNavItem( context: context, label: 'Прогресс', icon: Icons.insights_rounded, isActive: activeIndex == 2, onTap: () => _handleNavigation(context, '/stats'), ), ], ), ); } void _handleNavigation(BuildContext context, String location) { HapticFeedback.selectionClick(); context.go(location); } Widget _buildNavItem({ required BuildContext context, required String label, required IconData icon, required bool isActive, required VoidCallback onTap, }) { return GestureDetector( onTap: onTap, behavior: HitTestBehavior.opaque, child: SizedBox( width: 80.w, child: Column( mainAxisSize: MainAxisSize.min, children: [ AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, width: 56.w, height: 32.h, decoration: ShapeDecoration( color: isActive ? AppColors.surface.withValues(alpha: 0.5) : Colors.transparent, shape: const StadiumBorder(), ), child: Icon( icon, color: isActive ? AppColors.textMain : AppColors.accentSecondary, size: 22.w, ), ), SizedBox(height: 6.h), AnimatedDefaultTextStyle( duration: const Duration(milliseconds: 250), style: (isActive ? AppTypography.navActive : AppTypography.label).copyWith( fontSize: 11.sp, fontWeight: isActive ? FontWeight.w700 : FontWeight.w500, letterSpacing: 0.2, ), child: Text(label), ), ], ), ), ); } }