Skip to content

Refine sheet transitions#217

Merged
dokar3 merged 6 commits intomainfrom
refine-transitions
Apr 14, 2026
Merged

Refine sheet transitions#217
dokar3 merged 6 commits intomainfrom
refine-transitions

Conversation

@dokar3
Copy link
Copy Markdown
Owner

@dokar3 dokar3 commented Apr 14, 2026

Summary by CodeRabbit

  • Refactor
    • Improved keyboard (IME) visibility detection and handling across Android, Desktop, and WebAssembly for more reliable behavior.
    • Reworked sheet background rendering for more consistent visuals and shape-aware drawing.
    • Optimized sheet animation and timing around keyboard visibility, reducing layout glitches and yielding smoother expand/peek transitions.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 14, 2026

Warning

Rate limit exceeded

@dokar3 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 53 minutes and 34 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 53 minutes and 34 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1b56af23-ce7e-41ac-a308-07685e4707c5

📥 Commits

Reviewing files that changed from the base of the PR and between 647f3aa and 5a81635.

📒 Files selected for processing (1)
  • sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt
📝 Walkthrough

Walkthrough

Refactors sheet background rendering to use a DrawScope lambda, centralizes CoreBottomSheet layout overloads, and changes IME visibility handling: platforms set imeVisible + hasImeVisibilityUpdated; state now waits for IME visibility updates before applying expand/peek frame delays.

Changes

Cohort / File(s) Summary
IME Visibility Tracking
sheets-core/src/androidMain/kotlin/com/dokar/sheets/Platform.android.kt, sheets-core/src/desktopMain/kotlin/com/dokar/sheets/Platform.desktop.kt, sheets-core/src/wasmJsMain/kotlin/com/dokar/sheets/Platform.wasmJs.kt, sheets-core/src/commonMain/kotlin/com/dokar/sheets/BottomSheetState.kt
Added hasImeVisibilityUpdated flag and migrated imeVisible to a plain boolean. Platform SheetHost implementations set these flags via SideEffect. delayIfImeVisible now waits for hasImeVisibilityUpdated and uses timeout-wrapped frame-delay logic.
Background Rendering Refactor
sheets-core/src/commonMain/kotlin/com/dokar/sheets/BackgroundWithInsetsModifier.kt, sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheet.kt, sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt
Replaced Color/Brush background parameters with sheetBackground: DrawScope.(Outline) -> Unit. Introduced drawSheetBackground using drawWithCache to precompute outline; updated wrappers to convert color/brush into drawing lambdas and unified shared layout overload.
Content Offset & Alpha
sheets-core/src/commonMain/kotlin/com/dokar/sheets/layout/ContentOffsetY.kt
Removed contentAlpha parameter and removed content-alpha animations. Collapsed-state offset behavior now sets offset conditionally only when not animating and state is collapsed.
Core layout orchestration
sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt
Added new overload accepting sheetBackground lambda, moved .nestedScroll earlier, replaced prior snapshotFlow content-alpha logic with UpdateContentAlpha that reacts to (state.value, state.animState), and updated IME flag lifecycle (set in SideEffect, cleared in DisposableEffect).

Sequence Diagram

sequenceDiagram
    participant Platform as Platform Layer
    participant State as BottomSheetState
    participant Layout as CoreBottomSheetLayout
    participant Draw as DrawScope

    Platform->>State: SideEffect sets imeVisible & hasImeVisibilityUpdated
    activate State
    State->>State: expand()/peek() invoked
    alt animate = true
        State->>State: delayIfImeVisible()
        Note over State: wait for hasImeVisibilityUpdated (frame polling)
        State->>State: check final imeVisible
        alt imeVisible = false
            State->>State: apply frame delay (with timeout)
        else imeVisible = true
            State->>State: skip delay
        end
    end
    State->>Layout: trigger layout animation/update
    activate Layout
    Layout->>Draw: drawSheetBackground(outline)
    activate Draw
    Note over Draw: precompute outline via drawWithCache<br/>invoke sheetBackground(outline)
    Draw->>Draw: drawOutline (color/brush) or custom drawing
    deactivate Draw
    deactivate Layout
    deactivate State
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰
With insets, outlines, and a gentle sigh,
I watch the IME peek and pass me by.
Lambdas draw the sheet so neat and bright,
Animations wait till timing's right.
A little hop — the surface feels just right! 🎨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Refine sheet transitions' accurately captures the main objective of this PR, which focuses on improving IME visibility handling, state management, and transition timing in the sheets library.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refine-transitions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sheets-core/src/commonMain/kotlin/com/dokar/sheets/BottomSheetState.kt (1)

