jetc.dev Newsletter Issue #74

Published: 2021-07-20

rc02 shipped! And, since there are already signs of the first alpha for 1.1.0, it is extremely likely that next week will bring the stable Compose release! 🙌

This week, we look at how @Preview can work with a ViewModel parameter… again. We also get caught up on Coil’s first-class support for Compose, examine MVI in a Compose context, and talk about accessibility. We also see how to capture a composable in a “screenshot” bitmap and how to work with arrow keys, D-pads, and the like. And I point out that LocalContext helps you identify where you are doing things in a composable that might belong somewhere else in your architecture.

RC Reflections

Reviewing the release notes for the latest Jetpack Compose update!

Blissfully, rc02 has few changes, and most of those are just bug fixes. They did semi-revert an rc01 change, so now dialogs will be sized based on the platform by default — use usePlatformDefaultWidth = false to change this back to rc01-style behavior.

One Off the Stack, One Off the Slack

You’ve got questions. That’s understandable!

Responding to Long Clicks

The clickable() modifier is for responding to simple clicks. If you need to respond to long clicks as well, use the combinedClickable() modifier, as we see in this week’s highlighted Stack Overflow question.

@Preview and a ViewModel, Again

A few months ago, we looked at having a @Preview composable accept a ViewModel parameter. That subject came up again, garnering a few new possible solutions, as we see in this week’s highlighted Kotlinlang #compose Slack thread.

Composable Commentary

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

Coil: Migrating From Accompanist

Coil (an image-loading library) now has first-class support for Compose UI, supplanting the external implementation from Chris Banes’ Accompanist library. As you switch from Accompanist to a pure-Coil solution, though, there are several things that you will need to rename — this portion of the Coil documentation outlines what you will need to do.

New TwoPaneLayout Compose library preview

Microsoft has been hard at work to make Compose work well with the Microsoft Surface Duo, their Android-powered dual-screen device. Joy Liu returns with a blog post outlining their new library with a TwoPaneLayout() composable that reacts to hinge placement and orientation to help you present distinct content on each half of a foldable. Joy also recorded a Twitch livestream reviewing the use of this composable.

How Jetpack Compose is Helping Put Accessibility First

Chris Yono examines the options we have in Compose UI for providing accessibility information to Talkback and similar accessibility services. Chris explores the Compose edition of contentDescription, along with newer options like accessibilityHeading and onClickLabel.

Compose architecture: MVVM or MVI with Flow?

Catalin Ghita graces the newsletter again, this time touching “the third rail” of Android app development: architecture patterns. Here, Catalin looks at whether there are aspects of MVI that might be more amenable to Compose UI-based apps than a strict MVVM implementation would be.

Medium: Jetpack Compose: Missing piece to the MVI puzzle?

Garima Jain also examines MVI, seeing how Compose support for @Stable and @Immutable can help deal with MVI’s preference for immutable view states while minimizing the cost of recomposition as the view state changes.

Welcome you are visitor number 12345

Maia Grotepass continues her exploration of retro UIs with a look at the venerable page counter. In the 1990’s, a page counter would show how many visitors have visited a particular Web page, back when the Web had few users and few sites to visit. Maia’s counter uses an animated effect akin to an odometer to show the counter climb.

Medium: Create an Auto-Scroll ViewPager with transformation and ken burns effect in Android Jetpack Compose

Mr Umbrella returns, this time with a pager-style UI (using Accompanist) that automatically switches between pages. It also performs a scal animation on the images shown in the pager, to provide a Ken Burns effect for those images.

Video: Jetpack Compose Navigation for Beginners

Philipp Lackner is back to take a look at Navigation for Compose and the basic steps for adding it to a project and using it to navigate between screen composables.

Resource Roundup

100% pure code!

GitHub: JohannBlake / bitmap-from-composable

A popular question is: how do we generate a “screenshot” of a composable, converting what it renders on-screen into a bitmap? Johann Blake took a shot at it, using a ComposeView to wrap the composable, then capturing a bitmap of that ComposeView. Johann also wrote a Medium post with more information.

GitHub: thesauri / dpad-compose

Walter Berggren created a demonstration of how to pull off support for directional navigation in the current Compose UI code.

GitHub: boguszpawlowski / ComposeCalendar

Bogusz Pawłowski created a library for rendering interactive calendars, with customizable date/date range selection, along with customizable composables for rendering individual pieces (e.g., days, weeks).

…And One More Thing

The general pattern for Compose is that composable functions are pure top-level functions, not member functions of some activity, fragment, or other construct. This feels weird and will need some time for us to adapt. However, it has the advantage of helping to identify where we are doing things that may not be the best.

A big downside of Activity extending Context, and Context being a ‘god object’, is that it was very easy to do things in an Activity that might not belong there.

So, when you go reaching for LocalContext.current, ask yourself whether that logic really belongs in your current composable, versus belonging somewhere else:

  • Want to navigate to some legacy activity when a button gets clicked! Great! But your composable that calls Button() might not be the right spot to do that work. It may be better to have your composable take an on-button-click lambda expression that you can invoke instead, and let the actual navigation be handled in some higher-order composable.

  • Want to read a SharedPreferences value? Cool! But, that is data, and so really that should be coming from your viewmodel or a similar abstraction for getting data from your data sources. After all, you will want to supply test data when you test your composable.

  • Need the value of some resource? Wonderful! But there are built-in functions for a lot of that, such as stringResource(), that you may wish to use instead.

  • Need to talk to some system service? Um, OK. But, that too probably ought to be isolated in your viewmodel, except for the occasional system service that is purely UI-centric (e.g., InputMethodManager).

  • And so on

Roughly speaking, if the work you want to do with a Context is not tied very closely to a UI concern, probably there is a better place for you to do that work, such as a calling function or viewmodel.

Referencing LocalContext.current may not rise all the way to “code smell”, but it is the sort of thing that you should still look at closely. It may represent a “so-so” legacy pattern that is less appropriate in a Compose world.