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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 23:32:07