| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- 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<BirthdayDatePicker> createState() => _BirthdayDatePickerState();
- }
- class _BirthdayDatePickerState extends State<BirthdayDatePicker> {
- late int selectedYear;
- late int selectedMonth;
- late int selectedDay;
- // 吸附效果控制器
- final List<FixedExtentScrollController> _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);
- }
- }
|