@@ -43,6 +43,7 @@ import androidx.compose.ui.text.style.TextAlign
4343import androidx.compose.ui.unit.Dp
4444import androidx.compose.ui.unit.DpSize
4545import androidx.compose.ui.unit.dp
46+ import androidx.compose.ui.util.lerp
4647import kotlinx.coroutines.launch
4748import top.yukonga.miuix.kmp.basic.Text
4849import top.yukonga.miuix.kmp.theme.MiuixTheme
@@ -182,15 +183,15 @@ private fun SuperBottomSheetContent(
182183 WindowInsets .statusBars.getTop(density).toDp()
183184 }
184185
185- val rootBoxModifier = remember(onDismissRequest) {
186- Modifier
187- .pointerInput(onDismissRequest) {
188- detectTapGestures(
189- onTap = { onDismissRequest?.invoke() }
190- )
191- }
192- .fillMaxSize()
193- }
186+ val rootBoxModifier = Modifier
187+ .pointerInput(onDismissRequest) {
188+ detectTapGestures(
189+ onTap = {
190+ onDismissRequest?.invoke()
191+ }
192+ )
193+ }
194+ .fillMaxSize()
194195
195196 Box (modifier = rootBoxModifier) {
196197 SuperBottomSheetColumn (
@@ -241,10 +242,31 @@ private fun SuperBottomSheetColumn(
241242) {
242243 val coroutineScope = rememberCoroutineScope()
243244
245+ // Calculate the overscroll offset for background fill
246+ val dragOffsetYValue by remember { derivedStateOf { dragOffsetY.value } }
247+ val overscrollOffsetPx by remember {
248+ derivedStateOf {
249+ (- dragOffsetYValue).coerceAtLeast(0f )
250+ }
251+ }
252+
244253 Box (
245254 modifier = Modifier .fillMaxSize(),
246255 contentAlignment = Alignment .BottomCenter
247256 ) {
257+ // Background fill for the area revealed when dragging up (overscroll effect)
258+ if (overscrollOffsetPx > 0f ) {
259+ Box (
260+ modifier = Modifier
261+ .align(Alignment .BottomCenter )
262+ .widthIn(max = sheetMaxWidth)
263+ .fillMaxWidth()
264+ .height(with (density) { overscrollOffsetPx.toDp() })
265+ .padding(horizontal = outsideMargin.width)
266+ .background(backgroundColor)
267+ )
268+ }
269+
248270 Column (
249271 modifier = modifier
250272 .pointerInput(Unit ) {
@@ -308,6 +330,11 @@ private fun DragHandleArea(
308330 coroutineScope : kotlinx.coroutines.CoroutineScope ,
309331 onDismissRequest : (() -> Unit )?
310332) {
333+ val dragStartOffset = remember { mutableFloatStateOf(0f ) }
334+ val isPressing = remember { mutableFloatStateOf(0f ) }
335+ val pressScale = remember { Animatable (1f ) }
336+ val pressWidth = remember { Animatable (45f ) }
337+
311338 Box (
312339 modifier = Modifier
313340 .fillMaxWidth()
@@ -316,39 +343,91 @@ private fun DragHandleArea(
316343 detectVerticalDragGestures(
317344 onDragStart = {
318345 coroutineScope.launch {
346+ dragStartOffset.floatValue = dragOffsetY.value
319347 dragOffsetY.snapTo(dragOffsetY.value)
348+ // Animate press effect
349+ isPressing.floatValue = 1f
350+ launch {
351+ pressScale.animateTo(
352+ targetValue = 1.15f ,
353+ animationSpec = tween(durationMillis = 100 )
354+ )
355+ }
356+ launch {
357+ pressWidth.animateTo(
358+ targetValue = 55f ,
359+ animationSpec = tween(durationMillis = 100 )
360+ )
361+ }
320362 }
321363 },
322364 onDragEnd = {
323365 coroutineScope.launch {
366+ // Reset press effect
367+ isPressing.floatValue = 0f
368+ launch {
369+ pressScale.animateTo(
370+ targetValue = 1f ,
371+ animationSpec = tween(durationMillis = 150 )
372+ )
373+ }
374+ launch {
375+ pressWidth.animateTo(
376+ targetValue = 45f ,
377+ animationSpec = tween(durationMillis = 150 )
378+ )
379+ }
380+
381+ val currentOffset = dragOffsetY.value
382+ val dragDelta = currentOffset - dragStartOffset.floatValue
383+
324384 when {
325- // Dragged down significantly -> dismiss with animation
326- dragOffsetY.value > 150f -> {
327- // Animate to bottom of screen
385+ // Dragged down significantly -> dismiss
386+ dragDelta > 150f -> {
328387 onDismissRequest?.invoke()
388+ val windowHeightPx = windowHeight.value * density.density
329389 dragOffsetY.animateTo(
330- targetValue = windowHeight.value * density.density ,
390+ targetValue = windowHeightPx ,
331391 animationSpec = tween(durationMillis = 250 )
332392 )
333393 }
334- // Reset position if no action triggered
394+ // Reset to original position (including overscroll bounce back)
335395 else -> {
336- dragOffsetY.animateTo(0f , animationSpec = tween(durationMillis = 150 ))
337- // Reset dim alpha
396+ dragOffsetY.animateTo(
397+ targetValue = 0f ,
398+ animationSpec = tween(durationMillis = 250 )
399+ )
338400 dimAlpha.value = 1f
339401 }
340402 }
341403 }
342404 },
343405 onVerticalDrag = { _, dragAmount ->
344406 coroutineScope.launch {
345- // Only allow dragging down (positive offset)
346- val newOffset = (dragOffsetY.value + dragAmount).coerceAtLeast(0f )
347- dragOffsetY.snapTo(newOffset)
407+ val newOffset = dragOffsetY.value + dragAmount
348408
349- // Update dim alpha based on sheet height
409+ // Apply damping effect when dragging upward (negative offset)
410+ val finalOffset = if (newOffset < 0 ) {
411+ // Overscroll effect: reduce drag amount with damping
412+ val dampingFactor = 0.1f // Adjust this value for more/less resistance
413+ val dampedAmount = dragAmount * dampingFactor
414+ (dragOffsetY.value + dampedAmount).coerceAtMost(0f )
415+ } else {
416+ // Normal drag downward
417+ newOffset
418+ }
419+
420+ dragOffsetY.snapTo(finalOffset)
421+
422+ // Update dim alpha based on downward drag only
350423 val thresholdPx = if (sheetHeightPx.value > 0 ) sheetHeightPx.value.toFloat() else 500f
351- val alpha = 1f - (newOffset / thresholdPx).coerceIn(0f , 1f )
424+ val alpha = if (finalOffset >= 0 ) {
425+ // Dragging down - reduce alpha
426+ 1f - (finalOffset / thresholdPx).coerceIn(0f , 1f )
427+ } else {
428+ // Dragging up or at base position - keep alpha at 1
429+ 1f
430+ }
352431 dimAlpha.value = alpha
353432 }
354433 }
@@ -357,12 +436,17 @@ private fun DragHandleArea(
357436 contentAlignment = Alignment .Center
358437 ) {
359438 // Drag handle indicator
439+ val handleAlpha = lerp(0.2f , 0.35f , isPressing.floatValue)
440+
360441 Box (
361442 modifier = Modifier
362- .width(45 .dp)
443+ .width(pressWidth.value .dp)
363444 .height(4 .dp)
445+ .graphicsLayer {
446+ scaleY = pressScale.value
447+ }
364448 .clip(G2RoundedCornerShape (2 .dp))
365- .background(dragHandleColor)
449+ .background(dragHandleColor.copy(alpha = handleAlpha) )
366450 )
367451 }
368452}
@@ -434,5 +518,5 @@ object SuperBottomSheetDefaults {
434518 /* *
435519 * The default margin inside the [SuperBottomSheet].
436520 */
437- val insideMargin = DpSize (24 .dp, 24 .dp)
521+ val insideMargin = DpSize (24 .dp, 0 .dp)
438522}
0 commit comments