0

I am writing a small gallery app for my cat. It has a button by clicking on which a new PhotoItem is added to the displayed list, but it appears only after phone rotation and I want it to appear on the screen right after button was clicked.

Right now everything is stored in a mutableList inside savedStateHandle.getStateFlow but I also tried regular MutableStateFlow and mutableStateOf and it didn't help. I havent really used jatpack compose and just can't figure what to do (

App

@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable
fun BebrasPhotosApp() {
    val galaryViewModel = viewModel<GalaryViewModel>()
    val allPhotos by galaryViewModel.loadedPics.collectAsState()
    Scaffold(topBar = { BebraTopAppBar() }, floatingActionButton = {
        FloatingActionButton(
            onClick = { galaryViewModel.addPicture() },
            backgroundColor = MaterialTheme.colors.onBackground
        ) {
            Icon(
                imageVector = Icons.Rounded.Add,
                contentDescription = "Add Photo",
                tint = Color.White,
            )
        }
    }) {
        LazyColumn(modifier = Modifier.background(color = MaterialTheme.colors.background)) {
            items(allPhotos) {
                PhotoItem(bebra = it)
            }
        }
    }

}

ViewModel

class GalaryViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
        val loadedPics = savedStateHandle.getStateFlow(
        "pics", initialValue = mutableListOf<Bebra>(
            Bebra(R.string.photo_1, R.string.desc_1, R.drawable.bebra_pic_1, R.string.add_desc_1),
            Bebra(R.string.photo_2, R.string.desc_2, R.drawable.bebra_pic_2, R.string.add_desc_2),
            Bebra(R.string.photo_3, R.string.desc_3, R.drawable.bebra_pic_3, R.string.add_desc_3)
        )
    )



    fun addPicture() {
        val additionalBebraPhoto = Bebra(
            R.string.photo_placeholder,
            R.string.desc_placeholder,
            R.drawable.placeholder_cat,
            R.string.add_desc_placeholder
        )
        savedStateHandle.get<MutableList<Bebra>>("pics")!!.add(additionalBebraPhoto)
    }
}

PhotoItem

@Composable
fun PhotoItem(bebra: Bebra, modifier: Modifier = Modifier) {
    var expanded by remember { mutableStateOf(false) }
    Card(elevation = 4.dp, modifier = modifier
        .padding(8.dp)
        .clickable { expanded = !expanded }) {
        Column(
            modifier = modifier
                .padding(8.dp)
                .animateContentSize(
                    animationSpec = spring(
                        dampingRatio = Spring.DampingRatioMediumBouncy,
                        stiffness = Spring.StiffnessLow
                    )
                )
        ) {
            Text(
                text = stringResource(id = bebra.PicNumber),
                style = MaterialTheme.typography.h1,
                modifier = modifier.padding(bottom = 8.dp)
            )
            Text(
                text = stringResource(id = bebra.PicDesc),
                style = MaterialTheme.typography.body1,
                modifier = modifier.padding(bottom = 8.dp)
            )
            Image(
                painter = painterResource(id = bebra.Picture),
                contentDescription = stringResource(id = bebra.PicDesc),
                contentScale = ContentScale.Crop,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(256.dp)
                    .clip(RoundedCornerShape(12))
            )
            if (expanded) {
                BebraAdditionalDesc(bebra.additionalDesc)
            }
        }

    }
}

Bebra Data class

data class Bebra(
    @StringRes val PicNumber: Int,
    @StringRes val PicDesc: Int,
    @DrawableRes val Picture: Int,
    @StringRes val additionalDesc: Int
)
Useless
  • 29
  • 3

1 Answers1

0

So, I am also not super familiar with JC, but from first glance it looks like your method, addPicture() - which is called when the user taps on the button, does not update the state, therefore there's no recomposition happening, so the UI does not get updated.

Check:

fun addPicture() {
        // ...

        savedStateHandle.get<MutableList<Bebra>>("pics")!!.add(additionalBebraPhoto)
}

So here you are basically adding a new item to savedStateHandle, which I assume does not trigger a recomposition.

What I think you need to do, is to update loadedPics, somehow.

However, loadedPics is a StateFlow, to be able to update it you would need a MutableStateFlow.

For simplicity, this is how you would do it if you were operating with a list of strings:

// declare MutableStateFlow that can be updated and trigger recomposition
val _loadedPics = MutableStateFlow(
     savedStateHandle.get<MutableList<String>>("pics") ?: mutableListOf()
)

// use this in the JC layout to listen to state changes        
val loadedPics: StateFlow<List<String>> = _loadedPics
        
// addPicture:
val prevList = _loadedPics.value
prevList.add("item")

_loadedPics.value = prevList // triggers recomposition

// here you probably will want to save the item in the
// `savedStateHandle` as you already doing.
VCODE
  • 535
  • 5
  • 19
  • So i need to declare _loadedPics variable in my viewModel, also create loadedpics:StateFlow> = _loadedPics in my PhotosApp.kt file and finaly in addPhoto function replace my code with something like val prevList = _loadedPics.value prevList.add("item") _loadedPics.value = prevList savedStateHandle.get>("pics")!!.add(additionalBebraPhoto) – Useless Feb 17 '23 at 22:26