Welcome to my new article, this time I’ll guide you through CompositionLocal. I’ll try to make it as simple as I can so you can get how to use it and start implementing it in your project immediately.

CompositionLocal is an implicit approach to provide values in the composables.

✌🏼 Now simpler: If you think about it, Compose rely on the dependency injection pattern to work. Composables are meant to be standalone components, that gets arguments provided (injected), for instance:

Example 1

In Example 1, passing User to ProfileCard is a way to pass data explicitly to a composable.

But there’s a challenge when you need to pass the same data/values to multiple composables in multiple screens. That’s where CompositionLocal come in place.

To show this in practice, let’s remember that is recommended to wrap our compose code in a Theme, that’s because it implicitly provide values down the stream, sound familiar so far? 😉

So, a basic example looks like this:

Example 2

Now, if we go to the source code of MaterialTheme, we can get a hint of how that might be working. But before we jump to that, let me explain how CompositionLocal works.

To illustrate a practical example, let’s change the Text Composable default text color using the CompositionLocalProvider API:

Example 3

In the first Text composable, we use the default text color provided by MaterialTheme (black). Following that, we change the default value to red and blue. The above code will render to this:

With CompositionLocalProvider we can specify a Local* value inside of the scope of the CompositionLocalProvider. The first argument is a vararg of ProvidedValue think of it like a key-value pair. The second argument is a composable. Let’s break it down:

  • In the first argument, the key will be a CompositionLocal (we’ll dive deeper in this later) and the value is what this CompositonLocal will provide
  • The second argument uses the slot API and provide a way to have a composable scope where we can build UI.

In this particular example, we are changing the default text color, that will be implicitly provided to the Text composable if no color or style is specified in the composable itself (see source code).

📝 In summary:

  • Composables have default values provided by the theme with CompositonLocal: colors, alpha, text, shapes, etc.
  • Those default values can be overridden using CompositionLocal to specify our desired value in a specific scope.

There are some handy CompositionLocal that we can use on certain situations. For instance to get a string in a non-composable scope like a LaunchedEffect, to show a SnackBar for instance, we need a context. To get it we can use LocalContext.current.

But, there’s some cases where you’ll find yourself in the need to build a custom CompositionLocal.

For instance, if you want to use the WindowMetricsCalculator class to get the size of the screen to handle responsiveness, you’ll need to pass an activity in Build.VERSION_CODES.Q and older (see source).

There’s no built-in CompositionLocal to get an activity. Let’s see how we can build our own:

Creating the Composition Local Object

First, we need to create the composition local object. In this case it will look like this:

Example 4

Place it in a file as a top-level property. We can use the compositionLocalOf or the staticCompositionLocalOf builders. You need to specify a generic type, which will be the type you’d like to provide using this CompositionLocal.

The argument is a lambda for the default factory. There you specify if you’d like to return a default value if the CompositionLocal is not found, in this case we are erroring, you can adjust this depending on your case.

Providing The Value

The next step is to actually provide the value, similar to the example above:

Example 5

In your root composable, you can use the CompositionLocalProvider API to specify what the LocalActivity provides in its scope. In this case I placed it inside of the theme, but since we are not overriding a value, the order doesn’t really matter, but prefer the order above for consistency.

Using Our LocalActivity value

Now in your composables, inside of the scope of the CompositionLocalProvider, you will be able to get the value using the current property.

Example 6

Last but not least, as part of the Example 4 I mentioned:

We can use the compositionLocalOf or the staticCompositionLocalOf builders.

But, what’s the difference?

It is simple. In case the provided value changes, compositionLocalOf will cause the recomposition only where current is called. On the other side, staticCompositionLocalOf will cause the recomposition of the whole CompositionLocalProvider scope.

So, yes, it can represent a big difference.

CompositionLocal is the best way to provide values implicitly across multiple composables. This makes our code cleaner and more efficient. I hope I could help you understand how it works, if you have any question, please drop it in the comments section and if you like it, leave a clap and share. Cheers ✌🏼

Source link