animatedFloatAsState hold different value than updated target value jetpack compose - android

I am trying to animate the float value using animateFloatAsState but while I am updating the target value the animated value gives me random different value .
For eg:
This is the animatedValue :
val animatedOffsetValue by animateFloatAsState(
targetValue = titleBarOffsetHeightPx.value
)
This is target value which is being updated over time :
val titleBarOffsetHeightPx = remember { mutableStateOf(0f) }
So when I updated the value as titleBarOffsetHeightPx.value = "new value"
And I check the value of animatedOffsetValue , it gives different than titleBarOffsetHeightPx.
full code :
package com.example.productivitytrackergoalscheduler.features.history.presentation
import android.util.Log
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.example.productivitytrackergoalscheduler.business.util.DateConverter
import com.example.productivitytrackergoalscheduler.business.util.Extensions.isScrolledToTheEnd
import com.example.productivitytrackergoalscheduler.features.core.presentation.navigation.Screen
import com.example.productivitytrackergoalscheduler.features.core.util.SnackbarController
import com.example.productivitytrackergoalscheduler.features.history.presentation.components.DayGoalList
import com.example.productivitytrackergoalscheduler.features.history.presentation.components.HistoryTitleBar
import com.example.productivitytrackergoalscheduler.features.home.presentation.components.CustomNavBar
import com.example.productivitytrackergoalscheduler.features.home.presentation.components.ITEM_WIDTH
import com.example.productivitytrackergoalscheduler.features.home.presentation.util.NavItems
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
const val NAV_BAR_HEIGHT = ITEM_WIDTH + 110
#Composable
fun HistoryScreen(
...
) {
/** Height of title bar in dp */
val titleBarHeight = 66.dp
/** Height of title bar converted into float for offset */
val titleBarHeightPx = with(LocalDensity.current) { titleBarHeight.roundToPx().toFloat() }
/** Offset value of title bar. How much to move up or down */
val titleBarOffsetHeightPx = remember { mutableStateOf(0f) }
/** Is the scrolling is up or down */
val isUpScrolled = remember { mutableStateOf(false) }
val animatedOffsetValue by animateFloatAsState(
targetValue = titleBarOffsetHeightPx.value
)
val nestedScrollConnection = remember {
object: NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
isUpScrolled.value = available.y < 0
val delta = available.y
val newOffset = titleBarOffsetHeightPx.value + delta
titleBarOffsetHeightPx.value = newOffset.coerceIn(-titleBarHeightPx, 0f)
Log.d("TAG", "onPostFling: animated ${animatedOffsetValue}")
Log.d("TAG", "onPostFling: no animated ${titleBarOffsetHeightPx.value}")
return Offset.Zero
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
val scrolledTo = if(isUpScrolled.value) titleBarHeightPx/3 else titleBarHeightPx/2
if(-titleBarOffsetHeightPx.value < scrolledTo)
{
titleBarOffsetHeightPx.value = 0f
}else{
titleBarOffsetHeightPx.value = -titleBarHeightPx
}
return super.onPostFling(consumed, available)
}
}
}
val endOfScrollList by remember {
derivedStateOf {
scrollState.isScrolledToTheEnd()
}
}
Scaffold(
modifier = Modifier.nestedScroll(nestedScrollConnection),
scaffoldState = scaffoldState,
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(color = MaterialTheme.colors.background)
) {
Box(
modifier = Modifier.fillMaxHeight()
) {
Box(
modifier = Modifier
/** this offset id for while title bar hides or shows
* The lazyColumn content also should move towards the
* up and down as to fill empty space */
.offset { IntOffset(x = 0, y = animatedOffsetValue.roundToInt()) }
.padding(horizontal = 8.dp)
) {
LazyColumn(
state = scrollState,
/** This modifier offset gives the space for title bar */
modifier = Modifier.offset{ IntOffset(x = 0, y = titleBarHeightPx.toInt())}
){
...
}
}
}
/** Title Bar with z-index top */
Box(
modifier = Modifier
.align(Alignment.TopCenter)
.fillMaxWidth()
.offset { IntOffset(x = 0, y = titleBarOffsetHeightPx.value.roundToInt()) }
) {
HistoryTitleBar(navController = navController)
}
/** end title bar*/
...
}
}
}

