How to disable interaction when CircularProgressIndicator is active? - android

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

Related

Prevent other composables in a box to be focusable and clickable, Jetpack Compose

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.

Android compose, Indicator size problem with coil

Recently, I try to migrate to Jetpack compose.
So, I want to show 'card' where has 'indicator' while loading
but I can't change 'indicator' size in 'SubcomposeAsyncImage'
#Composable
private fun SponsorDialogContent(
imgUrl: String,
) {
Card(
modifier = Modifier
.width(300.dp)
.wrapContentHeight(),
elevation = CardDefaults.cardElevation(0.dp),
shape = RoundedCornerShape(10.dp)
) {
Box() {
SubcomposeAsyncImage(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.heightIn(min = 200.dp),
model = imgUrl,
contentDescription = null,
loading = {
CircularProgressIndicator(modifier = Modifier.size(30.dp).align(Alignment.Center), progress = 1f)
},
alignment = Alignment.Center,
contentScale = ContentScale.FillWidth
)
CircularProgressIndicator(1f, Modifier.width(30.dp).align(Alignment.Center))
}
}
}
So. I have try the code below
#Composable
private fun SponsorDialogContent(
imgUrl: String,
) {
Card(
modifier = Modifier
.width(300.dp)
.wrapContentHeight(),
elevation = CardDefaults.cardElevation(0.dp),
shape = RoundedCornerShape(10.dp)
) {
Box() {
SubcomposeAsyncImage(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.heightIn(min = 200.dp),
model = imgUrl,
contentDescription = null,
loading = {
Box(contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
},
alignment = Alignment.Center,
contentScale = ContentScale.FillWidth
)
}
}
}
Then, works well, but I don't know why
Does anyone know why?
I tried to find how to work this code but failed
As #Jan Bina mentioned it has propagateMinConstraints = true which means it forces minimum constraints to its direct child only. I explained in this answer how it effects with Surface because Surface is a Box with propagateMinConstraints = true. You can try out these with Box and you will see the same outcomes
Surface(
modifier = Modifier.size(200.dp),
onClick = {}) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Red, RoundedCornerShape(6.dp))
) {}
}
Spacer(modifier = Modifier.height(20.dp))
Surface(
modifier = Modifier.size(200.dp),
onClick = {}) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Red, RoundedCornerShape(6.dp))
) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Green, RoundedCornerShape(6.dp))
) {}
}
}
Spacer(modifier = Modifier.height(20.dp))
Box(
modifier = Modifier.size(200.dp)
) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Red, RoundedCornerShape(6.dp))
) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Green, RoundedCornerShape(6.dp))
) {}
}
}
In first example on Surface forces Column to have 200.dp size even though it has Modifier.size(50.dp).
In second example Box inside Column has 50.dp size because it's not a direct descendant of Surface.
In third example if we replace Surface(Box with propagateMinConstraints true) with Box it allows direct descendant to use its own constraints or dimensions.
And when you set Modifier.fillMaxWidth you set Constraints as minWidth = parent width, maxWidth = parentWidth
I answered about Constraint types here
If you browse the source code of SubcomposeAsyncImage, you will get to the point where you can see this:
BoxWithConstraints(
modifier = modifier, // this is the modifier you passed to SubcomposeAsyncImage
contentAlignment = alignment,
propagateMinConstraints = true,
) {
// image or your loading content based on state
}
So the behavior you describe is because of your modifier that has heightIn(min = 200.dp) and your progress indicator that ends up being direct child of this Box which has propagateMinConstraints = true.

Android Compose, clickable surface on top of buttons

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

How can i click on component behind any component in jetpack compose?

Let say i have a two surface in a box and they are on top of each other;
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Surface(
modifier = Modifier.size(200.dp).clickable{},
color = Color.Red
) {}
Surface(
modifier = Modifier.size(50.dp),
color = Color.Blue
){}
}
View:
When I click on the blue surface, I want the red surface to be clicked.
Or I want to make the blue surface "Click Invisible"
How can i achieve this?
If you want red one to have ripple effect when you touch blue Composable you can use a shared InteractionSource to trigger interaction for different composables
// 🔥 This interaction source is set by inner composable to trigger ripple on outer one
val interactionSource = MutableInteractionSource()
val coroutineScope = rememberCoroutineScope()
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Surface(
modifier = Modifier
.size(200.dp)
.clickable(
interactionSource = interactionSource,
indication = rememberRipple(),
onClick = {
}
),
color = Color.Red
) {}
Surface(
modifier = Modifier
.size(50.dp)
.clickable {
coroutineScope.launch {
val press = PressInteraction.Press(Offset.Zero)
interactionSource.emit(
press
)
interactionSource.emit(
PressInteraction.Release(press)
)
}
},
color = Color.Blue
) {}
}
The issue you will get here with blue Composable's ripple is it will start from Offset.Zero. You can set center of blue Composable manually. I don't know if there is a way to get exact position of press from InteractionSource or Interaction.
You could achieve the same like this -
Box(
modifier = Modifier.size(200.dp).clickable {},
contentAlignment = Alignment.Center
) {
Surface(
modifier = Modifier
.fillMaxSize(),
color = Color.Red
) {}
Surface(
modifier = Modifier.size(50.dp) ,
color = Color.Blue
){}
}
You can centre it inside another box if you want.

Arrange a single item inside a LazyColumn by using weight modifier

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:

Categories

Resources