效果图:这个项目是动态效果

动画逻辑流程
Dart
// 核心逻辑链
AnimationController(驱动)
↓
TweenSequence(定义动画序列)
↓
CurvedAnimation(应用动画曲线)
↓
AnimatedBuilder(监听变化)
↓
Widget树(重新构建)
时间线分析
Dart
时间 (秒) | 0 | 1 | 2 | 3 | 4 |
圆1大小 | 0 | 100 | 200 | 300 | 0 |
圆2大小 | 100 | 200 | 300 | 0 | 100 |
圆3大小 | 200 | 300 | 0 | 100 | 200 |
圆4大小 | 300 | 0 | 100 | 200 | 300 |
任何时候都有3个圆可见,1个圆透明或大小为0
数据流向
Dart
// 初始化阶段
initState()
↓
AnimationController创建
↓
TweenSequence定义动画路径
↓
动画对象绑定
// 运行阶段
_controller.repeat()
↓
值随时间变化 (0.0 → 1.0)
↓
TweenSequence计算当前值
↓
AnimatedBuilder重建UI
总结
Dart
核心架构特点:
单一控制器:一个 AnimationController 控制所有动画
相位偏移:四个圆的时间相位各差25%
同步变化:大小和颜色同步变化
循环动画:使用 repeat() 实现无限循环
底层逻辑核心:
时间驱动:AnimationController 作为时间源
插值计算:TweenSequence 实现多段动画
响应式UI:AnimatedBuilder 监听变化
数据结构本质:
动画状态机:通过 Animation 对象管理状态
配置数据:硬编码在代码中的动画参数
渲染数据:实时计算的 size 和 color 值
关键点:
1.为什么有4个圆,但是视图中只有3个
2.重复代码过多,如何优化
代码实例
Dart
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<StatefulWidget> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin{
late AnimationController _controller; //动画控制器
late Animation<double> _sizeAnimation;
late Animation<Color?> _colorAnimation;
late Animation<double> _size1Animation;
late Animation<Color?> _color1Animation;
late Animation<double> _size2Animation;
late Animation<Color?> _color2Animation;
late Animation<double> _size3Animation;
late Animation<Color?> _color3Animation;
@override
void initState(){
super.initState();
//初始化动画控制器
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 4)
);
//TweenSequence - 动画序列 ;TweenSequenceItem - 序列项
_sizeAnimation = TweenSequence<double>([
TweenSequenceItem(tween:Tween<double>(begin: 0,end:100),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 100,end:200),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 200,end:300),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 300,end:0),
weight: 1.0
),
]).animate(
CurvedAnimation(parent: _controller, curve: Curves.linear),
);
_colorAnimation = TweenSequence<Color?>([
TweenSequenceItem(
tween: ColorTween(
begin: Color(0xFF248EFF),
end: Color(0xFF248EFF).withOpacity(0.7),
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin:Color(0xFF248EFF).withOpacity(0.7),
end: Color(0xFF248EFF).withOpacity(0.4),
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin:Color(0xFF248EFF).withOpacity(0.4),
end: Color(0xFF248EFF).withOpacity(0.1),
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin: Colors.transparent,
end: Colors.transparent,
),
weight: 1.0,
),
]).animate(CurvedAnimation(parent: _controller, curve: Curves.linear));
_size1Animation = TweenSequence<double>([
TweenSequenceItem(tween:Tween<double>(begin: 100,end:200),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 200,end:300),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 300,end:0),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 0,end:100),
weight: 1.0
),
]).animate(
CurvedAnimation(parent: _controller, curve: Curves.linear),
);
_color1Animation = TweenSequence<Color?>([
TweenSequenceItem(
tween: ColorTween(
begin: Color(0xFF248EFF).withOpacity(0.7),
end: Color(0xFF248EFF).withOpacity(0.4),
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin: Color(0xFF248EFF).withOpacity(0.4),
end: Color(0xFF248EFF).withOpacity(0.1),
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin: Colors.transparent,
end: Colors.transparent,
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin: Color(0xFF248EFF),
end: Color(0xFF248EFF).withOpacity(0.7),
),
weight: 1.0,
),
]).animate(CurvedAnimation(parent: _controller, curve: Curves.linear));
_size2Animation = TweenSequence<double>([
TweenSequenceItem(tween:Tween<double>(begin: 200,end:300),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 300,end:0),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 0,end:100),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 100,end:200),
weight: 1.0
),
]).animate(
CurvedAnimation(parent: _controller, curve: Curves.linear),
);
_color2Animation = TweenSequence<Color?>([
TweenSequenceItem(
tween: ColorTween(
begin: Color(0xFF248EFF).withOpacity(0.4),
end: Color(0xFF248EFF).withOpacity(0.1),
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin: Colors.transparent,
end: Colors.transparent,
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin:Color(0xFF248EFF),
end: Color(0xFF248EFF).withOpacity(0.7),
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin: Color(0xFF248EFF).withOpacity(0.7),
end: Color(0xFF248EFF).withOpacity(0.4),
),
weight: 1.0,
),
]).animate(CurvedAnimation(parent: _controller, curve: Curves.linear));
_size3Animation = TweenSequence<double>([
TweenSequenceItem(tween:Tween<double>(begin: 300,end:0),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 0,end:100),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 100,end:200),
weight: 1.0
),
TweenSequenceItem(tween:Tween<double>(begin: 200,end:300),
weight: 1.0
),
]).animate(
CurvedAnimation(parent: _controller, curve: Curves.linear),
);
_color3Animation = TweenSequence<Color?>([
TweenSequenceItem(
tween: ColorTween(
begin: Colors.transparent,
end: Colors.transparent,
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin: Color(0xFF248EFF),
end: Color(0xFF248EFF).withOpacity(0.7),
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin:Color(0xFF248EFF).withOpacity(0.7),
end: Color(0xFF248EFF).withOpacity(0.4),
),
weight: 1.0,
),
TweenSequenceItem(
tween: ColorTween(
begin:Color(0xFF248EFF).withOpacity(0.4),
end: Color(0xFF248EFF).withOpacity(0.1),
),
weight: 1.0,
),
]).animate(CurvedAnimation(parent: _controller, curve: Curves.linear));
_controller.repeat(); //动画重复
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Stack(
alignment: Alignment.center, //关键:居中对齐
children: [
// 第4个圆 - 最大(在最底层)
_buildAnimatedCircle(
size: _size3Animation.value,
color: _color3Animation.value,
),
// 第3个圆 - 中大等(在中间层)
_buildAnimatedCircle(
size: _size2Animation.value,
color: _color2Animation.value,
),
// 第2个圆 - 中小等(在中间层)
_buildAnimatedCircle(
size: _size1Animation.value,
color: _color1Animation.value,
),
// 第1个圆 - 最小(在最上层)
_buildAnimatedCircle(
size: _sizeAnimation.value,
color: _colorAnimation.value,
),
Icon(Icons.search, color: Colors.white, size: 90),
],
);
},
),
),
);
}
// ============================构建动画圆形的通用方法==========================
Widget _buildAnimatedCircle({
required double size,
required Color? color,
Widget? child,
}) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
child: child != null ? Center(child: child) : null,
);
}
}