Related

Resize View onClick Jetpack Compose

I'm pretty new to compose and having difficulty resizing views. I have a view where I am initially hardcoding it to some size. When clicking the view I want it to expand it to fill the width to certain extent. How is this possible with in compose?
#Composable
fun InputBar(
hint: String,
onClick: () -> Unit
) {
Card(
onClick = { /* update view to fillMaxWidth() */ },
modifier = Modifier
.width(150.dp)
.height(44.dp),
) {
Row(modifier = Modifier.fillMaxWidth()) {
TextField(
value = "",
onValueChange = {
},
placeholder = { Text(hint) }
)
}
}
}
you could achieve your by using something like this:
import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlin.math.log
private const val TAG = "InputBar"
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun InputBar(
hint: String,
onClick: () -> Unit
) {
var isFillMaxWidth by remember { mutableStateOf(false) }
val isExpandModifier = remember(isFillMaxWidth){
Modifier
.height(44.dp)
.then(
if (isFillMaxWidth) {
Log.i(TAG, "InputBar: fillMaxWidth")
Modifier.fillMaxWidth()
} else {
Log.i(TAG, "InputBar: 150")
Modifier.width(150.dp)
}
)}
Card(
modifier = isExpandModifier,
) {
Row(modifier = Modifier.fillMaxWidth()) {
TextField(
value = "",
onValueChange = {
},
placeholder = { Text(hint) },
interactionSource = remember { MutableInteractionSource() }
.also { interactionSource ->
LaunchedEffect(interactionSource) {
interactionSource.interactions.collect {
if (it is PressInteraction.Release) {
// works like onClick
isFillMaxWidth = !isFillMaxWidth
Log.i(TAG, "InputBar: onClick")
}
}
}
}
)
}
}
}

jetpack compose exapmle No get method providing array access

I copied a piece of code from the example in jetpack compose.link
But in Android Studio a problem arises:
I wonder where is the problem? I'm still a beginner
The following is the complete code:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.material.MaterialTheme.colors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.freedom.android.ui.theme.MyApplicationTheme
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
drawerContent = { Text("Drawer content") },
topBar = {
TopAppBar(
title = { Text("Simple Scaffold Screen") },
navigationIcon = {
IconButton(
onClick = {
scope.launch { scaffoldState.drawerState.open() }
}
) {
Icon(Icons.Filled.Menu, contentDescription = "Localized description")
}
}
)
},
floatingActionButtonPosition = FabPosition.End,
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Inc") },
onClick = { /* fab click handler */ }
)
},
content = { innerPadding ->
LazyColumn(contentPadding = innerPadding) {
items(count = 100) {
Box(
Modifier
.fillMaxWidth()
.height(50.dp)
.background(colors[it % colors.size])
)
}
}
}
)
}
}
}
}
#Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
MyApplicationTheme {
Greeting("Android")
}
}
I think the problem is with the colors variable, it doesn't seem to be an array, but this was copied from the official documentation, I didn't change it.
package androidx.compose.material
object MaterialTheme {
/**
* Retrieves the current [Colors] at the call site's position in the hierarchy.
*
* #sample androidx.compose.material.samples.ThemeColorSample
*/
val colors: Colors
#Composable
#ReadOnlyComposable
get() = LocalColors.current
}
If you look at the full source code of the sample the docs use, it has a top level object:
private val colors = listOf(
Color(0xFFffd7d7.toInt()),
Color(0xFFffe9d6.toInt()),
Color(0xFFfffbd0.toInt()),
Color(0xFFe3ffd9.toInt()),
Color(0xFFd0fff8.toInt())
)
So yeah, the colors that sample is referring to is a list that you can index into. If you want to also have a semi-random set of colors for your backgrounds, you can copy that list into your code as well.

