I have a simple slider that I need to disable the button at star and when user moves the slider it should be enabled, i can disable it at first but i cant to enable it back, for some reason it doesnt take the new variable value this is my code.
My thoughts its because the var is declared at first and the slider value is capsuled somehow?
but if that is the case how do i pass the value to the component?
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun PopupWindowDialogStudent(
onConfirm: (Int, String) -> Unit,
parentUiState: StudentHomeUiState,
) {
// val openDialog = remember { mutableStateOf(parentUiState.showInAppFeedback) }
val openDialog = remember { mutableStateOf(!parentUiState.showInAppFeedback) }
var sliderPosition by remember { mutableStateOf(5f) }
var enable by remember { mutableStateOf(false) }
val recommend = sliderPosition.toInt()
Column(
) {
Box {
if (openDialog.value) {
Dialog(
onDismissRequest = { openDialog.value = false },
properties = DialogProperties(),
)
{
Box(
Modifier
.fillMaxWidth()
.height(500.dp)
.background(Color.White, RoundedCornerShape(10.dp))
.border(1.dp, color = Color.White, RoundedCornerShape(20.dp))
) {
Column(
modifier = Modifier
.fillMaxSize()
.fillMaxHeight()
.padding(horizontal = 5.dp)
.verticalScroll(rememberScrollState())
,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp)) {
var completeValue by remember { mutableStateOf("") }
completeValue = sliderPosition.toString()
Column(horizontalAlignment = Alignment.CenterHorizontally){
Slider(
value = sliderPosition,
onValueChange = {
sliderPosition = it
var enable = true
},
valueRange = 0f..10f,
steps = 9,
onValueChangeFinished = {
completeValue
},
modifier = Modifier.width(180.dp),
colors = SliderDefaults.colors(
thumbColor = secondaryColor,
activeTrackColor = Color.Blue
)
)
}
}
Spacer(modifier = Modifier.height(10.dp))
var comment by remember { mutableStateOf("") }
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
value = comment,
placeholder = { Text(text = "¿Tienes algún otro comentario?") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = {keyboardController?.hide()}),
onValueChange = {
comment = it
}
)
Spacer(modifier = Modifier.height(10.dp))
if (enable){
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
onConfirm(recommend,comment)
openDialog.value = !openDialog.value
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp))
}
}else{
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp))
}
}
}
}
}
}
}
}
}
}
In the Slider you can use the onValueChangeFinished to change the enable value:
var enable by remember { mutableStateOf(false) }
Slider(
value = sliderPosition,
onValueChange = {
sliderPosition = it
enable = true
},
onValueChangeFinished = {
completeValue
enable = false
},
//...
)
Also you can avoid to use the if statement for the Button. Just use:
Button(
//...
enabled = enable,
onClick = {
openDialog.value = !openDialog.value
}
) {
//...
}
Related
I am trying to move the focus from one component to another on a button click.
I have this code right now.
...
Column(
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.navigationBarsPadding()
.background(surfaceColors.surface)
) {
TopBar(
TopBarState(
endText = if (theViewPages[state.currentPageIndex].isShowSkip) stringResource(id = R.string.Skip) else null,
onEndTextPressed = { store.dispatch(TheViewAction.OnSkip) },
isBackButtonVisible = false
) //need to focus on this component when user clicks on button
)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxHeight()
) {
HorizontalPager(
HorizontalPagerState(
modifier = Modifier
.aspectRatio(1f / 1.5f),
count = theViewPages.size,
onPageChange = { store.dispatch(TheViewAction.OnPageChange(it)) },
manuallyScrollPage = state.manuallyScrollPage,
content = { currentPage ->
TheItemView(
item = TheViewItemModel(
theViewPages[currentPage].isShowSkip,
theViewPages[currentPage].title,
theViewPages[currentPage].image,
theViewPages[currentPage].description,
theViewPages[currentPage].buttonText
),
onButtonClick = {
if (state.currentPageIndex != theViewPages.size - 1) {
//when user clicks this button focus moves to above component
store.dispatch(TheViewAction.ManuallyScrollPage)
} else {
store.dispatch(TheViewAction.OnGettingStarted)
}
}
)
}
)
)
}
}
...
and I have this TheItemView
#Composable
fun TheItemView(
item: TheViewItemModel,
onButtonClick: () -> Unit
) {
val typoColors = EnhanceTheme.colors.typoColors
val defaultPadding = dimensionResource(id = DesignSystem.dimen.borderDefault)
val largePadding = dimensionResource(id = DesignSystem.dimen.large)
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
...
Text(
text = "ABCDF",
modifier = Modifier.padding(horizontal = largePadding)
)
Column(
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.weight(1f)
) {
Button(
buttonState =
ButtonState(
label = "TEST,
onClick = onButtonClick,
)
)
}
...
}
}
I want to do this for accessibility.
I have tried couple of option online but none of them worked.
tried this Jetpack Compose: Move focus between TextFields using D-Pad without onKeyEvent already and similar
Thanks.
You can use FocusRequester
Step 1
val scope = rememberCoroutineScope()
val focusRequester = remember { FocusRequester() }
Step 2
Attach FocusRequester to the modifier of target composable
Modifier.focusRequester(focusRequester)
Step 3
Request focus like this inside onClick lambda
scope.launch { focusRequester.requestFocus() }
Example
Surface(
modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.background
) {
val scope = rememberCoroutineScope()
val focusRequester = remember { FocusRequester() }
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
OutlinedTextField(
value = email,
onValueChange = { email = it },
modifier = Modifier
.fillMaxWidth(),
placeholder = {
Text(text = "email")
}
)
OutlinedTextField(
value = password,
onValueChange = { password = it },
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester),
placeholder = {
Text(text = "password")
}
)
Button(onClick = {
scope.launch {
focusRequester.requestFocus()
}
}) {
Text(text = "Move focus on password field")
}
}
}
I wrote an AutoCompletet TextField in Jetpack Compose. I used this view in a Row and between two others' views. The side views move down when autocomplete dropdown is opened. How can I fix this issue?
The Link of GIF file is end of page. in the GIF you can see side views of Autocomplete move to bottom when the suggest items is shows.
My AutoComplete codes:
#Composable
fun AutoCompleteTextField(
modifier: Modifier = Modifier,
list: List<Product>,
searchTerm: TextFieldValue,
updateSearchTerm: (TextFieldValue) -> Unit,
) {
var product by remember {
mutableStateOf("")
}
var heightTextFields by remember {
mutableStateOf(55.dp)
}
var textFieldSize by remember {
mutableStateOf(Size.Zero)
}
var expanded by remember {
mutableStateOf(false)
}
val interactionSource = remember {
MutableInteractionSource()
}
Column(
modifier = modifier
.width(200.dp)
// .padding(30.dp)
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = {
expanded = false
}
)
) {
Column(modifier = modifier.fillMaxWidth()) {
Row(
modifier = modifier
.border(BorderStroke(width = 1.dp, color = Color.LightGray))
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = {product = "" }) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "Add",
modifier = modifier
.padding(4.dp)
.align(Alignment.CenterVertically)
)
}
TextField(
modifier = Modifier
.fillMaxWidth()
.height(heightTextFields)
.onGloballyPositioned { coordinates ->
textFieldSize = coordinates.size.toSize()
},
value = product,
onValueChange = {
product = it
expanded = true
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
cursorColor = Color.Black
),
textStyle = TextStyle(
fontFamily = shabnamFontFamily,
color = MaterialTheme.colorScheme.secondary,
fontWeight = FontWeight.Bold,
fontSize = 14.sp,
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done
),
singleLine = true,
)
}
AnimatedVisibility(visible = expanded) {
Card(
modifier = modifier
.padding(horizontal = 4.dp)
.width(textFieldSize.width.dp),
elevation = 10.dp,
shape = RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp)
) {
LazyColumn(
modifier = Modifier.heightIn(max = 150.dp),
) {
if (product.isNotEmpty()) {
items(
list.filter {
it.productName.lowercase()
.contains(product.lowercase())
}
) { item ->
ProductItems(title = item.productName) { title ->
product = title
expanded = false
}
}
} else {
items(
items =
list
) { item ->
ProductItems(title = item.productName) { title ->
product = title
expanded = false
}
}
}
}
}
}
}
}
}
}
Screen GIF file
I have created one function called TextInput that contained TextField
#Composable
fun TextInput(
inputType: InputType
) {
var value by remember { mutableStateOf("") }
TextField(
modifier = Modifier.clip(RoundedCornerShape(30.dp)),
value = value,
onValueChange = { value = it },
leadingIcon = { Icon(imageVector = inputType.icon, null) },
label = { Text(text = inputType.label) },
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
),
singleLine = true,
keyboardOptions = inputType.keyboardOptions,
visualTransformation = inputType.visualTransformation,
)
}
Now on click of the button SIGN UP\SIGN IN I want to access the value and show success/error based on validation.
Button(
onClick = {
//here
},
modifier = Modifier
.padding(top = 30.dp)
.width(200.dp),
shape = RoundedCornerShape(20.dp)
) {
Text(
text = "SIGN IN",
)
}
==================================================
Button(
onClick = {
//here
},
modifier = Modifier
.padding(top = 5.dp)
.width(200.dp),
shape = RoundedCornerShape(20.dp)
) {
Text(text = "SIGN UP")
}
If you want to see whole code to understand what i mean exactly.
#Composable
fun Sign_in_up_Screen() {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(R.drawable.login),
contentDescription = "",
modifier = Modifier
.background(Color.Companion.Red)
.fillMaxWidth()
)
TopTextButton()
}
}
#Composable
fun Login_Screen() {
val context = LocalContext.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(20.dp),
modifier = Modifier.fillMaxSize()
) {
TextInput(InputType.Email)
TextInput(InputType.Password)
Button(
onClick = {
//here
},
modifier = Modifier
.padding(top = 30.dp)
.width(200.dp),
shape = RoundedCornerShape(20.dp)
) {
Text(
text = "SIGN IN",
)
}
}
}
#Composable
fun Signup_Screen() {
val context = LocalContext.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(20.dp),
modifier = Modifier.fillMaxSize()
) {
TextInput(InputType.FullName)
TextInput(InputType.Email)
TextInput(InputType.Password)
TextInput(InputType.ConfirmPassword)
Button(
onClick = {
//here
},
modifier = Modifier
.padding(top = 5.dp)
.width(200.dp),
shape = RoundedCornerShape(20.dp)
) {
Text(text = "SIGN UP")
}
}
}
sealed class InputType(
val label: String,
val icon: ImageVector,
val keyboardOptions: KeyboardOptions,
val visualTransformation: VisualTransformation
) {
object FullName : InputType(
label = "Full Name",
icon = Icons.Default.Person,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
visualTransformation = VisualTransformation.None
)
object Email : InputType(
label = "E-mail",
icon = Icons.Default.Email,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
visualTransformation = VisualTransformation.None
)
object Password : InputType(
label = "Password",
icon = Icons.Default.Lock,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
keyboardType = KeyboardType.Password
),
visualTransformation = PasswordVisualTransformation()
)
object ConfirmPassword : InputType(
label = "Confirm Password",
icon = Icons.Default.Lock,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
keyboardType = KeyboardType.Password
),
visualTransformation = PasswordVisualTransformation()
)
}
#Composable
fun TextInput(
inputType: InputType
) {
var value by remember { mutableStateOf("") }
TextField(
modifier = Modifier.clip(RoundedCornerShape(30.dp)),
value = value,
onValueChange = { value = it },
leadingIcon = { Icon(imageVector = inputType.icon, null) },
label = { Text(text = inputType.label) },
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
),
singleLine = true,
keyboardOptions = inputType.keyboardOptions,
visualTransformation = inputType.visualTransformation,
)
}
#Composable
fun TopTextButton() {
var screen by remember { mutableStateOf(false) }
Row(
modifier = Modifier.padding(bottom = 40.dp),
) {
TextButton( onClick = {
if (screen)
screen = !screen
}) {
Text(text = "Sign in")
}
Spacer(Modifier.width(145.dp))
TextButton(onClick = {
if (!screen)
screen = !screen
}) {
Text(text = "Sign up")
}
}
if (!screen){
Login_Screen()
}
if (screen) {
Signup_Screen()
}
}
Is this code correct or should I put a function for each {Textfield}?
So each text field has its own state
I am beginner android studio programming😅
You can add onValueChange param into TextInput like this;
#Composable
fun TextInput(
inputType: InputType,
onValueChange: (String) -> Unit
) {
TextField(
//
onValueChange = {
onValueChange(it)
value = it
}
//
}
print your value;
TextInput(InputType.Email){ textFieldValue ->
println(textFieldValue)
}
I want to create an alert dialog with Jetpack Compose. In this alert dialog include a TextField component from Compose.
I created that alert dialog but there is a problem here that when I press enter button where is on the device keyboard to add a new line my content is moving to up.
How can I solve this UI issue?
Thanks in advance.
The Problem;
Here is my code;
#Composable
fun SimpleAlert(
title: String = "Ask a Question",
onDismiss: () -> Unit,
onSubmit: (text: String) -> Unit
) {
val text = remember { mutableStateOf("") }
val textHint = "Write your question..."
val textLength = remember { mutableStateOf(0) }
val isAnonymous = remember { mutableStateOf(false) }
AlertDialog(
onDismissRequest = {
onDismiss()
},
title = {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.drawable.ic_exit),
contentDescription = "",
modifier = Modifier.
background(Color.Gray, CircleShape).clickable {
onDismiss()
}
)
}
Text(
text = title,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
},
text = {
Column(
verticalArrangement = Arrangement.Center
) {
TextField(
value = text.value,
onValueChange = {
if (it.length <= 200) {
textLength.value = it.length
text.value = it
}
},
label = {
Text(text = textHint)
},
modifier = Modifier
.padding(top = 16.dp)
.sizeIn(minHeight = 120.dp)
.background(Color.Transparent)
)
}
},
buttons = {
Row(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Switch(checked = isAnonymous.value, onCheckedChange = {
isAnonymous.value = it
})
Text(text = "Anonymously")
}
Text(text = "${textLength.value}/200")
}
Button(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, top = 0.dp, end = 16.dp, bottom = 24.dp),
onClick = {
onSubmit(text.value)
}) { Text(text = "Submit") }
}
)
}
SOLUTION ✅
To solve this issue just use Dialog instead of AlertDialog
This is definitely what you are looking for
TextField(
value = text.value,
onValueChange = {
if (it.length <= 200) {
textLength.value = it.length
text.value = it
}
},
label = {
Text(text = textHint)
},
modifier = Modifier
.padding(top = 16.dp)
.sizeIn(minHeight = 120.dp)
.background(Color.Transparent),
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
//Do something here
},
),
)
Below is the code for custom TextField. I have used TextField in Fragment and DialogFragment. I am having some issues while using it in DialogFragment. The phone keyboard opens when I click on the TextField below when it is used in Fragment. But even though it focuses on the TextField, the keyboard doesn't pop up when it is used in DialogFragment.
fun MyTextFiled(
search: (String) -> Unit,
query: String?
) {
var state by rememberSaveable { mutableStateOf(query) }
Card(
shape = RoundedCornerShape(dimensionResource(id = R.dimen.padding_5dp)),
) {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.height(36.dp).background(colorResource(id = R.color.background_wallet_searchView)),
) {
Icon(
painter = painterResource(id = R.drawable.ic_new_search),
contentDescription = null,
modifier = Modifier
.size(20.dp)
.padding(start = dimensionResource(id = R.dimen.padding_5dp)),
tint = colorResource(id = R.color.text_secondary),
)
BasicTextField(
value = state?:"",
onValueChange = {
search.invoke(it)
state = it
},
maxLines = 1,
modifier = Modifier
.weight(1F)
.align(Alignment.CenterVertically)
.padding(horizontal = dimensionResource(id = R.dimen.padding_5dp)),
singleLine = true,
textStyle = TextStyle(
color = colorResource(id = R.color.text_secondary),
fontSize = 13.sp,
fontStyle = MaterialTheme.typography.overline.fontStyle
),
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences,
autoCorrect = true,
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Search
),
decorationBox = { innerTextField ->
if (state.isNullOrEmpty()) {
Text(
text = stringResource(id = R.string.search),
style = MaterialTheme.typography.overline,
fontSize = 12.sp,
color = colorResource(id = R.color.text_secondary)
)
}
innerTextField()
}
)
if (!state.isNullOrEmpty())
Icon(
painter = painterResource(id = R.drawable.round_close_24),
contentDescription = null,
modifier = Modifier
.clickable {
state = ""
search.invoke("")
}
.size(20.dp)
.padding(end = dimensionResource(id = R.dimen.padding_5dp))
)
}
}
}
MessageScreen:
#Composable
fun MessageScreen(
messagesViewModel: MessagesViewModel,
navHostController: NavController,
sharedViewModel: SharedViewModel
) {
val listState = rememberLazyListState()
// Set State of Message Screen
val receiverProfile = sharedViewModel.receiverProfile
val senderProfile = sharedViewModel.senderProfile
Log.d("TAG", "MessageScreen: RECEIVER = $receiverProfile SENDER = $senderProfile")
// Get All Messages from Firebase
messagesViewModel.getAllMessageFromFirebase(receiverProfile, senderProfile)
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween,
) {
TopBar(
title = receiverProfile!!.displayName,
buttonIcon = painterResource(id = R.drawable.ic_back_arrow_back_24)
) {
navHostController.popBackStack()
}
Box(modifier = Modifier.weight(10f)) {
LazyColumn(
modifier = Modifier
.fillMaxWidth(),
verticalArrangement = Arrangement.Top,
state = listState
) {
items(items = messagesViewModel.allMessagesState) { message ->
MessageCard(message = message, sharedViewModel = sharedViewModel)
Log.d("TAG 14", message.toString())
}
CoroutineScope(Dispatchers.Main).launch {
if (messagesViewModel.allMessagesState.isNotEmpty()) {
listState.scrollToItem(messagesViewModel.allMessagesState.size - 1)
}
}
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp), contentAlignment = Alignment.Center
) {
SendMessageCard(messagesViewModel, sharedViewModel)
}
}
}
`
**SendMessageCard :-**
#Composable
fun SendMessageCard(messagesViewModel: MessagesViewModel, sharedViewModel: SharedViewModel) {
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
elevation = 8.dp
) {
Row(
modifier = Modifier,
horizontalArrangement = Arrangement.SpaceAround
) {
OutlinedTextField(
value = messagesViewModel.textState.value,
onValueChange = {
messagesViewModel.textState.value = it
},
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
trailingIcon = {
IconButton(onClick = {
messagesViewModel.sendMessage(
message = Message(
messagesViewModel.textState.value,
Timestamp.now(),
sharedViewModel.senderProfile!!.mailId
),
sharedViewModel = sharedViewModel
)
}) {
Icon(imageVector = Icons.Filled.Send, contentDescription = "Send Message")
}
},
textStyle = TextStyle(fontSize = 20.sp),
label = {
Text(text = "Type Message")
}
)
}
}
}
I figured out the solution to my problem. I used Dialog Compose inside Dialog Fragment and the keyboard popped up.