Aligning a resized TextField in Jetpack Compose - kotlin

I am having problem aligning a resized TextField in Jetpack Compose for Desktop. When I resize the width of the TextField, the TextField automatically adjust itself to center position on screen. I have tried using Modify.Align and it did not work.
Can someone help? Here is my code
#Composable
fun addSales(sales: Sales,
actionChangeSales: (sales: Sales) -> Unit,
actionQuantityInc: (() -> Unit),
actionItemNameInc: (() -> Unit)){
Column{
TextField(
value = sales.itemName,
textStyle = TextStyle(color = Color.DarkGray),
onValueChange = {
actionChangeSales(Sales(itemName = it))
actionItemNameInc.invoke()
},
label = { Text("Item name")}
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = roundQuantity(sales.quantitySold),
textStyle = TextStyle(color = Color.DarkGray),
onValueChange = {
actionChangeSales(Sales(quantitySold = getDoubleFromEditText(it)))
actionQuantityInc.invoke()
},
label = { Text("Quantity")},
modifier = Modifier.width(120.dp)
)
}
}

As a workaround, try to wrap OutlinedTextField with Box and apply the width modifier there:
Box(modifier = Modifier.width(120.dp)) {
OutlinedTextField(
value = "123456789",
textStyle = TextStyle(color = Color.DarkGray),
onValueChange = {},
label = { Text("Quantity") },
)
}

You have to wrap them in a Column again.
Pseudo code as below
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifiler.fillMaxWidth() // needed so it knows the full width to center the content within it
) {
// This wraps your content and centers it
Column( horizontalAlignment = Alignment.Start ){
// This wraps and aligns the content to the left/start
// place your content here
}
}

Related

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

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()

How can I position snackbar on top of each other with floating action button in kotlin compose?

I have been doing dictionary app for a while. when I delete dictionary snackBar shows and writes dictionary is deleted but there is a floating action button and when the snackBar appears on the screen ,the snackbar appears above the floating action button, I don't want it to appear on it. It just stays on the screen for 1-2 seconds. I want the floating action button and snackbar to appear on top of each other. I couldn't adapt this to my own code. How can I do it ? I will share my code and image
CreateYourOwnDictionaryScreen
#Composable
fun CreateYourOwnDictionaryScreen(
navController: NavController,
viewModel: CreateYourOwnDictionaryViewModel = hiltViewModel()
) {
val scaffoldState = rememberScaffoldState()
val state = viewModel.state.value
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
backgroundColor = bar,
title = {
androidx.compose.material3.Text(
text = "your dictionaries",
modifier = Modifier.fillMaxWidth(),
color = Color.White,
fontSize = 22.sp
)
},
navigationIcon = {
IconButton(onClick = {
navController.navigate(Screen.MainScreen.route)
}) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Go Back"
)
}
}
)
},
floatingActionButtonPosition = FabPosition.Center,
floatingActionButton = {
FloatingActionButton(
onClick = { navController.navigate(Screen.CreateDicScreen.route) },
backgroundColor = bar,
) {
Icon(Icons.Filled.Add, "fab")
}
}
) {
Box(modifier = Modifier.background(MaterialTheme.colors.background)) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(state.dictionaries) { dictionary ->
CreateYourOwnDictionaryItem(
dictionary = dictionary,
modifier = Modifier
.fillMaxWidth()
.clickable {
},
onDeleteClick = {
viewModel.onEvent(
CreateYourOwnDictionaryEvents.DeleteDictionary(dictionary)
)
scope.launch {
val result = scaffoldState.snackbarHostState.showSnackbar(
message = "dictionary is deleted",
actionLabel = "Undo",
duration = SnackbarDuration.Short
)
}
},
onEditClick = {
})
}
}
}
}
}
}
Image
I'm afraid this is difficult in Compose, you can dig the ScaffoldLayout and you'll find this code block.
val snackbarOffsetFromBottom = if (snackbarHeight != 0) {
snackbarHeight + (fabOffsetFromBottom ?: bottomBarHeight)
} else {
0
}
Scaffold will always offset the snackbar on top of a fab or a bottombar
And based on the answer in this post regarding material specs
This is specifically one of the "Don't" examples from the Material guidelines: https://material.io/components/snackbars#behavior
Making sure visual elements don't move out from underneath (say, when users are about to tap the FAB) is one of the key points to making a predictable UI
Also based on the Material Guidelines
Consecutive snackbars should appear above persistent bottom navigation

