如何在Flutter启动页实现字母聚合及底部按钮弹出动画?
Flutter启动页动画实现方案
核心实现思路
- 用
AnimationController统筹动画时序:先执行四个字母从四方向向中心聚合的位移动画,完成后触发底部社交按钮的弹出动画(结合透明度+位移动画)。 - 借助
Animation对象定义每个字母的偏移路径,以及按钮的透明度和位置变化。 - 监听动画完成状态,在所有动画结束后执行页面跳转逻辑。
完整代码实现
import 'package:flutter/material.dart'; class SplashScreen extends StatefulWidget { const SplashScreen({Key? key}) : super(key: key); @override State<SplashScreen> createState() => _SplashScreenState(); } class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin { late AnimationController _controller; // 四个字母的位移动画 late Animation<Offset> _bOffset; late Animation<Offset> _cOffset; late Animation<Offset> _dOffset; late Animation<Offset> _eOffset; // 底部按钮的透明度和位移动画 late Animation<double> _buttonOpacity; late Animation<Offset> _buttonOffset; @override void initState() { super.initState(); // 初始化动画控制器,总时长3秒(字母聚合2秒,按钮弹出1秒) _controller = AnimationController( vsync: this, duration: const Duration(seconds: 3), ); // 配置字母动画:从四个屏幕外方向向中心移动 _bOffset = Tween<Offset>( begin: const Offset(-1.5, 0), // 左侧屏幕外 end: Offset.zero, ).animate(CurvedAnimation( parent: _controller, curve: const Interval(0.0, 0.66, curve: Curves.easeOut), // 前2秒执行 )); _cOffset = Tween<Offset>( begin: const Offset(0, -1.5), // 顶部屏幕外 end: Offset.zero, ).animate(CurvedAnimation( parent: _controller, curve: const Interval(0.0, 0.66, curve: Curves.easeOut), )); _dOffset = Tween<Offset>( begin: const Offset(0, 1.5), // 底部屏幕外 end: Offset.zero, ).animate(CurvedAnimation( parent: _controller, curve: const Interval(0.0, 0.66, curve: Curves.easeOut), )); _eOffset = Tween<Offset>( begin: const Offset(1.5, 0), // 右侧屏幕外 end: Offset.zero, ).animate(CurvedAnimation( parent: _controller, curve: const Interval(0.0, 0.66, curve: Curves.easeOut), )); // 配置按钮动画:从底部向上弹出并渐显 _buttonOpacity = Tween<double>( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _controller, curve: const Interval(0.66, 1.0, curve: Curves.easeInOut), // 最后1秒执行 )); _buttonOffset = Tween<Offset>( begin: const Offset(0, 1), // 初始在底部下方 end: Offset.zero, ).animate(CurvedAnimation( parent: _controller, curve: const Interval(0.66, 1.0, curve: Curves.easeInOut), )); // 监听动画完成状态,执行页面跳转 _controller.addStatusListener((status) { if (status == AnimationStatus.completed) { navigatePage(); } }); // 启动动画 _controller.forward(); } void navigatePage() async { // 替换为你的登录状态判断逻辑 bool isLoggedIn = false; await Future.delayed(const Duration(milliseconds: 500)); // 按钮显示后稍作停留 if (mounted) { Navigator.pushReplacementNamed(context, isLoggedIn ? "home" : "login"); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xffF7F8F8), body: Stack( alignment: Alignment.center, children: [ // 字母聚合动画区域 Stack( alignment: Alignment.center, children: [ Transform.translate( offset: _bOffset.value * MediaQuery.of(context).size.width, child: const Text( "B", style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold), ), ), Transform.translate( offset: _cOffset.value * MediaQuery.of(context).size.height, child: const Text( "C", style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold), ), ), Transform.translate( offset: _dOffset.value * MediaQuery.of(context).size.height, child: const Text( "D", style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold), ), ), Transform.translate( offset: _eOffset.value * MediaQuery.of(context).size.width, child: const Text( "E", style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold), ), ), ], ), // 底部社交按钮区域 Positioned( bottom: 40, left: 0, right: 0, child: SlideTransition( position: _buttonOffset, child: FadeTransition( opacity: _buttonOpacity, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.facebook), onPressed: () {}, ), IconButton( icon: const Icon(Icons.google), onPressed: () {}, ), IconButton( icon: const Icon(Icons.apple), onPressed: () {}, ), ], ), ), ), ), ], ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } }
关键细节说明
- 动画时序通过
Interval精确控制:字母动画在前2秒(0.0到0.66区间)执行,按钮动画在最后1秒(0.66到1.0区间)触发,确保顺序执行。 - 用屏幕尺寸计算偏移量,让字母从屏幕外进入,适配不同设备分辨率。
- 动画完成后延迟500毫秒再跳转,给按钮显示留足视觉停留时间。
- 混入
SingleTickerProviderStateMixin为动画控制器提供帧同步支持,避免资源浪费。
内容的提问来源于stack exchange,提问作者Shruti Ramnandan Sharma
相关产品推荐
相关产品推荐

