Skip to content

Instantly share code, notes, and snippets.

@Abhimanyu14
Last active July 8, 2023 18:07
Show Gist options
  • Save Abhimanyu14/f7a7078c7aa1ef8507b0d20a4ca5aa57 to your computer and use it in GitHub Desktop.
Save Abhimanyu14/f7a7078c7aa1ef8507b0d20a4ca5aa57 to your computer and use it in GitHub Desktop.
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MyPullToRefreshLayout(
modifier: Modifier = Modifier,
isRefreshing: Boolean,
widthFraction: Float = 0.2F,
animationSpeedFactor: Int = 4,
defaultAnimationDuration: Int = 300,
iconPath: (Float) -> Path,
onRefresh: () -> Unit,
content: @Composable () -> Unit,
) {
val initialRefreshProgressValue: Float = 1F + widthFraction
val currentIsRefreshing by rememberUpdatedState(isRefreshing)
val refreshProgress = remember {
Animatable(initialValue = initialRefreshProgressValue)
}
var indicatorState: MyPullToRefreshIndicatorState by remember {
mutableStateOf(MyPullToRefreshIndicatorState.DEFAULT)
}
val state = rememberPullRefreshState(
refreshing = indicatorState == MyPullToRefreshIndicatorState.REFRESHING,
onRefresh = {
onRefresh()
indicatorState = MyPullToRefreshIndicatorState.REFRESHING
},
)
var cachedProgress by remember {
mutableFloatStateOf(0F)
}
LaunchedEffect(
key1 = state.progress,
key2 = indicatorState,
) {
if (state.progress != 0F) {
cachedProgress = min(state.progress, initialRefreshProgressValue)
}
if (state.progress > 0F && indicatorState == MyPullToRefreshIndicatorState.DEFAULT) {
indicatorState = MyPullToRefreshIndicatorState.PULL
} else if (state.progress == 0F && indicatorState == MyPullToRefreshIndicatorState.PULL) {
indicatorState = MyPullToRefreshIndicatorState.RELEASE
}
}
LaunchedEffect(
key1 = indicatorState,
) {
when (indicatorState) {
MyPullToRefreshIndicatorState.DEFAULT -> {}
MyPullToRefreshIndicatorState.PULL -> {}
MyPullToRefreshIndicatorState.RELEASE -> {
val progress = cachedProgress
changeFloatValueOverTime(
initialValue = progress,
targetValue = 0F,
durationMillis = if (cachedProgress < 1F) {
(defaultAnimationDuration * animationSpeedFactor * cachedProgress).toInt()
} else {
defaultAnimationDuration * animationSpeedFactor
},
onUpdate = {
cachedProgress = it
},
)
indicatorState = MyPullToRefreshIndicatorState.DEFAULT
}
MyPullToRefreshIndicatorState.REFRESHING -> {
while (currentIsRefreshing) {
refreshProgress.animateTo(
targetValue = 0F,
animationSpec = tween(
durationMillis = defaultAnimationDuration * animationSpeedFactor,
),
)
refreshProgress.animateTo(
targetValue = initialRefreshProgressValue,
animationSpec = tween(
durationMillis = defaultAnimationDuration * animationSpeedFactor,
),
)
}
indicatorState = MyPullToRefreshIndicatorState.REFRESH_COMPLETED
}
MyPullToRefreshIndicatorState.REFRESH_COMPLETED -> {
refreshProgress.animateTo(
targetValue = 0F,
animationSpec = tween(
durationMillis = defaultAnimationDuration * animationSpeedFactor,
),
)
indicatorState = MyPullToRefreshIndicatorState.DEFAULT
refreshProgress.animateTo(
targetValue = initialRefreshProgressValue,
animationSpec = snap(),
)
}
}
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier.pullRefresh(state = state),
) {
MyPullToRefreshIndicator(
iconPath = iconPath,
data = MyPullToRefreshIndicatorData(
pullProgress = cachedProgress,
refreshProgress = refreshProgress.value,
widthFraction = widthFraction,
state = indicatorState,
),
)
content()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment