https://kotlinlang.org logo
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
  • z

    Zoltan Demant

    07/19/2022, 11:46 AM
    Why does material button specify
    color = contentColor.copy(alpha = 1f)
    and then use
    CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha)
    to provide the actual alpha just mere lines later?
  • j

    juliocbcotta

    07/19/2022, 12:37 PM
    we still can't use kotlin 1.7.10 with compose, right ?Do we need a new release of the compiler?
    👍 2
    j
    c
    a
    • 4
    • 6
  • m

    mattinger

    07/19/2022, 1:20 PM
    Has anyone had any luck running a ComposeRule in a delayed fashion? WIth ActivityScenario, we can create it and launch it when we want. However, rules get executed immediately before the test starts, and as such don’t really give us the opportunity to setup test expectations prior to launching. I’m trying to find some way around this, but haven’t been able to find anything yet.
    j
    i
    • 3
    • 3
  • k

    Kwok Wong

    07/19/2022, 1:28 PM
    Hi, we need some help with a compose ui test. We have a
    LazyColumn
    and have been managed to follow the example here to implement the drag-and-drop operation for the list items. In a compose UI test, we want to test the drag-and-drop behaviours using code below:
    onNodeWithContentDescription("itemDesription", substring = true)
                    .onChild()
                    .performTouchInput {
                        down(bottomCenter)
                        moveTo(percentOffset(0.5f, -0.7f), delayMillis = 1000L)
                        up()
                    }
    What the code would do: 1. Select an item from list (long-click) 2. Move the item one position up However, the operation always fails with the step
    moveTo
    So, we are wondering whether we are using the moveTo API properly. Any suggestions? Regards,
    • 1
    • 1
  • c

    Can Korkmaz

    07/19/2022, 1:58 PM
    Anyways to make the ConstraintLayout barrier in the Compose? I want to make this layout without using box with linearGradent(didn't succeed). Compose CL barrier doesn't have the barrierDirection parameter. Bottom is Divider, and the top is barrier. I attached the xml to thread.
    c
    c
    • 3
    • 6
  • v

    Vivek Modi

    07/19/2022, 4:34 PM
    Hey guys, I am using viewmodel in my compose application. I am calling api in my viewModel through
    viewModelScope.launch
    so when I call in my compose function, should I use any thing else? I am reading Side-effects using LunchEffect and DispoableEffect. Is this neccessary in my composable function? If yes can someone guide me, bcoz I unable to understand this concept. Thanks
    class MainActivityViewModel(private val resultRepository: ResultRepository) : ViewModel() {
    
        init {
            getSportResult()
        }
    
        fun getSportResult() {
            viewModelScope.launch {
                val response = resultRepository.getSportResult()
                
            }
        }
    }
    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun SetupView(viewModel :MainActivityViewModel = koinViewModel()) {
        Scaffold(topBar = {
            TopAppBar(
                title = { Text(text = stringResource(id = R.string.app_name)) },
            )
        }, content = { padding ->
            Column(
                modifier = Modifier
                    .fillMaxSize()
            ) {
                Button(onClick = { /*TODO*/ }) {
                    Text(text = stringResource(id = R.string.get_result))
                }
            }
        })
    }
    Thanks guys..
    z
    • 2
    • 2
  • n

    Nat Strangerweather

    07/19/2022, 5:26 PM
    As a follow up from this conversation I wanted to ask you guys how you are dealing with your overflow menus. I am using a
    DropDownMenu
    but both Material 2 and Material 3 are leaking in Compose. Do you just put up with the memory leak or is there a workaround?
  • r

    raghunandan

    07/20/2022, 3:40 AM
    Would like to hear from folks has anyone tried tokenizing textfield in compose with @mentions. Would love to hear the approach taken.
    z
    • 2
    • 5
  • j

    jasu

    07/20/2022, 4:11 AM
    what’s is proper way to send file upload progress from repository to the compose. I’ve tried sharedFlow, stateFlow nothing seems to be working. One file upload at a time.
    f
    t
    • 3
    • 12
  • k

    Konyaco

    07/20/2022, 6:05 AM
    Hi guys. Is it possible to implement this layout without Constraint Layout or custom layout?
    j
    • 2
    • 2
  • k

    Konyaco

    07/20/2022, 6:17 AM
    And is it possible to implement this layout without custom layout?
    s
    t
    • 3
    • 3
  • s

    ste

    07/20/2022, 7:36 AM
    How can I achieve this particular text blend using
    Canvas
    ? Given the dark blue rect and the black text, how should I blend them? I tried every single
    BlendMode
    , but none of them gave the desired result. I need to whiten the part of the text that overlaps the rect (or, alternatively, clear the part or the rect the text would overlap)
    ✅ 1
    o
    • 2
    • 3
  • s

    Slackbot

    07/20/2022, 10:51 AM
    This message was deleted.
  • l

    Lukasz Kalnik

    07/20/2022, 12:39 PM
    We use
    LazyVerticalGrid
    with items containing some Coil `AsyncImage`s and the scroll is stuttering very hard. Is this a known problem?
    z
    l
    c
    • 4
    • 10
  • v

    Vivek Modi

    07/20/2022, 2:34 PM
    Hey guys, I need advice if I can do that or not in compose. Suppose I have
    A
    screen. Inside that I have
    Button
    . When I click I am calling api and getting data. In this whole process I am passing UI state event i.e.
    IsLoading
    ,
    OnSuccess
    and
    OnError
    . So when I press the
    Button
    the
    IsLoading
    is trigger and when I get data without any problem, I trigger`OnSuccess` with data. So question is 1. When
    IsLoading
    happening I want to called
    A
    screen ->
    B
    Screen. Note:- we are standing on
    B
    screen 2. When I get
    OnSuccess
    with data,
    A
    screen pass to
    B
    Screen again. Is it possible to do that through navigation compose or something else?
  • m

    Mohan manu

    07/20/2022, 4:58 PM
    How do we pass arguments between nested navigation graph. Since we navigate to root not actually the destination.
    i
    • 2
    • 2
  • v

    Vikas Singh

    07/20/2022, 6:26 PM
    How can i control zIndex in a horizontal pager ?
  • a

    andrew

    07/20/2022, 6:34 PM
    Why does onGloballyPosition run on the next composition, and is there any way to get layout coords immediately?
    z
    c
    c
    • 4
    • 55
  • j

    Jasmin Fajkic

    07/20/2022, 8:12 PM
    I have on one screen text input and on back button press i would like to intercept it and run some function. Could not make it with BackHandler. Is there any chance to run custom function on hardware back button press?
    c
    • 2
    • 3
  • s

    Sean Proctor

    07/20/2022, 9:01 PM
    Does anyone know how to get the character value from
    androidx.compose.ui.input.key.Key
    ?
  • c

    Colton Idle

    07/20/2022, 10:28 PM
    compiler:1.3.0-beta01 is out today 😛arty-parrot: • Added support for Kotlin
    1.7.10
    🔟 2
    🎉 13
    r
    i
    • 3
    • 7
  • c

    Colton Idle

    07/20/2022, 10:52 PM
    I need to implement something like this in my app where I can swipe up to swipe the item away. I know some libraries exist for swiping left to right, but does anyone have any ideas for how to implement something like this where I can swipe up to dismiss?
    j
    • 2
    • 2
  • k

    K Merle

    07/21/2022, 8:26 AM
    How to check if current theme is dark, without checking for
    system
    theme?
    z
    o
    • 3
    • 5
  • k

    Kulwinder Singh

    07/21/2022, 9:31 AM
    I need create this kind of Text in compose, can i use AnnotatedString here to Outline first styled text ?
    v
    • 2
    • 1
  • l

    loloof64

    07/21/2022, 9:55 AM
    Hi ! As a experiment and in order to understand why a part of my project does not work, I've created this ColorSquare experiment. 1. It is a simple `@Composable`which outputs a
    Surface
    with solid background color, and a
    Button
    . 2. When the user clicks on the button, the expected behaviour is that the square changes its color. 3. In this experiment, things are a bit more complicated : I'm using a
    ColorSquareState
    class, with a method
    chooseNextColor
    , and I'm storing a `mutableState`on an instance of this class. But when the `chooseNextColor`is called, it does not force recomposition. Why ? Is there a simple workaround keeping the use of `ColorSquareState`as long as method
    chooseNextColor
    ?
    ColorSquare_Composable.cpp
    o
    • 2
    • 3
  • j

    Jasmin Fajkic

    07/21/2022, 12:00 PM
    What is best way to position element at the bottom of screen like position absolute? I have text on top , lazy column and need this box on bottom of this screen.
    • 1
    • 1
  • l

    Lisandro Di Meo

    07/21/2022, 2:27 PM
    It's okay to use launched effects to navigate when a state changes? For example, a login view:
    val isLoginError by loginViewModel.loginError.observeAsState()
        val context = LocalContext.current
        LaunchedEffect(isLoginError){
            when (isLoginError) {
                true -> {
                    Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show()
                }
                false -> navHostController.navigate(Paths.HOME_PATH)
                null -> { // blah }
            }
        }
        LoginScreen(loginViewModel)
    j
    s
    • 3
    • 8
  • j

    Joseph Hawkes-Cates

    07/21/2022, 4:13 PM
    I’m seeing a difference in system back behavior between API 31 and API 30 in my app. I am using navigation compose and I have a home route which is the first or lowest item in the backstack. When the user is on that screen and presses the system back the app is backgrounded as expected. In API 31 when I resume the app I am still on my home route, but in API 30 the Activity is recreated when the app resumes which then causes some unexpected behavior for us in this case. Is this a known behavior of navigation compose and are there any workarounds?
    e
    a
    • 3
    • 6
  • v

    Vivek Modi

    07/21/2022, 4:42 PM
    Hey guys I am using jetpack compose navigation. I am trying to pass arraylist and some changes according to this stackoverflow. I am getting error + my value inside data class will drop as well. Can someone guide me, what I am doing wrong here? I am not adding imports, instead I'll add my class link in the name. NavigationGraph.kt
    private const val NEAREST_RESULT_JSON = "nearestResultJson"
    
    @Composable
    internal fun NavigationGraph() {
        val navController = rememberNavController()
        NavHost(navController = navController, startDestination = ScreenRoute.Home.route) {
            composable(ScreenRoute.Home.route) {
                SetupMainActivityView { nearestResult ->
                    val nearestResultJson = Uri.encode(Gson().toJson(nearestResult))
                    navController.navigate(ScreenRoute.Result.route + "/{$nearestResultJson}")
                }
            }
    
            composable(
                ScreenRoute.Result.route + "/{$NEAREST_RESULT_JSON}",
                arguments = listOf(
                    navArgument(NEAREST_RESULT_JSON) { type = NearestResultParamType() }
                )
            ) { backStackEntry ->
                ResultScreen(backStackEntry.arguments?.getParcelableArrayList(NEAREST_RESULT_JSON))
            }
        }
    }
    c
    i
    c
    • 4
    • 34
  • d

    dimsuz

    07/21/2022, 5:16 PM
    I have a setup where my screen state is formed from various sources which are gathered in the
    Flow<State>
    which then gets collected and rendered. When
    TextField
    emits a value change request — this too is considered a source of change and gets mapped/processed and then ends up in this state flow with actual
    text
    value calculated → rendered. Example usecase: "user inputs
    foo
    , ViewModel receives this request, merges it with some flags and decides to send
    foo-bar
    back to render in the text field". The thing is: if calculating "foo-bar" value consumes some time, then TextField state gets completely messed up, because it emits faster than it renders. In the attached video I simply press and hold the
    1
    key while in code I add some delay before sending it back to the
    TextField
    . See 🧵 for a minimum sample code. Is this something that can be done correctly?
    a
    j
    +2
    • 5
    • 36
Powered by Linen
Title
d

dimsuz

07/21/2022, 5:16 PM
I have a setup where my screen state is formed from various sources which are gathered in the
Flow<State>
which then gets collected and rendered. When
TextField
emits a value change request — this too is considered a source of change and gets mapped/processed and then ends up in this state flow with actual
text
value calculated → rendered. Example usecase: "user inputs
foo
, ViewModel receives this request, merges it with some flags and decides to send
foo-bar
back to render in the text field". The thing is: if calculating "foo-bar" value consumes some time, then TextField state gets completely messed up, because it emits faster than it renders. In the attached video I simply press and hold the
1
key while in code I add some delay before sending it back to the
TextField
. See 🧵 for a minimum sample code. Is this something that can be done correctly?
var state by remember { mutableStateOf(TextFieldValue()) }

val changeRequests = remember {
  MutableSharedFlow<TextFieldValue>(extraBufferCapacity = 50)
}

LaunchedEffect(Unit) {
  changeRequests.onEach { delay(100); state = it }.collect()
}

TextField(value = state, onValueChange = { changeRequests.tryEmit(it) })
a

Ale Stamato

07/21/2022, 6:08 PM
Hi there! What is happening is related to the current internal implementation of TextField which holds 3 copies of your state. These 3 copies should be kept in sync as they represent a tight feedback loop that goes to your IME and back to the textfield. By introducing a delay you’re breaking that loop, making the state to be out of sync. The way to avoid this behaviour is return the control of the TextField immediately. You can defer the async behaviours out of this state update, and maybe updating the value generating a derived state change.
d

dimsuz

07/21/2022, 6:16 PM
Thanks for explanation, I suspected something like this. Two questions: 1. I saw some mutable states in the TextField overload impl which takes a
String
, but here I use only the one with
TextFieldValue
— does it still has those 3 copies? (is this down to the
BasicTextField
?)
2. So there's no way I can receive change "request" then hold on to it and set the result back. For example I could want to implement text filtering in this way, or some other tricks which delay "rendering" of the requested state for the chance to modify it.
I mean that text would have to appear in the text field and there's no way I can substitute "foo" with "foO" without user seeing "foo" 🙂
a

Ale Stamato

07/21/2022, 6:41 PM
1. Yep, the situation im describing applies for both TextField and BasicTextField, it’s part of internal implementation (with TextField only being the Material theme layer on top of BasicTextField)
2. would depend on which type of filtering, you can probably still do something sync that’s quick like
input.copy(text = input.text.filter { !it.isDigit() })
. For other cases in which you have to, for example, launch a coroutine and “hold the state” youre bound to have an issue
d

dimsuz

07/21/2022, 10:42 PM
Understood. My main use case is that I have a flow of "change requests" from the user input, but also occasionally text could change after receiving some network response or another "dependent" field, or some calculation. And so I merge all those sources with the help of
Flow<>
(as I've described). This is quite flexible and reactive, but it seems that this "merging" itself sometimes can create enough delay to drive TextField out of sync. Thank you for explanation! If there will be some plans about improving this situation, I'd be interested to read more through whichever medium 🙂
👍 1
j

Joost Klitsie

07/22/2022, 11:23 AM
@dimsuz you should not delay the state update during typing. This should never be delayed, if you want to transform this then the transformation should happen without delay.
you wouldn't want to wait 100ms after you type to input a character 🙂
d

dimsuz

07/22/2022, 11:41 AM
That
delay(...)
is to model what happens, of course I'm not having it in my code 🙂 The transformation or merging effect sometimes can create delay, see my last message above. Of course you can say "should happen without delay", but the reality is that delays can happen due to slow hardware or software.
👍 1
a

Alexander Ivanov

07/22/2022, 11:42 AM
This should never be delayed, if you want to transform this then the transformation should happen without delay
Real world example would be some sort of validation which happens on Dispatcher other than Dispatchers.Main. Even without validation using Dispatchers.Default to reduce the state (e.g. in ViewModel) is enough to cause such a delay.
j

Joost Klitsie

07/22/2022, 11:45 AM
but then perhaps it should be constructed differently, I do have async things firing on typing, but they do not interfere with the typing itself. Even if you do expensive reformatting of the text, then you should probably find a way to do that without messing up the typing speed. For example you can have a state object for textValue, then you observe that from the formatter, which updates the value if it needs to. The Text element sets its value from this state, and directly updates it
// in ViewModel
val someText = MutableStateFlow("")

init {
	viewModelScope.launch {
		someText.collectLatest {
			formatter.format(it).also { formattedText ->
				someText.update { formattedText }
			}
		}
	}

}

fun updateText(text: String) { someText.update { text } }

// in composable
val someState by someText.collectAsState()

Text(value = someState, onValueChange = { viewModel.updateText(it) })
then during typing you do not experience sluggishness and the formatting will be cancelled if you type more
you can even debounce it like this
someText.debounce(100).collectLatest { <--format-->}
d

dimsuz

07/22/2022, 11:51 AM
Check out the example of merging several sources above. To recap: imagine I have •
flow1: Flow<NetworkResponse>
•
flow2: Flow<UserInput>
(strings) And I want to update field whenever I receive response in additiion to user input. User doesn't type, response comes → field is updated. So I roughly do this:
val text by remember { merge(flow1.map { it.responseText } ,flow2.map { it.text}) }.collectAsState()
TextField(text, onValueChange = { flow2.emit(it) })
That
merge
above can already cause slight delays depending on the device.
j

Joost Klitsie

07/22/2022, 11:52 AM
are you then using the right dispatchers?
like main.immediate
d

dimsuz

07/22/2022, 11:55 AM
It works on
main.immediate
, but not on
main
. And I'd really like to launch flow1 on Dispatchers.IO 🙂
another example would be validation as @Alexander Ivanov described above
s

Sean McQuillan [G]

08/12/2022, 4:20 PM
Hey eng working on TextField ghost bump here
We're trying to understand this async use case precisely
I think there's two bits here and I want to see if I can tease them apart
1. Sometimes you need to do things async based on text state, such as validation. The result of this processing should never interfere with typing 2. Sometimes a network request comes back, and you need to modify text state in response to this network request. This should not interfere with typing, but it does want to modify the displayed text
For #1 I think we're exploring on an API that will resolve that and get the async write-backs out of the way
For #2, we're actually kinda confused about the use case. I can imagine something like autocomplete similar to google docs. What else exists in this space? Specifically: I want to regularly write TextField state based on async results of (text state, network state) while typing is happening Or are we missing something here - do we actually want to say something like: Option B: If the user has not typed, then I want to set TextField state
Really just trying to understand concrete use cases here to make sure we're not designing something out
d

dimsuz

08/12/2022, 6:38 PM
Hi! Thanks for asking! After this thread I thought about this a bit and summed up our use case like this (gonna be a bit lenthy, to describe in detail): We have a `ViewModel`(or call it
Presenter
) which contains all the logic, no android deps, while Compose only does rendering. This
ViewModel
holds the
data class ViewState(text: String, whateverElse: Int)
for the whole screen and it also has
val state: StateFlow<ViewState>
for the renderer (Compose) to consume. On the Compose side we feed
viewState.text
to the
TextField
and
onValueChange
reports changed text to the
ViewModel
→ it updates
ViewState
and renders it. Cycle is complete. But
ViewModel
is also subscribed to other sources for text changes, let's say network request or some other system event can change it. So the
ViewModel
has something like this (in pseudo-code):
merge(textFieldChangeFlow, networkTextFlow, systemTextFlow).collect { newText -> 
  state.update { it.copy(text = newText) 
}
// other actions coming from UI can update text too
validateButtonClickFlow.collect { 
  state.update { it.copy(text = validateAndUpdate(it.text) }
}
The crucial thing here is that we want this
ViewState
in VM to be the single source of truth for the screen, including the text field. So that screen logic in VM always works with the latest state and can update and render it at any time. So whenever text changes in Сompose, we want not only set it to the text field, but update it in VM (which in turn can cause it to render). Or VM even can decide not to update state with the new text value: this way we can implement filters and masks in VM. While also keeping logic of working with other streams there, in one place (!). But if we have the requirement that
TextField
must receive text asap, then we have to use additional
remember { }
on the compose side and
onValueChanged
should update that MutableState and then also send it to the VM. And this leaves us with two sources of truth: local mutable state in compose and ViewState consumed from VM, and while they should be in-sync most of the time, often something does cause them to go out of sync very badly when fast-typing or fast-deleting the text in the field. Perhaps it's those Flow operator roundtrips or the fact that some flows can execute on different dispatchers before being merged on Dispatchers.Main, I am not 100% sure right now. Also this case excludes ability to implement filtering/masking on VM. In my original post here I have tried to reproduce this "out-of-sync" problem with large delays, but I'm not sure that this is actually what happens, but at least it looks very similar, while not depicting out actual setup 100%.
s

Sean McQuillan [G]

08/15/2022, 3:55 PM
Got it - thanks
So fundamentally, if you want something that’s async with typing to be the source of truth, you’ll always end up with dropped keyboard edits if they happen at-the-same-time with some other edit
This does help clarify, it sounds like your requirements are: 1. Ability for the user to always type uninterrupted (uncontrolled) 2. Ability to clobber state on some event (reset)
In the View system, we realized this pattern is usually performed by using the main thread as a mutex, so you do something like:
mutex(ON_MAIN_THREAD)
    if(getText() == expected) setText(...)
This is all really helpful - I think we’re heading in the right direction
thanks!
View count: 14