Column moves button - android

My scrollable column moves button if text is too long. I want to keep my green button at the bottom of the screen.
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween
) {
Column {
Spacer(modifier = Modifier.height(Padding.contentBig))
Column(modifier = Modifier.verticalScroll(rememberScrollState()).fillMaxSize().padding(bottom = 60.dp)) {
title.Widget()
Spacer(modifier = Modifier.height(Padding.contentBig))
description.Widget()
}
}
Row(modifier = Modifier.weight(1f, false)) {
button.Widget(
onClick = { onClickListener?.invoke(it) }
)
}
}

You can apply the weight modifier to the 1st nested Column:
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween
) {
Column(modifier = Modifier.weight(1f, true)) {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
){
//....
}
}
Row() {
Button(
onClick = { }
){}
}
}

You can use a Scaffold with a bottomBar:
val scaffoldState = rememberScaffoldState()
val scrollState = rememberScrollState()
Scaffold(
backgroundColor = Color.White,
scaffoldState = scaffoldState,
modifier = Modifier.fillMaxSize(),
bottomBar = {
Button(onClick = onClickListener) { // <-- your button
}
},
content = { paddingValues ->
val padding = paddingValues
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween
) {
Column {
Spacer(modifier = Modifier.height(Padding.contentBig))
Column(modifier = Modifier.verticalScroll(rememberScrollState()).fillMaxSize().padding(bottom = 60.dp)) {
title.Widget()
Spacer(modifier = Modifier.height(Padding.contentBig))
description.Widget()
}
}
}
}
)

Related

How to recompose Jetpack Compose UI after data change in Cloud Firestore database?

After data change in Cloud Firestore database, such as new item, or item's read value the Jetpack Compose UI won't recompose, even though i provided list as mutableState.
Getting the items from Firebase (viewModel)
private val _booksFromFB = mutableStateOf(listOf<BookFB>())
val booksFromFB = _booksFromFB
init {
getBooksFromFB()
}
fun getBooksFromFB() {
viewModelScope.launch {
_isLoading.value = true
_booksFromFB.value = repository.getBooksFromFB().data!!
if (_booksFromFB.value.isNotEmpty())
_isLoading.value = false
}
}
Getting the items from Firebase (repository)
override suspend fun getBooksFromFB(): Resource<List<BookFB>> {
return try {
Resource.Loading(true)
val response = queryBook.get().await().documents.map { documentSnapshot ->
documentSnapshot.toObject(BookFB::class.java)!!
}
if (response.isNotEmpty())
Resource.Loading(false)
Resource.Success(response)
} catch (exception: FirebaseFirestoreException) {
Resource.Error(exception.message.toString())
}
}
Getting the list and use it in UI (in LazyColumn)
val userBooks = viewModel.booksFromFB.value.filter {
it.userId == currentUser?.uid.toString()
}
Further usage of the userBooks (LazyColum included)
CurrentlyReadingSection(
context = context,
navController = navController,
userBooks = userBooks.filter { it.read == false },
commonViewModel = commonViewModel
)
CurrentlyReadingSection
LazyRow() {
items(userBooks) { book ->
BookRow(
modifier = Modifier.padding(4.dp),
onItemClicked = { }
) {
val isExpanded = remember {
mutableStateOf(false)
}
val isRead = rememberSaveable {
mutableStateOf(false)
}
Box(
modifier = Modifier
.fillMaxSize()
.background(AppColors.mBackgroundSec),
contentAlignment = BottomCenter
) {
Row {
AnimatedVisibility(visible = isExpanded.value) {
Box(
modifier = Modifier
.fillMaxSize()
.clickable {
isExpanded.value = !isExpanded.value
},
contentAlignment = Center
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
MyButton(
modifier = Modifier
.fillMaxWidth(0.7f)
.fillMaxHeight(0.2f)
.clip(RoundedCornerShape(12.dp)),
text = "READ",
fontSize = 12,
contentPadding = 8
) {
isRead.value = !isRead.value
isExpanded.value = !isExpanded.value
viewModel.updateBook(context = context, book = book, isRead = isRead.value)
}
Spacer(modifier = Modifier.height(4.dp))
MyButton(
modifier = Modifier
.fillMaxWidth(0.7f)
.fillMaxHeight(0.24f)
.clip(RoundedCornerShape(12.dp)),
text = "RATE",
fontSize = 12,
contentPadding = 8
) {
commonViewModel.currentBook.value = book
navController.navigate(Screen.Rate.route)
}
}
}
}
AsyncImage(
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(12.dp))
.clickable { isExpanded.value = !isExpanded.value },
model = ImageRequest.Builder(context)
.data(
if (isValid(book.image))
book.image
else
com.example.read.R.drawable.imagenotfound
)
.crossfade(true)
.build(),
contentScale = ContentScale.FillBounds,
contentDescription = "Book image"
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.1f)
.background(if (book.read!!) AppColors.mGreen else AppColors.mRed)
)
}
}
}
}
BookRow
#Composable
fun BookRow(
modifier: Modifier = Modifier,
book: BookFB = BookFB(),
category: MyCategory = MyCategory("", ""),
shapeDp: Int = 12,
heightSize: Int = 170,
widthSize: Int = 120,
isForYou: Boolean = false,
isYourCollection: Boolean = false,
onItemClicked: () -> Unit = {},
content: #Composable() () -> Unit,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = if (isYourCollection) Modifier.widthIn(max = widthSize.dp) else Modifier.fillMaxWidth()
) {
Surface(
shape = RoundedCornerShape(shapeDp.dp),
elevation = 4.dp,
modifier = modifier.border(2.dp, color = Color.Black, shape = RoundedCornerShape(shapeDp.dp))
) {
Box(
modifier = Modifier
.clickable {
onItemClicked()
}
.height(heightSize.dp)
.width(widthSize.dp)
.background(AppColors.mBackgroundSec))
{
content()
}
}
if (isYourCollection) {
Text(
modifier = Modifier.padding(top = 4.dp),
text = book.title.toString(),
fontWeight = FontWeight.Bold,
fontStyle = FontStyle.Italic,
fontSize = 14.sp,
color = AppColors.mTextWhite,
textAlign = TextAlign.Center,
maxLines = 3,
overflow = TextOverflow.Clip
)
}
if (isForYou) {
Text(
modifier = Modifier.padding(top = 4.dp),
text = category.name,
fontWeight = FontWeight.Bold,
fontStyle = FontStyle.Italic,
fontSize = 18.sp,
color = AppColors.mTextWhite,
textAlign = TextAlign.Center,
maxLines = 3,
overflow = TextOverflow.Clip
)
}
}
}
Progress Bar in Home Screen
if (viewModel.isLoading.value) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(color = AppColors.mMain)
}
} else {
content..
}

Jetpack Compose - Cannot animate Scaffold's bottom bar's height

I need to reveal some floating action buttons at the bottom of the screen when some other buttons, in a scrollable list, are no longer visible on the screen. Naturally, I feel like using LazyColumn, AnimatedVisibility and Scaffold's bottom bar for this (I need the snackbar and the screen's content be above the floating action buttons). But the animation doesn't work as expected. As the floating buttons appear/dissapear, the bottom bar's height is not animated (but I think it could actually be that the content padding isn't animated).
The result I'm getting:
The code:
val lazyListState = rememberLazyListState()
val isFloatingButtonVisible by derivedStateOf {
lazyListState.firstVisibleItemIndex >= 2
}
Scaffold(
bottomBar = {
AnimatedVisibility(
visible = isFloatingButtonVisible,
enter = slideInVertically { height -> height },
exit = slideOutVertically { height -> height }
) {
Row(
modifier = Modifier.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
repeat(2) {
Button(
modifier = Modifier
.height(48.dp)
.weight(1f),
onClick = {}
) {
Text(text = "Something")
}
}
}
}
}
) { contentPadding ->
LazyColumn(
modifier = Modifier.padding(contentPadding),
state = lazyListState,
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
Text(
text = LoremIpsum(50).values.joinToString(" ")
)
}
item {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
repeat(2) {
Button(onClick = {}) {
Text(text = "Bla Bla")
}
}
}
}
item {
Text(
text = LoremIpsum(700).values.joinToString(" ")
)
}
}
}
I don't think it's possible to do this with AnimatedVisibility. I got it like this
#Composable
fun Test() {
val lazyListState = rememberLazyListState()
val isFloatingButtonVisible by remember {
derivedStateOf {
lazyListState.firstVisibleItemIndex >= 1
}
}
var barHeight by remember { mutableStateOf(0.dp) }
var listSizeDelta by remember { mutableStateOf(0.dp) }
val bottomBarOffset = remember { Animatable(barHeight, Dp.VectorConverter) }
LaunchedEffect(isFloatingButtonVisible) {
if (isFloatingButtonVisible) {
bottomBarOffset.animateTo(barHeight)
listSizeDelta = barHeight
} else {
listSizeDelta = 0.dp
bottomBarOffset.animateTo(0.dp)
}
}
val density = LocalDensity.current
BoxWithConstraints(Modifier.fillMaxSize()) {
LazyColumn(
modifier = Modifier
.size(width = maxWidth, height = maxHeight - listSizeDelta)
.background(Color.Yellow),
state = lazyListState,
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
repeat(15) {
item {
Text(
text = LoremIpsum(50).values.joinToString(" ")
)
}
}
}
Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.graphicsLayer {
translationY =
density.run { (-bottomBarOffset.value + barHeight).toPx() }
}
.background(Color.Green)
.onSizeChanged {
barHeight = density.run { it.height.toDp()}
}
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
repeat(2) {
Button(
modifier = Modifier
.height(48.dp)
.weight(1f),
onClick = {}
) {
Text(text = "Something")
}
}
}
}
}
The content padding in lazycolumn modifier breaks the animation;
LazyColumn(
modifier = Modifier.padding(contentPadding) // -> Delete this.
)
For the same view;
Row(
modifier = Modifier
.background(Color.White) //-> Add this.
.padding(8.dp)
)
Got the right result using AnimatedContent instead of AnimatedVisibility.
val lazyListState = rememberLazyListState()
val isFloatingButtonVisible by derivedStateOf {
lazyListState.firstVisibleItemIndex >= 2
}
Scaffold(
bottomBar = {
AnimatedContent(
targetState = isFloatingButtonVisible,
transitionSpec = {
slideInVertically { height -> height } with
slideOutVertically { height -> height }
}
) { isVisible ->
if (isVisible) {
Row(
modifier = Modifier
.border(1.dp, Color.Red)
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
repeat(2) {
Button(
modifier = Modifier
.height(48.dp)
.weight(1f),
onClick = {}
) {
Text(text = "Something")
}
}
}
} else {
Box(modifier = Modifier.fillMaxWidth())
}
}
}
) { contentPadding ->
LazyColumn(
modifier = Modifier.padding(contentPadding),
state = lazyListState,
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
Text(
text = LoremIpsum(50).values.joinToString(" ")
)
}
item {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
repeat(2) {
Button(onClick = {}) {
Text(text = "Bla Bla")
}
}
}
}
item {
Text(
text = LoremIpsum(700).values.joinToString(" ")
)
}
}
}

Navigation drawer below TopAppBar in Jetpack Compose

