Jetpack Compose ExposedDropdownMenu always going up - android

I'm trying to implement ExposedDropdownMenu - which I want to be displayed underneath TextField - when I set height of dropdown to max. 20 dp then everything is okay. But for any greater value it is always displayed above. Do you know what could be the issue here?
How it looks like:
My code:
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun TextFieldWithDropdown(
modifier: Modifier = Modifier,
textFieldState: TextFieldState,
textCallback: (String) -> Unit,
list: List<String>,
keyboardOptions: KeyboardOptions,
textStyle: TextStyle
) {
// .align(Alignment.CenterStart)
val dropDownOptions = remember { mutableStateOf(listOf<String>()) }
val textFieldValue = remember { mutableStateOf(TextFieldValue()) }
val dropDownExpanded = remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
modifier = modifier,
expanded = dropDownExpanded.value, onExpandedChange = {
dropDownExpanded.value = !dropDownExpanded.value
}) {
TextField(
modifier = Modifier
.menuAnchor()
.fillMaxWidth()
.onFocusChanged { focusState ->
if (!focusState.isFocused)
dropDownExpanded.value = false
},
value = textFieldState.text.value,
onValueChange = {
textFieldState.text.value = it
textCallback.invoke(it)
dropDownOptions.value =
list.filter { it.startsWith(textFieldState.text.value) && it != textFieldState.text.value }
.take(3)
dropDownExpanded.value = dropDownOptions.value.isNotEmpty()
},
singleLine = true,
maxLines = 1,
textStyle = textStyle,
)
ExposedDropdownMenu(
expanded = dropDownExpanded.value,
onDismissRequest = {
dropDownExpanded.value = false
},
) {
dropDownOptions.value.forEach { text ->
DropdownMenuItem(text = {
Text(text = text)
}, onClick = {
textFieldState.text.value = text
dropDownExpanded.value = false
})
}
}
}
}

Related

My lazycolumn items and my textfield glide on the screen when the keyboard is opened in jetpack compose

I'm just at the beginning of a chat bot application and I'm showing dialogs with lazycolumn. I also have a textfield, the bot responds to the texts entered in this textfield and cards appear on the screen, but when the keyboard is opened, my lazycolumn items slide upwards and the textfield does not appear properly. To solve this, I used something called bringIntoViewRequester and
I added the following to
my
manifest android:windowSoftInputMode="adjustResize"
MainActivity window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
I'll share my codes below.
How can I solve this problem? also this method as far as I know is experimental, in my opinion if there is another non experimental method you know it would be better
my first screen
var index = 1
#Composable
fun FirstScreen() {
val list = remember { mutableStateListOf<Message>() }
val botList = listOf("Peter", "Francesca", "Luigi", "Igor")
val random = (0..3).random()
val botName: String = botList[random]
val hashMap: HashMap<String, String> = HashMap<String, String>()
customBotMessage(message = Message1, list)
LazyColumn {
items(list.size) { i ->
if (list[i].id == RECEIVE_ID)
Item(message = list[i], botName)
else
Item(message = list[i], "User")
}
}
SimpleOutlinedTextFieldSample(list, hashMap)
}
#OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)
#Composable
fun SimpleOutlinedTextFieldSample(
list: SnapshotStateList<Message>,
hashMap: HashMap<String, String>
) {
val coroutineScope = rememberCoroutineScope()
val keyboardController = LocalSoftwareKeyboardController.current
val bringIntoViewRequester = remember { BringIntoViewRequester() }
var text by remember { mutableStateOf("") }
Column(
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.Center
) {
OutlinedTextField(
modifier = Modifier
.padding(8.dp)
.onFocusEvent { focusState ->
if (focusState.isFocused) {
coroutineScope.launch {
bringIntoViewRequester.bringIntoView()
}
}
},
keyboardOptions = KeyboardOptions(imeAction = androidx.compose.ui.text.input.ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }),
value = text,
onValueChange = { text = it },
label = { Text("send message") })
IconButton(onClick = {
if (text.isNotEmpty()) {
list.add(
Message(
text,
SEND_ID,
Timestamp(System.currentTimeMillis()).toString()
)
)
hashMap.put(listOfMessageKeys[index - 1], text)
customBotMessage(listOfMessages[index], list)
index += 1
}
text = ""
}) {
Icon(
modifier = Modifier.padding(8.dp),
painter = painterResource(id = R.drawable.ic_baseline_send_24),
contentDescription = "send message img"
)
}
}
}
}
private fun customBotMessage(message: String, list: SnapshotStateList<Message>) {
GlobalScope.launch {
delay(1000)
withContext(Dispatchers.Main) {
list.add(Message(message, RECEIVE_ID, Timestamp(System.currentTimeMillis()).toString()))
}
}
}
LazyColumn - Item
#OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
#Composable
fun Item(
message : Message,
person : String,
){
val coroutineScope = rememberCoroutineScope()
val keyboardController = LocalSoftwareKeyboardController.current
val bringIntoViewRequester = remember { BringIntoViewRequester() }
Card(
modifier = Modifier
.padding(10.dp)
.bringIntoViewRequester(bringIntoViewRequester),
elevation = 10.dp
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
modifier = Modifier
.padding(10.dp)
.height(IntrinsicSize.Min)){
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(4.dp)
){
Text(
buildAnnotatedString {
withStyle(style = SpanStyle(fontWeight = FontWeight.Light)) {
append("$person: ")
}
},
modifier = Modifier
.padding(4.dp)
.onFocusEvent { focusState ->
if (focusState.isFocused) {
coroutineScope.launch {
bringIntoViewRequester.bringIntoView()
}
}
}
)
Text(
buildAnnotatedString {
withStyle(
style = SpanStyle(
fontWeight = FontWeight.W900,
color = Color(0xFF4552B8)
))
{
append(message.message)
}
},
modifier = Modifier.onFocusEvent { focusState ->
if (focusState.isFocused) {
coroutineScope.launch {
bringIntoViewRequester.bringIntoView()
}
}
}
)
}
}
}
}