Functions which invoke #Composable functions must be marked with the #Composable annotation in WebView

I have a custom Compose Function for WebView, in Which I want to show the Custom progress bar while loading the page but it gives this error.
Functions which invoke #Composable functions must be marked with the #Composable annotation
This is my Composable.
package com.example.devdocs
import android.graphics.Bitmap
import android.view.ViewGroup
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
#Composable
fun webView(
url: String = "www.google.com"
) {
var backEnabled by remember { mutableStateOf(false) }
var webView: WebView? = null
AndroidView(
factory = { context ->
WebView(context).apply {
settings.javaScriptEnabled = true
settings.userAgentString
settings.domStorageEnabled
settings.setSupportZoom(true)
settings.builtInZoomControls = true
settings.displayZoomControls = false
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
backEnabled = view.canGoBack()
LoadingAnimation(
modifier = Modifier
.fillMaxWidth(),
isVisible = true
)
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
LoadingAnimation(
modifier = Modifier
.fillMaxWidth(),
isVisible = false
)
}
}
webChromeClient = WebChromeClient()
loadUrl(url)
webView = this
}
}, update = {
webView = it
})
BackHandler(enabled = backEnabled) {
webView?.goBack()
}
}
"LoadingAnimation" is a custom animation class that shows the animation.
Here is Animation Class Source Code.
package com.example.devdocs
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
#Composable
fun LoadingAnimation(
modifier: Modifier = Modifier,
circleSize: Dp = 25.dp,
circleColor: Color = MaterialTheme.colorScheme.primary,
spaceBetween: Dp = 10.dp,
travelDistance: Dp = 20.dp,
isVisible: Boolean = true
) {
if (isVisible) {
val circles = listOf(
remember { Animatable(initialValue = 0f) },
remember { Animatable(initialValue = 0f) },
remember { Animatable(initialValue = 0f) }
)
circles.forEachIndexed { index, animatable ->
LaunchedEffect(key1 = animatable) {
delay(index * 100L)
animatable.animateTo(
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = keyframes {
durationMillis = 1200
0.0f at 0 with LinearOutSlowInEasing
1.0f at 300 with LinearOutSlowInEasing
0.0f at 600 with LinearOutSlowInEasing
0.0f at 1200 with LinearOutSlowInEasing
},
repeatMode = RepeatMode.Restart
)
)
}
}
val circleValues = circles.map { it.value }
val distance = with(LocalDensity.current) { travelDistance.toPx() }
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(spaceBetween)
) {
circleValues.forEach { value ->
Box(
modifier = Modifier
.size(circleSize)
.graphicsLayer {
translationY = -value * distance
}
.background(
color = circleColor,
shape = CircleShape
)
)
}
}
}
}
How can I solve this? Thanks.

How to change background color of the jetpack compose snackbar?

