ModalBottomSheetLayout disable clicking on the back - android

I use ModalBottomSheetLayout and I want the back part to darken when opening the lower curtain and it was not clickable. How to do it?
If you set screen Color = Color.Unspecified, the background will not be clickable, but at the same time it will be colorless. It doesn't suit me.
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun ForpostScreen(forpostViewModel: ForpostViewModel = koinViewModel()) {
val scope = rememberCoroutineScope()
val bottomSheetOpen = remember{ mutableStateOf(false) }
val viewState = forpostViewModel.forpostViewState.observeAsState()
val bottomSheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden
)
ModalBottomSheetLayout(
sheetState = bottomSheetState,
sheetContent = {
Box(modifier = Modifier
.fillMaxWidth()
.height(500.dp)) {
Image(
painter = painterResource(id = R.drawable.botton_sheet_top_line),
contentDescription = "Линия",
modifier = Modifier
.height(20.dp)
.width(70.dp)
.padding(top = 10.dp)
.align(Alignment.TopCenter)
)
Text(
text = stringResource(id = R.string.all_cams_button),
color = colorResource(id = R.color.title_text_color),
fontWeight = FontWeight(600),
fontSize = 25.sp,
modifier = Modifier
.padding(top = 30.dp)
.align(Alignment.TopCenter)
)
}
},
sheetShape = RoundedCornerShape(topStart = 25.dp, topEnd = 25.dp),
sheetElevation = 12.dp,
) {
Surface(color = Color.White) {
Column {
Button(
onClick = {
bottomSheetOpen.value = true
scope.launch {
bottomSheetState.show()
}
},
colors = ButtonDefaults.buttonColors(colorResource(id = R.color.button_color)),
shape = RoundedCornerShape(15.dp),
modifier = Modifier
.padding(top = 20.dp, start = 20.dp)
.height(30.dp)
.width(130.dp),
contentPadding = PaddingValues(bottom = 0.dp),
content = {
Text(
text = stringResource(id = R.string.all_cams_button),
color = Color.White,
fontWeight = FontWeight(400)
)
}
)
Text(
modifier = Modifier.padding(top = 50.dp, start = 20.dp),
text = "Репина, 1 Б",
color = colorResource(id = R.color.title_text_color),
fontWeight = FontWeight(700),
fontSize = 18.sp,
letterSpacing= 1.sp
)
ButtonsLazyColumn(
listItem = listOf("Двор", "Подъезд", "Парковка", "Въезд", "Пост охраны"),
height = 50,
modifierPaddingStart = 20,
modifierPaddingTop = 15,
modifierPaddingEnd = 10,
)
Surface(modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp)
.height(220.dp)
.background(Color.Black)) {
when (viewState.value) {
is ForpostViewState.Loading -> { VideoLoadingView() }
is ForpostViewState.Load -> { PlayerView(forpostViewModel = forpostViewModel) }
else -> {}
}
}
Divider(
modifier = Modifier.padding(top = 100.dp),
color = colorResource(id = R.color.sevstar_gray_light),
thickness = 1.dp
)
ButtonsLazyColumn(
listItem = listOf("1 сек", "1 мин", "5 мин", "10 мин", "30 мин"),
height = 50,
modifierPaddingStart = 20,
modifierPaddingTop = 15,
modifierPaddingEnd = 10
)
Divider(
modifier = Modifier.padding(top = 20.dp),
color = colorResource(id = R.color.sevstar_purple),
thickness = 1.dp
)
}
}
Box(
modifier = Modifier
.fillMaxSize()
.background(
color = if (bottomSheetOpen.value) Color.Black.copy(alpha = 0.5f)
else Color.Transparent
))
}
if (bottomSheetState.currentValue != ModalBottomSheetValue.Hidden
&& bottomSheetState.offset.value > 1800) bottomSheetOpen.value = false
LaunchedEffect(key1 = viewState, block = {
forpostViewModel.obtainEvent(event = ForpostEvents.EnterScreen)
})
}
I made the back darkening myself and it works, but in this case the back part becomes clickable.

You can use something like
val bottomSheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden,
confirmStateChange = {false}
)
and you have to manually close the bottom sheet like,
onClick = { scope.launch { bottomSheetState.hide() } }

Related

Keyboard alters weight of screen, how to avoid this on android (jetpack compose)?

I have problem when the software keyboard opens I do see the screen as follows:
I have something like:
#OptIn(
ExperimentalLayoutApi::class, ExperimentalComposeUiApi::class,
ExperimentalFoundationApi::class
)
#Composable
fun ScreenScanPrint(vmUser: ViewModelUser, vm: ViewModelScanPrint) {
val density = LocalDensity.current
Column(
modifier = Modifier
.background(lightGrayBackGround)
.fillMaxSize()
.navigationBarsPadding()
.imePadding()
) {
Box(
modifier = Modifier
.height(114.dp)
.fillMaxWidth()
) {
TopNavBarWithProfilePictureAndBackButton(
text = "Αναζήτηση Προϊόντος",
vmUser = vmUser,
isLight = true
)
}
Column(modifier = Modifier.padding(24.dp)) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(120.dp),
colors = CardDefaults.cardColors(containerColor = Color(0xFFF4F4FC)),
shape = RoundedCornerShape(10.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Row(
modifier = Modifier.padding(10.dp),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.ic_baseline_lens_blur_24),
modifier = Modifier.size(20.dp),
contentDescription = ""
)
Text(
modifier = Modifier
.padding(start = 4.dp),
text = "Περιγραφή",
fontFamily = FontFamily(Font(R.font.manrope_semi_bold)),
fontSize = 15.sp,
color = appGray
)
}
Text(
modifier = Modifier
.padding(start = 18.dp, end = 18.dp, top = 0.dp),
text = vm.state.productName,
fontFamily = FontFamily(Font(R.font.manrope_regular)),
fontSize = 15.sp,
color = appGray
)
}
Column(
modifier = Modifier.padding(horizontal = 18.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.weight(2f))
Row(verticalAlignment = Alignment.Bottom) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(bottom = 12.dp)
) {
Image(
painter = painterResource(id = R.drawable.ic_round_euro_24_gray),
contentDescription = "",
modifier = Modifier.size(16.dp),
colorFilter = ColorFilter.tint(appGray)
)
Text(
modifier = Modifier
.padding(start = 8.dp),
text = "Τιμή",
fontFamily = FontFamily(Font(R.font.manrope_semi_bold)),
fontSize = 14.sp,
color = appGray
)
}
Spacer(modifier = Modifier.weight(1f))
Text(
modifier = Modifier,
text = vm.state.price.ifEmpty { "-" },
fontFamily = FontFamily(Font(R.font.manrope_semi_bold)),
fontSize = 38.sp,
color = appGray
)
}
Spacer(modifier = Modifier.weight(1f))
Box(
Modifier
.height(2.dp)
.fillMaxWidth()
.background(
appGray.copy(0.15f),
shape = DottedShape(step = 15.dp)
)
)
Spacer(modifier = Modifier.weight(1f))
ItemWithIconAndText(
text = "Διαθέσημα Κεντρικό",
imageResource = R.drawable.ic_round_content_paste_24,
numberIs = vm.state.storeRoomCenter.ifEmpty { "-" }
)
Spacer(modifier = Modifier.weight(1f))
ItemWithIconAndText(
text = "Διαθέσημα Αποθήκη",
imageResource = R.drawable.ic_round_content_paste_24,
numberIs = vm.state.storeRoomWarehouse.ifEmpty { "-" }
)
Spacer(modifier = Modifier.weight(1f))
ItemWithIconAndText(
text = "Δεσμευμένα",
imageResource = R.drawable.ic_baseline_content_paste_go_24,
numberIs = vm.state.reservedProducts.ifEmpty { "-" }
)
Spacer(modifier = Modifier.weight(2f))
TextFieldWithIconAndButton(
text = vm.state.barcodeText,
imageResource = R.drawable.svg_barcode_white_small,
descriptionText = "Barcode",
onTextChange = { vm.onEvent(ScanPrintEvent.OnBarcodeChange(it)) }
) {
}
Spacer(modifier = Modifier.weight(1f))
TextFieldWithIconAndButton(
text = vm.state.printText,
imageResource = R.drawable.svg_printer_white_small,
descriptionText = "Εκτυπωτής ισόγειο",
onTextChange = { vm.onEvent(ScanPrintEvent.OnPrintChange(it)) }
) { }
Spacer(modifier = Modifier.weight(2f))
androidx.compose.material.Button(
shape = CircleShape,
enabled = true,
onClick = {
vm.onEvent(ScanPrintEvent.OnScanClick)
},
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent),
contentPadding = PaddingValues(),
elevation = ButtonDefaults.elevation(
defaultElevation = 12.dp,
pressedElevation = 4.dp
),
modifier = Modifier.size(84.dp)
) {
Box(
Modifier
.fillMaxSize()
.background(blueButtonGradient),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(id = R.drawable.svg_scanner_big_fab),
contentDescription = ""
)
}
}
Spacer(modifier = Modifier.weight(1f))
}
}
}
}
And in manifest I have set up as: android:windowSoftInputMode="adjustResize"
in my activity.
I would like to see the screen moving up with keyboard bellow, without altering the weights. For example when I click on barcode:
Problem is imePadding() remove it and see result
and try use adjustPan in manifest

ModalBottomSheetLayout content is not get centered, as well as sheet content

