Thread
#compose
    s

    Se7eN

    1 year ago
    @Composable
    fun Main() {
        val state = remember { PaletteState(...) }
        Scaffold(
            topBar = { TopBar(onSave = { viewModel.save(state.toPalette()) } }
        ) {
            ColorWheel(state)
        }
    }
    
    @Composable
    fun ColorWheel(state: PaletteState) {
        val colorWheel = rememeber { ColorWheel(...) }
        state.colors = colorWheel.getColors()
    
        Content(colorWheel)
    }
    I have a
    state
    that needs to be updated by a child composable. Is this the right way to do it?
    sindrenm

    sindrenm

    1 year ago
    I think I would rather pass in a callback to the child (
    onColorSelected: (Color) -> Unit
    or something) to the child.
    Lukas Sztefek

    Lukas Sztefek

    1 year ago
    Better pass just a lambda into the child component:
    fun ColorWheel(onPalletteChanged: (PaletteState) -> Unit) {
    s

    Se7eN

    1 year ago
    I could do that but the main concern is that I don't really have a way to get the initial colors of the color wheel. What can I do about that?
    And calling
    onPaletteChanged()
    or
    state.colors = colorWheel.getColors()
    without a callback doesn't seem right
    Lukas Sztefek

    Lukas Sztefek

    1 year ago
    Pass current colors as an parameter to the child component?
    s

    Se7eN

    1 year ago
    The child is responsible for initializing the palette using a color scheme
    Lukas Sztefek

    Lukas Sztefek

    1 year ago
    You’re saying
    I don't really have a way to get the initial colors of the color wheel
    but also
    The child is responsible for initializing the palette
    . This statements are going against each other aren’t they? Colors != palette?
    s

    Se7eN

    1 year ago
    Like the child creates an object which initializes the palette and stores the drag positions of the color picker. Then there's a drag modifier where I can call onPaletteChanged. But I don't know where to call onPaletteChanged for the initial colors. Do I call onPaletteChanged in the
    init { }
    of the class that is responsible for initalizing the palette?
    And yeah the palette is just a List<Color>
    jim

    jim

    1 year ago
    You've got your state ownership inverted. The
    ColorWheel
    should not be the one who controls the initial colors; if the parent needs to maintain the current color, then they should own that state, including owning the initial color.
    It is a little hard to understand your code snippets above without being able to see into the types of your data structures, but the important thing is that the parent controls the child. Whatever the parent chooses as the initial color is the one that happens to be selected when the ColorWheel is first opened, but the app owns the data that the child renders. Your result should probably look something more like one of the following:
    fun ColorWheel(palette: List<Color>, currentColor: Color, onColorSelected: (Color) -> Unit) {
       ...
    }
    Or:
    fun ColorWheel(palette: List<Color>, onPaletteSelected: (List<Color>) -> Unit) {
       ...
    }
    Or whatever it is exactly your widget is allowing you to select. The key is that the child is just rendering the state the parent passes down, and then informing the parent when the child thinks that state should be changed. The parent owns the data. When first getting started with Compose, it's often useful to ask yourself "How would I do this if I were only permitted to use immutable data structures", as that will often lead you to the proper data flow.
    s

    Se7eN

    1 year ago
    I'm generating the palette (list of colors) using a color scheme and a few other parameters.
    class ColorWheel(hueDistance, numColors, ...) {
        // Initialize the list of colors using hueDistance and numColors and a few other parameters
    }
    That's why I can't pass any initial color list to the color wheel. You think I should do the initial palette calculation outside the
    ColorWheel
    ?
    Lukas Sztefek

    Lukas Sztefek

    1 year ago
    Jim was first ⬆️, but it this might help you understand how to deal with lambdas:
    class PaletteState(
        var colors: List<Color>
    )
    
    @Composable
    fun Main() {
        val paletteState = remember { PaletteState(listOf(Color.Red, Color.Blue)) }
    
        ColorWheel(
            currentColors = paletteState.colors,
            onColorsChanged = { paletteState.colors = it }
        )
    }
    
    @Composable
    fun ColorWheel(currentColors: List<Color>, onColorsChanged: (List<Color>) -> Unit) {
        Content(
            currentColors = currentColors,
            onDrag = { selectedColors -> onColorsChanged(selectedColors) }
        )
    }
    s

    Se7eN

    1 year ago
    Yep I know lambdas but that wasn't my question. I didn't phrase it correctly
    Lukas Sztefek

    Lukas Sztefek

    1 year ago
    Oh I now understand what do you want to achieve. You want to “extract” computed colors from ColorWheel on save action. I see no other option then compute & hold colors in Main composable and pass it to ColorWheel.
    jim

    jim

    1 year ago
    Yes, you can hoist the initial palette calculation outside the 
    ColorWheel
    , perhaps into something like
    PaletteState
    or
    ColorWheelState
    , but keep these things immutable and just re-create them any time one of the parameters changes. That will keep your data flow correct.
    s

    Se7eN

    1 year ago
    Yep got it. I didn't think about this when writing the ColorWheel class. Guess I gotta refactor it to fit the compose way. Thanks for the help guys!