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

Flutter BottomNavigationBar非选中项动画异常问题

Flutter BottomNavigationBar非选中项动画异常问题

嘿,我完全懂你遇到的这个糟心问题——每次点底部导航栏的一个选项,它自己的动画正常播了,但其他所有没选中的图标都跟着跑一遍关闭动画,确实很影响体验。咱们来搞定它!

问题根源

你现在的写法是直接把Lottie实例作为activeIcon和icon传给BottomNavigationBarItem,每次currentIndex变化时,整个底部导航栏的所有项都会重建。这就导致每个非选中项的icon(也就是你的off动画)都会重新初始化并从头播放一遍,不管它之前有没有被激活过。

解决方案:用状态管理控制动画播放时机

我们可以自定义一个带状态的导航项组件,用Lottie的动画控制器来精准控制什么时候播放动画——只有当某个项从选中状态切换到非选中状态时,才播放它的off动画;初始未选中的项直接保持静态,不播放动画。

步骤1:自定义带状态的导航项组件

class NavItemWidget extends StatefulWidget {
  final String assetOn;
  final String assetOff;
  final String label;
  final bool isSelected;

  const NavItemWidget({
    super.key,
    required this.assetOn,
    required this.assetOff,
    required this.label,
    required this.isSelected,
  });

  @override
  State<NavItemWidget> createState() => _NavItemWidgetState();
}

class _NavItemWidgetState extends State<NavItemWidget> with TickerProviderStateMixin {
  late final LottieAnimationController _onController;
  late final LottieAnimationController _offController;
  bool _previousSelectedState = false;

  @override
  void initState() {
    super.initState();
    // 初始化两个动画控制器
    _onController = LottieAnimationController(vsync: this);
    _offController = LottieAnimationController(vsync: this);
    _previousSelectedState = widget.isSelected;

    // 初始状态处理:选中则播放on动画,未选中则停在off动画的结尾(避免播放)
    if (widget.isSelected) {
      _onController.play();
    } else {
      _offController.value = 1.0;
    }
  }

  @override
  void didUpdateWidget(covariant NavItemWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 只有当选中状态发生变化时,才触发对应动画
    if (widget.isSelected != _previousSelectedState) {
      if (widget.isSelected) {
        // 切换为选中:播放on动画,重置off动画
        _offController.reset();
        _onController.play(from: 0);
      } else {
        // 切换为未选中:播放off动画,重置on动画
        _onController.reset();
        _offController.play(from: 0);
      }
      _previousSelectedState = widget.isSelected;
    }
  }

  @override
  void dispose() {
    // 记得销毁控制器,避免内存泄漏
    _onController.dispose();
    _offController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBarItem(
      activeIcon: Lottie.asset(
        widget.assetOn,
        width: 20,
        height: 20,
        controller: _onController,
        repeat: false,
        frameRate: FrameRate.max,
      ),
      icon: Lottie.asset(
        widget.assetOff,
        width: 20,
        height: 20,
        controller: _offController,
        repeat: false,
        frameRate: FrameRate.max,
      ),
      label: tr(widget.label),
    );
  }
}

步骤2:修改原BottomNavigationBar的items部分

把原来的getNavItem调用换成我们自定义的组件,同时传入当前项是否被选中的状态:

BottomNavigationBar(
  currentIndex: navIndex ?? 0,
  onTap: (final index) {
    ref.read(bottomNavProvider.notifier).setAndPersistValue(index);
  },
  type: BottomNavigationBarType.fixed,
  elevation: 0,
  backgroundColor: Colors.transparent,
  showSelectedLabels: false,
  showUnselectedLabels: false,
  items: <BottomNavigationBarItem>[
    NavItemWidget(
      assetOn: 'assets/anim/home.json',
      assetOff: 'assets/anim/home_off.json',
      label: 'bottom_nav_home',
      isSelected: navIndex == 0,
    ),
    NavItemWidget(
      assetOn: 'assets/anim/tasks.json',
      assetOff: 'assets/anim/tasks_off.json',
      label: 'bottom_nav_tasks',
      isSelected: navIndex == 1,
    ),
    NavItemWidget(
      assetOn: 'assets/anim/subjects.json',
      assetOff: 'assets/anim/subjects_off.json',
      label: 'bottom_nav_reports',
      isSelected: navIndex == 2,
    ),
    NavItemWidget(
      assetOn: 'assets/anim/profile.json',
      assetOff: 'assets/anim/profile_off.json',
      label: 'bottom_nav_settings',
      isSelected: navIndex == 3,
    ),
  ],
),

原理说明

通过自定义的NavItemWidget,我们可以跟踪每个导航项的选中状态变化:

  • 初始加载时,未选中的项直接把off动画的控制器设为结尾值,不会播放动画
  • 只有当某个项的选中状态从true变成false时,才会触发off动画播放
  • 切换到选中状态时,触发on动画播放,同时重置另一个动画的状态

这样就完美解决了所有非选中项乱播动画的问题啦!

备注:内容来源于stack exchange,提问作者Tom

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 10:10:29