Creating a composable that expose a composable context through a specific method like LazyColumn

Viewed 35

Lately I've been tinkering with some more advanced usages of the Compose libraries, and was trying to implement a MediaPlayer composable to be used to manage player layout, events, playback state etc.

I've ran into some trouble when I realized that I need to limit this element's layout to a specific method, just like in LazyColumn where only the item or items methods actually provide a Composable context.

I set out to try and imitate what they are doing in the hope of finding a solution for this, but the more I dug in the source code the more complex the implementation revealed itself to be, and I couldn't really understand what was going on.

Is it possible to copy this behavior from LazyColumn?

This is what I have so far:

@Composable
fun MediaPlayer(
    playerState: MediaPlayerState = rememberMediaPlayerState(),
    content: MediaPlayerScope.() -> Unit
) {
    with(MediaPlayerScopeInstance) {
        this.playerState = playerState
        content()
    }
}

interface MediaPlayerScope {
    var controller: MediaPlayerController
    var playerState: MediaPlayerState

    fun mediaItems(items: List<MediaItem>)
    fun <T> mediaItems(original: List<T>, mapper: (T) -> MediaItem)

    fun playerLayout(layout: @Composable () -> Unit)
}

internal object MediaPlayerScopeInstance : MediaPlayerScope {
    override var controller: MediaPlayerController = DefaultController
    override var playerState = MediaPlayerState()

    override fun mediaItems(items: List<MediaItem>) {
        playerState.mediaItems = items
    }

    override fun <T> mediaItems(original: List<T>, mapper: (T) -> MediaItem) {
        playerState.mediaItems = original.map(mapper)
    }

    override fun playerLayout(layout: @Composable () -> Unit) {
        // this method should somehow invoke the layout parameter
    }
}

As for usage I want to achieve something like this:

MediaPlayer {
    mediaItems(viewModel.songList) {
        it.asMediaItem()
    }
    
    // the next statement should show an error as this is not a composable context
    Box {
        
    }

    // the end goal is to render this lambda to the screen
    playerLayout {
        // this should work
        Box {
            
        }
    }
}

As you can see this behavior is working as I want it to aside for rendering the layout, this is where I got stuck

Any help would be appreciated!

0 Answers
Related