349-367: ⚠️ Potential issue | 🟠 Major

Don't let the IME latch wait forever.

If the host is already disposed or never enters composition, hasImeVisibilityUpdated never flips back to true, so expand() / peek() can suspend indefinitely in this loop. sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt Lines 229-235 clear the flag again on disposal, and the IllegalStateException fallback only helps when there is no frame clock. This wait needs a bounded/abort path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sheets-core/src/commonMain/kotlin/com/dokar/sheets/BottomSheetState.kt`
around lines 349 - 367, The unbounded wait in delayIfImeVisible (waiting on
hasImeVisibilityUpdated using withFrameNanos) can hang forever; change it to a
bounded/abortable wait: wrap the wait-for-hasImeVisibilityUpdated loop in a
timeout (e.g., withTimeout or withTimeoutOrNull) or cap the number of frames
iterated, and if the timeout/limit is reached treat it as "no IME update" and
return so expand()/peek() won't suspend forever; also ensure you don't swallow
coroutine cancellation (don't catch CancellationException) and keep the existing
IllegalStateException fallback for no-frame-clock cases. Apply these changes
inside delayIfImeVisible, replacing the unbounded
while(!hasImeVisibilityUpdated) loop, keeping the later
repeat(imeVisibleDelayFrames) behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt`:
- Around line 355-379: The content alpha controller currently infers whether to
animate purely from animState, which lets setValue(..., animate = false) still
trigger content animations; fix this by propagating the explicit animate flag
through the state and using it in UpdateContentAlpha: add a boolean (e.g.,
transitionAnimated or isTransitionAnimated) to BottomSheetState that
setValue(...) sets based on its animate param, update any callers (e.g.,
showContent()) to respect/leave that flag, then change UpdateContentAlpha's
snapshotFlow to include that boolean (snapshotFlow { state.value to
state.animState to state.isTransitionAnimated }) and pass it into
contentAlpha.updateBySheetState (or extend updateBySheetState to accept the
animate boolean) so content alpha only runs animateTo when the transition was
requested as animated.

---