I am using Jetpack Compose for developing one app which contains multiple screens and we can navigate to them using Navigation Drawer.
How can I put my navigation drawer below TopAppBar ( or Toolbar). Below is the gif for the issue:
Below is the composable function for my screen:
fun Home(navController: NavHostController) {
val scaffoldState = rememberScaffoldState()
val coroutineScope = rememberCoroutineScope()
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
buildAnnotatedString {
append("Hello World")
pushStyle(SpanStyle(fontWeight = FontWeight.Medium, fontSize = 12.sp))
append("\nhelloworld#gmail.com")
pushStyle(SpanStyle(fontWeight = FontWeight.Light, fontSize = 10.sp))
}
)
},
navigationIcon = {
IconButton(onClick = {
coroutineScope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(
imageVector = Icons.Outlined.Menu,
contentDescription = null
)
}
}
)
},
drawerContent = {},
scaffoldState = scaffoldState,
drawerContentColor = MaterialTheme.colors.onBackground,
content = {
HomeScreenContent(scaffoldState, navController)
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text(text = "Compose") },
onClick = { navController.navigate("CreateMsg") },
modifier = Modifier.padding(0.dp),
icon = {
Icon(
imageVector = Icons.Outlined.Create,
contentDescription = null,
tint = Color.White,
)
},
shape = CircleShape,
backgroundColor = MaterialTheme.colors.primary
)
}
)
}
If you want it under your TopAppbar put TopAppbar and Scaffold inside a column as
Column(modifier=Modifier.fillMaxSize()){
TopAppbar()
Scaffold()
}
If you want your NavigationDrawer below TopAppbar put them inside a Box with
Box(modifier=Modifier.fillMaxSize()){
Scaffold()
TopAppbar()
}
And give your HomeContent topPadding as big as your TopAAppbar's height.
First one
#Composable
private fun MyComposable() {
val scaffoldState = rememberScaffoldState()
Column(modifier = Modifier.fillMaxSize()) {
val coroutineScope = rememberCoroutineScope()
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
buildAnnotatedString {
append("Hello World")
pushStyle(SpanStyle(fontWeight = FontWeight.Medium, fontSize = 12.sp))
append("\nhelloworld#gmail.com")
pushStyle(SpanStyle(fontWeight = FontWeight.Light, fontSize = 10.sp))
}
)
},
navigationIcon = {
IconButton(onClick = {
coroutineScope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(
imageVector = Icons.Outlined.Menu,
contentDescription = null
)
}
}
)
Scaffold(
modifier = Modifier
.fillMaxSize(),
drawerContent = {},
scaffoldState = scaffoldState,
drawerContentColor = MaterialTheme.colors.onBackground,
content = {
HomeScreenContent(scaffoldState)
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text(text = "Compose") },
onClick = { },
modifier = Modifier.padding(0.dp),
icon = {
Icon(
imageVector = Icons.Outlined.Create,
contentDescription = null,
tint = Color.White,
)
},
shape = CircleShape,
backgroundColor = MaterialTheme.colors.primary
)
}
)
}
}
#Composable
fun HomeScreenContent(scaffoldState: ScaffoldState) {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Red)
) {
Text("HOME CONTENT", fontSize = 30.sp, color = Color.White)
}
}
Second one
#Composable
private fun MyComposable() {
val scaffoldState = rememberScaffoldState()
Box(modifier = Modifier.fillMaxSize()) {
val coroutineScope = rememberCoroutineScope()
Scaffold(
modifier = Modifier
.fillMaxSize(),
drawerContent = {},
scaffoldState = scaffoldState,
drawerContentColor = MaterialTheme.colors.onBackground,
content = {
HomeScreenContent(scaffoldState, 56.dp)
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text(text = "Compose") },
onClick = { },
modifier = Modifier.padding(0.dp),
icon = {
Icon(
imageVector = Icons.Outlined.Create,
contentDescription = null,
tint = Color.White,
)
},
shape = CircleShape,
backgroundColor = MaterialTheme.colors.primary
)
}
)
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
buildAnnotatedString {
append("Hello World")
pushStyle(SpanStyle(fontWeight = FontWeight.Medium, fontSize = 12.sp))
append("\nhelloworld#gmail.com")
pushStyle(SpanStyle(fontWeight = FontWeight.Light, fontSize = 10.sp))
}
)
},
navigationIcon = {
IconButton(onClick = {
coroutineScope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(
imageVector = Icons.Outlined.Menu,
contentDescription = null
)
}
}
)
}
}
#Composable
fun HomeScreenContent(scaffoldState: ScaffoldState, topPadding:Dp) {
Column(
modifier = Modifier
.padding(top= topPadding)
.fillMaxSize()
.background(Color.Red)
) {
Text("HOME CONTENT", fontSize = 30.sp, color = Color.White)
}
}
56.dp is default size for TopAppBar on my device. If you have custom height you need to get it via Modifier.onSizeChanged{} Modifier that set to TopAppBar.

Some of Text Fields do not update when locale change in composable android

