Incular logoIncular
API ReferenceInput & Forms

GestureDetector

Every tap, drag, scale, and trackpad callback with details types.

First-class Flutter-style detector: tap, double-tap, long-press, pan, directional drag, scale, and trackpad. Default behavior DeferToChild.

A tappable counter — copy, paste, and run:

use incular::prelude::*;
use incular::gestures::TapDownDetails;
use incular::widgets::HitTestBehavior;

fn main() {
    let count = Signal::new(0u32);
    let app = Application::new(move |_cx| {
        let tapped = count.clone();
        Container::builder()
            .padding(EdgeInsets::all(24.0))
            .alignment(Alignment::CENTER)
            .child(
                GestureDetector::new(Text::new("Tap me"))
                    .on_tap(move || tapped.update(|c| *c += 1))
                    .on_tap_down(|_d: TapDownDetails| {})
                    .on_double_tap(|| {})
                    .on_long_press(|| {})
                    .on_pan_update(|_delta: Offset| {})
                    .behavior(HitTestBehavior::Opaque),
            )
            .build()
            .into()
    })
    .expect("valid application");
    incular::run(app).expect("run application");
}

The full callback tour (on_tap_up, on_double_tap_down, on_long_press_start, on_pan_start, directional drags, on_scale_update, on_trackpad_gesture, focus_node, autofocus) shares the same builder shape; see the Methods table below.

// Full callback tour (attach to any child; each line typechecks):
// .on_tap_up(|d: TapUpDetails| {})
// .on_double_tap_down(|d: TapDownDetails| {})
// .on_long_press_start(|d: LongPressStartDetails| {})
// .on_pan_start(|d: DragStartDetails| {})
// .on_horizontal_drag_start(|d: DragStartDetails| {})
// .on_scale_update(|d: ScaleUpdateDetails| {})
// .on_trackpad_gesture(|g: TrackpadGesture| {})
// .focus_node(node)
// .autofocus(true)

Missing child renders SizedBox::shrink().

Callback groupMethods
Tapon_tap, on_tap_down, on_tap_up, on_tap_cancel
Double tapon_double_tap, on_double_tap_down, on_double_tap_cancel
Long presson_long_press, on_long_press_start, on_long_press_move_update, on_long_press_up, on_long_press_end
Panon_pan_down, on_pan_start, on_pan_update, on_pan_end, on_pan_cancel
Directional dragon_horizontal_drag_*, on_vertical_drag_* (down/start/update/end/cancel each)
Scaleon_scale_start, on_scale_update, on_scale_end
Trackpadon_trackpad_gesture
Lifecycleon_cancel, behavior, callbacks, focus_node, autofocus
use incular::prelude::*;

fn main() {
    let count = Signal::new(0u32);
    let app = Application::new(move |_cx| {
        let tapped = count.clone();
        GestureDetector::new(Text::new("Tap me"))
                .on_tap(move || tapped.update(|c| *c += 1)).into()
    })
    .expect("valid application");
    incular::run(app).expect("run application");
}

See HitTestBehavior for hit-test contracts and Raw Input for arena-free listeners.

Drag and drop

A shared DragDropContext<T> connects drag sources and targets — copy, paste, and run:

use incular::prelude::*;

fn main() {
    let context = DragDropContext::<String>::new();
    let target_context = context.clone();
    let app = Application::new(move |_cx| {
        Container::builder()
            .padding(EdgeInsets::all(24.0))
            .alignment(Alignment::CENTER)
            .child(
                Column::builder()
                    .main_axis_size(MainAxisSize::Min)
                    .spacing(8.0)
                    .children(Vec::<Widget>::from([
                        Draggable::new(
                                context.clone(),
                                "file.txt".to_string(),
                                Text::new("Drag me"),
                            )
                            .feedback(|payload| {
                                Text::new(format!("Moving {payload}")).into()
                            })
                            .on_start(|payload| println!("start {payload}"))
                            .on_end(|payload| println!("end {payload}"))
                            .on_cancel(|payload| println!("cancel {payload}")).into(),
                        DragTarget::new(
                                target_context.clone(),
                                Text::new("Drop here"),
                            )
                            .on_will_accept(|payload: String| !payload.is_empty())
                            .on_enter(|payload| println!("enter {payload}"))
                            .on_leave(|payload| println!("leave {payload}"))
                            .on_drop(|payload| println!("dropped {payload}")).into(),
                        Dismissible::new(
                                DismissDirection::Horizontal,
                                Text::new("Swipe me"),
                                |direction| println!("dismissed {direction:?}"),
                            )
                            .threshold(0.5).into(),
                    ]))
                    .build(),
            )
            .build()
            .into()
    })
    .expect("valid application");
    incular::run(app).expect("run application");
}

Cross-window transfer and lazy-list reordering are intentionally out of scope for the local contract. DismissDirection: Horizontal / Vertical — the axis a Dismissible may claim. LongPressDraggable starts drags after a long-press instead of immediately.

Related: Focus.

On this page