I want to split my screen in half horizontally in Jetpack Compose like this:
#Composable
fun Splash(alpha: Float) {
val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp
val screenWidth = configuration.screenWidthDp.dp
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.cat2))
Box(
modifier = Modifier
.background(Blue)
.height(screenHeight / 2)
.padding(8.dp),
contentAlignment = Alignment.TopCenter
) {
Column() {
Text(text = "Example", fontSize = 44.sp)
}
}
Box(
modifier = Modifier
.background(Red)
.height(screenHeight / 2)
.padding(8.dp),
contentAlignment = Alignment.BottomCenter
){
Column {
Text(text = "Example", textAlign = TextAlign.End, color = Grey, fontSize = 12.sp)
}
}
}
I can get screen height with LocalConfiguration.current in dp and I set my top box and bottom box alignments as Alignment.TopCenter and Alignment.BottomCenter respectively but it didn't work. Second box(Red one) stays on top of the blue one.
You can wrap your Boxes with a Column and set Modifier.weight(1f) for each box to set both of them at same height with
#Composable
fun Splash() {
Column(modifier =Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Blue)
.weight(1f)
.padding(8.dp),
contentAlignment = Alignment.TopCenter
) {
Column() {
Text(text = "Example", fontSize = 44.sp)
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.background(Red)
.weight(1f)
.padding(8.dp),
contentAlignment = Alignment.Center
){
Column {
Text(text = "Example", textAlign = TextAlign.End, color = DarkGray, fontSize = 12.sp)
}
}
}
}
or wrap with a BoxWithConstraints which returns Contraints, maxWidth, maxHeight and use Modifier.align to one Box to top and other one to Bottom. BoxWithConstraints is useful for getting measurement parameters and set them as children Modifiers.
#Composable
fun Splash2() {
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Blue)
.height(maxHeight/2)
.align(Alignment.TopStart)
.padding(8.dp),
contentAlignment = Alignment.TopCenter
) {
Column() {
Text(text = "Example", fontSize = 44.sp)
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.background(Red)
.align(Alignment.BottomStart)
.height(maxHeight/2)
.padding(8.dp),
contentAlignment = Alignment.Center
){
Column {
Text(text = "Example", textAlign = TextAlign.End, color = DarkGray, fontSize = 12.sp)
}
}
}
}
The simplest way to set a height equal to the half-screen height is with a fractional in the modifier.
Column(
modifier = Modifier
.padding(10.dp)
.fillMaxHeight(0.5f)
){ //Content}
The best way to implement your image is this way:
Column(
Modifier
.fillMaxSize()
.padding(8.dp)
) {
Row(
Modifier
.fillMaxWidth()
.weight(1f)
.background(Blue),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
Text(text = "Example", fontSize = 44.sp)
}
Row(
Modifier
.fillMaxWidth()
.weight(1f)
.background(Red),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
Text(text = "Example", fontSize = 44.sp)
}
}
Related
I am trying to build the following layout:
I have the following code so far:
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(8.dp)
) {
//First Row
WidgetComposed(
Modifier
.fillMaxWidth()
.height(300.dp),
bgColor = Color.Red,
widgetName = "Widget 1"
)
Spacer(modifier = Modifier.height(8.dp))
// Second Row
Row(modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(), horizontalArrangement = Arrangement.SpaceBetween) {
WidgetComposed(
Modifier
.fillMaxWidth(0.6f)
.aspectRatio(2f / 3f, true),
bgColor = Color.Green,
widgetName = "Widget 2 (2:3)"
)
Spacer(modifier = Modifier.width(8.dp))
Column(modifier = Modifier
.fillMaxHeight()) {
WidgetComposed(
Modifier
.fillMaxWidth()
.weight(1f),
bgColor = Color.Blue,
widgetName = "Widget 3"
)
Spacer(modifier = Modifier.height(8.dp))
WidgetComposed(
Modifier
.fillMaxWidth()
.weight(1f),
bgColor = Color.Cyan,
widgetName = "Widget 4"
)
}
}
It gives me this:
The column with Widget 3 and 4 does not fill the height of the parent row. If I put hardcoded height value it works.
Aspect ration and 60% width is important to keep for Widget 2.
WidgetCompose is just a card and a box:
#Composable
fun WidgetComposed(modifier: Modifier = Modifier, bgColor: Color, widgetName: String) {
Card(modifier = modifier, shape = RoundedCornerShape(8.dp), backgroundColor = bgColor) {
Box(modifier = Modifier.padding(16.dp), contentAlignment = Alignment.Center) {
Text(text = widgetName)
}
}
}
How can I make the column with Widget 3 and 4 automatically fill the available height?
Preferably without using .onGloballyPositioned modifier
You can use:
Row(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min),
horizontalArrangement = Arrangement.SpaceBetween
) {
WidgetComposed(
Modifier
.weight(0.6f)
.aspectRatio(2f / 3f, true),
//...
)
Spacer(modifier = Modifier.width(8.dp).fillMaxHeight())
Column(
modifier = Modifier
.weight(0.4f)
.fillMaxHeight()
) {
//..
}
}
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
Can not understand why in this card, in the column, the spacebetween vertical alignment is not taking into account. I'm expecting the Title to be on top, and the "View Details" button to be at the bottom of the Column
fun UserCard(image:Int, text:String, actionButtonLabel:String){
Card(
elevation = 4.dp,
modifier = Modifier
.padding(12.dp)
.fillMaxWidth()
.wrapContentHeight()
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.padding(8.dp)
.border(width = 1.dp, color = Color.Blue)
.padding(12.dp)
) {
Image(
painter = painterResource(id = image),
contentDescription = "",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(120.dp)
.clip(CircleShape)
)
Column(
modifier= Modifier.fillMaxHeight(),
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.SpaceBetween, // <-- seems not to be taking into account
) {
Title(text)
Button(onClick = { }) {
Text(text = actionButtonLabel)
}
}
}
}
}
example of call of UserCard
UserCard(
image= R.drawable.iron,
text = "Live after death",
actionButtonLabel = "View details"
)
You can apply an intrinsic measurements to the Row container and the fillMaxHeight() Modifier to the Column.
Your issue is the height of the column.
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.height(IntrinsicSize.Min)
//..
){
/* Image */
Column(
modifier = Modifier
.fillMaxHeight(),
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.SpaceBetween){
//...
}
}
I am trying to prepare a design in jetpack compose with a partial border on one side of the box. Here is the UI I have right now,
The background is a solid color as of now but will be replaced with a image. I want to break border on bottom left of and add some text similar to the screenshot below while keeping the background as it is.
Here is my code:
Box(modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()) {
Box(modifier = Modifier.fillMaxHeight().fillMaxWidth().background(Color(0xFF37C7D7).copy(alpha = 0.6f)))
Box(modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.background(Color.Transparent)
.padding(20.dp,30.dp)
.border(width = 0.8.dp, color = Color.White.copy(alpha = 0.5f), shape=RoundedCornerShape(32.dp))
)
Box(modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.background(Color.Transparent)
.padding(28.dp,38.dp)
.border(width = 0.8.dp, color = Color.White.copy(alpha = 0.5f), shape=RoundedCornerShape(28.dp))
)
Column(modifier = Modifier.statusBarsPadding()
.fillMaxWidth()
.fillMaxHeight().padding(20.dp,40.dp),
verticalArrangement = Arrangement.Bottom) {
Text(text = "this is Test",modifier = Modifier.padding(0.dp,30.dp))
}
}
You can prevent part of the view from being drawn with Modifier.drawWithContent and DrawScope.clipRect. Using this method, you can create the following modifier:
fun Modifier.drawWithoutRect(rect: Rect?) =
drawWithContent {
if (rect != null) {
clipRect(
left = rect.left,
top = rect.top,
right = rect.right,
bottom = rect.bottom,
clipOp = ClipOp.Difference,
) {
this#drawWithContent.drawContent()
}
} else {
drawContent()
}
}
Use it like this:
Box(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
Image(
painterResource(id = R.drawable.my_image),
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier.fillMaxSize()
)
var textCoordinates by remember { mutableStateOf<Rect?>(null) }
Box(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.background(Color(0xFF37C7D7).copy(alpha = 0.6f))
)
Box(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.drawWithoutRect(textCoordinates)
.padding(20.dp, 30.dp)
.border(
width = 0.8.dp,
color = Color.White.copy(alpha = 0.5f),
shape = RoundedCornerShape(32.dp)
)
)
Box(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.drawWithoutRect(textCoordinates)
.padding(28.dp, 38.dp)
.border(
width = 0.8.dp,
color = Color.White.copy(alpha = 0.5f),
shape = RoundedCornerShape(28.dp)
)
)
Column(
verticalArrangement = Arrangement.Bottom,
modifier = Modifier
.statusBarsPadding()
.padding(20.dp, 40.dp)
.onGloballyPositioned {
textCoordinates = it.boundsInParent()
}
.align(Alignment.BottomStart)
) {
Text(text = "this is Test", modifier = Modifier.padding(0.dp, 30.dp))
}
}
Result:
I'm trying to use the new Jetpack Compose UI framework, but I'm running into an issue. I'd like to achieve this layout, which in xml is pretty easy to achieve:
But I can't figure out how to make the vertical divider take up the available vertical space, without specifying a fixed height. This code that I've tried doesn't seem to work:
#Composable
fun ListItem(item: PlateUI.Plate) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = item.code)
}
Spacer(
modifier = Modifier
.preferredWidth(1.dp)
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Spacer(modifier = Modifier.weight(1f))
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp),
text = item.name
)
Spacer(modifier = Modifier.weight(1f))
}
}
}
I keep getting this result:
I also tried with ConstraintLayout, but it still didn't work
#Composable
fun ListItem(item: PlateUI.Plate) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val(column, divider, text) = createRefs()
Column(
modifier = Modifier
.padding(8.dp)
.constrainAs(column){
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = item.code)
}
Spacer(
modifier = Modifier
.preferredWidth(1.dp)
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
.constrainAs(divider){
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(column.end)
}
)
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp)
.constrainAs(text){
start.linkTo(divider.end)
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
},
text = item.name
)
}
}
}
But nothing seems to work. Is this a bug, a missing feature or am I just missing something?
EDIT: Apparently the real problem is that the divider doesn't know how to measure when the Surface doesn't have a fixed height, setting height equal to some number solves the issue, but then the view doesn't adapt to the content height anymore, so this can't be the solution
You can apply:
the modifier .height(IntrinsicSize.Max) to the Row
the modifiers .width(1.dp).fillMaxHeight() to the Spacer
You can read more about the Intrinsic measurements here.
Something like:
Row(
modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
verticalAlignment = Alignment.CenterVertically
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
....
) {
Text(text = "....")
}
Spacer(
modifier = Modifier
.width(1.dp)
.fillMaxHeight()
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Text(...)
}
You can set Intrinsic.Max for the preferredHeight of the Row, then set the Spacer to fill max height. You can read more on Intrinsics in this codelab section.
#Composable
fun ListItem() {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
Row(
modifier = Modifier.fillMaxWidth().preferredHeight(IntrinsicSize.Max),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = "2456")
}
Spacer(
modifier = Modifier
.preferredWidth(1.dp)
.fillMaxHeight()
.background(color = Color.Black.copy(0.12f))
)
Spacer(modifier = Modifier.weight(1f))
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp),
text = "Some name"
)
Spacer(modifier = Modifier.weight(1f))
}
}
}
I've solved it using constraint layout:
Box(modifier = Modifier.padding(Dp(50f))) {
ConstraintLayout(
modifier = Modifier
.border(width = Dp(1f), color = Color.Black)
.fillMaxWidth()
) {
val (left, divider, right) = createRefs()
Column(
modifier = Modifier
.padding(horizontal = Dp(20f))
.constrainAs(left) {
width = Dimension.wrapContent
start.linkTo(parent.start)
top.linkTo(parent.top)
end.linkTo(divider.start)
bottom.linkTo(parent.bottom)
}
) {
Text(text = "Code")
Text(text = "A12")
}
Box(
modifier = Modifier
.width(Dp(1f))
.background(Color.Black)
.constrainAs(divider) {
width = Dimension.wrapContent
height = Dimension.fillToConstraints
start.linkTo(left.end)
top.linkTo(parent.top)
end.linkTo(right.start)
bottom.linkTo(parent.bottom)
}
)
Box(
modifier = Modifier
.constrainAs(right) {
width = Dimension.fillToConstraints
start.linkTo(divider.end)
top.linkTo(parent.top)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
) {
Text(
text = "Test",
modifier = Modifier
.padding(vertical = Dp(100f))
.align(Alignment.Center)
)
}
}
}
The key part is using that modifier height = Dimension.fillToConstraints
There are plenty of solutions here, but I thought I could demonstrate the ConstraintLayout approach and add a helpful usage of the IntrinsicSize enum that solves one of the issues (needing an adaptive height for the composable). Interestingly, either IntrinsicSize.Max or IntrinsicSize.Min will yield the desired behavior.
I used most of your code. The key differences are:
declares a guideline (my value passed in for the fraction does not produce the exact result you were looking for, but can be adjusted easily (use a fraction slightly smaller than .2) This can be useful if you expect wrapContent to alter your left Text to vary the location of a spacer, but would prefer a consistent spacer location across a list of these items.
others have mentioned, spacer modifier should include .fillMaxHeight()
define the height of the surface wrapper to be .height(IntrinsicSize.Min) docs ref here: https://developer.android.com/jetpack/compose/layout#intrinsic-measurements
divider start is constrained to the guideline
had to change the Spacer modifier to access the width, instead of preferredWidth
#Composable
fun ListItem(item: Plate) {
Surface(
modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Min),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val guideline = createGuidelineFromStart(0.2f)
val(column, divider, text) = createRefs()
Column(
modifier = Modifier
.padding(8.dp)
.constrainAs(column){
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(guideline)
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = item.code)
}
Spacer(
modifier = Modifier
.constrainAs(divider){
top.linkTo(column.top)
bottom.linkTo(column.bottom)
start.linkTo(guideline)
}
.width(1.dp)
.fillMaxHeight()
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp)
.constrainAs(text){
start.linkTo(divider.end)
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
},
text = item.name
)
}
}
}
I think Row layout is enough.
#Preview(showBackground = true, heightDp = 100)
#Composable
fun ListItem(item: PlateUI.Plate = PlateUI.Plate()) {
Card(
shape = RoundedCornerShape(8.dp)
) {
Row(
modifier = Modifier
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier.padding(8.dp),
text = "Code\n${item.code}",
textAlign = TextAlign.Center
)
Box(
Modifier
.fillMaxHeight()
.width(1.dp)
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Text(
modifier = Modifier
.weight(1f)
.padding(8.dp),
text = item.name,
textAlign = TextAlign.Center
)
}
}
}