https://kotlinlang.org logo
Title
p

Paul Nogas

02/26/2021, 4:35 PM
Hi, I'm playing around with jb-compose and seem to be getting in a recomposition loop when I do my "toggle dark mode" from the menu bar but not when I do it from a androidx.compose.material.Swtich in the UI. I've tried wrapping multiple things in remember {} but it doesn't seem to help. Can someone confirm if I'm doing something wrong or if this is a bug in jb-compose? Project is on Github and here's the composable in question https://github.com/pnogas/TempProject/blob/main/src/main/kotlin/com/paulnogas/log/analyzer/WindowActionManager.kt#L38
j

jim

02/26/2021, 4:49 PM
You're calling
darkModeViewModel.toggleDarkMode()
from within a composable function? That means every time you recompose, you want to flip to/from dark mode, which of course is going to cause a recomposition, and then cause it to flip again, and keep flipping forever.
Perhaps you wanted something like:
Button(onClick={darkModeViewModel.toggleDarkMode()})
p

Paul Nogas

02/26/2021, 6:23 PM
right, the button works fine, but I want to do this from menu bar / keyboard shortcut. So is this a limitation from jb-compose?
j

jim

02/26/2021, 6:38 PM
No, it is not a limitation of jb-compose, you just haven't adopted the "thinking in Compose" mental model yet. This is normal, it usually takes 3-6 months of usage before people have the "💡ah-ha , holly-shit this is good" moment. Until then, you will struggle with it, you will fight it, it will frustrate you, and then it will all click in your mind six months later. If there was anything we could do to improve the learning curve, we would, it's just a different way of thinking about programming. Think about how you almost certainly felt about
for
loops or
exceptions
when you were taking your very first programming class, it was probably a bit mind bending to start thinking the way a computer thinks. The entire Android team was fighting me for months when I first proposed Compose, until it finally clicked for all of them and now they all love it. You can't perform a side-effect in a composable function, and calling
toggleDarkMode()
is a side effect. You MUST perform the side effect in an event-handler callback of some sort. If you want to toggle the mode from a menu bar, the menu bar must have an operation that allows you to flip the toggle. If you want to do it from a keyboard shortcut, you must listen for the key event and flip the toggle in the key event handler. But you can't call
toggleDarkMode
from the composable function because the composable function gets called every time you recompose and the toggle operation is going to trigger a recompose.
👍 3
p

Paul Nogas

02/26/2021, 7:19 PM
So I think I understand where it all fell apart. One of the other options aside from dark mode in my menu is to open a file, and from https://github.com/JetBrains/compose-jb/issues/176 I ended up using AppWindowAmbient.current!!.window which forced me to wrap it with @Composable which then bubbled up the chain of functions. I tried just using null instead of that in the swing JFileChooser and it still seems to work fine. So now I could get rid of a lot of @Composable annotations and clean up my architecture to keep the side effects outside of the compossibles.
To be specific before I had
JFileChooser(System.getProperty("user.home")).apply {    showOpenDialog(AppWindowAmbient.current!!.window)
    result = selectedFile
}
and now I have
JFileChooser(System.getProperty("user.home")).apply {    showOpenDialog(null)
    result = selectedFile
}
@jim Thanks for all the help for a newbie like me. I agree the "thinking in compose" is a learning curve but leads to great results. I'm just halfway through my 3-6 month journey :)
Here's the diff in just in case any other reader becomes interested https://github.com/pnogas/TempProject/commit/89f1788cbb72f11d219b7a6c55418db6c81e2bcf
t

TheMrCodes

02/27/2021, 9:18 AM
I think the best way to learn the compose way of thinking is like @jim described it multiple times in the past. Composeables shout be functions that translate
Data into UI
just one-Way and so if you like to trigger an Event (a Callback) by user interaction you have to provide it as the data (or specifically as a parameter) of your composable