Drag and drop with custom appointment type

I'm using a custom appointment type "Shift" that extends Appointment. 

When I allowDragAndDrop to true, I can long press and drag appointments (shifts) around but then when I release them I get the below exception. 

If I comment out calendar_view.dart:2266, then drag and drop works perfectly, but unfortunately this is in the Syncfusion calendar package.

Are there any examples of drag and drop with custom meeting types? 

Any help would be greatly appreciated! Thanks!


════════ Exception caught by gesture ═══════════════════════════════════════════

The following _TypeError was thrown while handling a gesture:

type 'Appointment' is not a subtype of type 'Shift' of 'value'


When the exception was thrown, this was the stack

#0 List.add (dart:core-patch/growable_array.dart)

#1 _CustomCalendarScrollViewState._handleLongPressEnd package:syncfusion_flutter_calendar/…/views/calendar_view.dart:2266

#2 _CustomCalendarScrollViewState.build.<anonymous closure> package:syncfusion_flutter_calendar/…/views/calendar_view.dart:654


7 Replies

IR Indumathi Ravichandran Syncfusion Team January 12, 2022 07:18 AM UTC

Hi Grady, 
 
Thank you for contacting Syncfusion support. 
 
Based on the shared information, we have checked the mentioned issue “Exception occurred while using drag and drop with custom appointment in Flutter Calendar”. You need to override the method convertAppointmentToObject() for getting the data with custom object type. We have mentioned this in our UG documentation notes content. Please find the UG from the following link. 
 
UG link: 
 
We hope that this helps you. Please let us know if you need further assistance. 
 
Regards, 
Indumathi R 



GR Grady January 12, 2022 04:13 PM UTC

Thanks!

I tried following that example but am still getting the same error. 


Below is the code and the custom appointment class. Please let me know what I'm doing wrong. Thanks!

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:starter_architecture_flutter_firebase/app/home/models/shift.dart';
import 'package:starter_architecture_flutter_firebase/app/top_level_providers.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';

final shiftsStreamProvider = StreamProvider.autoDispose<List<Shift>>((ref) {
final database = ref.watch(databaseProvider)!;
return database.shiftsStream();
});