Based on what was answered in this question (Open ModalSheetLayout on TextField focus instead of Keyboard) and the exchange of comments I did with #Abhimanyu, I was able to get the ModalBottomSheetLayout to appear when I click on one of the TextFields, however I encountered two more problems. I can't center what's in the content, nor can I center the content that's inside the sheet content. Can anyone help me understand why?
Here is a print of what is happening and my code:
#ExperimentalMaterialApi
#Preview
#Composable
fun ProfileScreen() {
var profileModalBottomSheetType by remember { mutableStateOf(ProfileModalBottomSheetType.NONE) }
val modalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
if (modalBottomSheetState.currentValue != ModalBottomSheetValue.Hidden) {
DisposableEffect(Unit) {
onDispose {
profileModalBottomSheetType = ProfileModalBottomSheetType.NONE
}
}
}
ModalBottomSheetLayout(
sheetState = modalBottomSheetState,
sheetShape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp),
sheetContent = {
Box(
modifier = Modifier
.padding(top = 10.dp)
.height(10.dp)
.width(100.dp)
.background(
color = Color.LightGray,
shape = RoundedCornerShape(4.dp)
)
)
when (profileModalBottomSheetType) {
ProfileModalBottomSheetType.SELECT_RATE -> {
SelectRateModalBottomSheet(listOf("Exact Rate", "Range"))
}
else -> {}
}
}
) {
LazyColumn(
modifier = Modifier
.width(320.dp)
) {
item {
HeightSpacer(40.dp)
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(id = R.drawable.ic_clearjobs_logo_2x),
contentDescription = null
)
}
HeightSpacer(47.dp)
Column(
modifier = Modifier
.width(320.dp)
.padding(start = 20.dp, end = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Profile Light Title",
style = TextStyle32Light,
textAlign = TextAlign.Center
)
Text(
text = "Profile Bold Title",
style = TextStyle32Bold,
textAlign = TextAlign.Center
)
}
HeightSpacer(47.dp)
Column(
modifier = Modifier
.background(
shape = RoundedCornerShape(
topStart = 13.dp,
topEnd = 13.dp
), color = Color.White
)
.padding(bottom = 140.dp)
.width(320.dp)
) {
Text(
text = stringResource(id = R.string.your_profile),
style = TextStyle28Bold,
modifier = Modifier.padding(
top = 40.dp,
start = 20.dp,
bottom = 30.dp
)
)
Text(
text = stringResource(id = R.string.salary_range),
style = TextStyle28Bold,
modifier = Modifier.padding(
top = 40.dp,
start = 20.dp,
bottom = 30.dp
)
)
Box {
LightBlueBorderTextField(
title = "Rate",
initialState = "Exact Rate",
textFieldTextStyle = TextStyle16BlackOpacity50Normal,
enabled = false
) { innerTextField ->
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.weight(1f)
.padding(start = Dimen10)
) {
innerTextField()
}
}
}
Box(
modifier = Modifier
.matchParentSize()
.alpha(0f)
.clickable(
onClick = {
profileModalBottomSheetType =
ProfileModalBottomSheetType.SELECT_RATE
toggleModalBottomSheetState(
coroutineScope = coroutineScope,
modalBottomSheetState = modalBottomSheetState,
)
}
)
)
}
}
}
}
}
}
#Composable
fun SelectRateModalBottomSheet(options: List<String>) {
LazyColumn(
modifier = Modifier.padding(
start = 20.dp,
end = 20.dp,
top = 15.dp,
bottom = 15.dp
)
) {
items(options.size) { optionIndex ->
val option = options[optionIndex]
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 10.dp, end = 10.dp, bottom = 15.dp)
) {
Box(Modifier.weight(1f)) {
Text(text = option, style = TextStyle16BlackOpacity50Normal)
}
RadioButton(
selected = false,
onClick = { /*TODO*/ },
modifier = Modifier
.width(20.dp)
.height(20.dp)
)
}
if (optionIndex != options.lastIndex) {
Divider(color = Color.DarkGray, thickness = 1.dp)
HeightSpacer(dimen = 15.dp)
}
}
}
}
#Composable
fun HeightSpacer(dimen: Dp) {
Spacer(modifier = Modifier.height(dimen))
}
#Preview
#Composable
fun LightBlueBorderTextField(
title: String = "",
initialState: String = "",
textFieldTextStyle: TextStyle = TextStyle18Normal,
enabled: Boolean = true,
decorationBox: #Composable (innerTextField: #Composable () -> Unit) -> Unit = { innerTextField ->
Box(
Modifier.padding(start = Dimen10),
contentAlignment = Alignment.CenterStart
) {
innerTextField()
}
}
) {
Column {
val state = remember { mutableStateOf(TextFieldValue(initialState)) }
if (title.isNotEmpty()) {
Text(
text = title,
style = TextStyle16BlackBold,
modifier = Modifier.padding(
top = Dimen40,
start = Dimen30,
bottom = Dimen10
)
)
} else {
HeightSpacer(Dimen40)
}
CustomTextField(
state = state,
modifier = Modifier
.height(Dimen45)
.padding(start = Dimen20, end = Dimen20)
.border(
width = Dimen1,
color = LightBlue,
shape = RoundedCornerShape(Dimen13)
)
.background(Color.White, RoundedCornerShape(Dimen13))
.fillMaxWidth(),
textStyle = textFieldTextStyle,
decorationBox = decorationBox,
enabled = enabled
)
}
}
#Composable
fun CustomTextField(
state: MutableState<TextFieldValue>,
modifier: Modifier,
textStyle: TextStyle = TextStyle18Normal,
decorationBox: #Composable (innerTextField: #Composable () -> Unit) -> Unit,
enabled: Boolean = true
) {
BasicTextField(
modifier = modifier,
value = state.value,
onValueChange = { value -> state.value = value },
singleLine = true,
textStyle = textStyle,
decorationBox = decorationBox,
enabled = enabled
)
}
#ExperimentalMaterialApi
fun toggleModalBottomSheetState(
coroutineScope: CoroutineScope,
modalBottomSheetState: ModalBottomSheetState,
action: (() -> Unit)? = null,
) {
coroutineScope.launch {
if (!modalBottomSheetState.isAnimationRunning) {
if (modalBottomSheetState.isVisible) {
modalBottomSheetState.hide()
} else {
modalBottomSheetState.show()
}
}
action?.invoke()
}
}
internal enum class ProfileModalBottomSheetType {
NONE,
SELECT_JOB_KEYWORDS,
SELECT_WORK_LOCATIONS,
SELECT_TAGS,
SELECT_RATE,
SELECT_SALARY_PERIOD
}
I solved this issue by putting all the ModalBottomSheet content inside a Box with Modifier.fillMaxSize() and the contentAlignment = Alignment.Center. See my code below:
ModalBottomSheetLayout(
sheetState = modalBottomSheetState,
sheetShape = RoundedCornerShape(topStart = Dimen13, topEnd = Dimen13),
sheetContent = {
JobDetailsModalBottomSheet(modalBottomSheetType, jobClicked) {
modalBottomSheetType = JobOpeningsScreenModalBottomSheetType.NONE
toggleModalBottomSheetState(
coroutineScope = coroutineScope,
modalBottomSheetState = modalBottomSheetState,
)
}
}) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
// any content
}
}
I'm not really sure why this doesn't work without Box, but after a few tries, this was the solution I found and it worked.

