I'm using a column to show a list, the items in the list have the attribute isWeekTopic which is a Boolean, in the list I need to show first those elements that have isWeekTopic = true, and then show those ones that have isWeekTopic = false, the items that has isWeekTopic = true are going to change every week.
I managed to print the ones that has isWeekTopic = true with a header on the design, but I can't make them the first element to show this is my code
#Composable
fun TopicsItemComposable(
modifier: Modifier,
topicsItem: TopicsItemResponseModel,
onItemClicked: (TopicsItemResponseModel) -> Unit
) {
var cardSelected by remember { mutableStateOf(false) }
Card(
modifier = modifier
.clickable {
onItemClicked(topicsItem)
cardSelected = !cardSelected
}
.fillMaxWidth()
.wrapContentHeight(),
shape = RoundedCornerShape(12.dp),
backgroundColor = CYAN
) {
Column(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min),
) {
Column {
if (topicsItem.isWeekTopic){
TopicsItem()
TopicBody(topicsItem)
Row(modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min),) {
TopicIcons(topicsItem)
TopicImage()
}
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min),
) {
if (!topicsItem.isWeekTopic){
TopicBody(topicsItem)
Row(modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min),) {
TopicIcons(topicsItem)
TopicImage()
}
}
}
}
}
}
Related
#Composable
fun LoginForm() {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
// Store the state of the bottom sheet
var bottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
Column(
modifier = Modifier.fillMaxSize(),
) {
Box(modifier = Modifier.weight(1f)) {
// The weight modifier is added to expand the height of the Box to fill the remaining height of the Column
// This is necessary to push the buttons to the bottom of the screen
}
Box( modifier = Modifier
.fillMaxSize()
.align(Alignment.BottomEnd)
)
{
Button(
onClick = {
coroutineScope.launch { bottomSheetState.show() }
},
modifier = Modifier.padding(16.dp)
) {
Text("Login")
}
}
// Define the bottom sheet content
ModalBottomSheetLayout(
sheetContent = {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
OutlinedTextField(
value = username,
onValueChange = { username = it },
label = { Text("Username") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = { coroutineScope.launch { bottomSheetState.hide() } },
modifier = Modifier.align(Alignment.End)
) {
Text("Login")
}
}
},
sheetState = bottomSheetState,
sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
) {
// This composable is the "background" that will be visible when the bottom sheet is open
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.BottomCenter
) {
// Add any content you want to show in the background here
}
}
}
}
This is for a login screen for an app.
This is the error I have been getting : "Type mismatch: inferred type is Alignment but Alignment.Horizontal was expected"
I have been changing the Alignment.BottomEnd in the box layout for the Login button but nothing works.
I wanted to set the button to the bottom left end of the screen.
In the ColumnScope the align modifier requires a Alignment.Horizontal parameter.
In you case just use:
Box(
modifier = Modifier
.fillMaxWidth()
.background(Yellow), //it is not neeeded
contentAlignment = Alignment.BottomStart,
)
Otherwise you can avoid the 1st Box using:
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Bottom
) {
Box( modifier = Modifier
.fillMaxWidth()
)
}
Hi I am using Jetpack Compose to create a Heterogenous list. I was successful in implementing it. My requirement is when I try to click an Item in the list, I need to recompose the list. The app crashes when tried to refresh the list with below exception:
java.lang.IllegalStateException: 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
My code is below:
#Composable fun PrepareOverViewScreen(overViewList: List<OverViewListItem>) {
Scaffold(topBar = { TopBar("OverView") },
content = { DisplayOverViewScreen(overViewList = overViewList) },
backgroundColor = Color(0xFFf2f2f2)
)
}
#Composable fun DisplayOverViewScreen(
modifier: Modifier = Modifier, overViewList: List<OverViewListItem>
) {
LazyColumn(modifier = modifier) {
items(overViewList) { data ->
when (data) {
is OverViewHeaderItem -> {
HeaderItem(data,overViewList)
}
}
}
}
}
HeaderItem Composable Function is below :
#Composable fun HeaderItem(overViewHeaderItem: OverViewHeaderItem,overViewList: List<OverViewListItem>) { <br>
var angle by remember {
mutableStateOf(0f)
}
var canDisplayChild by remember {
mutableStateOf(false)
}
**if(canDisplayChild){
HandleHistoryTodayChild(canDisplayChild = true,overViewList)
}**
when (overViewHeaderItem.listType) {
ItemType.IN_PROGRESS_HEADER -> {
Column(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.background(color = Color(0xffdc8633)),
verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.Start
) {
Text(
text = "In Progress", color = Color.White,
modifier = Modifier.padding(start = 16.dp)
)
}
}
ItemType.HISTORY_TODAY_HEADER -> {
Column(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.background(color = Color(0xffd7d7d7)),
verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.Start
) {
Text(
text = stringResource(R.string.history_label), color = Color.Black,
modifier = Modifier.padding(start = 16.dp)
)
}
}
else -> {
Row(modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.background(color = Color(0xffd7d7d7)),
horizontalArrangement = Arrangement.Start, verticalAlignment = Alignment.CenterVertically) {
Text(
text = stringResource(R.string.history_yesterday), color = Color.Black,
modifier = Modifier.padding(start = 16.dp)
)
Spacer(Modifier.weight(1f))
**Image(
painter = painterResource(id = R.drawable.ic_expand_more),
modifier = Modifier
.padding(end = 5.dp)
.rotate(angle)
.clickable {
angle = (angle + 180) % 360f
canDisplayChild = !canDisplayChild
},
contentDescription = "Expandable Image"
)**
}
}
}
}
Handle History info where recomposition is called
#Composable
fun HandleHistoryTodayChild(canDisplayChild:Boolean,overViewList: List<OverViewListItem>) {
if(canDisplayChild){
**PrepareOverViewScreen(overViewList = overViewList)**
}
}
Your problem should be:
Spacer(Modifier.weight(1f))
Try to specify a fixed height and work your solution from there.
Is it possible to create full screen, clickable, transparent surface/box, that will overlap compose buttons and other composables. I want to keep buttons visible but unreachable for the user.
When I set them(buttons) as disabled, they turn to an unclickable area( on top of the clickable surface) that I can't have on the screen. Other composables, like Text,Box etc. act like they are "under" clickable surface.
#Composable
fun ShowAnswers(question: Question, onSurfaceClick: () -> Unit, questionsLeft: Int) {
Surface(modifier = Modifier.clickable { onSurfaceClick() }) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(8.dp)
) {
Text(
text = stringResource(id = R.string.questions_left, questionsLeft),
fontSize = 24.sp
)
Spacer(modifier = Modifier.height(20.dp))
QuestionCard(question = question)
Spacer(modifier = Modifier.height(20.dp))
ShowCorrectAnswer(question = question)
}
}
}
ShowCorrectAnswer(...) contains buttons that i need to "overlap"
You can use a simple transparent Box to overlay the Surface with the question.
Something like:
var isActive by remember { mutableStateOf(false) }
Box(modifier = Modifier.fillMaxSize()) {
Surface() {
Column() {
//....
QuestionCard(question = question)
//....
Button(onClick = { isActive = true }) {
//...
}
}
}
if (isActive){
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Transparent)
.clickable(
enabled=isActive,
interactionSource = interactionSource,
indication = null)
{
//do something
}
)
}
}
Adding a fillMaxSize Box inside the surface, after column, solved the problem.
But placing the Box before Column makie it appear "under" it.
#Composable
fun ShowAnswers(question: Question, onSurfaceClick: () -> Unit, questionsLeft: Int) {
Surface {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(8.dp)
) {
Text(
text = stringResource(id = R.string.questions_left, questionsLeft),
fontSize = 24.sp
)
Spacer(modifier = Modifier.height(20.dp))
QuestionCard(question = question)
Spacer(modifier = Modifier.height(20.dp))
ShowCorrectAnswer(question = question)
}
**Box(modifier = Modifier
.fillMaxSize()
.background(Color.Transparent)
.clickable { onSurfaceClick() })**
}
}
I'm using a HorizontalPager from accompanist package. The pager will have 1 page for each exercise stored by the user.
Each page in the pager has a LazyColumn which contain multiple cards with controls such as IconButtons and BasicTextField. The number of cards depends on number of sets configured by the user. I expect the typical number to be between 1 to 8 but only 3 to 5 would be visible on the screen at any given time (depending on the screen size and resolution).
The issue is that when this layout produces noticeable lag (animations skip frames) every time the HorizontalPager needs to build a new page that has more than 3 cards. This happens when swapping between pages. The same happens in debug and release versions running on a real device (Galaxy S10e) and emulator.
I'm trying to optimise this layout, so each frame renders in no more than 16ms regardless of the number of cards shown on the screen.
I've previously tried to solve this issue by setting fixed heights to some composables but that didn't help much. I've also tried using Text instead of BasicTextField, which would be then replaced with BasicTextField when users taps on the text but this hasn't helped much, therefore I removed this implementation.
Do you have some suggestions how performance of this layout could be improved to eliminate the lag?
Below is my code, screen shoot of the app screen and profiler:
#ExperimentalPagerApi
#Composable
fun WorkoutSessionScreen(
navHostController: NavHostController,
) {
val pagerState = rememberPagerState()
Scaffold(
topBar = { MyTopAppBar(navHostController = navHostController) }
) {
Box(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.background(MaterialTheme.colors.background)
.imePadding()
) {
HorizontalPager(
count = 10, state =
pagerState,
itemSpacing = 16.dp
) {
Log.e("==>", "Building horizontal pager")
TrackingControls()
}
Box(
modifier = Modifier
.height(70.dp)
.fillMaxWidth()
.background(Color(0xFAF7F7FF))
.align(Alignment.BottomCenter)
) {
Column(
Modifier.fillMaxWidth()
) {
Divider(color = Color(0x2A5C5C5C))
BottomControls()
}
}
}
}
}
#Composable
private fun TrackingControls() {
LazyColumn(
Modifier
.fillMaxHeight()
.fillMaxWidth(),
contentPadding = PaddingValues(vertical = 8.dp)
) {
items(6) { item ->
SetsAndRepsTrackingControls(
modifier = Modifier
)
}
}
}
#Composable
private fun BottomControls() {
Text(text = "Bottom Controlls")
}
#Composable
fun SetsAndRepsTrackingControls(modifier: Modifier = Modifier) {
val add = painterResource(id = R.drawable.ic_round_add_24)
val remove = painterResource(id = R.drawable.ic_round_remove_24)
Card(
modifier
.fillMaxWidth()
.height(200.dp)
.padding(vertical = 16.dp, horizontal = 8.dp),
backgroundColor = MaterialTheme.colors.surface,
shape = RoundedCornerShape(12.dp),
) {
Column() {
ControlsHeader()
TrackingInput(label = "REPS", add, remove)
Divider(color = Color.LightGray)
TrackingInput(label = "WEIGHT (KG)", add, remove)
}
}
}
#Composable
private fun ControlsHeader() {
Row(
modifier = Modifier
.height(56.dp)
.fillMaxWidth()
.background(MaterialTheme.colors.primary, RoundedCornerShape(12.dp))
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(text = "Set 1")
Text(text = "CheckBox")
}
}
#Composable
private fun TrackingInput(label: String = "Preview", add: Painter, remove: Painter) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(60.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = {}) {
Icon(
painter = painterResource(id = R.drawable.ic_round_remove_24),
contentDescription = "Minus",
tint = MaterialTheme.colors.onSurface
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
BasicTextField(
singleLine = true,
value = "8",
onValueChange = {},
textStyle = TextStyle(
textAlign = TextAlign.Center,
color = MaterialTheme.colors.onSurface
),
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = label,
style = MaterialTheme.typography.overline.copy(color = MaterialTheme.colors.onSurface)
)
}
IconButton(onClick = { Log.d("==>", "tada") }) {
Icon(
painter = painterResource(id = R.drawable.ic_round_add_24),
contentDescription = "Minus",
tint = MaterialTheme.colors.onSurface
)
}
}
}
This profiler print screen shows rendering of a single card called SetsAndRepsTrackingControls
I have a Dropdown Composable View that can use as a selection.
#Composable
fun <T> DropdownDemo(modifier: Modifier, items: List<T>, selected: MutableState<T>) {
var expanded by remember { mutableStateOf(false) }
Box(modifier = modifier.fillMaxWidth()) {
Text(
selected.value.toString(),
modifier = Modifier
.background(Color.LightGray)
.fillMaxWidth()
.clickable(
onClick = { expanded = true })
.padding(16.dp, 0.dp)
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.fillMaxWidth(),
) {
items.forEachIndexed { index, select ->
DropdownMenuItem(
onClick = {
selected.value = items[index]
expanded = false
},
modifier = Modifier.fillMaxWidth(),
) {
Text(text = select.toString(), modifier = Modifier.fillMaxWidth())
}
}
}
}
}
When no click on, the Text shown is fill up the entire width. However, when clicked and expanded, the pop up cannot be expanded fully, even though I set Modifier.fillMaxWidth() in the DropdownMenu and DropdownMenuItem.
How can I get the pop up of the DropdownMenu wider?
You can copy DropdownMenuItemContent and modify it to allow passing in the min and max width
#Composable
fun AdjustableWidthDropdownMenuItem(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
minWidth: Dp = 112.dp,
maxWidth: Dp = 280.dp,
content: #Composable RowScope.() -> Unit,
) {
Row(
modifier = modifier
.clickable(
enabled = enabled,
onClick = onClick,
interactionSource = interactionSource,
indication = rememberRipple(true)
)
.fillMaxWidth()
.sizeIn(
minWidth = minWidth,
maxWidth = maxWidth,
minHeight = 48.dp
)
.padding(contentPadding),
verticalAlignment = Alignment.CenterVertically
) {
val typography = MaterialTheme.typography
ProvideTextStyle(typography.subtitle1) {
val contentAlpha = if (enabled) ContentAlpha.high else ContentAlpha.disabled
CompositionLocalProvider(LocalContentAlpha provides contentAlpha) {
content()
}
}
}
}