jetc.dev Newsletter Issue #80

Published: 2021-08-31

This week, we explore the use cases for rememberUpdateState(), explore AndroidView() as part of a Compose UI migration process, and see what decompiled composables look like. And, I encourage you to check your use of custom views now, before you start trying to convert them to composables.

One Off the Stack, One Off the Slack

You’ve got questions. That’s understandable!

Implicit Layout Priorities

Sometimes, you need to explicitly declare your fill weights — like we used to do with LinearLayout — in order to get the UI that you want, as we see in this week’s highlighted Stack Overflow question.

Why Do We Need rememberUpdateState()?

Vishnu Haridas suggested highlighting this Kotlinlang #compose Slack thread, where we examine the situations where we need rememberUpdateState() and see why sometimes it appears that we can skip it.

Composable Commentary

Posts, videos, and other new information related to Jetpack Compose!

Video: 10 Jetpack Compose Tips From a Production Android App

Nate Ebel is migrating an app to Compose UI and brings us a screencast reviewing a series of tips based on that work. These include how to set up your composable APIs, how to make effective use of custom themes, and how to make the most of @Preview.

Learn with code: Jetpack Compose — Lists and Pagination (Part 1)

Ruben Quadros blends the Paging 3 library with Compose UI to populate a two-up grid of video games from RAWG, complete with tests!

Migrating to Compose - AndroidView

Joe Birch returns, with the flip side to Compose migration: using AndroidView() to wrap a View in a composable. This is useful for cases where you have an elaborate custom View — perhaps from a third-party library — and you are not prepared to switch to a Compose UI equivalent at this time.

Testing Composables with Robolectric

Rashad Sookram ran into problems getting Robolectric-based unit tests to properly exercise composables. Fortunately, Rashad took some notes and explains what it took to get ComposeTestRule working and from there getting composable tests to run.

Medium: Integrate Google sign in with Jetpack Compose

Rajesh Hadiya looks at how we can add the “Sign In with Google” button in a Compose UI app, including integration with GoogleSignInClient and launching the Google sign-in activity.

Jetpack Compose Animations Beyond the State Change

Gustavo Fão Valvassori explores how to create composable editions of some of the animated loading indicators from AVLoadingIndicatorView: bouncing balls, exploding circles, and spinning triangles.

Medium: Display Map Snapshot using Jetpack Compose

Piotr Prus is back, using the Google Maps API to get snapshots of maps using AndroidView() and MapView, then taking the snapshots and displaying them in an Image() composable.

Getting started with jetpack compose – Modifiers

Velmurugan wrote a long post with examples of 19 basic modifiers and what you get by using them. This includes some less-common ones like rotate() and shadow().

Resource Roundup

100% pure code!

GitHub: takahirom / decomposer

Takahiro Menju wondered what the Compose compiler plugin actually generates for our composables, leading to a Gradle plugin that decompiles the generated Kotlin into Java, so you can see exactly what is going on “under the hood”.

GitHub: kacmacuna / composed-xml

Bacho Kurtanidze implemented a “take a layout and generate a composable” tool, supporting both standard Compose UI and Compose for Desktop.

GitHub: loukwn / StageStepBar

Konstantinos Lountzis implemented a “staged step bar”: a linear progress indicator with specific sub-indicators for particular milestones in the progress. This is available both as a View and as a StageStepBar() composable.

…And One More Thing

One of the first steps that some developers will take when migrating an app from the classic View system to Compose UI is to use ComposeView. If the app implemented its own custom views, you could “simply” replace the implementation of the custom view with a ComposeView and composables. In theory, nothing that uses the custom view will be able to tell the difference.

This is only true for some values of “simply”, though.

One of the problems with the View system is that so much of the API surface is public. It is all but impossible to enforce encapsulation this way. For some developers, it will seem easier to change the custom view by using findViewById() to poke around its innards, rather than by actually changing the custom view itself. The problem is that those findViewById() calls will fail once the custom view changes implementation to something radically different… and Compose UI definitely qualifies as “radically different”.

If you maintained good discipline with your use of custom views, this will not be an issue. If discipline was lacking, though, then you might find yourself needing to do some cleanup first.

One way to approach this is to try to eliminate all findViewById() calls, such as by adopting view binding. The nice thing about view binding is that it helps enforce this sort of encapsulation: an outside consumer of a custom view does not have access to the binding object of the custom view itself.

(and, if for some reason you made the binding object part of the custom view’s public API… well, perhaps that was not a good choice)

So, as you clean up lingering findViewById() calls, you will find where you are “reaching into” a custom view from outside, and can switch that to use a proper API on the custom view itself.

findViewById() might be the worst problem, but it is not the only one. For example, consumers of a custom view might assume that the custom view extends from some particular View subclass and use an API unique to that subclass. This will break once you change the base class of the custom view to ComposeView. Those might be the sort of thing that you just deal with at the time of migrating the custom view to use composables.

This sort of thing is not unique to adopting Compose UI, as you can have encapsulation problems in any code. However, Compose UI migrations might stress your UI implementation in ways that expose encapsulation problems that you might not have considered otherwise.

And now, if you will excuse me, I need to go enter some tickets into Jira. Just sayin’. 😅