I have tried to use the if-else block to check if the task is completed which in turn will either display or hide the progress bar. The only problem is that it displays the progressBar but it doesn't hide it even after the task is completed
Here is the code snippet
#Composable
fun ProgressBar(isVissible: Boolean){
if (isVissible) {
Surface(
Modifier
.fillMaxSize(), color = Color.Transparent
) {
Column(
Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
CircularProgressIndicator(
color = appGreen
)
}
}
} else {
Spacer(modifier = Modifier.height(10.dp))
}
}
Related
When placing 2 or more composables in a Box() in jetpack compose, even if they overlap, they can be focused and clicked through each other, is there a way to prevent this behavior?
Box(
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { /*TODO*/ }) {
Text(text = "Should NOT be clickable and focusable")
}
}
}
Column(
modifier = Modifier
.fillMaxSize()
.background(
brush = SolidColor(Color.Gray),
alpha = 0.8f
),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { /*TODO*/ }) {
Text(text = "Should be clickable and focusable")
}
}
resulting in:
Expected behavior is when the button on the top is visible, I don't want the button in the top to be clickable And focusable.
I want to change the horizontalAlignment of particular child and remaining will use the same horizontalAlignment. Is this possible in Column?
For example, Column parent modifier use horizontalAlignment = Alignment.CenterHorizontally, and I want particular child modifier will be different horizontalAlignment.
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxSize()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
) {
ScreenImage()
Description()
if (viewModel.isBluetoothEnabled) {
ScanDeviceList(scanDeviceList)
} else {
Warning()
Spacer(modifier = Modifier.weight(1f))
TryAgainButtonView { tryAgainAction() }
ButtonView { openSettingAction() }
}
}
I want to change ScanDeviceList() to be different horizontalAlignment
#Composable
fun ColumnScope.ScanDeviceList(scanDeviceList: List<ScanResult>) {
Spacer(modifier = Modifier.height(20.dp))
AnimatedVisibility(scanDeviceList.isNotEmpty()) {
Text(
text = stringResource(R.string.vailable_device),
)
}
}
Many Thanks
You can use Modifier.align(Alignment.Start) to align a particular child.
So for example to make your ScanDeviceList() at the Start of the column the code will be like this:
#Composable
fun ColumnScope.ScanDeviceList(scanDeviceList: List<ScanResult>) {
Spacer(modifier = Modifier.height(20.dp))
AnimatedVisibility(
scanDeviceList.isNotEmpty(),
modifier = Modifier.align(Alignment.Start)
) {
Text(
text = stringResource(R.string.vailable_device),
)
}
}
You can also pass the modifier as an argument to ScanDeviceList composable function to make it more generic to the code will be like this:
#Composable
fun ColumnScope.ScanDeviceList(
scanDeviceList: List<ScanResult>,
modifier: Modifier = Modifier
) {
Spacer(modifier = Modifier.height(20.dp))
AnimatedVisibility(
scanDeviceList.isNotEmpty(),
modifier = modifier
) {
Text(
text = stringResource(R.string.vailable_device),
)
}
}
And when you call it you can specify the alignment that you want:
ScanDeviceList(scanDeviceList, modifier = Modifier.align(Alignment.Start))
Note: adding the modifier an argument to your composable functions is considered as a best practice because it will make the function reusable like the example above you can call ScanDeviceList with Alignment.Start or Alignment.End without changing the function itself, just by passing a different modifier as a parameter.
Is it possible to create full screen, clickable, transparent surface/box, that will overlap compose buttons and other composables. I want to keep buttons visible but unreachable for the user.
When I set them(buttons) as disabled, they turn to an unclickable area( on top of the clickable surface) that I can't have on the screen. Other composables, like Text,Box etc. act like they are "under" clickable surface.
#Composable
fun ShowAnswers(question: Question, onSurfaceClick: () -> Unit, questionsLeft: Int) {
Surface(modifier = Modifier.clickable { onSurfaceClick() }) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(8.dp)
) {
Text(
text = stringResource(id = R.string.questions_left, questionsLeft),
fontSize = 24.sp
)
Spacer(modifier = Modifier.height(20.dp))
QuestionCard(question = question)
Spacer(modifier = Modifier.height(20.dp))
ShowCorrectAnswer(question = question)
}
}
}
ShowCorrectAnswer(...) contains buttons that i need to "overlap"
You can use a simple transparent Box to overlay the Surface with the question.
Something like:
var isActive by remember { mutableStateOf(false) }
Box(modifier = Modifier.fillMaxSize()) {
Surface() {
Column() {
//....
QuestionCard(question = question)
//....
Button(onClick = { isActive = true }) {
//...
}
}
}
if (isActive){
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Transparent)
.clickable(
enabled=isActive,
interactionSource = interactionSource,
indication = null)
{
//do something
}
)
}
}
Adding a fillMaxSize Box inside the surface, after column, solved the problem.
But placing the Box before Column makie it appear "under" it.
#Composable
fun ShowAnswers(question: Question, onSurfaceClick: () -> Unit, questionsLeft: Int) {
Surface {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(8.dp)
) {
Text(
text = stringResource(id = R.string.questions_left, questionsLeft),
fontSize = 24.sp
)
Spacer(modifier = Modifier.height(20.dp))
QuestionCard(question = question)
Spacer(modifier = Modifier.height(20.dp))
ShowCorrectAnswer(question = question)
}
**Box(modifier = Modifier
.fillMaxSize()
.background(Color.Transparent)
.clickable { onSurfaceClick() })**
}
}
What is the correct way/approach to disable the background interaction when progress bar is active.
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
// Some content (when anyState is true, disable screen interaction)
if(anyState){
MaterialCircularProgressIndicator()
}
}
You need an other box, which you can fill with a translucent background(I prefer doing so user understand why his touches are not working), and you can interrupt touches with pointerInput. This is exactly same technique Compose uses to block touches under the Surface.
if(anyState){
Box(
Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.3f))
.pointerInput(Unit) {}
)
CircularProgressIndicator()
}
This worked out for me.
if (anyState) {
Box(modifier = Modifier
.fillMaxSize()
.clickable(
indication = null, // disable ripple effect
interactionSource = remember { MutableInteractionSource() },
onClick = { }
),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
You should a add clickable() modifier to the Box when any state is true.
Box(
modifier = Modifier
.fillMaxSize()
.let {
return#let if (anyState) {
it.clickable {}
} else it
},
contentAlignment = Alignment.Center
) {
MaterialCircularProgressIndicator()
}
I am looking to have a result like this. Two block with 2/3 size and 1/3 size respectively.
I am getting the expected result with this code.
#Preview(showBackground = true)
#Composable
fun LayoutCheck() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Surface(
modifier = Modifier
.width(200.dp)
.weight(3f),
color = MaterialTheme.colors.primary
) {}
Surface(
modifier = Modifier
.width(200.dp)
.weight(1f),
color = MaterialTheme.colors.secondary
) {}
}
}
But when i put that inside a lazycolumn, nothing seems working. Not even getting a display.
#Preview(showBackground = true)
#Composable
fun LayoutCheck() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(20.dp)
) {
item {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Surface(
modifier = Modifier
.width(200.dp)
.weight(3f),
color = MaterialTheme.colors.primary
) {}
Surface(
modifier = Modifier
.width(200.dp)
.weight(1f),
color = MaterialTheme.colors.secondary
) {}
}
}
}
}
}
If i remove t he weight and put some height,it works. But i don't want to hardcode there. So how to make it work with weight. Expecting some help..
Thanks
NB: I want scroll functionality, that's why going with LazyColumn
Real World scenario :
A Login Screen, with Logo at the first 2/3 portion and a Text and Button at the bottom 1/3 portion
Scenario 1 : Working Perfectly:
Scenario 2: If users font is bigger or screen is rotated, they wont be able to see the button
Since you have only one item just use a Column with a verticalScroll:
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally,
) {
//...
}
Also if you want 2/3 and 1/3, use in the 1st Surface the weight(2f) modififer.
You prevent the text from being resized by applying the weight modifier. Instead, I suggest that you make the element spacing flexible, like this:
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.weight(1f))
Image(
painter = painterResource(id = R.drawable.my_image),
contentDescription = "",
)
Spacer(Modifier.weight(1.35f))
Text(
"Your app is being reviewed",
)
Spacer(Modifier.weight(0.2f))
Button(onClick = { /*TODO*/ }) {
Text("Log out")
}
Spacer(Modifier.weight(0.2f))
}
Spacers explanation: