yuliskov/SmartTube · error · IllegalStateException

Cannot flingCapturedView outside of a call to Callback#onVie

Error message

Cannot flingCapturedView outside of a call to Callback#onViewReleased

What it means

flingCapturedView() applies a velocity-based fling to the currently captured view using the velocities the helper measured during the drag. Like settleCapturedViewAt, it is only legal while mReleaseInProgress is true — i.e., inside Callback#onViewReleased — and throws IllegalStateException anywhere else.

Source

Thrown at slidableactivity/src/main/java/com/r0adkll/slidr/util/ViewDragHelper.java:650

    private float distanceInfluenceForSnapDuration(float f) {
        f -= 0.5f; // center the values about 0.
        f *= 0.3f * Math.PI / 2.0f;
        return (float) Math.sin(f);
    }

    /**
     * Settle the captured view based on standard free-moving fling behavior.
     * The caller should invoke {@link #continueSettling(boolean)} on each subsequent frame
     * to continue the motion until it returns false.
     *
     * @param minLeft Minimum X position for the view's left edge
     * @param minTop  Minimum Y position for the view's top edge
     * @param maxLeft Maximum X position for the view's left edge
     * @param maxTop  Maximum Y position for the view's top edge
     */
    public void flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop) {
        if (!mReleaseInProgress) {
            throw new IllegalStateException("Cannot flingCapturedView outside of a call to " +
                    "Callback#onViewReleased");
        }
        mScroller.fling(mCapturedView.getLeft(), mCapturedView.getTop(),
                (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
                (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId),
                minLeft, maxLeft, minTop, maxTop);
        setDragState(STATE_SETTLING);
    }

    /**
     * Move the captured settling view by the appropriate amount for the current time.
     * If <code>continueSettling</code> returns true, the caller should call it again
     * on the next frame to continue.
     *
     * @param deferCallbacks true if state callbacks should be deferred via posted message.
     *                       Set this to true if you are calling this method from
     *                       {@link android.view.View#computeScroll()} or similar methods
     *                       invoked as part of layout or drawing.

View on GitHub (pinned to 3de8d90593)

Solutions

  1. Move flingCapturedView into Callback#onViewReleased, computing min/max bounds there.
  2. For programmatic dismissal outside a release, use smoothSlideViewTo(child, finalLeft, finalTop) plus continueSettling(true) per frame instead.
  3. If you need fling physics outside a release, compute the target yourself and settle with smoothSlideViewTo.

Example fix

// before
void dismissProgrammatically() {
    mDragHelper.flingCapturedView(0, 0, getWidth(), getHeight()); // throws outside release
}

// after
void dismissProgrammatically() {
    mDragHelper.smoothSlideViewTo(mDraggableChild, mParentView.getWidth(), mDraggableChild.getTop());
    ViewCompat.postInvalidateOnAnimation(mParentView);
}

@Override public void onViewReleased(View child, float xvel, float yvel) {
    mDragHelper.flingCapturedView(0, 0, mParentView.getWidth(), mParentView.getHeight());
    ViewCompat.postInvalidateOnAnimation(mParentView);
}
Defensive patterns

Strategy: validation

Validate before calling

// fling is legal only inside onViewReleased
@Override
public void onViewReleased(View child, float xvel, float yvel) {
    mDragHelper.flingCapturedView(0, 0, mParentView.getWidth(), mParentView.getHeight());
    ViewCompat.postInvalidateOnAnimation(mParentView);
}

// programmatic dismiss path (outside release):
mDragHelper.smoothSlideViewTo(child, mParentView.getWidth(), child.getTop());
ViewCompat.postInvalidateOnAnimation(mParentView);

Prevention

When it happens

Trigger: Calling flingCapturedView(...) from any code other than the onViewReleased override: dismiss buttons, timers, chained animations, or helper classes that trigger flings on their own schedule.

Common situations: Implementing slide-to-dismiss with an extra programmatic dismiss trigger; refactoring release logic into a separate class that calls fling directly; mimicking bottom-sheet behavior outside the release callback.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of yuliskov/SmartTube@3de8d90593 (2026-08-22). Data as JSON: /api/errors/1c5f9ce48a8216a2. Report an issue: GitHub.