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

Flutter实现GridView网格方块间跳跃动画,Hero/AnimatedContainer无效如何解决

问题根因

你当前的实现存在3个核心问题导致没有动画效果:

  • Hero动画仅适用于路由页面切换的共享元素场景,同页面内的元素位移不会触发Hero过渡
  • AnimatedContainer的所有属性都是固定值,没有随状态变化的变量,自然无法产生过渡动画
  • 圆点仅在匹配location == index的网格项中渲染,位置变化时旧位置的圆点直接销毁、新位置直接创建,只有闪烁没有过渡

实现思路

不要把圆点放在每个网格项内部,而是把圆点作为独立的动画层放在GridView的上层,通过动画修改圆点的偏移量实现跳跃效果,方案步骤如下:

  1. 预先计算每个网格项的尺寸:用crossAxisCount算出网格宽度,SliverGridDelegateWithFixedCrossAxisCount的默认宽高比是1,所以网格宽高相同
  2. 用Stack包裹GridView和圆点,圆点用AnimatedPositioned包裹,位移属性随location变化自动触发过渡动画
  3. 点击按钮修改location时,只需要更新圆点的top和left偏移量即可

完整可运行代码

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int location = 0;
  // 网格列数
  final int crossAxisCount = 3;
  // 圆点尺寸
  final double dotSize = 7;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(children: [
        Expanded(
          child: LayoutBuilder(
            // 用LayoutBuilder获取父容器可用宽度,计算网格尺寸
            builder: (context, constraints) {
              // 每个网格的宽度 = 总宽度 / 列数
              final double gridItemWidth = constraints.maxWidth / crossAxisCount;
              // 网格默认宽高比1,所以高度和宽度相同
              final double gridItemHeight = gridItemWidth;
              // 计算当前圆点所在的行和列
              final int col = location % crossAxisCount;
              final int row = location ~/ crossAxisCount;
              // 计算圆点的偏移量:网格中心位置减去圆点自身一半尺寸,实现居中显示
              final double left = col * gridItemWidth + (gridItemWidth - dotSize)/2;
              final double top = row * gridItemHeight + (gridItemHeight - dotSize)/2;

              return Stack(
                children: [
                  // 底层网格
                  GridView.builder(
                    itemCount: 10,
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                        crossAxisCount: crossAxisCount),
                    itemBuilder: (context, index) {
                      return Container(
                        decoration: BoxDecoration(
                          border: Border.all(color: Colors.black, width: 0.1),
                        ),
                      );
                    },
                  ),
                  // 上层动画圆点
                  AnimatedPositioned(
                    duration: const Duration(milliseconds: 400),
                    curve: Curves.bounceOut, // 跳跃效果的曲线,换成linear就是平滑移动
                    left: left,
                    top: top,
                    child: Container(
                      width: dotSize,
                      height: dotSize,
                      decoration: const BoxDecoration(
                        color: Colors.amber,
                        shape: BoxShape.circle,
                      ),
                    ),
                  )
                ],
              );
            }
          ),
        ),
        ElevatedButton(
          onPressed: () {
            setState(() {
              // 超过总数量就重置到开头,可根据需求修改
              location = (location + 2) % 10;
            });
          },
          child: const Text('Jump'),
        ),
      ]),
    );
  }
}

效果调整说明

  • 要修改跳跃的动效,只要替换AnimatedPositioned的curve参数即可,比如用Curves.easeInOut就是平滑移动,Curves.bounceOut就是带弹跳的跳跃效果
  • 如果网格项有自定义的padding、间距,只要在计算left和top偏移量的时候加上对应间距即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 11:00:01