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

Flutter实现GridView中Container从当前索引到新索引的动画效果

Flutter网格玩家平滑移动动画实现方案

你当前的代码没法实现平滑移动,因为是直接把玩家图标切换到新网格位置,没有真正的位置过渡动画。要实现玩家在网格间的平滑移动,需要把玩家元素从每个网格中抽离出来,独立控制它的位置,通过计算网格索引对应的坐标来做动画过渡。

修改思路

  • 用Stack包裹GridView和玩家图标,让玩家图标悬浮在网格上层
  • 把网格索引转换成对应坐标位置(列索引=index%4,行索引=index~/4)
  • 用AnimatedPositioned实现玩家图标的位置平滑过渡
  • 触发位置更新时,直接修改当前位置状态,让动画自动执行

完整代码实现

class PlayerGrid extends StatefulWidget {
  @override
  _PlayerGridState createState() => _PlayerGridState();
}

class _PlayerGridState extends State<PlayerGrid> {
  int currentPosition = 3;
  // 单个网格的尺寸,和Grid组件保持一致
  final double gridSize = 50;

  // 把网格索引转换成左上角坐标
  Offset getGridOffset(int index) {
    int column = index % 4;
    int row = index ~/ 4;
    return Offset(column * gridSize, row * gridSize);
  }

  // 触发移动到目标位置
  void moveToPosition(int targetIndex) {
    setState(() {
      currentPosition = targetIndex;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 250,
      width: 250,
      padding: const EdgeInsets.all(10),
      color: Colors.green.shade900,
      child: Stack(
        children: [
          // 基础网格
          GridView.builder(
            physics: const NeverScrollableScrollPhysics(), // 禁止滚动避免干扰
            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 4, crossAxisSpacing: 0, mainAxisSpacing: 0),
            itemCount: 16,
            itemBuilder: (context, index) {
              return Grid(
                isPlayerHere: false, // 不再让网格显示玩家,统一由上层控制
                index: index,
                onTap: () => moveToPosition(index), // 添加点击触发移动
              );
            },
          ),
          // 悬浮的玩家图标,实现平滑移动
          AnimatedPositioned(
            duration: const Duration(milliseconds: 500),
            curve: Curves.easeInOut,
            left: getGridOffset(currentPosition).dx,
            top: getGridOffset(currentPosition).dy,
            child: Image.asset(
              'assets/images/user.png',
              height: 30,
              color: Colors.red.shade900,
            ),
          ),
        ],
      ),
    );
  }
}

class Grid extends StatelessWidget {
  final bool isPlayerHere;
  final int index;
  final VoidCallback? onTap; // 添加点击回调
  const Grid({required this.isPlayerHere, required this.index, this.onTap});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        width: 50,
        height: 50,
        color: Colors.white,
        child: Center(
          child: Text(index.toString()),
        ),
      ),
    );
  }
}

关键说明

  1. 坐标转换:getGridOffset函数把网格索引转换成对应屏幕坐标,确保玩家图标精准对应网格位置。
  2. 独立控制玩家:玩家图标放在Stack上层,脱离每个网格的控制,才能自由移动。
  3. 动画触发:点击任意网格时调用moveToPosition更新currentPosition,AnimatedPositioned会自动根据新坐标执行平滑过渡动画。
  4. 网格交互:给Grid添加GestureDetector,让用户可以点击网格触发移动。

你可以根据需求调整动画时长(duration)和曲线(curve),比如把时长改成800毫秒,或者用Curves.bounceOut实现弹性效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:13:11