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

如何实现AnimatedAlign精准动画?底部导航栏白点定位问题

精准控制白色圆点动画的方案

方法1:优化AnimatedAlign的对齐计算

如果坚持用AnimatedAlign,核心是通过导航项的数量计算准确的Alignment参数:

  • 用Expanded给每个导航项均分宽度,确保占比一致。
  • Alignment的水平参数范围是-1到1(对应屏幕从左到右),假设导航有n个项,第i个选中项(从0开始计数)的水平对齐值为:2 * ((i + 0.5) / n) - 1,这个公式能把每个项的中心位置转换成AnimatedAlign可识别的坐标。
  • 配合AnimationController和Tween实现平滑过渡。

代码示例:

int _selectedIndex = 0;
final int _navCount = 4; // 根据实际导航项数量调整
late AnimationController _animController;
late Animation<Alignment> _dotAlignment;

@override
void initState() {
  super.initState();
  _animController = AnimationController(
    duration: const Duration(milliseconds: 300),
    vsync: this,
  );
  _updateDotAlignment();
}

void _updateDotAlignment() {
  double horizontalAlign = 2 * ((_selectedIndex + 0.5) / _navCount) - 1;
  _dotAlignment = Tween<Alignment>(
    begin: _dotAlignment?.value ?? Alignment(2*(0+0.5)/_navCount -1, 0),
    end: Alignment(horizontalAlign, 0),
  ).animate(CurvedAnimation(
    parent: _animController,
    curve: Curves.easeInOut,
  ));
  _animController.forward(from: 0);
}

void _onTapNavItem(int index) {
  setState(() {
    _selectedIndex = index;
    _updateDotAlignment();
  });
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    bottomNavigationBar: Container(
      height: 60,
      color: Colors.grey[800],
      child: Stack(
        children: [
          // 导航项列表
          Row(
            children: List.generate(_navCount, (index) {
              return Expanded(
                child: GestureDetector(
                  onTap: () => _onTapNavItem(index),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(
                        index == 0 ? Icons.home : Icons.search, // 替换成你的导航图标
                        color: _selectedIndex == index ? Colors.white : Colors.grey,
                      ),
                    ],
                  ),
                ),
              );
            }),
          ),
          // 动画圆点
          AnimatedAlign(
            alignment: _dotAlignment.value,
            duration: const Duration(milliseconds: 300),
            child: const SizedBox(
              width: 12,
              height: 12,
              child: DecoratedBox(
                decoration: BoxDecoration(
                  color: Colors.white,
                  shape: BoxShape.circle,
                ),
              ),
            ),
          ),
        ],
      ),
    ),
  );
}

方法2:用AnimatedPositioned更直观控制

如果觉得AnimatedAlign的坐标计算麻烦,可以换成AnimatedPositioned,直接计算圆点的绝对位置:

  • 获取屏幕宽度,除以导航项数量得到每个项的宽度。
  • 选中项时,圆点的left值为:(选中项索引 * 单项宽度) + (单项宽度 / 2) - 圆点半径。
  • 更新left值即可实现位移动画。

代码示例:

int _selectedIndex = 0;
final int _navCount = 4;
double _dotLeft = 0;

@override
void initState() {
  super.initState();
  // 初始化圆点位置
  WidgetsBinding.instance.addPostFrameCallback((_) {
    double itemWidth = MediaQuery.of(context).size.width / _navCount;
    _dotLeft = (0 * itemWidth) + (itemWidth / 2) - 6; // 6是圆点半径
    setState(() {});
  });
}

void _onTapNavItem(int index) {
  double itemWidth = MediaQuery.of(context).size.width / _navCount;
  setState(() {
    _selectedIndex = index;
    _dotLeft = (index * itemWidth) + (itemWidth / 2) - 6;
  });
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    bottomNavigationBar: Container(
      height: 60,
      color: Colors.grey[800],
      child: Stack(
        children: [
          // 导航项列表
          Row(
            children: List.generate(_navCount, (index) {
              return Expanded(
                child: GestureDetector(
                  onTap: () => _onTapNavItem(index),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(
                        index == 0 ? Icons.home : Icons.search,
                        color: _selectedIndex == index ? Colors.white : Colors.grey,
                      ),
                    ],
                  ),
                ),
              );
            }),
          ),
          // 动画圆点
          AnimatedPositioned(
            left: _dotLeft,
            bottom: 8,
            duration: const Duration(milliseconds: 300),
            curve: Curves.easeInOut,
            child: const SizedBox(
              width: 12,
              height: 12,
              child: DecoratedBox(
                decoration: BoxDecoration(
                  color: Colors.white,
                  shape: BoxShape.circle,
                ),
              ),
            ),
          ),
        ],
      ),
    ),
  );
}

两种方法都能实现精准对齐的动画效果,可根据习惯选择。


内容的提问来源于stack exchange,提问作者vicky_prog

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 13:15:09