Flutter中多Widget同步滑动动画实现问题
实现Flutter文本轮播滑动动画效果
要实现你描述的依次滑动切换文本的动画,核心需要处理**文本入场(从右到中心)、停留、旧文本退场+新文本入场(同时进行)**这几个阶段的逻辑。你之前用AnimatedSwitcher的思路是对的,但嵌套SlideTransition和错误的Tween配置导致效果不符合预期,下面给出两种可行的实现方案:
方案一:使用AnimatedSwitcher(简洁易维护)
AnimatedSwitcher可以自动处理新旧Widget的切换动画,通过自定义transitionBuilder分别控制入场和退场动画,配合定时器实现循环切换:
import 'dart:async'; import 'package:flutter/material.dart'; class TextSlideCarousel extends StatefulWidget { const TextSlideCarousel({super.key}); @override State<TextSlideCarousel> createState() => _TextSlideCarouselState(); } class _TextSlideCarouselState extends State<TextSlideCarousel> { final List<String> _texts = ['one', 'two', 'three']; int _currentIndex = 0; Timer? _cycleTimer; @override void initState() { super.initState(); // 初始文本停留1秒后开始循环切换 Timer(const Duration(seconds: 1), _startCycle); } void _startCycle() { // 每2秒切换一次(1秒动画时长 + 1秒停留时长) _cycleTimer = Timer.periodic(const Duration(seconds: 2), (timer) { setState(() { _currentIndex = (_currentIndex + 1) % _texts.length; }); }); } @override void dispose() { _cycleTimer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Center( child: AnimatedSwitcher( duration: const Duration(milliseconds: 1000), switchInCurve: Curves.easeInOut, switchOutCurve: Curves.easeInOut, // 堆叠新旧文本,保证动画同时显示 layoutBuilder: (currentChild, previousChildren) { return Stack( alignment: Alignment.center, children: [ ...previousChildren, if (currentChild != null) currentChild, ], ); }, transitionBuilder: (Widget child, Animation<double> animation) { // 判断是新文本(入场)还是旧文本(退场),分别设置动画 if (child.key == ValueKey(_texts[_currentIndex])) { // 新文本:从右侧滑到中心 return SlideTransition( position: Tween<Offset>( begin: const Offset(1, 0), end: Offset.zero, ).animate(animation), child: child, ); } else { // 旧文本:从中心滑到左侧 return SlideTransition( position: Tween<Offset>( begin: Offset.zero, end: const Offset(-1, 0), ).animate(animation), child: child, ); } }, child: Text( _texts[_currentIndex], key: ValueKey(_texts[_currentIndex]), // 唯一key触发切换 style: const TextStyle(fontSize: 30), ), ), ); } }
方案二:使用Stack+AnimationController(更灵活控制细节)
如果需要更精细的动画阶段控制,可直接用Stack叠加文本,通过AnimationController分别控制每个文本的位置:
import 'dart:async'; import 'package:flutter/material.dart'; class TextSlideCarousel extends StatefulWidget { const TextSlideCarousel({super.key}); @override State<TextSlideCarousel> createState() => _TextSlideCarouselState(); } class _TextSlideCarouselState extends State<TextSlideCarousel> with SingleTickerProviderStateMixin { final List<String> _texts = ['one', 'two', 'three']; int _currentIndex = 0; late AnimationController _slideController; late Animation<Offset> _currentTextOffset; late Animation<Offset> _nextTextOffset; bool _isTransitioning = false; @override void initState() { super.initState(); _slideController = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); // 初始动画:第一个文本从右侧滑到中心 _currentTextOffset = Tween<Offset>( begin: const Offset(1, 0), end: Offset.zero, ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeInOut)); _slideController.forward().then((_) { // 停留1秒后启动循环 Timer(const Duration(seconds: 1), _startTransition); }); } Future<void> _startTransition() async { setState(() => _isTransitioning = true); // 更新动画:当前文本滑向左侧,下一个文本从右侧滑入中心 _currentTextOffset = Tween<Offset>( begin: Offset.zero, end: const Offset(-1, 0), ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeInOut)); _nextTextOffset = Tween<Offset>( begin: const Offset(1, 0), end: Offset.zero, ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeInOut)); await _slideController.forward(from: 0); // 更新当前索引,重置动画状态 setState(() { _currentIndex = (_currentIndex + 1) % _texts.length; _isTransitioning = false; // 重置当前文本的位置到中心,准备下一次循环 _currentTextOffset = Tween<Offset>( begin: Offset.zero, end: Offset.zero, ).animate(_slideController); }); // 停留1秒后继续循环 Timer(const Duration(seconds: 1), _startTransition); } @override void dispose() { _slideController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Center( child: Stack( alignment: Alignment.center, children: [ // 当前显示的文本 SlideTransition( position: _currentTextOffset, child: Text( _texts[_currentIndex], style: const TextStyle(fontSize: 30), ), ), // 过渡时显示的下一个文本 if (_isTransitioning) SlideTransition( position: _nextTextOffset, child: Text( _texts[(_currentIndex + 1) % _texts.length], style: const TextStyle(fontSize: 30), ), ), ], ), ); } }
你之前代码的问题分析
- 嵌套
SlideTransition导致动画冲突:AnimatedSwitcher的transitionBuilder已经负责处理切换动画,无需再给子Widget套额外的SlideTransition。 - Tween配置错误:你设置的
begin: Offset(3.0, 0.0)和end: Offset(1, 0)不是从右侧到中心的正确偏移(Offset(1,0)代表完全在右侧,Offset.zero才是中心)。 - 退场动画未正确定义:没有处理旧文本从中心滑向左侧的逻辑。
内容的提问来源于stack exchange,提问作者Aryan Sethi
相关产品推荐
相关产品推荐

