shimmer_effect.dart 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import 'package:flutter/material.dart';
  2. class ShimmerEffect extends StatefulWidget {
  3. final Widget child;
  4. final Duration duration; // 光效滑动的时间
  5. final Duration delay; // 每轮动画结束后的延迟
  6. final Alignment begin; // 滑动起点
  7. final Alignment end; // 滑动终点
  8. final ImageProvider image; // 外部传入的光效图片
  9. final double shimmerWidthFactor; // 光效图片相对于 child 的宽度
  10. const ShimmerEffect({
  11. super.key,
  12. required this.child,
  13. required this.image,
  14. this.duration = const Duration(seconds: 2),
  15. this.delay = const Duration(seconds: 1),
  16. this.begin = const Alignment(-1, 0),
  17. this.end = const Alignment(1, 0),
  18. this.shimmerWidthFactor = 0.2,
  19. });
  20. @override
  21. State<ShimmerEffect> createState() => _ShimmerEffectState();
  22. }
  23. class _ShimmerEffectState extends State<ShimmerEffect>
  24. with SingleTickerProviderStateMixin {
  25. late final AnimationController _controller;
  26. late final Animation<double> _animation;
  27. @override
  28. void initState() {
  29. super.initState();
  30. _controller = AnimationController(
  31. vsync: this,
  32. duration: widget.duration,
  33. );
  34. _animation = Tween<double>(begin: -1.0, end: 1.0).animate(_controller);
  35. _startLoop();
  36. }
  37. void _startLoop() async {
  38. while (mounted) {
  39. await _controller.forward();
  40. _controller.reset();
  41. await Future.delayed(widget.delay);
  42. }
  43. }
  44. @override
  45. void dispose() {
  46. _controller.dispose();
  47. super.dispose();
  48. }
  49. @override
  50. Widget build(BuildContext context) {
  51. return LayoutBuilder(builder: (context, constraints) {
  52. final width = constraints.maxWidth;
  53. final shimmerWidth = width * widget.shimmerWidthFactor;
  54. return Stack(
  55. children: [
  56. widget.child,
  57. AnimatedBuilder(
  58. animation: _controller,
  59. builder: (context, _) {
  60. final dx = _animation.value * (width + shimmerWidth);
  61. return Positioned(
  62. left: dx,
  63. top: 0,
  64. width: shimmerWidth,
  65. height: constraints.maxHeight,
  66. child: IgnorePointer(
  67. child: Image(
  68. image: widget.image,
  69. fit: BoxFit.fill,
  70. ),
  71. ),
  72. );
  73. },
  74. )
  75. ],
  76. );
  77. });
  78. }
  79. }