Kotlin Multiplatform - Basic UI Creation in Android and iOS

 Basic UI Creation in Android and iOS using Kotlin Multiplatform



Lets create a basic registration page UI in both Android and iOS using Kotlin Multiplatform.


Download/Enable Kotlin Mutliplatform plugin and restart Android Studio.


Create Project:









   

Here, composeApp folder - for shared and Android, iOS specific code 
               -- androidMain - for Android platform specific code
               -- iOSMain - for iOS platform specific code
               -- jvmMain - for desktop specific code
               -- commonMain - for shared(all platform) code
          iosApp folder - for iOS project components to work in Xcode

Run/Install Application:

Android App:

Choose 'composeApp' from dropdown and click on 'Run' button.



                                                    



iOS App:

Choose 'iosApp' from dropdown and click on 'Run' button.


                                                        




Okay. Lets start with UI creation as below:





In this UI, we have to create three TextFields, One ImageView, One RadioGroup(2 - radio buttons), One Multi-Select Checkbox Group (3 - Checkboxes), One DropDown Menu, and a Button.
So we will cover all basic UIs in this one example. 

TextField:

First, lets create TextField: 

                             


@Composable
fun CustomTextField(
imageVector: ImageVector? = null,
hintText: String? = null,
labelText: String?,
modifier: Modifier
) {
var text by remember { mutableStateOf("") }

TextField(
value = text,
onValueChange = { newText -> text = newText },
label = { Text(labelText ?: "") },
placeholder = { Text(hintText ?: "") },
modifier = modifier,
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.White,
unfocusedContainerColor = Color.White
),
leadingIcon = {
imageVector?.let {
Icon(
imageVector = it,
contentDescription = ""
)
}
}
)
}
Here,    value - text to be displayed
             onValueChange - While user types, value will be changed. That latest text
             label - TextField's label text (shown above the input area)
             placeholder - hint; which will be displayed before user inputs
             modifier - TextField's decorator
             colors - Used to customise field's colors
             leadingIcon - left drawable icon

ImageView:

                                               


Image(
painter = painterResource(Res.drawable.profile),
modifier = Modifier.size(150.dp).clip(CircleShape)
.border(width = 2.dp, color = Color.LightGray, shape = CircleShape),
contentDescription = "",
contentScale = ContentScale.Crop
)

Here,
            painter - Image drawable file
            modifier - Image's decorator
            contentDescription - mandatory parameter; used for accessibility
            contentScale - Scale Type

Button:

There are different buttons available. In this example, we can create ElevatedButton, which have shadow and a bit of elevation.

                           


