Flutter Event Calendar Appointment Drag and Drop (SfCalendar)

18 Nov 201814 minutes to read

Easily reschedule an appointment by dragging it from one time slot or month cell and dropping it into a different time slot or month cell.

Allow Drag and Drop

To perform drag-and-drop operations within the calendar, enable the allowDragAndDrop property of SfCalendar.

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

void main() {
  runApp(const CalendarApp());
}

class CalendarApp extends StatelessWidget {
  const CalendarApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: SfCalendar(
              view: CalendarView.week,
              dataSource: _getCalendarDataSource(),
              allowDragAndDrop: true
          ),
        ),
      ),
    );
  }
}

_AppointmentDataSource _getCalendarDataSource() {
  List<Appointment> appointments = <Appointment>[];
  return _AppointmentDataSource(appointments);
}

class _AppointmentDataSource extends CalendarDataSource {
  _AppointmentDataSource(List<Appointment> source) {
    appointments = source;
  }
}

drag_and_drop

NOTE

  • It is not applicable for month view in mobile platform.

onDragStart

onDragStart callback is called whenever the appointment starts to drag in the SfCalendar. The AppointmentDragStartDetails arguments contains the dragging appointment and associated resource details.

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

void main() {
  runApp(const CalendarApp());
}

class CalendarApp extends StatelessWidget {
  const CalendarApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: SfCalendar(
            view: CalendarView.week,
            dataSource: _getCalendarDataSource(),
            allowDragAndDrop: true,
            onDragStart: dragStart,
          ),
        ),
      ),
    );
  }
}

void dragStart(AppointmentDragStartDetails appointmentDragStartDetails) {
  dynamic appointment = appointmentDragStartDetails.appointment;
  CalendarResource? resource = appointmentDragStartDetails.resource;
}

onDragUpdate

onDragUpdate callback is called whenever the appointment is dragging in the SfCalendar. The AppointmentDragUpdateDetails arguments contains the dragging appointment, dragging time, dragging offset, source resource, and target resource details.

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

void main() {
  runApp(const CalendarApp());
}

class CalendarApp extends StatelessWidget {
  const CalendarApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: SfCalendar(
            view: CalendarView.week,
            dataSource: _getCalendarDataSource(),
            allowDragAndDrop: true,
            onDragUpdate: dragUpdate,
          ),
        ),
      ),
    );
  }
}

void dragUpdate(AppointmentDragUpdateDetails appointmentDragUpdateDetails) {
  dynamic appointment = appointmentDragUpdateDetails.appointment;
  DateTime? draggingTime = appointmentDragUpdateDetails.draggingTime;
  Offset? draggingOffset = appointmentDragUpdateDetails.draggingPosition;
  CalendarResource? sourceResource = appointmentDragUpdateDetails.sourceResource;
  CalendarResource? targetResource = appointmentDragUpdateDetails.targetResource;
}

onDragEnd

onDragEnd callback is called when the dragging appointment is dropped in the SfCalendar. The AppointmentDragEndDetails arguments contains the dropped appointment, dropping time, source and target resource details.

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

void main() {
  runApp(const CalendarApp());
}

class CalendarApp extends StatelessWidget {
  const CalendarApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: SfCalendar(
            view: CalendarView.week,
            dataSource: _getCalendarDataSource(),
            allowDragAndDrop: true,
            onDragEnd: dragEnd,
          ),
        ),
      ),
    );
  }
}

void dragEnd(AppointmentDragEndDetails appointmentDragEndDetails) {
  dynamic appointment = appointmentDragEndDetails.appointment!;
  CalendarResource? sourceResource = appointmentDragEndDetails.sourceResource;
  CalendarResource? targetResource = appointmentDragEndDetails.targetResource;
  DateTime? droppingTime = appointmentDragEndDetails.droppingTime;
}

Disabling navigation when dragging appointment

You can restrict the navigation to the next/previous view when the dragging appointment reaches the start/end point of the current view in calendar by using the allowNavigation property of DragAndDropSettings. Default value of allowNavigation property is true.

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

void main() {
  runApp(const CalendarApp());
}

class CalendarApp extends StatelessWidget {
  const CalendarApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: SfCalendar(
            view: CalendarView.week,
            dataSource: _getCalendarDataSource(),
            allowDragAndDrop: true,
            dragAndDropSettings: const DragAndDropSettings(allowNavigation: true),
          ),
        ),
      ),
    );
  }
}

Disabling scroll when dragging appointment

You can restrict the timeslot views auto scroll when the appointment reaches the start/end point of the view port in the timeslot views of calendar by using the allowScroll property of DragAndDropSettings.

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

void main() {
  runApp(const CalendarApp());
}

class CalendarApp extends StatelessWidget {
  const CalendarApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: SfCalendar(
            view: CalendarView.week,
            dataSource: _getCalendarDataSource(),
            allowDragAndDrop: true,
            dragAndDropSettings: const DragAndDropSettings(allowScroll: true),
          ),
        ),
      ),
    );
  }
}

Time indicator

showTimeIndicator - This property handles whether to show the time indicator or not, which shows the dragging appointment current time position in time ruler. Default value of the showTimeIndicator property is true.

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

void main() {
  runApp(const CalendarApp());
}

class CalendarApp extends StatelessWidget {
  const CalendarApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: SfCalendar(
            view: CalendarView.week,
            dataSource: _getCalendarDataSource(),
            allowDragAndDrop: true,
            dragAndDropSettings: const DragAndDropSettings(showTimeIndicator: true),
          ),
        ),
      ),
    );
  }
}

Customize appearance of dragging Time Indicator

Using timeIndicatorStyle property you can customize the text style of the time indicator. Also using indicatorTimeFormat property you can customize the indicator time format.

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

void main() {
  runApp(const CalendarApp());
}

class CalendarApp extends StatelessWidget {
  const CalendarApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: SfCalendar(
            view: CalendarView.week,
            dataSource: _getCalendarDataSource(),
            allowDragAndDrop: true,
            dragAndDropSettings: const DragAndDropSettings(
              indicatorTimeFormat: 'hh:mm',
              showTimeIndicator: true,
              timeIndicatorStyle: TextStyle(
                backgroundColor: Color(0xFFCEE5D0),
                color: Colors.black,
                fontSize: 15,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

drag_and_drop_indicator_customization

View Navigation Delay

Using autoNavigateDelay property you can handle the navigation time when navigating to next/previous view while dragging the appointment.

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

void main() {
  runApp(const CalendarApp());
}

class CalendarApp extends StatelessWidget {
  const CalendarApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: SfCalendar(
            view: CalendarView.week,
            dataSource: _getCalendarDataSource(),
            allowDragAndDrop: true,
            dragAndDropSettings: const DragAndDropSettings(
                autoNavigateDelay: Duration(seconds: 1)),
          ),
        ),
      ),
    );
  }
}