Flutter实现手持摄像头抖动效果及ListView子项晃动动画咨询
Flutter 列表项循环小幅晃动效果实现方案
可用第三方包
flutter_shake_animated:开箱即用的晃动动画组件库,支持配置晃动方向、幅度、频率、循环模式等参数,引入后直接包裹列表项即可实现需求效果。simple_animations:通用动画工具库,内置预设的晃动动画效果,同时支持高度自定义,适合需要和其他动画效果搭配使用的场景。
原生手动实现方案
如果不想引入第三方依赖,可以通过Flutter原生动画API实现,步骤如下:
1. 封装可复用的晃动组件
import 'package:flutter/material.dart'; class ShakingItem extends StatefulWidget { final Widget child; /// 晃动幅度,单位为dp final double shakeOffset; /// 单次晃动周期 final Duration shakeDuration; /// 晃动方向,默认水平晃动 final Axis shakeAxis; const ShakingItem({ super.key, required this.child, this.shakeOffset = 2.5, this.shakeDuration = const Duration(milliseconds: 350), this.shakeAxis = Axis.horizontal, }); @override State<ShakingItem> createState() => _ShakingItemState(); } class _ShakingItemState extends State<ShakingItem> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _offsetAnimation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: widget.shakeDuration, )..repeat(reverse: true); // 开启循环往复动画 _offsetAnimation = Tween<double>( begin: -widget.shakeOffset, end: widget.shakeOffset, ).animate(CurvedAnimation( parent: _controller, curve: Curves.easeInOutSine, )); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _offsetAnimation, builder: (_, child) => Transform.translate( offset: widget.shakeAxis == Axis.horizontal ? Offset(_offsetAnimation.value, 0) : Offset(0, _offsetAnimation.value), child: child, ), child: widget.child, ); } }
2. 在ListView中使用
将列表项外层包裹ShakingItem即可,可通过给不同item设置不同的周期参数,避免所有列表项同步晃动,效果更自然:
ListView.builder( padding: const EdgeInsets.all(16), itemCount: 30, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: ShakingItem( // 错开每个列表项的晃动周期 shakeDuration: Duration(milliseconds: 350 + index * 15), child: Container( height: 60, padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: const [ BoxShadow(color: Colors.black12, blurRadius: 2, offset: Offset(0, 1)) ], ), alignment: Alignment.centerLeft, child: Text("列表项 $index"), ), ), ); }, )
3. 参数调整说明
- 小幅晃动的幅度建议设置在1.5~3dp之间,避免幅度过大影响内容阅读
- 曲线使用
Curves.easeInOutSine可以让晃动更平滑,也可替换为其他动画曲线调整效果 - 如需暂停晃动,可给
ShakingItem新增控制参数,调用_controller.stop()/_controller.repeat()即可控制动画启停
内容的提问来源于stack exchange,提问作者tsoof bar-or
相关产品推荐
相关产品推荐

