如何在Flutter中实现刻度线式PageView商品数量选择组件
Flutter实现刻度线滚动数量选择器(PageView版)
针对你需要的这种带刻度线的滚动数量选择组件,完全可以用Flutter的PageView配合自定义布局来实现,下面是具体的实现思路和代码示例:
核心实现思路
- 用
PageView.builder构建数量选项,通过viewportFraction设置小于1的值,让多个数量项同时显示在视口中,实现左右可见相邻选项的效果。 - 利用
PageController监听滚动进度,动态计算每个数量项的缩放比例、透明度,让选中的中间项放大高亮。 - 刻度线部分通过Row包裹小容器实现,根据滚动进度更新刻度的高亮状态,也可以用CustomPaint做更精细的绘制。
完整代码示例
import 'package:flutter/material.dart'; class QuantityPicker extends StatefulWidget { final int minQuantity; final int maxQuantity; final ValueChanged<int>? onQuantityChanged; const QuantityPicker({ super.key, this.minQuantity = 1, this.maxQuantity = 10, this.onQuantityChanged, }); @override State<QuantityPicker> createState() => _QuantityPickerState(); } class _QuantityPickerState extends State<QuantityPicker> { late PageController _pageController; double _currentPage = 0.0; @override void initState() { super.initState(); _currentPage = widget.minQuantity - 1.toDouble(); _pageController = PageController( initialPage: widget.minQuantity - 1, viewportFraction: 0.35, // 控制每个item占视口的比例,按需调整 )..addListener(() { setState(() { _currentPage = _pageController.page!; }); }); } @override void dispose() { _pageController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ // 顶部刻度线 _buildIndicator(), const SizedBox(height: 16), // PageView数量选择 SizedBox( height: 80, child: PageView.builder( controller: _pageController, itemCount: widget.maxQuantity - widget.minQuantity + 1, onPageChanged: (index) { widget.onQuantityChanged?.call(index + widget.minQuantity); }, itemBuilder: (context, index) { final quantity = index + widget.minQuantity; // 计算当前item的缩放比例:选中项放大,其他项缩小 final scale = 1 - (_currentPage - index).abs() * 0.7; return Center( child: Text( quantity.toString(), style: TextStyle( fontSize: 40 * scale, fontWeight: _currentPage.round() == index ? FontWeight.bold : FontWeight.normal, color: _currentPage.round() == index ? Colors.black : Colors.grey.shade400, ), ), ); }, ), ), const SizedBox(height: 16), // 底部刻度线(和顶部一致) _buildIndicator(), ], ); } Widget _buildIndicator() { final totalItems = widget.maxQuantity - widget.minQuantity + 1; return Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(totalItems, (index) { // 计算当前刻度的高亮状态:当前选中页附近的刻度高亮 final isActive = (_currentPage - index).abs() < 0.5; return Container( width: 8, height: isActive ? 6 : 3, margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: isActive ? Colors.black : Colors.grey.shade300, borderRadius: BorderRadius.circular(3), ), ); }), ); } } // 使用示例 class DemoScreen extends StatelessWidget { const DemoScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("数量选择器")), body: const Center( child: QuantityPicker( minQuantity: 1, maxQuantity: 10, ), ), ); } }
自定义调整说明
- 修改
viewportFraction可以调整同时显示的数量项个数,数值越小显示的项越多。 - 调整
_buildIndicator里的刻度尺寸、间距,以及itemBuilder里的缩放比例、字体大小,就能匹配你的设计图样式。 - 如果需要更精致的刻度动画,可以把
_buildIndicator换成CustomPaint,根据_currentPage的精确值绘制刻度的渐变高亮效果。
内容的提问来源于stack exchange,提问作者muhammed salih T
相关产品推荐
相关产品推荐

