jetc.dev Newsletter Issue #96

Published: 2021-12-21

We got slightly more than one RC release this past week — we’ll see what that means!

Beyond that, we look at child composables and how we pass colors down. We look at the alpha version of Jetpack Glance for creating app widgets. We explore custom password and multi-toggle button composables, plus swiping cards off of stacks and blocking Compose Material ripples. And I’m concerned about some advice from Chris Banes, which means I’m probably wrong, though perhaps I’m usefully wrong.

RC Reflections

Reviewing the release notes for the latest Jetpack Compose update!

1.1.0-rc02 is out for androidx.compose.compiler:compiler:1.1.0-rc02, with official support for Kotlin 1.6.10! They also started publishing a table showing what Kotlin versions are supported by which Compose compiler versions.

Note, though, that the rest of the Compose artifacts are still at 1.1.0-rc01. Mostly, those appear to be bug fixes. One that has a possible functional aspect: scrollToItem() and animateScrollToItem() on LazyListState now support negative scroll offsets.

One Off the Stack, One Off the Slack

You’ve got questions. That’s understandable!

How Does the Surface() Content Color Affect Children?

A Surface() takes a contentColor parameter, which affects the color of things like the text of a Text() that is a child of the Surface(). See how this is accomplished in this week’s highlighted Stack Overflow thread.

How Do We Examine the Content of Child Composables?

DRY (“Don’t Repeat Yourself”) is a fine programming maxim, but sometimes defining the single source of truth can be tricky. This is especially true in Compose UI, where often Composable A cannot interrogate Composable B to get a value. Learn ways of working around this in this week’s highlighted Kotlinlang #compose Slack thread!

Composable Commentary

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

Announcing Jetpack Glance Alpha for app widgets

Google has released the first alpha of Jetpack Glance, its new composable API for defining app widgets and, in the future, Wear OS tiles and perhaps other “glanceable” targets. One note: while the minSdkVersion is set to 21, Glance really only supports back to API Level 23.

Medium: Building a ComposeWidget using Jetpack Glance

Matthew Meehan wasted no time in putting the Jetpack Glance alpha to use, creating an app widget to keep track of how many glasses of water you drink. Matthew explores the boundaries of Jetpack Glance (hint: you still need a layout resource, for the widget preview), examines ActionCallback for responding to click events, and more. Also: stay hydrated! 😁 🚰

Jetpack Compose: Run Preview with Hilt

Chetan Gupta wanted to use @Preview with a composable that uses hiltViewModel(). In the end, that is not really practical, so Chetan walks through a workaround, involving a run configuration to launch a custom preview activity. Or, avoid using Hilt directly in leaf composables (the sorts of things that you want to preview).

Medium: Koin for Compose proposal (Jetpack and Multiplatform)

Back in issue 83, I linked to cokoin, a Koin wrapper for use with Compose (both the Compose Multiplatform and Google’s Jetpack edition). Here, Bruno Wieczorek explains the origins of cokoin, why it is a separate library, and how it works.

Medium: How to create a composable password with Jetpack Compose

Juan Guillermo Gómez Torres explores a PasswordTextField() composable, including its use of VisualTransformation, its options for password strength calculations (for creating new passwords), and more!

Exploring theming in Jetpack Compose

Chizoba Ogbonna reviews MaterialTheme() and its component parts (colors, typography, shapes, etc.) and how you can tailor them to match your target designs. Chizoba also looks at theme adapters for Material Component for Android and AppCompat, if you need to share theme definitions between composables and classic View-based UIs.

Jetpack Compose - Configuring a Simple Multi Toggle Button

The Dev Bits and Bytes team looks at creating a MultiToggleButton() composable for displaying a row of mutually-exclusive toggle buttons.

Resource Roundup

100% pure code!

GitHub: lhoyong / swiper

GitHub user lhoyong created a Swiper() composable, rendering a stack of items and allowing users to swipe off the top item to expose the one underneath.

Gist: mxalbert1996 / Scrollbar.kt

Albert Chang posted a snippet of code that implements a series of modifiers for drawing scrollbars for LazyColumn(), LazyRow(), and other scrollable containers.

Gist: hisetu / NoRipple.kt

GitHub user hisetu does not like ripples, apparently, and created noRippleClickable() and selectableNoRipple() modifiers to inhibit the default ripple effect from Compose Material.

…And One More Thing

About two months ago, Chris Banes wrote “Always provide a Modifier parameter”. This was well-received by the community, and I agree with its arguments in some situations. IMHO, though, its recommendations require more nuance for the overall ecosystem.

Other Compose UI experts extol the benefits of creating composables that implement a design system, whether that design system is wrapped around Compose Material or is created as a peer of Compose Material (based on pre-Material composables). IMHO, that is the better goal. There should be little question in the development team of how to assemble a Compose-based UI for your app: it should be based on a set of composables that in turn are based on what the designers ask for. Or, if the project lacks designers, there should still be a set of common UI elements that get applied consistently throughout the app.

Chris’ post does not prohibit the creation of composables for a design system. However, Chris’ objective appears to be maximum flexibility, and that can conflict with creating composables for a design system. Composables for a design system need to reliably implement the design system, more so than being flexible.

Chris’ final code snippet contains comments like:

// We can add any behavior we wish here!

and:

/**
 * This now has a modifier parameter, allowing callers
 * to customize the layout, behavior and more!
 */

When implementing composables for a design system, those can be bugs, not features, if they allow the composables to be used in ways that do not adhere to the designs. And, since Modifier is very flexible, exposing a Modifier parameter all but guarantees that the composables can be used in ways that do not adhere to the designs.

So, now we get down to team mechanics: is it better to have an API that is inflexible but is reliable, or is it better to have an API that is flexible but unreliable? Stronger teams can accomplish more in less time with the flexible-but-unreliable API, as they can rely upon external processes to enforce the design where the composables do not. For example, those teams have a large enough design team, or have a design-savvy QA team, such that they can do design reviews to ensure adherence to the designs. Not everyone has that sort of staffing, though. Or, they have teams with a lot of junior developers, ones for whom adhering to a design system is a new concept. For those, having an inflexible-but-reliable API may be the better course of action, even if it means creating a couple of renditions of a composable (perhaps wrapping around a flexible private implementation) to handle varying scenarios.

In time, perhaps with tooling improvements we will be able to create composables that are both flexible and reliable. For example, we could work out a way to validate a Modifier input to ensure that it does not mess with things that it should not. Right now, I do not think we are in position to perform that sort of validation.

Does this mean that you should never expose a Modifier input to your composables? Of course not. It does mean that you should think through the short-term and long-term role of the composables that you create, and consider whether supporting Modifier input is the right answer. It may be that the right answer is not “always” or “never” offer Modifier input, but rather “sometimes”, such as “only for private composables”.