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

Flutter:基于PageView实现无限滚动且选中项居中的选择器

解决方案

核心修改点

  1. 无限滚动实现:设置itemCount为无限大,通过索引取模映射到实际15个选项;在页面切换时无缝跳转,避免边界限制。
  2. 消除末尾空白:设置padEnds: false,统一每个选项的左右边距,结合无限滚动彻底解决首尾空白问题。
  3. 保持选中项居中:保留pageSnapping: true和合适的viewportFraction,确保滚动后选中项自动居中。

修改后的完整代码

class _MyAppState extends State<MyApp> {
  late PageController _pageController;
  late int _currentPage;
  // 初始设置在中间组,避免初始滚动到边界
  final int _initialOffset = 15 * 100;

  @override
  void initState() {
    _currentPage = 3;
    _pageController = PageController(
      initialPage: _initialOffset + 3,
      viewportFraction: 1 / 4,
    );
    super.initState();
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: FractionallySizedBox(
            heightFactor: 0.2,
            child: PageView.builder(
              itemCount: double.infinity,
              pageSnapping: true,
              padEnds: false,
              controller: _pageController,
              onPageChanged: (page) {
                setState(() {
                  _currentPage = page % 15;
                });
                // 无缝跳转,实现无限滚动
                if (page >= _initialOffset + 15 * 2) {
                  _pageController.jumpToPage(page - 15);
                } else if (page <= _initialOffset - 15) {
                  _pageController.jumpToPage(page + 15);
                }
              },
              itemBuilder: (_, index) {
                final realIndex = index % 15;
                final isSelected = realIndex == _currentPage;

                return InkWell(
                  onTap: () {
                    final currentPageInt = _pageController.page!.toInt();
                    int diff = realIndex - _currentPage;
                    // 选择最近的滚动方向,避免长距离滚动
                    if (diff.abs() > 7) {
                      diff = diff > 0 ? diff - 15 : diff + 15;
                    }
                    _pageController.animateToPage(
                      currentPageInt + diff,
                      curve: Curves.linear,
                      duration: const Duration(milliseconds: 320),
                    );
                  },
                  child: Container(
                    margin: const EdgeInsets.symmetric(horizontal: 10),
                    child: PhysicalModel(
                      elevation: isSelected ? 4 : 0,
                      color: isSelected ? Colors.blue : Colors.red,
                      borderRadius: BorderRadius.circular(12),
                      child: Center(child: Text(realIndex.toString())),
                    ),
                  ),
                );
              },
            ),
          ),
        ),
      ),
    );
  }
}

关键细节说明

  • 无限滚动逻辑:

    • itemCount: double.infinity让PageView可以无限滚动;realIndex = index %15将无限索引映射到15个实际选项。
    • 在onPageChanged中,当滚动距离超过1组(15个)时,通过jumpToPage无缝跳转到对应组的相同位置,视觉上没有断层。
  • 消除空白与居中:

    • padEnds: false关闭PageView首尾的默认填充,配合EdgeInsets.symmetric(horizontal:10)统一所有选项的左右边距,确保首尾选项滚动时不会出现空白。
    • viewportFraction:1/4设置每个Page的宽度为屏幕的1/4,结合pageSnapping: true,滚动后选中的选项会自动居中。
  • 点击优化:

    • 计算点击目标与当前选中项的差值,当差值绝对值大于7时(超过15个选项的一半),选择反向滚动(比如从最后一个选项到第一个,只滚动1步而非14步),提升滚动流畅度。

内容的提问来源于stack exchange,提问作者Trj-corp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 22:45:37