how do I pass cursor from one textfield to other textfield in jetpack compose? - kotlin

I have two textfields and the user will enter their weight and target weight. When the user clicks on the target weight, a picker appears and the user chooses his weight from there. If user selects his weight and presses the OK button, I want it to automatically switch to the target weight textfield.
hear is my code
I am sharing example textfield
Column(modifier = Modifier.fillMaxWidth()) {
TextField(
value = viewModel.defaultWeightValue.value ,
modifier = Modifier
.fillMaxWidth()
.padding(5.dp),
onValueChange = { viewModel.currentWeight.value = it },
label = { Text(text = "current weight (kg)") },
shape = RoundedCornerShape(5.dp),
colors = TextFieldDefaults.textFieldColors(
textColor = Grey2,
disabledTextColor = Color.Transparent,
backgroundColor = Grey3,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
),
interactionSource = remember { MutableInteractionSource() }
.also { interactionSource ->
LaunchedEffect(interactionSource) {
interactionSource.interactions.collect {
if (it is PressInteraction.Press) {
// works like onClick
viewModel.isUserClickTxtField.value = true
}
}
}
}
)
TextField(
value = viewModel.targetWeight.value,
modifier = Modifier
.fillMaxWidth()
.padding(5.dp),
onValueChange = { viewModel.targetWeight.value = it },
label = { Text(text = "target weight (kg)") },
shape = RoundedCornerShape(5.dp),
colors = TextFieldDefaults.textFieldColors(
textColor = Grey2,
disabledTextColor = Color.Transparent,
backgroundColor = Grey3,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
focusedLabelColor = DefaultDYTColor
)
)
}
DYTLoginAndContinueButton(
text = "continue",
navController,
Screen.SignUpFourthScreen.route
)
}
if (viewModel.isUserClickTxtField.value) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Bottom
) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.Gray),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
InfiniteNumberPicker(
modifier = Modifier.width(60.dp),
list = viewModel.weight,
firstIndex = 0,
onSelect = {
viewModel.weightPickerState.value = it
}
)
Text(text = "kg")
InfiniteNumberPicker(
modifier = Modifier.width(60.dp),
list = viewModel.gram,
firstIndex = 0,
onSelect = {
viewModel.grPickerState.value = it
}
)
Text(text = "gram")
}
Row(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
.background(Color.Gray)
.padding(start = 0.dp, top = 15.dp, end = 0.dp, bottom = 15.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "cancel",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = DefaultDYTColor,
modifier = Modifier
.padding(8.dp)
.clickable {
viewModel.isUserClickTxtField.value = false
})
Divider(
color = Color.White,
modifier = Modifier
.padding(8.dp)
.fillMaxHeight() //fill the max height
.width(1.dp)
)
TextButton(onClick = {
viewModel.isUserClickTxtField.value = false
viewModel.defaultWeightValue.value = "${viewModel.weightPickerState.value} ${viewModel.grPickerState.value}"
}) {
Text(
text = "Okey",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = DefaultDYTColor,
modifier = Modifier.padding(8.dp))
}
}
}
}
if user click textfield viewModel.isUserClickTxtField.value is true and picker pops up for user. if user click Okey button in picker that means user selected his weight on picker. I want to switch other textfield automatically how can I do that ?

You can use the solution provided by #bylazy or you can use the focusProperties modifier to specify the next/previous item for each TextField or composable in your screen
Then simply use the focusManager.moveFocus method to move the focus:
focusManager.moveFocus(FocusDirection.Next)
Something like:
val (item1, item2) = remember { FocusRequester.createRefs() }
val focusManager = LocalFocusManager.current
TextField(
value = text,
onValueChange = {text = it},
Modifier
.focusRequester(item1)
.focusProperties {
next = item2
right = item2
}
)
Button(
onClick = { focusManager.moveFocus(FocusDirection.Next) }
) { Text("OK") }
TextField(
value = text,
onValueChange = {text = it},
Modifier
.focusRequester(item2)
)
In your 1st TextField you can also add the keyboardActions attribute to move the focus clicking Done in the field.
TextField(
value = text,
onValueChange = {text = it},
Modifier
.focusRequester(item1)
.focusProperties {
next = item2
right = item2
},
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = { focusManager.moveFocus(FocusDirection.Next) }
)
)

