So, I have an AlertDialog with a ListItem inside, which in turn has 2 TextFields inside:
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val (nameFieldFocus, valueFieldFocus) = remember{FocusRequester.createRefs()}
AlertDialog(
...
text = {
ListItem(
...
leadingContent = {
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
TextField(
modifier = Modifier.focusRequester(nameFieldFocus).focusProperties{next = valueFieldFocus}.focusable(),
value = someValue,
onValueChange = {someValue = it},
label = {Text(text = "Some label")},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(onNext = {focusManager.moveFocus(FocusDirection.Next)}),
singleLine = true
)
TextField(
modifier = Modifier.focusRequester(valueFieldFocus).focusProperties{previous = nameFieldFocus}.focusable(),
value = anotherValue,
onValueChange = {anotherValue = it},
label = {Text(text = "Another label")},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
keyboardActions = KeyboardActions(onDone = {focusManager.clearFocus()}),
singleLine = true
)
}
}
)
}
)
The problem is, when I press the action key on the keyboard, no focus change occurs, regardless of whether the text fields have text in them or not. I also tried onNext = null and onDone = null, which is supposed to use the default implementation according to the documentation, but that also doesn't behave as expected.
P.S. I'm using androidx.compose.material3.
You should call val focusManager = LocalFocusManager.current inside AlertDialog. Also, remove the Modifier.focusable().
AlertDialog(
text = {
val focusManager = LocalFocusManager.current
val (nameFieldFocus, valueFieldFocus) = remember { FocusRequester.createRefs() }
ListItem(
leadingContent = {
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
TextField(
modifier = Modifier
.focusRequester(nameFieldFocus)
.focusProperties { next = valueFieldFocus },
value = someValue,
onValueChange = { someValue = it },
label = { Text(text = "Some label") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(onNext = {
focusManager.moveFocus(
FocusDirection.Next
)
}),
singleLine = true
)
TextField(
modifier = Modifier
.focusRequester(valueFieldFocus)
.focusProperties { previous = nameFieldFocus },
value = anotherValue,
onValueChange = { anotherValue = it },
label = { Text(text = "Another label") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
singleLine = true
)
}
},
headlineText = {
Text(text = "Headline Text")
}
)
},
confirmButton = {
Button(onClick = { /*TODO*/ }) {
Text(text = "Confirm")
}
},
onDismissRequest = {
}
)
Related
I am having an odd issue with jetpack compose dropdown menus.
I am working on an app that requires the user to register an account which requires 3 drop down menus (one for each question). The problem I am running into is that only the last one will update the text despite them all being written the exact same way.
I have tried to make a separate project and the problem still is still present. I have looked for tutorials but all of them have dealt with only one dropdown.
Here is the code any help would be greatly appreciated Thank you for your time and any help
Code:
package com.example.jetpackcomposedropdownissue
import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.jetpackcomposedropdownissue.ui.theme.JetpackComposeDropDownIssueTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeDropDownIssueTheme {
val context: Context = LocalContext.current
var securityQuestions = listOf(
"Please select a security Question",
"What is your favorite color ?",
"First pets name ?",
"Favorite musical artist or band ?",
"Favorite animal ?",
"Favorite tv Show ?"
)
var expanded by remember { mutableStateOf(false) }
var selectedSecurityQuestion1Text by remember { mutableStateOf(securityQuestions[0]) }
var selectedSecurityQuestion2Text by remember { mutableStateOf(securityQuestions[0]) }
var selectedSecurityQuestion3Text by remember { mutableStateOf(securityQuestions[0]) }
var icon = if (expanded) {
Icons.Filled.KeyboardArrowUp
} else {
Icons.Filled.KeyboardArrowDown
}
var createUserSecurityQuestion1AnswerState by remember { mutableStateOf("") }
var createUserSecurityQuestion2AnswerState by remember { mutableStateOf("") }
var createUserSecurityQuestion3AnswerState by remember { mutableStateOf("") }
var createUserUserNameTextFieldState by remember { mutableStateOf("") }
var createUserUserKeyTextFieldState by remember { mutableStateOf("") }
var createUserConfirmUserKeyTextFieldState by remember { mutableStateOf("") }
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
TextField(
value = selectedSecurityQuestion1Text,
onValueChange = { selectedSecurityQuestion1Text = it },
label = { Text(text = "Selected Security Question") },
trailingIcon = {
Icon(icon, "", Modifier.clickable { expanded = !expanded })
},
modifier = Modifier
.padding(PaddingValues(start = 70.dp, top = 40.dp))
.size(width = 315.dp, height = 60.dp)
)
DropdownMenu(expanded = expanded,
onDismissRequest = { expanded = false }
) {
securityQuestions.forEach { label ->
DropdownMenuItem(onClick = {
selectedSecurityQuestion1Text = label
expanded = true
}) {
Text(text = label)
}
}
}
TextField(
value = createUserSecurityQuestion1AnswerState,
label = {
Text("Answer 1")
},
onValueChange = {
createUserSecurityQuestion1AnswerState = it
},
singleLine = true,
modifier = Modifier
.padding(
PaddingValues(start = 70.dp, 110.dp)
)
.size(width = 210.dp, 48.dp)
)
TextField(
value = selectedSecurityQuestion2Text,
onValueChange = { selectedSecurityQuestion2Text = it },
label = { Text(text = "Selected Security Question") },
trailingIcon = {
Icon(icon, "", Modifier.clickable { expanded = !expanded })
},
modifier = Modifier
.padding(PaddingValues(start = 70.dp, top = 160.dp))
.size(width = 315.dp, height = 60.dp)
)
DropdownMenu(expanded = expanded,
onDismissRequest = { expanded = false }
) {
securityQuestions.forEach { label ->
DropdownMenuItem(onClick = {
selectedSecurityQuestion2Text = label
expanded = true
}) {
Text(text = label)
}
}
}
TextField(
value = createUserSecurityQuestion2AnswerState,
label = {
Text("Answer 2")
},
onValueChange = {
createUserSecurityQuestion2AnswerState = it
},
singleLine = true,
modifier = Modifier
.padding(
PaddingValues(start = 70.dp, 230.dp)
)
.size(width = 210.dp, 48.dp)
)
TextField(
value = selectedSecurityQuestion3Text,
onValueChange = { selectedSecurityQuestion3Text = it },
label = { Text(text = "Selected Security Question") },
trailingIcon = {
Icon(icon, "", Modifier.clickable { expanded = !expanded })
},
modifier = Modifier
.padding(PaddingValues(start = 70.dp, top = 280.dp))
.size(width = 315.dp, height = 60.dp)
)
DropdownMenu(expanded = expanded,
onDismissRequest = { expanded = false }
) {
securityQuestions.forEach { label ->
DropdownMenuItem(onClick = {
selectedSecurityQuestion3Text = label
expanded = true
}) {
Text(text = label)
}
}
}
TextField(
value = createUserSecurityQuestion3AnswerState,
label = {
Text("Answer 3")
},
onValueChange = {
createUserSecurityQuestion3AnswerState = it
},
singleLine = true,
modifier = Modifier
.padding(
PaddingValues(start = 70.dp, 350.dp)
)
.size(width = 210.dp, 48.dp)
)
TextField(
value = createUserUserNameTextFieldState,
label = {
Text("Username")
},
onValueChange = {
createUserUserNameTextFieldState = it
},
singleLine = true,
modifier = Modifier
.padding(
PaddingValues(start = 70.dp, 410.dp)
)
.size(width = 210.dp, 48.dp)
)
TextField(
value = createUserUserKeyTextFieldState,
label = {
Text("Userkey")
},
onValueChange = {
createUserUserKeyTextFieldState = it
},
singleLine = true,
modifier = Modifier
.padding(
PaddingValues(start = 70.dp, 470.dp)
)
.size(width = 210.dp, 48.dp)
)
TextField(
value = createUserConfirmUserKeyTextFieldState,
label = {
Text("Confirm Userkey")
},
onValueChange = {
createUserConfirmUserKeyTextFieldState = it
},
singleLine = true,
modifier = Modifier
.padding(
PaddingValues(start = 70.dp, 530.dp)
)
.size(width = 210.dp, 48.dp)
)
Button(
onClick = {
},
modifier = Modifier
.padding(PaddingValues(start = 100.dp, top = 600.dp))
.width(200.dp),
shape = RoundedCornerShape(20.dp)
) {
Text(text = "Create User")
}
Button(
onClick = {
},
modifier = Modifier
.padding(PaddingValues(start = 100.dp, top = 660.dp))
.width(200.dp),
shape = RoundedCornerShape(20.dp)
) {
Text(text = "Back To Login")
}
}
}
}
}
}
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
JetpackComposeDropDownIssueTheme {
}
}
I'm trying to do a ConstraintLayout in jetpack compose as I am having problems doing too many nested Columns and Rows.
Here is what I have:
#Composable
fun StateAndZipLayout(
modifier: Modifier,
onFormChanged: (FormType, String) -> Unit,
selectedLocation: Address,
stateError: Boolean,
zipError: Boolean
) {
val configuration = LocalConfiguration.current
val screenWidth = configuration.screenWidthDp.dp
val componentWidth = (screenWidth - 48.dp)/2
ConstraintLayout(modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()) {
val rightGuideline = createGuidelineFromStart(0.5f)
val (stateDropDown, shippingField) = createRefs()
StateSelection(
modifier = modifier
.constrainAs(stateDropDown) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
end.linkTo(rightGuideline, margin = 8.dp)
}
.requiredWidth(componentWidth)
.wrapContentHeight(),
onFormChanged = onFormChanged,
selectedLocation = selectedLocation,
label = "State",
error = stateError,
)
ShippingField(
modifier = modifier
.constrainAs(shippingField) {
start.linkTo(rightGuideline, margin = 8.dp)
top.linkTo(stateDropDown.top)
bottom.linkTo(stateDropDown.bottom)
end.linkTo(parent.end)
}
.requiredWidth(componentWidth),
onFormChanged = onFormChanged,
formType = FormType.SHIPPING_ZIP,
label = "Zip",
valueField = selectedLocation.zipCode,
error = zipError
)
}
}
Here is my state selection view:
#Composable
fun StateSelection(
modifier: Modifier,
onFormChanged: (FormType, String) -> Unit,
selectedLocation: Address,
error: Boolean,
label: String
) {
// State variables
val statesMap = AddressUtils.mapOfAmericanStatesToValue
var stateName: String by remember { mutableStateOf(selectedLocation.shippingState) }
var expanded by remember { mutableStateOf(false) }
val focusManager = LocalFocusManager.current
var errorState by remember { mutableStateOf(error) }
Column {
Row(
Modifier
.clickable {
expanded = !expanded
},
) { // Anchor view
TextField(
modifier = Modifier
.fillMaxWidth(),
value = stateName,
onValueChange = {
onFormChanged(FormType.SHIPPING_COUNTRY, it)
},
label = { Text(text = label) },
textStyle = MaterialTheme.typography.subtitle1,
singleLine = true,
trailingIcon = {
IconButton(onClick = { expanded = true }) {
Icon(
imageVector = Icons.Filled.ArrowDropDown,
contentDescription = "",
tint = if (errorState) MaterialTheme.colors.error
else MaterialTheme.colors.onPrimary
)
}
},
keyboardActions = KeyboardActions(onNext = {
focusManager.moveFocus(
FocusDirection.Down
)
}),
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
keyboardType = KeyboardType.Text
),
colors = TextFieldDefaults.textFieldColors(
cursorColor = MaterialTheme.colors.secondary,
textColor = MaterialTheme.colors.onPrimary,
focusedLabelColor = if (errorState) MaterialTheme.colors.error
else MaterialTheme.colors.secondary,
focusedIndicatorColor = if (errorState) MaterialTheme.colors.error
else MaterialTheme.colors.secondary,
backgroundColor = MaterialTheme.colors.secondaryVariant
)
) // state name label
DropdownMenu(expanded = expanded, onDismissRequest = {
expanded = false
}) {
statesMap.asIterable().iterator().forEach {
val (key, value) = it
DropdownMenuItem(
onClick = {
expanded = false
stateName = key
onFormChanged(FormType.SHIPPING_STATE, key)
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = key)
}
}
}
}
if (errorState && error) {
ErrorMessages(modifier = modifier, message = "$label is required")
}
}
}
This is what it looks like, the state drop down and the zip code field are overlapping:
I'm trying to do a constraint layout in my Compose view. Unfortunately, I get no output. I was having problems using columns to show error output after the TextField, so in desperation, I am using a constraint layout. Here is the component:
#Composable
fun StateAndZip(
modifier: Modifier,
onFormChanged: (FormType, String) -> Unit,
selectedLocation: Address,
stateError: Boolean,
zipError: Boolean
) {
ConstraintLayout {
val (stateDropDown, shippingField) = createRefs()
StateSelection(
modifier = modifier.constrainAs(stateDropDown) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(shippingField.start, margin = 8.dp)
width = Dimension.preferredWrapContent
height = Dimension.fillToConstraints
},
onFormChanged = onFormChanged,
selectedLocation = selectedLocation,
label = "State",
error = stateError,
)
ShippingField(
modifier = modifier.constrainAs(shippingField) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
end.linkTo(parent.end)
width = Dimension.preferredWrapContent
height = Dimension.fillToConstraints
},
onFormChanged = onFormChanged,
formType = FormType.SHIPPING_ZIP,
label = "Zip",
valueField = selectedLocation.zipCode,
error = zipError
)
}
}
Here is where I'm calling it:
#Composable
fun ShippingForm(
modifier: Modifier = Modifier,
onFormChanged: (FormType, String) -> Unit,
selectedLocation: Address,
validateErrors: Boolean
) {
var cityError by remember { mutableStateOf(false)}
var stateError by remember { mutableStateOf(false)}
var countryError by remember {mutableStateOf(false)}
var zipError by remember { mutableStateOf(false)}
if (validateErrors) {
if (selectedLocation.shippingCity.isBlank()) {
cityError = true
}
if (selectedLocation.shippingState.isBlank()) {
stateError = true
}
if (selectedLocation.zipCode.isBlank()) {
zipError = true
}
if (selectedLocation.shippingCountry.isBlank()) {
countryError = true
}
}
//already in a column, so no need to add another one.
Spacer(modifier = Modifier.height(spacerHeight()))
ShippingField(
modifier = modifier,
onFormChanged = onFormChanged,
formType = FormType.SHIPPING_2,
label = "Apartment, suite, etc. (Optional)",
valueField = selectedLocation.shipping2,
error = false
)
Spacer(modifier = Modifier.height(spacerHeight()))
ShippingField(
modifier = modifier,
onFormChanged = onFormChanged,
formType = FormType.SHIPPING_CITY,
label = "City",
valueField = selectedLocation.shippingCity,
error = cityError
)
Spacer(modifier = Modifier.height(spacerHeight()))
StateAndZip(
modifier = modifier,
onFormChanged = onFormChanged,
selectedLocation = selectedLocation,
stateError = stateError,
zipError = zipError
)
Spacer(modifier = Modifier.height(spacerHeight()))
CountrySelection(
onFormChanged = onFormChanged,
selectedLocation = selectedLocation,
label = "Country",
error = countryError
)
}
The individual views are quite complex. Here is the stateDropDown:
#Composable
fun StateSelection(
modifier: Modifier,
onFormChanged: (FormType, String) -> Unit,
selectedLocation: Address,
error: Boolean,
label: String
) {
// State variables
val statesMap = AddressUtils.mapOfAmericanStatesToValue
var stateName: String by remember { mutableStateOf(selectedLocation.shippingState) }
var expanded by remember { mutableStateOf(false)}
val focusManager = LocalFocusManager.current
var errorState by remember { mutableStateOf(error)}
// Create references for the composables to constrain
Box(
contentAlignment = Alignment.CenterStart,
modifier = modifier
) {
Row(
Modifier
.clickable {
expanded = !expanded
},
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) { // Anchor view
TextField(
modifier = Modifier
.fillMaxWidth(),
value = stateName,
onValueChange = {
onFormChanged(FormType.SHIPPING_COUNTRY, it)
},
label = { Text(text = label) },
textStyle = MaterialTheme.typography.subtitle1,
singleLine = true,
trailingIcon = {
IconButton(onClick = { expanded = true }) {
Icon(
imageVector = Icons.Filled.ArrowDropDown,
contentDescription = "",
tint = if (errorState) MaterialTheme.colors.error
else MaterialTheme.colors.onPrimary
)
}
},
keyboardActions = KeyboardActions(onNext = {
focusManager.moveFocus(
FocusDirection.Down
)
}),
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
keyboardType = KeyboardType.Text
),
colors = TextFieldDefaults.textFieldColors(
cursorColor = MaterialTheme.colors.secondary,
textColor = MaterialTheme.colors.onPrimary,
focusedLabelColor = if (errorState) MaterialTheme.colors.error
else MaterialTheme.colors.secondary,
focusedIndicatorColor = if (errorState) MaterialTheme.colors.error
else MaterialTheme.colors.secondary,
backgroundColor = MaterialTheme.colors.secondaryVariant
)
) // state name label
DropdownMenu(expanded = expanded, onDismissRequest = {
expanded = false
}) {
statesMap.asIterable().iterator().forEach {
val (key, value) = it
DropdownMenuItem(
onClick = {
expanded = false
stateName = key
onFormChanged(FormType.SHIPPING_STATE, key)
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = key)
}
}
}
}
}
if (errorState && error) {
ErrorMessages(modifier = modifier, message = "$label is required")
}
}
I have started to learn jetpack compose. I want to show bottomsheet in click of IconButton.But i got error #Composable invocations can only happen from the context of a #Composable function how I can implement this logic.
Here is ui screen
Here is code
#RequiresApi(Build.VERSION_CODES.O)
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun AddTaskScreen(navController: NavController) {
var taskTitle by remember { mutableStateOf("") }
val currentDate = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Date())
var taskDescription by remember { mutableStateOf("") }
val taskDuration by remember { mutableStateOf(currentDate.toString()) }
val taskTypes = listOf("Urgent", "Medium", "High")
var expanded by remember { mutableStateOf(false) }
var selectedOptionText by remember { mutableStateOf(taskTypes[0]) }
val clickHandler: () -> Unit = {
DateBottomSheet()
}
Column() {
AppToolBar(title = "AddTaskScreen") {
navController.navigateUp()
}
OutlinedTextField(
value = taskTitle,
label = { Text(text = "Please input task title") },
onValueChange = { text -> taskTitle = text },
modifier = textFieldModifier
)
OutlinedTextField(
value = taskDescription,
label = { Text(text = "Please input task description") },
onValueChange = { text -> taskDescription = text },
modifier = textFieldModifier.height(200.dp)
)
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = !expanded
},
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
) {
OutlinedTextField(
readOnly = true,
value = selectedOptionText,
onValueChange = { },
label = { Text("Task priority") },
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(
expanded = expanded
)
},
modifier = Modifier.fillMaxWidth()
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
}
) {
taskTypes.forEach { selectionOption ->
DropdownMenuItem(
onClick = {
selectedOptionText = selectionOption
expanded = false
}
) {
Text(text = selectionOption)
}
}
}
}
//task time textField
OutlinedTextField(
value = taskDuration,
readOnly = true,
label = { Text(text = "Please select task duration") },
onValueChange = { text -> taskDescription = text },
modifier = textFieldModifier,
trailingIcon = {
IconButton(
onClick = {
clickHandler.invoke()
}) {
Icon(Icons.Filled.DateRange, contentDescription = "")
}
}
)
}
}
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun DateBottomSheet() {
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = rememberBottomSheetState(
initialValue = BottomSheetValue.Collapsed
)
)
BottomSheetScaffold(
sheetContent = {
Column() {
Text(text = "ThisIsBottomSheet")
Text(text = "ThisIsBottomSheet")
Text(text = "ThisIsBottomSheet")
Text(text = "ThisIsBottomSheet")
Text(text = "ThisIsBottomSheet")
}
},
scaffoldState = bottomSheetScaffoldState
) {
}
But i got error #Composable invocations can only happen from the context of a #Composable function how I can implement this logic
You can simply use mutabelState for handling your button click event to show Bottom Sheet.
You can do following changes ->
#RequiresApi(Build.VERSION_CODES.O)
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun AddTaskScreen(navController: NavController) {
var taskTitle by remember { mutableStateOf("") }
val currentDate = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Date())
var taskDescription by remember { mutableStateOf("") }
val taskDuration by remember { mutableStateOf(currentDate.toString()) }
val taskTypes = listOf("Urgent", "Medium", "High")
var expanded by remember { mutableStateOf(false) }
var selectedOptionText by remember { mutableStateOf(taskTypes[0]) }
var openBottomSheet by rememberSaveable { mutableStateOf(false) }
Column() {
AppToolBar(title = "AddTaskScreen") {
navController.navigateUp()
}
OutlinedTextField(
value = taskTitle,
label = { Text(text = "Please input task title") },
onValueChange = { text -> taskTitle = text },
modifier = textFieldModifier
)
OutlinedTextField(
value = taskDescription,
label = { Text(text = "Please input task description") },
onValueChange = { text -> taskDescription = text },
modifier = textFieldModifier.height(200.dp)
)
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = !expanded
},
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
) {
OutlinedTextField(
readOnly = true,
value = selectedOptionText,
onValueChange = { },
label = { Text("Task priority") },
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(
expanded = expanded
)
},
modifier = Modifier.fillMaxWidth()
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
}
) {
taskTypes.forEach { selectionOption ->
DropdownMenuItem(
onClick = {
selectedOptionText = selectionOption
expanded = false
}
) {
Text(text = selectionOption)
}
}
}
}
//task time textField
OutlinedTextField(
value = taskDuration,
readOnly = true,
label = { Text(text = "Please select task duration") },
onValueChange = { text -> taskDescription = text },
modifier = textFieldModifier,
trailingIcon = {
IconButton(
onClick = {
openBottomSheet = true
}) {
Icon(Icons.Filled.DateRange, contentDescription = "")
}
}
)
if (openBottomSheet) {
DateBottomSheet()
}
}
}
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun DateBottomSheet() {
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = rememberBottomSheetState(
initialValue = BottomSheetValue.Collapsed
)
)
BottomSheetScaffold(
sheetContent = {
Column() {
Text(text = "ThisIsBottomSheet")
Text(text = "ThisIsBottomSheet")
Text(text = "ThisIsBottomSheet")
Text(text = "ThisIsBottomSheet")
Text(text = "ThisIsBottomSheet")
}
},
scaffoldState = bottomSheetScaffoldState
) {
}
val languages = listOf(
"en" to "English",
"vi" to "VietNam",
)
#Composable
fun LoginScreen(
navController: NavController?,
viewModel: LoginViewModel = hiltViewModel()
) {
val currentLanguageIndex = viewModel.language.observeAsState().value ?: 0
SetLanguage(currentLanguageIndex)
Scaffold() {
MainUI(navController = navController, viewModel = viewModel,index= currentLanguageIndex)
}
}
#Composable
fun SetLanguage(languageIndex: Int) {
val locale = Locale(if (languageIndex == 0) "en" else "vi")
val configuration = LocalConfiguration.current
configuration.setLocale(locale)
val resources = LocalContext.current.resources
resources.updateConfiguration(configuration, resources.displayMetrics)
}
#OptIn(ExperimentalComposeUiApi::class)
//#Preview(device = Devices.AUTOMOTIVE_1024p)
#Composable
fun MainUI(navController: NavController?, viewModel: LoginViewModel,index : Int) {
val keyboardController = LocalSoftwareKeyboardController.current
val image = painterResource(id = R.drawable.ic_login_img)
var emailValue by remember { mutableStateOf("") }
var passwordValue by remember { mutableStateOf("") }
val passwordVisibility = remember { mutableStateOf(false) }
val focusRequesterEmail = remember { FocusRequester() }
val focusRequesterPassword = remember { FocusRequester() }
val scrollState = rememberScrollState()
val checkState = remember {
mutableStateOf(true)
}
Column(
modifier = Modifier
.fillMaxWidth()
.background(color = Color.White),
horizontalAlignment = Alignment.End
) {
// DropdownDemo(viewModel = viewModel,index = index)
val scope = rememberCoroutineScope()
var expanded by remember { mutableStateOf(false) }
var selectedIndex by remember { mutableStateOf(index) }
Box(
modifier = Modifier
.width(150.dp)
.padding(top = 16.dp, end = 8.dp)
.wrapContentSize(Alignment.TopEnd)
) {
DropDownLabel(languages[selectedIndex].second, onClick = {
expanded = true
})
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.width(150.dp)
) {
languages.forEachIndexed { index, s ->
DropdownMenuItem(
onClick = {
selectedIndex = index
expanded = false
scope.launch {
viewModel.saveLocale(index)
}
},
) {
DropDownLabel(s.second, onClick = {
selectedIndex = index
expanded = false
scope.launch {
viewModel.saveLocale(index)
}
})
}
}
}
}
Spacer(modifier = Modifier.height(20.dp))
Column(
modifier = Modifier
.fillMaxSize()
.scrollable(state = scrollState, orientation = Orientation.Vertical),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.height(70.dp)
.width(140.dp),
contentAlignment = Alignment.TopCenter
) {
Image(painter = image, contentDescription = null)
}
Spacer(modifier = Modifier.height(19.dp))
Column(
modifier = Modifier
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = stringResource(R.string.hello),
style = MaterialTheme.typography.h4,
)
Spacer(modifier = Modifier.height(10.dp))
Text(
text = stringResource(R.string.enter_credentials_msg),
style = MaterialTheme.typography.body2,
fontSize = 14.sp
)
Spacer(modifier = Modifier.padding(10.dp))
Column(
modifier = Modifier
.fillMaxWidth(0.6f)
.padding(
start = 16.dp,
end = 16.dp
),
horizontalAlignment = Alignment.CenterHorizontally
) {
OutlinedTextField(
value = emailValue,
onValueChange = { emailValue = it },
label = { Text(text = stringResource(id = R.string.email),
style = MaterialTheme.typography.body2.copy(color = Gray1)) },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Next
),
placeholder = { Text(text = stringResource(id = R.string.email),
style = MaterialTheme.typography.body2.copy(color = Gray1)) },
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester = focusRequesterEmail),
colors = TextFieldDefaults.outlinedTextFieldColors(
unfocusedBorderColor = Gray2
),
keyboardActions = KeyboardActions(
onNext = {
CoroutineScope(Default).launch {
keyboardController?.hide()
delay(400)
focusRequesterPassword.requestFocus()
}
}
)
)
Spacer(modifier = Modifier.padding(5.dp))
OutlinedTextField(
value = passwordValue,
colors = TextFieldDefaults.outlinedTextFieldColors(
unfocusedBorderColor = Gray2
),
onValueChange = { passwordValue = it },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Done
),
trailingIcon = {
IconButton(onClick = {
passwordVisibility.value = !passwordVisibility.value
}) {
Icon(
painter = painterResource(id = if (passwordVisibility.value) R.drawable.ic_baseline_visibility_24 else R.drawable.ic_baseline_visibility_off_24),
contentDescription = "Visibility button",
)
}
},
label = { Text(stringResource(id = R.string.password),
style = MaterialTheme.typography.body2.copy(color = Gray1)) },
placeholder = { Text(text = stringResource(id = R.string.password),
style = MaterialTheme.typography.body2.copy(color = Gray1)) },
singleLine = true,
visualTransformation = if (passwordVisibility.value) VisualTransformation.None
else PasswordVisualTransformation(),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester = focusRequesterPassword),
keyboardActions = KeyboardActions(
onDone = { keyboardController?.hide() }
),
)
Spacer(modifier = Modifier.padding(25.dp))
Row(
modifier = Modifier.fillMaxWidth()
) {
Row(
modifier = Modifier.width(intrinsicSize = IntrinsicSize.Max),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(checked = checkState.value, onCheckedChange = {
checkState.value = it
})
Text(
stringResource(id = R.string.remember_me),
maxLines = 1,
style = MaterialTheme.typography.body2.copy(color = Gray1)
)
}
Box(
contentAlignment = Alignment.CenterEnd,
modifier = Modifier.fillMaxWidth()
) {
OutlinedButton(
onClick = {
// TODO
},
border = BorderStroke(0.5.dp, BlueLight),
shape = RoundedCornerShape(8.dp)
) {
Text(
text = stringResource(id = R.string.sign_in),
fontSize = 20.sp,
style = MaterialTheme.typography.button.copy(color = BlueLight)
)
}
}
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp),
contentAlignment = Alignment.BottomCenter
) {
Text(
text = stringResource(id = R.string.skytech_software_solutions_pvt_ltd),
style = MaterialTheme.typography.caption.copy(color = Gray1),
)
}
}
}
}
#Composable
fun DropdownDemo(viewModel: LoginViewModel,index: Int) {
val scope = rememberCoroutineScope()
var expanded by remember { mutableStateOf(false) }
var selectedIndex by remember { mutableStateOf(index) }
Box(
modifier = Modifier
.width(150.dp)
.padding(top = 16.dp, end = 8.dp)
.wrapContentSize(Alignment.TopEnd)
) {
DropDownLabel(languages[selectedIndex].second, onClick = {
expanded = true
})
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.width(150.dp)
) {
languages.forEachIndexed { index, s ->
DropdownMenuItem(
onClick = {
selectedIndex = index
expanded = false
scope.launch {
viewModel.saveLocale(index)
}
},
) {
DropDownLabel(s.second, onClick = {
selectedIndex = index
expanded = false
scope.launch {
viewModel.saveLocale(index)
}
})
}
}
}
}
}
#Composable
fun DropDownLabel(labelName: String, onClick: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
) {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_language_24),
contentDescription = "language"
)
Spacer(modifier = Modifier.width(10.dp))
Text(
labelName, modifier = Modifier
.fillMaxWidth()
)
}
}
when we change the language UI does not update some of text like sign in button and Edit text. but normal text are changing. how do we over come this situation. I have used mutable live data for the viewModel to UI. when we change language and kill the application and restart, then update all the labels.