
Compose-based, highly customizable video player offering multi-quality stream switching, playback speed control, repeat toggle, metadata extraction, and pluggable UI components like loading indicators and settings.
A highly customizable, Jetpack Compose-based video player for Kotlin Multiplatform (KMP) and Android. Nexus Player KMP provides out-of-the-box support for multi-quality video switching, playback speed control, repeat toggling, and fully customizable UI components like loading indicators and settings menus.
Add the dependency to your module's build.gradle.kts file:
dependencies {
implementation("io.github.mamon-aburawi:nexus-player-kmp:{last_version}")
}
Using Nexus Player KMP requires setting up your video data and managing a few standard Compose states.
First, create your video sources using the VideoModule and VideoQuality data classes. This allows the player to know what resolutions are available.
import player.module.VideoModule
import player.module.VideoQuality
private fun getVideos(): List<VideoModule> {
return listOf(
VideoModule(
id = "1",
title = "Sample Video",
quality = listOf(
VideoQuality(
label = "360p",
tagValue = "SD",
byteValue = 200,
url = "https://res.cloudinary.com/dxgzgmju8/video/upload/v1772400078/Storm_360p_wtsr2j.mp4"
),
VideoQuality(
label = "480p",
tagValue = "SD",
byteValue = 200,
url = "https://res.cloudinary.com/dxgzgmju8/video/upload/v1772400233/Storm_480p_oexi4l.mp4"
),
VideoQuality(
label = "720p",
tagValue = "HD",
byteValue = 200,
url = "https://res.cloudinary.com/dxgzgmju8/video/upload/v1772400348/Storm_720p_sfxagn.mp4"
),
VideoQuality(
label = "1080p",
tagValue = "FHD",
byteValue = 200,
url = "https://res.cloudinary.com/dxgzgmju8/video/upload/v1772400621/Storm_1080p_xaq3cz.mp4"
)
)
)
)
}
In your Composable, set up the states needed to control the active video, quality selection, speed, and metadata.
val videoList = getVideos()
var currentVideoIndex by remember { mutableIntStateOf(0) }
var currentVideoQualityIndex by remember { mutableIntStateOf(0) }
val videoData = videoList[currentVideoIndex]
var repeatEnabled by remember { mutableStateOf(false) }
var currentVideoSpeed by remember { mutableStateOf(1.0f) }
var videoMetaData by remember { mutableStateOf<NexusVideoMetaData?>(null) }
Pass your states into the VideoPlayer composable. You can provide a custom loading indicator via onProgress.
VideoPlayer(
data = videoData,
currentVideoQualityIndex = currentVideoQualityIndex,
repeatEnabled = repeatEnabled,
currentSpeed = currentVideoSpeed,
onProgress = {
CircularProgressIndicator(color = Color.Yellow)
},
// Settings and callbacks defined in Step 4...
)
The onSettings lambda allows you to display a custom settings overlay. Use the built-in PlayerMenu to effortlessly handle quality selection, playback speed, and repeat toggles.
onSettings = { onDismiss ->
PlayerMenu(
videoMetadata = videoMetaData,
onDismiss = { onDismiss() },
currentQualityIndex = currentVideoQualityIndex,
qualities = videoData.quality,
onSelectQuality = { currentQuality ->
currentVideoQualityIndex = videoData.quality.indexOf(currentQuality)
},
currentSpeed = currentVideoSpeed,
onPlaybackSpeed = { newSpeed -> currentVideoSpeed = newSpeed },
repeatEnabled = repeatEnabled,
onRepeatToggle = { repeatEnabled = it }
)
},
onVideoComplete = {
println("Video Complete: ${videoData.title}")
},
onDataLoaded = { videoMeta ->
videoMetaData = videoMeta
}
Here is how everything comes together in a single UI component:
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import player.VideoPlayer
import player.component.PlayerMenu
import player.module.NexusVideoMetaData
import player.module.VideoModule
import player.module.VideoQuality
@Composable
fun HomeContent() {
val videoList = getVideos()
var currentVideoIndex by remember { mutableIntStateOf(0) }
var currentVideoQualityIndex by remember { mutableIntStateOf(0) }
val videoData = videoList[currentVideoIndex]
var repeatEnabled by remember { mutableStateOf(false) }
var currentVideoSpeed by remember { mutableStateOf(1.0f) }
var videoMetaData by remember { mutableStateOf<NexusVideoMetaData?>(null) }
VideoPlayer(
data = videoData,
currentVideoQualityIndex = currentVideoQualityIndex,
repeatEnabled = repeatEnabled,
currentSpeed = currentVideoSpeed,
onProgress = {
CircularProgressIndicator(color = Color.Yellow)
},
onSettings = { onDismiss ->
PlayerMenu(
videoMetadata = videoMetaData,
onDismiss = { onDismiss() },
currentQualityIndex = currentVideoQualityIndex,
qualities = videoData.quality,
onSelectQuality = { currentQuality ->
currentVideoQualityIndex = videoData.quality.indexOf(currentQuality)
},
currentSpeed = currentVideoSpeed,
onPlaybackSpeed = { newSpeed ->
currentVideoSpeed = newSpeed
},
repeatEnabled = repeatEnabled,
onRepeatToggle = {
repeatEnabled = it
}
)
},
onVideoComplete = {
println("VideoState: Video Complete ${videoData.title}")
},
onDataLoaded = { videoMeta ->
videoMetaData = videoMeta
}
)
}
// Ensure you include your getVideos() and getVideoQualities() functions here...
| Component | Description |
|---|---|
VideoPlayer |
The primary Composable that renders the video surface and handles playback state. |
PlayerMenu |
A pre-built UI component for adjusting stream quality, speed, and looping settings. |
VideoModule |
Data class containing the unique ID, title, and a list of VideoQuality streams. |
VideoQuality |
Data class outlining a specific video stream (URL, label, tag value, byte size). |
A highly customizable, Jetpack Compose-based video player for Kotlin Multiplatform (KMP) and Android. Nexus Player KMP provides out-of-the-box support for multi-quality video switching, playback speed control, repeat toggling, and fully customizable UI components like loading indicators and settings menus.
Add the dependency to your module's build.gradle.kts file:
dependencies {
implementation("io.github.mamon-aburawi:nexus-player-kmp:{last_version}")
}
Using Nexus Player KMP requires setting up your video data and managing a few standard Compose states.
First, create your video sources using the VideoModule and VideoQuality data classes. This allows the player to know what resolutions are available.
import player.module.VideoModule
import player.module.VideoQuality
private fun getVideos(): List<VideoModule> {
return listOf(
VideoModule(
id = "1",
title = "Sample Video",
quality = listOf(
VideoQuality(
label = "360p",
tagValue = "SD",
byteValue = 200,
url = "https://res.cloudinary.com/dxgzgmju8/video/upload/v1772400078/Storm_360p_wtsr2j.mp4"
),
VideoQuality(
label = "480p",
tagValue = "SD",
byteValue = 200,
url = "https://res.cloudinary.com/dxgzgmju8/video/upload/v1772400233/Storm_480p_oexi4l.mp4"
),
VideoQuality(
label = "720p",
tagValue = "HD",
byteValue = 200,
url = "https://res.cloudinary.com/dxgzgmju8/video/upload/v1772400348/Storm_720p_sfxagn.mp4"
),
VideoQuality(
label = "1080p",
tagValue = "FHD",
byteValue = 200,
url = "https://res.cloudinary.com/dxgzgmju8/video/upload/v1772400621/Storm_1080p_xaq3cz.mp4"
)
)
)
)
}
In your Composable, set up the states needed to control the active video, quality selection, speed, and metadata.
val videoList = getVideos()
var currentVideoIndex by remember { mutableIntStateOf(0) }
var currentVideoQualityIndex by remember { mutableIntStateOf(0) }
val videoData = videoList[currentVideoIndex]
var repeatEnabled by remember { mutableStateOf(false) }
var currentVideoSpeed by remember { mutableStateOf(1.0f) }
var videoMetaData by remember { mutableStateOf<NexusVideoMetaData?>(null) }
Pass your states into the VideoPlayer composable. You can provide a custom loading indicator via onProgress.
VideoPlayer(
data = videoData,
currentVideoQualityIndex = currentVideoQualityIndex,
repeatEnabled = repeatEnabled,
currentSpeed = currentVideoSpeed,
onProgress = {
CircularProgressIndicator(color = Color.Yellow)
},
// Settings and callbacks defined in Step 4...
)
The onSettings lambda allows you to display a custom settings overlay. Use the built-in PlayerMenu to effortlessly handle quality selection, playback speed, and repeat toggles.
onSettings = { onDismiss ->
PlayerMenu(
videoMetadata = videoMetaData,
onDismiss = { onDismiss() },
currentQualityIndex = currentVideoQualityIndex,
qualities = videoData.quality,
onSelectQuality = { currentQuality ->
currentVideoQualityIndex = videoData.quality.indexOf(currentQuality)
},
currentSpeed = currentVideoSpeed,
onPlaybackSpeed = { newSpeed -> currentVideoSpeed = newSpeed },
repeatEnabled = repeatEnabled,
onRepeatToggle = { repeatEnabled = it }
)
},
onVideoComplete = {
println("Video Complete: ${videoData.title}")
},
onDataLoaded = { videoMeta ->
videoMetaData = videoMeta
}
Here is how everything comes together in a single UI component:
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import player.VideoPlayer
import player.component.PlayerMenu
import player.module.NexusVideoMetaData
import player.module.VideoModule
import player.module.VideoQuality
@Composable
fun HomeContent() {
val videoList = getVideos()
var currentVideoIndex by remember { mutableIntStateOf(0) }
var currentVideoQualityIndex by remember { mutableIntStateOf(0) }
val videoData = videoList[currentVideoIndex]
var repeatEnabled by remember { mutableStateOf(false) }
var currentVideoSpeed by remember { mutableStateOf(1.0f) }
var videoMetaData by remember { mutableStateOf<NexusVideoMetaData?>(null) }
VideoPlayer(
data = videoData,
currentVideoQualityIndex = currentVideoQualityIndex,
repeatEnabled = repeatEnabled,
currentSpeed = currentVideoSpeed,
onProgress = {
CircularProgressIndicator(color = Color.Yellow)
},
onSettings = { onDismiss ->
PlayerMenu(
videoMetadata = videoMetaData,
onDismiss = { onDismiss() },
currentQualityIndex = currentVideoQualityIndex,
qualities = videoData.quality,
onSelectQuality = { currentQuality ->
currentVideoQualityIndex = videoData.quality.indexOf(currentQuality)
},
currentSpeed = currentVideoSpeed,
onPlaybackSpeed = { newSpeed ->
currentVideoSpeed = newSpeed
},
repeatEnabled = repeatEnabled,
onRepeatToggle = {
repeatEnabled = it
}
)
},
onVideoComplete = {
println("VideoState: Video Complete ${videoData.title}")
},
onDataLoaded = { videoMeta ->
videoMetaData = videoMeta
}
)
}
// Ensure you include your getVideos() and getVideoQualities() functions here...
| Component | Description |
|---|---|
VideoPlayer |
The primary Composable that renders the video surface and handles playback state. |
PlayerMenu |
A pre-built UI component for adjusting stream quality, speed, and looping settings. |
VideoModule |
Data class containing the unique ID, title, and a list of VideoQuality streams. |
VideoQuality |
Data class outlining a specific video stream (URL, label, tag value, byte size). |