I want to create an alert dialog with Jetpack Compose. In this alert dialog include a TextField component from Compose.
I created that alert dialog but there is a problem here that when I press enter button where is on the device keyboard to add a new line my content is moving to up.
How can I solve this UI issue?
Thanks in advance.
The Problem;
Here is my code;
#Composable
fun SimpleAlert(
title: String = "Ask a Question",
onDismiss: () -> Unit,
onSubmit: (text: String) -> Unit
) {
val text = remember { mutableStateOf("") }
val textHint = "Write your question..."
val textLength = remember { mutableStateOf(0) }
val isAnonymous = remember { mutableStateOf(false) }
AlertDialog(
onDismissRequest = {
onDismiss()
},
title = {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.drawable.ic_exit),
contentDescription = "",
modifier = Modifier.
background(Color.Gray, CircleShape).clickable {
onDismiss()
}
)
}
Text(
text = title,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
},
text = {
Column(
verticalArrangement = Arrangement.Center
) {
TextField(
value = text.value,
onValueChange = {
if (it.length <= 200) {
textLength.value = it.length
text.value = it
}
},
label = {
Text(text = textHint)
},
modifier = Modifier
.padding(top = 16.dp)
.sizeIn(minHeight = 120.dp)
.background(Color.Transparent)
)
}
},
buttons = {
Row(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Switch(checked = isAnonymous.value, onCheckedChange = {
isAnonymous.value = it
})
Text(text = "Anonymously")
}
Text(text = "${textLength.value}/200")
}
Button(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, top = 0.dp, end = 16.dp, bottom = 24.dp),
onClick = {
onSubmit(text.value)
}) { Text(text = "Submit") }
}
)
}
SOLUTION ✅
To solve this issue just use Dialog instead of AlertDialog
This is definitely what you are looking for
TextField(
value = text.value,
onValueChange = {
if (it.length <= 200) {
textLength.value = it.length
text.value = it
}
},
label = {
Text(text = textHint)
},
modifier = Modifier
.padding(top = 16.dp)
.sizeIn(minHeight = 120.dp)
.background(Color.Transparent),
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
//Do something here
},
),
)
Related
I have a simple slider that I need to disable the button at star and when user moves the slider it should be enabled, i can disable it at first but i cant to enable it back, for some reason it doesnt take the new variable value this is my code.
My thoughts its because the var is declared at first and the slider value is capsuled somehow?
but if that is the case how do i pass the value to the component?
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun PopupWindowDialogStudent(
onConfirm: (Int, String) -> Unit,
parentUiState: StudentHomeUiState,
) {
// val openDialog = remember { mutableStateOf(parentUiState.showInAppFeedback) }
val openDialog = remember { mutableStateOf(!parentUiState.showInAppFeedback) }
var sliderPosition by remember { mutableStateOf(5f) }
var enable by remember { mutableStateOf(false) }
val recommend = sliderPosition.toInt()
Column(
) {
Box {
if (openDialog.value) {
Dialog(
onDismissRequest = { openDialog.value = false },
properties = DialogProperties(),
)
{
Box(
Modifier
.fillMaxWidth()
.height(500.dp)
.background(Color.White, RoundedCornerShape(10.dp))
.border(1.dp, color = Color.White, RoundedCornerShape(20.dp))
) {
Column(
modifier = Modifier
.fillMaxSize()
.fillMaxHeight()
.padding(horizontal = 5.dp)
.verticalScroll(rememberScrollState())
,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp)) {
var completeValue by remember { mutableStateOf("") }
completeValue = sliderPosition.toString()
Column(horizontalAlignment = Alignment.CenterHorizontally){
Slider(
value = sliderPosition,
onValueChange = {
sliderPosition = it
var enable = true
},
valueRange = 0f..10f,
steps = 9,
onValueChangeFinished = {
completeValue
},
modifier = Modifier.width(180.dp),
colors = SliderDefaults.colors(
thumbColor = secondaryColor,
activeTrackColor = Color.Blue
)
)
}
}
Spacer(modifier = Modifier.height(10.dp))
var comment by remember { mutableStateOf("") }
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
value = comment,
placeholder = { Text(text = "¿Tienes algún otro comentario?") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = {keyboardController?.hide()}),
onValueChange = {
comment = it
}
)
Spacer(modifier = Modifier.height(10.dp))
if (enable){
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
onConfirm(recommend,comment)
openDialog.value = !openDialog.value
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp))
}
}else{
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
}
) {
Text(
text = "¡Contesta y gana +20 puntos!",
style = MaterialTheme.typography.subtitle2,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(3.dp))
}
}
}
}
}
}
}
}
}
}
In the Slider you can use the onValueChangeFinished to change the enable value:
var enable by remember { mutableStateOf(false) }
Slider(
value = sliderPosition,
onValueChange = {
sliderPosition = it
enable = true
},
onValueChangeFinished = {
completeValue
enable = false
},
//...
)
Also you can avoid to use the if statement for the Button. Just use:
Button(
//...
enabled = enable,
onClick = {
openDialog.value = !openDialog.value
}
) {
//...
}
Below is the code for custom TextField. I have used TextField in Fragment and DialogFragment. I am having some issues while using it in DialogFragment. The phone keyboard opens when I click on the TextField below when it is used in Fragment. But even though it focuses on the TextField, the keyboard doesn't pop up when it is used in DialogFragment.
fun MyTextFiled(
search: (String) -> Unit,
query: String?
) {
var state by rememberSaveable { mutableStateOf(query) }
Card(
shape = RoundedCornerShape(dimensionResource(id = R.dimen.padding_5dp)),
) {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.height(36.dp).background(colorResource(id = R.color.background_wallet_searchView)),
) {
Icon(
painter = painterResource(id = R.drawable.ic_new_search),
contentDescription = null,
modifier = Modifier
.size(20.dp)
.padding(start = dimensionResource(id = R.dimen.padding_5dp)),
tint = colorResource(id = R.color.text_secondary),
)
BasicTextField(
value = state?:"",
onValueChange = {
search.invoke(it)
state = it
},
maxLines = 1,
modifier = Modifier
.weight(1F)
.align(Alignment.CenterVertically)
.padding(horizontal = dimensionResource(id = R.dimen.padding_5dp)),
singleLine = true,
textStyle = TextStyle(
color = colorResource(id = R.color.text_secondary),
fontSize = 13.sp,
fontStyle = MaterialTheme.typography.overline.fontStyle
),
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences,
autoCorrect = true,
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Search
),
decorationBox = { innerTextField ->
if (state.isNullOrEmpty()) {
Text(
text = stringResource(id = R.string.search),
style = MaterialTheme.typography.overline,
fontSize = 12.sp,
color = colorResource(id = R.color.text_secondary)
)
}
innerTextField()
}
)
if (!state.isNullOrEmpty())
Icon(
painter = painterResource(id = R.drawable.round_close_24),
contentDescription = null,
modifier = Modifier
.clickable {
state = ""
search.invoke("")
}
.size(20.dp)
.padding(end = dimensionResource(id = R.dimen.padding_5dp))
)
}
}
}
MessageScreen:
#Composable
fun MessageScreen(
messagesViewModel: MessagesViewModel,
navHostController: NavController,
sharedViewModel: SharedViewModel
) {
val listState = rememberLazyListState()
// Set State of Message Screen
val receiverProfile = sharedViewModel.receiverProfile
val senderProfile = sharedViewModel.senderProfile
Log.d("TAG", "MessageScreen: RECEIVER = $receiverProfile SENDER = $senderProfile")
// Get All Messages from Firebase
messagesViewModel.getAllMessageFromFirebase(receiverProfile, senderProfile)
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween,
) {
TopBar(
title = receiverProfile!!.displayName,
buttonIcon = painterResource(id = R.drawable.ic_back_arrow_back_24)
) {
navHostController.popBackStack()
}
Box(modifier = Modifier.weight(10f)) {
LazyColumn(
modifier = Modifier
.fillMaxWidth(),
verticalArrangement = Arrangement.Top,
state = listState
) {
items(items = messagesViewModel.allMessagesState) { message ->
MessageCard(message = message, sharedViewModel = sharedViewModel)
Log.d("TAG 14", message.toString())
}
CoroutineScope(Dispatchers.Main).launch {
if (messagesViewModel.allMessagesState.isNotEmpty()) {
listState.scrollToItem(messagesViewModel.allMessagesState.size - 1)
}
}
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp), contentAlignment = Alignment.Center
) {
SendMessageCard(messagesViewModel, sharedViewModel)
}
}
}
`
**SendMessageCard :-**
#Composable
fun SendMessageCard(messagesViewModel: MessagesViewModel, sharedViewModel: SharedViewModel) {
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
elevation = 8.dp
) {
Row(
modifier = Modifier,
horizontalArrangement = Arrangement.SpaceAround
) {
OutlinedTextField(
value = messagesViewModel.textState.value,
onValueChange = {
messagesViewModel.textState.value = it
},
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
trailingIcon = {
IconButton(onClick = {
messagesViewModel.sendMessage(
message = Message(
messagesViewModel.textState.value,
Timestamp.now(),
sharedViewModel.senderProfile!!.mailId
),
sharedViewModel = sharedViewModel
)
}) {
Icon(imageVector = Icons.Filled.Send, contentDescription = "Send Message")
}
},
textStyle = TextStyle(fontSize = 20.sp),
label = {
Text(text = "Type Message")
}
)
}
}
}
I figured out the solution to my problem. I used Dialog Compose inside Dialog Fragment and the keyboard popped up.
I have a compile time error as shown in the title
(#Composable invocations can only happen from the context of a #Composable function),
I have one card in this code so I want to show An AlertDialog after clicking the card clickable,
I know that this problem is exist in the platform,
So how can i solve this problem?
#Composable
fun Screen_A_with_WithTopBar(navController: NavController) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = "Top App Bar")
},
navigationIcon = {
IconButton(onClick = {
navController.navigate(Screen.Home.route)
}) {
Icon(Icons.Filled.Menu, "backIcon")
}
},
backgroundColor = MaterialTheme.colors.primary,
contentColor = Color.White,
elevation = 10.dp
)
}, content = {
Screen_A(navController)
}
)
}
#Composable
fun Screen_A(navController: NavController) {
val context = LocalContext.current
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
//.background(ScreenColor)
) {
Text(
text = "Dooro",
fontSize = 42.sp
)
Spacer(modifier = Modifier.height(33.dp))
Row(
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.SpaceAround,
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
) {
Card(
modifier = Modifier
.width(150.dp)
.height(80.dp)
.clickable {
Alert()
},
RoundedCornerShape(7.dp),
elevation = 7.dp
//.padding(40.dp)
) {
Text(
text = "None",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
}
} // END Row
}
}
#Composable
fun Alert() {
val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
AlertDialog(
onDismissRequest = {
openDialog.value = false
},
title = {
Text(text = "Title")
},
text = {
Text(
"This area typically contains the supportive text " +
"which presents the details regarding the Dialog's purpose."
)
},
buttons = {
Row(
modifier = Modifier.padding(all = 8.dp),
horizontalArrangement = Arrangement.Center
) {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { openDialog.value = false }
) {
Text("Dismiss")
}
}
}
)
}
}
Here you can use mutableStateOf to be able to show your Alert.
on Screen_A, you should add the following
var showAlert by mutableStateOf(false)
#Composable
private fun ShowAlert() {
if (showAlert)
Alert()
}
Finally in your Card, you will change the value of showAlert field to true.
Card(
modifier = Modifier
.width(150.dp)
.height(80.dp)
.clickable {
showAlert = true
},
RoundedCornerShape(7.dp),
elevation = 7.dp
//.padding(40.dp)
)
I have the following page on which I'm trying to display a list of saved cards with the ability to add a new one. The last item in the column is an expandable one so that when clicked, the user can see a form for filling out card info in order to add a new card. This was working just fine yesterday, but I can't figure out why it's not working today.
The actual CardItem elements receive clicks just fine but the custom expandable one does not and neither does the ShadowWrapper parent nor the RaisedCard one.
Cards screen:
private data class CreditCard(val type: CreditCardTypes, val lastDigits: String)
#Composable
fun CardSelectionScreen(onCardSelected: () -> Unit) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(vertical = 24.dp, horizontal = 16.dp)
.verticalScroll(rememberScrollState())
) {
var selectedCardIndex by remember { mutableStateOf(0) }
val cardList = listOf(
CreditCard(CreditCardTypes.MASTERCARD, "3429"),
CreditCard(CreditCardTypes.VISA, "3429"),
CreditCard(CreditCardTypes.MASTERCARD, "3429")
)
TopBarPadding(true)
Spacer(modifier = Modifier.height(10.dp))
RaisedCard() {
Column(
modifier = Modifier.padding(vertical = 20.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
text = stringResource(id = R.string.please_select_a_card),
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Start
)
Spacer(modifier = Modifier.height(9.dp))
for (i in cardList.indices) {
CreditCardItem(cardList[i],
isSelected = selectedCardIndex == i, onItemSelected = { ->
selectedCardIndex = i
})
}
ShadowWrapper( // This is the item's layout
cardElevation = 1.dp,
shadowElevation = 3.dp
) {
Column(
modifier = Modifier
.animateContentSize()
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(6.dp))
.background(
if (selectedCardIndex == cardList.size) colorResource(
id = R.color.bottom_modal_drawer_background
) else Color.White
)
.padding(horizontal = 16.dp, vertical = 16.dp)
.clickable(
indication = null,
interactionSource = MutableInteractionSource()
) { // this does not register at all, tried with Log.d
selectedCardIndex = cardList.size
},
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.ic_add_credit_card),
contentDescription = "Add credit card icon"
)
Spacer(modifier = Modifier.width(13.dp))
Text(
stringResource(id = R.string.new_card_addition),
textAlign = TextAlign.Start,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = colorResource(id = R.color.Orange_100)
)
}
if (selectedCardIndex == cardList.size) {
Column(
modifier = Modifier.padding(
horizontal = 16.dp
)
) {
Spacer(modifier = Modifier.padding(22.fixedDp()))
Text(
text = LocalContext.current.getString(R.string.add_credit_card_top_msg),
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = colorResource(id = R.color.Black_100)
)
Spacer(modifier = Modifier.padding(10.dp))
InputField(label = LocalContext.current.getString(R.string.owner_name))
Spacer(modifier = Modifier.padding(18.fixedDp()))
InputField(label = LocalContext.current.getString(R.string.credit_card_number))
Spacer(modifier = Modifier.padding(18.fixedDp()))
Row() {
Box(
modifier = Modifier
.weight(1.5f)
) {
InputField(label = LocalContext.current.getString(R.string.expiration_date))
}
Spacer(modifier = Modifier.padding(6.fixedDp()))
Box(
modifier = Modifier
.weight(1f)
) {
InputField(
label = LocalContext.current.getString(R.string.cvv),
isPassword = true,
placeholder = ""
)
}
}
Spacer(modifier = Modifier.height(34.fixedDp()))
Row() {
MyCheckbox(
modifier = Modifier.padding(top = 3.dp),
isCheckedInitially = true
)
Spacer(modifier = Modifier.width(13.dp))
Text(
stringResource(id = R.string.save_card_for_future_transactions),
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
color = colorResource(id = R.color.Black_100)
)
}
Spacer(modifier = Modifier.padding(22.fixedDp()))
}
}
}
}
Spacer(modifier = Modifier.height(2.dp))
}
}
Spacer(modifier = Modifier.height(32.dp))
MyButton(
text = stringResource(id = R.string.continue_text),
MyButtonType.PRIMARY,
onClick = { onCardSelected() }
)
Spacer(modifier = Modifier.height(20.dp))
AcceptedCardsFooter()
BottomBarPadding(true)
}
}
#Composable
private fun CreditCardItem(
cardDetails: CreditCard,
isSelected: Boolean,
onItemSelected: () -> Unit
) {
ShadowWrapper(cardElevation = 1.dp, shadowElevation = 3.dp) {
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(6.dp))
.background(if (isSelected) colorResource(id = R.color.bottom_modal_drawer_background) else Color.White)
.padding(horizontal = 16.dp, vertical = 15.dp)
.clickable(indication = null, interactionSource = MutableInteractionSource()) {
onItemSelected()
},
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(verticalAlignment = Alignment.CenterVertically) {
MyRadioButton(label = "", selected = isSelected)
Spacer(modifier = Modifier.width(16.dp))
Box(
modifier = Modifier
.width(43.dp)
.height(33.dp)
.clip(RoundedCornerShape(4.dp))
.background(colorResource(id = R.color.Grey_10))
.padding(horizontal = 6.dp, vertical = 7.dp)
) {
Image(
painter = painterResource(id = cardDetails.type.icon),
contentDescription = "",
modifier = Modifier.align(Alignment.Center)
)
}
Spacer(modifier = Modifier.padding(8.fixedDp()))
Text(
text = "${cardDetails.type.prefix}****${cardDetails.lastDigits}",
fontSize = 16.sp,
color = colorResource(id = R.color.Black_100)
)
}
}
}
}
RaisedCard.kt:
#Composable
fun RaisedCard(
modifier: Modifier = Modifier,
mainBody: #Composable () -> Unit
) {
Card(
shape = RoundedCornerShape(13.dp),
elevation = 10.dp,
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
) {
Column(
modifier = Modifier
.background(Color.White)
.padding(horizontal = 16.dp)
) {
mainBody()
}
}
}
ShadowWrapper.kt:
#Composable
fun ShadowWrapper(
modifier: Modifier = Modifier,
border: BorderStroke = BorderStroke(0.dp, Color.Transparent),
cardElevation: Dp = 2.dp,
shadowElevation: Dp = 1.dp,
shadowShapeRadius: Dp = 6.dp,
content: #Composable () -> Unit,
) {
Card(
elevation = cardElevation,
border = border,
shape = RoundedCornerShape(shadowShapeRadius),
modifier = modifier.shadow(shadowElevation, RoundedCornerShape(shadowShapeRadius)).wrapContentHeight()
) {
content()
}
}
I wasn't able to reproduce your issue, probably because other parts of your application are triggering additional recompositions. However the cause is most likely that you forget to remember your MutableInteractionSources.
Like this:
otherModifiers.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) { // this does not register at all, tried with Log.d
selectedCardIndex = cardList.size
}
If you do not wrap MutableInteractionSource in a remember, a new instance is created on every recomposition, so state like a previous touch events is lost
I am creating a profile page for the app and need to perform different actions based upon items clicked inside LazyColumn.
What I want is when I click on Rate us, play store to open, click on feedback browser should open, clicking on settings user should navigate to the settings screen. How to perform such different actions in LazyColumn click listener.
Here is code,
ProfileListModel.kt
data class ProfileListModel(
val imageVector: ImageVector,
#StringRes val contentDescription: Int,
#StringRes val text: Int
)
ProfileListRepository.kt
class ProfileListRepository {
fun getAllData(): List<ProfileListModel> {
return listOf(
ProfileListModel(
imageVector = Icons.Default.InvertColors,
contentDescription = R.string.app_theme,
text = R.string.app_theme
),
ProfileListModel(
imageVector = Icons.Default.NoAccounts,
contentDescription = R.string.turn_on_incognito,
text = R.string.turn_on_incognito
),
ProfileListModel(
imageVector = Icons.Default.Language,
contentDescription = R.string.app_language,
text = R.string.app_language
),
ProfileListModel(
imageVector = Icons.Default.Settings,
contentDescription = R.string.settings,
text = R.string.settings
),
ProfileListModel(
imageVector = Icons.Default.ModeOfTravel,
contentDescription = R.string.drive_mode,
text = R.string.drive_mode
),
ProfileListModel(
imageVector = Icons.Default.ChildCare,
contentDescription = R.string.child_mode,
text = R.string.child_mode
),
ProfileListModel(
imageVector = Icons.Default.BarChart,
contentDescription = R.string.time_watched,
text = R.string.time_watched
),
ProfileListModel(
imageVector = Icons.Default.Storage,
contentDescription = R.string.account_data,
text = R.string.account_data
),
ProfileListModel(
imageVector = Icons.Default.StarRate,
contentDescription = R.string.rate_us_on_play_store,
text = R.string.rate_us_on_play_store
),
ProfileListModel(
imageVector = Icons.Default.Feedback,
contentDescription = R.string.feedback,
text = R.string.feedback
),
ProfileListModel(
imageVector = Icons.Default.Forum,
contentDescription = R.string.forum,
text = R.string.forum
),
ProfileListModel(
imageVector = Icons.Default.HelpCenter,
contentDescription = R.string.help_center,
text = R.string.help_center
),
ProfileListModel(
imageVector = Icons.Default.PrivacyTip,
contentDescription = R.string.privacy_policy,
text = R.string.privacy_policy
),
ProfileListModel(
imageVector = Icons.Default.ControlPointDuplicate,
contentDescription = R.string.terms_of_service,
text = R.string.terms_of_service
)
)
}
}
ProfileListItem.kt
#Composable
fun ProfileListItem(profileListModel: ProfileListModel) {
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.clickable(
onClick = {
/*todo("perform different actions based upon item clicked")*/
}
)
/*.clickable { selectedItem(profileListModel) }*/
.padding(all = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = profileListModel.imageVector,
contentDescription = stringResource(id = profileListModel.contentDescription),
tint = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(end = 16.dp)
)
Text(
modifier = Modifier.wrapContentSize(),
text = stringResource(id = profileListModel.text)
)
}
}
ProfileScreen.kt
#ExperimentalMaterial3Api
#Composable
fun ProfileScreen() {
Box(modifier = Modifier.fillMaxSize()) {
Surface(color = MaterialTheme.colorScheme.background) {
ProfileList()
}
}
}
#ExperimentalMaterial3Api
#Composable
fun ProfileList() {
val profileListRepository = ProfileListRepository()
val getAllData = profileListRepository.getAllData()
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
) {
item {
ProfileCard()
}
item {
NightLight()
}
itemsIndexed(items = getAllData) { index, profileListModel ->
ProfileListItem(profileListModel = profileListModel)
}
}
}
#Composable
fun NightLight() {
val checkedState = remember { mutableStateOf(false) }
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.clickable(onClick = { checkedState.value = !checkedState.value })
.padding(horizontal = 16.dp, vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(modifier = Modifier.wrapContentSize(), verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Default.ModeNight,
contentDescription = stringResource(id = R.string.night_light),
tint = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(end = 16.dp)
)
Text(
modifier = Modifier.wrapContentSize(),
text = stringResource(id = R.string.night_light)
)
}
Switch(checked = checkedState.value, onCheckedChange = { checkedState.value = it })
}
}
#ExperimentalMaterial3Api
#Composable
fun ProfileCard(
modifier: Modifier = Modifier
) {
Card(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(all = 16.dp),
shape = RoundedCornerShape(size = 16.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
) {
Column(
modifier = Modifier.padding(all = 16.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text = "Click to login")
Image(
painter = painterResource(id = R.drawable.avatar),
contentDescription = "avatar",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(size = 72.dp)
.clip(shape = CircleShape)
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text = "Level 0")
Card(
modifier = Modifier.wrapContentSize(),
shape = RoundedCornerShape(size = 16.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 3.dp)
) {
Text(
text = "Check in",
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
)
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(top = 16.dp)
) {
Column(
modifier = Modifier
.wrapContentHeight()
.weight(1f)
) {
Text(text = "0")
Text(text = "Social reputation")
}
Column(
modifier = Modifier
.wrapContentHeight()
.weight(1f)
) {
Text(text = "0", modifier = Modifier.fillMaxWidth())
Text(text = "Credit score", modifier = Modifier.fillMaxWidth())
}
}
}
}
}
#ExperimentalMaterial3Api
#Preview(
showBackground = true, name = "Light mode",
uiMode = Configuration.UI_MODE_NIGHT_NO or Configuration.UI_MODE_TYPE_NORMAL
)
/*#Preview(
showBackground = true, name = "Night mode",
uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL
)*/
#Composable
fun ProfileScreenPreview() {
ProfileScreen()
}
You can pass in an id to identify the item being clicked and then when the item is clicked, pass that id back to your viewmodel. This is what uni-directional flow is about in Compose. Example:
#ExperimentalMaterialApi
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startActivity(intent)
setContent {
ProfileList(
onItemClick = {itemId ->
viewmodel.handleItem(itemId)
}
)
}
}
}
#Composable
fun ProfileList(
onItemClick: (itemId: MenuItemIDs) -> Unit
) {
val profileListRepository = ProfileListRepository()
val getAllData = profileListRepository.getAllData()
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
) {
item {
ProfileCard(
itemId = MenuItemIDs.ProfilCard,
onItemClick = onItemClick
)
}
item {
NightLight()
}
itemsIndexed(items = getAllData) { index, profileListModel ->
ProfileListItem(profileListModel = profileListModel)
}
}
}
#Composable
fun ProfileCard(
modifier: Modifier = Modifier,
itemId: MenuItemIDs,
onItemClick: (itemId: MenuItemIDs) -> Unit
) {
Card(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(all = 16.dp)
.clickable {
onItemClick(itemId)
}
) {
}
}
enum class MenuItemIDs {
ProfilCard,
NightLight,
AppTheme,
blah, blah, blah...
}