如何实现类似Snake Navigation Bar的Flutter自定义导航栏?
自定义导航栏实现优化方案
现有代码局限
你当前的组件仅实现了基础的导航切换功能,缺少自定义导航栏常见的视觉区分细节(如选中态高亮、过渡动画、布局规整性等),这是无法匹配目标样式的核心原因。以下是优化后的实现,覆盖主流自定义导航栏的样式需求:
优化后的CustomNavigationBar组件
class CustomNavigationBar extends StatelessWidget { final List<NavigationDestination> destinations; final int selectedIndex; final Function(int index) onDestinationSelected; final Color backgroundColor; final Color selectedColor; final Color unselectedColor; final double height; const CustomNavigationBar({ Key? key, required this.destinations, required this.selectedIndex, required this.onDestinationSelected, this.backgroundColor = Colors.green, this.selectedColor = Colors.white, this.unselectedColor = Colors.black54, this.height = 65, }) : super(key: key); @override Widget build(BuildContext context) { return Container( height: height, color: backgroundColor, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: List.generate(destinations.length, (index) { final isSelected = index == selectedIndex; return Expanded( child: GestureDetector( onTap: () => onDestinationSelected(index), child: AnimatedContainer( duration: const Duration(milliseconds: 250), padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12), decoration: isSelected ? BoxDecoration( color: selectedColor.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ) : null, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ AnimatedScale( scale: isSelected ? 1.2 : 1.0, duration: const Duration(milliseconds: 250), child: Icon( isSelected ? destinations[index].selectedIcon ?? destinations[index].icon : destinations[index].icon, color: isSelected ? selectedColor : unselectedColor, size: 24, ), ), const SizedBox(height: 4), if (destinations[index].label != null) AnimatedDefaultTextStyle( style: TextStyle( color: isSelected ? selectedColor : unselectedColor, fontSize: isSelected ? 12 : 11, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, ), duration: const Duration(milliseconds: 250), child: Text(destinations[index].label!), ), ], ), ), ), ); }), ), ); } }
核心优化点
- 可配置参数:支持自定义背景色、选中/未选中颜色、导航栏高度,适配不同UI需求
- 选中态高亮:给选中项添加半透明背景圆角,强化视觉区分
- 过渡动画:图标缩放、文字样式平滑过渡,提升交互体验
- 规整布局:用
Expanded让每个导航项平分宽度,避免布局错乱 - 图标差异化:支持
selectedIcon属性,选中时可切换为填充图标
使用示例
// 定义带选中态图标的导航项 final List<NavigationDestination> destinations = [ NavigationDestination( icon: Icons.home_outlined, selectedIcon: Icons.home, label: '首页', ), NavigationDestination( icon: Icons.search_outlined, selectedIcon: Icons.search, label: '搜索', ), NavigationDestination( icon: Icons.shopping_cart_outlined, selectedIcon: Icons.shopping_cart, label: '购物车', ), ]; // 在页面中集成 CustomNavigationBar( destinations: destinations, selectedIndex: _selectedIndex, onDestinationSelected: (index) { setState(() { _selectedIndex = index; }); }, backgroundColor: Colors.green.shade700, selectedColor: Colors.white, unselectedColor: Colors.white70, )
针对特殊样式的扩展
如果你的目标样式有特殊需求,可针对性修改:
- 底部指示器:在选中项的
Column末尾添加:if (isSelected) Container( height: 2, width: 24, color: selectedColor, margin: const EdgeInsets.only(top: 4), ), - 凸起胶囊选中态:修改
AnimatedContainer的decoration:decoration: isSelected ? BoxDecoration( color: selectedColor, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black12, blurRadius: 8, offset: const Offset(0, -2), ), ], ) : null,
内容的提问来源于stack exchange,提问作者Hossam Hassan
相关产品推荐
相关产品推荐

