I got into Jetpack compose and I am having a hard time finding examples of a normal screen. Everyone just uses scaffold and calls it a day. Using fillMaxSize is supposed to do exactly this, can someone explain how composable inherit their size? The topmost composable specifically (root composable)?
#Composable
fun MainContent() {
Surface(modifier = Modifier.fillMaxSize()) {
Column() {
SearchBox()
CurrentWeather()
WindAndHumidityBox()
}
}
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeweathertestTheme {
// A surface container using the 'background' color from the theme
Surface(
color = MaterialTheme.colors.background,
modifier = Modifier.fillMaxSize()
) {
MainContent()
}
}
}
}
}
Also the PURPLE portion is using this code.
Surface(
modifier = Modifier
.padding(40.dp).wrapContentHeight(), elevation = 3.dp, shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colors.primarySurface
) {
In XML we could use wrap content and apply padding to it, this doesn't seem to be the case with compose? I am trying to do something like this
EDIT: Whole compose file
#Composable
private fun Content() {
Column(modifier = Modifier.fillMaxSize()) {
SearchBox()
CurrentWeather()
WindAndHumidityBox()
}
}
#Composable
fun WindAndHumidityBox() {
Surface(
modifier = Modifier
.padding(40.dp)
.wrapContentHeight(), elevation = 3.dp, shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colors.primarySurface
) {
Row {
Row(modifier = Modifier.align(Alignment.CenterVertically)) {
Image(
painter = painterResource(id = R.drawable.day_clear),
contentDescription = "humidity"
)
Text(
text = "Precipitation \n 10%", fontWeight = FontWeight.Bold, fontSize = 20.sp,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
Row(modifier = Modifier.align(Alignment.CenterVertically)) {
Image(
painter = painterResource(id = R.drawable.day_clear),
contentDescription = "humidity"
)
Text(
text = "Precipitation \n 10%", fontWeight = FontWeight.Bold, fontSize = 20.sp,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
}
}
}
#Composable
private fun CurrentWeather() {
Box(modifier = Modifier.fillMaxWidth()) {
Column(Modifier.align(Alignment.Center)) {
Column(modifier = Modifier.padding(bottom = 20.dp)) { // location and time/date
Row(modifier = Modifier.align(Alignment.CenterHorizontally)) {
Icon(
painter = painterResource(id = R.drawable.ic_location),
contentDescription = "location"
)
Text(
text = "London", fontWeight = FontWeight.Bold, fontSize = 20.sp,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
Text(text = "Thu 4 December 8:41 am")
}
Row(Modifier.align(Alignment.CenterHorizontally)) {
Image(
painterResource(id = R.drawable.day_clear),
modifier = Modifier.align(alignment = Alignment.CenterVertically),
contentDescription = "current weather image displaying the current weather"
)
Text(
text = "27", fontSize = 40.sp, modifier = Modifier
.padding(5.dp)
.align(Alignment.Top)
)
}
Text(
text = "Sunny",
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(top = 20.dp),
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
}
}
}
#Composable
private fun SearchBox() {
Box {
var textValue by remember { mutableStateOf("") }
val context = LocalContext.current
OutlinedTextField(value = textValue,
leadingIcon = {
Icon(
painterResource(id = R.drawable.ic_baseline_search_24),
"search"
)
},
label = { Text("Search For A City") },
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Search,
keyboardType = KeyboardType.Text
),
keyboardActions = KeyboardActions(
onSearch = {
hideKeyboard(context = context)
}),
onValueChange = { text ->
textValue = text
}
)
}
}
You are using too many parent containers.
Just use:
Surface(
color = MaterialTheme.colors.background,
modifier = Modifier.fillMaxSize()
){
Column(){
WindAndHumidityBox()
}
}
Then you can achieve the PURPLE portion in many ways.
In particular check the Intrinsic measurements.
For example:
Surface(
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
elevation = 3.dp,
shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colors.primarySurface
) {
Row(
Modifier.padding(40.dp), //Padding inside the Row
horizontalArrangement = Arrangement.Center
) {
Row(modifier = Modifier
.align(Alignment.CenterVertically)
.height(IntrinsicSize.Min) //Intrinsic measurements to center the icon vertically
) {
Image(
modifier = Modifier.fillMaxHeight(),
//..
)
//...
}
//...
}
}
Related
I am new in jetpack compose, and I am try to learn it, so I have simple onboarding screen in jetpack compose, when I update project to jetpack compose 1.1.1 , "Next" button not working for onboarding screen to scroll horizontally. it was working when I use jetpack compose 1.0.0 version, I do not know what change in new version, any idea?
#ExperimentalPagerApi
#Composable
fun OnBoardScreen() {
val scaffoldState = rememberScaffoldState()
val onBoardViewModel : OnBoardViewModel = viewModel()
val context = LocalContext.current
val currentPage = onBoardViewModel.currentPage.collectAsState()
Toast.makeText(context, "${currentPage.value}", Toast.LENGTH_SHORT).show()
val pagerState = rememberPagerState(
pageCount = onBoardItem.size,
initialOffscreenLimit = 2,
initialPage = 0,
infiniteLoop = false
)
Scaffold(
modifier = Modifier.fillMaxSize(),
scaffoldState = scaffoldState
) {
Surface(
modifier = Modifier.fillMaxSize()
) {
LaunchedEffect(scaffoldState.snackbarHostState){
pagerState.animateScrollToPage(
page = currentPage.value
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.background(Gray200)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
HorizontalPager(
state = pagerState
) { page ->
Column(
modifier = Modifier
.padding(top = 65.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = onBoardItem[page].image),
contentDescription = "OnBoardImage",
modifier = Modifier
.size(250.dp)
)
Text(
text = onBoardItem[page].title,
modifier = Modifier
.padding(top = 50.dp),
color = Color.White,
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
Text(
text = onBoardItem[page].desc,
modifier = Modifier
.padding(30.dp),
color = Color.White,
fontSize = 18.sp,
textAlign = TextAlign.Center
)
}
}
PagerIndicator(onBoardItem.size, pagerState.currentPage)
}
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
) {
Row(
modifier = Modifier
.padding(bottom = 20.dp)
.fillMaxWidth(),
horizontalArrangement = if (pagerState.currentPage != 2 ) {
Arrangement.SpaceBetween
} else {
Arrangement.Center
}
) {
if (pagerState.currentPage == 2) {
OutlinedButton(
onClick = {
Toast.makeText(context, "Start the Screen", Toast.LENGTH_SHORT).show()
},
shape = RoundedCornerShape(45.dp)
) {
Text(
text = "Get Started",
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 40.dp
),
color = Color.Black
)
}
} else {
Text(
text = "Skip",
color = Color.White,
modifier = Modifier.padding(start = 20.dp),
fontSize = 18.sp,
fontWeight = FontWeight.Medium
)
Text(
text = "Next",
color = Color.White,
modifier = Modifier
.clickable {
onBoardViewModel.setCurrentPage(pagerState.currentPage + 1)
}
.padding(end = 20.dp),
fontSize = 18.sp,
fontWeight = FontWeight.Medium
)
}
}
}
}
}
}
}
It looks like you expect these lines to scroll the pager every time you change the value of currentPage:
LaunchedEffect(scaffoldState.snackbarHostState) {
pagerState.animateScrollToPage(
page = currentPage.value
)
}
But scaffoldState.snackbarHostState in this scope is a static value, which means LaunchedEffect is not gonna be relaunched.
One option is to pass currentPage instead, but also, as your currentPage is backed by flow, it's cleaner to collect it:
LaunchedEffect(Unit) {
onBoardViewModel.currentPage
.collect {
pagerState.animateScrollToPage(
page = currentPage.value
)
}
}
p.s.
Also, when you need to make a text button, instead of adding clickable to the Text you can use TextButton:
TextButton(
onClick = {
}
) {
Text(/*...*/)
}
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
I am trying to put a TextField and a FAB inside a bottomBar using Jetpack Compose.
I wrapped the two with a box, which has the modifier "fillMaxWidth".
But the two controls dont use the full width.
Does anyone know, how to fix this issue?
Here is my Code:
#Composable
fun ChatView() {
Scaffold(
topBar= { ChannelButton() },
bottomBar = { ChatBox() },
modifier = Modifier
.padding(10.dp)
) {
ChatList()
}
}
#Composable
fun ChatBox() {
Box(modifier = Modifier
.background(DiscordDarkGray)
.fillMaxWidth()
){
Column(modifier = Modifier
.padding(10.dp)
.fillMaxWidth()) {
HorizontalCenteredRow(modifier = Modifier
.fillMaxWidth()) {
val textState = remember { mutableStateOf(TextFieldValue()) }
TextField(
value = textState.value,
onValueChange = { textState.value = it }
)
Spacer(modifier = Modifier.width(10.dp))
FloatingIconActionButton (
icon = Icons.Default.Send,
onClick = { /*TODO*/ },
backgroundColor = DiscordBlue
)
}
Spacer(modifier = Modifier.height(60.dp))
}
}
}
Here is the Code of the HorizontalCenteredRow:
#Composable
fun HorizontalCenteredRow(
modifier: Modifier = Modifier,
content: #Composable RowScope.() -> Unit
) {
Row(
modifier = modifier
.wrapContentSize(Alignment.Center),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
content = content
)
}
Here is the code of the FAB:
#Composable
fun FloatingIconActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
backgroundColor: Color = MaterialTheme.colors.secondary,
contentColor: Color = contentColorFor(backgroundColor),
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
icon: ImageVector = Icons.Default.QuestionAnswer,
iconContentDescription: String = "",
) {
Surface(
modifier = modifier.let {
if (enabled) {
it.clickable(
onClick = onClick,
role = Role.Button,
interactionSource = interactionSource,
indication = null
)
} else it
},
shape = shape,
color = backgroundColor,
contentColor = contentColor,
elevation = elevation.elevation(interactionSource).value
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(MaterialTheme.typography.button) {
Box(
modifier = Modifier
.defaultMinSize(minWidth = FabSize, minHeight = FabSize)
.indication(interactionSource, rememberRipple()),
contentAlignment = Alignment.Center
) {
Icon(
icon,
iconContentDescription,
tint = if (enabled) {
colors().onPrimary
} else {
colors().onPrimary.transparentize(.6f)
}
)
}
}
}
}
}
Using
HorizontalCenteredRow(
modifier = Modifier.fillMaxWidth()
the Row fills all the space, but it doesn't mean that the children occupy the entire space.
If you want to fill all the space with the children you have to change the default dimensions of them.
For example you can apply modifier = Modifier.weight(1f) to the TextField.
Something like:
HorizontalCenteredRow(modifier = Modifier
.fillMaxWidth()) {
val textState = remember { mutableStateOf(TextFieldValue()) }
TextField(
value = textState.value,
onValueChange = { textState.value = it },
modifier = Modifier.weight(1f)
)
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,
)
}
}
I want to create Cardview using jetpack compose but i am not able to find any
example.
You can use something like:
Card(
shape = RoundedCornerShape(8.dp),
backgroundColor = MaterialTheme.colors.surface,
) {
Column(
modifier = Modifier.height(200.dp).padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
){
Text(text = "This is a card view",
style = MaterialTheme.typography.h4)
}
}
With Material3 you can use:
Card(
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = /* ... */)
)
in v0.1.0-dev09 version, can be done like this, where the padding of Card sets the margin of the card, the padding of Box sets the padding inside the card.
Card(
shape = RoundedCornerShape(8.dp),
modifier = Modifier.padding(16.dp).fillMaxWidth()
) {
Box(modifier = Modifier.height(200.dp).padding(16.dp)) {
Text("This is a card view")
}
}
As the friends recommended, Card is a way of creating CardView but you can do that by surface too :
val shape = RoundedCornerShape(10.dp)
Surface(modifier = Modifier.size(250.dp, 70.dp), elevation = 8.dp, shape = shape) {
Text(text = "Sample text")
}
Note that Surface and Card cannot be used for positioning items so that if you wanna position that Text(text = "Sample text") , you should use one layout inside that Surface like this :
val shape = RoundedCornerShape(10.dp)
Surface(modifier = Modifier.size(250.dp, 70.dp), elevation = 8.dp, shape = shape) {
Box(modifier = Modifier.fillMaxSize()) {
Text(modifier = Modifier.align(Alignment.Center), text = "Sample text")
}
}
The appropriate way for creating CardView is this but if you wanna just create shadow for a view, you can use Modifier.shadow (Note that Modifier.shadow and Surface/Card are not the same):
Box(modifier = Modifier.size(250.dp, 70.dp).shadow(8.dp, RoundedCornerShape(10.dp)), contentAlignment = Alignment.Center) {
Text(text = "Sample Text")
}
Code:
class ImageCardActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val painter = painterResource(id = R.drawable.grogu)
val contentDescription = "Grogu says hi"
val title = "Force is strong with Grogu"
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier.fillMaxWidth(0.5f)
) {
ImageCard(
title = title,
contentDescription = contentDescription,
painter = painter
)
}
}
}
}
}
#Composable
fun ImageCard(
title: String,
contentDescription:String,
painter: Painter,
modifier:Modifier=Modifier
){
Card(
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(18.dp),
elevation = 5.dp
) {
Box(
modifier = Modifier.height(200.dp)
) {
// Image
Image(
painter = painter,
contentDescription = contentDescription,
contentScale = ContentScale.Crop
)
// Gradient
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent, Color.Black
),
startY = 300f
)
)
)
// Title
Box(
modifier = Modifier
.fillMaxSize()
.padding(12.dp),
contentAlignment = Alignment.BottomStart
) {
Text(
text = title,
style = TextStyle(color = Color.White, fontSize = 12.sp)
)
}
}
}
}