Outside diff comments:
In `@sheets-core/src/commonMain/kotlin/com/dokar/sheets/BottomSheetState.kt`:
- Around line 349-367: The unbounded wait in delayIfImeVisible (waiting on
hasImeVisibilityUpdated using withFrameNanos) can hang forever; change it to a
bounded/abortable wait: wrap the wait-for-hasImeVisibilityUpdated loop in a
timeout (e.g., withTimeout or withTimeoutOrNull) or cap the number of frames
iterated, and if the timeout/limit is reached treat it as "no IME update" and
return so expand()/peek() won't suspend forever; also ensure you don't swallow
coroutine cancellation (don't catch CancellationException) and keep the existing
IllegalStateException fallback for no-frame-clock cases. Apply these changes
inside delayIfImeVisible, replacing the unbounded
while(!hasImeVisibilityUpdated) loop, keeping the later
repeat(imeVisibleDelayFrames) behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: abd4af77-5196-4583-8c23-50c5fa178687

📥 Commits

Reviewing files that changed from the base of the PR and between b4e0231 and bc40bb4.

📒 Files selected for processing (8)
  • sheets-core/src/androidMain/kotlin/com/dokar/sheets/Platform.android.kt
  • sheets-core/src/commonMain/kotlin/com/dokar/sheets/BackgroundWithInsetsModifier.kt
  • sheets-core/src/commonMain/kotlin/com/dokar/sheets/BottomSheetState.kt
  • sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheet.kt
  • sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt
  • sheets-core/src/commonMain/kotlin/com/dokar/sheets/layout/ContentOffsetY.kt
  • sheets-core/src/desktopMain/kotlin/com/dokar/sheets/Platform.desktop.kt
  • sheets-core/src/wasmJsMain/kotlin/com/dokar/sheets/Platform.wasmJs.kt

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt (2)

38-38: Unused import: CornerRadius

CornerRadius is imported but not used anywhere in this file.

🧹 Proposed fix
-import androidx.compose.ui.geometry.CornerRadius
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt`
at line 38, Remove the unused import androidx.compose.ui.geometry.CornerRadius
from the CoreBottomSheetLayout.kt file; locate the CoreBottomSheetLayout
declaration and its imports at the top of the file and delete the CornerRadius
import line so there are no unused imports left.

364-372: Consider adding a comment to clarify the cancellation-handling logic.

The early return when targetValue != null && animState == None && value != targetValue handles interrupted animations, but the intent isn't immediately obvious. A brief comment would improve maintainability.

📝 Suggested clarification
                 val oldAnimState = previousAnimState
                 previousAnimState = animState
                 val targetValue = oldAnimState.targetValue()
+                // Skip update if the previous animation was interrupted before reaching its target.
+                // This avoids incorrect alpha changes when, e.g., collapsing while expanding.
                 if (targetValue != null &&
                     animState == BottomSheetState.AnimState.None &&
                     value != targetValue
                 ) {
                     return@collectLatest
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt`
around lines 364 - 372, Add a concise inline comment above the if-block inside
the collectLatest lambda in CoreBottomSheetLayout.kt explaining the
cancellation-handling: note that previousAnimState is preserved, targetValue is
read from it, and when targetValue != null && animState ==
BottomSheetState.AnimState.None && value != targetValue we early-return to
ignore this emission because it comes from an interrupted/aborted animation
(avoids applying a stale target value). Reference the symbols previousAnimState,
animState, BottomSheetState.AnimState.None, targetValue, value and collectLatest
so reviewers can find and understand the intent quickly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt`:
- Line 38: Remove the unused import androidx.compose.ui.geometry.CornerRadius
from the CoreBottomSheetLayout.kt file; locate the CoreBottomSheetLayout
declaration and its imports at the top of the file and delete the CornerRadius
import line so there are no unused imports left.
- Around line 364-372: Add a concise inline comment above the if-block inside
the collectLatest lambda in CoreBottomSheetLayout.kt explaining the
cancellation-handling: note that previousAnimState is preserved, targetValue is
read from it, and when targetValue != null && animState ==
BottomSheetState.AnimState.None && value != targetValue we early-return to
ignore this emission because it comes from an interrupted/aborted animation
(avoids applying a stale target value). Reference the symbols previousAnimState,
animState, BottomSheetState.AnimState.None, targetValue, value and collectLatest
so reviewers can find and understand the intent quickly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 27118a2f-ecff-4262-9cda-e070870d2996

📥 Commits

Reviewing files that changed from the base of the PR and between bc40bb4 and 647f3aa.

📒 Files selected for processing (2)
  • sheets-core/src/commonMain/kotlin/com/dokar/sheets/BottomSheetState.kt
  • sheets-core/src/commonMain/kotlin/com/dokar/sheets/CoreBottomSheetLayout.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • sheets-core/src/commonMain/kotlin/com/dokar/sheets/BottomSheetState.kt

@dokar3 dokar3 merged commit ef41681 into main Apr 14, 2026
2 checks passed
@dokar3 dokar3 deleted the refine-transitions branch April 14, 2026 05:05
@coderabbitai coderabbitai bot mentioned this pull request Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant