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; const BirthdayDatePicker({ super.key, required this.initialDate, required this.minimumDate, required this.maximumDate, required this.onDateChanged, }); @override State createState() => _BirthdayDatePickerState(); } class _BirthdayDatePickerState extends State { late int selectedYear; late int selectedMonth; late int selectedDay; @override void initState() { super.initState(); selectedYear = widget.initialDate.year; selectedMonth = widget.initialDate.month; selectedDay = widget.initialDate.day; } @override Widget build(BuildContext context) { return Container( height: 150.h, child: Stack( children: [ Positioned( bottom: 57.h, // Position at the bottom left: 0, right: 0, top: 57.h, child: Container( height: 36.h, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.6), // Background color borderRadius: BorderRadius.circular(14.r), ), ), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildPickerBuilder( count: widget.maximumDate.year - widget.minimumDate.year + 1, initialIndex: selectedYear - widget.minimumDate.year, itemBuilder: (index) => '${widget.minimumDate.year + index}年', onSelectedItemChanged: (index) { setState(() { selectedYear = widget.minimumDate.year + index; _adjustMonthAndDay(); _notify(); }); }, ), _buildPickerBuilder( count: _getMaxMonth(), initialIndex: selectedMonth - 1, itemBuilder: (index) => '${index + 1}月', onSelectedItemChanged: (index) { setState(() { selectedMonth = index + 1; _adjustDayForMonth(); _notify(); }); }, ), _buildPickerBuilder( count: _getMaxDay(), initialIndex: selectedDay - 1, itemBuilder: (index) => '${index + 1}日', onSelectedItemChanged: (index) { setState(() { selectedDay = index + 1; _notify(); }); }, ), ], ), // Positioned Stack item (will be at the bottom of the Row) ], ), ); } Widget _buildPickerBuilder({ required int count, required int initialIndex, required String Function(int) itemBuilder, required Function(int) onSelectedItemChanged, }) { return Expanded( child: ListWheelScrollView.useDelegate( controller: FixedExtentScrollController(initialItem: initialIndex), itemExtent: 36.h, onSelectedItemChanged: onSelectedItemChanged, overAndUnderCenterOpacity: 0.35, childDelegate: ListWheelChildBuilderDelegate( builder: (context, index) { // 判断选中项 bool isSelected = index == initialIndex; return Container( alignment: Alignment.center, color: Colors.transparent, child: Text( itemBuilder(index), style: TextStyle( fontSize: 16.sp, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, color: isSelected ? Colors.black : Colors.black, ), ), ); }, 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); } }