class Scheduler extends ConsumerWidget {
final GlobalKey _globalKey = GlobalKey();
final ScrollController _controller = ScrollController();
final CalendarController _calendarController = CalendarController();

final List<CalendarView> _allowedViews = <CalendarView>[
CalendarView.day,
CalendarView.week,
CalendarView.workWeek,
CalendarView.month,
CalendarView.schedule
];

Scheduler({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final shiftsStream = ref.watch(shiftsStreamProvider);

return shiftsStream.when(
data: (data) {
return Scaffold(
body: SfCalendar(
controller: _calendarController,
allowedViews: _allowedViews,
dataSource: _ShiftDataSource(data),
allowDragAndDrop: true,
),
);
},
loading: () {
return const CircularProgressIndicator();
},
error: (Object error, StackTrace? stackTrace) {
return Scaffold(body: Text('Error $error $stackTrace'));
},
);
}
}

/// An object to set the appointment collection data source to collection, which
/// used to map the custom appointment data to the calendar appointment, and
/// allows to add, remove or reset the appointment collection.
class _ShiftDataSource extends CalendarDataSource<Shift> {
_ShiftDataSource(this.source);

List<Shift> source;

@override
List<dynamic> get appointments => source;

@override
String getId(int index) {
return source[index].id;
}

@override
DateTime getStartTime(int index) {
return source[index].startTime;
}

@override
DateTime getEndTime(int index) {
return source[index].endTime;
}

@override
bool isAllDay(int index) {
return source[index].isAllDay;
}

@override
String getSubject(int index) {
return source[index].subject;
}

@override
Color getColor(int index) {
return source[index].color;
}

@override
Shift convertAppointmentToObject(Shift eventName, Appointment appointment) {
return Shift(
id: appointment.id.toString(),
subject: appointment.subject,
startTime: appointment.startTime,
endTime: appointment.endTime,
color: appointment.color,
isAllDay: appointment.isAllDay);
}
}


And the custom appointment type:

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';

class Shift extends Appointment {
Shift({
required this.id,
required DateTime startTime,
required DateTime endTime,
required bool isAllDay,
required String subject,
required Color color,
String? startTimeZone,
String? endTimeZone,
String? recurrenceRule,
List<DateTime>? recurrenceExceptionDates,
String? notes,
String? location,
List<Object>? resourceIds,
Object? recurrenceId,
// Object? id,
}) : super(
startTime: startTime,
endTime: endTime,
isAllDay: isAllDay,
subject: subject,
color: color,
startTimeZone: startTimeZone,
endTimeZone: endTimeZone,
recurrenceRule: recurrenceRule,
recurrenceExceptionDates: recurrenceExceptionDates,
notes: notes,
location: location,
resourceIds: resourceIds,
recurrenceId: recurrenceId,
// id: id,
// );
);

final String id;

Map<String, dynamic> toMap() {
return {
'startTime': startTime,
'endTime': endTime,
'isAllDay': isAllDay,
'subject': subject,
// 'colorString': color.toString(),
// 'color': color,
'startTimeZone': startTimeZone,
'endTimeZone': endTimeZone,
'recurrenceRule': recurrenceRule,
'recurrenceExceptionDates': recurrenceExceptionDates,
'notes': notes,
'location': location,
'resourceIds': resourceIds,
'recurrenceId': recurrenceId,
'id': id,
};
}

factory Shift.fromMap(Map<dynamic, dynamic>? value, String shiftId) {
if (value == null) {
throw StateError('missing data for shiftId: $shiftId');
}

final id = value['id'] as String;
final startTime = value['startTime'].toDate() as DateTime;
final endTime = value['endTime'].toDate() as DateTime;
final isAllDay = value['isAllDay'] as bool;
final subject = value['subject'] as String;
// final colorString = value['colorString'] as String;
// final color = value['color'] as Color;

return Shift(
id: id,
startTime: startTime,
endTime: endTime,
isAllDay: isAllDay,
subject: subject,
color: Colors.lightBlueAccent,
// color: colorString,
// id: id,
// resourceIds: resourceIds.cast(),
// comment: value['comment'] as String? ?? '',
);
}
}



IR Indumathi Ravichandran Syncfusion Team January 13, 2022 06:12 AM UTC

Hi Grady, 
 
Thank you for the update. 
 
We have prepared the simple sample for drag and drop with custom appointment, but there is no exception from our end, and it was working fine as expected. Please find the sample from the following link. 
 
Sample link: 
 
Please check the sample once and let us know still if you are facing same issue? It would be helpful for us to analyze and provide you a solution at the earliest. 
 
Regards, 
Indumathi R


GR Grady January 17, 2022 11:41 PM UTC

Thank you for your response! I followed the example but unfortunately I am still facing the same issue :(



IR Indumathi Ravichandran Syncfusion Team January 18, 2022 07:28 AM UTC

Hi Grady, 
 
Thank you for the update. 
 
We have checked the custom appointment sample with appointment drag and drag using Firestore database data, we are unable to reproduce the mentioned issue. We have attached the tested sample for the same. Please find the sample from the following link. 
 
 
Please check the sample and let us know if you still facing the same issue? If not, please modify the sample based on your scenario and revert us with following details, 
 
1.       Issue reproducing video. 
2.       Code snippet 
 
Regards, 
Indumathi R 



MS Martin Seubert February 9, 2024 03:29 PM UTC

Could you please eloberate how this can be achieved when dealing with resources in the DataSource? I get your sample working, but when adding resources, it fails again! 


class _AppointmentDataSource extends CalendarDataSource<CustomAppointment> {
_AppointmentDataSource(
List<CustomAppointment> source, List<EvseCalendarResource> resourceColl) {
appointments = source;
resources = resourceColl;
}

@override
DateTime getStartTime(int index) {
return appointments![index].startTime;
}

@override
DateTime getEndTime(int index) {
return appointments![index].endTime;
}

@override
Color getColor(int index) {
return appointments![index].color as Color;
}

@override
String getSubject(int index) {
return appointments![index].subject as String;
}

@override
CustomAppointment convertAppointmentToObject(
CustomAppointment customData, Appointment appointment) {
// TODO: implement convertAppointmentToObject
return CustomAppointment(
startTime: appointment.startTime,
endTime: appointment.endTime,
subject: appointment.subject,
color: appointment.color,
userId: "gggg",
resourceIds: appointment.resourceIds!);
}
}

class CustomAppointment extends Appointment {
final String userId;

CustomAppointment({
required DateTime startTime,
required DateTime endTime,
required String subject,
required Color color,
required List<Object> resourceIds,
required this.userId,
}) : super(
startTime: startTime,
endTime: endTime,
subject: subject,
color: color,
resourceIds: resourceIds);
}

class EvseCalendarResource extends CalendarResource {
final String evse;

EvseCalendarResource({required Object id, required this.evse})
: super(
id: id,
);
}



YG Yuvaraj Gajaraj Syncfusion Team February 12, 2024 01:05 PM UTC

Hi Martin,


We have KB that explains how to get data from Firebase add it to calendar appointments and store calendar appointments in the Firebase database. We have shared it below for your reference. If this is not your query then please share more details that illustrate your requirement. That would be more helpful to us to provide a solution sooner.


KB, https://support.syncfusion.com/kb/article/10404/how-to-work-with-the-firebase-database-and-the-flutter-calendar-for-appointments


Regards,

Yuvaraj.


Loader.
Up arrow icon