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:
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
Post a Comment