You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 10:21:39