GuidesAPI ReferenceRelease Notes
HomeLog InHome
Guides

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:

EMPTYPLACEHOLDERPLAYER

  • 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 unique containerId
  • Individual Instance: Never share state handlers between different composables
  • Proper Keying: Use remember(uniqueKey) to ensure stable instances per item

Required Parameters:

Complete state handler details →

2. Composable Integration

Integrate the composable into your UI with the state handler:

BlazeVideosInlinePlayerCompose(
    modifier = Modifier.fillMaxSize(),
    stateHandler = stateHandler
)

Complete composable details →

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 remember to 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

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.


Did this page help you?