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

Flutter中如何从Datepicker选择的生日自动计算年龄?

修复Flutter生日选择后的年龄计算与显示问题

原代码存在的核心问题

  • calAge方法未使用用户实际选中的生日日期,而是定义了空变量birt,导致年龄计算逻辑从未触发
  • 界面缺少年龄显示的UI组件,计算结果无法呈现给用户
  • 从SharedPreferences读取保存的生日后,未同步触发年龄计算

修复方案

  1. 修正年龄计算逻辑:直接使用选中的selectedDate调用age_calculator库计算精确年龄
  2. 添加年龄展示UI:在界面中新增文本组件,实时显示计算后的年龄信息
  3. 同步历史数据的年龄计算:读取SharedPreferences中的生日数据后,解析为DateTime并触发年龄计算

完整修复代码

import 'package:age_calculator/age_calculator.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class BirthdayScreen extends StatefulWidget {
  const BirthdayScreen({Key? key}) : super(key: key);

  @override
  State<BirthdayScreen> createState() => _BirthdayScreenState();
}

class _BirthdayScreenState extends State<BirthdayScreen> {
  @override
  void initState() {
    super.initState();
    dropdownValueBirthday = birthday.first;
    checkValueBirthday();
  }

  DateTime? selectedDate;
  DateTime now = DateTime.now();
  
  void showDatePicker() {
    DateTime mindate = DateTime(now.year - 2, now.month, now.day - 29);
    DateTime maxdate = DateTime(now.year - 1, now.month, now.day);
    showCupertinoModalPopup(
        context: context,
        builder: (BuildContext builder) {
          return Container(
            height: MediaQuery.of(context).copyWith().size.height * 0.25,
            color: Colors.white,
            child: CupertinoDatePicker(
              mode: CupertinoDatePickerMode.date,
              initialDateTime: selectedDate ?? mindate,
              onDateTimeChanged: (value) {
                if (value != selectedDate) {
                  setState(() {
                    selectedDate = value;
                    dropdownValueBirthday =
                        '${selectedDate?.year}/${selectedDate?.month}/${selectedDate?.day}';
                    calAge();
                  });
                }
              },
              maximumDate: maxdate,
              minimumDate: mindate,
            ),
          );
        });
  }

  String? dropdownValueBirthday;
  List<String> birthday = [
    'Birthday',
  ];

  checkValueBirthday() {
    _getDataBirthday();
  }

  _saveDataBirthday(String dropdownValueBirthdayShared) async {
    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
    sharedPreferences.setString("dataBirthday", dropdownValueBirthdayShared);
  }

  _getDataBirthday() async {
    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
    dropdownValueBirthday =
        sharedPreferences.getString("dataBirthday") ?? birthday.first;
    
    // 解析保存的生日字符串,同步计算年龄
    if (dropdownValueBirthday != birthday.first) {
      try {
        List<String> dateParts = dropdownValueBirthday!.split('/');
        selectedDate = DateTime(
          int.parse(dateParts[0]),
          int.parse(dateParts[1]),
          int.parse(dateParts[2]),
        );
        calAge();
      } catch (e) {
        print('解析生日失败: $e');
      }
    }
    setState(() {});
  }

  String age = "请选择生日";
  DateDuration? duration;
  
  // 调用age_calculator计算精确年龄
  void calAge() {
    if (selectedDate != null) {
      setState(() {
        duration = AgeCalculator.age(selectedDate!);
        age = '${duration?.years}岁 ${duration?.months}个月 ${duration?.days}天';
      });
    } else {
      age = "请选择生日";
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        padding: const EdgeInsets.symmetric(horizontal: 15),
        child: Column(
          children: [
            const SizedBox(height: 100),
            Row(
              children: <Widget>[
                const Icon(
                  Icons.brightness_1,
                  color: Colors.black,
                  size: 10,
                ),
                const Padding(
                  padding: EdgeInsets.only(left: 15.0),
                  child: Text("birthday",
                      style: TextStyle(
                        fontSize: 16.0,
                      )),
                ),
                Padding(
                  padding: const EdgeInsets.only(left: 25.0),
                  child: SizedBox(
                    width: 110.0,
                    height: 25.0,
                    child: DecoratedBox(
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(12),
                        color: Colors.white,
                        border: Border.all(color: Colors.grey.shade300),
                      ),
                      child: Center(
                        child: Text(
                          selectedDate == null
                              ? (dropdownValueBirthday ?? birthday.first)
                              : '${selectedDate?.year}/${selectedDate?.month}/${selectedDate?.day}',
                          style: const TextStyle(
                              fontSize: 16, fontWeight: FontWeight.w500),
                        ),
                      ),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.only(left: 15.0),
                  child: SizedBox(
                    width: 88.0,
                    height: 25.0,
                    child: MaterialButton(
                      onPressed: showDatePicker,
                      shape: const StadiumBorder(),
                      color: Colors.blue,
                      child: const Text(
                        'select',
                        style: TextStyle(color: Colors.white, fontSize: 12),
                      ),
                    ),
                  ),
                ),
              ],
            ),
            // 新增年龄显示组件
            Padding(
              padding: const EdgeInsets.only(top: 30.0),
              child: Text(
                age,
                style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
              ),
            ),
            const Spacer(),
            Padding(
              padding: const EdgeInsets.only(bottom: 30.0),
              child: SizedBox(
                width: 160.0,
                height: 35.0,
                child: ElevatedButton(
                    style: ButtonStyle(
                      shape: MaterialStateProperty.all<RoundedRectangleBorder>(
                        RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(18.0),
                          side: const BorderSide(
                            color: Colors.blueAccent,
                          ),
                        ),
                      ),
                    ),
                    onPressed: () {
                      if (dropdownValueBirthday != birthday.first) {
                        _saveDataBirthday(dropdownValueBirthday!);
                      }
                    },
                    child: const Text('next')),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

修复细节说明

  • 优化calAge方法:直接使用选中的日期调用第三方库计算精确年龄,包含年、月、日的完整信息
  • 同步历史数据:读取保存的生日字符串后解析为DateTime,自动触发年龄计算,保证重启APP后年龄正常显示
  • 新增年龄UI:在生日选择区域下方添加醒目的年龄展示文本
  • 完善初始值:日期选择器优先使用已选中的日期,提升用户体验
  • 增加保存判断:避免默认的"Birthday"文本被存入SharedPreferences

内容的提问来源于stack exchange,提问作者Dasun Dola

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 02:05:23