Checkout why compose UI better than standard XML
By Subhash — “Android Monk”
Jetpack compose is a modern way of UI development toolkit recommended and preferred by Google. It accelerates the development of UI with less code, and less time and provides powerful APIs. It has many alternative and advanced concepts compared to native XML. It provides a way for enhanced coding and design.
In standard ways of declaring, we usually have sharedFlow, state flow, or LiveData, to collect the recently emitted states to update the UI views. But in compose we have a special state called “mutableStateOf” where it can simply emit the latest state to all of its active listening collectors in a life cycle-aware manner.
In standard ways, we have to write code in a lifecycle-aware manner. But in compose, we have a fantastic API “collectasStatewithLifecycle()”, which helps to avoid those boilerplate codes.
sealed class Data {
object one : Data()
object two : Data()
object zero : Data()
}
private var _state = MutableStateFlow<Data>(Data.zero)
var state = _state.asStateFlow()
// one can also use shared flow for one shot events. eg. toast
private var _shared = MutableSharedFlow<Data>()
var shared = _shared.asSharedFlow()
private fun emittor() {
_state.tryEmit(Data.one)
}
@Composable
fun CollectingFlows() {
// Below line for collecting stateflows
val flows by state.collectAsStateWithLifecycle()
// Below line collecting sharedflows requires initial state
val sharedflow by state.collectAsStateWithLifeCycle(Data.zero)
// Here colleting only the stateflow
when(flows) {
is Data.zero -> {
// our logic
}
is Data.two -> {}
is Data.one -> {}
}
}
As, we all know in existing XMLs to update a view we will require an adapter, and data binding and it requires a lot of boilerplate codes such as getting the view type and assigning it to a view holder. But here we have an exciting API called “Lazy Column for Vertical orientation “, “Lazy row for Horizontal Orientation “ and “Lazy Grids for Grid views and Tags”. Here we are having arrangement and alignment keys in making the UIs relative to screen width in adjusting the dimensions.
@Composable
fun RecyclerView() {
LazyColumn(
Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
item {
// Below components executes one time
Text("One")
Text("Two")
}
items(5) {
// repeatively creates the below the components
// for the mentioned count
Text("One")
Text("Two")
}
}
}
@Composable
fun RecyclerLazyRow() {
LazyRow(
Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
item {
// Below components executes one time
Text("One")
Text("Two")
}
items(5) {
// repeatively creates the below the components
// for the mentioned count
Text("One")
Text("Two")
}
}
}
Most of the XML attributes come in a simple compose component called “Modifier”. Modifier provides more advanced API attributes for UI creation in the simplest way. Compose provides reusability of UI components and methods.
“remember ”, as the name suggests that it remembers the states of a UI and prevents the unnecessary recomposition of UI components. Whenever a method gets recomposed or recalled, the “remember” keyword helps to avoid the recomposition of particular segments of codes and reduces the redundancy.
@Composable
fun SimpleCounter() {
var count by remember {
mutableStateOf(0)
}
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = count.toString(), fontSize = 14.sp)
Button(onClick = { count++ }) {
Text(text = "Increase")
}
}
}
Compose has a specialized API for handling recomposition. They are
Executes, when composition enters and cancels when it cancels after leaving the composition. Cancels/ relaunches when the key changes.
Once, the side effects get executed, it needs to be cleaned up from the memory. The DisposableEffect can take complete control of it.
@Composable
fun SideEffects() {
LaunchedEffect(key1 = Unit, block = {
// make network calls
})
DisposableEffect(key1 = Unit, effect = {
onDispose {
//logic for cleaning an un used variables
}
})
}
In the native XML way, we have a bottom sheet as a separate fragment whereas in Compose we have a separate API for bottom sheets and all we have to do is, simply call the method and pass the bottom sheet state to it.
We have a separate API “Animated Visibility ”for providing animations to the UI components such as enter/ exit transitions, most of them use “animateAsState ”, which accepts float or Int as a param.
Below Example demonstrates the replacement of BottomSheetDialogFragment
@Composable
@OptIn(ExperimentalMaterialApi::class)
fun BottomSheetExample() {
val SheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden
)
ModalBottomSheetLayout(sheetState = SheetState, sheetContent = {
// composables inside the sheet
}) {
// composables outside the sheet
}
}
7. XML vs Compose vs Interoperability
In conclusion, the introduction of compose doesn’t mean that the XML is deprecated. Maybe after a few years, Google suggests that compose will be the preferred way of creating easy and reusable UI components. This can minimize a lot of boilerplate codes and improves startup time, application size, and build time.
Author
Reviewed By
Editor
We at CaratLane are solving some of the most intriguing challenges to make our mark in the relatively uncharted omnichannel jewellery industry. If you are interested in tackling such obstacles, feel free to drop your updated resume/CV to careers@caratlane.com!
Leave a Reply