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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 21:54:03