jetc.dev Newsletter Issue #99
Published: 2022-01-11
This week, while we wait for the next Compose release, we look at state hoisting
and changing TextField()
colors. We spend a lot of time exploring testing, particularly
automated screenshot testing. We look at two composable-to-a-bitmap libraries and
two Wordle clones. And I suggest a bit of code organization to help avoid accidentally
mixing and matching your Compose UI and Glance composables.
One Off the Stack, One Off the Slack
You’ve got questions. That’s understandable!
Why Does State Hoisting Use Callbacks?
State hoisting, in the official documentation, is handled not by passing State
or MutableState
objects around, but rather by passing function types, such as lambda
expressions. See how this represents a composable form of encapsulation in this week’s
highlighted Stack Overflow question.
How Do We Tie Theme Colors Together?
Compose Material follows Material Design and is rather rigid in how it applies colors.
As a result, there is no good way to swap colors for, say, a TextField()
, except
by creating a full TextFieldColors
or MaterialTheme
, as we see in this week’s
highlighted Kotlinlang #compose
Slack thread.
Composable Commentary
Posts, videos, and other new information related to Jetpack Compose!
Automatic screenshot testing for all your Compose @Previews
David Vávra profiles how AirBnB’s Showkase library
not only gives you rapid access to all of your @Preview
composables, but how it also
gives you access to bitmaps for use with your favorite screenshot testing framework.
This allows your @Preview
composables to perform “double duty”, both for examining
the UI manually and for automated testing.
Medium: Compose UI Screenshot Testing with Firebase Test Lab and CircleCI
Dan Nesfeder continues the screenshot testing theme, showing how to set up screenshot tests that run on Firebase Test Lab, automated via a Circle CI task, after setting up the tests locally.
Medium: Test Your Jetpack Compose Layout
If you have not done much in the way of testing your composables, Anang Kurniawan
offers a basic introduction to createComposeRule()
and Espresso-style
“look up a node and assert its state” tests.
Jetpack compose accessibility best practices
Carlos Monzon brings us an extensive post outlining different ways to improve
accessibility in our Compose UI interfaces. The post covers things like
multiple ways to increase your touch target sizes, using contentDescription
and semantic nodes, and more!
Medium: Visual Transformation for any mask in Jetpack Compose TextField
VisualTransformation
is Compose UI’s way to let you control how the text
in a TextField()
is rendered, independently of what the actual text value is.
While the classic example is password shrouding, Thiago Souza explores using
it for many other scenarios, such as date formatting and phone number formatting.
Medium: Detect text overflow in Jetpack Compose
Sometimes our text is a bit long, and our Text()
composable truncates the rendered
output, perhaps with an ellipsis. However, we may still need to give the user some
idea of what the text is that is not visible. Sinan Kozak explores using the onTextLayout
to Text()
to determine what text was truncated, so another composable can render
some hints based on that data.
Tiny things on big screens
Thomas Künneth continues exploring larger screen devices, including foldables, in the world of Compose UI. Previously, Thomas has focused on how to get your UI to adapt to larger screens by making use of the available space. This time, Thomas looks at cases where you might not have a good idea of how to make use of that space.
Other Interesting Links
- Medium: Introduction to Animation in Jetpack Compose
- Medium: Compose Destinations: simpler and safer navigation in Compose with no compromises
- Medium: Passing arguments to screens in Jetpack Compose
- Interoperability in Jetpack Compose
- Medium: MVP/MVC to Reactive Architectures for Jetpack Compose
- Introducing Jetpack Compose into an existing project
- Medium: How to apply State Management and State Hoisting in Jetpack Compose Apps
- Bottom Navigation in Jetpack Compose
- Medium: Swipe to delete in Jetpack Compose Lazy Column Android
- Medium: UI Testing in Jetpack Compose
- Canvas: How to draw text in Jetpack Compose?
Resource Roundup
100% pure code!
GitHub: PatilShreyas / Capturable
Shreyas Patil wrapped up the “use ComposeView
to render to a Canvas
to get a bitmap”
trick into a reusable library.
GitHub: KaustubhPatange / kapture
Kaustubh Patange did the same thing (capture a composable to a bitmap), with a slightly different API.
GitHub: skgmn / ComposeTooltip
Many apps wind up needing tooltips to highlight and document certain UI elements,
perhaps a part of a first-time use education mode. GitHub user skgmm created one
for Compose UI that can be displayed as an independent popup or be included
as part of a ConstraintLayout()
-based composable.
GitHub: joreilly / WordMasterKMP
John O’Reilly created a Kotlin/Multiplatform edition of a word game similar to Wordle. John’s supports Android, iOS, and Compose for Desktop. Olivier Patry created the same sort of app, supporting Compose for Desktop and an ASCII terminal mode.
Other Interesting Links
- GitHub: tunjid / Mutator (state pipeline from actions)
- GitHub: Madrapps / plot (line graphs)
- GitHub: SimformSolutionsPvtLtd / SSJetpackComposeSwipeableView (swipe to edit/dismiss)
- GitHub: k0shk0sh / ComposeEasyForms (data entry form management)
- GitHub: amirhwsyn/W-PatternLock (pattern lock UI library)
- GitHub: softartdev / MaterialThemePrefs (KMP library for dark/light themes)
- GitHub: wangyung / persona (particle animation system)
- GitHub: DanielMartinus / Konfetti (confetti animation)
…And One More Thing
Compose UI has Box()
. Glance has Box()
. They are not interchangeable.
Compose UI has Text()
. Glance has Text()
. They are not interchangeable.
Compose UI has Button()
. Glance has Button()
. They are not interchangeable.
And so on.
As the Glance documentation notes:
Glance uses its own set of Composables. Do not combine
androidx.compose
andandroidx.glance
composables.
The problem is that if you have both in the same module, auto-complete support in the IDE might steer you to import the wrong one, causing confusion until you track down the flawed import.
One solution for this is to use separate modules for your Compose UI composables and your Glance composables. Do not have the Glance dependency in the Compose UI module, and do not have the Compose UI dependencies in the Glance module.
The implication is that classes that are in common between them — use cases,
repositories, etc. — come from some central module(s) that the Compose UI
and Glance modules themselves depend upon. That in turn may require some extra
work to get your dependency inversion logic set up to be able to inject things
into both the Compose UI and Glance modules. That sounds like a headache, and to
an extent it will be for the initial setup. However, this appears to be the long-term
Android app development approach: many smaller modules making up the app, so you
can use dependency lists, Kotlin’s internal
keyword, and other things to help
shape the API surface of a module and the APIs visible within that module from
other modules.
Or, just keep fussing with getting your Glance and Compose UI composables mixed up.
Or, you can subscribe to the Atom feed or follow Mark Murphy in the Fediverse.
Recent Issues:
- 2024-12-03: Rebecca Franks on clipping and masking! Stefano Natali on graphicsLayer()! FunkyMuse on type-safe nav results! And... if we have enough maps, do we need to store our maps in a Map?!?
- 2024-11-26: Math! Shared element transitions! Custom modifiers! Macrobenchmark! Adapting to platform-specific design systems! And... why does wrapContentSize() not wrap my content size?!?
- 2024-11-19: Compose alphas! Compose Multiplatform patch! PaddingValues! Graphics layers! Swiping! Heatmaps! Navigation! And... why did we get a new production Compose BOM?!?