Paul Woitaschek

    Paul Woitaschek

    1 year ago
    One thing I'm having really difficulties with compose is naming. How do I name my things? Let's say I have a counter: Intuitively, I'd create a function called:
    @Compsable fun Counter()
    But now I need to separate the logic and UI and need to create a counter class that is the actual counter and not the ui. Intuitively I'd call that counter
    class Counter
    . Now having both of these named the same is obviously a bad idea, but what's the solution to this? Consistently suffixing every composable ui function as
    CounterUI
    ? Or
    CounterView
    ? How do you handle this?
    Arkadii Ivanov

    Arkadii Ivanov

    1 year ago
    In such cases I prefer CounterUi
    Paul Woitaschek

    Paul Woitaschek

    1 year ago
    But only on name clashes?
    Arkadii Ivanov

    Arkadii Ivanov

    1 year ago
    Yep
    Specially when there is such a separation
    c

    curioustechizen

    1 year ago
    When you say actual counter, do you mean another Composable? Or the state? If it is the latter you might consider
    CounterState
    .
    Paul Woitaschek

    Paul Woitaschek

    1 year ago
    I mean the state producer, not the state
    And totally off topic: Did you find a way to get rid of the "rename your compose functions lower case" kotlin warning? ^^
    Arkadii Ivanov

    Arkadii Ivanov

    1 year ago
    I thought it is fixed in three latest AS canary, isn't it?
    Paul Woitaschek

    Paul Woitaschek

    1 year ago
    not in -14 at least
    Arkadii Ivanov

    Arkadii Ivanov

    1 year ago
    As a workaround you can disable it for the entire project in the settings
    Paul Woitaschek

    Paul Woitaschek

    1 year ago
    Could you do a review on https://github.com/PaulWoitaschek/Voice/commit/885d1501c283eea9d027fc55febcab928fe93b26 and tell me if that compose usage is correct like I wrote? Its my first attempt on compose and everything is new 🙂
    d

    dewildte

    1 year ago
    I postfix all my Composables with UI. And so far I pass them all a single data class post fixed with UIState
    For example:
    @Composable
    fun HomeScreenUI(
      state: HomeScreenUIState
    ) {
      ...
    }
    Colton Idle

    Colton Idle

    1 year ago
    I have the same problem. Haven't found a good solution, but I don't really like the UI postfix. Then again, I've been struggling with naming my network, database, and domain models as well. So add this to the list. 😄
    v

    Vinay Gaba

    1 year ago
    There’s actually some guidance about naming in general in the API guidelines that’s shared by the Compose team. I don’t think its advertised too much so I would highly recommend checking it out - https://github.com/androidx/androidx/blob/androidx-main/compose/docs/compose-api-guidelines.md
    e

    ephemient

    1 year ago
    it's also a lot easier to deal with naming conflicts in Kotlin than in Java. if you have two different classes named Counter in two different packages and want to use both of them, you can
    import some.package.foo.Counter as FooCounter
    import other.package.bar.Counter as BarCounter
    and use both of them in the same file without having to fully-qualify either of them.
    that, and overlap between function names (which composables are) and class names is OK as long as overload resolution can distinguish a function call from a constructor call
    Paul Woitaschek

    Paul Woitaschek

    1 year ago
    Yeah, I usually do
    import androidx.compose.runtime.Composable as Compostable
    :troll:
    Halil Ozercan

    Halil Ozercan

    1 year ago
    I recently asked a very similar question on Twitter and got a convincing answer https://twitter.com/commonsguy/status/1380535450755084291?s=19
    Paul Woitaschek

    Paul Woitaschek

    1 year ago
    Ah yes that's a pretty good idea. While refactoring, I started that XYZRow pattern anyways because it just made sense.
    Colton Idle

    Colton Idle

    1 year ago
    adding a suffix on the composable based on the presentation
    Ooh. I do like that too. But do any of you have a Ui data class? Let's say I have a Person that has like 9 fields. And a PersonCardData class has 4 fields (some are straight mappings from Person, but some are formatted (e.g. think First Name + Get Middle Initial + last name)) Then I want to display PersonCardData in a PersonCard composable? Does that sound reasonable?
    Paul Woitaschek

    Paul Woitaschek

    1 year ago
    Yes, that's reasonable. I have a settings screen and use the row pattern there because it's a list:https://github.com/PaulWoitaschek/Voice/blob/main/settings/src/main/java/voice/settings/views/autoRewind.kt#L15
    Sean McQuillan [G]

    Sean McQuillan [G]

    1 year ago
    public interface CounterState
    
    // default impl with default state policy
    private class CounterStateImpl: CounterState()
    
    public fun CounterState(): CounterState = CounterStateImp()
    
    @Composable
    public fun Counter(
        state: CounterState = remember { CounterState() }
    ) {
       ...
    }
    Seems to be the default naming convention for this esp. if the class is a state-like object. You can do without the interface, but showing the full pattern as it's often useful in letting you have more control without restricting callers ability to specialize behavior.
    This section covers the pattern more fully https://github.com/androidx/androidx/blob/androidx-main/compose/docs/compose-api-guidelines.md#default-policies-through-hoisted-state-objects Again, the interface is optional - the main thing it wins is the ability to make a default impl without hardcoding the policy.
    All that said, this is just the patterns used in the compose library, other conventions are valid. As long as you don't verb your composables, then we'll have to talk 😂
    This is also a good section to point to: https://github.com/androidx/androidx/blob/androidx-main/compose/docs/compose-api-guidelines.md#extensibility-of-hoisted-state-types Using final state classes bit us a lot in early compose APIs because it made it difficult to create single sources of truths for multiple composables. This came up in Kotlin a lot due to it's (good) preference for final classes which restricted the ability to merge things while making the type system happy. The note in the docs there is spot on though - since replacing a final class with an interface is not a source-breaking change it's a pretty cheap refactor to introduce at any point. Starting with a simple state-like class won't cause problems here most of the time. It's useful to know the full pattern when it's not enough.
    One final note on the topic, we found that composable factories tended to not be the right solution to things that might be instantiated outside of compose (e.g. in a ViewModel) Instead of
    @Composable
    fun makeCounterState()
    Prefer
    fun CounterState()
    The exception to this rule is if
    CounterState
    must always be remembered, you can add a convenience remember-constructor
    @Composable
    fun rememberCounterState() = remember { CounterState() }
    But if there's any chance you'll ever want to instantiate one outside of compose, provide another mechanism 🙂.