val languages = listOf(
"en" to "English",
"vi" to "VietNam",
)
#Composable
fun LoginScreen(
navController: NavController?,
viewModel: LoginViewModel = hiltViewModel()
) {
val currentLanguageIndex = viewModel.language.observeAsState().value ?: 0
SetLanguage(currentLanguageIndex)
Scaffold() {
MainUI(navController = navController, viewModel = viewModel,index= currentLanguageIndex)
}
}
#Composable
fun SetLanguage(languageIndex: Int) {
val locale = Locale(if (languageIndex == 0) "en" else "vi")
val configuration = LocalConfiguration.current
configuration.setLocale(locale)
val resources = LocalContext.current.resources
resources.updateConfiguration(configuration, resources.displayMetrics)
}
#OptIn(ExperimentalComposeUiApi::class)
//#Preview(device = Devices.AUTOMOTIVE_1024p)
#Composable
fun MainUI(navController: NavController?, viewModel: LoginViewModel,index : Int) {
val keyboardController = LocalSoftwareKeyboardController.current
val image = painterResource(id = R.drawable.ic_login_img)
var emailValue by remember { mutableStateOf("") }
var passwordValue by remember { mutableStateOf("") }
val passwordVisibility = remember { mutableStateOf(false) }
val focusRequesterEmail = remember { FocusRequester() }
val focusRequesterPassword = remember { FocusRequester() }
val scrollState = rememberScrollState()
val checkState = remember {
mutableStateOf(true)
}
Column(
modifier = Modifier
.fillMaxWidth()
.background(color = Color.White),
horizontalAlignment = Alignment.End
) {
// DropdownDemo(viewModel = viewModel,index = index)
val scope = rememberCoroutineScope()
var expanded by remember { mutableStateOf(false) }
var selectedIndex by remember { mutableStateOf(index) }
Box(
modifier = Modifier
.width(150.dp)
.padding(top = 16.dp, end = 8.dp)
.wrapContentSize(Alignment.TopEnd)
) {
DropDownLabel(languages[selectedIndex].second, onClick = {
expanded = true
})
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.width(150.dp)
) {
languages.forEachIndexed { index, s ->
DropdownMenuItem(
onClick = {
selectedIndex = index
expanded = false
scope.launch {
viewModel.saveLocale(index)
}
},
) {
DropDownLabel(s.second, onClick = {
selectedIndex = index
expanded = false
scope.launch {
viewModel.saveLocale(index)
}
})
}
}
}
}
Spacer(modifier = Modifier.height(20.dp))
Column(
modifier = Modifier
.fillMaxSize()
.scrollable(state = scrollState, orientation = Orientation.Vertical),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.height(70.dp)
.width(140.dp),
contentAlignment = Alignment.TopCenter
) {
Image(painter = image, contentDescription = null)
}
Spacer(modifier = Modifier.height(19.dp))
Column(
modifier = Modifier
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = stringResource(R.string.hello),
style = MaterialTheme.typography.h4,
)
Spacer(modifier = Modifier.height(10.dp))
Text(
text = stringResource(R.string.enter_credentials_msg),
style = MaterialTheme.typography.body2,
fontSize = 14.sp
)
Spacer(modifier = Modifier.padding(10.dp))
Column(
modifier = Modifier
.fillMaxWidth(0.6f)
.padding(
start = 16.dp,
end = 16.dp
),
horizontalAlignment = Alignment.CenterHorizontally
) {
OutlinedTextField(
value = emailValue,
onValueChange = { emailValue = it },
label = { Text(text = stringResource(id = R.string.email),
style = MaterialTheme.typography.body2.copy(color = Gray1)) },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Next
),
placeholder = { Text(text = stringResource(id = R.string.email),
style = MaterialTheme.typography.body2.copy(color = Gray1)) },
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester = focusRequesterEmail),
colors = TextFieldDefaults.outlinedTextFieldColors(
unfocusedBorderColor = Gray2
),
keyboardActions = KeyboardActions(
onNext = {
CoroutineScope(Default).launch {
keyboardController?.hide()
delay(400)
focusRequesterPassword.requestFocus()
}
}
)
)
Spacer(modifier = Modifier.padding(5.dp))
OutlinedTextField(
value = passwordValue,
colors = TextFieldDefaults.outlinedTextFieldColors(
unfocusedBorderColor = Gray2
),
onValueChange = { passwordValue = it },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Done
),
trailingIcon = {
IconButton(onClick = {
passwordVisibility.value = !passwordVisibility.value
}) {
Icon(
painter = painterResource(id = if (passwordVisibility.value) R.drawable.ic_baseline_visibility_24 else R.drawable.ic_baseline_visibility_off_24),
contentDescription = "Visibility button",
)
}
},
label = { Text(stringResource(id = R.string.password),
style = MaterialTheme.typography.body2.copy(color = Gray1)) },
placeholder = { Text(text = stringResource(id = R.string.password),
style = MaterialTheme.typography.body2.copy(color = Gray1)) },
singleLine = true,
visualTransformation = if (passwordVisibility.value) VisualTransformation.None
else PasswordVisualTransformation(),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester = focusRequesterPassword),
keyboardActions = KeyboardActions(
onDone = { keyboardController?.hide() }
),
)
Spacer(modifier = Modifier.padding(25.dp))
Row(
modifier = Modifier.fillMaxWidth()
) {
Row(
modifier = Modifier.width(intrinsicSize = IntrinsicSize.Max),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(checked = checkState.value, onCheckedChange = {
checkState.value = it
})
Text(
stringResource(id = R.string.remember_me),
maxLines = 1,
style = MaterialTheme.typography.body2.copy(color = Gray1)
)
}
Box(
contentAlignment = Alignment.CenterEnd,
modifier = Modifier.fillMaxWidth()
) {
OutlinedButton(
onClick = {
// TODO
},
border = BorderStroke(0.5.dp, BlueLight),
shape = RoundedCornerShape(8.dp)
) {
Text(
text = stringResource(id = R.string.sign_in),
fontSize = 20.sp,
style = MaterialTheme.typography.button.copy(color = BlueLight)
)
}
}
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp),
contentAlignment = Alignment.BottomCenter
) {
Text(
text = stringResource(id = R.string.skytech_software_solutions_pvt_ltd),
style = MaterialTheme.typography.caption.copy(color = Gray1),
)
}
}
}
}
#Composable
fun DropdownDemo(viewModel: LoginViewModel,index: Int) {
val scope = rememberCoroutineScope()
var expanded by remember { mutableStateOf(false) }
var selectedIndex by remember { mutableStateOf(index) }
Box(
modifier = Modifier
.width(150.dp)
.padding(top = 16.dp, end = 8.dp)
.wrapContentSize(Alignment.TopEnd)
) {
DropDownLabel(languages[selectedIndex].second, onClick = {
expanded = true
})
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.width(150.dp)
) {
languages.forEachIndexed { index, s ->
DropdownMenuItem(
onClick = {
selectedIndex = index
expanded = false
scope.launch {
viewModel.saveLocale(index)
}
},
) {
DropDownLabel(s.second, onClick = {
selectedIndex = index
expanded = false
scope.launch {
viewModel.saveLocale(index)
}
})
}
}
}
}
}
#Composable
fun DropDownLabel(labelName: String, onClick: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
) {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_language_24),
contentDescription = "language"
)
Spacer(modifier = Modifier.width(10.dp))
Text(
labelName, modifier = Modifier
.fillMaxWidth()
)
}
}
when we change the language UI does not update some of text like sign in button and Edit text. but normal text are changing. how do we over come this situation. I have used mutable live data for the viewModel to UI. when we change language and kill the application and restart, then update all the labels.

