在这里插入图片描述

案例概述

本案例展示如何使用 showDatePickershowDateRangePicker 实现日期选择功能,包括单日期选择和日期范围选择。

核心概念

1. showDatePicker

  • 显示日期选择对话框;
  • 返回选中的 DateTime
  • 支持初始日期、最小/最大日期等参数。

2. showDateRangePicker

  • 显示日期范围选择对话框;
  • 返回 DateTimeRange
  • 包含开始日期和结束日期。

3. DateTime 操作

  • 获取年月日:date.yeardate.monthdate.day
  • 计算日期差:DateTime.now().difference(date).inDays

代码详解

1. 单日期选择

Future<void> _selectDate(BuildContext context) async {
  final DateTime? picked = await showDatePicker(
    context: context,
    initialDate: _selectedDate ?? DateTime.now(),
    firstDate: DateTime(1900),
    lastDate: DateTime.now(),
  );
  if (picked != null) {
    setState(() {
      _selectedDate = picked;
    });
  }
}

2. 日期范围选择

Future<void> _selectDateRange(BuildContext context) async {
  final DateTimeRange? picked = await showDateRangePicker(
    context: context,
    firstDate: DateTime(2020),
    lastDate: DateTime.now(),
  );
  if (picked != null) {
    setState(() {
      _startDate = picked.start;
      _endDate = picked.end;
    });
  }
}

3. 日期格式化

String formatDate(DateTime date) {
  return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
}

高级话题:日期选择的高级应用

1. 自定义日期选择器

创建自定义的日期选择器以满足特殊需求:

class CustomDatePicker extends StatefulWidget {
  
  State<CustomDatePicker> createState() => _CustomDatePickerState();
}

2. 日期验证

validator: (value) {
  if (value == null) return '请选择日期';
  if (value.isBefore(DateTime.now())) return '不能选择过去的日期';
  return null;
}

3. 日期范围验证

if (_endDate.isBefore(_startDate)) {
  return '结束日期不能早于开始日期';
}

4. 日期快捷选择

提供常用的日期快捷选项:

Row(
  children: [
    ElevatedButton(
      onPressed: () => _selectDateRange(
        DateTime.now().subtract(Duration(days: 7)),
        DateTime.now(),
      ),
      child: Text('最近7天'),
    ),
    ElevatedButton(
      onPressed: () => _selectDateRange(
        DateTime.now().subtract(Duration(days: 30)),
        DateTime.now(),
      ),
      child: Text('最近30天'),
    ),
  ],
)

5. 日期显示格式化

final formatter = DateFormat('yyyy-MM-dd HH:mm:ss');
Text(formatter.format(_selectedDate))

6. 日期计算

// 计算年龄
int calculateAge(DateTime birthDate) {
  return DateTime.now().year - birthDate.year;
}

// 计算工作天数
int calculateWorkingDays(DateTime start, DateTime end) {
  int days = 0;
  for (var date = start; date.isBefore(end); date = date.add(Duration(days: 1))) {
    if (date.weekday < 6) days++;  // 周一到周五
  }
  return days;
}

7. 日期范围的可视化

Container(
  padding: EdgeInsets.all(16),
  decoration: BoxDecoration(
    color: Colors.blue.shade50,
    borderRadius: BorderRadius.circular(8),
  ),
  child: Column(
    children: [
      Text('开始日期:${formatDate(_startDate)}'),
      Text('结束日期:${formatDate(_endDate)}'),
      Text('天数:${_endDate.difference(_startDate).inDays}'),
    ],
  ),
)

8. 日期选择的国际化

showDatePicker(
  context: context,
  locale: Locale('zh', 'CN'),
  initialDate: DateTime.now(),
  firstDate: DateTime(2020),
  lastDate: DateTime.now(),
)

9. 日期选择的性能优化

缓存日期选择器的状态,避免重复创建:

late final _datePickerState = DatePickerState();


void dispose() {
  _datePickerState.dispose();
  super.dispose();
}

10. 日期选择与表单集成

TextFormField(
  readOnly: true,
  decoration: InputDecoration(
    suffixIcon: Icon(Icons.calendar_today),
  ),
  onTap: () => _selectDate(context),
  validator: (value) {
    if (_selectedDate == null) return '请选择日期';
    return null;
  },
)

通过这些高级技巧,你可以构建出功能完整的日期选择系统。

高级话题:日期选择的企业级应用

1. 禁用特定日期

showDatePicker(
  context: context,
  initialDate: DateTime.now(),
  firstDate: DateTime(2020),
  lastDate: DateTime.now(),
  selectableDayPredicate: (DateTime date) {
    // 禁用周末
    if (date.weekday == 6 || date.weekday == 7) return false;
    // 禁用特定日期
    if (disabledDates.contains(date)) return false;
    return true;
  },
)

2. 日期范围的业务逻辑

bool isValidDateRange(DateTime start, DateTime end) {
  if (end.isBefore(start)) return false;
  if (end.difference(start).inDays > 365) return false;
  return true;
}

3. 日期的本地化显示

final formatter = DateFormat('yyyy年MM月dd日', 'zh_CN');
Text(formatter.format(_selectedDate))

4. 日期的快捷选择

Row(
  children: [
    ElevatedButton(
      onPressed: () => _selectDateRange(
        DateTime.now().subtract(Duration(days: 7)),
        DateTime.now(),
      ),
      child: Text('最近7天'),
    ),
    ElevatedButton(
      onPressed: () => _selectDateRange(
        DateTime.now().subtract(Duration(days: 30)),
        DateTime.now(),
      ),
      child: Text('最近30天'),
    ),
  ],
)

5. 日期的重复选择

class RecurringDate {
  final DateTime startDate;
  final String frequency;  // 'daily', 'weekly', 'monthly'
  final int? endAfter;     // 重复次数
  final DateTime? endDate; // 结束日期

  List<DateTime> getOccurrences() {
    // 计算所有重复日期
  }
}

6. 日期的时区处理

final utcDate = _selectedDate.toUtc();
final localDate = utcDate.toLocal();

7. 日期的导出

String exportDateAsISO8601(DateTime date) {
  return date.toIso8601String();
}

DateTime importDateFromISO8601(String dateString) {
  return DateTime.parse(dateString);
}

8. 日期的比较

bool isSameDay(DateTime a, DateTime b) {
  return a.year == b.year && a.month == b.month && a.day == b.day;
}

bool isToday(DateTime date) {
  return isSameDay(date, DateTime.now());
}

9. 日期的缓存

Future<void> _saveDateSelection() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('selected_date', _selectedDate.toIso8601String());
}

Future<void> _loadDateSelection() async {
  final prefs = await SharedPreferences.getInstance();
  final dateString = prefs.getString('selected_date');
  if (dateString != null) {
    setState(() => _selectedDate = DateTime.parse(dateString));
  }
}

10. 日期的测试

test('日期范围验证', () {
  final start = DateTime(2024, 1, 1);
  final end = DateTime(2024, 1, 31);
  expect(isValidDateRange(start, end), true);
  expect(isValidDateRange(end, start), false);
});

通过这些企业级技巧,你可以构建出功能完整的日期管理系统。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