如何在点击或下滑时改变BottomNavigationBar的尺寸并移除部分项?
核心思路
靠一个状态变量控制导航栏的展开/收缩状态,搭配动画实现平滑的尺寸过渡,同时动态调整显示的导航项数量。
具体实现代码
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: const MapScreen(), theme: ThemeData(primarySwatch: Colors.blue), ); } } class MapScreen extends StatefulWidget { const MapScreen({super.key}); @override State<MapScreen> createState() => _MapScreenState(); } class _MapScreenState extends State<MapScreen> with SingleTickerProviderStateMixin { bool _isExpanded = true; late AnimationController _animationController; late Animation<double> _heightAnimation; // 完整导航项列表 final List<BottomNavigationBarItem> _fullItems = const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"), BottomNavigationBarItem(icon: Icon(Icons.search), label: "搜索"), BottomNavigationBarItem(icon: Icon(Icons.favorite), label: "收藏"), BottomNavigationBarItem(icon: Icon(Icons.settings), label: "设置"), ]; // 收缩时保留的导航项(示例只留前2个) List<BottomNavigationBarItem> get _collapsedItems => _fullItems.take(2).toList(); @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _heightAnimation = Tween<double>(begin: 80, end: 50).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, )); } @override void dispose() { _animationController.dispose(); super.dispose(); } // 切换展开/收缩状态 void _toggleBarState() { setState(() { _isExpanded = !_isExpanded; _isExpanded ? _animationController.reverse() : _animationController.forward(); }); } @override Widget build(BuildContext context) { return Scaffold( body: GestureDetector( // 点击地图区域切换状态 onTap: _toggleBarState, // 下滑手势切换状态 onVerticalDragUpdate: (details) { if (details.delta.dy > 10 && _isExpanded) _toggleBarState(); if (details.delta.dy < -10 && !_isExpanded) _toggleBarState(); }, child: Container( color: Colors.grey[100], child: const Center(child: Text("地图区域")), ), ), bottomNavigationBar: AnimatedBuilder( animation: _animationController, builder: (context, child) { return SizedBox( height: _heightAnimation.value, child: BottomNavigationBar( items: _isExpanded ? _fullItems : _collapsedItems, selectedItemColor: Colors.blue, unselectedItemColor: Colors.grey, type: BottomNavigationBarType.fixed, // 收缩时隐藏文字标签 showSelectedLabels: _isExpanded, showUnselectedLabels: _isExpanded, ), ); }, ), ); } }
关键细节说明
- 状态切换:用
_isExpanded标记当前状态,切换时触发动画控制器的正向/反向播放 - 平滑过渡:通过
AnimatedBuilder监听动画值,动态修改导航栏高度,避免生硬的尺寸跳转 - 手势响应:在地图区域加
GestureDetector,监听点击和垂直滑动动作,触发状态变更 - 动态项控制:根据展开状态返回完整或精简的导航项列表,同时控制文字标签的显示
扩展优化建议
- 可以根据滑动速度调整动画时长,让交互更跟手
- 收缩时优先保留核心功能项,隐藏次要项,贴合实际业务需求
- 加入状态切换的轻量音效,增强用户操作反馈
内容的提问来源于stack exchange,提问作者ElectricArdv
相关产品推荐
相关产品推荐