I want to change solid or gradient color to jetpack compose snack bar. Please guide me how to
change color
Here is my snack bar using material3 compose, I am looking solution to change the background color
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import compose.material.theme.ui.theme.Material3ComposeTheme
import compose.material.theme.ui.theme.Purple40
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
#OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Material3ComposeTheme {
val context = LocalContext.current
val snackState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
Scaffold(
topBar = {
},
content = {
fun launchSnackbar(message: String, actionLabel : String?=null, duration: SnackbarDuration = SnackbarDuration.Short){
scope.launch {
snackState.showSnackbar(message = message,actionLabel=actionLabel, duration=duration)
}
}
Column(
modifier = Modifier
.padding(it)
.verticalScroll(rememberScrollState())
) {
Spacer(modifier = Modifier.height(47.dp))
Text("Snackbar", Modifier.padding(bottom = 10.dp), style = MaterialTheme.typography.labelLarge)
Button(onClick = {
// * Snackbar
launchSnackbar(message = "Hi i am snackbar message", actionLabel = "Hide", duration = SnackbarDuration.Long)
}) { Text("Snackbar",style = MaterialTheme.typography.labelLarge) }
ListDividerPadding()
Text("Toast", Modifier.padding(bottom = 10.dp), style = MaterialTheme.typography.labelLarge)
Button(onClick = {
Toast.makeText(
context,
"Hi i am toast message",
Toast.LENGTH_LONG
).show()
}) { Text("Toast",style = MaterialTheme.typography.labelLarge) }
}
}
)
Box(modifier = Modifier.fillMaxSize(), Alignment.BottomCenter){
SnackbarHost(hostState = snackState)
}
}
}
}
}
You can add SnackBar composable to SnackbarHost and change colors as
SnackbarHost(hostState = snackState) {
Snackbar(
snackbarData = it,
containerColor = Color.Green,
contentColor = Color.Red
)
}
Edit
There is no overload function that takes Brush instead of Color but you can add another Composable as with gradient color or more customization via content: #Composable () -> Unit
#Composable
fun Snackbar(
modifier: Modifier = Modifier,
action: #Composable (() -> Unit)? = null,
dismissAction: #Composable (() -> Unit)? = null,
actionOnNewLine: Boolean = false,
shape: Shape = SnackbarTokens.ContainerShape.toShape(),
containerColor: Color = SnackbarTokens.ContainerColor.toColor(),
contentColor: Color = SnackbarTokens.SupportingTextColor.toColor(),
actionContentColor: Color = SnackbarTokens.ActionLabelTextColor.toColor(),
dismissActionContentColor: Color = SnackbarTokens.IconColor.toColor(),
content: #Composable () -> Unit
)
Can be used as
Snackbar {
Row(
modifier = Modifier.background(
brush = Brush.horizontalGradient(
listOf(
Color.Red,
Color.Green,
Color.Blue
)
)
)
) {
Text("Hello World")
}
}

How to transfer the selected day from the calendar with the selected day to the text?