Clickable modifier on Row not receiving events

I have the following page on which I'm trying to display a list of saved cards with the ability to add a new one. The last item in the column is an expandable one so that when clicked, the user can see a form for filling out card info in order to add a new card. This was working just fine yesterday, but I can't figure out why it's not working today.
The actual CardItem elements receive clicks just fine but the custom expandable one does not and neither does the ShadowWrapper parent nor the RaisedCard one.
Cards screen:
private data class CreditCard(val type: CreditCardTypes, val lastDigits: String)
#Composable
fun CardSelectionScreen(onCardSelected: () -> Unit) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(vertical = 24.dp, horizontal = 16.dp)
.verticalScroll(rememberScrollState())
) {
var selectedCardIndex by remember { mutableStateOf(0) }
val cardList = listOf(
CreditCard(CreditCardTypes.MASTERCARD, "3429"),
CreditCard(CreditCardTypes.VISA, "3429"),
CreditCard(CreditCardTypes.MASTERCARD, "3429")
)
TopBarPadding(true)
Spacer(modifier = Modifier.height(10.dp))
RaisedCard() {
Column(
modifier = Modifier.padding(vertical = 20.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
text = stringResource(id = R.string.please_select_a_card),
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Start
)
Spacer(modifier = Modifier.height(9.dp))
for (i in cardList.indices) {
CreditCardItem(cardList[i],
isSelected = selectedCardIndex == i, onItemSelected = { ->
selectedCardIndex = i
})
}
ShadowWrapper( // This is the item's layout
cardElevation = 1.dp,
shadowElevation = 3.dp
) {
Column(
modifier = Modifier
.animateContentSize()
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(6.dp))
.background(
if (selectedCardIndex == cardList.size) colorResource(
id = R.color.bottom_modal_drawer_background
) else Color.White
)
.padding(horizontal = 16.dp, vertical = 16.dp)
.clickable(
indication = null,
interactionSource = MutableInteractionSource()
) { // this does not register at all, tried with Log.d
selectedCardIndex = cardList.size
},
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.ic_add_credit_card),
contentDescription = "Add credit card icon"
)
Spacer(modifier = Modifier.width(13.dp))
Text(
stringResource(id = R.string.new_card_addition),
textAlign = TextAlign.Start,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = colorResource(id = R.color.Orange_100)
)
}
if (selectedCardIndex == cardList.size) {
Column(
modifier = Modifier.padding(
horizontal = 16.dp
)
) {
Spacer(modifier = Modifier.padding(22.fixedDp()))
Text(
text = LocalContext.current.getString(R.string.add_credit_card_top_msg),
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = colorResource(id = R.color.Black_100)
)
Spacer(modifier = Modifier.padding(10.dp))
InputField(label = LocalContext.current.getString(R.string.owner_name))
Spacer(modifier = Modifier.padding(18.fixedDp()))
InputField(label = LocalContext.current.getString(R.string.credit_card_number))
Spacer(modifier = Modifier.padding(18.fixedDp()))
Row() {
Box(
modifier = Modifier
.weight(1.5f)
) {
InputField(label = LocalContext.current.getString(R.string.expiration_date))
}
Spacer(modifier = Modifier.padding(6.fixedDp()))
Box(
modifier = Modifier
.weight(1f)
) {
InputField(
label = LocalContext.current.getString(R.string.cvv),
isPassword = true,
placeholder = ""
)
}
}
Spacer(modifier = Modifier.height(34.fixedDp()))
Row() {
MyCheckbox(
modifier = Modifier.padding(top = 3.dp),
isCheckedInitially = true
)
Spacer(modifier = Modifier.width(13.dp))
Text(
stringResource(id = R.string.save_card_for_future_transactions),
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
color = colorResource(id = R.color.Black_100)
)
}
Spacer(modifier = Modifier.padding(22.fixedDp()))
}
}
}
}
Spacer(modifier = Modifier.height(2.dp))
}
}
Spacer(modifier = Modifier.height(32.dp))
MyButton(
text = stringResource(id = R.string.continue_text),
MyButtonType.PRIMARY,
onClick = { onCardSelected() }
)
Spacer(modifier = Modifier.height(20.dp))
AcceptedCardsFooter()
BottomBarPadding(true)
}
}
#Composable
private fun CreditCardItem(
cardDetails: CreditCard,
isSelected: Boolean,
onItemSelected: () -> Unit
) {
ShadowWrapper(cardElevation = 1.dp, shadowElevation = 3.dp) {
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(6.dp))
.background(if (isSelected) colorResource(id = R.color.bottom_modal_drawer_background) else Color.White)
.padding(horizontal = 16.dp, vertical = 15.dp)
.clickable(indication = null, interactionSource = MutableInteractionSource()) {
onItemSelected()
},
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(verticalAlignment = Alignment.CenterVertically) {
MyRadioButton(label = "", selected = isSelected)
Spacer(modifier = Modifier.width(16.dp))
Box(
modifier = Modifier
.width(43.dp)
.height(33.dp)
.clip(RoundedCornerShape(4.dp))
.background(colorResource(id = R.color.Grey_10))
.padding(horizontal = 6.dp, vertical = 7.dp)
) {
Image(
painter = painterResource(id = cardDetails.type.icon),
contentDescription = "",
modifier = Modifier.align(Alignment.Center)
)
}
Spacer(modifier = Modifier.padding(8.fixedDp()))
Text(
text = "${cardDetails.type.prefix}****${cardDetails.lastDigits}",
fontSize = 16.sp,
color = colorResource(id = R.color.Black_100)
)
}
}
}
}
RaisedCard.kt:
#Composable
fun RaisedCard(
modifier: Modifier = Modifier,
mainBody: #Composable () -> Unit
) {
Card(
shape = RoundedCornerShape(13.dp),
elevation = 10.dp,
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
) {
Column(
modifier = Modifier
.background(Color.White)
.padding(horizontal = 16.dp)
) {
mainBody()
}
}
}
ShadowWrapper.kt:
#Composable
fun ShadowWrapper(
modifier: Modifier = Modifier,
border: BorderStroke = BorderStroke(0.dp, Color.Transparent),
cardElevation: Dp = 2.dp,
shadowElevation: Dp = 1.dp,
shadowShapeRadius: Dp = 6.dp,
content: #Composable () -> Unit,
) {
Card(
elevation = cardElevation,
border = border,
shape = RoundedCornerShape(shadowShapeRadius),
modifier = modifier.shadow(shadowElevation, RoundedCornerShape(shadowShapeRadius)).wrapContentHeight()
) {
content()
}
}
I wasn't able to reproduce your issue, probably because other parts of your application are triggering additional recompositions. However the cause is most likely that you forget to remember your MutableInteractionSources.
Like this:
otherModifiers.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) { // this does not register at all, tried with Log.d
selectedCardIndex = cardList.size
}
If you do not wrap MutableInteractionSource in a remember, a new instance is created on every recomposition, so state like a previous touch events is lost

