https://kotlinlang.org logo
Docs
Join the conversationJoin Slack
Channels
100daysofcode
100daysofkotlin
100daysofkotlin-2021
advent-of-code
aem
ai
alexa
algeria
algolialibraries
amsterdam
android
android-architecture
android-databinding
android-studio
androidgithubprojects
androidthings
androidx
androidx-xprocessing
anime
anko
announcements
apollo-kotlin
appintro
arabic
argentina
arkenv
arksemdevteam
armenia
arrow
arrow-contributors
arrow-meta
ass
atlanta
atm17
atrium
austin
australia
austria
awesome-kotlin
ballast
bangladesh
barcelona
bayarea
bazel
beepiz-libraries
belgium
berlin
big-data
books
boston
brazil
brikk
budapest
build
build-tools
bulgaria
bydgoszcz
cambodia
canada
carrat
carrat-dev
carrat-feed
chicago
chile
china
chucker
cincinnati-user-group
cli
clikt
cloudfoundry
cn
cobalt
code-coverage
codeforces
codemash-precompiler
codereview
codingame
codingconventions
coimbatore
collaborations
colombia
colorado
communities
competitive-programming
competitivecoding
compiler
compose
compose-android
compose-desktop
compose-hiring
compose-ios
compose-mp
compose-ui-showcase
compose-wear
compose-web
connect-audit-events
corda
cork
coroutines
couchbase
coursera
croatia
cryptography
cscenter-course-2016
cucumber-bdd
cyprus
czech
dagger
data2viz
databinding
datascience
dckotlin
debugging
decompose
decouple
denmark
deprecated
detekt
detekt-hint
dev-core
dfw
docs-revamped
dokka
domain-driven-design
doodle
dsl
dublin
dutch
eap
eclipse
ecuador
edinburgh
education
effective-kotlin
effectivekotlin
emacs
embedded-kotlin
estatik
event21-community-content
events
exposed
failgood
fb-internal-demo
feed
firebase
flow
fluid-libraries
forkhandles
forum
fosdem
fp-in-kotlin
framework-elide
freenode
french
fritz2
fuchsia
functional
funktionale
gamedev
ge-kotlin
general-advice
georgia
geospatial
german-lang
getting-started
github-workflows-kt
glance
godot-kotlin
google-io
gradle
graphic
graphkool
graphql
graphql-kotlin
graviton-browser
greece
grpc
gsoc
gui
hackathons
hacktoberfest
hamburg
hamkrest
helios
helsinki
hexagon
hibernate
hikari-cp
hire-me
hiring
hongkong
hoplite
http4k
hungary
hyderabad
image-processing
india
indonesia
inkremental
intellij
intellij-plugins
intellij-tricks
internships
introduce-yourself
io
ios
iran
israel
istanbulcoders
italian
jackson-kotlin
jadx
japanese
jasync-sql
java-to-kotlin-refactoring
javadevelopers
javafx
javalin
javascript
jdbi
jhipster-kotlin
jobsworldwide
jpa
jshdq
juul-libraries
jvm-ir-backend-feedback
jxadapter
k2-early-adopters
kaal
kafka
kakao
kalasim
kapt
karachi
karg
karlsruhe
kash_shell
kaskade
kbuild
kdbc
kgen-doc-tools
kgraphql
kinta
klaxon
klock
kloudformation
kmdc
kmm-español
kmongo
knbt
knote
koalaql
koans
kobalt
kobweb
kodein
kodex
kohesive
koin
koin-dev
komapper
kondor-json
kong
kontent
kontributors
korau
korean
korge
korim
korio
korlibs
korte
kotest
kotest-contributors
kotless
kotlick
kotlin-asia
kotlin-beam
kotlin-by-example
kotlin-csv
kotlin-data-storage
kotlin-foundation
kotlin-fuel
kotlin-in-action
kotlin-inject
kotlin-latam
kotlin-logging
kotlin-multiplatform-contest
kotlin-mumbai
kotlin-native
kotlin-pakistan
kotlin-plugin
kotlin-pune
kotlin-roadmap
kotlin-samples
kotlin-sap
kotlin-serbia
kotlin-spark
kotlin-szeged
kotlin-website
kotlinacademy
kotlinbot
kotlinconf
kotlindl
kotlinforbeginners
kotlingforbeginners
kotlinlondon
kotlinmad
kotlinprogrammers
kotlinsu
kotlintest
kotlintest-devs
kotlintlv
kotlinultimatechallenge
kotlinx-datetime
kotlinx-files
kotlinx-html
kotrix
kotson
kovenant
kprompt
kraph
krawler
kroto-plus
ksp
ktcc
ktfmt
ktlint
ktor
ktp
kubed
kug-leads
kug-torino
kvision
kweb
lambdaworld_cadiz
lanark
language-evolution
language-proposals
latvia
leakcanary
leedskotlinusergroup
lets-have-fun
libgdx
libkgd
library-development
linkeddata
lithuania
london
losangeles
lottie
love
lychee
macedonia
machinelearningbawas
madrid
malaysia
mathematics
meetkotlin
memes
meta
metro-detroit
mexico
miami
micronaut
minnesota
minutest
mirror
mockk
moko
moldova
monsterpuzzle
montreal
moonbean
morocco
motionlayout
mpapt
mu
multiplatform
mumbai
munich
mvikotlin
mvrx
myndocs-oauth2-server
naming
navigation-architecture-component
nepal
new-mexico
new-zealand
newname
nigeria
nodejs
norway
npm-publish
nyc
oceania
ohio-kotlin-users
oldenburg
oolong
opensource
orbit-mvi
osgi
otpisani
package-search
pakistan
panamá
pattern-matching
pbandk
pdx
peru
philippines
phoenix
pinoy
pocketgitclient
polish
popkorn
portugal
practical-functional-programming
proguard
prozis-android-backup
pyhsikal
python
python-contributors
quasar
random
re
react
reaktive
realm
realworldkotlin
reductor
reduks
redux
redux-kotlin
refactoring-to-kotlin
reflect
refreshversions
reports
result
rethink
revolver
rhein-main
rocksdb
romania
room
rpi-pico
rsocket
russian
russian_feed
russian-kotlinasfirst
rx
rxjava
san-diego
science
scotland
scrcast
scrimage
script
scripting
seattle
serialization
server
sg-user-group
singapore
skia-wasm-interop-temp
skrape-it
slovak
snake
sofl-user-group
southafrica
spacemacs
spain
spanish
speaking
spek
spin
splitties
spotify-mobius
spring
spring-security
squarelibraries
stackoverflow
stacks
stayhungrystayfoolish
stdlib
stlouis
strife-discord-lib
strikt
students
stuttgart
sudan
swagger-gradle-codegen
swarm
sweden
swing
swiss-user-group
switzerland
talking-kotlin
tallinn
tampa
teamcity
tegal
tempe
tensorflow
terminal
test
testing
testtestest
texas
tgbotapi
thailand
tornadofx
touchlab-tools
training
tricity-kotlin-user-group
trójmiasto
truth
tunisia
turkey
turkiye
twitter-feed
uae
udacityindia
uk
ukrainian
uniflow
unkonf
uruguay
utah
uuid
vancouver
vankotlin
vertx
videos
vienna
vietnam
vim
vkug
vuejs
web-mpp
webassembly
webrtc
wimix_sentry
wwdc
zircon
Powered by Linen
compose
  • a

    allan.conda

    12/16/2020, 12:34 PM
    What’s the best practice for triggering snackbars (outside onClick)?
    LaunchedEffect(error) {
      snackbarState.showSnackbar(error.message)
    }
    I have this but this doesn’t run if exactly the same error happens.
    n
    t
    +2
    • 5
    • 36
  • t

    Tony Kazanjian

    12/16/2020, 6:23 PM
    Hi all! I'm new to Compose and was wondering how the typical ViewModel approach (i.e. one fragment/activity -> one ViewModel) applies to Compose. If my entire app is a single activity that navigates through composables, and each composable would have different data for it's state that is fetched via a coroutine in a ViewModel, would I then inject every single VM I'd need for the whole app inside the Activity? That doesn't seem right.
    i
    r
    b
    • 4
    • 13
  • v

    Vadim

    12/16/2020, 6:55 PM
    I have a question about using `imageResource`/`vectorResource` in older Android SDK versions. I have an icon
    Icon(vectorResource(R.drawable.ic_accessory_arrow))
    where
    ic_accessory_arrow
    is a vector drawable. This code crashes on API 21 with
    android.content.res.Resources$NotFoundException: File res/drawable-xxhdpi-v4/ic_accessory_arrow.png from xml type xml resource ID #0x7f0800e3
    exception. So, what should I do for older SDKs? Write it like that?
    // Random number here: I don't even know which API versions implicitly convert vector drawables to png
    if (Build.VERSION.SDK_INT > 23) { 
        Icon(vectorResource(…)) 
    } else { 
        Icon(imageResource(…)) 
    }
    n
    • 2
    • 3
  • n

    Nemanja Scepanovic

    12/16/2020, 8:48 PM
    Hey guys, quick question, why does the app crash when
    DialogFragment
    , created with composables, when explicitly dismissed by some callback (ex. button click)? This composable has
    ScollableColumn/LazyColumn
    , and if I replace it with a regular
    Column
    everything is working? Stacktrace in the thread. Cheers 😄
    • 1
    • 1
  • s

    Sam

    12/16/2020, 9:01 PM
    Hi everyone, it appears
    alpha09
    was released today 🥳🥳🥳 check out the latest here https://developer.android.com/jetpack/androidx/versions/all-channel
    🎉 2
    t
    • 2
    • 1
  • t

    tcracknell

    12/16/2020, 9:05 PM
    Hello! The latest Jetpack Compose minor release, alpha09, is here! You can get it now from Google’s Maven Repository. Release notes are available at https://developer.android.com/jetpack/androidx/versions/all-channel. Important note #1: You will need to update your Kotlin compiler version to 1.4.21 for alpha09. Important note #2: This is the final release for 2020. Have a happy holiday and we’ll see you again in January.
    🎄 5
    🙏🏼 10
    🎉 28
    👍 5
    😍 11
    😱 6
    s
    c
    r
    • 4
    • 5
  • d

    Doris Liu

    12/16/2020, 11:12 PM
    Hi everyone, we've recently redesigned
    Transition
    animation to make it easier to use and more extensible. tl;dr No more `PropKey`s and `transitionDefinition`s, and instead animations can be declared dynamically. 🎉 Please give the new Transition (available since alpha 09) a try. I would love to hear what you think! 🙏 (P.S. Seeking isn't supported yet, but dynamic target values (e.g. theme-specific values) are now supported.)
    :mind-blown: 10
    😍 23
    👍 22
    🎉 10
    r
    a
    +2
    • 5
    • 23
  • r

    robnik

    12/16/2020, 11:19 PM
    When I try to create a ViewModel in a Composable I get this error: "java.lang.ClassCastException: java.lang.Object cannot be cast to androidx.lifecycle.ViewModel" The line is as show in the docs:
    val model: ContactSellerViewModel by viewModel()
    Any ideas?
    s
    • 2
    • 6
  • m

    matvei

    12/17/2020, 1:04 PM
    Oh, also in the release above ^ Nested Scrolling is available! I remember people in this channel asked about collapsing toolbars, collapsing calendars and proper backdrop supports. You can do all that right now in alpha09. check it out, the entrance point for this APIs is
    Modifier.nestedScroll
    . Basic components like
    LazyColumn
    and
    BackdropScaffold
    already support this, but you can do even more yourself! Let me know if you have any feedback or feature requests 🙂
    📜 4
    🎉 14
    p
    c
    +2
    • 5
    • 16
  • j

    Joost Klitsie

    12/17/2020, 1:48 PM
    Hi guys! I have created an indeterminate border modifier, which can basically animate any shape you give it as an indeterminate loading spinner, for example along the contoures of a button. I created this ProgressBorderModifier based on a pre-compose version of the same concept I once made, but for the animation I now could copy (I mean get inspired by) the CircularProgressIndicator and I also looked at the Border Modifier to see how to draw it in the end. So my question: Within androidx.compose.foundation.Border.kt there is a BorderModifier private class. This overrides the equals, and I was curious as to why is that exactly? (I am guessing so compose will figure out when to redraw/cache the object as a form of optimization) Also if we compare that to my own ProgressBorder: https://github.com/joost-klitsie/compose-progress-button/blob/master/app/src/main/java/com/klitsie/progressbutton/ui/ProgressBorder.kt Should I also implement this somehow as a feat of optimization? I guess my progress border is perhaps a bit different, as it will continuously needs to be redrawn when it is visible, as opposed to a static button that only needs to be drawn once.
    device-2020-12-17-143208.mp4
    👍🏻 2
    😎 14
    🤩 7
    a
    • 2
    • 1
  • j

    John O'Reilly

    12/17/2020, 5:24 PM
    In SwiftUI if you do something like following (no params to
    padding()
    then default system padding values are used (using
    Modifier.padding()
    ). In Compose it looks like
    0dp
    is used if no parameters are passed. Would be nice I think to use SwiftUI's approach here..
    VStack {
        ....
    }
    .padding()
    b
    j
    k
    • 4
    • 23
  • r

    robnik

    12/17/2020, 6:01 PM
    Is there a way to tell
    Text
    to wrap to a second line to avoid pushing things off screen? Inside a screen-wide Column I have:
    Row {
       Text("A somewhat long line that needs to wrap probably")
       Spacer(Modifier.width(3.dp))
       Switch(model.foo, { model.foo = it })
    }
    The Text and Switch are overlapping a bit, and the Switch half pushed off screen.
    v
    l
    • 3
    • 7
  • j

    John O'Reilly

    12/17/2020, 7:58 PM
    Next up on SwiftUI/Compose comparison 🙂 ....in SwiftUI I can apply a font to all
    Text
    elements within something like
    HStack
    as shown below. Is anything similar possible right now with Compose (or planned)?
    HStack {
        Text("some text")
        Text("some more text")
    }
    .font(.caption)
    b
    k
    • 3
    • 5
  • z

    zoha131

    12/17/2020, 8:50 PM
    Iike
    suspend
    if something goes wrong inside
    composable
    function and app crashes then AS don’t provide any fatal error in the logcat. Is there any way to get the fatal report from AS?
    g
    • 2
    • 5
  • d

    Dominaezzz

    12/17/2020, 8:56 PM
    Has anyone tried
    LazyColum(reverseLayout=true)
    with
    VerticalScrollbar(...)
    ? (Mine goes in the wrong direction)
    i
    • 2
    • 1
  • d

    Denis

    12/17/2020, 10:42 PM
    How to properly use
    Modifier.scale
    (or
    .graphicsLayer
    with `scaleX`/`scaleY` parameters)? Here's a sample code:
    @Preview
    @Composable
    fun TestScale() {
        Row(Modifier.fillMaxSize()) {
            val origin0 = TransformOrigin(0f, 0f)
            Box(Modifier.graphicsLayer(scaleX = .5f, scaleY = .5f, transformOrigin = origin0)) {
                SomeComposable(Color.Cyan)
            }
            Box(Modifier.graphicsLayer(scaleX = 2f, scaleY = 2f, transformOrigin = origin0)) {
                SomeComposable(Color.Green)
            }
            Box(Modifier.graphicsLayer(scaleX = 3f, scaleY = 3f, transformOrigin = origin0)) {
                SomeComposable(Color.Yellow)
            }
        }
    }
    
    @Composable
    fun SomeComposable(color: Color) {
        Box(modifier = Modifier.background(color).padding(16.dp)) {
            Text("OK")
        }
    }
    I want it to draw 3 boxes side-by-side but it looks like it doesn't respect scale at all. And there are those original size wireframes drawn when you hover over the the boxes. Do I need to write a custom layout or is there anything wrong with my code?
    r
    • 2
    • 4
  • m

    Madhava

    12/18/2020, 1:03 AM
    Hi all!! I just wanted to say Thank You to everyone here who helped make JetPack Compose and get it stable with the recent releases. I have launched an app which I just built with alpha-09 https://play.google.com/store/apps/details?id=au.net.swc.calc&hl=en&gl=US It was risky starting this app at the start of the year not knowing when Jetpack Compose would be stable, and then COVID hit but I stuck with it instead of re-writing and im proud to say it basically works and its fairly stable and its all the time and budget I had to invest in it.
    👍 4
    🎉 5
    c
    • 2
    • 2
  • c

    Chethan

    12/18/2020, 5:02 AM
    How to give character limit to OutlinedTextField or TextField ?? I do not see this attribute to set
    s
    • 2
    • 2
  • a

    Afzal Najam

    12/18/2020, 6:11 AM
    Now that I'm trying to convert from my own navigation code (based on https://github.com/android/compose-samples/blob/main/JetNews/app/src/main/java/com/example/jetnews/ui/Navigation.kt), to navigation-compose. I'm finding all these navigate() calls to be somewhat non-declarative. Anyone else feel this way?
    r
    • 2
    • 4
  • l

    len

    12/18/2020, 11:18 AM
    Why is
    FlowRow
    being removed? It felt nice for things like a list of chips. Is a replacement planned? I don't really like the idea of creating a custom layout for it.
    ➕ 9
    m
    a
    r
    • 4
    • 5
  • r

    Raul Portales

    12/18/2020, 12:32 PM
    Hi! I am trying to integrate compose into a large monorepo + multi-module app (I have what I want on a separate compose project that works fine). I have migrated to kotlin 1.4.2 and also updated the compiler to use IR. The app compiles, but it crashes in runtime with a class not found exception.
    java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/lifecycle/ViewTreeLifecycleOwner
    I updated the dependencies to use
    androidx.lifecycle:lifecycle-common-java8
    instead of
    androidx.lifecycle:lifecycle-compiler
    and
    androidx.lifecycle:lifecycle-common
    but the crash is still happening. Any ideas of what can I be missing?
    a
    z
    • 3
    • 5
  • l

    Lilly

    12/18/2020, 3:00 PM
    Hi, what advantages would I have when I collect a flow directly in composable function instead of collecting it in view model? And when would I prefer one over the other?
    a
    r
    • 3
    • 23
  • z

    ziv kesten

    12/18/2020, 3:15 PM
    Is there a way to create sticky headers in compose?
    v
    m
    k
    • 4
    • 8
  • h

    Hitanshu Dhawan

    12/18/2020, 5:13 PM
    Hey, what’s the best way to smooth scroll to a particular position in
    LazyColumnFor
    I’m using alpha08, and I can find methods like
    LazyListState.snapToItemIndex
    but this is without smooth scrolling and the method
    LazyListState.smoothScrollBy
    doesn’t take the index. How to smooth scroll to a specific index? Does the API exists for this right now?
    a
    d
    m
    • 4
    • 5
  • c

    Casey Brooks

    12/18/2020, 5:22 PM
    More of a meta-question: why does Compose use an
    @Composable
    annotation instead of a
    composable
    keyword, and might this change in the future? Compose goes way past the normal annotation-processing, and the semantics of composable functions seem to follow more closely with the
    suspend
    keyword. And Facebook’s new AI library adds a
    differentiable fun
    keyword so it seems natural that Compose would support a
    composable fun
    keyword as well.
    👍 3
    v
    • 2
    • 1
  • d

    Danilo Herrera

    12/18/2020, 6:01 PM
    Happy Friday! I hadn't used Compose since the first alpha. Managed to update the dependencies and successfully run my sample project, but can't seem to find the Preview tab. Was there a change to how we view composables annotated with @Preview? 🤔
    r
    c
    a
    • 4
    • 7
  • l

    Lilly

    12/18/2020, 8:36 PM
    Recently someone posted here a link to a library about composables and shimmer effect. I searched for
    shimmer
    in this channel but the most recent result is from Mai. Maybe the author used another keyword than
    shimmer
    when posted it. Does someone have an idea?
    r
    c
    +2
    • 5
    • 7
  • u

    3bdoelnaggar

    12/18/2020, 9:05 PM
    Is the stack layout gone somewhere? alpha9
    c
    r
    • 3
    • 2
  • l

    lilypuchi

    12/19/2020, 6:30 AM
    With
    1.0.0alpha-09
    Since `Dp.VectorConverter` and similar converters have both the deprecated (at
    compose.animation
    ) and relocated version (at
    compose.animation.core
    ) with the same name, I’m getting the Overload resolution ambiguity error and unable to build ahead. 🤔
    d
    • 2
    • 4
  • l

    Lilly

    12/19/2020, 2:04 PM
    Is there any difference between
    val devices = remember { mutableStateListOf<DeviceModel>() }
    
        LaunchedEffect(subject = viewModel.devices, block = {
            viewModel.devices.collect { model ->
                if (!devices.contains(model)) devices.add(model)
            }
        })
    and
    val devices by produceState(initialValue = mutableStateListOf<DeviceModel>(), viewModel) {
            viewModel.devices.collect { model ->
                if (!value.contains(model)) value.add(model)
            }
        }
    a
    d
    a
    • 4
    • 38
Powered by Linen
Title
l

Lilly

12/19/2020, 2:04 PM
Is there any difference between
val devices = remember { mutableStateListOf<DeviceModel>() }

    LaunchedEffect(subject = viewModel.devices, block = {
        viewModel.devices.collect { model ->
            if (!devices.contains(model)) devices.add(model)
        }
    })
and
val devices by produceState(initialValue = mutableStateListOf<DeviceModel>(), viewModel) {
        viewModel.devices.collect { model ->
            if (!value.contains(model)) value.add(model)
        }
    }
a

allan.conda

12/19/2020, 2:17 PM
check the source and you’ll see how it’s different
👍 1
second one only runs a LaunchedEffect once per composition even if devices state changed, it seems
l

Lilly

12/19/2020, 3:55 PM
🤦‍♀️ my bad
@allan.conda Are you aware of an elegant way to only have unique objects in the
mutableStateList
?
👍 1
d

Dominaezzz

12/19/2020, 5:09 PM
The second one is slightly less efficient. Or at least add a bit of unnecessary complexity.
Because you never change the state object.
➕ 1
Consider using
List<DeviceModel>
for the second case, then they become more comparable.
👍 1
a

Adam Powell

12/19/2020, 5:12 PM
or
PersistentList
, I think
MutableStateList
uses the persistent collections under the hood
➕ 1
d

Dominaezzz

12/19/2020, 5:12 PM
To keep the objects unique you can use a
LinkedHashSet
.
l

Lilly

12/20/2020, 4:14 AM
Thanks guys. @Dominaezzz @Adam Powell When I use
linkedSetOf
or
mutableListOf
for second snippt, no items are shown which makes sense, because I don't do
value = device
so
devices
doesn't change but when I use
mutableStateListOf
and do
value.add(device)
,
devices
changed. Or am I wrong? Btw...it seems that
PersistentList
doesn't exist, same for
MutableStateList
?!
a

Adam Powell

12/20/2020, 4:22 AM
PersistentList comes from the kotlinx.collections.immutable artifact; by MutableStateList I mean the object returned by mutableStateListOf. The analogy here is
var list = mutableListOf()
- you don't need both the list and the reference to the list to be mutable
Pick one;
produceState
is about that leading
var
Your first snippet is about the collection being mutable but the reference to it immutable; probably what you want here
l

Lilly

12/20/2020, 4:27 AM
Waht do you mean with
produceState
 is about that leading 
var
a

Adam Powell

12/20/2020, 4:30 AM
produceState
is just a convenience around
val state = remember { mutableStateOf(initialValue) }
LaunchedEffect {
  state.block()
}
return state
It gives you one observable mutable variable that the provided block feeds
But you don't want one observable mutable value, you want one observable mutable list
l

Lilly

12/20/2020, 4:33 AM
ok I guess I got it. In second snippet both the list and the reference is mutable
a

Adam Powell

12/20/2020, 4:33 AM
Yep
l

Lilly

12/20/2020, 4:34 AM
The first one fits definetely better here 🙂
a

Adam Powell

12/20/2020, 4:34 AM
I think so too 🙂
l

Lilly

12/20/2020, 4:36 AM
But I still have the problem with duplicates 😕
a

Adam Powell

12/20/2020, 5:00 AM
we've only got
mutableStateListOf
and
mutableStateMapOf
for snapshot-observable collections at the moment, we don't have a set. If you combine
PersistentSet
from kotlinx.collections.immutable and
produceState
from compose you should have what you need
which is to say,
PersistentSet
is an efficient immutable set type that potentially returns a new set instance whenever you add or remove from it, and if you store that reference in a
mutableStateOf
holder, you get the observability you need with an efficient immutable set implementation
l

Lilly

12/21/2020, 3:56 AM
@Adam Powell Couldn't I also add the device directly to the LazyColumn instead of adding it to a list/set first? 🤔
a

Adam Powell

12/21/2020, 4:01 AM
a LazyColumn (and any composable function, really) just reads from state you supply. Once this patch lands, you can use any backing state type you like, but there will still be no concept of adding to a data structure private to/owned by LazyColumn itself https://android-review.googlesource.com/c/platform/frameworks/support/+/1530327
l

Lilly

12/21/2020, 9:43 PM
@Adam Powell Alright thanks. Lets say a device is collected which is already in list/set but I would like to replace the new one with the existing one because the new one has updates fields, how would you achieve this?
I could remove the existing one and add the new one but this would trigger recompose twice or?
a

Adam Powell

12/21/2020, 10:42 PM
When you add or remove from a persistent collection, it returns a new immutable collection instance that copies as little as possible from the original while sharing internal structure. Once you perform the manipulation you need, assign the new collection to a MutableState object once.
Either way though, if you want to perform several snapshot state updates atomically with regard to one another, see
withMutableSnapshot
. Any changes you make within an explicit mutable snapshot all happen together or not at all, and changes aren't made visible to other threads unless you commit all changes together or otherwise pass the snapshot between threads explicitly.
Compose will only recompose for full snapshot commits, not for partial snapshots.
l

Lilly

12/21/2020, 11:12 PM
I still get duplicates:
val devices = remember { mutableStateOf(persistentListOf<DeviceModel>()) }

    LaunchedEffect(subject = viewModel.deviceFlow, block = {
        viewModel.deviceFlow.collect { model ->
            if (!devices.value.contains(model)) {
                devices.value = devices.value.add(model)
            }
        }
    })

    BluetoothDeviceListComponent(devices.value, onConnectDevice)
What do I wrong? :/
d

Dominaezzz

12/21/2020, 11:15 PM
Does
DeviceModel
have an
equals
implementation?
l

Lilly

12/21/2020, 11:18 PM
data class DeviceModel(val bluetoothDevice: BluetoothDevice, val rssi: Int)
data classes generate equals functions right?
a

Adam Powell

12/21/2020, 11:20 PM
They do, so if the rssi keeps changing, they aren't equal and it will look like a new object to that code 🙂
Remove the element with the same unique id, or use a map with the unique id as the key
l

Lilly

12/22/2020, 1:03 AM
@Dominaezzz Thanks for the hint. To just get rid of the duplicates I only had to override the equals function:
data class BluetoothDeviceModel(val bluetoothDevice: BluetoothDevice, val rssi: Int) {

        override fun equals(other: Any?): Boolean {
            if (other is BluetoothDeviceModel) {
                return this.bluetoothDevice.address == other.bluetoothDevice.address
            }
            return false
        }
   }
Additionally when I want to update the list with newer devices there are multiple options. To preserve the order of the items I came up with this solution:
val devices = remember { mutableStateListOf<DiscoverBluetoothDevicesUseCase.BluetoothDeviceModel>() }

    LaunchedEffect(subject = viewModel.deviceFlow, block = {
        viewModel.deviceFlow.collect { model ->
            withMutableSnapshot {
                devices.indexOf(model).takeIf { it != -1 }?.let { i ->
                    devices.remove(devices[i])
                    devices.add(i, model)
                } ?: devices.add(model)
            }
        }
    })
@Adam Powell Is this implementation legit regarding performance and do I use
withMutableSnapshot
properly?
a

Adam Powell

12/22/2020, 1:47 AM
That'll do fine
👍 1
View count: 12