Flutter中如何实现Cupertino风格年份选择器及自定义配置
自定义Cupertino风格日期选择组件方案
一、仅年份选择器实现
原生CupertinoDatePicker不支持单独年份模式,直接用CupertinoPicker自定义,同时限定1980到当前年份减26的范围:
void _showYearPicker() { final int currentYear = DateTime.now().year; final int endYear = currentYear - 26; // 生成1980到endYear的年份列表 final List<int> years = List.generate(endYear - 1980 + 1, (index) => 1980 + index); // 默认选中当前日期的年份,若不在范围内则选最后一个 int selectedIndex = years.indexWhere((year) => year == datetime.year); if (selectedIndex == -1) selectedIndex = years.length - 1; showCupertinoModalPopup( context: context, builder: (context) => Container( height: 250, color: Colors.white, child: Column( children: [ // 顶部确认栏 Row( mainAxisAlignment: MainAxisAlignment.end, children: [ CupertinoButton( child: const Text('确定'), onPressed: () { setState(() { datetime = DateTime(years[selectedIndex], datetime.month, datetime.day); }); Navigator.pop(context); }, ), ], ), Expanded( child: CupertinoPicker( itemExtent: 32, onSelectedItemChanged: (index) { setState(() { selectedIndex = index; }); }, children: years.map((year) => Center(child: Text(year.toString()))).toList(), ), ), ], ), ), ); }
二、其他独立选择器实现
1. 仅月份选择器
生成1-12月的列表,用CupertinoPicker实现:
void _showMonthPicker() { final List<String> months = List.generate(12, (index) => '${index + 1}月'); int selectedIndex = datetime.month - 1; showCupertinoModalPopup( context: context, builder: (context) => Container( height: 250, color: Colors.white, child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ CupertinoButton( child: const Text('确定'), onPressed: () { setState(() { datetime = DateTime(datetime.year, selectedIndex + 1, datetime.day); }); Navigator.pop(context); }, ), ], ), Expanded( child: CupertinoPicker( itemExtent: 32, onSelectedItemChanged: (index) { setState(() { selectedIndex = index; }); }, children: months.map((month) => Center(child: Text(month))).toList(), ), ), ], ), ), ); }
2. 仅日期选择器
基于原生CupertinoDatePicker的date模式,限定当前年月的日期范围:
void _showDayPicker() { showCupertinoModalPopup( context: context, builder: (context) => SizedBox( height: 250, child: CupertinoDatePicker( backgroundColor: Colors.white, initialDateTime: datetime, onDateTimeChanged: (newTime) { setState(() { datetime = DateTime(datetime.year, datetime.month, newTime.day); }); }, mode: CupertinoDatePickerMode.date, minimumDate: DateTime(datetime.year, datetime.month, 1), maximumDate: DateTime(datetime.year, datetime.month + 1, 0), ), ), ); }
3. 仅时间选择器
直接使用原生CupertinoDatePicker的time模式:
void _showTimePicker() { showCupertinoModalPopup( context: context, builder: (context) => SizedBox( height: 250, child: CupertinoDatePicker( backgroundColor: Colors.white, initialDateTime: datetime, onDateTimeChanged: (newTime) { setState(() { datetime = DateTime(datetime.year, datetime.month, datetime.day, newTime.hour, newTime.minute); }); }, use24hFormat: true, mode: CupertinoDatePickerMode.time, ), ), ); }
4. 周选择器
依赖intl包计算周数(需先在pubspec.yaml添加intl: ^0.19.0依赖):
import 'package:intl/intl.dart'; void _showWeekPicker() { final currentYear = datetime.year; final firstDay = DateTime(currentYear, 1, 1); // 获取当年总周数 final int totalWeeks = int.parse(DateFormat('w').format(DateTime(currentYear, 12, 31))); final List<int> weeks = List.generate(totalWeeks, (index) => index + 1); int selectedWeek = int.parse(DateFormat('w').format(datetime)); showCupertinoModalPopup( context: context, builder: (context) => Container( height: 250, color: Colors.white, child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ CupertinoButton( child: const Text('确定'), onPressed: () { // 计算选中周的周一日期(可根据需求调整为周日或其他) DateTime weekStart = firstDay.add( Duration(days: (selectedWeek - 1) * 7 - firstDay.weekday + 1), ); setState(() { datetime = weekStart; }); Navigator.pop(context); }, ), ], ), Expanded( child: CupertinoPicker( itemExtent: 32, onSelectedItemChanged: (index) { setState(() { selectedWeek = index + 1; }); }, children: weeks.map((week) => Center(child: Text('第$week周'))).toList(), ), ), ], ), ), ); }
三、整合到原组件
将上述方法添加到_DatePickerState类中,替换原按钮为多按钮触发不同选择器:
// 替换原Center内的CupertinoButton Row( mainAxisAlignment: MainAxisAlignment.center, children: [ CupertinoButton.filled(child: const Text('选年份'), onPressed: _showYearPicker), const SizedBox(width: 10), CupertinoButton.filled(child: const Text('选月份'), onPressed: _showMonthPicker), const SizedBox(width: 10), CupertinoButton.filled(child: const Text('选日期'), onPressed: _showDayPicker), const SizedBox(width: 10), CupertinoButton.filled(child: const Text('选时间'), onPressed: _showTimePicker), const SizedBox(width: 10), CupertinoButton.filled(child: const Text('选周'), onPressed: _showWeekPicker), ], )
关键技巧总结
- 原生
CupertinoDatePicker无单独年份/月份模式,用CupertinoPicker自定义是最优解 - 限定年份范围时,直接生成固定区间的年份列表,彻底避免无限滚动
- 自定义选择器需先构建对应数据列表,再通过
onSelectedItemChanged更新选中状态 - 周选择器依赖日期工具类(如
intl)简化周数计算,也可自行实现日期转周数逻辑 - 所有弹窗统一使用
showCupertinoModalPopup,保持iOS风格一致性
内容的提问来源于stack exchange,提问作者Loolii
相关产品推荐
相关产品推荐