Row composable not taking up the full width of parent

I have a shopping list item composable that is not taking up the entire width of the parent, as you can see below with the red border. I want it to be flush against the parent's edge. And why is there some space or padding just before the checkbox? What needs to be modified?
Composable
#Composable
fun ShoppingListScreenItem(
itemName: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit
) {
Row(
modifier = Modifier.fillMaxWidth().border(2.dp, Red),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
modifier = Modifier.padding(0.dp),
checked = checked,
onCheckedChange = onCheckedChange
)
Text(
modifier = Modifier.padding(start = 8.dp),
fontWeight = FontWeight.Bold,
text = itemName
)
}
}
Parent Composable
#Composable
fun ShoppingListScreen(
navController: NavHostController,
shoppingListScreenViewModel: ShoppingListScreenViewModel
) {
val scope = rememberCoroutineScope()
val name = stringResource(id = R.string.item_name)
val nameError = stringResource(id = R.string.item_IsNameError)
val category = stringResource(id = R.string.item_category)
val categoryError = stringResource(id = R.string.item_IsCategoryError)
val focusManager = LocalFocusManager.current
val allItems =
shoppingListScreenViewModel.shoppingListItemsState.value?.collectAsLazyPagingItems()
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "AppBar") },
backgroundColor = Color.White,
navigationIcon = if (navController.previousBackStackEntry != null) {
{
IconButton(onClick = { navController.navigateUp() }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Back"
)
}
}
} else {
null
}
)
},
floatingActionButton = {
FloatingActionButton(
onClick = {
shoppingListScreenViewModel.setStateValue("ShowAddItemDialog", true)
},
backgroundColor = Color.Blue,
contentColor = Color.White
) {
Icon(Icons.Filled.Add, "")
}
},
// Defaults to false
isFloatingActionButtonDocked = false,
bottomBar = { BottomNavigationBar(navController = navController) }
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
LazyColumn(
contentPadding = PaddingValues(
vertical = 8.dp,
horizontal = 8.dp
)
) {
//todo change it to non null
items(allItems!!) { item ->
ShoppingListScreenItem(
itemName = item?.name!!,
checked = item.isInCart
) { isChecked ->
scope.launch {
shoppingListScreenViewModel.changeItemChecked(item, isChecked)
}
}
}
}
if (shoppingListScreenViewModel.shoppingListScreenState.value.showAddItemDialog) {
OnTheFlyAddItemDialog(
shoppingListScreenViewModel = shoppingListScreenViewModel,
focusManager = focusManager,
navController = navController,
onDismiss = {
shoppingListScreenViewModel.setStateValue(name, "")
shoppingListScreenViewModel.setStateValue(nameError, false)
shoppingListScreenViewModel.setStateValue(category, "")
shoppingListScreenViewModel.setStateValue(categoryError, false)
shoppingListScreenViewModel.setStateValue("ShowAddItemDialog", false)
}
)
{
scope.launch {
shoppingListScreenViewModel.addShoppingListItemToDb()
shoppingListScreenViewModel.setStateValue("ShowAddItemDialog", false)
shoppingListScreenViewModel.setStateValue(name, "")
shoppingListScreenViewModel.setStateValue(nameError, false)
shoppingListScreenViewModel.setStateValue(category, "")
shoppingListScreenViewModel.setStateValue(categoryError, false)
// triggerCount++
}
}
}
Button(
modifier = Modifier.padding(vertical = 24.dp),
onClick = {
navController.navigate(NavScreens.AddItemScreen.route) {
popUpTo(NavScreens.AddItemScreen.route) {
inclusive = true
}
}
}
) {
Text("Goto add item screen")
}
}
}
}
You set a contentPadding in your LazyColumn that is responsible for the spaces. Remove it, or set it to zero.
LazyColumn(
contentPadding = PaddingValues(
vertical = 8.dp,
horizontal = 0.dp
)
)
The padding around the Checkbox depends on the minimumTouchTargetSize defined when the onClick is not null with a hardcoded value of 48.dp.
You can override it using:
CompositionLocalProvider(
LocalMinimumTouchTargetEnforcement provides false,
) {
Checkbox(
modifier = Modifier.padding(0.dp), //4.dp
checked = false,
onCheckedChange = {}
)
}
but it is not the best choice in term of accessibility.
.
The space around the Row depends on the contentPadding defined in the LazyColumn.

