You're reading for free via Jayant Kumar🇮🇳's Friend Link. Become a member to access the best of Medium.

Member-only story

Composition Local In Jetpack Compose.

Jayant Kumar🇮🇳

Photo by Elaine Casap on Unsplash

Link For Non-Members

In this article we will see Composition Local in Jetpack Compose and also see when to use or not.

If I asked a question to you , how do you pass any data to child composable functions?

Well your answer will be , pass data in the child’s composable function parameter, That’s it .

But if i say, now you don’t need to pass any data explicitly in the child composable function parameter instead of that use Composition Local.

So what Composition Local does , without passing anything in the function parameter , you can use that object directly in the child composable function.

Predefined Composition Locals

Jetpack Compose provides multiple predefined composition Locals implementations that start with the word Local .

The most popular or you heard is -

val context = LocalContext.current

here LocalContext is a Composition Local which find the context of current screen through .current .

The best part is now you don’t need to pass this context variable in the child composable function’s parameter every time , you can directly use this val context = LocalContext.current where ever you want.

@Composable
fun CopyTextFromClipBoard(
modifier: Modifier = Modifier
)
{
val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current

Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Button(onClick = {
clipboardManager.setText(AnnotatedString(context.getString(R.string.copy_text_from_clipboard)))
}) {
Text(text = stringResource(R.string.copy_text))
}
}

}

LocalClipboardManager.current is used to copy text from clipboard.

val textStyle = LocalTextStyle.current  //default typograyphy

Text(text = stringResource(R.string.hello), style = textStyle)

Through LocalTextStyle.currrent , we can find the current / default typography.

There are lot more options in Predefined Composition Local, you can use according to your requirements.

Creating Your Own CompositionLocals

There are two ways to create your own custom composition locals

  • staticCompositionLocalOf()
  • compositionLocalOf()

staticCompositionLocalOf()

When the value of your CompositionLocal doesn’t change often, staticCompositionLocalOf() is a good choice.

The value created with staticCompositionLocalOf is immutable and cannot be modified once created.

Let’s create a custom textstyle which store some custom textstyle data , and see how to provide that data to child composables.

@Stable
class CustomTextStyle(
val fontSize: TextUnit,
val fontWeight: FontWeight,
val color: Color
)

First we created a class which will store above types of data.

val LocalCustomTextStyle = staticCompositionLocalOf<CustomTextStyle> {
error("No Parameter is available")
}

it’s a good practise to start with Local when you are creating any composition local. staticCompositionLocalOf will create a composition local and if no value is passed to CustomTextStyle class then it will thrown an error.

object ProvideCustomTextStyleData {

val customTextStyle = CustomTextStyle(
fontSize = 30.sp,
fontWeight = FontWeight.W500,
color = Color.Blue
)

}

In the above code we passed some static data that will be using later.

@Composable
fun ChildComposable() {

val customTextStyle = LocalCustomTextStyle.current

Text(
text = stringResource(R.string.child_composable_function_using_composition_local), style = TextStyle(
color = customTextStyle.color,
fontSize = customTextStyle.fontSize,
fontWeight = customTextStyle.fontWeight
)
)
}

As you can see above you can use composition local in this way LocalCustomTextStyle.current to get the data.

But when you run this app , application will crash , because we added some static data above but didn’t tell composition local to use that data.

@Composable
fun CompositionLocalScreen(
modifier: Modifier
)
{
CompositionLocalProvider(
LocalCustomTextStyle provides ProvideCustomTextStyleData.customTextStyle,

) {
ChildComposable()
}
}

CompositionLocalProvider function takes varargs , you can pass as much as custom composition locals there separated by , (comma). we can provide the data with provides infix operator

Here whatever child composable function comes under CompositionLocalProvider can use that customTextStyle’s data.

As you noticed here , we didn’t need to pass data in the child composable’s parameter.

Let’s take another example

You all used NavHostController for navigation in jetpack compose. For all the screen NavHostController's object will be static and same. In that case we can use staticCompositionLocal() to get rid from passing NavHostController’s object every time in the composable function’s parameter.

val LocalNavigation = staticCompositionLocalOf<NavHostController> {
error("No object of Navigation")
}

First we created a composition local of type NavHostController .

@Composable
fun PerformNavigation() {

val navHostController = LocalNavigation.current
NavHost(navController = navHostController, startDestination = "Screen" ){
composable("screen"){
// your composable
}
}
}

Now with the help of LocalNavigation.current , get the current value.

CompositionLocalProvider(
LocalNavigation provides rememberNavController(),
) {
PerformNavigation()
}

At last and most important to pass PerformNavigation() function under CompositionLocalProvider and also provide rememberNavController() as a current value.

Now the same NavHostController object will be used in all the composables .

compositionLocalOf()

In case of compositionLocalOf() , value will not be static.

The value created with compositionLocalOf() is mutable and can be modified within the same composition. You can use the current property of the CompositionLocal to read and update the value.

Use compositionLocalOf() when you know value might be change frequently over and over time.

val LocalTextStyleDemo = compositionLocalOf {
// provided default value
CustomTextStyle(
fontSize = 30.sp,
fontWeight = FontWeight.W500,
color = Color.Blue
)
}

Understanding When to Use CompositionLocal

  1. You need to provide a good default value, or as you learned, throw an error if you forget to provide a default value.
  2. You can provide a value through CompositionLocal when the value is a UI tree-wide value. As you saw before with navHostController, you implemented in the previous sections can be used by all composables, a subset, and even several composables at once.

That’s all for today my friends , Hope you enjoyed and learnt something new.

Jayant Kumar🇮🇳
Jayant Kumar🇮🇳

Written by Jayant Kumar🇮🇳

Hello My name is Jayant Kumar, I am a software Engineer , specialist in Mobile Development (Android , IOS , Flutter , React Native) from India 🇮🇳

Responses (1)

Write a response

Great read!