diff --git a/example/src/commonMain/kotlin/MainPage.kt b/example/src/commonMain/kotlin/MainPage.kt index 2a7d53b0..fa8ecb7a 100644 --- a/example/src/commonMain/kotlin/MainPage.kt +++ b/example/src/commonMain/kotlin/MainPage.kt @@ -25,12 +25,13 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import component.OtherComponent import component.TextComponent +import component.otherComponent import top.yukonga.miuix.kmp.basic.BasicComponent import top.yukonga.miuix.kmp.basic.InputField import top.yukonga.miuix.kmp.basic.ScrollBehavior @@ -97,11 +98,11 @@ fun MainPage( superSwitchAnimState ) } - val otherComponent = @Composable { OtherComponent(padding) } BoxWithConstraints( modifier = Modifier.fillMaxSize() ) { + val focusManager = LocalFocusManager.current if (maxWidth < 840.dp) { LazyColumn( modifier = Modifier @@ -166,8 +167,8 @@ fun MainPage( if (!expanded) { item { textComponent() - otherComponent() } + otherComponent(focusManager, padding) } } } else { @@ -232,8 +233,8 @@ fun MainPage( } } if (!expanded) { + otherComponent(focusManager, padding) item { - otherComponent() Spacer(modifier = Modifier.height(6.dp)) } } diff --git a/example/src/commonMain/kotlin/component/OtherComponent.kt b/example/src/commonMain/kotlin/component/OtherComponent.kt index ab84f585..4cee2bca 100644 --- a/example/src/commonMain/kotlin/component/OtherComponent.kt +++ b/example/src/commonMain/kotlin/component/OtherComponent.kt @@ -20,9 +20,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -30,9 +30,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.graphics.Color import androidx.compose.ui.hapticfeedback.HapticFeedbackType -import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction @@ -100,32 +100,7 @@ import top.yukonga.miuix.kmp.utils.PressFeedbackType import top.yukonga.miuix.kmp.utils.SmoothRoundedCornerShape import kotlin.math.round -@Composable -fun OtherComponent(padding: PaddingValues) { - var buttonText by remember { mutableStateOf("Cancel") } - var submitButtonText by remember { mutableStateOf("Submit") } - var clickCount by remember { mutableStateOf(0) } - var submitClickCount by remember { mutableStateOf(0) } - val hapticFeedback = LocalHapticFeedback.current - val focusManager = LocalFocusManager.current - var text1 by remember { mutableStateOf("") } - var text2 by remember { mutableStateOf(TextFieldValue("")) } - var text3 by remember { mutableStateOf("") } - var progress by remember { mutableStateOf(0.5f) } - var progressHaptic by remember { mutableStateOf(0.5f) } - val animatedProgressValue by rememberInfiniteTransition().animateFloat( - initialValue = 0f, - targetValue = 1f, - animationSpec = infiniteRepeatable( - animation = tween(1000), - repeatMode = RepeatMode.Reverse - ) - ) - val progressValues = remember { listOf(0.0f, 0.25f, 0.5f, 0.75f, 1.0f, null) } - val progressDisable by remember { mutableStateOf(0.5f) } - val tabTexts = listOf("Tab 1", "Tab 2", "Tab 3", "Tab 4", "Tab 5", "Tab 6") - var selectedTabIndex by remember { mutableStateOf(0) } - var selectedTabIndex1 by remember { mutableStateOf(0) } +fun LazyListScope.otherComponent(focusManager: FocusManager, padding: PaddingValues) { val miuixIconsNormal = listOf( MiuixIcons.Useful.AddSecret, MiuixIcons.Useful.Back, @@ -168,323 +143,382 @@ fun OtherComponent(padding: PaddingValues) { MiuixIcons.Useful.Unstick, MiuixIcons.Useful.Update ) - val miuixColor = MiuixTheme.colorScheme.primary - var selectedColor by remember { mutableStateOf(miuixColor) } - SmallTitle(text = "Button") - Row( - modifier = Modifier - .padding(horizontal = 12.dp) - .padding(bottom = 12.dp), - horizontalArrangement = Arrangement.SpaceBetween - ) { - TextButton( - text = buttonText, - onClick = { - clickCount++ - buttonText = "Click: $clickCount" - }, - modifier = Modifier.weight(1f) - ) - Spacer(Modifier.width(12.dp)) - TextButton( - text = submitButtonText, - onClick = { - submitClickCount++ - submitButtonText = "Click: $submitClickCount" - }, - modifier = Modifier.weight(1f), - colors = ButtonDefaults.textButtonColorsPrimary() - ) + item { + var buttonText by remember { mutableStateOf("Cancel") } + var submitButtonText by remember { mutableStateOf("Submit") } + var clickCount by remember { mutableStateOf(0) } + var submitClickCount by remember { mutableStateOf(0) } + + SmallTitle(text = "Button") + Row( + modifier = Modifier + .padding(horizontal = 12.dp) + .padding(bottom = 12.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + TextButton( + text = buttonText, + onClick = { + clickCount++ + buttonText = "Click: $clickCount" + }, + modifier = Modifier.weight(1f) + ) + Spacer(Modifier.width(12.dp)) + TextButton( + text = submitButtonText, + onClick = { + submitClickCount++ + submitButtonText = "Click: $submitClickCount" + }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.textButtonColorsPrimary() + ) + } } - Row( - modifier = Modifier - .padding(horizontal = 12.dp) - .padding(bottom = 6.dp), - horizontalArrangement = Arrangement.SpaceBetween - ) { - TextButton( - text = "Disabled", - onClick = {}, - modifier = Modifier.weight(1f), - enabled = false - ) - Spacer(Modifier.width(12.dp)) - TextButton( - text = "Disabled", - onClick = {}, - enabled = false, - modifier = Modifier.weight(1f), - colors = ButtonDefaults.textButtonColorsPrimary() - ) + item { + Row( + modifier = Modifier + .padding(horizontal = 12.dp) + .padding(bottom = 6.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + TextButton( + text = "Disabled", + onClick = {}, + modifier = Modifier.weight(1f), + enabled = false + ) + Spacer(Modifier.width(12.dp)) + TextButton( + text = "Disabled", + onClick = {}, + enabled = false, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.textButtonColorsPrimary() + ) + } } - SmallTitle(text = "ProgressIndicator") - LinearProgressIndicator( - progress = animatedProgressValue, - modifier = Modifier - .padding(horizontal = 15.dp) - .padding(bottom = 12.dp) - ) - progressValues.forEach { progressValue -> + item { + SmallTitle(text = "ProgressIndicator") + val progressValues = listOf(0.0f, 0.25f, 0.5f, 0.75f, 1.0f, null) + val animatedProgressValue by rememberInfiniteTransition().animateFloat( + initialValue = 0f, + targetValue = 1f, + animationSpec = infiniteRepeatable( + animation = tween(1000), + repeatMode = RepeatMode.Reverse + ) + ) + LinearProgressIndicator( - progress = progressValue, + progress = animatedProgressValue, modifier = Modifier - .padding(horizontal = 15.dp) // Increased from 12.dp. + .padding(horizontal = 15.dp) .padding(bottom = 12.dp) ) - } - - FlowRow( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp) - .padding(bottom = 6.dp), - horizontalArrangement = Arrangement.SpaceEvenly, - ) { - CircularProgressIndicator( - progress = animatedProgressValue - ) progressValues.forEach { progressValue -> + LinearProgressIndicator( + progress = progressValue, + modifier = Modifier + .padding(horizontal = 15.dp) // Increased from 12.dp. + .padding(bottom = 12.dp) + ) + } + FlowRow( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp) + .padding(bottom = 6.dp), + horizontalArrangement = Arrangement.SpaceEvenly, + ) { CircularProgressIndicator( - progress = progressValue + progress = animatedProgressValue + ) + progressValues.forEach { progressValue -> + CircularProgressIndicator( + progress = progressValue + ) + } + InfiniteProgressIndicator( + modifier = Modifier + .align(alignment = Alignment.CenterVertically) ) } - InfiniteProgressIndicator( + } + + item { + var text1 by remember { mutableStateOf("") } + + SmallTitle(text = "TextField") + TextField( + value = text1, + onValueChange = { text1 = it }, modifier = Modifier - .align(alignment = Alignment.CenterVertically) + .padding(horizontal = 12.dp) + .padding(bottom = 12.dp), + keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done) ) } + item { + var text2 by remember { mutableStateOf(TextFieldValue("")) } - SmallTitle(text = "TextField") - TextField( - value = text1, - onValueChange = { text1 = it }, - modifier = Modifier - .padding(horizontal = 12.dp) - .padding(bottom = 12.dp), - keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done) - ) + TextField( + value = text2, + onValueChange = { text2 = it }, + label = "Text Field", + modifier = Modifier + .padding(horizontal = 12.dp) + .padding(bottom = 12.dp), + keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done) + ) + } - TextField( - value = text2, - onValueChange = { text2 = it }, - label = "Text Field", - modifier = Modifier - .padding(horizontal = 12.dp) - .padding(bottom = 12.dp), - keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done) - ) + item { + var text3 by remember { mutableStateOf("") } - TextField( - value = text3, - onValueChange = { text3 = it }, - label = "Placeholder & SingleLine", - useLabelAsPlaceholder = true, - singleLine = true, - modifier = Modifier - .padding(horizontal = 12.dp) - .padding(bottom = 6.dp), - keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done) - ) - - SmallTitle(text = "Slider") - Slider( - progress = progress, - onProgressChange = { newProgress -> progress = newProgress }, - decimalPlaces = 3, - modifier = Modifier - .padding(horizontal = 12.dp) - .padding(bottom = 12.dp) - ) + TextField( + value = text3, + onValueChange = { text3 = it }, + label = "Placeholder & SingleLine", + useLabelAsPlaceholder = true, + singleLine = true, + modifier = Modifier + .padding(horizontal = 12.dp) + .padding(bottom = 6.dp), + keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done) + ) + } - Slider( - progress = progressHaptic, - onProgressChange = { newProgress -> progressHaptic = newProgress }, - hapticEffect = SliderDefaults.SliderHapticEffect.Step, - modifier = Modifier - .padding(horizontal = 12.dp) - .padding(bottom = 12.dp) - ) + item { + SmallTitle(text = "Slider") + var progress by remember { mutableStateOf(0.5f) } + Slider( + progress = progress, + onProgressChange = { newProgress -> progress = newProgress }, + decimalPlaces = 3, + modifier = Modifier + .padding(horizontal = 12.dp) + .padding(bottom = 12.dp) + ) + } - Slider( - progress = progressDisable, - onProgressChange = {}, - enabled = false, - modifier = Modifier - .padding(horizontal = 12.dp) - .padding(bottom = 6.dp) - ) + item { + var progressHaptic by remember { mutableStateOf(0.5f) } + Slider( + progress = progressHaptic, + onProgressChange = { newProgress -> progressHaptic = newProgress }, + hapticEffect = SliderDefaults.SliderHapticEffect.Step, + modifier = Modifier + .padding(horizontal = 12.dp) + .padding(bottom = 12.dp) + ) + } - SmallTitle(text = "TabRow") - TabRow( - tabs = tabTexts, - selectedTabIndex = selectedTabIndex, - modifier = Modifier - .padding(horizontal = 12.dp) - .padding(bottom = 12.dp) - ) { - selectedTabIndex = it + item { + val progressDisable by remember { mutableStateOf(0.5f) } + Slider( + progress = progressDisable, + onProgressChange = {}, + enabled = false, + modifier = Modifier + .padding(horizontal = 12.dp) + .padding(bottom = 6.dp) + ) } - Card( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp) - .padding(bottom = 6.dp), - insideMargin = PaddingValues(16.dp) - ) { - TabRowWithContour( + + item { + SmallTitle(text = "TabRow") + val tabTexts = listOf("Tab 1", "Tab 2", "Tab 3", "Tab 4", "Tab 5", "Tab 6") + var selectedTabIndex by remember { mutableStateOf(0) } + var selectedTabIndex1 by remember { mutableStateOf(0) } + TabRow( tabs = tabTexts, - selectedTabIndex = selectedTabIndex1, + selectedTabIndex = selectedTabIndex, + modifier = Modifier + .padding(horizontal = 12.dp) + .padding(bottom = 12.dp) ) { - selectedTabIndex1 = it + selectedTabIndex = it } - Text( - text = "Selected Tab: ${tabTexts[selectedTabIndex1]}", - modifier = Modifier.padding(top = 12.dp) - ) - } - - SmallTitle(text = "Icon") - Card( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp) - .padding(bottom = 6.dp), - insideMargin = PaddingValues(16.dp) - ) { - FlowRow { - miuixIconsNormal.forEach { icon -> - Icon( - imageVector = icon, - contentDescription = null, - tint = if (icon != MiuixIcons.Useful.Like) MiuixTheme.colorScheme.onBackground else Color.Unspecified, - modifier = Modifier.size(24.dp) - ) + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp) + .padding(bottom = 6.dp), + insideMargin = PaddingValues(16.dp) + ) { + TabRowWithContour( + tabs = tabTexts, + selectedTabIndex = selectedTabIndex1, + ) { + selectedTabIndex1 = it } + Text( + text = "Selected Tab: ${tabTexts[selectedTabIndex1]}", + modifier = Modifier.padding(top = 12.dp) + ) } } - SmallTitle(text = "ColorPicker") - Card( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp) - .padding(bottom = 6.dp), - insideMargin = PaddingValues(16.dp) - ) { - Row( - modifier = Modifier.padding(bottom = 12.dp), - verticalAlignment = Alignment.CenterVertically + item { + SmallTitle(text = "Icon") + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp) + .padding(bottom = 6.dp), + insideMargin = PaddingValues(16.dp) ) { - Text( - text = "Selected Color:\nRGBA: " + - "${(selectedColor.red * 255).toInt()}," + - "${(selectedColor.green * 255).toInt()}," + - "${(selectedColor.blue * 255).toInt()}," + - "${(round(selectedColor.alpha * 100) / 100.0)}" + - "\nHEX: #" + - (selectedColor.alpha * 255).toInt().toString(16).padStart(2, '0').uppercase() + - (selectedColor.red * 255).toInt().toString(16).padStart(2, '0').uppercase() + - (selectedColor.green * 255).toInt().toString(16).padStart(2, '0').uppercase() + - (selectedColor.blue * 255).toInt().toString(16).padStart(2, '0').uppercase(), - modifier = Modifier.weight(1f) - ) - Spacer(Modifier.width(12.dp)) - Box( - modifier = Modifier - .height(60.dp) - .width(100.dp) - .align(Alignment.CenterVertically) - .clip(SmoothRoundedCornerShape(12.dp)) - .background(selectedColor) - ) + FlowRow { + miuixIconsNormal.forEach { icon -> + Icon( + imageVector = icon, + contentDescription = null, + tint = if (icon != MiuixIcons.Useful.Like) MiuixTheme.colorScheme.onBackground else Color.Unspecified, + modifier = Modifier.size(24.dp) + ) + } + } } - - ColorPicker( - initialColor = selectedColor, - onColorChanged = { selectedColor = it }, - showPreview = false, - hapticEffect = SliderDefaults.SliderHapticEffect.Step - ) } - SmallTitle(text = "Card") - Card( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp) - .padding(bottom = 12.dp), - color = MiuixTheme.colorScheme.primaryVariant, - insideMargin = PaddingValues(16.dp), - pressFeedbackType = PressFeedbackType.None, - showIndication = true, - onClick = { } - ) { - Text( - color = MiuixTheme.colorScheme.onPrimary, - text = "Card", - fontSize = 19.sp, - fontWeight = FontWeight.SemiBold - ) - Text( - color = MiuixTheme.colorScheme.onPrimaryVariant, - text = "ShowIndication: true", - fontSize = 17.sp, - fontWeight = FontWeight.Normal - ) - } + item { + SmallTitle(text = "ColorPicker") + val miuixColor = MiuixTheme.colorScheme.primary + var selectedColor by remember { mutableStateOf(miuixColor) } - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp) - .padding(bottom = 12.dp + padding.calculateBottomPadding()), - horizontalArrangement = Arrangement.spacedBy(12.dp) - ) { Card( - modifier = Modifier.weight(1f), - insideMargin = PaddingValues(16.dp), - pressFeedbackType = PressFeedbackType.Sink, - showIndication = true, - onClick = { }, - content = { + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp) + .padding(bottom = 6.dp), + insideMargin = PaddingValues(16.dp) + ) { + Row( + modifier = Modifier.padding(bottom = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { Text( - color = MiuixTheme.colorScheme.onSurface, - text = "Card", - fontSize = 18.sp, - fontWeight = FontWeight.Medium + text = "Selected Color:\nRGBA: " + + "${(selectedColor.red * 255).toInt()}," + + "${(selectedColor.green * 255).toInt()}," + + "${(selectedColor.blue * 255).toInt()}," + + "${(round(selectedColor.alpha * 100) / 100.0)}" + + "\nHEX: #" + + (selectedColor.alpha * 255).toInt().toString(16).padStart(2, '0') + .uppercase() + + (selectedColor.red * 255).toInt().toString(16).padStart(2, '0') + .uppercase() + + (selectedColor.green * 255).toInt().toString(16).padStart(2, '0') + .uppercase() + + (selectedColor.blue * 255).toInt().toString(16).padStart(2, '0') + .uppercase(), + modifier = Modifier.weight(1f) ) - Text( - color = MiuixTheme.colorScheme.onSurfaceVariantSummary, - text = "PressFeedback\nType: Sink", - style = MiuixTheme.textStyles.paragraph + Spacer(Modifier.width(12.dp)) + Box( + modifier = Modifier + .height(60.dp) + .width(100.dp) + .align(Alignment.CenterVertically) + .clip(SmoothRoundedCornerShape(12.dp)) + .background(selectedColor) ) } - ) + ColorPicker( + initialColor = selectedColor, + onColorChanged = { selectedColor = it }, + showPreview = false, + hapticEffect = SliderDefaults.SliderHapticEffect.Step + ) + } + } + + item { + SmallTitle(text = "Card") Card( - modifier = Modifier.weight(1f), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp) + .padding(bottom = 12.dp), + color = MiuixTheme.colorScheme.primaryVariant, insideMargin = PaddingValues(16.dp), - pressFeedbackType = PressFeedbackType.Tilt, - onLongPress = { hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) }, - content = { - Text( - color = MiuixTheme.colorScheme.onSurface, - text = "Card", - fontSize = 18.sp, - fontWeight = FontWeight.Medium - ) - Text( - color = MiuixTheme.colorScheme.onSurfaceVariantSummary, - text = "PressFeedback\nType: Tilt", - style = MiuixTheme.textStyles.paragraph - ) - } - ) + pressFeedbackType = PressFeedbackType.None, + showIndication = true, + onClick = { } + ) { + Text( + color = MiuixTheme.colorScheme.onPrimary, + text = "Card", + fontSize = 19.sp, + fontWeight = FontWeight.SemiBold + ) + Text( + color = MiuixTheme.colorScheme.onPrimaryVariant, + text = "ShowIndication: true", + fontSize = 17.sp, + fontWeight = FontWeight.Normal + ) + } + } + + item { + val hapticFeedback = LocalHapticFeedback.current + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp) + .padding(bottom = 12.dp + padding.calculateBottomPadding()), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Card( + modifier = Modifier.weight(1f), + insideMargin = PaddingValues(16.dp), + pressFeedbackType = PressFeedbackType.Sink, + showIndication = true, + onClick = { }, + content = { + Text( + color = MiuixTheme.colorScheme.onSurface, + text = "Card", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + Text( + color = MiuixTheme.colorScheme.onSurfaceVariantSummary, + text = "PressFeedback\nType: Sink", + style = MiuixTheme.textStyles.paragraph + ) + } + ) + + Card( + modifier = Modifier.weight(1f), + insideMargin = PaddingValues(16.dp), + pressFeedbackType = PressFeedbackType.Tilt, + onLongPress = { hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) }, + content = { + Text( + color = MiuixTheme.colorScheme.onSurface, + text = "Card", + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + Text( + color = MiuixTheme.colorScheme.onSurfaceVariantSummary, + text = "PressFeedback\nType: Tilt", + style = MiuixTheme.textStyles.paragraph + ) + } + ) + } } } \ No newline at end of file diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ProgressIndicator.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ProgressIndicator.kt index c3eeee9d..227aaef9 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ProgressIndicator.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ProgressIndicator.kt @@ -6,7 +6,10 @@ package top.yukonga.miuix.kmp.basic import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.keyframes +import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.fillMaxWidth @@ -137,11 +140,7 @@ fun LinearProgressIndicator( ) val minWidth = cornerRadius * 2 - val progressWidth = if (progressValue == 0f) { - minWidth - } else { - minWidth + (size.width - minWidth) * progressValue - } + val progressWidth = minWidth + (size.width - minWidth) * progressValue drawRoundRect( color = currentForegroundColor, @@ -175,31 +174,28 @@ fun CircularProgressIndicator( } if (progress == null) { - val rotationAnim = remember { Animatable(0f) } - val sweepAnim = remember { Animatable(30f) } + val transition = rememberInfiniteTransition() - LaunchedEffect(Unit) { - rotationAnim.animateTo( - targetValue = 360f, - animationSpec = infiniteRepeatable( - animation = tween(1000, easing = LinearEasing), - repeatMode = RepeatMode.Restart - ) + val rotationAnim by transition.animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable( + animation = tween(1000, easing = LinearEasing), + repeatMode = RepeatMode.Restart ) - } - - LaunchedEffect(Unit) { - while (true) { - sweepAnim.animateTo( - targetValue = 120f, - animationSpec = tween(800, easing = LinearEasing) - ) - sweepAnim.animateTo( - targetValue = 30f, - animationSpec = tween(800, easing = LinearEasing) - ) - } - } + ) + val sweepAnim by transition.animateFloat( + initialValue = 30f, + targetValue = 120f, + animationSpec = infiniteRepeatable( + animation = keyframes { + durationMillis = 1600 + 120f at 800 using LinearEasing + 30f at 1600 using LinearEasing + }, + repeatMode = RepeatMode.Restart + ) + ) Canvas(modifier = canvasModifier) { val currentBackgroundColor = colors.backgroundColor() @@ -216,43 +212,14 @@ fun CircularProgressIndicator( style = Stroke(width = strokeWidthPx) ) - val startAngle = rotationAnim.value - val sweepAngle = sweepAnim.value - drawArc( color = currentForegroundColor, - startAngle = startAngle, - sweepAngle = sweepAngle, + startAngle = rotationAnim, + sweepAngle = sweepAnim, useCenter = false, topLeft = Offset(strokeWidthPx / 2, strokeWidthPx / 2), size = Size(2 * radius, 2 * radius), - style = Stroke(width = strokeWidthPx) - ) - - val halfStroke = strokeWidthPx / 2 - - val startRadians = startAngle * (PI / 180f).toFloat() - val startCapCenter = Offset( - center.x + radius * cos(startRadians), - center.y + radius * sin(startRadians) - ) - - val endRadians = (startAngle + sweepAngle) * (PI / 180f).toFloat() - val endCapCenter = Offset( - center.x + radius * cos(endRadians), - center.y + radius * sin(endRadians) - ) - - drawCircle( - color = currentForegroundColor, - radius = halfStroke, - center = startCapCenter - ) - - drawCircle( - color = currentForegroundColor, - radius = halfStroke, - center = endCapCenter + style = Stroke(width = strokeWidthPx, cap = StrokeCap.Round), ) } } else { @@ -274,11 +241,7 @@ fun CircularProgressIndicator( val minSweepAngle = 0.1f - val sweepAngle = if (progressValue == 0f) { - minSweepAngle - } else { - minSweepAngle + (360f - minSweepAngle) * progressValue - } + val sweepAngle = minSweepAngle + (360f - minSweepAngle) * progressValue drawArc( color = currentForegroundColor, @@ -287,28 +250,7 @@ fun CircularProgressIndicator( useCenter = false, topLeft = Offset(strokeWidthPx / 2, strokeWidthPx / 2), size = Size(2 * radius, 2 * radius), - style = Stroke(width = strokeWidthPx) - ) - - // Draw start and end caps - val startCapCenter = Offset(center.x, center.y - radius) - - val endRadians = (-90f + sweepAngle) * (PI / 180f).toFloat() - val endCapCenter = Offset( - center.x + radius * cos(endRadians), - center.y + radius * sin(endRadians) - ) - - drawCircle( - color = currentForegroundColor, - radius = strokeWidthPx / 2, - center = startCapCenter - ) - - drawCircle( - color = currentForegroundColor, - radius = strokeWidthPx / 2, - center = endCapCenter + style = Stroke(width = strokeWidthPx, cap = StrokeCap.Round) ) } }