How to implement search in Jetpack Compose - Android

var noteListState by remember { mutableStateOf(listOf("Drink water", "Walk")) }
This is my list of items. Any leads would be appreciated
To Get a Main UI with list and searchview
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MainScreen()
}
}
For Main()
#Composable
fun MainScreen() {
val textState = remember { mutableStateOf(TextFieldValue("")) }
Column {
SearchView(textState)
ItemList(state = textState)
}
}
For Serchview and list
#Composable
fun SearchView(state: MutableState<TextFieldValue>) {
TextField(
value = state.value,
onValueChange = { value ->
state.value = value
},
modifier = Modifier.fillMaxWidth(),
textStyle = TextStyle(color = Color.White, fontSize = 18.sp),
leadingIcon = {
Icon(
Icons.Default.Search,
contentDescription = "",
modifier = Modifier
.padding(15.dp)
.size(24.dp)
)
},
trailingIcon = {
if (state.value != TextFieldValue("")) {
IconButton(
onClick = {
state.value =
TextFieldValue("") // Remove text from TextField when you press the 'X' icon
}
) {
Icon(
Icons.Default.Close,
contentDescription = "",
modifier = Modifier
.padding(15.dp)
.size(24.dp)
)
}
}
},
singleLine = true,
shape = RectangleShape, // The TextFiled has rounded corners top left and right by default
colors = TextFieldDefaults.textFieldColors(
textColor = Color.White,
cursorColor = Color.White,
leadingIconColor = Color.White,
trailingIconColor = Color.White,
backgroundColor = MaterialTheme.colors.primary,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
)
)
}
#Composable
fun ItemList(state: MutableState<TextFieldValue>) {
val items by remember { mutableStateOf(listOf("Drink water", "Walk")) }
var filteredItems: List<String>
LazyColumn(modifier = Modifier.fillMaxWidth()) {
val searchedText = state.value.text
filteredItems = if (searchedText.isEmpty()) {
items
} else {
val resultList = ArrayList<String>()
for (item in items) {
if (item.lowercase(Locale.getDefault())
.contains(searchedText.lowercase(Locale.getDefault()))
) {
resultList.add(item)
}
}
resultList
}
items(filteredItems) { filteredItem ->
ItemListItem(
ItemText = filteredItem,
onItemClick = { /*Click event code needs to be implement*/
}
)
}
}
}
#Composable
fun ItemListItem(ItemText: String, onItemClick: (String) -> Unit) {
Row(
modifier = Modifier
.clickable(onClick = { onItemClick(ItemText) })
.background(colorResource(id = R.color.purple_700))
.height(57.dp)
.fillMaxWidth()
.padding(PaddingValues(8.dp, 16.dp))
) {
Text(text = ItemText, fontSize = 18.sp, color = Color.White)
}
}
For More Detailed answer you can refer this link
Please could you explain more, this question is a bit short and unclear.
One way (i think you could do) is just to loop through the list and check if a element (of your list) contains that text.
val filterPattern = text.toString().lowercase(Locale.getDefault()) // text you are looking for
for (item in copyList) { // first make a copy of the list
if (item.name.lowercase(Locale.getDefault()).contains(filterPattern)) {
filteredList.add(item) // if the item contains that text add it to the list
}
}
... // here you then override noteListState
After you have made the filteredList you can just override the noteListState. Make sure to make a copy of that list beforehand and set it back when you don't want to show the filtered results.

