先上个图:
//随机颜色
Color get _randomColor => Color.fromRGBO(
Random().nextInt(256),
Random().nextInt(256),
Random().nextInt(256),
1,
);
class AnimatedNumberPage extends StatefulWidget {
const AnimatedNumberPage({
Key? key,
}) : super(key: key);
@override
_AnimatedNumberPageState createState() => _AnimatedNumberPageState();
}
class _AnimatedNumberPageState extends State<AnimatedNumberPage> {
Timer? _timer;
late final ValueNotifier<int> _notifier = ValueNotifier(0);
void _startTimer() {
if (_timer != null && _timer!.isActive) {
_stopTimer();
}
_timer = Timer.periodic(const Duration(milliseconds: 1000), (timer) {
_notifier.value++;
});
}
void _stopTimer() {
if (_timer != null) {
_timer!.cancel();
}
}
@override
void dispose() {
_stopTimer();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.5,
title: const Text(
'AnimatedSwitcher 动画',
style: TextStyle(
color: Colors.white,
),
),
),
body: Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: 180,
alignment: Alignment.center,
child: _buildAnimatedSwitcher(context),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
TextButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.blueAccent)),
child: const Text(
'开始',
style: TextStyle(
color: Colors.white,
),
),
onPressed: () => _startTimer(),
),
TextButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.blueAccent)),
child: const Text(
'停止',
style: TextStyle(
color: Colors.white,
),
),
onPressed: () => _stopTimer(),
),
],
),
],
),
);
}
}
Widget _buildAnimatedSwitcher(BuildContext context) {
return ValueListenableBuilder<int>(
valueListenable: _notifier,
builder: (BuildContext context, int value, Widget? child) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (child, animation) {
// 构建切换使用组合动画
return _buildMultAnimation(animation, child);
},
child: Text(
'$value',
key: ValueKey<int>(value), //⚠️重要: 必须设置。显示指定key,不同的key会被认为是不同的Text
style: TextStyle(
color: _randomColor,
fontSize: 30,
fontWeight: FontWeight.w400,
),
),
);
},
);
}
Widget _buildMultAnimation(
Animation<double> animation,
Widget child,
) {
Offset beginOfsset = const Offset(0, -1.2);
Offset endOffset = Offset.zero;
if (animation.status == AnimationStatus.completed) {
// Text移出的平移动画
beginOfsset = const Offset(0.0, 1.2);
endOffset = Offset.zero;
}
return SlideTransition(
// 平移动画
position: Tween<Offset>(begin: beginOfsset, end: endOffset).animate(
CurvedAnimation(
parent: animation,
curve: Curves.linear,
),
),
child: FadeTransition(
// 渐变动画
opacity: Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: animation,
curve: Curves.linear,
),
),
child: ScaleTransition(
// 缩放动画
scale: Tween(begin: 0.6, end: 1.0).animate(
CurvedAnimation(
parent: animation,
curve: Curves.linear,
),
),
child: child,
),
),
);
}