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
相关产品推荐
相关产品推荐

