在這篇文章中,我們將介紹如何使用 Flutter 實現(xiàn)一個帶有透明度漸變效果和過渡動畫的頭像疊加列表。通過這種效果,可以在圖片切換時實現(xiàn)平滑的動畫,使 UI 更加生動和吸引人。
需求
我們的目標是實現(xiàn)一個頭像疊加列表,在每隔 2 秒時切換頭像,并且在切換過程中,前一個頭像逐漸消失,新進入的頭像逐漸顯示,同時有一個從右向左的移動過渡效果。
具體需求包括:
- 支持頭像圓形顯示。
- 支持設置頭像重疊比例。
- 支持配置間隔時間切換一次頭像。
- 切換時,前一個頭像透明度漸變消失,后一個頭像透明度漸變顯示。
- 切換時,有平滑的移動動畫。
效果

converted_animation.gif
實現(xiàn)思路
為了實現(xiàn)這個效果,我們將使用 Flutter 的 AnimatedBuilder、AnimationController 和 Tween 來實現(xiàn)過渡動畫和透明度漸變效果。主要步驟包括:
- 創(chuàng)建一個
CircularImageList組件,用于顯示頭像列表。 - 使用
AnimationController控制動畫的執(zhí)行。 - 使用
AnimatedBuilder和Opacity實現(xiàn)透明度漸變效果。 - 使用
Positioned和AnimatedBuilder實現(xiàn)位置移動過渡效果。 - 每隔 2 秒觸發(fā)一次動畫,并更新顯示的頭像列表。
實現(xiàn)代碼
下面是實現(xiàn)上述需求的完整代碼:
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class CircularImageList extends StatefulWidget {
final List<String> imageUrls;
final int maxDisplayCount;
final double overlapRatio;
final double height;
final Duration animDuration;
final Duration delayedDuration;
const CircularImageList({
super.key,
required this.imageUrls,
required this.maxDisplayCount,
required this.overlapRatio,
required this.height,
this.animDuration = const Duration(milliseconds: 500),
this.delayedDuration = const Duration(seconds: 1),
});
@override
CircularImageListState createState() => CircularImageListState();
}
class CircularImageListState extends State<CircularImageList>
with SingleTickerProviderStateMixin {
int _currentIndex = 0;
List<String> _currentImages = [];
late AnimationController _animationController;
late Animation<double> _animation;
int get maxDisplayCount {
return widget.maxDisplayCount + 1;
}
double get circularImageWidth {
var realCount = maxDisplayCount - 1;
return realCount * widget.height -
widget.height * (1 - widget.overlapRatio) * (realCount - 1);
}
@override
void initState() {
super.initState();
_currentImages = widget.imageUrls.take(maxDisplayCount).toList();
_animationController = AnimationController(
duration: widget.animDuration,
vsync: this,
);
_animation = Tween<double>(begin: 0, end: 1).animate(_animationController)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
setState(() {
_currentIndex = (_currentIndex + 1) % widget.imageUrls.length;
_currentImages.removeAt(0);
_currentImages.add(widget.imageUrls[_currentIndex]);
});
_animationController.reset();
Future.delayed(widget.delayedDuration, () {
_animationController.forward();
});
}
});
Future.delayed(widget.delayedDuration, () {
_animationController.forward();
});
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
clipBehavior: Clip.none,
width: circularImageWidth,
height: widget.height,
child: Stack(
clipBehavior: Clip.none,
children: _buildImageStack(),
),
);
}
double _opacity(int index) {
if (index == 0) {
return 1 - _animation.value;
} else if (index == _currentImages.length - 1) {
return _animation.value;
} else {
return 1;
}
}
List<Widget> _buildImageStack() {
List<Widget> stackChildren = [];
for (int i = 0; i < _currentImages.length; i++) {
double leftOffset = i * (widget.height * widget.overlapRatio);
stackChildren.add(
AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Positioned(
left: leftOffset -
(_animation.value * widget.height * widget.overlapRatio),
child: Opacity(
opacity: _opacity(i),
child: child!,
),
);
},
child: ClipOval(
key: ValueKey<String>(_currentImages[i]),
child: CachedNetworkImage(
imageUrl: _currentImages[i],
width: widget.height,
height: widget.height,
fit: BoxFit.cover,
),
),
),
);
}
return stackChildren;
}
}
結束語
通過上述代碼,我們實現(xiàn)了一個帶有透明度漸變效果和過渡動畫的頭像疊加列表。在實際開發(fā)中,可以根據(jù)需求對動畫的時長、重疊比例等進行調整,以達到最佳效果。希望這篇文章對您有所幫助,如果有任何問題或建議,詳情見:github.com/yixiaolunhui/flutter_xy