I'm using a third party calendar library. I tried to do it through the current date, but it didn’t work out. Maybe someone knows how to transfer data from a function to another function? I need the date that the user has selected in the calendar to display as text, but I don't know how to do it (
Gradle
val androidMain by getting {
dependencies {
implementation ("com.himanshoe:kalendar:1.0.0-RC5")
implementation("androidx.core:core-ktx:1.8.0")
implementation("androidx.appcompat:appcompat:1.4.2")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.0")
implementation("androidx.activity:activity-compose:1.5.0")
implementation("androidx.compose.ui:ui:1.2.0-rc03")
implementation("androidx.compose.material:material:1.2.0-rc03")
implementation("androidx.compose.ui:ui-tooling-preview:1.2.0-rc03")
implementation("androidx.compose.material:material-icons-extended:1.2.0-rc03")
implementation("com.google.accompanist:accompanist-systemuicontroller:0.24.13-rc")
implementation("io.coil-kt:coil-compose:2.1.0")
implementation("io.coil-kt:coil-gif:2.1.0")
implementation("io.insert-koin:koin-core:3.2.0")
implementation("io.insert-koin:koin-androidx-compose:3.2.0")
implementation("io.github.alexgladkov:odyssey-core:1.0.0-beta12")
implementation("io.github.alexgladkov:odyssey-compose:1.0.0-beta12")
}
DatePicker
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.himanshoe.kalendar.common.KalendarSelector
import com.himanshoe.kalendar.common.KalendarStyle
import com.himanshoe.kalendar.ui.Kalendar
import com.himanshoe.kalendar.ui.KalendarType
import java.time.LocalDate
#Composable
fun DatePicker(modifier: Modifier = Modifier, onDaySelected: (LocalDate) -> Unit) {
Box(modifier = modifier) {
Kalendar(
kalendarType = KalendarType.Firey(),
kalendarStyle = KalendarStyle(
kalendarBackgroundColor = Color.White,
kalendarColor = Color.White,
kalendarSelector = KalendarSelector.Circle(
selectedColor = Color.Black,
eventTextColor = Color.Black,
todayColor = Color.White,
selectedTextColor = Color.White
),
elevation = 0.dp
),
onCurrentDayClick = { day, _ ->
onDaySelected(day)
})
}
}
SelectedDate
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.happy.R
import java.time.LocalDate
#Composable
fun SelectedDate(
date: LocalDate,
modifier: Modifier = Modifier,
onDateClick: () -> Unit
) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
Row(
modifier = modifier.clickable { onDateClick() },
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = date.toString(),
style = MaterialTheme.typography.body1
)
Spacer(modifier = Modifier.width(8.dp))
Icon(
painter = painterResource(id = R.drawable.ic_calendar_outline_24),
contentDescription = null
)
}
}
}
DashboardUIState
import com.happy.screens.dashboard.presentation.models.TaskUi
import java.time.LocalDate
#Immutable
data class DashboardUiState(
val currentDate:LocalDate = LocalDate.now(),
val dayOfTheWeek: String = "",
val taskList: List<TaskUi> = emptyList()
) {
companion object {
val Empty = DashboardUiState()
}
}
#Immutable
sealed class DashboardUiEvent {
object OnAddTask : DashboardUiEvent()
class OnTaskClick(val id: Int) : DashboardUiEvent()
}
#Immutable
sealed class DashboardUiEffect {
object NavigateToTaskCreation : DashboardUiEffect()
class NavigateToTaskDetails(val id: Int) : DashboardUiEffect()
}
DashboardViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.time.LocalDate
class DashboardViewModel : ViewModel() {
private val _state = MutableStateFlow(DashboardUiState.Empty)
val state = _state.asStateFlow()
private val _effect = MutableSharedFlow<DashboardUiEffect>()
val effect = _effect.asSharedFlow()
init {
_state.update { it.copy(currentDate = LocalDate.now(), dayOfTheWeek = "Сегодня") }
// val exampleList = listOf(
// TaskUi(id = 0, unicode = "\uD83D\uDD25", isDone = true),
// TaskUi(id = 1, unicode = "\uD83D\uDD25", isDone = false),
// TaskUi(id = 0, unicode = "\uD83D\uDD25", isDone = true),
// TaskUi(id = 0, unicode = "\uD83D\uDD25", isDone = true),
// TaskUi(id = 0, unicode = "\uD83D\uDD25", isDone = true),
// TaskUi(id = 0, unicode = "\uD83D\uDD25", isDone = true),
// TaskUi(id = 0, unicode = "\uD83D\uDD25", isDone = true),
// TaskUi(id = 0, unicode = "\uD83D\uDD25", isDone = true),
// TaskUi(id = 0, unicode = "\uD83D\uDD25", isDone = true)
// )
// _state.update { it.copy(taskList = exampleList) }
}
fun sendEvent(event: DashboardUiEvent) {
when (event) {
DashboardUiEvent.OnAddTask -> {
viewModelScope.launch {
_effect.emit(DashboardUiEffect.NavigateToTaskCreation)
}
}
is DashboardUiEvent.OnTaskClick -> {
viewModelScope.launch {
_effect.emit(DashboardUiEffect.NavigateToTaskDetails(event.id))
}
}
}
}
}
DashboardScreen
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.happy.core.navigation.Screens
import com.happy.core.ui.rememberStateWithLifecycle
import com.happy.screens.calendar.DatePicker
import com.happy.screens.calendar.DayOfTheWeekText
import com.happy.screens.calendar.SelectedDate
import com.happy.screens.dashboard.presentation.components.AddTaskButton
import com.happy.screens.dashboard.presentation.components.EmptyTaskMessage
import com.happy.screens.dashboard.presentation.components.ProfileImage
import com.happy.screens.dashboard.presentation.components.TaskItem
import org.koin.androidx.compose.getViewModel
import kotlinx.coroutines.launch
import ru.alexgladkov.odyssey.compose.extensions.present
import ru.alexgladkov.odyssey.compose.local.LocalRootController
import ru.alexgladkov.odyssey.compose.navigation.modal_navigation.ModalSheetConfiguration
import java.time.LocalDate
#Composable
fun DashboardScreen(
) {
DashboardScreen(
viewModel = getViewModel(),
onTaskCreationClick = {},
onTaskClick = {}
)
}
#Composable
private fun DashboardScreen(
viewModel: DashboardViewModel,
onTaskCreationClick: () -> Unit,
onTaskClick: (Int) -> Unit,
) {
val uiState by rememberStateWithLifecycle(viewModel.state)
val systemUiController = rememberSystemUiController()
val coroutineScope = rememberCoroutineScope()
val rootController = LocalRootController.current
val modalController = rootController.findModalController()
val modalSheetConfiguration = ModalSheetConfiguration(
maxHeight = 0.7f,
cornerRadius = 20,
closeOnSwipe = true
)
var date:LocalDate =LocalDate.now()
LaunchedEffect(Unit) {
viewModel.effect.collect { effect ->
when (effect) {
DashboardUiEffect.NavigateToTaskCreation -> onTaskCreationClick()
is DashboardUiEffect.NavigateToTaskDetails -> onTaskClick(effect.id)
}
}
}
SideEffect {
systemUiController.setSystemBarsColor(color = Color.Transparent, darkIcons = true)
}
Box(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding()
) {
Column(modifier = Modifier.fillMaxSize()) {
Row(
modifier = Modifier
.padding(horizontal = 24.dp)
.padding(top = 16.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
SelectedDate(
date = uiState.currentDate
) {
coroutineScope.launch {
modalController.present(modalSheetConfiguration, content = {
DatePicker {
modalController.popBackStack(animate = true)
}
})
}
}
ProfileImage(isAuthorized = false)
}
DayOfTheWeekText(
day = uiState.dayOfTheWeek,
modifier = Modifier.padding(start = 24.dp, top = 12.dp)
)
if (uiState.taskList.isEmpty()) {
EmptyTaskMessage(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 148.dp)
.padding(horizontal = 34.dp)
)
} else {
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(44.dp),
horizontalArrangement = Arrangement.spacedBy(84.dp),
) {
items(uiState.taskList) { task ->
TaskItem(
task = task,
modifier = Modifier
.size(60.dp)
.clickable {
rootController.launch(Screens.TaskList.name)
}
)
}
}
}
}
AddTaskButton(
onClick = { viewModel.sendEvent(DashboardUiEvent.OnAddTask) },
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 16.dp)
)
}
}
DashboardScreen
Since your UI state DashboardUIState is managed by your view model DashboardViewModel, you have to notify DashboardViewModel when the date is changed by the user. The user selects the date in the DatePicker which has a callback onDaySelected: (LocalDate) -> Unit. Inside this callback you can notify your DashboardViewModel that there was a change and that the state should be updated.
In your DashboardScreen update this part of the code
SelectedDate(
date = uiState.currentDate
) {
coroutineScope.launch {
modalController.present(modalSheetConfiguration, content = {
DatePicker { selectedDate ->
modalController.popBackStack(animate = true)
viewModel.updateCurrentDate(selectedDate)
}
})
}
}
In your view model DashboardViewModel add a function that will update the state correctly with the received date value.
fun updateCurrentDate(date: LocalDate) {
val locale = Locale.getDefault() // or set your Locale if the default is not correct
val dayOfTheWeek = date.dayOfWeek.getDisplayName(TextStyle.FULL, locale)
_state.update { it.copy(currentDate = date, dayOfTheWeek = dayOfTheWeek) }
}

Categories

Resources