Compose-Based Inline Player Overview
This section provides a technical overview of implementing inline video players using Jetpack Compose and declarative UI patterns.
Technical Architecture
The Compose-based implementation leverages declarative UI principles with reactive state management. It provides modern Compose integration while maintaining full native performance and capabilities.
Main Composable Function
BlazeVideosInlinePlayerCompose- Core composable for rendering the player- Integrates seamlessly with Compose lifecycle and state management
- Uses AndroidView interop for native player performance
State Handler Component
BlazeVideosInlinePlayerComposeStateHandler- Manages player state and interactions- Acts as single source of truth for player state
- Provides reactive state updates for UI changes
Compose Integration
- Declarative state-driven UI updates
- Automatic lifecycle management and cleanup
- Reactive state management patterns
State Overview
The player operates in three distinct states:
EMPTY → PLACEHOLDER → PLAYER
- EMPTY: No content displayed, initial state
- PLACEHOLDER: Static preview/thumbnail with click interaction
- PLAYER: Active video player with full functionality
Implementation Approach
The Compose-based implementation follows declarative UI patterns with reactive state management through a dedicated state handler.
1. State Handler Creation
Create the state handler with required parameters and configuration:
val stateHandler = remember(uniqueKey) {
BlazeVideosInlinePlayerComposeStateHandler(
dataSource = dataSource,
playerDelegate = playerDelegate,
containerId = "unique-player-id", // ← MUST be unique per instance
playerMode = playerMode
)
}⚠️ Critical Requirements:
- Unique
containerId: Each state handler MUST have a uniquecontainerId - Individual Instance: Never share state handlers between different composables
- Proper Keying: Use
remember(uniqueKey)to ensure stable instances per item
Required Parameters:
dataSource,playerDelegate,containerId- Choose player mode (Preview or Interactive)
- Set up event delegate for callbacks
Complete state handler details →
2. Composable Integration
Integrate the composable into your UI with the state handler:
BlazeVideosInlinePlayerCompose(
modifier = Modifier.fillMaxSize(),
stateHandler = stateHandler
)3. State Control
Control the container through reactive state changes:
// Embedding control
LaunchedEffect(shouldShowPlaceholder) {
if (shouldShowPlaceholder) {
stateHandler.embedPlaceholder()
}
}
LaunchedEffect(shouldShowPlayer) {
if (shouldShowPlayer) {
stateHandler.embedPlayer(shouldAutoPlayOnStart = true)
}
}
// Playback control
stateHandler.pausePlayer()
stateHandler.resumePlayer()
stateHandler.blockInteraction()Complete methods documentation →
4. Event Handling
Receive feedback about container flow and user actions:
val playerDelegate = object : BlazePlayerInInlineDelegate {
override fun onPlaceholderClicked(playerType: BlazePlayerType, sourceId: String?) {
// Called when placeholder is clicked in Preview mode
// Fullscreen player opens automatically, returns to placeholder when closed
}
override fun onPlayerDidEnterFullScreen(playerType: BlazePlayerType, sourceId: String?) {
// Called when player enters fullscreen mode
}
// Additional callbacks for data loading, state changes, etc.
}Note: Placeholder is only clickable in Preview mode. In Interactive mode, placeholder has no click behavior.
Complete delegate documentation →
Container State Flow
Reactive State Patterns
// Start with placeholder (recommended for feeds)
LaunchedEffect(Unit) {
stateHandler.embedPlaceholder() // EMPTY → PLACEHOLDER
}
// When you want to show active player (user scrolled into view, clicked item, etc.)
LaunchedEffect(shouldShowPlayer) {
if (shouldShowPlayer) {
stateHandler.embedPlayer(shouldAutoPlayOnStart = true) // PLACEHOLDER → PLAYER
}
}
// When user scrolls away or you want to save resources
LaunchedEffect(shouldShowPlaceholder) {
if (shouldShowPlaceholder) {
stateHandler.resetToPlaceholder() // PLAYER → PLACEHOLDER
}
}
// Automatic cleanup when composable leaves composition
DisposableEffect(stateHandler) {
onDispose {
stateHandler.disposeContainer() // Any state → EMPTY
}
}State Hoisting
Hoist active player selection to parent composable for coordinated management across multiple players:
@Composable
fun VideoFeedScreen() {
val listState = rememberLazyListState()
var activePlayerIndex by remember { mutableStateOf(-1) }
// Hoist active player selection logic to parent level
LazyColumnScrollVisibilityCenterActivation(
listState = listState,
onActiveIndexChanged = { index -> activePlayerIndex = index }
)
LazyColumn(state = listState) {
itemsIndexed(
items = feedItems,
key = { _, item -> item.id }
) { index, item ->
VideoItem(
item = item,
isActive = activePlayerIndex == index, // Pass down active state
// ... other parameters
)
}
}
}
@Composable
fun VideoItem(
item: FeedItem,
isActive: Boolean, // Receive active state from parent
// ... other parameters
) {
val stateHandler = remember(item.id) {
BlazeVideosInlinePlayerComposeStateHandler(/* parameters */)
}
// React to active state changes
PlayerActiveStateEffect(
stateHandler = stateHandler,
isActive = isActive
)
BlazeVideosInlinePlayerCompose(stateHandler = stateHandler)
}State Hoisting Pattern:
- Parent Level: Manages which player should be active (
activePlayerIndex) - Child Level: Receives active state and creates individual state handlers
- Coordination: Only one player active at a time across the entire feed
- Performance: State handlers keyed by item ID to prevent unnecessary recreation
Conditional Rendering
Use Compose's conditional rendering with player state for dynamic UI:
@Composable
fun ConditionalVideoPlayer(showVideo: Boolean) {
if (showVideo) {
val stateHandler = remember {
BlazeVideosInlinePlayerComposeStateHandler(/* parameters */)
}
BlazeVideosInlinePlayerCompose(stateHandler = stateHandler)
} else {
PlaceholderContent()
}
}Performance Considerations:
- State handlers are preserved across conditional rendering
- Automatic cleanup when condition becomes false
- Efficient memory usage in dynamic UI scenarios
Compose Lifecycle Integration
Performance Optimization:
- Composition: Use
rememberto prevent unnecessary state handler recreation - Recomposition: State is preserved across recompositions when using
remember - Disposal: Automatic cleanup when leaving composition
⚠️ Important: Resource Cleanup
Unlike view-based implementation, Compose handles disposal automatically when the composable leaves composition. However, for manual cleanup, you can still call stateHandler.disposeContainer().
Usage Patterns
- Standalone Player - Single video player in dedicated screen
- Column Integration - Multiple players in scrollable content
- LazyColumn Integration - Dynamic video feeds with lifecycle management
- Pagination Integration - Advanced lazy loading with pagination support
The Compose-based implementation provides a modern, declarative approach to inline video players while maintaining complete control over the player lifecycle with reactive state management patterns suitable for any Compose application architecture.
Updated 4 months ago
