如何在Flutter中实现带动态参数的类游戏风格信封开启动画
方案一:优化Rive动画实现(适配动态参数)
针对你用Rive遇到的动态参数问题,核心是把动态属性和Rive状态机绑定,同时将难动态替换的内容(卡片文本、图片)放到Flutter层处理:
动态颜色绑定:
在Rive编辑器中给信封、背景的填充层添加Color类型的State Machine输入(命名如envelopColor、bgColor)。在Flutter代码中通过控制器获取这些输入并赋值:final artboard = riveFile.mainArtboard; final controller = StateMachineController.fromArtboard(artboard, 'EnvelopeMachine'); if (controller != null) { artboard.addController(controller); // 设置信封颜色 final envColorInput = controller.findInput<ColorInput>('envelopColor'); if (envColorInput != null) { // Rive的Color值为ARGB格式int,需转换 envColorInput.value = envelopColor.value; } // 设置背景颜色同理 final bgColorInput = controller.findInput<ColorInput>('bgColor'); if (bgColorInput != null) { bgColorInput.value = backgroundColor.value; } }卡片内容动态化:
不在Rive中制作卡片的文本和图片,直接用Flutter的Text、Image组件,通过Stack叠在Rive动画上方。监听Rive动画进度,当信封打开到指定阶段(比如进度>0.6)时,触发卡片的淡入、平移动画:bool _hasTriggeredCardAnim = false; controller.addListener(() { final progress = controller.progress; if (progress > 0.6 && !_hasTriggeredCardAnim) { _cardAnimController.forward(); _hasTriggeredCardAnim = true; } });卡片动画可通过
AnimatedBuilder结合Tween实现平移+透明度变化。信封阴影动态生成:
不在Rive内做阴影,给Rive动画外层套一个Container,根据信封颜色生成阴影色:Color generateShadow(Color base) { final hsl = HSLColor.fromColor(base); // 降低亮度并添加透明度生成阴影 return hsl.withLightness(hsl.lightness * 0.7).withAlpha(0.4).toColor(); } // 使用示例 Container( decoration: BoxDecoration( boxShadow: [ BoxShadow( color: generateShadow(envelopColor), blurRadius: 12, offset: Offset(0, 6), ) ], ), child: RiveAnimation.asset(...), )
方案二:纯Flutter自定义动画(完全可控)
如果Rive的动态绑定仍有局限,可采用纯Flutter实现,灵活性更高:
核心动画控制:
用AnimationController管理整体动画进度,配合Tween定义信封盖的旋转角度、卡片的平移距离和透明度:final _animController = AnimationController( duration: Duration(milliseconds: 1500), vsync: this, ); final _envelopeCoverRotate = Tween<double>(begin: 0, end: pi / 2).animate( CurvedAnimation(parent: _animController, curve: Curves.easeInOut), ); final _cardTranslate = Tween<Offset>(begin: Offset(0, 20), end: Offset(0, 0)).animate( CurvedAnimation(parent: _animController, curve: Curves.easeOut), ); final _cardOpacity = Tween<double>(begin: 0, end: 1).animate( CurvedAnimation(parent: _animController, curve: Interval(0.5, 1)), );自定义信封绘制:
用CustomPainter绘制信封的身和盖,接收envelopColor参数,根据_envelopeCoverRotate的值更新盖的角度:class EnvelopePainter extends CustomPainter { final Color color; final double coverRotation; EnvelopePainter(this.color, this.coverRotation); @override void paint(Canvas canvas, Size size) { // 绘制信封身(矩形) final bodyPaint = Paint()..color = color; canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), bodyPaint); // 绘制信封盖(多边形,结合旋转角度变换顶点位置) final coverPath = Path(); coverPath.moveTo(size.width / 2, 0); coverPath.lineTo(size.width, size.height / 2); coverPath.lineTo(size.width / 2, size.height); coverPath.lineTo(0, size.height / 2); coverPath.close(); canvas.save(); canvas.translate(size.width / 2, size.height / 2); canvas.rotate(coverRotation); canvas.translate(-size.width / 2, -size.height / 2); canvas.drawPath(coverPath, bodyPaint); canvas.restore(); } @override bool shouldRepaint(covariant EnvelopePainter oldDelegate) { return oldDelegate.color != color || oldDelegate.coverRotation != coverRotation; } }动态参数整合:
背景色直接用外层Container的color属性;卡片文本、图片直接传入Text、Image.network组件;阴影同样用BoxShadow结合颜色生成函数实现。交互触发:
用户点击时调用_animController.forward()触发动画,如需重置可调用_animController.reset()。
内容的提问来源于stack exchange,提问作者Rafid Hossain