Create and remember FocusRequester:
val focusRequester = remember { FocusRequester() }
Add this FocusRequester to the target TextField with modifier:
modifier = Modifier.focusRequester(focusRequester)
Use this code to request focus (cursor):
focusRequester.requestFocus()

Related

How stop BottomSheet from Overlaying BottomNavBar (with Jetpack Compose)

This is my first time using the BottomSheet.
I follow a tutorial and implemented the bottomsheet in a code with bottomnavbar. This was the result. {The bottomsheet overlaps the bottomnavbar, when it is collapsed}
As you can see the bottomsheet overlays the bottomnavbar.
Here's the code of the bottomSheet
#SuppressLint("UnusedMaterialScaffoldPaddingParameter")
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun BottomSheetLayout() {
val bottomSheetItems = listOf(
BottomSheetItem(title = "Notification", icon = Icons.Default.Notifications),
...
)
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
)
val coroutineScope = rememberCoroutineScope()
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
sheetShape = RoundedCornerShape(topEnd = 30.dp),
sheetContent = {
Column(
content = {
Spacer(modifier = Modifier.padding(16.dp))
Text(
text = "Create New",
modifier = Modifier
.fillMaxWidth(),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
fontSize = 21.sp,
color = Color.White
)
LazyVerticalGrid(
//cells = GridCells.Fixed(3),
columns = GridCells.Fixed(3)
) {
items(bottomSheetItems.size, itemContent = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.padding(top = 24.dp)
.clickable {
},
) {
Spacer(modifier = Modifier.padding(8.dp))
Icon(
bottomSheetItems[it].icon,
bottomSheetItems[it].title,
tint = Color.White
)
Spacer(modifier = Modifier.padding(8.dp))
Text(text = bottomSheetItems[it].title, color = Color.White)
}
})
}
}, modifier = Modifier
.fillMaxWidth()
.height(350.dp)
//.background(MaterialTheme.colors.primary)
.background(
brush = Brush.linearGradient(colors = listOf(MaterialTheme.colors.primary, Color.White)
)
)
.padding(16.dp)
)
},
) {
Column(modifier = Modifier.fillMaxSize()) {
Button(
modifier = Modifier
.padding(20.dp),
onClick = {
coroutineScope.launch {
if (bottomSheetScaffoldState.bottomSheetState.isCollapsed) {
bottomSheetScaffoldState.bottomSheetState.expand()
} else {
bottomSheetScaffoldState.bottomSheetState.collapse()
}
}
}
) {
Text(
text = "Click to show Bottom Sheet"
)
}
}
}
}
Thanks for your help in advance.
I'd also appreciate any tip and advice about the bottomSheet, thanks again.

How can I put the bottom bar inside the surface in kotlin compose?

