I would like to align my components based on the Material responsive layout grid, as visualized by from https://material.io/design/layout/responsive-layout-grid.html#columns-gutters-and-margins.
I don't necessarily want my content to be included in a column but rather put baselines on either a start or an end of a column.
A simple example:
// FIXME: What to do?
#Composable
fun example() {
Button(onClick = {
}, text = "Stretch me between start of column 2 and end of column 4"
)
}
My best guess is that I need to use this library somehow: https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary
But I'm still clueless...
How can I achieve what I want to do in Jetpack Compose?
The requirement is not clear. Please comment if this is not what you are looking for.
To have a button with a width of 2 columns in a 4 column grid system. You can use weight and Spacer to achieve the requirement.
This starts at starting of Column 2 and ends at the ending of Column 3.
Row(
modifier = Modifier.fillMaxWidth(),
) {
Spacer(modifier = Modifier.weight(1f))
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.fillMaxWidth()
.weight(2f),
) {
Text(text = "Center Button")
}
Spacer(modifier = Modifier.weight(1f))
}
Using weight and padding modifiers, along with Spacer we can achieve most of the grid requirements.
Related
I'm currently trying to recreate a behavior, upon adding a new element to a LazyColumn the items start shifting to the right, in order to represent a Tree and make the elements easier to read.
The mockup in question:
Documentation
Reading through the documentation of Jetpack Compose in Lists and grids I found the following.
Keep in mind that cases where you’re nesting different direction layouts, for example, a scrollable parent Row and a child LazyColumn, are allowed:
Row(
modifier = Modifier.horizontalScroll(scrollState)
) {
LazyColumn {
// ...
}
}
My implementation
Box(Modifier.padding(start = 10.dp)) {
Row(
modifier = Modifier
.horizontalScroll(scrollState)
.border(border = BorderStroke(1.dp, Color.Black))
) {
LazyColumn(
modifier = Modifier.fillMaxWidth()
) {
for (i in 0..25) {
item {
OptionItem(Modifier.padding(start = (i*20).dp))
}
item {
TaskItem(Modifier.padding(start = (i*10).dp))
}
}
}
}
.
.
.
}
OptionItem represents the element with the dot at the beginning, and TaskItem the other one.
When testing the LazyColumn, it appears as if instead of having a fixed size, the size of the column starts growing just after the elements have gone outside the screen, this causes a strange effect.
As you can see in the GIF, the width of the column starts increasing after the elements no longer fit in the screen.
The Question
I want to prevent this effect from happening, so is there any way I could maintain the width of the column to the maximum all the time?
The reason that applying a simple fillMaxWidth will not work because you are telling a composable to stretch to max, but that is impossible because the view itself can stretch indefinitely since it can be horizontally scrollable. I'm not sure why do you want to prevent this behavior but perhaps maybe you want your views to have some initial width then apply the padding, while maintaining the same width. what you can do in such case is simply give your composables a specific width, or what you can do is to get the width of the box and apply them to your composables by width (i used a text in this case)
val localDensity = LocalDensity.current
var lazyRowWidthDp by remember { mutableStateOf(0.dp) }
Box(
Modifier
.padding(start = 10.dp)
.onGloballyPositioned { layoutCoordinates -> // This function will get called once the layout has been positioned
lazyRowWidthDp =
with(localDensity) { layoutCoordinates.size.width.toDp() } // with Density is required to convert to correct Dp
}
) {
val scrollState = rememberScrollState()
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(scrollState)
) {
items(25) { i ->
Text(
text = "Hello",
modifier = Modifier
.padding(start = (i * 20).dp)
.width(lazyRowWidthDp)
.border(1.dp, Color.Green)
)
}
items(25) { i ->
Text(
text = "World",
modifier = Modifier
.padding(start = (i * 10).dp)
.width(lazyRowWidthDp)
.border(1.dp, Color.Green)
)
}
}
}
Edit:
you can apply horizontal scroll to the lazy column itself and it will scroll in both directions
This question already has answers here:
Compose: wrap text in Row layout, instead of pushing siblings out
(2 answers)
Closed 11 months ago.
I want to set RadioButton component at the end of Row in Jetpack Compose. Tried to using Constraint Layout and moved RadioButton outside the Row but then the RadioButton wasn't centered with other components in Row. What should I do?
Here is my code:
ConstraintLayout {
val (row, button) = createRefs()
Row(
modifier = Modifier
.height(56.dp)
.fillMaxWidth()
.constrainAs(row){
start.linkTo(parent.start)
end.linkTo(parent.end)
},
verticalAlignment = Alignment.CenterVertically
) {
Icon(
/* *** */
)
Text(
text = "mail#gmail.com",
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
)
RadioButton(
/* *** */
)
}
}
What is more i want to cut the Text component if the text is too long (not overlay or underlay the Radio Button)
The simplest solution would be to add a Spacer with Modifier.weight(1f) between your text and the radio button. Row and Column distribute remaining available space between components with a weight Modifier according to their weight. Since there is only one, it will receive all of the remaining space, pushing the radio button to the far right.
For example following code would produce your desired behavior:
Row(modifier = Modifier.height(56.dp).fillMaxWidth(), verticalAlignment = Alignment.CenterVertically){
Icon(Icons.Default.Add,null)
Text("Some text here")
Spacer(Modifier.weight(1f).fillMaxHeight().background(Color.Green)) // height and background only for demonstration
RadioButton(selected = false, onClick = { /*TODO*/ })
}
As I said remaining space is distributed according to the weight of each element, so although this is not what you want to achieve, an example how this might look like
Row(modifier = Modifier.height(56.dp).fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Add, null)
Spacer(Modifier.weight(1f).fillMaxHeight().background(Color.Red)) // height and background only for demonstration
Text("Some text here")
Spacer(Modifier.weight(4f).fillMaxHeight().background(Color.Green)) // height and background only for demonstration
RadioButton(selected = false, onClick = { /*TODO*/ })
}
will get you
The remaining space after measuring the icon, text and radio button are distributed as 20% to the red Spacer and 80% to the green one, since that is their share of the total weight (1/5 and 4/5)
You could create a Box that fills the rest of the Row and put the Button inside it. You can then align the Button to the right.
Box(modifier = Modifier.fillMaxWidth()) {
RadioButton(modifier = Modifier.align(Alignment.End)){}
}
The same can be achieved with columns instead of the Box, but then every separate element inside the row should have a column wrapping it.
I have a Column like this,
Column {
Comopsable1()
Comopsable2()
Comopsable3()
}
I want Composable1() followed by Composable2() from the top.
But, I want to place Comosable3() at the bottom of the column.
Is there a way to do this without introducing a wrapper Composable for Composale3()?
My current code which works,
Column {
Comopsable1()
Comopsable2()
Column(
verticalArrangement = Arrangement.Bottom,
modifier = Modifier.fillMaxHeight(),
) {
Comopsable3()
}
}
Is there a way to remove the newly introduced Column?
You can add a spacer and set it to have a weight of 1
Column {
Composable1()
Composable2()
Spacer(modifier = Modifier.weight(1f))
Composable3()
}
I have some text.
I want to centre it on the screen.
I am using Jetpack Compose.
How do I do this?
I know that there are three types of layouts in Jetpack Compose.
Box
Column
Horizontal
Which one should I use?
I don't know how layouts work.
Are they full screen by default like in XML?
If so, how do I position elements like ConstraintLayout?
How do I set padding and margin from only one side and how do I link elements?
I guess all your questions can be clarified if you follow the Compose Pathway. But I'll try to summarize for you...
You can organize your components using one of the following "layout managers" (which in Compose are just called layouts):
Column (similar to LinearLayout with vertical orientation)
Row (similar to LinearLayout with horizontal orientation)
Box (similar to FrameLayout)
and ConstraintLayout.
If you need something different of these, you can create a custom layout using the Layout composable.
"Which one should I use?"
You can use any of these, depending of the case... To simply display a text in the center of the screen, you can achieve with all of them.
Using Column:
Column(
Modifier.fillMaxSize(), // to fill the whole screen
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Hello")
}
Using Box
Box(
Modifier.fillMaxSize()
) {
Text(text = "Hello",
modifier = Modifier.align(Alignment.Center))
}
"Are they full screen by default like in XML?"
No, they are "wrap_content" by default.
"how do I position elements like ConstraintLayout? How do I set padding and margin from only one side and how do I link elements?"
You need to declare the references to the components and then positioning them accordingly.
Here is a simple example...
ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) {
// Creating refs...
val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs()
Text("Name",
// Linking the reference to this component
modifier = Modifier.constrainAs(text1Ref) {
// linking the top of this component to the parent top
top.linkTo(parent.top)
centerHorizontallyTo(parent)
})
TextField(
value = "",
onValueChange = {},
label = { Text("Name") },
modifier = Modifier.padding(top = 8.dp)
.constrainAs(edit1Ref) {
start.linkTo(parent.start)
end.linkTo(parent.end)
// linking this component with the previous component
top.linkTo(text1Ref.bottom)
})
Button(onClick = {},
content = { Text("OK") },
modifier = Modifier.padding(top = 8.dp).constrainAs(btn1Ref) {
end.linkTo(edit1Ref.end)
top.linkTo(edit1Ref.bottom)
}
)
TextButton(onClick = {},
content = { Text("Cancel") },
modifier = Modifier.padding(end = 8.dp)
.constrainAs(btn2Ref) {
end.linkTo(btn1Ref.start)
baseline.linkTo(btn1Ref.baseline)
}
)
}
I'm still getting the hang of Android's Jetpack Compose declarative UI library and would appreciate some help. The column (containing an image "icon" and text) isn't rendering at all on smaller screens. Here is the relevant code -->
#Composable
fun ComposableExample() {
...
ScrollableColumn(modifier = Modifier.fillMaxWidth()) {
// Scrollable column should have one child.
Column {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Text(text = "This is the Title of the Video")
//--------- This column is rendering on wider (tablet+) screens but not phone sized screens.
Column(horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()) {
Image(imageResource(id = R.drawable.icon_download_arrow),
modifier = Modifier.preferredSize(20.dp)
.clickable(onClick = { }))
Text(text = "Download")
}
// ---------
}
...
}
}
}
I've tried a ton of different modifiers/permutations and can't get the column to render on smaller screens. I'd really appreciate some help, thanks!
Simply swapping the column and the text is working, i.e. rendering the the clickable column of icon image and text BEFORE the video title. Not entirely sure why this is working, but it is!