I have been using onebone Library for implementing collapse toolbar. I wanted to know how to implement action menu like below in the library code:
I wanted to add 3 menu icons and for that I tried to add code in CollapsingToolbarScaffold but its not coming properly on the extreme right corner.
I could find the code ie :
#Composable
internal fun MainScreen() {
val state = rememberCollapsingToolbarScaffoldState()
CollapsingToolbarScaffold(
modifier = Modifier
.fillMaxSize(),
state = state,
scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
toolbar = {
val textSize = (18 + (30 - 18) * state.toolbarState.progress).sp
Box(
modifier = Modifier
.background(MaterialTheme.colors.primary)
.fillMaxWidth()
.height(150.dp)
.pin()
)
Text(
text = "Guided Selling",
modifier = Modifier
.road(Alignment.CenterStart, Alignment.BottomStart)
.padding(60.dp, 16.dp, 16.dp, 16.dp),
color = Color.White,
fontSize = textSize
)
Image(
modifier = Modifier
.pin()
.padding(16.dp),
painter = painterResource(id = R.drawable.abc_vector_test),
contentDescription = null
)
}
) {
LazyColumn(
modifier = Modifier
.fillMaxWidth()
) {
items(100) {
Text(
text = "Item $it",
modifier = Modifier.padding(8.dp)
)
}
}
// Box(
// modifier = Modifier
// .fillMaxWidth()
// .alpha(0.5f)
// .background(MaterialTheme.colors.secondary)
// .height(40.dp)
// )
}
CollapsingToolbarScaffold(
modifier = Modifier.fillMaxSize(),
state = rememberCollapsingToolbarScaffoldState(),
scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
toolbar = {
// toolbar contents...
}
) {
// body contents...
}
}
I could find the solution to this custom view; Below is my code for action items.
CollapsingToolbarScaffold(
modifier = Modifier.fillMaxSize(),
state = rememberCollapsingToolbarScaffoldState(),
scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
toolbar = {
// toolbar contents...
Row(
modifier = Modifier
.padding(all = 8.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.End
)
{
Image(painter = painterResource(id = R.drawable.ic_menu_search), contentDescription =null )
Spacer(modifier = Modifier.width(16.dp))
Image(painter = painterResource(id = R.drawable.ic_create), contentDescription =null )
Spacer(modifier = Modifier.width(16.dp))
Image(painter = painterResource(id = R.drawable.ic_avatar),
modifier = Modifier
.size(32.dp)
.clip(CircleShape),
contentDescription =null
)
}
}
)
Related
I have a composable that animates the content size on scroll. This composable contains one Surface that contains Image and Column
#Composable
fun TopCard(modifier: Modifier = Modifier, canScrollUp: Boolean = false) {
val animateHeigh by animateDpAsState(targetValue = if (canScrollUp) 110.dp else 180.dp)
Surface(
modifier = modifier
.padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 0.dp)
.height(animateHeigh)
.fillMaxWidth()
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioHighBouncy,
stiffness = Spring.StiffnessMedium
)
),
shape = RoundedCornerShape(16.dp),
color = MaterialTheme.colors.primary
) {
Image(
painter = painterResource(id = R.drawable.cardbackground),
contentDescription = "card background",
contentScale = ContentScale.Crop
)
Column(modifier = modifier.padding(16.dp)) {
Text(text = "Ono Cash")
Text(text = "Total Saldo", modifier = Modifier.padding(top = 8.dp))
Text(text = "Rp 0", modifier = Modifier.padding(top = 8.dp))
Row(
modifier = Modifier
.padding(top = 16.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(imageVector = Icons.Default.AddCircle, contentDescription = "Top Up")
Text(text = "Top Up")
}
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(imageVector = Icons.Outlined.FileUpload, contentDescription = "Transfer")
Text(text = "Transfer")
}
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(imageVector = Icons.Outlined.Menu, contentDescription = "Transfer")
Text(text = "Transfer")
}
}
}
}
}
The animation becomes laggy when I set contentScale property of Image composable to Crop as you can see on this video
But, when I remove the contentScale property, the animation works well. The video
How do I solve this issue?
I've tried to set animateContentSize() function on the Image composable. But it didn't solve the issue
It turns out that the image was to big that causes the laggy
I want to display the icons below so the center one is overlapping.
I am trying to use the Box but not sure how to arrange them so they are overlapping and in the center of the screen.
I have started using a Box with 3 Box stacked on each other. But not sure about how to arrange them so the center slightly overlaps the left and right icons. And slightly higher.
fun SurveyIconScreen() {
Box {
Box(modifier = Modifier
.clip(CircleShape)
.size(22.dp)
.background(color = Color.White),
contentAlignment = Alignment.Center) {
Icon(painter = painterResource(id = R.drawable.ic_star), contentDescription = "Star")
}
Box(modifier = Modifier
.clip(CircleShape)
.size(22.dp)
.background(color = Color.White),
contentAlignment = Alignment.Center) {
Icon(painter = painterResource(id = R.drawable.ic_cart), contentDescription = "Star")
}
Box(modifier = Modifier
.clip(CircleShape)
.size(22.dp)
.background(color = Color.White),
contentAlignment = Alignment.Center) {
Icon(painter = painterResource(id = R.drawable.ic_cart), contentDescription = "Star")
}
}
}
You can apply an offset modifier to overlap the icons.
Also use the zIndex(1f) modifier to drawn the icon in the center on top of all other icons.
Something like:
val shape = RoundedCornerShape(4.dp)
val borderColor = LightGray
Row(
modifier = Modifier.fillMaxWidth().height(70.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
){
Icon(Icons.Outlined.Star, contentDescription = "Star",
modifier = Modifier
.offset(x = 3.dp)
.size(32.dp)
.border(BorderStroke(1.dp,borderColor), shape)
.background(White)
)
Icon(
Icons.Outlined.ShoppingCart,
contentDescription = "Star",
modifier = Modifier
.zIndex(1f)
.offset(y = -12.dp)
.size(32.dp)
.border(BorderStroke(1.dp,borderColor), shape)
.background(White)
)
Icon(Icons.Outlined.Note, contentDescription = "Star",
modifier = Modifier
.offset(x = -3.dp)
.size(32.dp)
.border(BorderStroke(1.dp,borderColor), shape)
.background(White)
)
}
I am trying to build the following component.
Following is my code,
#Composable
fun View(modifier: Modifier = Modifier){
Row(modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = { /*TODO*/ },
modifier = Modifier
.background(shape = roundShape, color = Color.Blue)
.size(40.dp)) {
Icon(
painter = painterResource(id = R.drawable.ic_microphone_2),
contentDescription = null,
tint = Color.White
)
}
BasicTextField(
value = "",
onValueChange = { },
modifier=Modifier
.height(40.dp)
.fillMaxWidth()
.border(1.dp,Color.Gray,roundShape)
)
}
The content of the IconButton is no longer centered after switching the LayoutDirection to Rtl.
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
View()
}
this is the result of my code.
#Composable
fun IconView(modifier: Modifier = Modifier) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(modifier = Modifier
.background(shape = RoundedCornerShape(20.dp), color = Color.Blue)
.size(50.dp).clickable {
}
){
Icon(
modifier = Modifier.align(alignment = Center),
painter = painterResource(id = android.R.drawable.ic_menu_call),
contentDescription = null,
tint = Color.White
)
}
BasicTextField(
value = "",
onValueChange = { },
modifier = Modifier
.height(40.dp)
.fillMaxWidth()
.border(1.dp, Color.Gray, RoundedCornerShape(20.dp))
)
}
}
Don't use iconButton as parent use Box and align child Icon to center. I Hope it will help you. Cheers
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
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,
)
}
}