ElevatedButton(
modifier = Modifier.fillMaxWidth(),
onClick = { println("Registration success!!!") },
colors = ButtonDefaults.buttonColors(
contentColor = Color.White,
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text("Register", modifier = Modifier.padding(10.dp))
}

Here,
            modifier - Button's decorator
            onClick - action to be performed while user clicks button
            colors - Used to customise Button colors

RadioGroup:


Let's create single-select radio group. Here, we need 2 RadioButtons. So, lets create array of size 2. 
    val options = arrayOf("Male", "Female")

we have to iterate the array and create RadioButtons. To store the selectedValue, one variable should be created.
    var selectedOption by remember { mutableStateOf(options[0]) }

While selecting a radioButton, that particular value should be stored in that variable. Later, selectedOption will be used to mark select/unselect.
@Composable
fun RadioGroup() {
val options = arrayOf("Male", "Female")
var selectedOption by remember { mutableStateOf(options[0]) }
Row(modifier = Modifier.fillMaxWidth()) {
options.forEach { option ->
RadioButton(
selected = (option == selectedOption),
onClick = { selectedOption = option }
)
Text(
text = option,
modifier = Modifier.padding(top = 10.dp)
)
Spacer(modifier = Modifier.padding(15.dp)) //To create space in-between
}

}
}

CheckBoxGroup:



Let's create multi-select checkboxes. Here we need to create 3 checkboxes. So lets create an array of size 3.
    val options = arrayOf("Books", "Travel", "Movies")
Since its multi-select, need to save states of each checkbox, so lets create mutableStateList.
    val checkedStatus = remember{mutableStateListOf(false, false, false, false, false)}
We have to iterate the array with index and use that index to read and change checked status if needed.
@Composable
fun CheckBoxGroup() {
val options = arrayOf("Books", "Travel", "Movies")
val checkedStatus = remember {mutableStateListOf(false, false, false, false, false) }
options.forEachIndexed { index, option ->
Row(modifier = Modifier.fillMaxWidth()) {
Checkbox(
checked = checkedStatus[index],
onCheckedChange = { checkedStatus[index] = it })
Text(
text = option,
modifier = Modifier.padding(top = 10.dp)
)
}

}
}

DropDown:









To create a dropdown with options, lets create an array first:

 val options=arrayOf("India", "Singapore", "Malaysia", "China", "Korea""SriLanka" )

One boolean to track whether the dropdown is expanded or not.

    var expanded by remember { mutableStateOf(false) }

Another variable to track which one is selected:
    var selectedOption by remember { mutableStateOf(options[0]) }

@Composable
fun DropDown(){
val options = arrayOf("India", "Singapore", "Malaysia", "China", "Korea", "SriLanka" )
var expanded by remember { mutableStateOf(false) }
var selectedOption by remember { mutableStateOf(options[0]) }

ExposedDropdownMenuBox(
modifier = Modifier.fillMaxWidth(),
expanded = expanded,
onExpandedChange = { expanded = !expanded }) {
TextField(
modifier = Modifier.fillMaxWidth()
                                .menuAnchor(MenuAnchorType.PrimaryNotEditable),
value = selectedOption,
readOnly = true,
onValueChange = {},
label = {
Text(text="Country")
},
trailingIcon = {
Icon(imageVector = Icons.Default.ArrowDropDown, contentDescription="")
},
colors = TextFieldDefaults.colors(focusedContainerColor = Color.White,
unfocusedContainerColor = Color.White)
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = {expanded = false},
modifier = Modifier.fillMaxWidth().background(Color.White)
){
options.forEachIndexed { index, data ->
DropdownMenuItem(
onClick = {
selectedOption = data
expanded = false
},
text = {Text(data)}
)
}
}
}
}


Finally the UI will be,

@Composable
@Preview
fun App() {
MaterialTheme {
Column(
modifier = Modifier
.background(color = Color.White)
.safeContentPadding()
.fillMaxSize().
verticalScroll(state = rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.size(50.dp))
Image(
painter = painterResource(Res.drawable.profile),
modifier = Modifier.size(150.dp).clip(CircleShape)
.border(width = 2.dp, color = Color.LightGray
                     shape = CircleShape),
contentDescription = "",
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.size(50.dp))
CustomTextField(
imageVector = Icons.Default.AccountCircle,
hintText = "Enter Name",
labelText = "Name",
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.size(15.dp))
CustomTextField(
imageVector = Icons.Default.Mail,
hintText = "Enter Email",
labelText = "Email",
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.size(15.dp))
CustomTextField(
imageVector = Icons.Default.Phone,
hintText = "Enter Mobile No.",
labelText = "Mobile No.",
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.size(15.dp))
DropDown()
Spacer(modifier = Modifier.size(15.dp))
Text("Sex:", modifier = Modifier.fillMaxWidth(), 
                         fontWeight = FontWeight.Bold)
RadioGroup()
Spacer(modifier = Modifier.size(15.dp))
Text("Hobbies:", modifier = Modifier.fillMaxWidth(), 
                             fontWeight = FontWeight.Bold)
CheckBoxGroup()
Spacer(modifier = Modifier.size(40.dp))
ElevatedButton(
modifier = Modifier.fillMaxWidth(),
onClick = { println("Registration success!!!") },
colors = ButtonDefaults.buttonColors(
contentColor = Color.White,
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text("Register", modifier = Modifier.padding(10.dp))
}

}
}
}

FullCode on GitHub: KMPBasicUI

Android:

















iOS:




Comments

Popular posts from this blog

SOAP Client using ksoap2 in Android - Kotlin

RecyclerView with different number of columns using SpanSizeLookup

Bottom Navigation using design Library in Android - Kotlin

Exploring Android Slices - JetPack

Exploring Android Navigation Architecture Component - MVVM - Kotlin

Braintree Integration in Android - Kotlin

AppIntro (OnBoard) Screen - Kotlin

Android JetPack - Scheduling Tasks with WorkManager

Android Architecture Components - Room - Kotlin

Room with LiveData, ViewModel - Android Architecture Components - Kotlin