Jetpack Compose - Can't change AlertDialog white window background

I'm new to Kotlin Multiplatform Desktop development and I'm using Compose to build UI. I have one AlertDialog with custom, rounded background, problem is, it still has white corners which I can not remove for some reasons. The dialog:
The code I've written to achieve this:
AlertDialog(
title = { Text("თქვენ ტოვებთ პროგრამას") },
text = { Text("ნამდვილად გსურთ პროგრამიდან გასვლა?") },
onDismissRequest = {},
backgroundColor = Colors.DARKER_GRAY,
contentColor = Colors.WHITE,
shape = RoundedCornerShape(16.dp),
confirmButton = {
TextButton(onClick = onPositiveClick) {
Text(text = "დიახ", color = Colors.RED)
}
},
dismissButton = {
TextButton(onClick = onNegativeClick) {
Text(text = "არა", color = Colors.LIGHTER_GRAY)
}
},
modifier = Modifier.defaultMinSize(300.dp).border(0.dp, Color.Transparent, RoundedCornerShape(0))
)
Anyone has any idea how can I solve this? Thank You in advance.
გაუმარჯოს! Try to add the same shape size to Modifier's border as you applied to Dialog's shape:
modifier = Modifier.defaultMinSize(300.dp).border(0.dp, Color.Transparent, RoundedCornerShape(16.dp))
Or you just remove it. The AlertDialogs come without borders.
Perhaps there might be difference between Android and Desktop and you really need that.
At least from Android point of view that's not necessary.
წარმატება!
If I may ask you, is that the preview because if I wrap your code in a column and change its background colour and runs it, it works perfectly okay.
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Red),
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
///Your code is here
AlertDialog(
title = { Text("თქვენ ტოვებთ პროგრამას") },
text = { Text("ნამდვილად გსურთ პროგრამიდან გასვლა?") },
onDismissRequest = {},
backgroundColor = Color.Gray,
contentColor = Color.White,
shape = RoundedCornerShape(16.dp),
confirmButton = {
TextButton(onClick = { /*onPositiveClick*/ }) {
Text(text = "დიახ", color = Color.Red)
}
},
dismissButton = {
TextButton(onClick = { /*onNegativeClick*/ }) {
Text(text = "არა", color = Color.LightGray)
}
},
modifier = Modifier
.defaultMinSize(300.dp)
.border(0.dp, Color.Transparent, RoundedCornerShape(0))
)
}
The bug is related to the native android theme color.
Just change background color on your theme:
<item name="android:background">#android:color/transparent</item>

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.

DecorationBox in Jetpack Compose BasicTextField

I use jetpack compose create a editText,I want to show a hint like before "android:hint",
so I try to use decorationBox,but after I created it the input isn't displayed and the log can display my input content.
this is my code,
val passState= remember { mutableStateOf(TextFieldValue("")) }
BasicTextField(
decorationBox = {
Text("password",color = loginGrayColor)
},
value = passState.value,
onValueChange = { passState.value = it ; Log.d("password",it.text) },
singleLine = true,
maxLines = 1,
textStyle = TextStyle(
fontSize = 15.sp,
color = loginInputTextColor
),
modifier = Modifier
.padding(start = 10.dp, top = 10.dp)
.height(20.dp)
)
You have to add the innerTextField provided by the decorationBox.
Something like:
var value by remember { mutableStateOf(TextFieldValue("")) }
BasicTextField(
value = value,
onValueChange = { value = it },
decorationBox = { innerTextField ->
Row(
Modifier
.background(Color.LightGray, RoundedCornerShape(percent = 30))
.padding(16.dp)
) {
if (value.text.isEmpty()) {
Text("Label")
}
innerTextField() //<-- Add this
}
},
)
If you would like to have the cursor start at the beginning of the placeholder label, put the decorationBox content inside a Box rather than a Row.