Jetpack compose code to scroll down to the position of a specific UI element on clicking a Text

I am trying to scroll down to the position of a specific UI element on clicking a Text.
The code for my Text is:
Text(
"What is autosaving?",
color = colorResource(id = R.color.text_highlight),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
},
fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier.constrainAs(whatIsAutosaving) {
top.linkTo(glWhatIsAutoSaving)
start.linkTo(parent.start)
end.linkTo(parent.end)
},
)
On clicking this Text my screen should scroll to the beginning position of another Text. The code for this another Text is:
Text(
stringResource(id = R.string.autosave_info),
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier.constrainAs(autoSaveInfo) {
top.linkTo(glAutoSaveInfo)
start.linkTo(glLeft)
end.linkTo(glRight)
width = Dimension.fillToConstraints
},
)
How do I achieve this?
EDIT:
The complete code for my screen is:
#Composable
fun Autosave(navController: NavController) {
val query = remember { mutableStateOf("") }
val errorMsg = remember { mutableStateOf(false) }
Box(
modifier = Modifier
.background(color = MaterialTheme.colors.background)
.fillMaxSize()
) {
ConstraintLayout(
modifier = Modifier.verticalScroll(rememberScrollState())
) {
val (logo, illustration, title, triangle, slider, percent, maxLimitTxt,
maxLimitTextField, buttonSave, whatIsAutosaving, autoSaveInfo, progressBar,
detailsRow, iconUp, spacer, error) = createRefs()
val glLogo = createGuidelineFromTop(0.0075f)
val glIllustrationTop = createGuidelineFromTop(0.0235f)
val glIllustrationBottom = createGuidelineFromTop(0.045f)
val glIllustrationLeft = createGuidelineFromStart(0.27f)
val glIllustrationRight = createGuidelineFromEnd(0.27f)
val glTitle = createGuidelineFromTop(0.053f)
val glSlider = createGuidelineFromTop(0.062f)
val glMaxLimitTxt = createGuidelineFromTop(0.086f)
val glMaxLimitTextField = createGuidelineFromTop(0.09f)
val glButtonSaveTop = createGuidelineFromTop(0.11f)
val glButtonSaveBottom = createGuidelineFromTop(0.12f)
val glWhatIsAutoSaving = createGuidelineFromTop(0.125f)
val glAutoSaveInfo = createGuidelineFromTop(0.175f)
val glSpacer = createGuidelineFromTop(0.99f)
val glLeft = createGuidelineFromStart(0.1f)
val glRight = createGuidelineFromEnd(0.1f)
val glRightIcon = createGuidelineFromEnd(0.825f)
val glLeftTextField = createGuidelineFromStart(0.3f)
val glRightTextField = createGuidelineFromEnd(0.3f)
val coroutineScope = rememberCoroutineScope()
val scrollState = rememberScrollState()
var scrollToPosition by remember { mutableStateOf(0F) }
Image(
painter = painterResource(id = R.drawable.effect_app_bg_720),
contentDescription = "effect top",
modifier = Modifier
.fillMaxSize()
.scale(1.325f)
)
Image(
painter = painterResource(id = R.drawable.logo_voodlee),
contentDescription = "logo", modifier = Modifier
.constrainAs(logo) {
top.linkTo(glLogo)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
Image(
painter = painterResource(id = R.drawable.img_autosaving),
contentDescription = "autosave image",
modifier = Modifier.constrainAs(illustration) {
top.linkTo(glIllustrationTop)
bottom.linkTo(glIllustrationBottom)
start.linkTo(glIllustrationLeft)
end.linkTo(glIllustrationRight)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
}
)
Text(
"Set the percentage for autosaving",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._13ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier.constrainAs(title) {
top.linkTo(glTitle)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
Image(
painter = painterResource(id = R.drawable.ic_triangle_dn),
modifier = Modifier
.height(39.dp)
.width(29.dp)
.constrainAs(triangle) {
top.linkTo(title.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
},
contentDescription = "triangle down"
)
Column(modifier = Modifier.constrainAs(slider) {
top.linkTo(glSlider)
start.linkTo(parent.start)
end.linkTo(parent.end)
}) {
val context = LocalContext.current
val customView = remember { com.shawnlin.numberpicker.NumberPicker(context) }
// Adds view to Compose
AndroidView({ customView }) { view ->
// View's been inflated - add logic here if necessary
with(view) {
orientation = HORIZONTAL
//dividerDrawable = ResourcesCompat.getDrawable(resources, R.drawable.bg_blue, null)
textColor =
ResourcesCompat.getColor(resources, R.color.slider_num_color, null)
selectedTextColor =
ResourcesCompat.getColor(resources, R.color.slider_num_color, null)
selectedTextSize = 120f
wheelItemCount = 6
value = 10
minValue = 0
maxValue = 99
layoutParams.width = MATCH_PARENT
setDividerColorResource(R.color.fade_green)
setDividerDistance(180)
setDividerThickness(10)
}
}
Text(
"%",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._18ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier
.align(CenterHorizontally)
.offset(y = (-5).dp)
)
}
Text(
"Max Limit per autosaving",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._13ssp).toSp()
},
fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier.constrainAs(maxLimitTxt) {
top.linkTo(glMaxLimitTxt)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
TextField(
value = query.value,
onValueChange = { newValue -> query.value = newValue
if (newValue != "")
errorMsg.value = newValue.toInt() > 1500
},
label = {
Text(" Amount",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._15ssp).toSp()
},
textAlign = TextAlign.Center
)
},
textStyle = TextStyle(
textAlign = TextAlign.Center,
color = colorResource(id = R.color.bright_green),
fontFamily = FontFamily(Font(R.font.poppins_regular)),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._15ssp).toSp()
},
),
modifier = Modifier.constrainAs(maxLimitTextField) {
top.linkTo(glMaxLimitTextField)
start.linkTo(glLeftTextField)
end.linkTo(glRightTextField)
width = Dimension.fillToConstraints
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
unfocusedIndicatorColor = colorResource(id = R.color.bright_green),
focusedIndicatorColor = colorResource(id = R.color.bright_green)
)
)
Text(
text =
"*Please enter amount less than Rs.1500",
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._8ssp).toSp()
},
color = colorResource(id = R.color.voodlee_red),
modifier = Modifier
.padding(top = 8.dp)
.alpha(
if (errorMsg.value) {
1f
} else 0f
)
.constrainAs(error) {
top.linkTo(maxLimitTextField.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
},
)
Button(
onClick = {
navController.navigate("fourth_screen")
},
modifier = Modifier.constrainAs(buttonSave) {
top.linkTo(glButtonSaveTop)
bottom.linkTo(glButtonSaveBottom)
start.linkTo(glLeft)
end.linkTo(glRight)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
},
colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(id = R.color.voodlee_red))
) {
Text(
"Save", color = colorResource(id = R.color.dark_blue),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._16ssp).toSp()
},
)
}
Text(
"What is autosaving?",
color = colorResource(id = R.color.text_highlight),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
},
fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier
.constrainAs(whatIsAutosaving) {
top.linkTo(glWhatIsAutoSaving)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
.clickable {
coroutineScope.launch {
scrollState.animateScrollTo(scrollToPosition.roundToInt())
}
},
)
Text(
stringResource(id = R.string.autosave_info),
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier
.constrainAs(autoSaveInfo) {
top.linkTo(glAutoSaveInfo)
start.linkTo(glLeft)
end.linkTo(glRight)
width = Dimension.fillToConstraints
}
.onGloballyPositioned { coordinates ->
scrollToPosition = coordinates.positionInParent().y
},
)
Row(
modifier = Modifier
.padding(top = 40.dp, bottom = 50.dp)
.constrainAs(detailsRow) {
top.linkTo(autoSaveInfo.bottom)
start.linkTo(glLeft)
end.linkTo(glRight)
width = Dimension.fillToConstraints
},
) {
Text(
text = "For more details",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
)
Spacer(modifier = Modifier.padding(5.dp))
Text(
text = "Click here",
color = colorResource(id = R.color.text_highlight),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
},
fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
)
}
Image(
painter = painterResource(id = R.drawable.ic_btn_upward),
modifier = Modifier
.height(32.dp)
.constrainAs(iconUp) {
top.linkTo(detailsRow.bottom)
start.linkTo(glLeft)
end.linkTo(glRightIcon)
width = Dimension.fillToConstraints
},
contentDescription = ""
)
Spacer(modifier = Modifier
.padding(bottom = 50.dp)
.constrainAs(spacer) {
top.linkTo(glSpacer)
start.linkTo(parent.start)
end.linkTo(parent.end)
},)
}
Card(
Modifier
.align(BottomCenter)
.fillMaxWidth()
.alpha(if (query.value == "") 1f else 0f),
backgroundColor = MaterialTheme.colors.secondaryVariant
) {
ProgressBar5UI(
Modifier
.padding(start = 40.dp, end = 40.dp, top = 10.dp)
)
}
Card(
Modifier
.align(BottomCenter)
.fillMaxWidth()
.alpha(if (errorMsg.value) 1f else 0f),
backgroundColor = MaterialTheme.colors.secondaryVariant
) {
ProgressBar6UI(
Modifier
.padding(start = 40.dp, end = 40.dp, top = 10.dp)
)
}
Card(
Modifier
.align(BottomCenter)
.fillMaxWidth()
.alpha(if (query.value != "" && !errorMsg.value) 1f else 0f),
backgroundColor = MaterialTheme.colors.secondaryVariant
) {
ProgressBar7UI(
Modifier
.padding(start = 40.dp, end = 40.dp, top = 10.dp)
)
}
}
}
Is there possibly any special way to scroll on clicking an element while using Constraint Layout?
You can use the onGloballyPositioned modifier to retrieve the position of a composable and then use the method scrollState.animateScrollTo to scroll to that position.
Something like:
val coroutineScope = rememberCoroutineScope()
val scrollState = rememberScrollState()
var scrollToPosition by remember { mutableStateOf(0F) }
Column(Modifier.verticalScroll(scrollState)) {
Text(
"Click here to scroll",
modifier = Modifier.clickable {
coroutineScope.launch {
scrollState.animateScrollTo(scrollToPosition.roundToInt())
}
}
)
//...
Text(
"Target",
modifier = Modifier.onGloballyPositioned { coordinates ->
scrollToPosition = coordinates.positionInParent().y
}
)
}

