Last active
July 8, 2023 18:13
-
-
Save Abhimanyu14/7fdda75d1c323901f674ee758c4636cc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @Immutable | |
| enum class MyPullToRefreshIndicatorState { | |
| DEFAULT, // No user interaction and no refreshing | |
| PULL, // User is pull the UI | |
| RELEASE, // User released the pull, but did not cross the threshold to refresh | |
| REFRESHING, // User crossed the threshold to refresh and released the pull | |
| REFRESH_COMPLETED, // Refreshing is completed - to perform state reset | |
| } | |
| @Immutable | |
| data class MyPullToRefreshIndicatorData( | |
| val pullProgress: Float, | |
| val refreshProgress: Float, | |
| val widthFraction: Float, | |
| val state: MyPullToRefreshIndicatorState, | |
| ) | |
| @Composable | |
| fun MyPullToRefreshIndicator( | |
| modifier: Modifier = Modifier, | |
| iconPath: (Float) -> Path, | |
| data: MyPullToRefreshIndicatorData, | |
| ) { | |
| val initialSizeFactor = 6.dp | |
| val fraction = when (data.state) { | |
| DEFAULT -> 0F | |
| PULL, RELEASE -> min(data.pullProgress, 1F) | |
| REFRESHING -> data.refreshProgress | |
| REFRESH_COMPLETED -> min(data.refreshProgress, 1F) | |
| } | |
| val sizeFactor = when (data.state) { | |
| DEFAULT -> initialSizeFactor | |
| PULL, RELEASE -> initialSizeFactor * (1 + fraction / 4) | |
| REFRESHING -> initialSizeFactor * (1 + min(fraction, 1F) / 4) | |
| REFRESH_COMPLETED -> initialSizeFactor * (1 + fraction / 4) | |
| } | |
| val sizeInPx = sizeFactor.dpToPx() | |
| val backgroundPathColor by animateColorAsState( | |
| targetValue = if (data.state == DEFAULT) { Color.Black } else { Color.LightGray }, | |
| label = "", | |
| ) | |
| Spacer( | |
| modifier = modifier | |
| .size(sizeFactor * 24) | |
| .drawWithCache { | |
| val path = iconPath(sizeInPx) | |
| val pathMeasure = PathMeasure() | |
| pathMeasure.setPath( path = path, forceClosed = false) | |
| val pathLength = pathMeasure.length * (1 + data.widthFraction) | |
| val loaderPath = Path() | |
| val startDistance = when (data.state) { | |
| DEFAULT -> 0F | |
| PULL, RELEASE -> { | |
| if (fraction < data.widthFraction) { | |
| 0F | |
| } else { | |
| pathLength * (data.pullProgress - data.widthFraction) | |
| } | |
| } | |
| REFRESHING -> { | |
| if (fraction < data.widthFraction) { | |
| 0F | |
| } else { | |
| pathLength * (data.refreshProgress - data.widthFraction) | |
| } | |
| } | |
| REFRESH_COMPLETED -> pathLength * fraction | |
| } | |
| val stopDistance = when (data.state) { | |
| DEFAULT -> 0F | |
| PULL, RELEASE, REFRESHING -> pathLength * fraction | |
| REFRESH_COMPLETED -> pathLength | |
| } | |
| onDrawBehind { | |
| pathMeasure.getSegment( | |
| startDistance = startDistance, | |
| stopDistance = stopDistance, | |
| destination = loaderPath, | |
| ) | |
| val style = Stroke( | |
| cap = StrokeCap.Round, | |
| join = StrokeJoin.Round, | |
| width = 4.dp.toPx(), | |
| ) | |
| // Background path | |
| drawPath( | |
| path = path, | |
| color = backgroundPathColor, | |
| style = style, | |
| ) | |
| // Loading path | |
| drawPath( | |
| path = loaderPath, | |
| color = Color.Black, | |
| style = style, | |
| ) | |
| } | |
| } | |
| ) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment