I am trying to make a short content center-aligned on the screen inside a Scaffold content. Using fillMaxSize() on the scaffold contents seems like having no impact at all. How can I make this content full size?
Scaffold(
topBar = { },
floatingActionButtonPosition = FabPosition.End,
floatingActionButton = { },
bottomBar = { }
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(paddingValues)
) {
NavHost(
navController = navController,
startDestination = Screen.Running.route
) {
composable(Screen.Business.route) {
BusinessScreen(onSetAppTitle = { appTitle = it })
}
...
}
#Composable
fun BusinessScreen(onSetAppTitle: (String) -> Unit) {
LaunchedEffect(Unit) {
onSetAppTitle("Business Dashboard")
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxSize()
.background(Color.White)
.padding(16.dp)
) {
Text("Business Dashboard")
}
}
verticalScroll wraps content, and can be stretched to very long. That's why .fillMaxHeight (which is a part of fillMaxSize) doesn't work inside verticalScroll: it's ambiguous.
You need to set height explicitly. This is the case when you can pass it from onSizeChanged modifier added on the top Column.
But I believe that your composition is generally not really good: applying verticalScroll to a view on top of the layout tree is not the best solution.
Try adding verticalScroll inside each route only to those views that are really bigger than the screen.
Related
Is there a way to set height of elements in a Scaffold to be exactly the same? I want both the top bar and the content to occupy half of the screen's height
Scaffold(
topBar = {
MyLargeTopAppBar(modifier = Modifier.height(1f))
},
content = {
LazyColumn(modifier = Modifier.padding(it).height(1f)) {
...
}
},
containerColor = MaterialTheme.colorScheme.background
)
Arthur Kasparian's suggestion
You can skip using a scaffold if none of its other elements are needed, the layout then simply becomes a column like so:
Column(
modifier = Modifier.fillMaxSize()
) {
TopAppBar(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {}
Content(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
)
}
The height of the content body is determined in part by the topBar body height, and the weight setter is inaccessible in those scopes, so I don't think it's possible. However what you can do is simply include the top bar in the content:
val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp
val size = screenHeight / 2
Scaffold(
content = {
Column {
TopAppBar(
modifier = Modifier
.height(size)
.weight(1f)
.fillMaxWidth()
) {
Text("$size")
}
Box(
modifier = Modifier
.height(size)
.weight(1f)
.fillMaxWidth()
.background(Color.Red)
) {
Text("$size")
}
}
},
)
You get something like this:
How to add a lazy column inside another lazy column in jetpack compose like below screenshot.(The inner list size maybe vary)
#Composable
fun SingleItem() {
LazyColumn(modifier = Modifier
.fillMaxWidth()
),
) {
item {
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.ic_school),
contentScale = ContentScale.Crop,
contentDescription = null
)
Text(
text = "School",
modifier = Modifier
.fillMaxWidth()
.weight(1f)
)
Icon(
painter = painterResource(id = R.drawable.ic_down_arrow),
modifier = Modifier
.size(dimensionResource(id = R.dimen.margin_large))
.clip(CircleShape)
.clickable { isExpand = !isExpand },
contentDescription = stringResource(id = R.string.expand_list)
)
}
}
if (isExpand) {
item {
Divider(
modifier = Modifier
.padding(vertical = dimensionResource(id = R.dimen.margin_large))
.background(DividerColor.copy(alpha = 0.5F))
.fillMaxWidth(), thickness = 0.5.dp
)
}
items(3) {
Row(modifier = Modifier
.padding(horizontal = dimensionResource(id = R.dimen.margin_large))
.fillMaxWidth()) {
Text("School",
modifier = Modifier
.fillMaxWidth()
.weight(1f),
)
Text(text = "3 KM",
textAlign = TextAlign.End
)
}
}
}
}
}
//For Full list
#Composable
fun MainList()
{
LazyColumn() {
items(10) {
SingleItem()
}
}
}
But using this code it shows following error.
Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed. One of the common reasons is nesting layouts like LazyColumn and Column(Modifier.verticalScroll()). If you want to add a header before the list of items please add a header as a separate item() before the main items() inside the LazyColumn scope. There are could be other reasons for this to happen: your ComposeView was added into a LinearLayout with some weight, you applied Modifier.wrapContentSize(unbounded = true) or wrote a custom layout. Please try to remove the source of infinite constraints in the hierarchy above the scrolling container.
It is recommended to avoid Nested Scrolling in the same direction. One possible hack if you already know the size of the component you can use that in modifier.
or try wrapping all of your composables inside one parent LazyColumn and using its DSL to pass in different types of content.
In your example
#Composable
fun MainList()
{
LazyColumn() {
items(10) { item ->
ListComposable(item)
}
}
}
#Composable
private fun ListComposable(item: List<String>){
//...
}
This item can also be a multiple list item.
Reference: https://developer.android.com/jetpack/compose/lists#avoid-nesting-scrollable
In version 1.2.0 of Jetpack Compose a padding parameter is being asked for by the Scaffold, content section.
How do I declare padding values in this section, when they have already been specified in my custom LazyColumn? All I get is paddp.
Content padding parameter it is not used
Scaffold(
topBar = { MyLargeTopAppBar(title = "Hello Android") },
content = { MyLazyColumn(lazyItems = arrayOf(...)) }
)
#Composable
fun MyLazyColumn(lazyItems: Array<Items>
) {
LazyColumn(
contentPadding = PaddingValues(top = 8.dp, bottom = 8.dp)
) {
}
}
There's no need to pass Scaffold content padding into LazyColumn.contentPadding, because this padding gonna be covered by the Scaffold bar anyway, and you don't need your cells to be drawn under the bar. So same as with any other view, you can use Modifier.padding:
Scaffold(
topBar = { MyLargeTopAppBar(title = "Hello Android") },
content = { MyLazyColumn(lazyItems = arrayOf(...), modifier = Modifier.padding(it)) }
)
#Composable
fun MyLazyColumn(lazyItems: Array<Items>, modifier: Modifier) {
LazyColumn(
contentPadding = PaddingValues(top = 8.dp, bottom = 8.dp),
modifier = modifier
) {
}
}
You can always pass modifier in composable constructor from where you call your composable.
And also add what ever you want above that modifier you passed in the constructor.
to fix this you can change your Composable like this
#Composable
fun MyLazyColumn(
modifier: Modifier,
lazyItems: Array<Items>
) {
LazyColumn(
modifier = modifier,
contentPadding = PaddingValues(top = 8.dp, bottom = 8.dp)
) {
}
}
Then you can call your composable in Scaffold and pass that padding to your Composable constructor
Scaffold(
topBar = { MyLargeTopAppBar(title = "Hello Android") },
content = {
MyLazyColumn(
Modifier.padding(it),
lazyItems = arrayOf(...)
)
}
)
I am looking to have a result like this. Two block with 2/3 size and 1/3 size respectively.
I am getting the expected result with this code.
#Preview(showBackground = true)
#Composable
fun LayoutCheck() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Surface(
modifier = Modifier
.width(200.dp)
.weight(3f),
color = MaterialTheme.colors.primary
) {}
Surface(
modifier = Modifier
.width(200.dp)
.weight(1f),
color = MaterialTheme.colors.secondary
) {}
}
}
But when i put that inside a lazycolumn, nothing seems working. Not even getting a display.
#Preview(showBackground = true)
#Composable
fun LayoutCheck() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(20.dp)
) {
item {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Surface(
modifier = Modifier
.width(200.dp)
.weight(3f),
color = MaterialTheme.colors.primary
) {}
Surface(
modifier = Modifier
.width(200.dp)
.weight(1f),
color = MaterialTheme.colors.secondary
) {}
}
}
}
}
}
If i remove t he weight and put some height,it works. But i don't want to hardcode there. So how to make it work with weight. Expecting some help..
Thanks
NB: I want scroll functionality, that's why going with LazyColumn
Real World scenario :
A Login Screen, with Logo at the first 2/3 portion and a Text and Button at the bottom 1/3 portion
Scenario 1 : Working Perfectly:
Scenario 2: If users font is bigger or screen is rotated, they wont be able to see the button
Since you have only one item just use a Column with a verticalScroll:
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally,
) {
//...
}
Also if you want 2/3 and 1/3, use in the 1st Surface the weight(2f) modififer.
You prevent the text from being resized by applying the weight modifier. Instead, I suggest that you make the element spacing flexible, like this:
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.weight(1f))
Image(
painter = painterResource(id = R.drawable.my_image),
contentDescription = "",
)
Spacer(Modifier.weight(1.35f))
Text(
"Your app is being reviewed",
)
Spacer(Modifier.weight(0.2f))
Button(onClick = { /*TODO*/ }) {
Text("Log out")
}
Spacer(Modifier.weight(0.2f))
}
Spacers explanation:
I have a 'Text' and another composable within a Column. I want to put the Text to the top and the layout defined by the composable to the bottom. I am not able to find a way to provide different alignments to the 'elements' within Column, as the alignment/arrangement specified in the 'Modifier' of Column works for all the 'elements' within it. My current code aligns both to the bottom. This is my code:
#Composable
fun SignUpDetails(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "First Screen\n" +
"Click me to go to Second Screen",
color = Color.Green,
style = TextStyle(textAlign = TextAlign.Center),
modifier = Modifier
.padding(24.dp)
.clickable(onClick = {
// this will navigate to second screen
navController.navigate("second_screen")
})
)
ProgressBar1UI()
}
}
How do I achieve my requirement?
You can use the verticalArrangement = Arrangement.SpaceBetween:
Place children such that they are spaced evenly across the main axis, without free space before the first child or after the last child
Something like:
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
Text( /*...*/ )
ProgressBar1UI()
}
Otherwise you can use something different like a Box and the Modifier.align:
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier
.align(Alignment.TopCenter)
)
ProgressBar1UI(Modifier.align(Alignment.BottomCenter))
}
The other way you can achieve such behavior (one view top second view bottom) is to add the Spacer component with a weight modifier between items.
#Composable
fun Example() {
Column {
Text("FirstText")
Spacer(modifier = Modifier.weight(1f))
Text("SecondText")
}
}