Flutter中如何实现滚轮选择器默认选中列表中间元素
滚轮选择器默认选中中间项实现方法
核心修改逻辑分三步:
- 初始化时优先计算列表中间索引作为默认选中位,若传入了自定义
startPosition则使用传入值覆盖默认值 - 直接在
FixedExtentScrollController初始化时传入计算好的初始索引,不要等组件渲染完成后再调用jumpTo,避免初始滚动闪动 - 配置
ListWheelScrollView的视口参数,保证选中吸附位置和视觉上的中间选中框完全对齐
修改后的可直接运行代码如下:
class WheelSpeedPicker extends StatefulWidget { final Function(dynamic) onValueChanged; final int? startPosition; final double itemSize; final double? listHeight; final int currentPosition; final double? listWidth; final FixedExtentScrollController? controller; final List<dynamic> items; const WheelSpeedPicker({ Key? key, required this.onValueChanged, required this.currentPosition, required this.itemSize, required this.items, this.startPosition, this.listHeight, this.controller, this.listWidth, }) : super(key: key); @override _WheelTimePickerState createState() => _WheelTimePickerState(); } class _WheelTimePickerState extends State<WheelSpeedPicker> { late FixedExtentScrollController fixedExtentScrollController; late int currentPosition; @override void initState() { super.initState(); // 计算默认中间索引,传入startPosition时优先使用自定义初始位置 final int defaultMiddleIndex = widget.startPosition ?? (widget.items.length / 2).floor(); currentPosition = widget.currentPosition >= 0 ? widget.currentPosition : defaultMiddleIndex; fixedExtentScrollController = widget.controller ?? FixedExtentScrollController(initialItem: currentPosition); } void _listener(int position) { setState(() { currentPosition = position; }); widget.onValueChanged(currentPosition); } @override Widget build(BuildContext context) { // 未传列表高度时默认展示5个item,保证中间项刚好处于视口中心 final double pickerHeight = widget.listHeight ?? widget.itemSize * 5; return SizedBox( width: widget.listWidth ?? double.infinity, height: pickerHeight, child: Stack( alignment: Alignment.center, children: [ // 中间选中态蒙层,高度必须和itemSize完全一致 Container( height: widget.itemSize, decoration: BoxDecoration( color: Colors.grey.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), ), ListWheelScrollView.useDelegate( controller: fixedExtentScrollController, itemExtent: widget.itemSize, // 以下三个参数为居中吸附核心配置,不要随意改动 diameterRatio: 1.5, perspective: 0.008, offAxisFraction: 0, onSelectedItemChanged: _listener, childDelegate: ListWheelChildBuilderDelegate( builder: (context, index) { return Center( child: Text( widget.items[index].toString(), style: TextStyle( fontSize: 16, fontWeight: index == currentPosition ? FontWeight.w600 : FontWeight.w400, color: index == currentPosition ? Colors.black : Colors.grey, ), ), ); }, childCount: widget.items.length, ), ), ], ), ); } @override void dispose() { // 外部未传入controller时自行释放资源,避免内存泄漏 if(widget.controller == null){ fixedExtentScrollController.dispose(); } super.dispose(); } }
注意事项:
- 如果是无限循环的滚轮选择器,不需要按数据源长度计算中间索引,直接给
initialItem传一个足够大的整数(比如10000),渲染item时对索引取模映射到真实数据即可 - 中间选中蒙层的高度必须和
itemSize完全相等,否则会出现视觉选中项和实际返回值错位的问题 - 不要使用
addPostFrameCallback在首帧渲染完成后再跳转选中位置,会出现明显的初始滚动跳闪动
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

