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()), ), ), ); } }
关键说明
- 坐标转换:
getGridOffset函数把网格索引转换成对应屏幕坐标,确保玩家图标精准对应网格位置。 - 独立控制玩家:玩家图标放在
Stack上层,脱离每个网格的控制,才能自由移动。 - 动画触发:点击任意网格时调用
moveToPosition更新currentPosition,AnimatedPositioned会自动根据新坐标执行平滑过渡动画。 - 网格交互:给
Grid添加GestureDetector,让用户可以点击网格触发移动。
你可以根据需求调整动画时长(duration)和曲线(curve),比如把时长改成800毫秒,或者用Curves.bounceOut实现弹性效果。
内容的提问来源于stack exchange,提问作者Mona
相关产品推荐
相关产品推荐

