Why doesn't the "onDragEnd" event work inside my SFCalendar?

Hello team, 

I'm writing to request support with an issue I'm experiencing while trying to implement the drag & drop functionality (onDragEnd not working) in the Syncfusion Flutter Calendar. The custom model I'm using for my appointments is not working correctly with this feature. 

Here are the details of my environment:

 * **Flutter Version:** 3.32.8 

* **Target Devices:** * iOS 18.5.4 (Physical device) * iOS 26.0 (Virtual device)

I have confirmed that my Syncfusion Flutter packages are up to date. I have attached a code snippet that demonstrates the issue. Any assistance you can provide would be greatly appreciated. 


Best regards,


SfCalendar(
      key: _calendarKey,
      controller: _calendarController,
      view: widget.viewModel.calendarView,
      allowedViews: const [
        CalendarView.day,
        CalendarView.week,
        CalendarView.month,
        CalendarView.schedule,
      ],
      showTodayButton: true,
      showWeekNumber: true,
      showNavigationArrow: true,
      showDatePickerButton: true,
      todayHighlightColor: Theme.of(context).colorScheme.primary,
      initialDisplayDate: DateTime.now(),
      firstDayOfWeek: 1,
      dataSource: AppointmentDataSource(widget.viewModel.appointments),
      onTap: _calendarTapped,
      appointmentBuilder: _buildAppointmentInCalendar,
      timeSlotViewSettings: TimeSlotViewSettings(
        startHour: widget.viewModel.startHour,
        endHour: widget.viewModel.endHour,
        timeInterval: Duration(minutes: widget.viewModel.slotDuration ?? 60),
        timeFormat: 'HH:mm',
      ),
      allowDragAndDrop: true,
      onDragEnd: (appointmentDragEndDetails) {
        print("Dragged appointment: ${appointmentDragEndDetails.appointment}");
      },
    );


Datasource

class AppointmentDataSource extends CalendarDataSource {
  AppointmentDataSource(List<KAppointment> source) {
    appointments = source;
  }


  KAppointment _getAppointment(int index) {
    final dynamic appointment = appointments![index];
    late final KAppointment kAppointment;
    if (appointment is KAppointment) {
      kAppointment = appointment;
    }
    return kAppointment;
  }


  @override
  DateTime getStartTime(int index) {
    final fechaIniStr = _getAppointment(index).from;
    return DateFormat('yyyy-MM-dd HH:mm:ss').parse(fechaIniStr);
  }


  @override
  DateTime getEndTime(int index) {
    final fechaFinStr = _getAppointment(index).to;
    return DateFormat('yyyy-MM-dd HH:mm:ss').parse(fechaFinStr);
  }


  @override
  Object? getId(int index) {
    return _getAppointment(index).id.toString();
  }


  @override
  String getSubject(int index) {
    return _getAppointment(index).nombre;
  }


  @override
  Object? convertAppointmentToObject(
    Object? customData,
    Appointment appointment,
  ) {
    if (customData is KAppointment) {
      try {
        final updated = customData.copyWith(
          from: DateFormat('yyyy-MM-dd HH:mm:ss').format(appointment.startTime),
          to: DateFormat('yyyy-MM-dd HH:mm:ss').format(appointment.endTime),
        );
        return updated;
      } catch (e) {
        return customData;
      }
    }
    return customData;
  }
}


and the model


@freezed

abstract class KAppointment with _$KAppointment {
  const factory KAppointment({
    required int id,
    @JsonKey(name: 'tipo_actividad') required int tipoActividad,
    required String nombre,
    String? descripcion,
    @JsonKey(name: 'fecha_ini') required String from,
    @JsonKey(name: 'fecha_fin') required String to,
    @JsonKey(name: 'id_estatus') required int idEstatus,
    required String estatus,
    required String color,
    @JsonKey(name: 'id_tipo') int? idTipo,
    String? tipoEvento,
    @JsonKey(name: 'id_responsable') required int idResponsable,
    required String responsable,
    @JsonKey(name: 'id_cuenta') required int idCuenta,
    @JsonKey(name: 'cliente') required String cliente,
    @JsonKey(name: 'id_proyecto') int? idProyecto,
    String? proyecto,
    @JsonKey(name: 'imagen_min') String? imagenMin,
    required double balance,
    @JsonKey(name: 'cuenta_id_empresa') required int cuentaIdEmpresa,
    @JsonKey(name: 'es_cliente') required String esCliente,
    @JsonKey(name: 'id_giro_crm') required int idGiroCrm,
    required int confirmado,
    @JsonKey(name: 'tipo_color') String? tipoColor,
    required int pagado,
    @JsonKey(name: 'creado_por_cliente') required int creadoPorCliente,
    @JsonKey(name: 'confirmado_cliente') required int confirmadoCliente,
    required int retrabajo,
    required String title,
    @JsonKey(name: 'foto_url') String? fotoUrl,
    required String backgroundColor,
    required String borderColor,
  }) = _KAppointment;


  factory KAppointment.fromJson(Map<String, dynamic> json) =>
      _$KAppointmentFromJson(json);
}

4 Replies

PB Praveen Balu Syncfusion Team August 8, 2025 01:59 PM UTC

Hi Edgar,


We have tried to replicate the mentioned scenario on our end but were unable to reproduce it. We confirmed that the onDragEnd callback of the SfCalendar is functioning as expected by adding a dialog box within the callback, which displayed immediately after the drag ended.


Shared the tested details below:

  1. Flutter version: 3.32.8.

  2. Syncfusion Calendar version: 30.2.4 (Latest version).

  3. Tested on both an iPad device and an iOS emulator.


Additionally, we have provided a test sample along with a demo for your reference, which confirms that the onDragEnd callback is working correctly. If this scenario still occurs, we kindly request that you share the modified sample to help us replicate the mentioned behavior.


Regards,

Praveen Balu.


Attachment: forum_197285_243505f5.zip


GE Grimer Elon August 11, 2025 07:55 AM UTC

If this scenario still occurs, we kindly request that you share the modified sample to help us replicate the mentioned behavior.



DM Drew Moran August 18, 2025 03:12 AM UTC

From: Geometry Dash Lite

Hello Praveen,

It helps a lot to see that the onDragEnd callback is indeed firing as expected in your setup. I’ll test again with my current code and confirm whether the behavior is reproducible on my end. If I spot any inconsistencies, I’ll upload a sample project to illustrate.



PB Praveen Balu Syncfusion Team August 18, 2025 10:40 AM UTC

Hi Drew,

No problem at all. Please take your time to test the sample, we’ll be here and waiting for your update.
If you have any further questions, feel free to reach out to us.

Regards,
Praveen Balu



Loader.
Up arrow icon