How to create a OTP layout in android compose?

I was making a login for my app in the new android jetpack's compose.
I want to make a OTP layout like in the given photo.
check full example here
const val PIN_VIEW_TYPE_UNDERLINE = 0
const val PIN_VIEW_TYPE_BORDER = 1
#Composable
fun PinView(
pinText: String,
onPinTextChange: (String) -> Unit,
digitColor: Color = MaterialTheme.colors.onBackground,
digitSize: TextUnit = 16.sp,
containerSize: Dp = digitSize.value.dp * 2,
digitCount: Int = 4,
type: Int = PIN_VIEW_TYPE_UNDERLINE,
) {
BasicTextField(value = pinText,
onValueChange = onPinTextChange,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
decorationBox = {
Row(horizontalArrangement = Arrangement.SpaceBetween) {
repeat(digitCount) { index ->
DigitView(index, pinText, digitColor, digitSize, containerSize, type = type)
Spacer(modifier = Modifier.width(5.dp))
}
}
})
}
#Composable
private fun DigitView(
index: Int,
pinText: String,
digitColor: Color,
digitSize: TextUnit,
containerSize: Dp,
type: Int = PIN_VIEW_TYPE_UNDERLINE,
) {
val modifier = if (type == PIN_VIEW_TYPE_BORDER) {
Modifier
.width(containerSize)
.border(
width = 1.dp,
color = digitColor,
shape = MaterialTheme.shapes.medium
)
.padding(bottom = 3.dp)
} else Modifier.width(containerSize)
Column(horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center) {
Text(
text = if (index >= pinText.length) "" else pinText[index].toString(),
color = digitColor,
modifier = modifier,
style = MaterialTheme.typography.body1,
fontSize = digitSize,
textAlign = TextAlign.Center)
if (type == PIN_VIEW_TYPE_UNDERLINE) {
Spacer(modifier = Modifier.height(2.dp))
Box(
modifier = Modifier
.background(digitColor)
.height(1.dp)
.width(containerSize)
)
}
}
}
You can use a very simple layout for each char in the otp.
Something like
#Composable
fun OtpChar(){
var text by remember { mutableStateOf("1") }
val maxChar = 1
Column(Modifier.background(DarkGray),
horizontalAlignment = Alignment.CenterHorizontally){
TextField(
value =text,
onValueChange = {if (it.length <= maxChar) text = it},
modifier = Modifier.width(50.dp),
singleLine = true,
textStyle = LocalTextStyle.current.copy(
fontSize = 20.sp,
textAlign= TextAlign.Center),
colors= TextFieldDefaults.textFieldColors(
textColor = White,
backgroundColor = Transparent,
unfocusedIndicatorColor = Transparent,
focusedIndicatorColor = Transparent)
)
Divider(Modifier
.width(28.dp)
.padding(bottom = 2.dp)
.offset(y=-10.dp),
color = White,
thickness = 1.dp)
}
}
You can add some features like:
manage the focus in Next direction with the TAB key
manage the focus in Previous direction with the BACK SPACE key
how to move to the next textfield when a digit is entered
Something like:
fun OtpChar(
modifier: Modifier = Modifier
){
val pattern = remember { Regex("^[^\\t]*\$") } //to not accept the tab key as value
var (text,setText) = remember { mutableStateOf("") }
val maxChar = 1
val focusManager = LocalFocusManager.current
LaunchedEffect(
key1 = text,
) {
if (text.isNotEmpty()) {
focusManager.moveFocus(
focusDirection = FocusDirection.Next,
)
}
}
Column(
horizontalAlignment = Alignment.CenterHorizontally
){
TextField(
value =text,
onValueChange = {
if (it.length <= maxChar &&
((it.isEmpty() || it.matches(pattern))))
setText(it)
},
modifier = modifier
.width(50.dp)
.onKeyEvent {
if (it.key == Key.Tab) {
focusManager.moveFocus(FocusDirection.Next)
true
}
if (text.isEmpty() && it.key == Key.Backspace) {
focusManager.moveFocus(FocusDirection.Previous)
}
false
},
textStyle = LocalTextStyle.current.copy(
fontSize = 20.sp,
textAlign= TextAlign.Center),
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next
),
colors= TextFieldDefaults.textFieldColors(
backgroundColor = Transparent,
unfocusedIndicatorColor = Transparent,
focusedIndicatorColor = Transparent),
)
Divider(
Modifier
.width(28.dp)
.padding(bottom = 2.dp)
.offset(y = -10.dp),
color = Teal200,
thickness = 1.dp)
}
}
Then just use something like a Row to display 4 OtpChars
val (item1, item2, item3, item4) = FocusRequester.createRefs()
Row(horizontalArrangement = Arrangement.SpaceBetween){
OtpChar(
modifier = Modifier
.focusRequester(item1)
.focusProperties {
next = item2
previous = item1
}
)
OtpChar(
modifier = Modifier
.focusRequester(item2)
.focusProperties {
next = item3
previous = item1
}
)
OtpChar(
modifier = Modifier
.focusRequester(item3)
.focusProperties {
next = item4
previous = item2
}
)
OtpChar(
modifier = Modifier
.focusRequester(item4)
.focusProperties {
previous = item3
next = item4
}
)
//....
}
If you faced keyboard issues try the code below:
#Composable
fun OtpCell(
modifier: Modifier,
value: String,
isCursorVisible: Boolean = false
) {
val scope = rememberCoroutineScope()
val (cursorSymbol, setCursorSymbol) = remember { mutableStateOf("") }
LaunchedEffect(key1 = cursorSymbol, isCursorVisible) {
if (isCursorVisible) {
scope.launch {
delay(350)
setCursorSymbol(if (cursorSymbol.isEmpty()) "|" else "")
}
}
}
Box(
modifier = modifier
) {
Text(
text = if (isCursorVisible) cursorSymbol else value,
style = MaterialTheme.typography.body1,
modifier = Modifier.align(Alignment.Center)
)
}
}
#ExperimentalComposeUiApi
#Composable
fun PinInput(
modifier: Modifier = Modifier,
length: Int = 5,
value: String = "",
onValueChanged: (String) -> Unit
) {
val focusRequester = remember { FocusRequester() }
val keyboard = LocalSoftwareKeyboardController.current
TextField(
value = value,
onValueChange = {
if (it.length <= length) {
if (it.all { c -> c in '0'..'9' }) {
onValueChanged(it)
}
if (it.length >= length) {
keyboard?.hide()
}
}
},
// Hide the text field
modifier = Modifier
.size(0.dp)
.focusRequester(focusRequester),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number
)
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
repeat(length) {
OtpCell(
modifier = modifier
.size(width = 45.dp, height = 60.dp)
.clip(MaterialTheme.shapes.large)
.background(MaterialTheme.colors.surface)
.clickable {
focusRequester.requestFocus()
keyboard?.show()
},
value = value.getOrNull(it)?.toString() ?: "",
isCursorVisible = value.length == it
)
if (it != length - 1) Spacer(modifier = Modifier.size(8.dp))
}
}
}
Result:
First make the common TextField for OTP Screen
#Composable
fun CommonOtpTextField(otp: MutableState<String>) {
val max = 1
OutlinedTextField(
value = otp.value,
singleLine = true,
onValueChange = { if (it.length <= max) otp.value = it },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
shape = RoundedCornerShape(20.dp),
modifier = Modifier
.width(60.dp)
.height(60.dp),
maxLines = 1,
textStyle = LocalTextStyle.current.copy(
textAlign = TextAlign.Center
)
)
}
And Now use above CommonTextField to make four otp Field
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 15.dp, start = 15.dp, end = 15.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
CommonOtpTextField(otp = otpOne)
CommonOtpTextField(otp = otpTwo)
CommonOtpTextField(otp = otpThree)
CommonOtpTextField(otp = otpFour)
}
Simple solution that uses one TextField and different code length
#Composable
fun RegistrationCodeInput(codeLength: Int) {
val code = remember { mutableStateOf("") }
val focusRequester = FocusRequester()
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
BasicTextField(
value = code.value,
onValueChange = { if (it.length <= codeLength) code.value = it },
Modifier.focusRequester(focusRequester = focusRequester),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
decorationBox = {
CodeInputDecoration(code.value, codeLength)
})
}
}
#Composable
private fun CodeInputDecoration(code: String, length: Int) {
Box(
modifier = Modifier
.padding(16.dp)
.border(
border = BorderStroke(2.dp, color = borderColor),
shape = Shapes.small
)
) {
Row(
) {
for (i in 0 until length) {
val text = if (i < code.length) code[i].toString() else ""
CodeEntry(text)
}
}
}
}
#Composable
private fun CodeEntry(text: String) {
Box(
modifier = Modifier
.width(42.dp)
.height(42.dp),
contentAlignment = Alignment.Center
) {
Text(text = text)
}
}
#Preview
#Composable
fun PreviewInput() {
RegistrationCodeInput(4)
}

