You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在点击或下滑时改变BottomNavigationBar的尺寸并移除部分项?

实现可收缩/扩展的BottomNavigationBar(复刻Citymapper效果)

核心思路

靠一个状态变量控制导航栏的展开/收缩状态,搭配动画实现平滑的尺寸过渡,同时动态调整显示的导航项数量。

具体实现代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 17:17:22