Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.revenuecat.placeholderdemo
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -40,6 +41,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.revenuecat.placeholder.PlaceholderDefaults
import com.revenuecat.placeholder.PlaceholderHighlight
import com.revenuecat.placeholder.PlaceholderTransitions
import com.revenuecat.placeholder.placeholder
import kotlinx.coroutines.delay

Expand All @@ -63,34 +65,43 @@ class MainActivity : ComponentActivity() {
Item(
enabled = enabled,
highlight = PlaceholderDefaults.shimmer,
transitionSpec = PlaceholderTransitions.fast,
)

Item(
enabled = enabled,
highlight = PlaceholderDefaults.fade,
transitionSpec = PlaceholderTransitions.smooth,
)

Item(
enabled = enabled,
highlight = PlaceholderDefaults.pulse,
transitionSpec = PlaceholderTransitions.snappy,
)

Item(
enabled = enabled,
highlight = PlaceholderDefaults.lightReveal,
transitionSpec = PlaceholderTransitions.bouncy,
)

Item(
enabled = enabled,
highlight = PlaceholderDefaults.circularReveal,
transitionSpec = PlaceholderTransitions.tween(100),
)
}
}
}
}

@Composable
private fun Item(enabled: Boolean, highlight: PlaceholderHighlight) {
private fun Item(
enabled: Boolean,
highlight: PlaceholderHighlight,
transitionSpec: () -> FiniteAnimationSpec<Float>,
) {
Row(
modifier = Modifier
.fillMaxWidth()
Expand All @@ -101,6 +112,8 @@ private fun Item(enabled: Boolean, highlight: PlaceholderHighlight) {
.placeholder(
enabled = enabled,
highlight = highlight,
contentFadeTransitionSpec = transitionSpec,
placeholderFadeTransitionSpec = transitionSpec,
)
.background(Color.Green)
.size(64.dp)
Expand All @@ -115,6 +128,8 @@ private fun Item(enabled: Boolean, highlight: PlaceholderHighlight) {
.placeholder(
enabled = enabled,
highlight = highlight,
contentFadeTransitionSpec = transitionSpec,
placeholderFadeTransitionSpec = transitionSpec,
),
text = "Hello",
)
Expand Down
14 changes: 14 additions & 0 deletions placeholder/api/android/placeholder.api
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ public abstract interface class com/revenuecat/placeholder/PlaceholderHighlight
public abstract fun getAnimationSpec ()Landroidx/compose/animation/core/InfiniteRepeatableSpec;
}

public final class com/revenuecat/placeholder/PlaceholderTransitions {
public static final field $stable I
public static final field INSTANCE Lcom/revenuecat/placeholder/PlaceholderTransitions;
public final fun custom (FF)Lkotlin/jvm/functions/Function0;
public static synthetic fun custom$default (Lcom/revenuecat/placeholder/PlaceholderTransitions;FFILjava/lang/Object;)Lkotlin/jvm/functions/Function0;
public final fun getBouncy ()Lkotlin/jvm/functions/Function0;
public final fun getFast ()Lkotlin/jvm/functions/Function0;
public final fun getNormal ()Lkotlin/jvm/functions/Function0;
public final fun getSlow ()Lkotlin/jvm/functions/Function0;
public final fun getSmooth ()Lkotlin/jvm/functions/Function0;
public final fun getSnappy ()Lkotlin/jvm/functions/Function0;
public final fun tween (I)Lkotlin/jvm/functions/Function0;
}

public final class com/revenuecat/placeholder/Pulse : com/revenuecat/placeholder/PlaceholderHighlight {
public static final field $stable I
public synthetic fun <init> (JLandroidx/compose/animation/core/InfiniteRepeatableSpec;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down
14 changes: 14 additions & 0 deletions placeholder/api/desktop/placeholder.api
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ public abstract interface class com/revenuecat/placeholder/PlaceholderHighlight
public abstract fun getAnimationSpec ()Landroidx/compose/animation/core/InfiniteRepeatableSpec;
}

public final class com/revenuecat/placeholder/PlaceholderTransitions {
public static final field $stable I
public static final field INSTANCE Lcom/revenuecat/placeholder/PlaceholderTransitions;
public final fun custom (FF)Lkotlin/jvm/functions/Function0;
public static synthetic fun custom$default (Lcom/revenuecat/placeholder/PlaceholderTransitions;FFILjava/lang/Object;)Lkotlin/jvm/functions/Function0;
public final fun getBouncy ()Lkotlin/jvm/functions/Function0;
public final fun getFast ()Lkotlin/jvm/functions/Function0;
public final fun getNormal ()Lkotlin/jvm/functions/Function0;
public final fun getSlow ()Lkotlin/jvm/functions/Function0;
public final fun getSmooth ()Lkotlin/jvm/functions/Function0;
public final fun getSnappy ()Lkotlin/jvm/functions/Function0;
public final fun tween (I)Lkotlin/jvm/functions/Function0;
}

public final class com/revenuecat/placeholder/Pulse : com/revenuecat/placeholder/PlaceholderHighlight {
public static final field $stable I
public synthetic fun <init> (JLandroidx/compose/animation/core/InfiniteRepeatableSpec;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright (c) 2025 RevenueCat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.revenuecat.placeholder

import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring

/**
* Provides preset animation specifications for placeholder fade-in/fade-out transitions.
*
* These presets control how smoothly the placeholder appears and disappears, separate from
* the highlight animation effects (like shimmer or fade). Use these with the
* [placeholderFadeTransitionSpec] and [contentFadeTransitionSpec] parameters.
*
* Example usage:
* ```kotlin
* Box(
* modifier = Modifier
* .size(200.dp, 50.dp)
* .placeholder(
* visible = isLoading,
* highlight = PlaceholderDefaults.shimmer,
* placeholderFadeTransitionSpec = PlaceholderTransitions.fast,
* contentFadeTransitionSpec = PlaceholderTransitions.smooth
* )
* )
* ```
*
* The difference between these and [PlaceholderDefaults]:
* - **PlaceholderDefaults** (shimmer, fade, pulse) = The animated effect shown **on** the placeholder
* - **PlaceholderTransitions** = How the placeholder itself appears/disappears
*
* @see PlaceholderDefaults for highlight effect presets
*/
public object PlaceholderTransitions {

/**
* Fast transition preset for quick fade in/out (200ms).
*
* Uses a tween animation, ideal for scenarios where content loads quickly
* and you want minimal delay.
*
* **Best for:**
* - Quick API responses
* - Cached content
* - Minimal loading states
*/
public val fast: () -> FiniteAnimationSpec<Float> = {
androidx.compose.animation.core.tween(durationMillis = 200)
}

/**
* Normal transition preset with balanced spring physics.
*
* Uses default spring animation, providing natural and responsive motion.
* This is the library's default behavior.
*
* **Best for:**
* - Standard loading scenarios
* - General-purpose placeholders
* - Most use cases
*/
public val normal: () -> FiniteAnimationSpec<Float> = { spring() }

/**
* Smooth transition preset with gentle, elegant motion.
*
* Uses low stiffness spring for gradual transitions that feel refined.
*
* **Best for:**
* - Premium app experiences
* - Image-heavy content
* - When aesthetics matter
*/
public val smooth: () -> FiniteAnimationSpec<Float> = {
spring(stiffness = Spring.StiffnessLow)
}

/**
* Slow transition preset for deliberate fade in/out (800ms).
*
* Uses a tween animation, creating a pronounced loading effect.
*
* **Best for:**
* - Long-running operations
* - Educational or onboarding flows
* - Emphasizing loading state
*/
public val slow: () -> FiniteAnimationSpec<Float> = {
androidx.compose.animation.core.tween(durationMillis = 800)
}

/**
* Snappy transition preset with instant, energetic motion.
*
* Uses high stiffness spring for quick, responsive feel.
*
* **Best for:**
* - Interactions needing instant feedback
* - Small UI elements
* - When responsiveness is critical
*/
public val snappy: () -> FiniteAnimationSpec<Float> = {
spring(stiffness = Spring.StiffnessHigh)
}

/**
* Bouncy transition preset with playful overshoot.
*
* Uses low damping spring that bounces slightly before settling.
* Adds personality to transitions.
*
* **Best for:**
* - Playful or casual apps
* - Gaming or entertainment
* - Adding character to loading
*/
public val bouncy: () -> FiniteAnimationSpec<Float> = {
spring(
dampingRatio = Spring.DampingRatioLowBouncy,
stiffness = Spring.StiffnessMedium,
)
}

/**
* Creates a custom spring transition with specific damping and stiffness.
*
* @param dampingRatio Controls oscillation (higher = less bounce)
* @param stiffness Controls speed (higher = faster)
*/
public fun custom(
dampingRatio: Float = Spring.DampingRatioNoBouncy,
stiffness: Float = Spring.StiffnessMedium,
): () -> FiniteAnimationSpec<Float> = {
spring(dampingRatio = dampingRatio, stiffness = stiffness)
}

/**
* Creates a custom tween transition with specific duration.
*
* @param durationMillis Duration of the transition in milliseconds
*/
public fun tween(durationMillis: Int): () -> FiniteAnimationSpec<Float> = {
androidx.compose.animation.core.tween(durationMillis = durationMillis)
}
}
Loading