Flutter Widget间滑动动画问题:分类切换动画需求及代码求助
Flutter分类切换动画优化方案
我正在开发一款需要实现屏幕动画效果的应用,目前完成了部分开发,但未达到预期效果。需求是:点击分类时,实现选中分类的颜色平滑过渡至目标分类,同时内容从左侧或右侧滑入显示。当前实现代码如下:
Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: VisitCardCategory.values .map((e) => AnimatedBuilder( animation: _animationController, builder: (context, child) { return SlideTransition( position: Tween<Offset>( begin: Offset( _animationController.value, 0.0), end: const Offset(0.0, 0.0), ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeIn)), child: FilterWidget( onTap: () { filterByCategory(category: e); }, category: e, selectedColor: e == selectedCategory ? e.color : Colors.white, controller: _animationController, ), ); })) .toList()),
一、颜色平滑过渡实现
当前代码中颜色直接通过三元表达式切换,无过渡效果。可在FilterWidget内部用AnimatedContainer实现颜色渐变:
class FilterWidget extends StatelessWidget { final VisitCardCategory category; final bool isSelected; final VoidCallback onTap; final Color selectedColor; const FilterWidget({ super.key, required this.category, required this.isSelected, required this.onTap, required this.selectedColor, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, decoration: BoxDecoration( color: isSelected ? selectedColor : Colors.white, borderRadius: BorderRadius.circular(8), ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Text( category.name, style: TextStyle( color: isSelected ? Colors.white : Colors.black, ), ), ), ); } }
二、内容方向滑入动画优化
当前所有分类的滑动起始偏移量绑定同一个动画控制器值,无法实现左右方向区分。需新增索引判断逻辑,动态设置滑入方向:
// State类中新增变量记录上一个选中分类的索引 int _previousCategoryIndex = 0; Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: VisitCardCategory.values.asMap().entries.map((entry) { final index = entry.key; final e = entry.value; return AnimatedBuilder( animation: _animationController, builder: (context, child) { // 根据索引差判断滑入方向:当前索引大于上一个 → 从右侧滑入,反之从左侧 final beginOffset = index > _previousCategoryIndex ? const Offset(1.0, 0.0) : const Offset(-1.0, 0.0); return SlideTransition( position: Tween<Offset>( begin: beginOffset, end: const Offset(0.0, 0.0), ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, )), child: FilterWidget( onTap: () { // 更新上一个分类索引 _previousCategoryIndex = VisitCardCategory.values.indexOf(selectedCategory); filterByCategory(category: e); // 重置并播放动画 _animationController.reset(); _animationController.forward(); }, category: e, isSelected: e == selectedCategory, selectedColor: e.color, ), ); }, ); }).toList(), )
三、动画控制器初始化
在State类中正确初始化和销毁控制器:
late AnimationController _animationController; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); // 初始化上一个分类索引为当前选中项 _previousCategoryIndex = VisitCardCategory.values.indexOf(selectedCategory); } @override void dispose() { _animationController.dispose(); super.dispose(); }
关键优化总结
- 用
AnimatedContainer替代直接颜色赋值,实现平滑过渡 - 通过索引对比动态设置滑入方向,符合原型交互逻辑
- 每次点击时重置动画控制器,保证切换时动画完整触发
内容的提问来源于stack exchange,提问作者Abhinav
相关产品推荐
相关产品推荐

