import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class BirthdayDatePicker extends StatefulWidget { final DateTime initialDate; final DateTime minimumDate; final DateTime maximumDate; final void Function(DateTime) onDateChanged; final Color backgroundColor; // 每一项的间距 final double itemExtent; final double height; const BirthdayDatePicker({ super.key, required this.initialDate, required this.minimumDate, required this.maximumDate, required this.onDateChanged, this.backgroundColor = const Color.fromRGBO(255, 255, 255, 0.6), this.itemExtent = 36, this.height = 150, }); @override State createState() => _BirthdayDatePickerState(); } class _BirthdayDatePickerState extends State { late int selectedYear; late int selectedMonth; late int selectedDay; // 吸附效果控制器 final List _controllers = [ FixedExtentScrollController(), FixedExtentScrollController(), FixedExtentScrollController(), ]; @override void initState() { super.initState(); selectedYear = widget.initialDate.year; selectedMonth = widget.initialDate.month; selectedDay = widget.initialDate.day; // 初始化控制器位置 WidgetsBinding.instance.addPostFrameCallback((_) { _controllers[0].jumpToItem(selectedYear - widget.minimumDate.year); _controllers[1].jumpToItem(selectedMonth - 1); _controllers[2].jumpToItem(selectedDay - 1); }); } @override void dispose() { _controllers.forEach((controller) => controller.dispose()); super.dispose(); } @override Widget build(BuildContext context) { return Container( height: widget.height, child: Stack( children: [ Positioned( bottom: 57.h, left: 0, right: 0, top: 57.h, child: Container( height: 36.h, decoration: BoxDecoration( color: widget.backgroundColor, borderRadius: BorderRadius.circular(14.r), ), ), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildYearPicker(), _buildMonthPicker(), _buildDayPicker(), ], ), ], ), ); } Widget _buildYearPicker() { final yearCount = widget.maximumDate.year - widget.minimumDate.year + 1; return _buildPickerBuilder( controller: _controllers[0], count: yearCount, initialIndex: selectedYear - widget.minimumDate.year, itemBuilder: (index) => '${widget.minimumDate.year + index}年', onSelectedItemChanged: (index) { setState(() { selectedYear = widget.minimumDate.year + index; _adjustMonthAndDay(); _notify(); }); }, ); } Widget _buildMonthPicker() { return _buildPickerBuilder( controller: _controllers[1], count: _getMaxMonth(), initialIndex: selectedMonth - 1, itemBuilder: (index) => '${index + 1}月', onSelectedItemChanged: (index) { setState(() { selectedMonth = index + 1; _adjustDayForMonth(); _notify(); }); }, ); } Widget _buildDayPicker() { return _buildPickerBuilder( controller: _controllers[2], count: _getMaxDay(), initialIndex: selectedDay - 1, itemBuilder: (index) => '${index + 1}日', onSelectedItemChanged: (index) { setState(() { selectedDay = index + 1; _notify(); }); }, ); } Widget _buildPickerBuilder({ required FixedExtentScrollController controller, required int count, required int initialIndex, required String Function(int) itemBuilder, required Function(int) onSelectedItemChanged, }) { return Expanded( child: ListWheelScrollView.useDelegate( controller: controller, itemExtent: widget.itemExtent, onSelectedItemChanged: onSelectedItemChanged, physics: FixedExtentScrollPhysics(), overAndUnderCenterOpacity: 0.35, childDelegate: ListWheelChildBuilderDelegate( builder: (context, index) { final isSelected = controller.selectedItem == index; return Container( alignment: Alignment.center, child: Text( itemBuilder(index), style: TextStyle( fontSize: 16.sp, color: isSelected ? Colors.black : Colors.black, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ); }, childCount: count, ), ), ); } // 自动调整月和日 void _adjustMonthAndDay() { if (selectedYear == widget.maximumDate.year && selectedMonth > widget.maximumDate.month) { selectedMonth = widget.maximumDate.month; } _adjustDayForMonth(); } void _adjustDayForMonth() { int maxDay = _getMaxDay(); if (selectedDay > maxDay) { selectedDay = maxDay; } } int _getMaxMonth() { if (selectedYear == widget.maximumDate.year) { return widget.maximumDate.month; } return 12; } int _getMaxDay() { if (selectedYear == widget.maximumDate.year && selectedMonth == widget.maximumDate.month) { return widget.maximumDate.day; } return _getDaysInMonth(selectedYear, selectedMonth); } int _getDaysInMonth(int year, int month) { switch (month) { case 2: if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { return 29; } return 28; case 4: case 6: case 9: case 11: return 30; default: return 31; } } // 回调函数通知外部 void _notify() { final selectedDate = DateTime(selectedYear, selectedMonth, selectedDay); widget.onDateChanged(selectedDate); } }