Flutter:基于PageView实现无限滚动且选中项居中的选择器
解决方案
核心修改点
- 无限滚动实现:设置
itemCount为无限大,通过索引取模映射到实际15个选项;在页面切换时无缝跳转,避免边界限制。 - 消除末尾空白:设置
padEnds: false,统一每个选项的左右边距,结合无限滚动彻底解决首尾空白问题。 - 保持选中项居中:保留
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
相关产品推荐
相关产品推荐

