为ListWheelScrollView的进入与退出子项添加平滑淡入淡出过渡效果
ListWheelScrollView的进入与退出子项添加平滑淡入淡出过渡效果
我懂你现在的困扰——用ListWheelScrollView的时候,子项滚出容器范围就突然消失,完全没过渡,用户体验确实拉胯。之前ChatGPT给的代码思路是对的,但计算过渡动画的逻辑没踩对点子,所以还是没解决问题。我给你调整一下代码,实现真正平滑的进入/退出过渡效果,包括淡入淡出和缩放动画。
修正后的完整可运行代码
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'List', theme: ThemeData(primarySwatch: Colors.blue), debugShowCheckedModeBanner: false, home: const ListWheelWithSmoothTransition(), ); } } class ListWheelWithSmoothTransition extends StatefulWidget { const ListWheelWithSmoothTransition({Key? key}) : super(key: key); @override _ListWheelWithSmoothTransitionState createState() => _ListWheelWithSmoothTransitionState(); } class _ListWheelWithSmoothTransitionState extends State<ListWheelWithSmoothTransition> { final FixedExtentScrollController _scrollController = FixedExtentScrollController(); final double _itemExtent = 100.0; final int _itemCount = 5; // 可视区域内最多显示的子项数量(根据容器高度计算) final int _visibleItemCount = 5; @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: SizedBox( height: _itemExtent * _visibleItemCount, child: ListWheelScrollView( controller: _scrollController, itemExtent: _itemExtent, physics: const FixedExtentScrollPhysics(), onSelectedItemChanged: (value) {}, children: List.generate(_itemCount, (index) { return AnimatedBuilder( animation: _scrollController, builder: (context, child) { // 计算当前子项相对于可视区域的偏移量 final double itemOffset = _scrollController.offset - (index * _itemExtent); // 计算子项在可视区域内的位置比例(-1到1之间,0表示在中心) final double positionRatio = (itemOffset / _itemExtent).clamp(-1.0, 1.0); // 根据位置比例计算透明度和缩放值 final double opacity = 1.0 - positionRatio.abs(); final double scale = 1.0 - (positionRatio.abs() * 0.3); // 最多缩小30% return Opacity( opacity: opacity.clamp(0.0, 1.0), child: Transform.scale( scale: scale.clamp(0.7, 1.0), child: child, ), ); }, child: Container( color: Colors.green, height: 50, width: 50, child: Center(child: Text('Item $index', style: const TextStyle(color: Colors.white))), ), ); }), ), ), ), ); } }
关键逻辑说明
- 滚动控制器的作用:用
FixedExtentScrollController实时获取滚动偏移量,这是实现过渡动画的核心依据 - 子项位置计算:对每个子项单独计算它相对于可视区域的偏移量,转换成
positionRatio(范围-1到1):- 当子项处于可视区域中心时,
positionRatio为0,此时透明度为1、缩放比例为1 - 当子项逐渐滚出可视区域时,
positionRatio趋近于±1,透明度逐渐降到0,缩放比例逐渐缩小到0.7
- 当子项处于可视区域中心时,
- 动画触发:通过
AnimatedBuilder监听滚动控制器的动画状态,实时更新每个子项的透明度和缩放值,滚动时就能自然触发平滑过渡
为什么之前的代码无效?
之前ChatGPT的代码用scrollOffset % itemHeight计算过渡值,这个逻辑没有关联到子项的索引,导致所有子项的过渡计算是统一的,无法反映每个子项的实际进入/退出状态,所以达不到平滑过渡的效果。
备注:内容来源于stack exchange,提问作者Ramji
相关产品推荐
相关产品推荐

