On Android especially, it's easy to get trapped by the details of where tests are running. There are tradeoffs for running on a device/emulator, Robolectric, or without Android APIs available - but I think it's dangerous to have the "where" drive the "why". I like "why" driving "where" as the goal.
Posts by Alex Vanyo
This reminded me that I still haven't gotten to other planets yet, oh no
π Want to see a preview of what's coming up in #JetpackCompose?
We heard you that debugging shared elements is tricky. In 1.11.0-alpha03, a new composable LookaheadAnimationVisualDebugging was added.
Wrap your SharedTransitionLayout to add visual cues to track down those hard to debug issues. π‡οΈ
That way, resizing the window smaller will remain on the list until you've directly navigated to the detail, and then resizing smaller will remain on the detail. No need for an empty detail placeholder:
One technique to solve this is to not have the detail in the underlying backstack state at first, and derive an entry list with a "transient" detail entry at larger window sizes. Only when you actually navigate to that entry does the entry get "realized" and get added to the underlying backstack.
Idea 2: Because Navigation 3 allows hoisting the backstack state arbitrarily, you can give a backstack to the NavDisplay that is derived from some other state. Another tricky list-detail case is when you want the first detail visible initially, but only if there's enough space for it.
Idea 1: I have a list-detail setup for my settings, but if there's even more space available, I want the entire list-detail setup to be in a dialog. I can do that by creating a custom DialogSceneStrategy that wraps my other scene strategies, and having the DialogScene wrap an arbitrary Scene:
I've converted my side project to Navigation 3 (or at least, a similar close fork of Navigation 3), which you can try out on the web here!
alex.vanyo.dev/composelife
Two more complex scene ideas I've been playing around with: A SceneStrategy wrapping another SceneStrategy, and derived entry lists.
Navigation 3 is here, and yes β it changes things π
Alex Vanyo & Don Turner break down whatβs new, whatβs better, and what not to do.
Watch the VOD β youtu.be/I9Ws2Lxv0dw
Navigation 3 sets the stage for navigating with scenes!
Be sure to tune in to @codewiththeitalians.it at cwti.link/twitch now to see @donaldturner.bsky.social and myself experiment with Navigation 3!
Every day this week we're publishing content to help you get started with Nav3. android-developers.googleblog.com/2025/12/lear....
Got questions? Post them using #AskAndroid and we'll answer them live at 9am PST on Friday here: www.youtube.com/live/JsugLEM....
But now that you can conditionally display more than one entry at a time by wrapping panes with other UI, the sky is the limit.
I think there's a lot of possibilities around navigational systems that will go from "nigh impossible" to "very doable" with Navigation 3.
The previous approaches were a lot harder to work with: the best approach before Navigation 3 was combining the list and detail together into a single destination, and before that, there was nesting NavDisplays with independent backstacks. Both of those approaches had annoying drawbacks.
This metadata is then used by the scene strategy passed to the NavDisplay, specifically the ListDetailSceneStrategy:
github.com/android/nowi...
Using scenes, the ListDetailSceneStrategy can choose to render panes side-by-side only if they have the right metadata, and if the window is big enough.
The interests entry provides extra metadata that denotes that it is semantically a "list":
github.com/android/nowi...
Similarly, the topic entry provides extra metadata that denotes that it is semantically a "detail":
github.com/android/nowi...
It's satisfying to see the integration point between Navigation 3 and ListDetailPaneScaffold come together after the previous approaches - the list and detail panes can finally be proper backstack entries _and_ be displayed simultaneously without nesting!
Pointing out key parts of the new setup:
The orientation and tilt isn't directly exposed through Compose's PointerEvent, but you can fetch the underlying Android MotionEvent since 1.9 to get at them: developer.android.com/reference/ko...
I'd bet styluses would be most likely to have that information available.
A small change that flew under the radar for me for #JetpackCompose: calling setContent in a test more than once doesn't crash now!
It's still a bit awkward to test Activity recreation, but the workaround is a lot easier now:
issuetracker.google.com/271226817#co...
My talk on Navigation 3 from #dcldn25 is live (very impressive publishing from the droidcon team!). www.youtube.com/watch?v=j1Oi...
The reason I did catch this later was a failing StrictMode ThreadPolicy. But a lint rule warning against with(CoroutineContext) seems like it'd be nice to have.
Anyone have a lint rule that warns against doing with(dispatchers.IO) because you almost certainly wanted withContext(dispatchers.IO) instead?
Asking for someone who did something silly: github.com/alexvanyo/co...
The recording from my talk on how to handle configuration changes in Compose at #dcnyc25 is out!
Hopefully this helps explain what android:configChanges is responsible for in #AndroidDev and why it's important to know what promises you make when setting it.
www.youtube.com/watch?v=K5-9...
I'm curious how prevalent the onGloballyPositioned pattern is and why - does it feel like the best way to accomplish more custom layout behavior? Is it the easiest thing to get something working close enough? Are there missing tools that would make it less likely you'd use onGloballyPositioned?
This video from last year goes into a lot more detail about the problem space here, along with some examples of how to approach different problems:
www.youtube.com/watch?v=PUxt...
Unfortunately there's not really a one-size-fits-all solution for every case, but don't be afraid of a custom layout - if you're already doing some math to change the layout of something based on something else, you're about 80% of the way there to writing a custom layout.
This can also wreck havoc on animations - maybe they can cover up some of the jankiness, but animations need to have a consistent view of the layout as it animates at each point in time. Shared element transitions don't really work if the shared element disappears completely for a frame.
Nesting this approach makes the situation even worse - each time you put a calculation like this inside another one, it adds an additional frame required to resolve the whole layout.
When you have composition being driven by information that you can only get in a later phase, like measurement, layout or drawing, the first composition can't be correct!
This means that the first frame might be blank, or components might jump around as you get more info.
A response to the section on "How Can I Determine the Size and Position of Descendants of Peers?"
Using onGloballyPositioned (or onSizeChanged, or similar modifiers) like this is a quick way to get yourself into trouble with layouts that take multiple frames to resolve that can flash or look janky.
I smile every time I hear a voice assistant says "Panic! at the Disco" out loud when playing music, it dutifully pronounces the punctuation.