I am trying to use Compose and Kotlin in this code to make the button unclickable when counter == 3.
When I run the code and counter presumably is 3, the button stays clickable and wont change to uncklickable.
var counter = 0
var isClickable by remember { mutableStateOf(true) }
fun QuizScreen (modifier: androidx.compose.ui.Modifier) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top
){
Button( enabled = isClickable,onClick = {
if(counter == 3){
isClickable = false
}
}
}
}
Tried using remember variable so when I change the variable from onClick, it changes the state of the button, but it still wont change
You can use a condition (count <3) for the enabled parameter:
var count by rememberSaveable { mutableStateOf(0) }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top
) {
Text(text = "$count")
Button(
enabled = count <3,
onClick = { count++ }
) {
Text(text = "Count++")
}
}
In your code counter is not incremented and it is not a state and the Button is never recomposed.
#Composable
fun QuizScreen(modifier: Modifier = Modifier) {
var count by rememberSaveable { mutableStateOf(0) }
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top
) {
Text(text = count.toString())
Button( enabled = count <3,
onClick = { count++ }
) {
Text(text = "Count++")
}
}
}
Related
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
}
) {
//...
}
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 have a Dialog that I cant close when I press the button this is my code, I believe that is for the if cycle, but I need it to make it true under those circumstances, any idea that could help me here?
#Composable
fun PopupWindowDialog(
parentUiState: ParentHomeUiState,
) {
val openDialog = remember { mutableStateOf(false) }
var sliderPosition by remember { mutableStateOf(0f) }
if (!parentUiState.showInAppFeedback){
openDialog.value = true
}
val recommend = sliderPosition.toInt()
Column(
) {
Box {
if (openDialog.value) {
Dialog(
onDismissRequest = { openDialog.value = false },
properties = DialogProperties(),
){
Box(
Modifier
.fillMaxWidth()
.fillMaxHeight()
//.padding(vertical = 70.dp, horizontal = 10.dp)
.padding(vertical = 70.dp )
.background(Color.White, RoundedCornerShape(10.dp))
//.border(1.dp, color = Color.Black, RoundedCornerShape(20.dp))
.border(1.dp, color = Color.White, RoundedCornerShape(20.dp))
) {
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
openDialog.value = !openDialog.value
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp))
}
}
}
}
}
}
}
I have a Dialog that I cant close when I press the button
Since you didn't show how your posted code (composable) is being called, ill assume the .showInAppFeedback is evaluating true.
Pay attention to these parts of your codes
val openDialog = remember { mutableStateOf(false) }
...
...
if (!parentUiState.showInAppFeedback) {
openDialog.value = true
}
, openDialog is a state and when it changes value it will re-compose your entire composable, so when the dialog is visible, and when you set it to false via button click, the entire composable will re-compose and re-evaluate your if condition, setting openDialog.value to true again, and dialog will never close.
I don't know your use-case but you can wrap it inside a LaunchedEffect
LaunchedEffect(parentUiState.showInAppFeedback) {
openDialog.value = true
}
Since parentUiState.showInAppFeedback is not updated, openDialog.value will always be set to true when the view recomposes. You should use parentUiState.showInAppFeedback in the initial remember block but not for future recomposition.
#Composable
fun PopupWindowDialog(parentUiState: ParentHomeUiState) {
val openDialog = remember { mutableStateOf(!parentUiState.showInAppFeedback) }
var sliderPosition by remember { mutableStateOf(0f) }
val recommend = sliderPosition.toInt()
Column {
Box {
if (openDialog.value) {
Dialog(
onDismissRequest = { openDialog.value = false },
properties = DialogProperties(),
) {
Box(
Modifier
.fillMaxWidth()
.fillMaxHeight()
//.padding(vertical = 70.dp, horizontal = 10.dp)
.padding(vertical = 70.dp)
.background(Color.White, RoundedCornerShape(10.dp))
//.border(1.dp, color = Color.Black, RoundedCornerShape(20.dp))
.border(1.dp, color = Color.White, RoundedCornerShape(20.dp))
) {
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
openDialog.value = !openDialog.value
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp)
)
}
}
}
}
}
}
}
I have an image and I want to show a dropdownMenuItem when user click in the image. I was debugging the app and I can see that the code go through the DropdownDemo method but is not showing anything.
Am I doing something wrong?
Click code:
#Composable
fun Header(currentItem: CartListItems) {
var showDialog by remember { mutableStateOf(false) }
Box(Modifier.fillMaxWidth()) {
Text(modifier = Modifier.align(Alignment.TopStart),
text = currentItem.type,
color = colorResource(id = R.color.app_grey_dark),
fontSize = 12.sp)
Image(painter = painterResource(R.drawable.three_dots),
contentDescription = "more options button",
Modifier
.align(Alignment.CenterEnd)
.width(24.dp)
.height(6.75.dp)
.clickable(indication = null,
interactionSource = remember { MutableInteractionSource() },
onClick = {
showDialog = true
}))
if(showDialog) {
DropdownDemo()
showDialog = false
}
}
}
Dropmenu:
#Composable
fun DropdownDemo() {
var expanded by remember { mutableStateOf(false) }
val items = listOf("A", "B", "C", "D", "E", "F")
val disabledValue = "B"
var selectedIndex by remember { mutableStateOf(0) }
Box(modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.TopStart)) {
Text(items[selectedIndex],modifier = Modifier
.fillMaxWidth()
.clickable(onClick = { expanded = true })
.background(
Color.Gray
))
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.fillMaxWidth()
.background(
Color.Red
)
) {
items.forEachIndexed { index, s ->
DropdownMenuItem(onClick = {
selectedIndex = index
expanded = false
}) {
val disabledText = if (s == disabledValue) {
" (Disabled)"
} else {
""
}
Text(text = s + disabledText)
}
}
}
}
}
showDialog appears to be a MutableState object. Hence, when the image is clicked, it becomes true, and a recomposition is triggered, after which the conditional block is executed, displaying the DropDownMenu. However, in the very next line. You equate showDialog to false, re-trigerring a recomposition, and rendering the DropDownMenu collapsed.
In my viewModel I have "state" for every single screen. e.g.
class MainState(val commonState: CommonState) {
val text = MutableStateFlow("text")
}
I pass viewModel to my JetpackCompose screen.
#Composable
fun MainScreen(text: String, viewModel: HomeViewModel) {
val textState = remember { mutableStateOf(TextFieldValue()) }
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = viewModel.state.mainState.text.value,
color = Color.Blue,
fontSize = 40.sp
)
Button(
onClick = { viewModel.state.mainState.text.value = "New text" },
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Green
),
modifier = Modifier.padding(16.dp)
) {
Text(text)
}
TextField(
value = textState.value,
onValueChange = { textState.value = it },
label = { Text("Input text") }
)
}
}
When I click button I change value of state and I expect UI will update but it does not. I have to click on TextField and then text in TextView updates.
Any suggestion why UI does not update automatically?
That's how I pass components and start whole screen in startActivity;
class HomeActivity : ComponentActivity() {
private val viewModel by viewModel<HomeViewModel>()
private val homeState: HomeState get() = viewModel.state
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
RateMeAppTheme {
ContentScreen(viewModel, homeState)
}
}
}
}
In this simple case u should use mutableStateOf("text") in class MainState instead of mutableStateFlow
class MainState(val commonState: CommonState) {
val text = mutableStateOf("text")
}
Using MutableStateFlow
To use MutableStateFlow (which is not required in the current scenario) , we need to collect the flow.
Like the following:-
val state = viewModel.mainState.text.collectAsState() // we can collect a stateflow as state in a composable function
Then we can use the observed state value in the Text using:-
Text(text = state.value, ..)
Finally your composable function should look like:-
#Composable
fun MainScreen(text: String, viewModel: HomeViewModel) {
val textState = remember { mutableStateOf(TextFieldValue()) }
val state = viewModel.mainState.text.collectAsState()
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = state.value,
color = Color.Blue,
fontSize = 40.sp
)
Button(
onClick = { viewModel.mainState.text.value = "New text" },
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Green
),
modifier = Modifier.padding(16.dp)
) {
Text(text)
}
TextField(
value = textState.value,
onValueChange = { textState.value = it },
label = { Text("Input text") }
)
}
}