How to open datePicker on Button Click ? in jetpack compose

I have made datePicker composeable, when i call that composable in on click of Icon , it show
#Composable invocation can happen only within the context of composable
I simply want to open Date picker while on clicking and on ok press My Text is updated .
My DatePicker
#Composable
fun DatePicker(onDateSelected: (LocalDate) -> Unit, onDismissRequest: () -> Unit) {
val selDate = remember { mutableStateOf(LocalDate.now()) }
Dialog(onDismissRequest = { onDismissRequest() }, properties = DialogProperties()) {
Column(
modifier = Modifier
.wrapContentSize()
.background(
color = MaterialTheme.colors.surface,
shape = RoundedCornerShape(size = 16.dp)
)
) {
Column(
Modifier
.defaultMinSize(minHeight = 72.dp)
.fillMaxWidth()
.background(
color = MaterialTheme.colors.primary,
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
)
.padding(16.dp)
) {
Text(
text = "Select date".uppercase(),
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onPrimary
)
Spacer(modifier = Modifier.size(24.dp))
Text(
text = selDate.value.format(DateTimeFormatter.ofPattern("MMM d, YYYY")),
style = MaterialTheme.typography.h4,
color = MaterialTheme.colors.onPrimary
)
Spacer(modifier = Modifier.size(16.dp))
}
CustomCalendarView(onDateSelected = {
selDate.value = it
})
Spacer(modifier = Modifier.size(8.dp))
Row(
modifier = Modifier
.align(Alignment.End)
.padding(bottom = 16.dp, end = 16.dp)
) {
TextButton(
onClick = onDismissRequest
) {
//TODO - hardcode string
Text(
text = "Cancel",
style = MaterialTheme.typography.button,
color = MaterialTheme.colors.onPrimary
)
}
TextButton(
onClick = {
onDateSelected(selDate.value)
onDismissRequest()
}
) {
//TODO - hardcode string
Text(
text = "OK",
style = MaterialTheme.typography.button,
color = MaterialTheme.colors.onPrimary
)
}
}
}
}
}
#Composable
fun CustomCalendarView(onDateSelected: (LocalDate) -> Unit) {
AndroidView(
modifier = Modifier.wrapContentSize(),
factory = { context ->
CalendarView(ContextThemeWrapper(context, R.style.CalenderViewCustom))
},
update = { view ->
view.setOnDateChangeListener { _, year, month, dayOfMonth ->
onDateSelected(
LocalDate
.now()
.withMonth(month + 1)
.withYear(year)
.withDayOfMonth(dayOfMonth)
)
}
}
)
}
ViewModel Class :
#HiltViewModel
class AddWeightViewModel #Inject() constructor(private val repository: WeightRepository)
:
ViewModel() {
val weightState = mutableStateOf("")
val dateState = mutableStateOf("")
fun onWeightChange(weight: String) {
weightState.value = weight
}
fun onDateChange(date : String) {
dateState.value = date
}
fun addWeight() = viewModelScope.launch {
val weight = Weight( 0, weightState.value , dateState.value )
repository.addWeight(weight)
}
}
MyWeightScreen :
#ExperimentalComposeUiApi
#Composable
fun AddWeightScreen(navController: NavController, viewModel: AddWeightViewModel) {
Column(modifier = Modifier
.fillMaxSize()
.background(color = MaterialTheme.colors.primary)
.padding(bottom = 56.dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Cancel", fontSize = 16.sp, modifier = Modifier
.padding(16.dp)
.align(alignment = Alignment.Start)
.clickable {
navController.navigate(Screens.MyWeight.route)
}, color = background)
WeightData(viewModel)
Button(
onClick = {
viewModel.addWeight()
navController.navigate(Screens.MyWeight.route)
},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
colors = ButtonDefaults.buttonColors(backgroundColor = background,
contentColor = textDark)
) {
Text(text = "Done", fontSize = 18.sp)
}
}
}
#ExperimentalComposeUiApi
#Composable
fun WeightData(viewModel: AddWeightViewModel) {
val selDate = LocalDate.now()
val date = viewModel.dateState.value
val keyboardController = LocalSoftwareKeyboardController.current
Column(horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(22.dp),
modifier = Modifier
.drawBehind {
drawCircle(
color = textColor,
radius = 450f,
style = Stroke(width = 1.dp.toPx())
)
}
) {
Text(text = "Enter Weight", color = Color.White.copy(alpha = .8f))
TextField(
value = viewModel.weightState.value,
onValueChange = { viewModel.onWeightChange(it) },
modifier = Modifier.background(color = MaterialTheme.colors.primary),
keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number,imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }),
singleLine = true)
Row(verticalAlignment = Alignment.CenterVertically , horizontalArrangement = Arrangement.Center ) {
Icon(imageVector = Icons.Default.DateRange,
contentDescription = null,
modifier = Modifier.clickable { DatePicker(onDateSelected = ) {
}})
Spacer(modifier = Modifier.width(20.dp))
DatePicker(onDateSelected = {selDate}) {
viewModel.onDateChange(date)
}
}
}
This is what i did .

Categories

Resources