TextField with hint text in jetpack compose

I want to create textfield with hint text in jetpackcompose. Any example how create textfield using jectpack? Thanks
compose_version = '1.0.0-beta07'
Use parameter placeholder to show hint
TextField(value = "", onValueChange = {}, placeholder = { Text("Enter Email") })
Use parameter label to show floating label
TextField(value = "", onValueChange = {}, label = { Text("Enter Email") })
You can use
the label parameter in the TextField composable to display a floating label
the placeholder parameter to display a placeholder when the text field is in focus and the input text is empty.
You can also use them together.
Something like:
var text by remember { mutableStateOf("text") }
OutlinedTextField(
value = text,
onValueChange = {
text = it
},
label = {
Text("Label")
}
)
or
TextField(
value = text,
onValueChange = {
text = it
},
label = {
Text("Label")
},
placeholder = {
Text("Placeholder")
}
)
You can create hintTextField in jetpackCompose like below code:
#Composable
fun HintEditText(hintText: #Composable() () -> Unit) {
val state = state { "" } // The unary plus is no longer needed. +state{""}
val inputField = #Composable {
TextField(
value = state.value,
onValueChange = { state.value = it }
)
}
if (state.value.isNotEmpty()) {
inputField()
} else {
Layout(inputField, hintText) { measurable, constraints ->
val inputfieldPlacable = measurable[inputField].first().measure(constraints)
val hintTextPlacable = measurable[hintText].first().measure(constraints)
layout(inputfieldPlacable.width, inputfieldPlacable.height) {
inputfieldPlacable.place(0.ipx, 0.ipx)
hintTextPlacable.place(0.ipx, 0.ipx)
} }
}
}
Call #Compose function like below:
HintEditText #Composable {
Text(
text = "Enter Email",
style = TextStyle(
color = Color.White,
fontSize = 18.sp
)
)
}
Jetpack compose version: dev08
The benefit of compose is that we can easily create our widgets by composing current composable functions.
We can just create a function with all parameters of the current TextField and add a
hint: String parameter.
#Composable
fun TextFieldWithHint(
value: String,
modifier: Modifier = Modifier.None,
hint: String,
onValueChange: (String) -> Unit,
textStyle: TextStyle = currentTextStyle(),
keyboardType: KeyboardType = KeyboardType.Text,
imeAction: ImeAction = ImeAction.Unspecified,
onFocus: () -> Unit = {},
onBlur: () -> Unit = {},
focusIdentifier: String? = null,
onImeActionPerformed: (ImeAction) -> Unit = {},
visualTransformation: VisualTransformation? = null,
onTextLayout: (TextLayoutResult) -> Unit = {}
) {
Stack(Modifier.weight(1f)) {
TextField(value = value,
modifier = modifier,
onValueChange = onValueChange,
textStyle = textStyle,
keyboardType = keyboardType,
imeAction = imeAction,
onFocus = onFocus,
onBlur = onBlur,
focusIdentifier = focusIdentifier,
onImeActionPerformed = onImeActionPerformed,
visualTransformation = visualTransformation,
onTextLayout = onTextLayout)
if (value.isEmpty()) Text(hint)
}
}
We can use it like this:
#Model
object model { var text: String = "" }
TextFieldWithHint(value = model.text, onValueChange = { data -> model.text = data },
hint= "Type book name or author")
The pitfall of this approach is we are passing the hint as a string so if we want to style the hint we should add extra parameters to the TextFieldWithHint (e.g hintStyle, hintModifier, hintSoftWrap, ...)
The better approach is to accept a composable lambda instead of string:
#Composable
fun TextFieldWithHint(
value: String,
modifier: Modifier = Modifier.None,
hint: #Composable() () -> Unit,
onValueChange: (String) -> Unit,
textStyle: TextStyle = currentTextStyle(),
keyboardType: KeyboardType = KeyboardType.Text,
imeAction: ImeAction = ImeAction.Unspecified,
onFocus: () -> Unit = {},
onBlur: () -> Unit = {},
focusIdentifier: String? = null,
onImeActionPerformed: (ImeAction) -> Unit = {},
visualTransformation: VisualTransformation? = null,
onTextLayout: (TextLayoutResult) -> Unit = {}
) {
Stack(Modifier.weight(1f)) {
TextField(value = value,
modifier = modifier,
onValueChange = onValueChange,
textStyle = textStyle,
keyboardType = keyboardType,
imeAction = imeAction,
onFocus = onFocus,
onBlur = onBlur,
focusIdentifier = focusIdentifier,
onImeActionPerformed = onImeActionPerformed,
visualTransformation = visualTransformation,
onTextLayout = onTextLayout)
if (value.isEmpty()) hint()
}
}
We can use it like this:
#Model
object model { var text: String = "" }
TextFieldWithHint(value = model.text, onValueChange = { data -> model.text = data },
hint= { Text("Type book name or author", style = TextStyle(color = Color(0xFFC7C7C7))) })
var textState by remember { mutableStateOf(TextFieldValue()) }
var errorState by remember { mutableStateOf(false) }
var errorMessage by remember { mutableStateOf("") }
TextField(
value = textState,
onValueChange = {
textState = it
when {
textState.text.isEmpty() -> {
errorState = true
errorMessage = "Please Enter Site Code"
}
else -> {
errorState = false
errorMessage = ""
}
}
},
isError = errorState,
label = {
Text(
text = if (errorState) errorMessage
else "You Hint"
)
},
modifier = Modifier
.padding(top = 20.dp, start = 30.dp, end = 30.dp)
.fillMaxWidth())
If you want to create a text field with hint text and custom color here is the code for that.
#Composable
fun PhoneOrEmail() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Phone/email", style = TextStyle(color =
Color.Red)) }
)//End of TextField
}//End of Function
Here's what works for me (I think it's a bit simpler than what Anas posted since it's using the same component:
#Composable
fun TextBox(
loginInput: LoginInput,
hint: String = "enter value",
color: Color = Color.LightGray,
height: Dp = 50.dp
) {
val state = +state { "" }
state.value = if (loginInput.usernameEntered) loginInput.username else hint
Surface(color = color) {
Row {
Container(modifier = Expanded, height = height) {
Clip(shape = RoundedCornerShape(15.dp)) {
Padding(padding = 15.dp) {
TextField(
value = state.value,
keyboardType = KeyboardType.Text,
onFocus = {
if (!loginInput.usernameEntered)
state.value = ""
},
onValueChange = {
loginInput.usernameEntered = true
loginInput.username = it
state.value = loginInput.username
}
)
}
}
}
}
}
}
the label parameter will be displayed as text if text is empty and moves above the textfield (as label) on typing input:
#Composable
fun SearchField() {
val (text, setText) = remember { mutableStateOf(TextFieldValue("")) }
Box(modifier = Modifier.width(180.dp).padding(2.dp)) {
TextField(
modifier = Modifier.fillMaxWidth(),
value = text,
onValueChange = { setText(it) },
label = { Text("quick do:") },
)
}
}

Categories

Resources