### Basic KaraokeLyricsView Setup
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/guides/karaoke-lyrics
Minimal setup for KaraokeLyricsView, requiring a scroll state, lyrics data, current position lambda, and click/press callbacks.
```kotlin
@Composable
fun LyricsPanel(
lyrics: SyncedLyrics,
currentPosition: () -> Int,
onSeek: (Int) -> Unit,
onShare: (ISyncedLine) -> Unit
) {
val listState = rememberLazyListState()
KaraokeLyricsView(
listState = listState,
lyrics = lyrics,
currentPosition = currentPosition,
onLineClicked = { line -> onSeek(line.start) },
onLinePressed = { line -> onShare(line) }
)
}
```
--------------------------------
### Basic KaraokeLyricsView Setup
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/quickstart
Shows the basic parameters for configuring KaraokeLyricsView, including options for displaying translations and phonetic text.
```kotlin
showTranslation = true, // Show translation layer if present
showPhonetic = true, // Show phonetic layer (e.g. pinyin)
modifier = Modifier.fillMaxSize()
```
--------------------------------
### Seek to Line on Click
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/guides/karaoke-lyrics
Handle line clicks by seeking the media player to the start time of the clicked lyric line. The start time is provided in milliseconds.
```kotlin
onLineClicked = { line ->
player.seekTo(line.start.toLong())
}
```
--------------------------------
### Modify Color Channels
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/color-brush-utils
Examples demonstrating how to use Color.copyHsl to adjust lightness, saturation, hue, and alpha of a brand color.
```kotlin
// Make a color 20 % lighter (lightness 0.0–1.0, so +0.2 moves toward white)
val lighter = brandColor.copyHsl(lightness = 0.7f)
// Desaturate to a neutral gray (preserves lightness)
val gray = brandColor.copyHsl(saturation = 0f)
// Shift hue to 210 degrees (a cool blue tint)
val shifted = brandColor.copyHsl(hue = 210f)
// Reduce opacity while keeping color
val faded = brandColor.copyHsl(alpha = 0.4f)
```
--------------------------------
### Import KaraokeLyricsView for Verification
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/installation
Verify the installation by importing the main composable, KaraokeLyricsView, into a Kotlin file. A successful import indicates correct dependency configuration.
```kotlin
import com.mocharealm.accompanist.lyrics.ui.composable.lyrics.KaraokeLyricsView
```
--------------------------------
### Custom Easing Example with NewtonPolynomialInterpolationEasing
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/easing-functions
Demonstrates how to create and use a custom easing function with Compose's Animatable or animateFloatAsState. The easing is applied via the animationSpec.
```kotlin
val myEasing = NewtonPolynomialInterpolationEasing(
0.0 to 0.0,
0.3 to 0.8,
1.0 to 1.0
)
// Use with Compose Animatable or animateFloatAsState:
val progress by animateFloatAsState(
targetValue = 1f,
animationSpec = tween(durationMillis = 600, easing = myEasing)
)
```
--------------------------------
### Apple Music TTML Example
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/concepts/lyric-formats
This TTML excerpt demonstrates word-timed lyrics and background vocals using the 'x-bg' role. Ensure the 'itunes:timing="Word"' attribute is present for syllable-level timing.
```xml
Dun-dun, dun-dun-dun
Dun-dun, dun-dun-dun
It was just two lovers Mm, ooh
```
--------------------------------
### LYS Format Example
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/concepts/lyric-formats
This is an example of the LYS format, which is native to the Accompanist ecosystem. It encodes syllable-level timing compactly on a single line per lyric phrase, with each word followed by its (start_ms, duration_ms) pair. Voice identifiers appear at the start of each line.
```lys
[4]I (0,214)promise (214,345)that (559,185)you'll (744,154)never (898,334)find (1232,202)another (1434,470)like (1904,363)me(2267,658)
[4]I (3476,185)know (3661,150)that (3811,161)I'm (3972,184)a (4156,155)handful, (4311,672)baby, (4983,672)uh(5655,401)
[4]I (6113,213)know (6326,237)I (6563,165)never (6728,293)think (7021,339)before (7360,649)I (8009,113)jump(8122,563)
[7]And (11407,134)there's (11541,178)a (11719,125)lot (11844,100)of (11944,245)cool (12189,309)chicks (12498,399)out (12897,220)there(13117,758)
```
--------------------------------
### Custom Text Styles for Main and Accompaniment Lines
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/guides/multivoice-duet
This example demonstrates how to configure distinct text styles for the primary vocal line and the accompaniment line within KaraokeLyricsView. It sets different font sizes and font weights to visually differentiate the two.
```kotlin
KaraokeLyricsView(
listState = listState,
lyrics = lyrics,
currentPosition = currentPositionProvider,
onLineClicked = { onSeekTo(it.start) },
onLinePressed = { /* ... */ },
// Primary voice — large and prominent
normalLineTextStyle = TextStyle(
fontSize = 34.sp,
fontWeight = FontWeight.Bold,
textMotion = TextMotion.Animated
),
// Background voice — smaller, visually secondary
accompanimentLineTextStyle = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textMotion = TextMotion.Animated
)
)
```
--------------------------------
### Custom DipAndRise Easing
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/easing-functions
Example of creating a custom DipAndRise easing instance with modified parameters for a gentler animation.
```kotlin
val gentleRise = DipAndRise(dip = 0.1, rise = 1.0)
```
--------------------------------
### LRC Lyric Format Example
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/concepts/lyric-formats
This LRC example shows line-based timestamps. This format is supported for convenience but does not enable syllable-level animations.
```lrc
[00:04.11]It was just two lovers
[00:06.09]Sittin' in the car, listening to Blonde,
[00:07.98]fallin' for each other
[00:09.84]Pink and orange skies,
[00:10.95]feelin' super childish,
```
--------------------------------
### Configuring KaraokeLyricsView with Translations
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/guides/translations-phonetics
This example demonstrates how to enable or disable the display of line-level translations globally for the KaraokeLyricsView by setting the `showTranslation` flag.
```kotlin
KaraokeLyricsView(
listState = listState,
lyrics = lyrics,
currentPosition = currentPositionProvider,
onLineClicked = { onSeekTo(it.start) },
onLinePressed = { /* ... */ },
showTranslation = true // set to false to hide all translations
)
```
--------------------------------
### Standard Interactive Lyric Line
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/lyrics-line-item
Example of an interactive lyric line that can be focused and clicked for seeking. It uses KaraokeLineText to display the lyric content.
```kotlin
@Composable
fun MyLyricLine(
line: KaraokeLine.MainKaraokeLine,
isFocused: Boolean,
blurRadius: () -> Float,
onSeek: (Int) -> Unit
) {
LyricsLineItem(
isFocused = isFocused,
isRightAligned = false,
onLineClicked = { onSeek(line.start) },
onLinePressed = { /* show context menu */ },
blurRadius = blurRadius,
blendMode = BlendMode.Plus
) {
KaraokeLineText(
line = line,
currentTimeProvider = { /* player position */ 0 },
activeColor = Color.White,
blendMode = BlendMode.Plus
)
}
}
```
--------------------------------
### Providing Current Position for Karaoke Animation
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/guides/karaoke-lyrics
Example demonstrating how to provide a stable lambda for the current playback position to ensure smooth, frame-locked animation without recomposition. Updates the position state on each vsync.
```kotlin
@Composable
fun PlayerScreen(playerViewModel: PlayerViewModel) {
val listState = rememberLazyListState()
// 1. A mutable long that can be updated off the main thread's composition cycle
val animatedPositionState = remember { mutableLongStateOf(0L) }
// 2. A stable lambda captured once — reading the state happens only when the
// lambda is *invoked* inside the Canvas, not at composition time
val currentPositionProvider = remember {
{ animatedPositionState.longValue.toInt() }
}
val uiState = playerViewModel.uiState.collectAsState()
val isPlaying by remember { derivedStateOf { uiState.value.playbackState.isPlaying } }
// 3. A frame loop that updates the position state every vsync
LaunchedEffect(isPlaying) {
if (isPlaying) {
while (true) {
val playbackState = uiState.value.playbackState
val elapsed = System.currentTimeMillis() - playbackState.lastUpdateTime
val newPosition = (playbackState.position + elapsed)
.coerceAtMost(playbackState.duration)
// Only write when necessary to avoid thrashing
if (animatedPositionState.longValue <= newPosition ||
kotlin.math.abs(newPosition - animatedPositionState.longValue) >= 100
) {
animatedPositionState.longValue = newPosition
}
awaitFrame() // suspend until the next vsync frame
}
} else {
animatedPositionState.longValue = uiState.value.playbackState.position
}
}
KaraokeLyricsView(
listState = listState,
lyrics = lyrics,
currentPosition = currentPositionProvider,
onLineClicked = { line -> playerViewModel.seekTo(line.start) },
onLinePressed = { line -> /* show share sheet */ }
)
}
```
--------------------------------
### Lyrics Measurement and Rendering Pass
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/wrapped-line
A typical usage example demonstrating the sequence of operations for measuring syllables, wrapping them into lines, assigning static layouts, and computing RowRenderData within a LaunchedEffect. This shows the integration of WrappedLine and RowRenderData.
```kotlin
// 1. Measure syllables
val syllableLayouts = measureSyllablesAndDetermineAnimation(
syllables = line.syllables,
textMeasurer = textMeasurer,
style = lyricsStyle,
phoneticStyle = phoneticStyle,
isAccompanimentLine = line.isAccompaniment,
spaceWidth = spaceWidth
)
// 2. Wrap into rows → List
val wrappedLines = calculateBalancedLines(
syllableLayouts = syllableLayouts,
availableWidthPx = canvasWidthPx,
textMeasurer = textMeasurer,
style = lyricsStyle
)
// 3. Assign positions → List>
val lineLayouts = calculateStaticLineLayout(
wrappedLines = wrappedLines,
isLineRightAligned = isRightAligned,
canvasWidth = canvasWidthPx,
lineHeight = lineHeightPx,
phoneticHeight = phoneticHeightPx,
isRtl = isRtl
)
// 4. Compute render data → List
val rowRenderData = calculateRowRenderData(
lineLayouts = lineLayouts,
showPhonetic = showPhonetic,
density = density.density
)
// rowRenderData is now ready for the drawing layer
```
--------------------------------
### RTL Auto-Detection Example
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/synced-line-text
Demonstrates how to automatically detect if a line of text is right-to-left using the isRtl() utility function. This is typically done within KaraokeLyricsView or a custom layout.
```kotlin
// Inside KaraokeLyricsView
val isLineRtl = remember(line.content) { line.content.isRtl() }
```
--------------------------------
### Run Desktop JVM Target for Lyrics Preview
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/sample/overview
Execute the sample's desktop entry point to preview lyrics rendering on a JVM target without needing an Android device. This is useful for quick visual checks of the KaraokeLyricsView.
```bash
./gradlew :sample:run
```
--------------------------------
### Clone and Build Accompanist from Source
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/sample/overview
Clone the Accompanist repository and build the sample application from the command line. This is useful for development or if you prefer building from source.
```bash
git clone https://github.com/6xingyv/Accompanist.git
cd Accompanist
# Open in Android Studio, or run from CLI:
./gradlew :sample:installDebug
```
--------------------------------
### KaraokeAlignment Enum Definition
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/concepts/lyrics-model
Defines the horizontal anchor points for lyric lines. Use Start for leading edge alignment, End for trailing edge alignment (e.g., second voice in duets), and Unspecified which defaults to Start.
```kotlin
enum class KaraokeAlignment {
Start,
End,
Unspecified
}
```
--------------------------------
### Spring Animation Specification Logic
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/spring-placement-modifier
Illustrates the internal logic for selecting the animation specification, defaulting to snap() for the first frame or manual scrolling, and spring() otherwise.
```kotlin
val spec = if (isFirstFrame || isManualScrolling) snap()
else spring(dampingRatio = 0.95f, stiffness = stiffness)
```
--------------------------------
### Character Staggering Calculation
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/concepts/animations
Calculates the animation start time for individual characters within a word to distribute animations evenly.
```kotlin
val charRatio = if (n > 1) i.toFloat() / (n - 1) else 0.5f
val awesomeStartTime = earliestStart + (latestStart - earliestStart) * charRatio
```
--------------------------------
### ISyncedLine Interface
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/concepts/lyrics-model
The common interface for all line types, providing the time window (start and end in milliseconds) during which a line is active.
```kotlin
interface ISyncedLine {
val start: Int // milliseconds
val end: Int // milliseconds
}
```
--------------------------------
### Non-Interactive Accompaniment Line
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/lyrics-line-item
Example of a non-interactive sub-line, such as a decorative accompaniment element. It demonstrates setting various alpha values and disabling interactivity.
```kotlin
// Example of a non-interactive sub-line (e.g. a decorative accompaniment element)
LyricsLineItem(
isFocused = true,
isRightAligned = isRightAligned,
onLineClicked = { },
onLinePressed = { },
blurRadius = { 0f },
blendMode = blendMode,
activeAlpha = 0.6f,
inactiveAlpha = 0.2f,
isInteractive = false
) {
KaraokeLineText(
line = accompanimentLine,
currentTimeProvider = currentTimeProvider,
// ...
)
}
```
--------------------------------
### Implement Version Catalog Dependencies in build.gradle.kts
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/installation
Reference the declared accompanist libraries from your version catalog in your module's build.gradle.kts file.
```kotlin
dependencies {
implementation(libs.accompanist.lyrics.ui)
implementation(libs.accompanist.lyrics.core)
}
```
--------------------------------
### Spring Animation Configuration
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/concepts/animations
Configures the spring physics for list item placement with a fixed damping ratio and dynamic stiffness.
```kotlin
spring(dampingRatio = 0.95f, stiffness = stiffness)
```
--------------------------------
### Minimal Preview Composable for Verification
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/installation
Create a minimal preview composable to confirm that the KaraokeLyricsView can be rendered at design time, ensuring the dependency is fully resolved.
```kotlin
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.mocharealm.accompanist.lyrics.ui.composable.lyrics.KaraokeLyricsView
@Preview
@Composable
fun LyricsPreview() {
// KaraokeLyricsView requires a SyncedLyrics object —
// a successful import here confirms the dependency is resolved.
}
```
--------------------------------
### PlayerLyrics Composable with Glow Effect Setup
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/sample/player-screen
Configures the PlayerLyrics composable to render lyrics with a glow effect using additive blending and offscreen compositing.
```kotlin
@Composable
fun PlayerLyrics(
listState: LazyListState,
lyrics: SyncedLyrics?,
currentPosition: () -> Int,
showTranslation: Boolean,
showPhonetic: Boolean,
onSeekTo: (Int) -> Unit,
onShare: (KaraokeLine) -> Unit,
modifier: Modifier = Modifier
) {
if (lyrics == null) return
val currentTextStyle = LocalTextStyle.current
val sf = SFPro()
val normalStyle = remember(currentTextStyle) {
currentTextStyle.copy(
fontSize = 34.sp,
fontFamily = sf,
fontWeight = FontWeight.Bold,
textMotion = TextMotion.Animated,
)
}
val accompanimentStyle = remember(currentTextStyle) {
currentTextStyle.copy(
fontSize = 20.sp,
fontFamily = sf,
fontWeight = FontWeight.Bold,
textMotion = TextMotion.Animated,
)
}
KaraokeLyricsView(
listState = listState,
lyrics = lyrics,
currentPosition = currentPosition,
onLineClicked = { line -> onSeekTo(line.start) },
onLinePressed = { line -> onShare(line as KaraokeLine) },
showTranslation = showTranslation,
showPhonetic = showPhonetic,
normalLineTextStyle = normalStyle,
accompanimentLineTextStyle = accompanimentStyle,
modifier = modifier.graphicsLayer {
blendMode = BlendMode.Plus // additive blending = glow effect
compositingStrategy = CompositingStrategy.Offscreen // required for correct blend
},
useBlurEffect = false
)
}
```
--------------------------------
### Declare Libraries in gradle/libs.versions.toml
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/installation
Define the accompanist-lyrics-ui and accompanist-lyrics-core libraries in your Gradle version catalog for centralized version management.
```toml
[libraries]
accompanist-lyrics-ui = { module = "com.mocharealm.accompanist:lyrics-ui", version = "1.0.18" }
accompanist-lyrics-core = { module = "com.mocharealm.accompanist:lyrics-core", version = "0.4.4" }
```
--------------------------------
### Apple Music Glow Effect with KaraokeLyricsView
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/quickstart
Demonstrates how to achieve the Apple Music glow effect by using additive blending with `graphicsLayer` and `CompositingStrategy.Offscreen`.
```kotlin
Box(
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
) {
KaraokeLyricsView(
// ...
blendMode = BlendMode.Plus,
)
}
```
--------------------------------
### Basic KaraokeLyricsView Usage
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/karaoke-lyrics-view
Demonstrates how to use the KaraokeLyricsView composable within a Jetpack Compose screen. It requires a list of SyncedLyrics and a MediaPlayer to track the current playback position and handle seeking. Customization options for text styles, colors, effects, and layout are shown.
```kotlin
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextMotion
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
// Assuming SyncedLyrics, MediaPlayer, showShareSheet, and KaraokeLyricsView are defined elsewhere
@Composable
fun LyricsScreen(
lyrics: SyncedLyrics,
player: MediaPlayer
) {
val listState = rememberLazyListState()
KaraokeLyricsView(
listState = listState,
lyrics = lyrics,
currentPosition = { player.currentPositionMs },
onLineClicked = { line -> player.seekTo(line.start) },
onLinePressed = { line -> showShareSheet(line) },
modifier = Modifier.fillMaxSize(),
normalLineTextStyle = TextStyle(
fontSize = 34.sp,
fontWeight = FontWeight.Bold,
textMotion = TextMotion.Animated
),
accompanimentLineTextStyle = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textMotion = TextMotion.Animated
),
textColor = Color.White,
blendMode = BlendMode.Plus,
useBlurEffect = true,
showTranslation = true,
showPhonetic = true,
offset = 32.dp,
keepAliveZone = 100.dp,
blurDelta = 3f,
breathingDotsDefaults = KaraokeBreathingDotsDefaults(
number = 3,
size = 16.dp,
breathingDotsColor = Color.White
)
)
}
```
--------------------------------
### Standalone KaraokeLineText Demo
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/karaoke-line-text
Demonstrates how to use KaraokeLineText within a composable. It requires a KaraokeLine, a MediaPlayer for time tracking, and a TextMeasurer. Customizes text styles, colors, and display of translations and phonetics.
```kotlin
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextMotion
import androidx.compose.ui.unit.sp
import android.media.MediaPlayer
import androidx.compose.foundation.text.rememberTextMeasurer
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import com.example.accompanist.lyrics.ui.karaoke.KaraokeLine
import com.example.accompanist.lyrics.ui.karaoke.KaraokeLineText
import com.example.accompanist.lyrics.ui.karaoke.LyricsLineItem
@Composable
fun StandaloneKaraokeLineDemo(
line: KaraokeLine.MainKaraokeLine,
player: MediaPlayer
) {
val textMeasurer = rememberTextMeasurer()
LyricsLineItem(
isFocused = true,
isRightAligned = false,
onLineClicked = { player.seekTo(line.start) },
onLinePressed = { },
blurRadius = { 0f },
blendMode = BlendMode.Plus
) {
KaraokeLineText(
line = line,
currentTimeProvider = { player.currentPositionMs },
normalLineTextStyle = TextStyle(
fontSize = 34.sp,
fontWeight = FontWeight.Bold,
textMotion = TextMotion.Animated
),
accompanimentLineTextStyle = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textMotion = TextMotion.Animated
),
phoneticTextStyle = TextStyle(
fontSize = 13.sp,
fontWeight = FontWeight.Normal
),
activeColor = Color.White,
blendMode = BlendMode.Plus,
showTranslation = true,
showPhonetic = true,
textMeasurer = textMeasurer
)
}
}
```
--------------------------------
### Usage Example for Spring Placement Modifier
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/spring-placement-modifier
Demonstrates how to use the springPlacement modifier within a LazyColumn inside a LookaheadScope. Ensure the composable is a child of LookaheadScope to avoid runtime crashes.
```kotlin
LookaheadScope {
LazyColumn {
itemsIndexed(lyricLines, key = { _, line -> line.id }) {
index,
line ->
val distanceFromActive = abs(index - activeLine)
val stiffness = (120f - distanceFromActive * 10f).coerceAtLeast(20f)
LyricsLineItem(
line = line,
modifier = Modifier.springPlacement(
lookaheadScope = this@LookaheadScope,
itemKey = line.id,
isManualScrolling = isManualScrolling,
stiffness = stiffness
)
)
}
}
}
```
--------------------------------
### AccompanimentKaraokeLine Data Model
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/concepts/lyrics-model
Represents a background or harmony vocal line associated with a MainKaraokeLine. It has a smaller text size and reduced alpha, and is rendered above or below the main line based on its start time.
```kotlin
data class AccompanimentKaraokeLine(
override val start: Int,
override val end: Int,
val syllables: List,
val alignment: KaraokeAlignment,
val translation: String?,
val phonetic: String?
) : KaraokeLine()
```
--------------------------------
### Share Flow Initiation in MobilePlayerScreen
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/sample/player-screen
Details the sequence of actions performed when the onShare lambda is invoked. It pauses playback, prepares sharing context, and seeds the ShareViewModel.
```kotlin
onShare = {
line ->
lyrics?.let {
playerViewModel.onShareRequested() // pauses playback, shows the modal
val context = ShareContext(
lyrics = lyrics,
initialLine = line,
backgroundState = backgroundState,
title = currentMusicItem?.label ?: "Unknown Title",
artist = currentMusicItem?.testTarget?.split(" [")?.get(0) ?: "Unknown",
cover = cover // android.graphics.Bitmap for the share card
)
shareViewModel.prepareForSharing(context) // seeds ShareViewModel state
playerViewModel.onShareRequested()
}
}
```
--------------------------------
### Apply Customized Breathing Dots
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/guides/theming-styling
Applies a customized `KaraokeBreathingDotsDefaults` instance to `KaraokeLyricsView`, overriding default values for dot appearance and animation timing. This example uses a soft purple tint for the dots.
```kotlin
KaraokeLyricsView(
listState = listState,
lyrics = lyrics,
currentPosition = currentPositionProvider,
onLineClicked = { onSeekTo(it.start) },
onLinePressed = { /* ... */ },
breathingDotsDefaults = KaraokeBreathingDotsDefaults(
number = 3,
size = 20.dp,
margin = 10.dp,
enterDurationMs = 2000,
breathingDotsColor = Color(0xFFE8C4FF) // a soft purple tint
)
)
```
--------------------------------
### Spring Placement Implementation Classes
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/spring-placement-modifier
Defines the data class for carrying configuration and the actual node that runs the animation for spring placement.
```kotlin
data class SpringPlacementNodeElement(
val lookaheadScope: LookaheadScope,
val itemKey: Any,
val isManualScrolling: Boolean,
val stiffness: Float
) : ModifierNodeElement()
// The actual node that runs the animation
class SpringPlacementModifierNode(
var lookaheadScope: LookaheadScope,
var itemKey: Any,
var isManualScrolling: Boolean,
var stiffness: Float
) : ApproachLayoutModifierNode, Modifier.Node()
```
--------------------------------
### Add Dependencies to build.gradle.kts (KMP Module)
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/installation
For Kotlin Multiplatform modules, add the lyrics-ui and lyrics-core dependencies within the commonMain source set.
```kotlin
kotlin {
sourceSets {
commonMain {
dependencies {
implementation("com.mocharealm.accompanist:lyrics-ui:1.0.18")
implementation("com.mocharealm.accompanist:lyrics-core:0.4.4")
}
}
}
}
```
--------------------------------
### KaraokeBreathingDots Composable Function Signature
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/karaoke-breathing-dots
The main composable function for rendering animated breathing dots. It requires alignment, start and end times, and a current time provider. Customize its appearance and timing using the defaults parameter.
```kotlin
@Composable
fun KaraokeBreathingDots(
alignment: KaraokeAlignment,
startTimeMs: Int,
endTimeMs: Int,
currentTimeProvider: () -> Int,
modifier: Modifier = Modifier,
defaults: KaraokeBreathingDotsDefaults = KaraokeBreathingDotsDefaults()
)
```
--------------------------------
### Add Dependencies to build.gradle.kts
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/quickstart
Add the lyrics-ui and lyrics-core Gradle dependencies to your module's build.gradle.kts file. The lyrics-ui artifact provides the composable renderer, while lyrics-core offers the parser and the SyncedLyrics data model.
```kotlin
// build.gradle.kts (module)
dependencies {
implementation("com.mocharealm.accompanist:lyrics-ui:1.0.18")
implementation("com.mocharealm.accompanist:lyrics-core:0.4.4")
}
```
--------------------------------
### Add Dependencies to build.gradle.kts (Module)
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/installation
Include the lyrics-ui and lyrics-core artifacts in your module's build.gradle.kts file for standard Android projects.
```kotlin
dependencies {
implementation("com.mocharealm.accompanist:lyrics-ui:1.0.18")
implementation("com.mocharealm.accompanist:lyrics-core:0.4.4")
}
```
--------------------------------
### Deriving Background State in PlayerScreen
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/sample/player-screen
Shows how to derive the background visual state efficiently in PlayerScreen using derivedStateOf to avoid recomposing the entire screen when the background state changes.
```kotlin
val backgroundState by remember { derivedStateOf { uiStateState.value.backgroundState } }
FlowingLightBackground(
state = backgroundState,
modifier = Modifier.fillMaxSize()
)
```
--------------------------------
### Animated Visibility for Accompaniment Lines
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/guides/multivoice-duet
This snippet shows how to use AnimatedVisibility to control the appearance and disappearance of accompaniment lines. It defines a visibility state based on playback time and applies a combination of enter and exit transitions.
```kotlin
val isAccompanimentVisible by remember(bgLine) {
derivedStateOf {
val currentTime = currentTimeProvider()
currentTime >= (bgLine.start - 600) && currentTime <= (bgLine.end + 600)
}
}
AnimatedVisibility(
visible = isAccompanimentVisible,
enter = scaleIn(tween(600), transformOrigin = TransformOrigin(
if (isRightAligned) 1f else 0f,
if (isBefore) 1f else 0f
)) + fadeIn(tween(600)) + slideInVertically(tween(600)) + expandVertically(tween(600)),
exit = scaleOut(tween(600), transformOrigin = TransformOrigin(
if (isRightAligned) 1f else 0f,
if (isBefore) 1f else 0f
)) + fadeOut(tween(600)) + slideOutVertically(tween(600)) + shrinkVertically(tween(600))
) {
// AccompanimentKaraokeLine rendered here
}
```
--------------------------------
### Run the Frame Loop with LaunchedEffect
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/sample/player-screen
Launches a coroutine that updates the position at the display refresh rate by suspending until the next VSYNC signal.
```kotlin
LaunchedEffect(isPlaying) {
while (true) {
awaitFrame()
// Update position logic here
}
}
```
--------------------------------
### calculateBalancedLines
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/api/lyrics-layout-calculator
Wraps a flat list of SyllableLayout objects into display rows using a dynamic-programming line-breaking algorithm that minimizes the sum of squared end-of-line slack.
```APIDOC
## calculateBalancedLines
### Description
Wraps a flat list of `SyllableLayout` objects into display rows using a dynamic-programming line-breaking algorithm that minimises the sum of squared end-of-line slack (typographic *badness*).
### Method Signature
```kotlin
fun calculateBalancedLines(
syllableLayouts: List,
availableWidthPx: Float,
textMeasurer: TextMeasurer,
style: TextStyle
): List
```
### Parameters
#### Path Parameters
- **syllableLayouts** (List) - Required - The output of `measureSyllablesAndDetermineAnimation()`. Syllables must still be in lyric order.
- **availableWidthPx** (Float) - Required - Maximum row width in pixels. Syllables are placed until this limit would be exceeded, then a new row starts.
- **textMeasurer** (TextMeasurer) - Required - Used only by the internal `trimDisplayLineTrailingSpaces()` helper to re-measure trailing syllables after trailing whitespace is stripped.
- **style** (TextStyle) - Required - Passed through to `trimDisplayLineTrailingSpaces()`.
### Returns
`List` — each item holds the syllables for one display row and the row's total rendered width. Syllable positions are **not** set yet.
### Algorithm
The algorithm fills a `costs` array of size `n + 1` where `costs[i]` is the minimum total badness achievable when breaking the first `i` syllables optimally. Badness for a line ending at position `i` starting at position `j` is:
```
badness = (availableWidthPx − lineWidth)²
```
Word boundaries are respected — the inner loop skips break points that would split a word across lines. If the DP finds no finite-cost solution (e.g., a single word is wider than `availableWidthPx`), it falls back to `calculateGreedyWrappedLines()`, which breaks at word boundaries and syllable boundaries when no alternative exists.
For most lines the DP produces noticeably more even line lengths than greedy wrapping, which matters most for two- and three-line blocks where unbalanced rows look awkward.
```
--------------------------------
### PlayerScreen Composable with Frame-Loop Position Provider
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/sample/player-screen
Demonstrates how to implement a custom position provider for `KaraokeLyricsView` to avoid recompositions. It uses `mutableLongStateOf` and `awaitFrame` to update the position only within the Canvas, ensuring a smooth animation.
```kotlin
PlayerScreen.kt
@Composable
fun PlayerScreen(
playerViewModel: PlayerViewModel = koinViewModel(),
shareViewModel: ShareViewModel = koinViewModel(),
) {
val listState = rememberLazyListState()
val animatedPositionState = remember { mutableLongStateOf(0L) }
// A stable lambda captured once — only reads the State when KaraokeLyricsView calls it
val currentPositionProvider = remember {
{
animatedPositionState.longValue.toInt()
}
}
val uiStateState = playerViewModel.uiState.collectAsState()
val isPlaying by remember { derivedStateOf { uiStateState.value.playbackState.isPlaying } }
LaunchedEffect(isPlaying) {
if (isPlaying) {
while (true) {
val playbackState = uiStateState.value.playbackState
val elapsed = System.currentTimeMillis() - playbackState.lastUpdateTime
val newPosition = (playbackState.position + elapsed).coerceAtMost(
playbackState.duration
)
val currentAnimPos = animatedPositionState.longValue
if (currentAnimPos <= newPosition || abs(newPosition - currentAnimPos) >= 100) {
// Writing to MutableLongState does NOT recompose PlayerScreen —
// it only invalidates the DrawScope inside KaraokeLyricsView's Canvas
animatedPositionState.longValue = newPosition
}
awaitFrame() // suspend until the next Choreographer frame
}
} else {
animatedPositionState.longValue = uiStateState.value.playbackState.position
}
}
// ...
```
--------------------------------
### Customizing KaraokeLyricsView with Phonetic Style
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/guides/translations-phonetics
Demonstrates how to customize `normalLineTextStyle` and `phoneticTextStyle` when initializing `KaraokeLyricsView`.
```kotlin
KaraokeLyricsView(
listState = listState,
lyrics = lyrics,
currentPosition = currentPositionProvider,
onLineClicked = { onSeekTo(it.start) },
onLinePressed = { /* ... */ },
normalLineTextStyle = TextStyle(
fontSize = 34.sp,
fontWeight = FontWeight.Bold,
fontFamily = myFontFamily,
textMotion = TextMotion.Animated
),
phoneticTextStyle = TextStyle(
fontSize = 11.sp,
fontWeight = FontWeight.Normal,
fontFamily = myFontFamily,
letterSpacing = 0.05.em
)
)
```
--------------------------------
### Tablet/Desktop Layout Rendering
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/sample/player-screen
Renders the PadPlayerScreen composable for larger screen sizes like tablets and desktops (WindowLayoutType.Pad or other non-phone types).
```kotlin
else -> {
PadPlayerScreen(
listState = listState,
animatedPosition = currentPositionProvider,
playerViewModel = playerViewModel,
shareViewModel = shareViewModel,
backgroundState = backgroundState,
currentMusicItem = currentMusicItem,
showTranslation = showTranslation,
showPhonetic = showPhonetic,
lyrics = lyrics
)
}
```
--------------------------------
### Calculating Phonetic Height for Layout
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/guides/translations-phonetics
Illustrates how to calculate the `phoneticHeight` based on the `phoneticTextStyle` and use it in `calculateStaticLineLayout`.
```kotlin
val phoneticHeight = remember(phoneticTextStyle) {
textMeasurer.measure("M", phoneticTextStyle).size.height.toFloat()
}
val finalLineLayouts = remember(
wrappedLines, availableWidthPx, lineHeight, isLineRtl, isRightAligned, showPhonetic
) {
calculateStaticLineLayout(
wrappedLines = wrappedLines,
isLineRightAligned = isRightAligned,
canvasWidth = availableWidthPx,
lineHeight = lineHeight,
phoneticHeight = if (showPhonetic) phoneticHeight else 0f, // only add space when enabled
isRtl = isLineRtl
)
}
```
--------------------------------
### Render KaraokeLyricsView with Player Integration
Source: https://mintlify.wiki/6xingyv/accompanist-lyrics-ui/quickstart
Integrate KaraokeLyricsView into your composable hierarchy, providing the LazyListState, SyncedLyrics object, and lambdas for current playback position and line click events. The currentPosition lambda is read per frame without triggering recomposition, and onLineClicked allows for seek-to-line functionality.
```kotlin
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.mocharealm.accompanist.lyrics.ui.composable.lyrics.KaraokeLyricsView
@Composable
fun LyricsScreen(player: Player, syncedLyrics: SyncedLyrics) {
val listState = rememberLazyListState()
KaraokeLyricsView(
listState = listState,
lyrics = syncedLyrics,
currentPosition = { player.currentPosition.toInt() },
onLineClicked = { line -> player.seekTo(line.start.toLong()) },
onLinePressed = { line -> /* show share sheet or context menu */ },
modifier = Modifier.fillMaxSize()
)
}
```