Interaction topic

Interaction

This is part of the kalender documentation.

What the user can do inside a view: create, reschedule, resize, snap and zoom. Reacting to what they did is separate, see Controllers & Callbacks.


Interaction & Snapping

interaction sets what the user may do. KalenderHeader and KalenderBody both accept it, so a calendar can allow different things in its header than in its body. snapping is accepted by KalenderBody only, and only the multi-day body reads it.

Both blocks below show every option at its default value.

KalenderBody(
  interaction: KalenderInteraction(
    allowResizing: true,
    allowRescheduling: true,
    allowEventCreation: true,
    // Tap on desktop, long-press on mobile, when left unset.
    createEventGesture: EventInteractionGesture.tap,
    // The gesture that starts modifying an existing event, same defaults.
    modifyEventGesture: EventInteractionGesture.tap,
    // Input mode affects resize handle positioning and visibility:
    //   auto:      detects dynamically from pointer events
    //   precise:   mouse, stylus, trackpad (full-width handles, hover-to-show)
    //   imprecise: touch/finger (corner handles, selection-to-show)
    inputMode: InputMode.auto,
    // Opt in to horizontal resize handles in imprecise/touch mode.
    allowHorizontalImpreciseResize: false,
  ),
  snapping: KalenderSnapping(
    snapIntervalMinutes: 10,
    snapToTimeIndicator: true,
    snapToOtherEvents: true,
    snapRange: const Duration(minutes: 15),
    eventSnapStrategy: const EventSnapStrategy.interval(),
  ),
)

Snapping strategies

eventSnapStrategy decides where a dragged event lands. EventSnapStrategy.interval(), the default, snaps to the nearest multiple of snapIntervalMinutes. EventSnapStrategy.none() leaves the cursor position alone. snapToTimeIndicator and snapToOtherEvents apply either way.

For anything else, extend EventSnapStrategy and override snap. Compare on runtimeType in ==, as Layout describes for the layout strategies and for the same reason.


Locking a single event

KalenderInteraction applies to the whole calendar. To hold one event in place while the rest stay editable, give it an EventInteraction. Anything the event forbids stays forbidden even where the calendar allows it.

KalenderEvent(
  start: range.start,
  end: range.end,
  // Movable, but its start and end are fixed.
  interaction: EventInteraction(
    allowStartResize: false,
    allowEndResize: false,
    allowRescheduling: true,
  ),
)

EventInteraction.allowNone() makes an event fully read-only, and EventInteraction.allowAll() is the default every event gets. A subclass passes it through with super.interaction, as in Custom Events.


Zoom

Zoom the calendar in and out by changing the heightPerMinute value on the MultiDayViewController. The web_demo example shows a full implementation with ZoomDetector.

Here is a minimal example of driving zoom from Ctrl+scroll on desktop. PointerScrollEvent and HardwareKeyboard are not exported by material.dart, so both imports are needed:

import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';

class ZoomableCalendar extends StatelessWidget {
  final KalenderController kalenderController;
  final Widget child;

  const ZoomableCalendar({
    super.key,
    required this.kalenderController,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    return Listener(
      onPointerSignal: (event) {
        if (!HardwareKeyboard.instance.isControlPressed) return;
        if (event is! PointerScrollEvent) return;

        final viewController = kalenderController.viewController;
        if (viewController is! MultiDayViewController) return;

        final heightPerMinute = viewController.heightPerMinute;
        final delta = event.scrollDelta.dy.sign * -0.1;
        heightPerMinute.value = (heightPerMinute.value + delta).clamp(0.5, 2.0);
      },
      child: child,
    );
  }
}

Wrap your KalenderView with this widget to enable Ctrl+scroll zooming.

Classes

DefaultResizeHandles Interaction
The default layout for the resize handles of an event tile.
EventInteraction Interaction
The EventInteraction class defines the interaction settings for individual calendar events.
EventSnapStrategy Interaction
Decides where a dragged event lands, given the position of the cursor.
IntervalSnapStrategy Interaction
Snaps to the nearest multiple of the snap interval.
KalenderInteraction Interaction
The KalenderInteraction class defines the interaction settings for the calendar.
KalenderSnapping Interaction
The KalenderSnapping class defines the snapping settings for the calendar.
NoSnapStrategy Interaction
Returns the cursor position unchanged.
PageTriggerConfiguration Interaction
The configuration for the page triggers.
ResizeDetector Interaction
The draggable that detects a resize gesture, wrapping the handle widget from TileComponents.verticalResizeHandle or TileComponents.horizontalResizeHandle.
ResizeHandleDetails Interaction
What a ResizeHandlePositioner needs to lay out the resize handles of one event tile.
ScrollTriggerConfiguration Interaction
The configuration for the scroll triggers.

Mixins

DragTargetUtilities<T extends StatefulWidget> Interaction
Shared drag-target behaviour for the calendar's DragTarget states.

Constants

kDefaultNewEventDuration → const Duration Interaction
kDefaultSnapStrategy → const EventSnapStrategy Interaction
The strategy a calendar uses when nothing overrides it.

Enums

EventInteractionGesture Interaction
The EventInteractionGesture is used to differentiate between the different ways to create and modify an event.
InputMode Interaction
The InputMode defines the type of input the calendar should optimize for.
ResizeDirection Interaction
The ResizeDirection is used to differentiate between the different directions that an event can be resized in.

Typedefs

HorizontalTriggerWidgetBuilder = Widget Function(BuildContext context, double pageWidth) Interaction
The trigger widget builder, should be constrained in width.
OnWillAcceptWithDetailsHorizontal = bool Function(DragTargetDetails<Object?> details, KalenderController controller, HorizontalConfiguration configuration) Interaction
The callback for when a drag target is evaluating whether to accept a draggable.
OnWillAcceptWithDetailsVertical = bool Function(DragTargetDetails<Object?> details, KalenderController controller, VerticalConfiguration configuration) Interaction
The callback for when a drag target is evaluating whether to accept a draggable.
ResizeHandlePositioner = Widget Function(BuildContext context, ResizeHandleDetails details) Interaction
The builder that positions the resize handles of an event tile.
VerticalTriggerWidgetBuilder = Widget Function(BuildContext context, double pageWidth) Interaction
The trigger widget builder, should be constrained in height.