how to make Jetpack compose checkbox rounded

How do I create a rounded checkbox in Jetpackcompose like this. I tried using a Shape composable on it but it doesn't work.
I was looking on how to do the same thing you were asking, your question helped me on my journey so it is only fair I share. Add some animations and you are set my friend.
Make a round looking icon by using a box and an icon
Box(
modifier = Modifier
.clip(CircleShape)
.size(40.dp)
.background(Color.Black)
.padding(3.dp)
.clip(CircleShape)
.background(Color.White),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
2.Place the newly made rounded icon and some text next to each other by using a Row
Row(
verticalAlignment = Alignment.CenterVertically,
){
Box(
modifier = Modifier
.clip(CircleShape)
.size(40.dp)
.background(Color.Black)
.padding(3.dp)
.clip(CircleShape)
.background(Color.White),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
Text(
text = checkedText.value,
color = color.value,
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 5.dp)
)
}
3.Replace whatever you want with variables so you can customize
it
#Composable
fun RoundedCheckView(
) {
val isChecked = remember { mutableStateOf(false) }
val checkedText = remember { mutableStateOf("unChecked") }
val circleSize = remember { mutableStateOf(20.dp) }
val circleThickness = remember { mutableStateOf(2.dp) }
val color = remember { mutableStateOf(Color.Gray) }
Row(
verticalAlignment = Alignment.CenterVertically,
{
Box(
modifier = Modifier
.clip(CircleShape)
.size(circleSize.value)
.background(color.value)
.padding(circleThickness.value)
.clip(CircleShape)
.background(Color.White) ,
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
Text(
text = checkedText.value,
color = color.value,
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 5.dp)
)
}
}
4.Finally add Modifier.toggleable to the row, basically making it a clickable item that toggles (between true and false) a variable in this case isChecked. Then just customize the variables according to what you need
#Composable
fun RoundedCheckView()
{
val isChecked = remember { mutableStateOf(false) }
val checkedText = remember { mutableStateOf("unChecked") }
val circleSize = remember { mutableStateOf(20.dp) }
val circleThickness = remember { mutableStateOf(2.dp) }
val color = remember { mutableStateOf(Color.Gray) }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.toggleable(value = isChecked.value,role = Role.Checkbox) {
isChecked.value = it
if (isChecked.value) {
checkedText.value = "Checked"
circleSize.value = 40.dp
circleThickness.value = 3.dp
color.value = Color.Black
} else {
checkedText.value = "unChecked"
circleSize.value = 20.dp
circleThickness.value = 2.dp
color.value = Color.Gray
}
}) {
Box(
modifier = Modifier
.clip(CircleShape)
.size(circleSize.value)
.background(color.value)
.padding(circleThickness.value)
.clip(CircleShape)
.background(Color.White) ,
contentAlignment = Alignment.Center
) {
if(isChecked.value){
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
}
Text(
text = checkedText.value,
color = color.value,
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 5.dp)
)
}
}
This is how we can make a custom check box in jetpack compose
val isCheck = remember { mutableStateOf(false) }
Row {
Card(
modifier = Modifier.background(Color.White),
elevation = 0.dp,
shape = RoundedCornerShape(6.dp),
border = BorderStroke(1.5.dp, color = titleColor)
) {
Box(
modifier = Modifier
.size(25.dp)
.background(if (isCheck.value) titleColor else Color.White)
.clickable {
isCheck.value = !isCheck.value
},
contentAlignment = Center
) {
if(isCheck.value)
Icon(Icons.Default.Check, contentDescription = "", tint = Color.White)
}
}
Text(
modifier = Modifier
.align(CenterVertically)
.padding(start = 10.dp),
text = "I agree with the terms & condition",
)
}
You can try to make it use Box with modifier content alignment center. and put an icon on there.
#Preview
#Composable
fun Check() {
Box(
modifier = Modifier
.clip(CircleShape)
.size(50.dp)
.background(Color.Red)
.padding(5.dp)
.clip(CircleShape)
.background(Color.Blue),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
}
We can use animations to make it behave similar to the default Checkbox. Using Modifier.toggleable on the top-level Row, the entire thing is clickable, including the label. This also creates the proper semantics for screen reader users. You can change the shape of the card to get a circular checkbox.
#Composable
fun PrimaryCheckbox(
label: String,
modifier: Modifier = Modifier,
size: Float = 24f,
checkedColor: Color = DarkGray,
uncheckedColor: Color = White,
checkmarkColor: Color = White,
onValueChange: () -> Unit
) {
var isChecked by remember { mutableStateOf(false) }
val checkboxColor: Color by animateColorAsState(if (isChecked) checkedColor else uncheckedColor)
val density = LocalDensity.current
val duration = 200
Row(
modifier = modifier
.toggleable(
value = isChecked,
role = Role.Checkbox,
onValueChange = {
isChecked = !isChecked
onValueChange.invoke()
}
)
) {
Card(
elevation = 0.dp,
shape = RoundedCornerShape(4.dp),
border = BorderStroke(1.5.dp, color = checkedColor),
) {
Box(
modifier = Modifier
.size(size.dp)
.background(checkboxColor),
contentAlignment = Alignment.Center
) {
androidx.compose.animation.AnimatedVisibility(
visible = isChecked,
enter = slideInHorizontally(
animationSpec = tween(duration)
) {
with(density) { (size * -0.5).dp.roundToPx() }
} + expandHorizontally(
expandFrom = Alignment.Start,
animationSpec = tween(duration)
),
exit = fadeOut()
) {
Icon(
Icons.Default.Check,
contentDescription = null,
tint = checkmarkColor
)
}
}
}
Text(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(start = 8.dp),
text = label,
)
}
}

Categories

Resources