如何在Flutter中实现可切换周且支持自定义的周历Widget
自定义Flutter周历Widget实现方案
你需要的是一款支持前后周切换、可自定义单元格样式的固定周块布局日历,现有DatePicker组件无法满足需求,以下是完全自定义的实现方案:
核心实现思路
- 维护当前展示周的起始日期(默认周一为周起始,可按需调整为周日)
- 根据起始日期生成一周7天的日期列表
- 自定义日期单元格,支持背景色、圆角、选中状态等样式配置
- 添加周切换按钮,通过修改起始日期刷新视图
完整代码示例
import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; class CustomWeekCalendar extends StatefulWidget { final DateTime? initialDate; final Color cellBgColor; final Color selectedCellBgColor; final double cellBorderRadius; final ValueChanged<DateTime>? onDateSelected; const CustomWeekCalendar({ super.key, this.initialDate, this.cellBgColor = Colors.grey[100]!, this.selectedCellBgColor = Colors.blue, this.cellBorderRadius = 8, this.onDateSelected, }); @override State<CustomWeekCalendar> createState() => _CustomWeekCalendarState(); } class _CustomWeekCalendarState extends State<CustomWeekCalendar> { late DateTime currentWeekStart; DateTime? selectedDate; @override void initState() { super.initState(); // 初始化当前周起始日期(周一为起始) final initDate = widget.initialDate ?? DateTime.now(); currentWeekStart = _getWeekStart(initDate); selectedDate = initDate; } // 获取指定日期所在周的起始日期(周一) DateTime _getWeekStart(DateTime date) { final weekday = date.weekday; return date.subtract(Duration(days: weekday - 1)); } // 生成当前周的7天日期列表 List<DateTime> _getCurrentWeekDates() { return List.generate(7, (index) => currentWeekStart.add(Duration(days: index))); } // 切换到上一周 void _previousWeek() { setState(() { currentWeekStart = currentWeekStart.subtract(const Duration(days: 7)); }); } // 切换到下一周 void _nextWeek() { setState(() { currentWeekStart = currentWeekStart.add(const Duration(days: 7)); }); } // 格式化日期显示(如:10/12) String _formatDate(DateTime date) { return DateFormat('MM/dd').format(date); } // 格式化星期显示(如:周一) String _formatWeekday(DateTime date) { return DateFormat('EEE', 'zh_CN').format(date); } @override Widget build(BuildContext context) { final weekDates = _getCurrentWeekDates(); return Column( children: [ // 周切换栏 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( icon: const Icon(Icons.chevron_left), onPressed: _previousWeek, ), Text( '${_formatDate(weekDates.first)} - ${_formatDate(weekDates.last)}', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), IconButton( icon: const Icon(Icons.chevron_right), onPressed: _nextWeek, ), ], ), const SizedBox(height: 12), // 日期单元格行 Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: weekDates.map((date) { final isSelected = isSameDate(selectedDate, date); return GestureDetector( onTap: () { setState(() { selectedDate = date; }); widget.onDateSelected?.call(date); }, child: Container( width: MediaQuery.of(context).size.width / 8, padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: isSelected ? widget.selectedCellBgColor : widget.cellBgColor, borderRadius: BorderRadius.circular(widget.cellBorderRadius), ), child: Column( children: [ Text( _formatWeekday(date), style: TextStyle( color: isSelected ? Colors.white : Colors.black87, fontSize: 12, ), ), const SizedBox(height: 8), Text( date.day.toString(), style: TextStyle( color: isSelected ? Colors.white : Colors.black, fontSize: 18, fontWeight: FontWeight.w600, ), ), ], ), ), ); }).toList(), ), ], ); } // 判断两个日期是否为同一天(忽略时分秒) bool isSameDate(DateTime? a, DateTime? b) { if (a == null || b == null) return false; return a.year == b.year && a.month == b.month && a.day == b.day; } } // 使用示例 class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('自定义周历')), body: Padding( padding: const EdgeInsets.all(16.0), child: CustomWeekCalendar( initialDate: DateTime.now(), cellBgColor: Colors.grey[200]!, selectedCellBgColor: Colors.green, cellBorderRadius: 12, onDateSelected: (date) { print('选中日期:${DateFormat('yyyy-MM-dd').format(date)}'); }, ), ), ); } }
关键功能说明
- 周切换:通过左右箭头按钮切换前后周,每次调整起始日期并刷新日期列表
- 自定义样式:支持传入单元格背景色、选中状态背景色、圆角半径,可根据需求修改装饰样式(如添加边框、阴影)
- 日期选中:点击单元格切换选中状态,同时支持回调外部处理选中事件
- 周起始调整:如需将周日设为周起始,修改
_getWeekStart方法中的weekday - 1为weekday % 7即可
内容的提问来源于stack exchange,提问作者Quang Hồ Vũ Minh
相关产品推荐
相关产品推荐