I want the bottom bar to be where I show it at the bottom, but it will overlap with the surface, so I want the bottom bar at the bottom and the bottom bar on the surface. I tried to put it in the box, but I couldn't do that and I don't want to create your own dictionary text corrupted, I want to adjust accordingly. I couldn't adapt this to my own code. Please help me how can I do this.
my screen
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Card()
Surface(color = Color.White, modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = ZeroCornerSize, topEnd = ZeroCornerSize)) {
Column(modifier = Modifier
.padding(5.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(id = R.drawable.resim ),
contentDescription = "image",
modifier = Modifier
.padding(5.dp)
.requiredHeight(300.dp)
.requiredWidth(300.dp)
)
Spacer(modifier = Modifier.padding(16.dp))
OutlinedTextField(
value = text.value,
onValueChange = { text.value = it },
modifier= Modifier
.fillMaxWidth()
.padding(5.dp)
,
textStyle = TextStyle(color = Color.Black),
label = { Text(text = "dictionary name") },
colors = TextFieldDefaults.outlinedTextFieldColors(
//focusedBorderColor = Color.Blue,
unfocusedBorderColor = Color.Blue
),
)
Spacer(modifier=Modifier.padding(5.dp))
Button(onClick = {},
modifier= Modifier
.padding(5.dp)
.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
backgroundColor = orangish,
contentColor = Color.White),
shape = shapes.medium,
contentPadding = PaddingValues(8.dp),
) {
Text(text = "Save")
}
}
}
}
#Composable
fun Card(){
Surface(color = purplish, modifier = Modifier.fillMaxSize()) {
Column(verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.offset(y = (-30).dp)
) {
Spacer(modifier = Modifier.padding(vertical = 8.dp))
Text(text = "create your own dictionary", color = Color.White)
}
}
}
#Composable
fun bottomBar(){
var bottomState = remember {
mutableStateOf("Home")
}
Scaffold(
bottomBar = {
BottomNavigation(
modifier = Modifier
.clip(shape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp)),
backgroundColor = Color(0xFFFEDBD0),
contentColor = Color(0xFF442c2E)
) {
BottomNavigationItem(
selected = bottomState.equals("Home") ,
onClick = { bottomState.equals("Home")},
label = { Text(text = "Home")},
icon = { Icon(imageVector = Icons.Default.Home, contentDescription = null)}
)
BottomNavigationItem(
selected = bottomState.equals("Account") ,
onClick = { bottomState.equals("Account")},
label = { Text(text = "Account")},
icon = { Icon(imageVector = Icons.Default.AccountCircle, contentDescription = null)}
)
BottomNavigationItem(
selected = bottomState.equals("Search") ,
onClick = { bottomState.equals("Search")},
label = { Text(text = "Search")},
icon = { Icon(imageVector = Icons.Default.Search, contentDescription = null)}
)
BottomNavigationItem(
selected = bottomState.equals("Setting") ,
onClick = { bottomState.equals("Setting") },
label = { Text(text = "Setting")},
icon = { Icon(imageVector = Icons.Default.Settings, contentDescription = null)}
)
}
}
){
}
}
my screen right now
First, you can show bottomNavigation with Scaffold as you wrote in your code, but you don't need to use Scaffold if it's only purpose is to show bottomNavigation (inside your bottomBar() composable). You can use Box.
#Composable
fun bottomBar() {
val bottomState = remember {
mutableStateOf("Home")
}
Box {
BottomNavigation(
modifier = Modifier
.clip(shape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp)),
backgroundColor = Color(0xFFFEDBD0),
contentColor = Color(0xFF442c2E)
) {
BottomNavigationItem(
selected = bottomState.equals("Home"),
onClick = { bottomState.equals("Home") },
label = { Text(text = "Home") },
icon = { Icon(imageVector = Icons.Default.Home, contentDescription = null) }
)
BottomNavigationItem(
selected = bottomState.equals("Account"),
onClick = { bottomState.equals("Account") },
label = { Text(text = "Account") },
icon = {
Icon(imageVector = Icons.Default.AccountCircle,
contentDescription = null)
}
)
BottomNavigationItem(
selected = bottomState.equals("Search"),
onClick = { bottomState.equals("Search") },
label = { Text(text = "Search") },
icon = { Icon(imageVector = Icons.Default.Search, contentDescription = null) }
)
BottomNavigationItem(
selected = bottomState.equals("Setting"),
onClick = { bottomState.equals("Setting") },
label = { Text(text = "Setting") },
icon = { Icon(imageVector = Icons.Default.Settings, contentDescription = null) }
)
}
}
}
To show your bottomBar() on screen, you have several ways to do that. You can use Box again:
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
Card()
bottomBar()
Box(modifier = Modifier.fillMaxHeight()) {
Surface(
color = Color.White,
modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = ZeroCornerSize,
topEnd = ZeroCornerSize)
) {
...
}
}
}
}
With this approach we were forced to set contentAlignment to Bottom, and to wrap Surface with another Box layout and fill height of it.
Second way if you want bottomBar() to be in your Surface (inside composable "Card" (as you named it)), you can do that like this:
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
Card()
Box(modifier = Modifier.fillMaxHeight()) {
Surface(
color = Color.White,
modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = CornerSize(0.dp),
topEnd = CornerSize(0.dp))
) {
...
}
}
}
}
#Composable
fun Card() {
Surface(color = Color.Magenta, modifier = Modifier.fillMaxSize()) {
Column(
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.padding(vertical = 8.dp))
bottomBar()
}
}
}
and the bottomBar() stays as I mentioned above.
Extra:
I recommend you to use Scaffold for you design (if you don't have specific reason to put your bottomBar in Surface) because it has all features you need here. Here is your design only with Scaffold:
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Scaffold(
modifier = Modifier
.fillMaxSize(),
bottomBar = { bottomBar() }
) {
Surface(
color = Color.LightGray,
modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = CornerSize(0.dp),
topEnd = CornerSize(0.dp))
) {
...
}
}
}
P.S. While trying to run your code on my Android I've received several times
java.lang.IllegalArgumentException: Corner size in Px can't be negative(topStart = 78.75, topEnd = -196350.0, bottomEnd = 0.0, bottomStart = 0.0)!
change your ZeroCornerSize for Surface to CornerSize(0.dp) to avoid this error.
Your Screen on phone

