im still learning compose.in this prototype im buiding,my Column containing the card views,in this case 1 dummy card,is overlapping with the appbar. i have tried using scaffold too,same result
here is the Card code:
#Composable
fun DiaryCard(){
val bs = "filler text,strings,anything "+
"jadsjjadj adsnjasjd d saasd" +" sadsad asdasd adsasda" +
"sasdasdas dsa d"
Column {
Spacer(modifier = Modifier.padding(top = 6.dp))
Card(modifier = Modifier
.fillMaxWidth()
.padding(13.dp),
shape = MaterialTheme.shapes.small,
elevation = 5.dp, backgroundColor = Color.White){
Row(modifier = Modifier.padding(bottom = 2.dp)){
Text(text = "29 Sept. 2019", modifier = Modifier
.fillMaxWidth(0.75F)
.padding(start = 1.5.dp),color = Color.Black)
}
Divider()
Spacer(modifier = Modifier.padding(bottom = 3.dp,top = 2.dp))
Column(modifier = Modifier.fillMaxWidth()) {
Text(text = "today was a good day",color = Color.Black)
Spacer(modifier = Modifier.padding(bottom = 3.dp))
Text(text = bs, color = Color.Black)
}
}
}
}
The Appbar:
#Composable
fun topAppbar(){
TopAppBar(backgroundColor = Color.DarkGray) {
}
}
You can use innerPadding,
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
...
)
},
navigationIcon = {
IconButton(onClick = { }) {
Icon(imageVector = Icons.Default.Menu, contentDescription = "Menu Icon" )
}
},
backgroundColor = colorResource(id = R.color.purple_200),
contentColor = Color.White,
elevation = 12.dp
)
},
){
innerPadding ->
Box(modifier = Modifier.padding(innerPadding)){
DiaryCard()
}
}
You can:
Add a Scaffold with topBar = { topAppbar() }
Add a Column() to wrap all the content of the Card
Something like:
Scaffold(topBar = { topAppbar() },
) {
DiaryCard()
}
with:
#Composable
fun DiaryCard(){
val bs = "filler text,strings,anything "+
"jadsjjadj adsnjasjd d saasd" +" sadsad asdasd adsasda" +
"sasdasdas dsa d"
Column {
Spacer(modifier = Modifier.padding(top = 6.dp))
Card(
modifier = Modifier
.fillMaxWidth()
//.background(Color.Red)
.padding(13.dp),
shape = MaterialTheme.shapes.small,
elevation = 5.dp, backgroundColor = Color.White
) {
Column() { //It is important to avoid overlap inside the Card
Row(modifier = Modifier.padding(bottom = 2.dp)) {
Text(
text = "29 Sept. 2019", modifier = Modifier
.fillMaxWidth(0.75F)
.padding(start = 1.5.dp), color = Color.Black
)
}
Divider()
Spacer(modifier = Modifier.padding(bottom = 3.dp, top = 2.dp))
Column(modifier = Modifier.fillMaxWidth()) {
Text(text = "today was a good day", color = Color.Black)
Spacer(modifier = Modifier.padding(bottom = 3.dp))
Text(text = bs, color = Color.Black)
}
}
}
}
}
and:
#Composable
fun topAppbar(){
TopAppBar(backgroundColor = Color.DarkGray) {
Text("AppBar")
}
}
Related
I use ModalBottomSheetLayout and I want the back part to darken when opening the lower curtain and it was not clickable. How to do it?
If you set screen Color = Color.Unspecified, the background will not be clickable, but at the same time it will be colorless. It doesn't suit me.
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun ForpostScreen(forpostViewModel: ForpostViewModel = koinViewModel()) {
val scope = rememberCoroutineScope()
val bottomSheetOpen = remember{ mutableStateOf(false) }
val viewState = forpostViewModel.forpostViewState.observeAsState()
val bottomSheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden
)
ModalBottomSheetLayout(
sheetState = bottomSheetState,
sheetContent = {
Box(modifier = Modifier
.fillMaxWidth()
.height(500.dp)) {
Image(
painter = painterResource(id = R.drawable.botton_sheet_top_line),
contentDescription = "Линия",
modifier = Modifier
.height(20.dp)
.width(70.dp)
.padding(top = 10.dp)
.align(Alignment.TopCenter)
)
Text(
text = stringResource(id = R.string.all_cams_button),
color = colorResource(id = R.color.title_text_color),
fontWeight = FontWeight(600),
fontSize = 25.sp,
modifier = Modifier
.padding(top = 30.dp)
.align(Alignment.TopCenter)
)
}
},
sheetShape = RoundedCornerShape(topStart = 25.dp, topEnd = 25.dp),
sheetElevation = 12.dp,
) {
Surface(color = Color.White) {
Column {
Button(
onClick = {
bottomSheetOpen.value = true
scope.launch {
bottomSheetState.show()
}
},
colors = ButtonDefaults.buttonColors(colorResource(id = R.color.button_color)),
shape = RoundedCornerShape(15.dp),
modifier = Modifier
.padding(top = 20.dp, start = 20.dp)
.height(30.dp)
.width(130.dp),
contentPadding = PaddingValues(bottom = 0.dp),
content = {
Text(
text = stringResource(id = R.string.all_cams_button),
color = Color.White,
fontWeight = FontWeight(400)
)
}
)
Text(
modifier = Modifier.padding(top = 50.dp, start = 20.dp),
text = "Репина, 1 Б",
color = colorResource(id = R.color.title_text_color),
fontWeight = FontWeight(700),
fontSize = 18.sp,
letterSpacing= 1.sp
)
ButtonsLazyColumn(
listItem = listOf("Двор", "Подъезд", "Парковка", "Въезд", "Пост охраны"),
height = 50,
modifierPaddingStart = 20,
modifierPaddingTop = 15,
modifierPaddingEnd = 10,
)
Surface(modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp)
.height(220.dp)
.background(Color.Black)) {
when (viewState.value) {
is ForpostViewState.Loading -> { VideoLoadingView() }
is ForpostViewState.Load -> { PlayerView(forpostViewModel = forpostViewModel) }
else -> {}
}
}
Divider(
modifier = Modifier.padding(top = 100.dp),
color = colorResource(id = R.color.sevstar_gray_light),
thickness = 1.dp
)
ButtonsLazyColumn(
listItem = listOf("1 сек", "1 мин", "5 мин", "10 мин", "30 мин"),
height = 50,
modifierPaddingStart = 20,
modifierPaddingTop = 15,
modifierPaddingEnd = 10
)
Divider(
modifier = Modifier.padding(top = 20.dp),
color = colorResource(id = R.color.sevstar_purple),
thickness = 1.dp
)
}
}
Box(
modifier = Modifier
.fillMaxSize()
.background(
color = if (bottomSheetOpen.value) Color.Black.copy(alpha = 0.5f)
else Color.Transparent
))
}
if (bottomSheetState.currentValue != ModalBottomSheetValue.Hidden
&& bottomSheetState.offset.value > 1800) bottomSheetOpen.value = false
LaunchedEffect(key1 = viewState, block = {
forpostViewModel.obtainEvent(event = ForpostEvents.EnterScreen)
})
}
I made the back darkening myself and it works, but in this case the back part becomes clickable.
You can use something like
val bottomSheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden,
confirmStateChange = {false}
)
and you have to manually close the bottom sheet like,
onClick = { scope.launch { bottomSheetState.hide() } }
Based on what was answered in this question (Open ModalSheetLayout on TextField focus instead of Keyboard) and the exchange of comments I did with #Abhimanyu, I was able to get the ModalBottomSheetLayout to appear when I click on one of the TextFields, however I encountered two more problems. I can't center what's in the content, nor can I center the content that's inside the sheet content. Can anyone help me understand why?
Here is a print of what is happening and my code:
#ExperimentalMaterialApi
#Preview
#Composable
fun ProfileScreen() {
var profileModalBottomSheetType by remember { mutableStateOf(ProfileModalBottomSheetType.NONE) }
val modalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
if (modalBottomSheetState.currentValue != ModalBottomSheetValue.Hidden) {
DisposableEffect(Unit) {
onDispose {
profileModalBottomSheetType = ProfileModalBottomSheetType.NONE
}
}
}
ModalBottomSheetLayout(
sheetState = modalBottomSheetState,
sheetShape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp),
sheetContent = {
Box(
modifier = Modifier
.padding(top = 10.dp)
.height(10.dp)
.width(100.dp)
.background(
color = Color.LightGray,
shape = RoundedCornerShape(4.dp)
)
)
when (profileModalBottomSheetType) {
ProfileModalBottomSheetType.SELECT_RATE -> {
SelectRateModalBottomSheet(listOf("Exact Rate", "Range"))
}
else -> {}
}
}
) {
LazyColumn(
modifier = Modifier
.width(320.dp)
) {
item {
HeightSpacer(40.dp)
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(id = R.drawable.ic_clearjobs_logo_2x),
contentDescription = null
)
}
HeightSpacer(47.dp)
Column(
modifier = Modifier
.width(320.dp)
.padding(start = 20.dp, end = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Profile Light Title",
style = TextStyle32Light,
textAlign = TextAlign.Center
)
Text(
text = "Profile Bold Title",
style = TextStyle32Bold,
textAlign = TextAlign.Center
)
}
HeightSpacer(47.dp)
Column(
modifier = Modifier
.background(
shape = RoundedCornerShape(
topStart = 13.dp,
topEnd = 13.dp
), color = Color.White
)
.padding(bottom = 140.dp)
.width(320.dp)
) {
Text(
text = stringResource(id = R.string.your_profile),
style = TextStyle28Bold,
modifier = Modifier.padding(
top = 40.dp,
start = 20.dp,
bottom = 30.dp
)
)
Text(
text = stringResource(id = R.string.salary_range),
style = TextStyle28Bold,
modifier = Modifier.padding(
top = 40.dp,
start = 20.dp,
bottom = 30.dp
)
)
Box {
LightBlueBorderTextField(
title = "Rate",
initialState = "Exact Rate",
textFieldTextStyle = TextStyle16BlackOpacity50Normal,
enabled = false
) { innerTextField ->
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.weight(1f)
.padding(start = Dimen10)
) {
innerTextField()
}
}
}
Box(
modifier = Modifier
.matchParentSize()
.alpha(0f)
.clickable(
onClick = {
profileModalBottomSheetType =
ProfileModalBottomSheetType.SELECT_RATE
toggleModalBottomSheetState(
coroutineScope = coroutineScope,
modalBottomSheetState = modalBottomSheetState,
)
}
)
)
}
}
}
}
}
}
#Composable
fun SelectRateModalBottomSheet(options: List<String>) {
LazyColumn(
modifier = Modifier.padding(
start = 20.dp,
end = 20.dp,
top = 15.dp,
bottom = 15.dp
)
) {
items(options.size) { optionIndex ->
val option = options[optionIndex]
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 10.dp, end = 10.dp, bottom = 15.dp)
) {
Box(Modifier.weight(1f)) {
Text(text = option, style = TextStyle16BlackOpacity50Normal)
}
RadioButton(
selected = false,
onClick = { /*TODO*/ },
modifier = Modifier
.width(20.dp)
.height(20.dp)
)
}
if (optionIndex != options.lastIndex) {
Divider(color = Color.DarkGray, thickness = 1.dp)
HeightSpacer(dimen = 15.dp)
}
}
}
}
#Composable
fun HeightSpacer(dimen: Dp) {
Spacer(modifier = Modifier.height(dimen))
}
#Preview
#Composable
fun LightBlueBorderTextField(
title: String = "",
initialState: String = "",
textFieldTextStyle: TextStyle = TextStyle18Normal,
enabled: Boolean = true,
decorationBox: #Composable (innerTextField: #Composable () -> Unit) -> Unit = { innerTextField ->
Box(
Modifier.padding(start = Dimen10),
contentAlignment = Alignment.CenterStart
) {
innerTextField()
}
}
) {
Column {
val state = remember { mutableStateOf(TextFieldValue(initialState)) }
if (title.isNotEmpty()) {
Text(
text = title,
style = TextStyle16BlackBold,
modifier = Modifier.padding(
top = Dimen40,
start = Dimen30,
bottom = Dimen10
)
)
} else {
HeightSpacer(Dimen40)
}
CustomTextField(
state = state,
modifier = Modifier
.height(Dimen45)
.padding(start = Dimen20, end = Dimen20)
.border(
width = Dimen1,
color = LightBlue,
shape = RoundedCornerShape(Dimen13)
)
.background(Color.White, RoundedCornerShape(Dimen13))
.fillMaxWidth(),
textStyle = textFieldTextStyle,
decorationBox = decorationBox,
enabled = enabled
)
}
}
#Composable
fun CustomTextField(
state: MutableState<TextFieldValue>,
modifier: Modifier,
textStyle: TextStyle = TextStyle18Normal,
decorationBox: #Composable (innerTextField: #Composable () -> Unit) -> Unit,
enabled: Boolean = true
) {
BasicTextField(
modifier = modifier,
value = state.value,
onValueChange = { value -> state.value = value },
singleLine = true,
textStyle = textStyle,
decorationBox = decorationBox,
enabled = enabled
)
}
#ExperimentalMaterialApi
fun toggleModalBottomSheetState(
coroutineScope: CoroutineScope,
modalBottomSheetState: ModalBottomSheetState,
action: (() -> Unit)? = null,
) {
coroutineScope.launch {
if (!modalBottomSheetState.isAnimationRunning) {
if (modalBottomSheetState.isVisible) {
modalBottomSheetState.hide()
} else {
modalBottomSheetState.show()
}
}
action?.invoke()
}
}
internal enum class ProfileModalBottomSheetType {
NONE,
SELECT_JOB_KEYWORDS,
SELECT_WORK_LOCATIONS,
SELECT_TAGS,
SELECT_RATE,
SELECT_SALARY_PERIOD
}
I solved this issue by putting all the ModalBottomSheet content inside a Box with Modifier.fillMaxSize() and the contentAlignment = Alignment.Center. See my code below:
ModalBottomSheetLayout(
sheetState = modalBottomSheetState,
sheetShape = RoundedCornerShape(topStart = Dimen13, topEnd = Dimen13),
sheetContent = {
JobDetailsModalBottomSheet(modalBottomSheetType, jobClicked) {
modalBottomSheetType = JobOpeningsScreenModalBottomSheetType.NONE
toggleModalBottomSheetState(
coroutineScope = coroutineScope,
modalBottomSheetState = modalBottomSheetState,
)
}
}) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
// any content
}
}
I'm not really sure why this doesn't work without Box, but after a few tries, this was the solution I found and it worked.
I want to create a few cards that are separated by dashes in the jetpack compose but I do not know how it is created. Does anyone know how it is created?
Note: These are not one card, there are several cards separated by dashes, and the corners of the dashes are triangular.
I created this with canvas:
Column {
Box(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.background(AppColor.brandColor.BLUE_DE_FRANCE)
) {
Column(
modifier = modifier
.wrapContentSize()
.padding(16.dp)
) {
Card(
modifier = modifier
.fillMaxWidth(),
shape = CutCornerShape(4.dp)
) {
Column(modifier = modifier.wrapContentSize()) {
Text(text = "text")
Text(text = "text")
Text(text = "text")
Text(text = "text")
}
}
DrawDashLine()
Card(
modifier = modifier
.fillMaxWidth(),
shape = CutCornerShape(4.dp)
) {
Column(modifier = modifier.wrapContentSize()) {
Text(text = "text")
Text(text = "text")
Text(text = "text")
Text(text = "text")
}
}
DrawDashLine()
Card(
modifier = modifier
.fillMaxWidth(),
shape = CutCornerShape(4.dp)
) {
Column(modifier = modifier.wrapContentSize()) {
Text(text = "text")
Text(text = "text")
Text(text = "text")
Text(text = "text")
}
}
}
}
Spacer(modifier = Modifier.size(32.dp))
DefaultSecondaryButton(
text = stringResource(id = R.string.get_help_now),
textColor = AppColor.brandColor.BLUE_DE_FRANCE,
borderColor = AppColor.brandColor.BLUE_DE_FRANCE,
onClick = {
},
modifier = modifier.fillMaxWidth(),
icon = {
Icon(
modifier = modifier.padding(top = 2.dp),
painter = painterResource(id = R.drawable.ic_chat_support),
contentDescription = "support icon",
tint = Color.Unspecified
)
}
)
}
#Composable
private fun DrawDashLine() {
val pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 10f), 0f)
Canvas(
Modifier.fillMaxWidth()
) {
drawLine(
color = AppColor.brandColor.BLUE_DE_FRANCE,
strokeWidth = 5f,
start = Offset(20f, 0f),
end = Offset(size.width - 20, 0f),
pathEffect = pathEffect
)
}
}
I am trying to scroll down to the position of a specific UI element on clicking a Text.
The code for my Text is:
Text(
"What is autosaving?",
color = colorResource(id = R.color.text_highlight),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
},
fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier.constrainAs(whatIsAutosaving) {
top.linkTo(glWhatIsAutoSaving)
start.linkTo(parent.start)
end.linkTo(parent.end)
},
)
On clicking this Text my screen should scroll to the beginning position of another Text. The code for this another Text is:
Text(
stringResource(id = R.string.autosave_info),
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier.constrainAs(autoSaveInfo) {
top.linkTo(glAutoSaveInfo)
start.linkTo(glLeft)
end.linkTo(glRight)
width = Dimension.fillToConstraints
},
)
How do I achieve this?
EDIT:
The complete code for my screen is:
#Composable
fun Autosave(navController: NavController) {
val query = remember { mutableStateOf("") }
val errorMsg = remember { mutableStateOf(false) }
Box(
modifier = Modifier
.background(color = MaterialTheme.colors.background)
.fillMaxSize()
) {
ConstraintLayout(
modifier = Modifier.verticalScroll(rememberScrollState())
) {
val (logo, illustration, title, triangle, slider, percent, maxLimitTxt,
maxLimitTextField, buttonSave, whatIsAutosaving, autoSaveInfo, progressBar,
detailsRow, iconUp, spacer, error) = createRefs()
val glLogo = createGuidelineFromTop(0.0075f)
val glIllustrationTop = createGuidelineFromTop(0.0235f)
val glIllustrationBottom = createGuidelineFromTop(0.045f)
val glIllustrationLeft = createGuidelineFromStart(0.27f)
val glIllustrationRight = createGuidelineFromEnd(0.27f)
val glTitle = createGuidelineFromTop(0.053f)
val glSlider = createGuidelineFromTop(0.062f)
val glMaxLimitTxt = createGuidelineFromTop(0.086f)
val glMaxLimitTextField = createGuidelineFromTop(0.09f)
val glButtonSaveTop = createGuidelineFromTop(0.11f)
val glButtonSaveBottom = createGuidelineFromTop(0.12f)
val glWhatIsAutoSaving = createGuidelineFromTop(0.125f)
val glAutoSaveInfo = createGuidelineFromTop(0.175f)
val glSpacer = createGuidelineFromTop(0.99f)
val glLeft = createGuidelineFromStart(0.1f)
val glRight = createGuidelineFromEnd(0.1f)
val glRightIcon = createGuidelineFromEnd(0.825f)
val glLeftTextField = createGuidelineFromStart(0.3f)
val glRightTextField = createGuidelineFromEnd(0.3f)
val coroutineScope = rememberCoroutineScope()
val scrollState = rememberScrollState()
var scrollToPosition by remember { mutableStateOf(0F) }
Image(
painter = painterResource(id = R.drawable.effect_app_bg_720),
contentDescription = "effect top",
modifier = Modifier
.fillMaxSize()
.scale(1.325f)
)
Image(
painter = painterResource(id = R.drawable.logo_voodlee),
contentDescription = "logo", modifier = Modifier
.constrainAs(logo) {
top.linkTo(glLogo)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
Image(
painter = painterResource(id = R.drawable.img_autosaving),
contentDescription = "autosave image",
modifier = Modifier.constrainAs(illustration) {
top.linkTo(glIllustrationTop)
bottom.linkTo(glIllustrationBottom)
start.linkTo(glIllustrationLeft)
end.linkTo(glIllustrationRight)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
}
)
Text(
"Set the percentage for autosaving",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._13ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier.constrainAs(title) {
top.linkTo(glTitle)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
Image(
painter = painterResource(id = R.drawable.ic_triangle_dn),
modifier = Modifier
.height(39.dp)
.width(29.dp)
.constrainAs(triangle) {
top.linkTo(title.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
},
contentDescription = "triangle down"
)
Column(modifier = Modifier.constrainAs(slider) {
top.linkTo(glSlider)
start.linkTo(parent.start)
end.linkTo(parent.end)
}) {
val context = LocalContext.current
val customView = remember { com.shawnlin.numberpicker.NumberPicker(context) }
// Adds view to Compose
AndroidView({ customView }) { view ->
// View's been inflated - add logic here if necessary
with(view) {
orientation = HORIZONTAL
//dividerDrawable = ResourcesCompat.getDrawable(resources, R.drawable.bg_blue, null)
textColor =
ResourcesCompat.getColor(resources, R.color.slider_num_color, null)
selectedTextColor =
ResourcesCompat.getColor(resources, R.color.slider_num_color, null)
selectedTextSize = 120f
wheelItemCount = 6
value = 10
minValue = 0
maxValue = 99
layoutParams.width = MATCH_PARENT
setDividerColorResource(R.color.fade_green)
setDividerDistance(180)
setDividerThickness(10)
}
}
Text(
"%",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._18ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier
.align(CenterHorizontally)
.offset(y = (-5).dp)
)
}
Text(
"Max Limit per autosaving",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._13ssp).toSp()
},
fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier.constrainAs(maxLimitTxt) {
top.linkTo(glMaxLimitTxt)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
TextField(
value = query.value,
onValueChange = { newValue -> query.value = newValue
if (newValue != "")
errorMsg.value = newValue.toInt() > 1500
},
label = {
Text(" Amount",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._15ssp).toSp()
},
textAlign = TextAlign.Center
)
},
textStyle = TextStyle(
textAlign = TextAlign.Center,
color = colorResource(id = R.color.bright_green),
fontFamily = FontFamily(Font(R.font.poppins_regular)),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._15ssp).toSp()
},
),
modifier = Modifier.constrainAs(maxLimitTextField) {
top.linkTo(glMaxLimitTextField)
start.linkTo(glLeftTextField)
end.linkTo(glRightTextField)
width = Dimension.fillToConstraints
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
unfocusedIndicatorColor = colorResource(id = R.color.bright_green),
focusedIndicatorColor = colorResource(id = R.color.bright_green)
)
)
Text(
text =
"*Please enter amount less than Rs.1500",
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._8ssp).toSp()
},
color = colorResource(id = R.color.voodlee_red),
modifier = Modifier
.padding(top = 8.dp)
.alpha(
if (errorMsg.value) {
1f
} else 0f
)
.constrainAs(error) {
top.linkTo(maxLimitTextField.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
},
)
Button(
onClick = {
navController.navigate("fourth_screen")
},
modifier = Modifier.constrainAs(buttonSave) {
top.linkTo(glButtonSaveTop)
bottom.linkTo(glButtonSaveBottom)
start.linkTo(glLeft)
end.linkTo(glRight)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
},
colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(id = R.color.voodlee_red))
) {
Text(
"Save", color = colorResource(id = R.color.dark_blue),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._16ssp).toSp()
},
)
}
Text(
"What is autosaving?",
color = colorResource(id = R.color.text_highlight),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
},
fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier
.constrainAs(whatIsAutosaving) {
top.linkTo(glWhatIsAutoSaving)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
.clickable {
coroutineScope.launch {
scrollState.animateScrollTo(scrollToPosition.roundToInt())
}
},
)
Text(
stringResource(id = R.string.autosave_info),
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
modifier = Modifier
.constrainAs(autoSaveInfo) {
top.linkTo(glAutoSaveInfo)
start.linkTo(glLeft)
end.linkTo(glRight)
width = Dimension.fillToConstraints
}
.onGloballyPositioned { coordinates ->
scrollToPosition = coordinates.positionInParent().y
},
)
Row(
modifier = Modifier
.padding(top = 40.dp, bottom = 50.dp)
.constrainAs(detailsRow) {
top.linkTo(autoSaveInfo.bottom)
start.linkTo(glLeft)
end.linkTo(glRight)
width = Dimension.fillToConstraints
},
) {
Text(
text = "For more details",
color = colorResource(id = R.color.bright_green),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
}, fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
)
Spacer(modifier = Modifier.padding(5.dp))
Text(
text = "Click here",
color = colorResource(id = R.color.text_highlight),
fontSize = with(LocalDensity.current) {
dimensionResource(id = R.dimen._11ssp).toSp()
},
fontFamily = FontFamily(
Font(R.font.poppins_regular)
),
)
}
Image(
painter = painterResource(id = R.drawable.ic_btn_upward),
modifier = Modifier
.height(32.dp)
.constrainAs(iconUp) {
top.linkTo(detailsRow.bottom)
start.linkTo(glLeft)
end.linkTo(glRightIcon)
width = Dimension.fillToConstraints
},
contentDescription = ""
)
Spacer(modifier = Modifier
.padding(bottom = 50.dp)
.constrainAs(spacer) {
top.linkTo(glSpacer)
start.linkTo(parent.start)
end.linkTo(parent.end)
},)
}
Card(
Modifier
.align(BottomCenter)
.fillMaxWidth()
.alpha(if (query.value == "") 1f else 0f),
backgroundColor = MaterialTheme.colors.secondaryVariant
) {
ProgressBar5UI(
Modifier
.padding(start = 40.dp, end = 40.dp, top = 10.dp)
)
}
Card(
Modifier
.align(BottomCenter)
.fillMaxWidth()
.alpha(if (errorMsg.value) 1f else 0f),
backgroundColor = MaterialTheme.colors.secondaryVariant
) {
ProgressBar6UI(
Modifier
.padding(start = 40.dp, end = 40.dp, top = 10.dp)
)
}
Card(
Modifier
.align(BottomCenter)
.fillMaxWidth()
.alpha(if (query.value != "" && !errorMsg.value) 1f else 0f),
backgroundColor = MaterialTheme.colors.secondaryVariant
) {
ProgressBar7UI(
Modifier
.padding(start = 40.dp, end = 40.dp, top = 10.dp)
)
}
}
}
Is there possibly any special way to scroll on clicking an element while using Constraint Layout?
You can use the onGloballyPositioned modifier to retrieve the position of a composable and then use the method scrollState.animateScrollTo to scroll to that position.
Something like:
val coroutineScope = rememberCoroutineScope()
val scrollState = rememberScrollState()
var scrollToPosition by remember { mutableStateOf(0F) }
Column(Modifier.verticalScroll(scrollState)) {
Text(
"Click here to scroll",
modifier = Modifier.clickable {
coroutineScope.launch {
scrollState.animateScrollTo(scrollToPosition.roundToInt())
}
}
)
//...
Text(
"Target",
modifier = Modifier.onGloballyPositioned { coordinates ->
scrollToPosition = coordinates.positionInParent().y
}
)
}
I have the following composable function to build a Chip:
#Composable
fun CategoryChip(
category: String,
isSelected: Boolean = false,
onSelectedCategoryChanged: (String) -> Unit,
onExecuteSearch: () -> Unit
) {
Surface(
modifier = Modifier.padding(end = 8.dp, bottom = 8.dp),
elevation = 8.dp,
shape = RoundedCornerShape(16.dp),
color = when {
isSelected -> colorResource(R.color.teal_200)
else -> colorResource(R.color.purple_500)
}
) {
Row(modifier = Modifier
.toggleable(
value = isSelected,
onValueChange = {
onSelectedCategoryChanged(category)
onExecuteSearch()
}
)) {
Text(
text = category,
style = MaterialTheme.typography.body2,
color = Color.White,
modifier = Modifier.padding(8.dp)
)
}
}
}
This creates the following chip:
But what I am trying to achieve is the following:
Is it possible to create a shape like that with Jetpack Compose?
Starting with M2 1.2.0-alpha02 you can use the Chip or FilterChip composable:
Chip(
onClick = { /* Do something! */ },
border = BorderStroke(
ChipDefaults.OutlinedBorderSize,
Color.Red
),
colors = ChipDefaults.chipColors(
backgroundColor = Color.White,
contentColor = Color.Red
),
leadingIcon = {
Icon(
Icons.Filled.Settings,
contentDescription = "Localized description"
)
}
) {
Text("Change settings")
}
With M3 (androidx.compose.material3) you can use one of these options:
AssistChip
FilterChip
InputChip
SuggestionChip
Something like:
AssistChip(
onClick = { /* Do something! */ },
label = { Text("Assist Chip") },
leadingIcon = {
Icon(
Icons.Filled.Settings,
contentDescription = "Localized description",
Modifier.size(AssistChipDefaults.IconSize)
)
}
)
Yes, all you have to do is add a border to your surface.
Surface(
modifier = Modifier.padding(end = 8.dp, bottom = 8.dp),
elevation = 8.dp,
shape = RoundedCornerShape(16.dp),
border = BorderStroke(
width = 1.dp,
color = when {
isSelected -> colorResource(R.color.teal_200)
else -> colorResource(R.color.purple_500)
}
)
)
Building on Code Poet's answer i wanted to show how to do a Material Chip with background color:
#Composable
fun buildChip(label: String, icon: ImageVector? = null) {
Box(modifier = Modifier.padding(8.dp)) {
Surface(
elevation = 1.dp,
shape = MaterialTheme.shapes.small,
color = Color.LightGray
) {
Row(verticalAlignment = Alignment.CenterVertically) {
if (icon != null) Icon(
icon,
modifier = Modifier
.fillMaxHeight()
.padding(horizontal = 4.dp)
)
Text(
label,
modifier = Modifier.padding(8.dp),
style = MaterialTheme.typography.button.copy(color = Color.DarkGray)
)
}
}
}
}
Simply use ChipDefaults.outlinedBorder and Defaults.outlinedChipColors():
Chip(
onClick = {},
border = ChipDefaults.outlinedBorder,
colors = ChipDefaults.outlinedChipColors(),
) {
Text(
text = "Trends",
)
}
compose version 1.2.1 , kotlinCompilerExtensionVersion 1.3.1
Also add accompanist Flow library
implementation
"com.google.accompanist:accompanist-flowlayout:0.26.4-beta"
We will use SuggestionChip composable function to create chips.
#Composable
fun SuggestionChipLayout() {
val chips by remember { mutableStateOf(listOf("India", "France", "Spain","Netherland","Austarlia","Nepal")) }
var chipState by remember { mutableStateOf("") }
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Suggestion Chip Example")
Spacer(modifier = Modifier.height(20.dp))
FlowRow(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
mainAxisSpacing = 16.dp,
crossAxisSpacing = 16.dp,
) {
chips.forEach {
SuggestionChipEachRow(chip = it, it == chipState) { chip ->
chipState = chip
}
}
}
}
}
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun SuggestionChipEachRow(
chip: String,
selected: Boolean,
onChipState: (String) -> Unit
) {
SuggestionChip(onClick = {
if (!selected)
onChipState(chip)
else
onChipState("")
}, label = {
Text(text = chip)
},
border = SuggestionChipDefaults.suggestionChipBorder(
borderWidth = 1.dp,
borderColor = if (selected) Color.Transparent else PurpleGrey40
),
modifier = Modifier.padding(horizontal = 16.dp),
colors = SuggestionChipDefaults.suggestionChipColors(
containerColor = if (selected) Purple80 else Color.Transparent
),
shape = RoundedCornerShape(16.dp)
)
}
Filter Chips: In filter chip we can select multiple items at a time
compose version 1.2.1 , kotlinCompilerExtensionVersion 1.3.1 Also add accompanist Flow library
implementation
"com.google.accompanist:accompanist-flowlayout:0.26.4-beta"
we will use FilterChip composable function to create Filter Chip
#SuppressLint("MutableCollectionMutableState")
#Composable
fun FilterChipLayout() {
val originalChips by remember {
mutableStateOf(
listOf(
"chip1",
"chip2",
"chip3",
"chip4",
"chip5"
)
)
}
val temp: Set<Int> = emptySet()
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Filter Chip Example")
Spacer(modifier = Modifier.height(20.dp))
FilterChipEachRow(chipList = originalChips, tempList = temp)
}
}
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun FilterChipEachRow(
chipList: List<String>,
tempList: Set<Int>
) {
var multipleChecked by rememberSaveable { mutableStateOf(tempList) }
FlowRow(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
mainAxisSpacing = 16.dp,
crossAxisSpacing = 16.dp,
) {
chipList.forEachIndexed { index, s ->
FilterChip(selected = multipleChecked.contains(index), onClick = {
multipleChecked = if (multipleChecked.contains(index))
multipleChecked.minus(index)
else
multipleChecked.plus(index)
}, label = {
Text(text = s)
},
border = FilterChipDefaults.filterChipBorder(
borderColor = if (!multipleChecked.contains(index)) PurpleGrey40 else Color.Transparent,
borderWidth = if (multipleChecked.contains(index)) 0.dp else 2.dp
),
shape = RoundedCornerShape(8.dp),
leadingIcon = {
(if (multipleChecked.contains(index)) Icons.Default.Check else null)?.let {
Icon(
it,
contentDescription = ""
)
}
}
)
}
}
}