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

Flutter启动页基于Stack/Positioned实现Widget尺寸变化与多组件联动动画

实现方案

无需引入额外第三方依赖,用Flutter原生动画API即可轻量实现需求,步骤如下:

1. 改造页面State类,初始化动画配置

给State类混入SingleTickerProviderStateMixin,定义动画控制器和对应的补间动画,启动动画并做好资源销毁:

class SplashPageState extends State<SplashPage> with SingleTickerProviderStateMixin {
  late AnimationController _animController;
  // 右上白色斜条组向上位移动画
  late Animation<double> _topWhiteBlockOffset;
  // 左下深绿色斜条组向下位移动画
  late Animation<double> _bottomGreenBlockOffset;
  // 中间logo翻转动画
  late Animation<double> _logoRotateAnim;

  @override
  void initState() {
    super.initState();
    // 动画时长可根据实际效果调整
    _animController = AnimationController(vsync: this, duration: const Duration(seconds: 2));
    // 位移数值可根据视觉效果自行调整
    _topWhiteBlockOffset = Tween<double>(begin: 0, end: -160).animate(
      CurvedAnimation(parent: _animController, curve: Curves.easeInOut)
    );
    _bottomGreenBlockOffset = Tween<double>(begin: 0, end: 160).animate(
      CurvedAnimation(parent: _animController, curve: Curves.easeInOut)
    );
    // 从右到左翻转设置end为-2*pi即可
    _logoRotateAnim = Tween<double>(begin: 0, end: 2 * 3.1415926).animate(
      CurvedAnimation(parent: _animController, curve: Curves.easeInOut)
    );
    // 启动动画,需要循环播放的话改成 _animController.repeat()
    _animController.forward();
  }

  @override
  void dispose() {
    _animController.dispose();
    super.dispose();
  }

2. 绑定动画值到对应Widget

用AnimatedBuilder监听动画更新,无需主动调用setState,性能更轻量:

  • 给右上白色斜条组的Positioned添加top属性,值绑定_topWhiteBlockOffset.value实现上移
  • 给左下深绿色斜条组的Positioned修改bottom属性为320 - _bottomGreenBlockOffset.value实现下移
  • 给中间logo的旋转属性绑定_logoRotateAnim.value实现翻转动画

完整布局代码如下:

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Container(
      height: double.infinity,
      width: double.infinity,
      child: AnimatedBuilder(
        animation: _animController,
        builder: (context, child) {
          return Stack(
            children: [
              SizedBox.expand(
                child: Image.asset(
                  "assets/images/splash.png",
                  fit: BoxFit.fill,
                ),
              ),
              // 右上白色斜条组
              Positioned(
                right: -150,
                top: _topWhiteBlockOffset.value,
                child: Transform.rotate(
                  angle: -math.pi / 4.0,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.start,
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Container(
                        height: 10,
                        width: 300,
                        decoration: BoxDecoration(
                          color: Colors.white.withOpacity(0.5),
                          borderRadius: BorderRadius.circular(20),
                        ),
                      ),
                      const SizedBox(height: 15),
                      Container(
                        height: 25,
                        width: 400,
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(20),
                        ),
                      ),
                      const SizedBox(height: 15),
                      Container(
                        height: 15,
                        width: 210,
                        decoration: BoxDecoration(
                          color: Colors.white.withOpacity(0.9),
                          borderRadius: BorderRadius.circular(20),
                        ),
                      )
                    ],
                  ),
                ),
              ),
              Positioned(
                left: 0,
                right: 0,
                bottom: 10,
                top: -120,
                child: Center(
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      Text(
                        'Un',
                        style: TextStyle(
                            color: Colors.white,
                            fontSize: 45.sp,
                            fontWeight: FontWeight.w500),
                      ),
                      const SizedBox(width: 5),
                      // 中间logo翻转动画
                      Transform.rotate(
                        angle: _logoRotateAnim.value,
                        child: SvgPicture.asset(
                          "assets/white_lgo.svg",
                          width: 50.w,
                          height: 35.h,
                        ),
                      ),
                      const SizedBox(width: 5),
                      Text(
                        'leap',
                        style: TextStyle(
                            color: Colors.white,
                            fontSize: 45.sp,
                            fontWeight: FontWeight.w500),
                      ),
                    ],
                  ),
                ),
              ),
              // 左下深绿色斜条组
              Positioned(
                left: -140,
                bottom: 320 - _bottomGreenBlockOffset.value,
                child: Transform.rotate(
                  angle: -math.pi / 4.0,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.start,
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Container(
                        height: 10,
                        width: 140,
                        decoration: BoxDecoration(
                          color: const Color(0xff103E3F),
                          borderRadius: BorderRadius.circular(20),
                        ),
                      ),
                      const SizedBox(height: 15),
                      Container(
                        height: 25,
                        width: 70,
                        decoration: BoxDecoration(
                          color: const Color(0xff103E3F),
                          borderRadius: BorderRadius.circular(20),
                        ),
                      ),
                      const SizedBox(height: 15),
                      Container(
                        height: 13,
                        width: 300,
                        decoration: BoxDecoration(
                          color: const Color(0xff103E3F),
                          borderRadius: BorderRadius.circular(20),
                        ),
                      )
                    ],
                  ),
                ),
              ),
            ],
          );
        },
      ),
    ),
  );
}

如果你不想替换原来使用的Animator组件,直接把原有代码里的angle: 0改成angle: anim.value即可实现logo旋转效果,无需调整其他逻辑。

内容的提问来源于stack exchange,提问作者Gbenga B Ayannuga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 13:24:10