Products
Products
Video Hosting
Upload and manage your videos in a centralized video library.
Image Hosting
Upload and manage all your images in a centralized library.
Galleries
Choose from 100+templates to showcase your media in style.
Video Messaging
Record, and send personalized video messages.
CincoTube
Create your own community video hub your team, students or fans.
Pages
Create dedicated webpages to share your videos and images.
Live
Create dedicated webpages to share your videos and images.
For Developers
Video API
Build a unique video experience.
DeepUploader
Collect and store user content from anywhere with our file uploader.
Solutions
Solutions
Enterprise
Supercharge your business with secure, internal communication.
Townhall
Webinars
Team Collaboration
Learning & Development
Creative Professionals
Get creative with a built in-suite of editing and marketing tools.
eCommerce
Boost sales with interactive video and easy-embedding.
Townhall
Webinars
Team Collaboration
Learning & Development
eLearning & Training
Host and share course materials in a centralized portal.
Sales & Marketing
Attract, engage and convert with interactive tools and analytics.
"Cincopa helped my Enterprise organization collaborate better through video."
Book a Demo
Resources
Resources
Blog
Learn about the latest industry trends, tips & tricks.
Help Centre
Get access to help articles FAQs, and all things Cincopa.
Partners
Check out our valued list of partners.
Product Updates
Stay up-to-date with our latest greatest features.
Ebooks, Guides & More
Customer Stories
Hear how we've helped businesses succeed.
Boost Campaign Performance Through Video
Discover how to boost your next campaign by using video.
Download Now
Pricing
Watch a Demo
Demo
Login
Start Free Trial
Jetpack Compose is Google's modern UI toolkit for Android, designed to simplify UI development by using Kotlin programming and a declarative approach. This toolkit replaces XML-based layouts with a more concise and intuitive Kotlin-based DSL. While Jetpack Compose can be used for any Android UI, it shines when developing dynamic, media-rich applications, including those that require video playback features. ExoPlayer Integration with Jetpack Compose ExoPlayer is a media player that integrates with Jetpack Compose, enabling developers to implement video playback functionality. In a Compose-based app, ExoPlayer can be integrated with minimal boilerplate code, making it easier to control video playback, manage state, and handle user interactions. Example : Setting Up ExoPlayer in Jetpack Compose @Composable fun VideoPlayerScreen(videoUrl: String, videoTitle: String) { val context = LocalContext.current val player = remember { ExoPlayer.Builder(context).build() } // Load the video URL into the player LaunchedEffect(videoUrl) { player.setMediaItem(MediaItem.fromUri(videoUrl)) player.prepare() } // Display the video using Surface Surface(modifier = Modifier.fillMaxSize()) { VideoSurface(player = player) // Display video title and playback controls Column(modifier = Modifier.align(Alignment.BottomCenter).padding(16.dp)) { Text(text = videoTitle, style = MaterialTheme.typography.h6) PlaybackControls(player = player) } } // Release the player when the composable is disposed DisposableEffect(Unit) { onDispose { player.release() } } } Explanation : val player = remember { ExoPlayer.Builder(context).build() } : Initializes an ExoPlayer instance once and retains it across recompositions using remember. Prevents unnecessary recreation of the player. LaunchedEffect(videoUrl) { ... } : Runs the enclosed block when videoUrl changes. Ensures setMediaItem() and prepare() are called reactively and only when needed. DisposableEffect(Unit) { onDispose { player.release() } } : Ensures player.release() is called when the composable leaves the composition, preventing resource leaks. Modifier.align(Alignment.BottomCenter) : Must be applied inside a Box layout. Will throw if used in a layout that doesn’t support alignment (e.g., Column). Declarative UI with Kotlin Functions Jetpack Compose uses composable functions to build UI in a state-driven, functional style. Each of these UI elements is declared as a function that re-executes automatically when the state changes. Example : Basic Composable Showing A Greeting Message @Composable fun Greeting(name: String) { Text(text = 'Hello, $name!', fontSize = 20.sp) } Explanation : @Composable: Marks the function as a composable, allowing it to emit UI elements. Text(...): Displays a string on the screen; fontSize = 20.sp sets the text size in scale-independent pixels. Jetpack Compose App Structure In Jetpack Compose, the setContent block inside an Activity acts as the entry point where the entire composable UI tree is defined. This is typically wrapped in a MaterialTheme for styling consistency and a Surface to apply layout constraints and background color. Example : Root Composable in an Android app class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme { Surface(modifier = Modifier.fillMaxSize()) { Greeting('Compose') } } } } } Explanation : setContent { ... } : Entry point for defining the UI in Jetpack Compose. This replaces setContentView() used in traditional XML-based Android UIs. MaterialTheme { ... } : Applies a consistent Material Design theme across all nested composables. Required for proper styling and use of theme-based components like Text, Button, etc. Surface(modifier = Modifier.fillMaxSize()) : Creates a container that fills the entire screen. Useful for setting background color or defining layout boundaries. Compose apps use this structure to declare layout behavior in a reactive, hierarchical manner. State and Recomposition In Jetpack Compose, state is stored using remember and mutableStateOf, allowing the UI to automatically recompose only the affected parts when values change. For more structured or shared state, StateFlow or LiveData can be observed directly in composables using collectAsState or observeAsState. Example : Stateful Counter with Recomposition @Composable fun Counter() { var count by remember { mutableStateOf(0) } Column { Text(text = 'Count: $count') Button(onClick = { count++ }) { Text('Increment') } } } Explanation : var count by remember { mutableStateOf(0) } : Declares a reactive state variable using mutableStateOf, which triggers recomposition when updated. Text(text = 'Count: $count') : Automatically reflects the latest value of count each time the state changes due to recomposition. Button(onClick = { count++ }) : Increments the count state. Since count is a mutableState, the UI will recompose with the updated value. Recomposition is localized to only affected parts of the UI tree, improving rendering efficiency. Layouts and Modifiers In Jetpack Compose, layouts like Row, Column, and Box structure the UI by organizing child composables either horizontally, vertically, or in layered stacks. These are paired with Modifier chains to precisely control padding, size, alignment, and interactions, completely replacing XML constraints or nested containers. Example : Layout With Spacing And Alignment @Composable fun LayoutExample() { Column( modifier = Modifier .padding(16.dp) .fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { Icon(Icons.Default.Star, contentDescription = null, modifier = Modifier.size(48.dp)) Spacer(modifier = Modifier.height(16.dp)) Text('Jetpack Compose', fontSize = 24.sp, fontWeight = FontWeight.Bold) } } Explanation : padding(16.dp) : Adds 16 dp of space inside the composable (acts like internal margin) fillMaxWidth() : Expands the composable horizontally to match the full width of its parent Spacer(modifier = Modifier.height( 16.dp )) : Acts as a separate composable that adds vertical space. Preferred over padding between siblings for clearer intent and flexibility Icon size & Modifier.size( 48.dp ) : Using .size() explicitly sets both width and height. Without this, the Icon defaults to its intrinsic size, which might not match your design. ViewModel and StateFlow Integration Jetpack Compose integrates with ViewModel and StateFlow, allowing state to be managed outside the UI layer and observed reactively within composables. By collecting StateFlow with collectAsState(), Compose automatically triggers recomposition when the data changes, ensuring a unidirectional data flow. Example : StateFlow-based ViewModel in Compose class CounterViewModel : ViewModel() { private val _count = MutableStateFlow(0) val count: StateFlow
= _count fun increment() { _count.value++ } } Explanation : private val _count = MutableStateFlow(0) : Defines a mutable state holder backed by Kotlin’s StateFlow. The underscore (_count) indicates it's internal to the class and should not be exposed directly to the UI. val count: StateFlow
= _count : Exposes an immutable version of the flow to observers (e.g., UI), enforcing unidirectional data flow and preventing external mutation. _count.value++ : Increments the current value of the state flow. Updating .value triggers observers to re-collect the latest value in the UI layer. Composable Usage : @Composable fun CounterScreen(viewModel: CounterViewModel = viewModel()) { val count by viewModel.count.collectAsState() Column { Text('Count: $count') Button(onClick = { viewModel.increment() }) { Text('Increment') } } } Explanation : viewModel: CounterViewModel = viewModel() : Uses Compose’s viewModel() function to retrieve or create the CounterViewModel instance scoped to the current composable lifecycle. val count by viewModel.count.collectAsState() : Collects the StateFlow from the ViewModel as a Compose State object. Button(onClick = { viewModel.increment() }) : Triggers a ViewModel method that updates the state. Compose automatically re-renders the UI when count changes, preserving unidirectional data flow. Managing Video Playback with Controls In video applications, users expect intuitive and responsive controls. Jetpack Compose’s declarative nature allows handling of playback controls like play, pause, seek, and mute. Example : Play/Pause Controls @Composable fun PlaybackControls(player: ExoPlayer) { val playbackState = player.playbackState IconButton(onClick = { if (player.isPlaying) { player.pause() } else { player.play() } }) { Icon( imageVector = if (player.isPlaying) Icons.Default.Pause else Icons.Default.PlayArrow, contentDescription = 'Play/Pause Button' ) } } Explanation : val playbackState = player.playbackState : Reads the current playback state (e.g., STATE_READY, STATE_ENDED) at the time of composition. if (player.isPlaying) { ... } : Directly checks if the ExoPlayer is currently playing. This property reflects the real-time state. Icon(imageVector = if (...) Icons.Default.Pause else Icons.Default.PlayArrow, ...) : Dynamically chooses the icon based on the current playback state. Navigation with Jetpack Compose Using the Navigation Compose library, you can define navigation flows with composable functions—no XML required. Example : Simple Navigation Between Two Screens @Composable fun NavGraph(navController: NavHostController) { NavHost(navController, startDestination = 'home') { composable('home') { HomeScreen(navController) } composable('details') { DetailsScreen() } } } Explanation : NavHost(navController, startDestination = 'home') : Sets up the navigation graph and determines the first screen shown (home). composable('home') { HomeScreen(navController) } : Defines a navigation destination with the route 'home'. composable('details') { DetailsScreen() } : Registers a second route 'details' that loads DetailsScreen when navigated to. Routing logic is centralized and defined entirely in Kotlin, eliminating XML navigation graphs. UI Testing with Compose Jetpack Compose provides a purpose-built test framework to interact with and validate composables. Example : Basic UI Test for Button Interaction @get:Rule val composeTestRule = createComposeRule() @Test fun counter_increments_onClick() { composeTestRule.setContent { Counter() } composeTestRule.onNodeWithText('Increment').performClick() composeTestRule.onNodeWithText('Count: 1').assertExists() } Explanation : @get:Rule val composeTestRule = createComposeRule() : Creates a JUnit test rule that sets up the Compose testing environment. composeTestRule.setContent { Counter() } : Injects the Counter composable into the test environment, rendering it for UI interaction and assertions. composeTestRule.onNodeWithText('Increment').performClick() : Finds the UI element with the exact text 'Increment' and simulates a click event on it. composeTestRule.onNodeWithText('Count: 1').assertExists() : Verifies that after the click, a UI element displaying 'Count: 1' exists. Compose testing enables end-to-end UI validation with high precision and minimal boilerplate. Jetpack Compose Development Tools Jetpack Compose seamlessly integrates with Android Studio, providing tools like live previews, code completion, and interactive UI design to speed up development. Features like the Layout Inspector and Live Edit allow developers to inspect UI hierarchies and make instant changes without restarting the app, boosting productivity and reducing iteration time. Android Studio (Giraffe or later) : Full Compose editor, live previews, and code completion. Layout Inspector : Real-time inspection of the composable hierarchy. Live Edit : Immediate UI updates without restarting the emulator. Lint & Compiler Plugins : Static analysis and optimization of composables.