Compose Desktop AlertDialog rounded corners

In a compose desktop application I'm displaying an AlertDialog with rounded corners shape but there still appears a white rectangle at the corners.
Here is my code:
AlertDialog(
modifier = Modifier
.size(280.dp, 260.dp)
.shadow(elevation = 20.dp),
onDismissRequest = {},
buttons = {
Button(
modifier = Modifier.padding(start = 100.dp, top = 0.dp),
onClick = { onClose() }
) {
Text(
text = "OK",
textAlign = TextAlign.Center
)
}
},
title = {
Text(
"A Title"
)
},
text = {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Some Text")
}
},
shape = RoundedCornerShape(24.dp),
backgroundColor = Color.Red
)
How can I get rid of the background white corners that are visible behind the dialog.
#Composable
fun CustomAlertDialog(showingDialog: Boolean = false, onClickButton: () -> Unit) {
val color = if (isSystemInDarkTheme()) WhiteColor else Black90
val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
AlertDialog(
modifier = Modifier.clip(RoundedCornerShape(12.sdp)), // corner rounded
onDismissRequest = {
openDialog.value = false
},
title = {
Text(text = "Title")
},
text = {
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center
) {
Text(stringResource(R.string.send_password_email), color = color)
}
},
buttons = {
Row(
modifier = Modifier.padding(all = 8.sdp),
horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier
.fillMaxWidth()
.clickable { openDialog.value = false }, text = "Ok", color = ColorMain, textAlign = TextAlign.Center
)
}
}
)
}
}
I was having an issue with .clip() not working because of the fact that Order of modifiers matters, the .clip() effect was being overwritten by my .background() modifier.
This doesn't work:
modifier = Modifier
.background(MyTheme.colors.background) // overwrites .clip()
.clip(RoundedCornerShape(28.dp)), // .clip() evaluated first
This does work:
modifier = Modifier
.clip(RoundedCornerShape(28.dp)) // then we can safely .clip()
.background(MyTheme.colors.background), // .background() evaluated first
Here is what the AlertDialog code looks like:
AlertDialog(
modifier = Modifier
.clip(RoundedCornerShape(28.dp))
.background(MyTheme.colors.background),
onDismissRequest = { /* On Dismiss */ },
text = { /* Text */ },
buttons = { /* Buttons */ },
title = { /* Title * / },
)
Just remember that the order of the modifiers is of significant importance.

How can I use textfield control for multi textfield in jetpack compose?

I have a multi text field in a screen in my android jetpack compose app and I want to check the textfields are not empty, button will be active and if one of textfield is empty, button will be active, I was asked for check box control, but I do not know how can I use it for multi text field, any idea?
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun LoginScreen(
navController: NavController,
) {
val email = remember { mutableStateOf(TextFieldValue()) }
val password = remember { mutableStateOf(TextFieldValue()) }
val passwordVisibility = remember { mutableStateOf(false) }
Column(
Modifier
.fillMaxSize()
,
horizontalAlignment = Alignment.CenterHorizontally
) {
val context = LocalContext.current
OutlinedTextField(
value = email.value,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = white,
focusedIndicatorColor = Grey,
unfocusedIndicatorColor = Grey,
focusedLabelColor = Grey,
unfocusedLabelColor = Grey,
cursorColor = custom,
textColor = custom,
),
onValueChange = { email.value = it },
label = { Text(text = "Email Address") },
placeholder = { Text(text = "Email Address") },
singleLine = true,
modifier = Modifier.fillMaxWidth(0.8f)
)
Spacer(modifier = Modifier.padding(2.dp))
OutlinedTextField(
value = password.value,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = white,
focusedIndicatorColor = Grey,
unfocusedIndicatorColor = Grey,
focusedLabelColor = Grey,
unfocusedLabelColor = Grey,
cursorColor = custom,
textColor = custom,
),
onValueChange = { password.value = it },
label = { Text(text = "Password") },
placeholder = { Text(text = "Password") },
singleLine = true,
modifier = Modifier.fillMaxWidth(0.8f),
trailingIcon = {
IconButton(onClick = {
passwordVisibility.value = !passwordVisibility.value
}) {
Icon(
painter = painterResource(id = R.drawable.password_eye),
contentDescription = "",
tint = if (passwordVisibility.value) custom else Grey
)
}
},
visualTransformation = if (passwordVisibility.value) VisualTransformation.None
else PasswordVisualTransformation()
)
Spacer(modifier = Modifier.padding(20.dp))
Button(
modifier = Modifier
.width(285.dp)
.height(55.dp),
onClick = {
focus.clearFocus(force = true)
model.onSignUpOne(
email.value.text,
password.value.text,
)
},
colors = ButtonDefaults.buttonColors(
backgroundColor = custom
),
shape = RoundedCornerShape(30)
) {
Text(
text = "Login",
style = TextStyle(
fontSize = 18.sp,
color = white,
)
}
}}}}
You can simply use the enabled property of the button for this.
Button(
enabled = email.value.isNotBlank() && password.value.isNotBlank()
) {
}

How to make BottomNavigationItem fill space available?

I want to make BottomNavigation with text appearing from right side of selected item. How can I make BottomNavigationItem fill available space or move other items, to prevent text from wrapping?
here's image
Tried this, but didn't work:
#Composable
fun BottomNavigationBar(
items: List<BottomNavItem>,
navController: NavController,
onItemClick: (BottomNavItem) -> Unit
) {
val backStackEntry = navController.currentBackStackEntryAsState()
BottomNavigation(
modifier = Modifier,
elevation = 0.dp,
backgroundColor = light
) {
items.forEach{
val selected = it.screen_route == backStackEntry.value?.destination?.route
BottomNavigationItem(
selected = selected,
selectedContentColor = primary_color,
unselectedContentColor = shaded,
onClick = { onItemClick(it) },
icon = {
Row(
modifier = if (selected) Modifier
.fillMaxWidth()
.padding(horizontal = 15.dp)
else Modifier
.padding(horizontal = 15.dp)
) {
Icon(
imageVector = it.icon,
contentDescription = it.title,
tint = if (selected) primary_color else shaded,
)
if (selected){
Text(
text = it.title,
color = primary_color,
textAlign = TextAlign.Center,
fontSize = 20.sp,
modifier = Modifier.padding(start = 2.dp).align(Alignment.CenterVertically),
overflow = TextOverflow.Visible
)
}
}
}
)
}
}
}
You can check solution from Jetsnack sample app. I think this is the same behavior you want to achieve.