jetc.dev Newsletter Issue #33
Published: 2020-09-29
This week, we continue banging the drum for using immutable types with Compose. We also look at Hilt and GraphQL with Compose, and we explore how dependency inversion might work with a Compose-based UI. We also watch a Compose-rendered T-Rex run and jump!
One Off the Stack, One Off the Slack
You’ve got questions. That’s understandable!
This week’s options for Stack Overflow questions mostly were incomplete, so let’s focus on Slack instead…
Immutability Is Your Friend
Hardly a week goes by without somebody being bitten in the posterior by trying to use mutable data with Compose. While Compose can use mutable properties and types, they may not trigger recompositions when you mutate their contents. On the whole, Compose works better with immutable types, as we see in this week’s highlighted Slack thread.
Composable Commentary
Posts, videos, and other new information related to Jetpack Compose!
How to Handle Navigation in Jetpack Compose
Gastón Saillén reviews one approach for navigation in Compose, with a custom
navigator and nav graph classes reminiscent of the NavController
and Kotlin DSL of the Jetpack Navigation component.
Use Jetpack Compose Correctly!
Medium user MR3Y offers up five tips for working with Compose, including
implementing custom states and the value of layoutId
.
Exploring Jetpack Compose with Hilt and ViewModels
Last week’s issue included a link to Jaewong Eum’s DisneyCompose sample app. This week, Jaewong has a post decribing how it works, including how Hilt was integrated into the app.
Add GraphQL to Your Jetpack Compose Apps
Martin Bonnin shows us how to connect the Apollo-Android GraphQL client up to our composables, using a coroutine-based API mapping GraphQL responses to UI states.
Videos: Jetpack Compose for Beginners
YouTube user Yet Another Dev is publishing a series of screencasts exploring
Compose, including looks at themes, Stack()
, and scrollable lists.
Jetpack Compose: How to Make a Scrollable List?
Gérard Paligot wrote a Medium post comparing and contrasting ScrollableColumn()
and LazyColumnFor()
as approaches for creating a scrollable list of content.
Resource Roundup
100% pure code!
Koin Composable Support
Koin is a dependency inversion (DI) system written in Kotlin, used as an alternative
to Dagger. The current pre-release build of Koin offers dependency resolution from
within a composable, both for arbitrary Koin-supplied objects and specifically for ViewModel
instances. For those looking to perhaps use Compose in Kotlin/Multiplatform
projects, Koin is an important option, given that it supports KMP while Dagger is
tied exclusively to the JVM.
GitHub: skydoves / Landscapist
Jaewong Eum appears again in this issue, this time with another set of image-loading composables, backed by your choice of Glide or Fresco.
Gist: vganin / ComposeNumberPicker.kt
Vsevolod Ganin brings us NumberPicker()
composable, complete with fling support!
GitHub: wajahatkarim3 / DinoCompose
Wajahat Karim ported Chrome’s T-Rex themed endless runner game to Compose! Watch out for the cacti!
…And One More Thing
There are many aspects of modern Android app development that we need to work into
Compose-based development. Dependency inversion (DI) is one of those.
While a lot of DI takes place away from the UI layer, modern activities and fragments
still often use some amount of DI, such as for obtaining configured ViewModel
instances. So we need to determine where in a composable hierarchy we should be
using DI… if anywhere.
Koin’s release of composable DI support, mentioned earlier in this issue, sparked some discussion on this point. Some developers believe that composables should not be interacting with DI services — instead, they should be getting all injectable elements passed into them instead.
Personally, I am skeptical of that argument, for the simple reason that
the @Composable
annotation is going to be everywhere.
For example, once a Compose-ready edition of the Jetpack Navigation component is ready, my guess is that our Compose activity will look like:
-
onCreate()
callingsetContent()
with a composable lambda, as we have it now -
That lambda will set up Navigation using the Kotlin DSL
-
Nav destinations will be calls to composable functions
This does not preclude limiting DI to being outside of the composable hierarchy. But that means everything that we are getting from DI that is needed by the UI layer goes in this activity. That seems excessive, particularly for larger apps.
My guess is that we will adopt DI patterns reminiscent of how we handle state. Right now, the state recommendations are:
-
Prefer stateless composables to stateful ones, particularly at leaves of the composable hierarchy
-
For composables that need state, prefer to receive the state via parameters rather than declaring it locally
-
Aim to have your state at the point of commonality: the spot in the composable hierarchy where all consumers of that state descend from
So, for DI, we might wind up with:
-
Prefer composables without DI-provided dependencies, particularly at leaves of the composable hierarchy
-
For composables that need dependencies, prefer to receive them via parameters rather than having it be injected locally
-
Aim to have your injections at the point of commonality: the spot in the composable hierarchy where all consumers of that dependency descend from
But, that is what the next several months are for, as we progress through alpha releases and “kick the tires” on Compose. By the time Compose ships in stable form — and preferably even before a beta — hopefully we will have a pattern that seems reasonable that we can provide to newcomers.
Or, you can subscribe to the Atom feed or follow Mark Murphy in the Fediverse.
Recent Issues:
- 2024-12-10: A Compose Multiplatform alpha! Hot reload! Presentation! Sprites! Calendars!
- 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?!?