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

Flutter中SfCalendar资源行高度调整及固定顶部问题求助

解决方案

一、调整资源行高度以完整显示预约内容

SfCalendar的资源行高度无法通过resourceHeaderBuilder内部容器高度直接控制,需使用组件内置的resourceViewSettings配置项,搭配自定义预约布局实现文本完整展示:

  1. 在SfCalendar中添加资源视图高度配置:
return SfCalendar(
  showDatePickerButton: true,
  controller: _calendarController,
  allowDragAndDrop: true,
  allowedViews: _allowedViews,
  minDate: _minDate,
  timeRegionBuilder: _getSpecialRegionWidget,
  specialRegions: _specialTimeRegions,
  showNavigationArrow: true,
  dataSource: _calendarDataSource,
  onViewChanged: viewChangedCallback,
  resourceViewHeaderBuilder: _resourceHeaderBuilder,
  appointmentTimeTextFormat: 'HH:mm',
  // 添加资源视图高度设置
  resourceViewSettings: const ResourceViewSettings(
    resourceHeight: 220, // 根据实际需求调整高度值
  ),
  timeSlotViewSettings: const TimeSlotViewSettings(
    timeInterval: Duration(minutes: 15),
    timeFormat: 'HH:mm',
    startHour: 7,
    endHour: 18,
  ),
  // 自定义预约布局,实现文本自动换行
  appointmentBuilder: (context, details) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
      child: Text(
        details.appointments.first.subject,
        maxLines: null,
        overflow: TextOverflow.visible,
        style: const TextStyle(fontSize: 12),
      ),
    );
  },
);

二、将"N/A"资源行固定在顶部

通过拆分资源列表、组合固定区域与滚动日历的方式实现固定效果,同时保留拖拽交互能力:

  1. 拆分资源数据源:
// 从原始资源列表中分离N/A资源与其他资源
final Resource nonAssignedResource = _resources.firstWhere((r) => r.displayName == "N/A");
final List<Resource> scrollableResources = _resources.where((r) => r.displayName != "N/A").toList();
  1. 构建固定顶部+滚动日历的组合布局:
return Column(
  children: [
    // 顶部固定的N/A资源行
    _buildFixedNonAssignedHeader(nonAssignedResource),
    // 下方滚动的其他资源日历
    Expanded(
      child: SfCalendar(
        // 复用原有配置,仅替换资源列表
        showDatePickerButton: true,
        controller: _calendarController,
        allowDragAndDrop: true,
        allowedViews: _allowedViews,
        minDate: _minDate,
        timeRegionBuilder: _getSpecialRegionWidget,
        specialRegions: _specialTimeRegions,
        showNavigationArrow: true,
        dataSource: _calendarDataSource..resources = scrollableResources,
        onViewChanged: viewChangedCallback,
        resourceViewHeaderBuilder: _resourceHeaderBuilder,
        appointmentTimeTextFormat: 'HH:mm',
        resourceViewSettings: const ResourceViewSettings(
          resourceHeight: 220, // 与固定行高度保持一致
        ),
        timeSlotViewSettings: const TimeSlotViewSettings(
          timeInterval: Duration(minutes: 15),
          timeFormat: 'HH:mm',
          startHour: 7,
          endHour: 18,
        ),
        appointmentBuilder: (context, details) {
          return Container(
            padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
            child: Text(
              details.appointments.first.subject,
              maxLines: null,
              overflow: TextOverflow.visible,
              style: const TextStyle(fontSize: 12),
            ),
          );
        },
      ),
    ),
  ],
);
  1. 实现固定行的构建函数(含拖拽触发逻辑):
Widget _buildFixedNonAssignedHeader(Resource resource) {
  int capacity = _visibleAppointments.where((app) => 
    app.resourceIds != null && 
    app.resourceIds!.contains(resource.id)
  ).length;

  return Container(
    height: 220,
    color: resource.color,
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        icons[Random().nextInt(5)],
        Text(
          resource.displayName,
          style: const TextStyle(
              color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
        ),
        Padding(
          padding: const EdgeInsets.only(top: 5),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: const <Widget>[
              Text(
                'Termine: ',
                textAlign: TextAlign.center,
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 10,
                ),
              ),
              Text(
                capacity.toString(),
                textAlign: TextAlign.center,
                style: TextStyle(
                    color: Colors.white,
                    fontSize: 12,
                    fontWeight: FontWeight.bold),
              ),
            ],
          ),
        ),
        // 展示N/A资源下的预约,支持长按拖拽
        Expanded(
          child: SingleChildScrollView(
            padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
            child: Column(
              children: _visibleAppointments
                  .where((app) => 
                    app.resourceIds != null && 
                    app.resourceIds!.contains(resource.id)
                  )
                  .map((app) => GestureDetector(
                    onLongPressStart: (details) {
                      _calendarController.startAppointmentDrag(app, details.globalPosition);
                    },
                    child: Container(
                      margin: const EdgeInsets.only(bottom: 4),
                      padding: const EdgeInsets.all(6),
                      color: Colors.white.withOpacity(0.2),
                      child: Text(
                        app.subject,
                        maxLines: null,
                        style: const TextStyle(color: Colors.white, fontSize: 12),
                      ),
                    ),
                  ))
                  .toList(),
            ),
          ),
        ),
      ],
    ),
  );
}

内容的提问来源于stack exchange,提问作者Lukas Fürst

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 07:54:56