### Compose RadioGroup Component API Example Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md This example presents a potential API for a `RadioGroup` component in Compose, designed to handle generic options, orientation, content padding, and custom option rendering. ```kotlin ``` @Composable fun RadioGroup( // `options` are a generic type options: List, // horizontal or vertical orientation: Orientation, // some adjustments around content layout contentPadding: PaddingValues, modifier: Modifier = Modifier, optionContent: @Composable (T) -> Unit ) { ... } ``` ``` -------------------------------- ### Compose RadioGroup Component API Example Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Presents a potential API for a generic RadioGroup component in Compose, accommodating various layouts and data types. This serves as an example for evaluating the necessity and complexity of a new component. ```kotlin @Composable fun RadioGroup( // `options` are a generic type options: List, // horizontal or vertical orientation: Orientation, // some adjustments around content layout contentPadding: PaddingValues, modifier: Modifier = Modifier, optionContent: @Composable (T) -> Unit ) { ... } ``` -------------------------------- ### Semantics Merging Example Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Demonstrates how to manually create a semantic node that merges its children's semantics by setting `mergeDescendants = true` on a `Modifier.semantics` modifier. ```kotlin Modifier.semantics(mergeDescendants = true) ``` -------------------------------- ### Compose Component Layering Example Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Illustrates component layering in Compose. It shows single-purpose building blocks like Checkbox, Text, and Row, and a higher-level component CheckboxRow that combines them, demonstrating opinionated defaults and reduced customization at higher levels. ```kotlin // single purpose building blocks component @Composable fun Checkbox(...) @Composable fun Text(...) @Composable fun Row(...) // high level component that is more opinionated combination of lower level blocks @Composable fun CheckboxRow(...) { Row { Checkbox(...) Spacer(...) Text(...) } } ``` -------------------------------- ### Avoid DSL-based TabRow Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Avoid DSL-based slots like this TabRow example. Prefer plain slot @Composable lambdas for greater flexibility and a simpler API. ```kotlin @Composable fun TabRow( tabs: TabRowScope.() -> Unit ) {} interface TabRowScope { // can be a string fun tab(string: String) // Can be a @composable as well fun tab(tabContent: @Composable () -> Unit) } ``` -------------------------------- ### Create Single Purpose Components in Compose Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md This example shows how to correctly implement components by adhering to the single responsibility principle. The 'Button' component handles clicks, and 'ToggleButton' handles checked state, promoting reusability and clarity. ```kotlin @Composable fun Button( // problem 1: button is a clickable rectangle onClick: () -> Unit, ) { ... } ``` ```kotlin @Composable fun ToggleButton( // problem 1: button is a check/uncheck checkbox-like component checked: Boolean, onCheckedChange: (Boolean) -> Unit, ) { ... } ``` -------------------------------- ### Create Single-Purpose Components in Compose Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md This example shows how to correctly implement the single responsibility principle by separating concerns into distinct components. A `Button` handles clicks, and a `ToggleButton` handles checked state. ```kotlin ``` @Composable fun Button( // problem 1: button is a clickable rectangle onClick: () -> Unit, ) { ... } @Composable fun ToggleButton( // problem 1: button is a check/uncheck checkbox-like component checked: Boolean, onCheckedChange: (Boolean) -> Unit, ) { ... } ``` ``` -------------------------------- ### Simple Label Composable Element Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md A basic example of a composable function that emits a single Compose UI tree node, functioning as a UI element. ```kotlin @Composable fun SimpleLabel( text: String, modifier: Modifier = Modifier ) ``` -------------------------------- ### BadgedBox Composable Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md This snippet shows the definition and a basic usage example of the BadgedBox composable, which is used to display badges with content. ```APIDOC ## `@Composable` `BadgedBox` ### Description Material Design badge box. A badge represents dynamic information such as a number of pending requests in a navigation bar. Badges can be icon only or contain short text. A common use case is to display a badge with navigation bar items. A simple icon with badge example looks like: ```kotlin @sample androidx.compose.material3.samples.NavigationBarItemWithBadge ``` ### Parameters - **badge** (`@Composable BoxScope.() -> Unit`) - Required - The badge to be displayed - typically a `Badge`. - **modifier** (`Modifier`) - Optional - The `Modifier` to be applied to this BadgedBox. Defaults to `Modifier`. - **content** (`@Composable BoxScope.() -> Unit`) - Required - The anchor to which this badge will be positioned. ### Request Example ```kotlin BadgedBox(badge = { Badge { Text("3") } }) { Icon(Icons.Filled.Mail, contentDescription = "Notifications") } ``` ### Response (This is a Composable function, not an API endpoint with request/response bodies. The 'Response' section is not applicable here.) ``` -------------------------------- ### Prefer Specific Composable Functions for Button Styles Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Illustrates the recommended approach of creating separate @Composable functions for distinct button styles, promoting semantic clarity and easier customization. Usage examples show direct use and wrapping. ```kotlin // library code @Composable fun PrimaryButton( onClick: () -> Unit, background: Color, border: BorderStroke, // other relevant parameters ) { // impl } @Composable fun SecondaryButton( onClick: () -> Unit, background: Color, border: BorderStroke, // other relevant parameters ) { // impl } // usage 1: PrimaryButton(onClick = { loginViewModel.login() }, border = NoBorder) // usage 2: @Composable fun MyLoginButton( onClick: () -> Unit ) { // delegate to and wrap other components or its building blocks SecondaryButton( onClick, background = MyLoginGreen, border = LoginStroke ) } ``` -------------------------------- ### Avoid State as Parameter Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Do not use State as a direct parameter. Instead, use plain types or lambdas to provide flexibility. This example shows the incorrect way to define a parameter. ```kotlin fun Badge(position: State) {} ``` ```kotlin // not possible since only State is allowed Badge(position = scrollState.offset) // DOES NOT COMPILE ``` -------------------------------- ### Modifier Chain Construction Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md Modifier chains are built using a fluent builder syntax with Kotlin extension functions. This example demonstrates chaining size, background color, and padding modifiers. ```kotlin Modifier.preferredSize(50.dp) .backgroundColor(Color.Blue) .padding(10.dp) ``` -------------------------------- ### Use Lambda for State Parameter Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Prefer using a lambda function `() -> T` for parameters that might hold state. This allows delayed reading of the value and avoids unnecessary recompositions. This example demonstrates the correct approach. ```kotlin fun Badge(position: () -> Dp) {} ``` ```kotlin // works ok Badge(position = { scrollState.offset }) ``` -------------------------------- ### Composable function that emits and returns a value (Don't) Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md This example shows an anti-pattern where a composable function both emits content and returns a value. This makes communication with the composable difficult and order-dependent. ```kotlin // Emits a text input field element and returns an input value holder @Composable fun InputField(): UserInputState { // ... // Communicating with the InputField is made difficult Button("Clear input", onClick = { TODO("???") }) val inputState = InputField() ``` -------------------------------- ### Use Parameters for Core Component Functionality Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Use parameters to customize the core functionality of a component. For example, the background color of a Button is considered core functionality and is appropriate as a parameter. ```kotlin @Composable fun Button( onClick: () -> Unit, // modifier param specified so that width, padding etc can be added modifier: Modifier = Modifier, // button is a colored rect that clicks, so background // considered as a core functionality, OK as a param backgroundColor: Color = MaterialTheme.colors.primary ) ``` -------------------------------- ### Fancy Button Composable returning state (Don't) Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md This example illustrates an anti-pattern where a composable element returns a state object instead of emitting its UI node and receiving callbacks. ```kotlin interface ButtonState { val clicks: Flow val measuredSize: Size } @Composable fun FancyButton( text: String, modifier: Modifier = Modifier ): ButtonState { ``` -------------------------------- ### Avoid Bloated Button API Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Avoid creating a Button API with numerous specific parameters like text and icon. This approach leads to inflexibility and API bloat. This example shows an API to avoid. ```kotlin @Composable fun Button( onClick: () -> Unit, text: String? = null, icon: ImageBitmap? = null ) {} ``` -------------------------------- ### Avoid Multipurpose Components in Compose Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md This example demonstrates a multipurpose button that handles both click events and checked state, violating the single responsibility principle. It's recommended to split such functionality into separate components. ```kotlin // avoid multipurpose components: for example, this button solves more than 1 problem @Composable fun Button( // problem 1: button is a clickable rectangle onClick: () -> Unit = {}, // problem 2: button is a check/uncheck checkbox-like component checked: Boolean = false, onCheckedChange: (Boolean) -> Unit, ) { ... } ``` -------------------------------- ### Avoid Multipurpose Components in Compose Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md This example demonstrates a multipurpose button that handles both click events and checked state, violating the single responsibility principle. It's recommended to split such functionality into separate components. ```kotlin ``` // avoid multipurpose components: for example, this button solves more than 1 problem @Composable fun Button( // problem 1: button is a clickable rectangle onClick: () -> Unit = {}, // problem 2: button is a check/uncheck checkbox-like component checked: Boolean = false, onCheckedChange: (Boolean) -> Unit, ) { ... } ``` ``` -------------------------------- ### Composable Element with Modifier Parameter Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md Elements must accept a `Modifier` parameter named 'modifier' as the first optional parameter. This example shows how to apply modifiers to a `Text` element within a `FancyButton`. ```kotlin @Composable fun FancyButton( text: String, onClick: () -> Unit, modifier: Modifier = Modifier ) = Text( text = text, modifier = modifier.surface(elevation = 4.dp) .clickable(onClick) .padding(horizontal = 32.dp, vertical = 16.dp) ) ``` -------------------------------- ### Compose RadioGroup Implementation Using Building Blocks Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Demonstrates how a `RadioGroup` could be implemented by a user using existing Compose building blocks like `Column`, `Row`, `Text`, `RadioButton`, and modifiers like `selectableGroup` and `selectable`. ```kotlin ``` // Modifier.selectableGroup adds semantics of a radio-group like behavior // accessibility services will treat it as a parent of various options Column(Modifier.selectableGroup()) { options.forEach { item -> Row( modifier = Modifier.selectable( selected = (select.value == item), onClick = { select.value = item } ), verticalAlignment = Alignment.CenterVertically ) { Text(item.toString()) RadioButton( selected = (select.value == item), onClick = { select.value = item } ) } } } ``` ``` -------------------------------- ### BasicComponent vs Component Naming Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Use the 'Basic*' prefix for components providing barebones functionality without decoration or design-system opinions, signaling they are meant to be wrapped. Use 'Component' without a prefix for ready-to-use, decorated components. ```kotlin // component that has no decoration, but basic functionality @Composable fun BasicTextField( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, ... ) // ready to use component with decorations @Composable fun TextField( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, ... ) ``` -------------------------------- ### Icon Composable Parameter Order Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Demonstrates the recommended parameter order for the Icon composable, with required parameters first, followed by modifier and optional parameters. ```kotlin import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.res.LocalContentColor import androidx.compose.ui.res.LocalContentAlpha @Composable fun Icon( // image bitmap and contentDescription are required // bitmap goes first since it is the required data for the icon bitmap: ImageBitmap, // contentDescription follows as required, but it is a "metadata", so // it goes after the "data" above. contentDescription: String?, // modifier is the first optional parameter modifier: Modifier = Modifier, // tint is optional, default value uses theme-like composition locals // so it's clear where it's coming from and to change it tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current) ) ``` -------------------------------- ### Before: Stateless Parameters and Event Callbacks Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md Illustrates a composable function with separate parameters for scroll position, scroll range, and callbacks for changes. This pattern can become unwieldy as the number of parameters increases. ```kotlin @Composable fun VerticalScroller( scrollPosition: Int, scrollRange: Int, onScrollPositionChange: (Int) -> Unit, onScrollRangeChange: (Int) -> Unit ) { ``` -------------------------------- ### Icon Composable Parameter Order Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Demonstrates the recommended parameter order for the Icon composable, with required parameters first, followed by modifier, and then optional parameters. ```kotlin @Composable fun Icon( // image bitmap and contentDescription are required // bitmap goes first since it is the required data for the icon bitmap: ImageBitmap, // contentDescription follows as required, but it is a "metadata", so // it goes after the "data" above. contentDescription: String?, // modifier is the first optional parameter modifier: Modifier = Modifier, // tint is optional, default value uses theme-like composition locals // so it's clear where it's coming from and to change it tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current) ) ``` -------------------------------- ### Incorrect: Missing Modifier Parameter Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Components that emit UI should always include a `modifier` parameter of type `Modifier`. This example shows an `Icon` composable missing this essential parameter. ```kotlin @Composable fun Icon( bitmap: ImageBitmap, // no modifier parameter tint: Color = Color.Black ) ``` -------------------------------- ### Compose RadioGroup Implementation Using Building Blocks Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Demonstrates how a RadioGroup functionality can be achieved using existing Compose building blocks like Column, Row, Modifier.selectableGroup, Text, and RadioButton. This approach highlights the flexibility available to developers and aids in deciding whether a dedicated RadioGroup component is necessary. ```kotlin // Modifier.selectableGroup adds semantics of a radio-group like behavior // accessibility services will treat it as a parent of various options Column(Modifier.selectableGroup()) { options.forEach { item -> Row( modifier = Modifier.selectable( selected = (select.value == item), onClick = { select.value = item } ), verticalAlignment = Alignment.CenterVertically ) { Text(item.toString()) RadioButton( selected = (select.value == item), onClick = { select.value = item } ) } } } ``` -------------------------------- ### Incorrect: Modifier Applied Incorrectly Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md The `modifier` parameter must be applied once as the first modifier to the root-most layout in the component implementation. This example incorrectly applies the modifier after other modifiers and to a child layout. ```kotlin @Composable fun IconButton( buttonBitmap: ImageBitmap, modifier: Modifier = Modifier, tint: Color = Color.Black ) { Box(Modifier.padding(16.dp)) { Icon( buttonBitmap, // modifier should be applied to the outer-most layout // and be the first one in the chain modifier = Modifier.aspectRatio(1f).then(modifier), tint = tint ) } } ``` -------------------------------- ### Compose Component Layering: Building Blocks and High-Level Components Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Illustrates component layering in Compose. It shows single-purpose building blocks like `Checkbox`, `Text`, and `Row`, and a higher-level `CheckboxRow` component that combines them. ```kotlin ``` // single purpose building blocks component @Composable fun Checkbox(...) @Composable fun Text(...) @Composable fun Row(...) // high level component that is more opinionated combination of lower level blocks @Composable fun CheckboxRow(...) { Row { Checkbox(...) Spacer(...) Text(...) } } ``` ``` -------------------------------- ### Use CompositionLocals for Global App/Screen Styling (DO) Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md When using global CompositionLocals for theming, read them in the default expressions of component parameters. This allows users to easily override the values when customizing or wrapping the component. ```kotlin // this is ok: theme is app global class Theme(val mainAppColor: Color) val LocalAppTheme = compositionLocalOf { Theme(Color.Green) } @Composable fun Button( onClick: () -> Unit, // easy to see where the values comes from and change it backgroundColor: Color = LocalAppTheme.current.mainAppColor ) { Box(modifier = Modifier.background(backgroundColor)) { ... } ``` -------------------------------- ### Component Naming with Prefixes Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Avoid company or module prefixes. Use use-case or domain-specific names. If building on a design system, use specification prefixes for similar components with different appearances. ```kotlin // This button is called ContainedButton in the spec // It has no prefix because it is the most common one @Composable fun Button(...) {} // Other variations of buttons below: @Composable fun OutlinedButton(...) {} @Composable fun TextButton(...) {} @Composable fun GlideImage(...) {} ``` ```kotlin // package com.company.project // depends on foundation, DOES NOT depend on material or material3 @Composable fun Button(...) {} // simple name that feel like a first-class button @Composable fun TextField(...) {} // simple name that feel like a first-class TF ``` -------------------------------- ### Composable Button with Slot API Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Demonstrates the recommended way to define a composable function like `Button` using a slot parameter for its content. This provides maximum flexibility for users to define what appears inside the button. ```kotlin @Composable fun Button( onClick: () -> Unit, content: @Composable () -> Unit ) {} // usage Button(onClick = { /* handle the click */}) { Icon(...) } ``` -------------------------------- ### LazyColumn Composable Parameter Order Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Illustrates the parameter order for LazyColumn, showing how to handle optional parameters and a trailing content lambda, with related parameters grouped. ```kotlin @Composable fun LazyColumn( // no required parameters beyond content, modifier is the first optional modifier: Modifier = Modifier, // state is important and is a "data": second optional parameter state: LazyListState = rememberLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, // arrangement and alignment go one-by-one since they are related verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalAlignment: Alignment.Horizontal = Alignment.Start, flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), userScrollEnabled: Boolean = true, // trailing lambda with content content: LazyListScope.() -> Unit ) ``` -------------------------------- ### Use CompositionLocals for Global App/Screen Styling (DON'T) Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Avoid reading global CompositionLocals directly within a component's implementation if it makes opting out of the global style impossible. This is acceptable for app-wide theming but can hinder customization. ```kotlin // this is ok: theme is app global, but... class Theme(val mainAppColor: Color) val LocalAppTheme = compositionLocalOf { Theme(Color.Green) } @Composable fun Button( onClick: () -> Unit, ) { // reading theme in implementation makes it impossible to opt out val buttonColor = LocalAppTheme.current.mainAppColor Box(modifier = Modifier.background(buttonColor)) { ... } ``` -------------------------------- ### Prefer Plain Slots for TabRow Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Use plain slots with parameters instead of DSLs. This approach allows users to leverage familiar tools without sacrificing flexibility. ```kotlin @Composable fun TabRow( tabs: @Composable () -> Unit ) {} @Composable fun Tab(...) // usage TabRow { tabsData.forEach { data -> Tab(...) } } ``` -------------------------------- ### Avoid Grab-Bag Button Styles in Compose Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Demonstrates an anti-pattern of using a single style class with numerous parameters for different button variations. This approach can lead to unmanageable code and unclear usage. ```kotlin // library code class ButtonStyles( /* grab bag of different parameters like colors, paddings, borders */ background: Color, border: BorderStroke, textColor: Color, shape: Shape, contentPadding: PaddingValues ) val PrimaryButtonStyle = ButtonStyle(...) val SecondaryButtonStyle = ButtonStyle(...) val AdditionalButtonStyle = ButtonStyle(...) @Composable fun Button( onClick: () -> Unit, style: ButtonStyle = SecondaryButtonStyle ) { // impl } // usage val myLoginStyle = ButtonStyle(...) Button(style = myLoginStyle) ``` -------------------------------- ### Defining a Layout-Scoped Modifier (weight) Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md Defines a custom weight modifier within a specific layout scope (`MyRowScope`) to ensure it's only applicable within that layout's content lambda. This prevents misuse and clarifies intent. The example shows correct usage within `MyRow` and a compile-time error when attempted in an incompatible scope (`Box`). ```kotlin @LayoutScopeMarker @Stable interface MyRowScope { fun Modifier.weight(weight: Float): Modifier } @Composable fun MyRow(content: @Composable MyRowScope.() -> Unit) { // ... } // The weight modifier can only be used inside MyRowScope. MyRow { Text("Hello", Modifier.weight(1f)) // weight is available on the receiver scope } // This will not compile, as MyRowScope is not present. Box { Text("Hello", Modifier.weight(1f)) // COMPILE ERROR } ``` -------------------------------- ### Name CompositionLocals descriptively Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md When naming `CompositionLocal` keys, use a descriptive noun for the value and avoid using "CompositionLocal" or "Local" as a noun suffix. "Local" may be used as a prefix if no other descriptive name is suitable. ```kotlin // "Local" is used here as an adjective, "Theme" is the noun. val LocalTheme = staticCompositionLocalOf() ``` -------------------------------- ### Simpler API for HorizontalPager Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Even for lazy composition, consider simpler API designs first. HorizontalPager demonstrates a simpler, easier-to-use API that still provides laziness without requiring a DSL. ```kotlin @Composable fun HorizontalPager( // pager still lazily composes pages when needed // but the api is simpler and easier to use; no need for DSL pageContent: @Composable (pageIndex: Int) -> Unit ) {} ``` -------------------------------- ### CAPITALS_AND_UNDERSCORES Naming Convention (Discouraged) Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md Illustrates the discouraged CAPITALS_AND_UNDERSCORES naming convention for constants and enum values, which should be avoided in favor of PascalCase for Jetpack Compose development. ```kotlin const val DEFAULT_KEY_NAME = "__defaultKey" val STRUCTURALLY_EQUAL: ComparisonPolicy = StructurallyEqualsImpl(...) object ReferenceEqual : ComparisonPolicy { // ... } sealed class LoadResult { object Loading : LoadResult() class Done(val result: T) : LoadResult() class Error(val cause: Throwable) : LoadResult() } enum class Status { IDLE, BUSY } ``` -------------------------------- ### Define Button with Slot Parameters Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Define composable functions like Button using slot parameters for content. This provides maximum flexibility for users to customize the button's appearance and behavior. ```kotlin @Composable fun Button( onClick: () -> Unit, content: @Composable () -> Unit ) {} // usage Button(onClick = { /* handle the click */}) { Icon(...) } ``` -------------------------------- ### LazyColumn Composable Parameter Order Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Illustrates the parameter order for LazyColumn, showing no required parameters (beyond content), followed by modifier, state, padding, and other optional parameters, concluding with the trailing content lambda. ```kotlin import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.ScrollableDefaults import androidx.compose.foundation.gestures.FlingBehavior import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.foundation.Arrangement import androidx.compose.ui.unit.dp @Composable fun LazyColumn( // no required parameters beyond content, modifier is the first optional modifier: Modifier = Modifier, // state is important and is a "data": second optional parameter state: LazyListState = rememberLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, // arrangement and alignment go one-by-one since they are related verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalAlignment: Alignment.Horizontal = Alignment.Start, flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), userScrollEnabled: Boolean = true, // trailing lambda with content content: LazyListScope.() -> Unit ) ``` -------------------------------- ### Simple Button with Conditional Defaults Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Use simple if-else expressions for conditional logic when providing default values for parameters like background color and elevation. This approach is suitable for straightforward customization. ```kotlin @Composable fun Button( onClick: () -> Unit, enabled: Boolean = true, backgroundColor = if (enabled) ButtonDefaults.enabledBackgroundColor else ButtonDefaults.disabledBackgroundColor, elevation = if (enabled) ButtonDefaults.enabledElevation else ButtonDefaults.disabledElevation, content: @Composable RowScope.() -> Unit ) {} ``` -------------------------------- ### Use Explicit Parameters for Component Customizations Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Explicitly define parameters for component customizations, even if they have reasonable default values. This makes customization clear and manageable. ```kotlin @Composable fun Button( onClick: () -> Unit, // explicitly asking for explicit parameter that might have // reasonable default value border: BorderStroke = ButtonDefaults.borderStroke, ) { // impl } ``` -------------------------------- ### Avoid CompositionLocals for Component-Specific Customizations Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Avoid using CompositionLocals for component-specific customizations as they introduce implicit dependencies, making components harder to change, test, and use. Prefer explicit parameters instead. ```kotlin // avoid composition locals for component specific customisations // they are implicit. Components become difficult to change, test, use. val LocalButtonBorder = compositionLocalOf(...) @Composable fun Button( onClick: () -> Unit, ) { val border = LocalButtonBorder.current } ``` -------------------------------- ### Composable Layout with Content Lambda Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md Layout functions should use the name 'content' for a single `@Composable` function parameter and place it last to support trailing lambda syntax. ```kotlin @Composable fun SimpleRow( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { ``` -------------------------------- ### Avoid CompositionLocals for Component Customization Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Avoid using CompositionLocals for component-specific customizations as they introduce implicit dependencies, making components harder to change, test, and use. Prefer explicit parameters instead. ```kotlin val LocalButtonBorder = compositionLocalOf(...) @Composable fun Button( onClick: () -> Unit, ) { val border = LocalButtonBorder.current } ``` -------------------------------- ### Fancy Button Composable Element (Do) Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md This composable function adheres to the 'Elements return Unit' guideline by emitting its root UI node and accepting behavior callbacks as parameters. ```kotlin @Composable fun FancyButton( text: String, onClick: () -> Unit, modifier: Modifier = Modifier ) ``` -------------------------------- ### Button with Single Content Slot Overload Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Presents a common pattern where a component provides an overload with a single `content` slot. This is useful for components that manage the layout of multiple internal elements, offering flexibility in how the content is arranged. ```kotlin @Composable fun Button( onClick: () -> Unit, content: @Composable () -> Unit ) {} // usage Button(onClick = { /* handle the click */}) { Row { Icon(...) Text(...) } } ``` -------------------------------- ### Button with Single Content Slot Overload Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Provide an overload for components that manage multiple slots, using a single 'content' slot. This simplifies usage for common cases while retaining flexibility. ```kotlin @Composable fun Button( onClick: () -> Unit, content: @Composable () -> Unit ) {} // usage Button(onClick = { /* handle the click */}) { Row { Icon(...) Text(...) } } ``` -------------------------------- ### Flexible Button with Slot APIs Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Implement a flexible Button component by accepting composable lambdas for text and icon slots. This allows users to provide any composable content. ```kotlin @Composable fun Button( onClick: () -> Unit, text: @Composable () -> Unit, icon: @Composable () -> Unit ) {} ``` -------------------------------- ### Use Plain Slots for TabRow Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Implement TabRow using plain @Composable lambdas for a simpler and more flexible API. ```kotlin @Composable fun TabRow( tabs: @Composable () -> Unit ) {} @Composable fun Tab(...) {} // usage TabRow { tabsData.forEach { data -> Tab(...) } } ``` -------------------------------- ### Button with Complex Color Logic Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md Isolate complex conditional logic for parameters like colors into dedicated domain-specific classes (e.g., ButtonColors). Use default factories (e.g., ButtonDefaults.colors()) to provide instances of these classes, allowing for more granular control over states. ```kotlin class ButtonColors( backgroundColor: Color, disabledBackgroundColor: Color, contentColor: Color, disabledContentColor: Color ) { fun backgroundColor(enabled: Boolean): Color { ... } fun contentColor(enabled: Boolean): Color { ... } } object ButtonDefaults { // default factory for the class // can be @Composable to access the theme composition locals fun colors( backgroundColor: Color = ..., disabledBackgroundColor: Color = ..., contentColor: Color = ..., disabledContentColor: Color = ... ): ButtonColors { ... } } @Composable fun Button( onClick: () -> Unit, enabled: Boolean = true, colors: ButtonColors = ButtonDefaults.colors(), content: @Composable RowScope.() -> Unit ) { val resolvedBackgroundColor = colors.backgroundColor(enabled) } ``` -------------------------------- ### Composable function with state passed as parameter Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md This pattern demonstrates how to communicate with a composable by passing parameters forward, allowing for aggregation of state into types used as parameters for callers. ```kotlin interface DetailCardState { val actionRailState: ActionRailState // ... } @Composable fun DetailCard(state: DetailCardState) { Surface { // ... ActionRail(state.actionRailState) } } @Composable fun ActionRail(state: ActionRailState) { // ... } ``` -------------------------------- ### Correct Lifecycle Management with Custom Layout Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md Use a custom layout to relayout existing composable instances when state changes, preserving their lifecycle. This approach ensures that the 'content' composable is not disposed and recomposed unnecessarily. ```kotlin @Composable fun PreferenceItem( checked: Boolean, content: @Composable () -> Unit ) { Layout({ Text("Preference item") content() }) { // custom layout that relayouts the same instance of `content` // when `checked` changes } } ``` -------------------------------- ### After: Hoisted State Type for VerticalScroller Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md Demonstrates refactoring to use a hoisted state type, `VerticalScrollerState`, which groups related state properties. This improves organization and allows for custom get/set behaviors. ```kotlin @Stable interface VerticalScrollerState { var scrollPosition: Int var scrollRange: Int } @Composable fun VerticalScroller( verticalScrollerState: VerticalScrollerState ) { ``` -------------------------------- ### Accessibility of the BadgedBox Component Source: https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md This section outlines accessibility best practices for the BadgedBox component, including semantics merging and accessibility-related parameters. ```APIDOC ## Accessibility of the BadgedBox Component ### Semantics Merging Jetpack Compose uses semantics merging for accessibility purposes. To manually create a node that will merge all of its children, you can set a `Modifier.semantics(mergeDescendants = true)` modifier to your component. This will force all non-merging children to collect and pass the data to your component, so it will be treated as a single entity. Some foundation-layer modifiers merge descendants by default (example: `Modifier.clickable` or `Modifier.toggleable`). ### Accessibility Related Parameters For especially common accessibility needs, developers might want to accept some accessibility-related parameters to let users help to provide better accessibility. This is especially true for leaf components like `Image` or `Icon`. `Image` has a required parameter `contentDescription` to signal to the user the need to pass the necessary description for an image. When developing components, developers need to make a conscious decision on what to build in in the implementation vs what to ask from the user via parameters. Note that if you follow the normal best practice of providing an ordinary Modifier parameter and put it on your root layout element, this on its own provides a large amount of implicit accessibility customizability. Because the user of your component can provide their own `Modifier.semantics` which will apply to your component. In addition, this also provides a way for developers to override a portion of your component’s default semantics: if there are two `SemanticsProperties` with identical keys on one modifier chain, Compose resolves the conflict by having the first one win and the later ones ignored. Therefore, you don’t need to add a parameter for every possible semantics your component might need. You should reserve them for especially common cases where it would be inconvenient to write out the `semantics` block every time, or use cases where for some reason the Modifier mechanism doesn’t work (for example, you need to add semantics to an inner child of your component). ### Accessibility Tuning While basic accessibility capabilities will be granted by using foundation layer building blocks, there’s a potential for developers to make the component more accessible. There are specific semantics expected for individual categories of components: simple components typically require 1-3 semantics, whereas more complex components like text fields, scroll containers or time/date pickers require a very rich set of semantics to function correctly with screenreaders. When developing a new custom component, first consider which of the existing standard Compose components it’s most similar to, and imitating the semantics provided by that component’s implementation, and the exact foundation building blocks it uses. Go from there to fine-tune and add more semantical actions and/or properties when needed. ``` -------------------------------- ### Accessibility of the BadgedBox Component Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-component-api-guidelines.md This section covers accessibility best practices for the BadgedBox component, including semantics merging and accessibility-related parameters. ```APIDOC ## Accessibility of the `BadgedBox` Component ### Semantics Merging Jetpack Compose uses semantics merging for accessibility purposes. To manually create a node that will merge all of its children, you can set a `Modifier.semantics(mergeDescendants = true)` modifier to your component. This will force all non-merging children to collect and pass the data to your component, so it will be treated as a single entity. Some foundation-layer modifiers merge descendants by default (example: `Modifier.clickable` or `Modifier.toggleable`). ### Accessibility Related Parameters For especially common accessibility needs, developers might want to accept some accessibility-related parameters to let users help to provide better accessibility. This is especially true for leaf components like `Image` or `Icon`. `Image` has a required parameter `contentDescription` to signal to the user the need to pass the necessary description for an image. When developing components, developers need to make a conscious decision on what to build in in the implementation vs what to ask from the user via parameters. Note that if you follow the normal best practice of providing an ordinary Modifier parameter and put it on your root layout element, this on its own provides a large amount of implicit accessibility customizability. Because the user of your component can provide their own `Modifier.semantics` which will apply to your component. In addition, this also provides a way for developers to override a portion of your component’s default semantics: if there are two `SemanticsProperties` with identical keys on one modifier chain, Compose resolves the conflict by having the first one win and the later ones ignored. Therefore, you don’t need to add a parameter for every possible semantics your component might need. You should reserve them for especially common cases where it would be inconvenient to write out the `semantics` block every time, or use cases where for some reason the Modifier mechanism doesn’t work (for example, you need to add semantics to an inner child of your component). ### Accessibility Tuning While basic accessibility capabilities will be granted by using foundation layer building blocks, there’s a potential for developers to make the component more accessible. There are specific semantics expected for individual categories of components: simple components typically require 1-3 semantics, whereas more complex components like text fields, scroll containers or time/date pickers require a very rich set of semantics to function correctly with screenreaders. When developing a new custom component, first consider which of the existing standard Compose components it’s most similar to, and imitating the semantics provided by that component’s implementation, and the exact foundation building blocks it uses. Go from there to fine-tune and add more semantical actions and/or properties when needed. ``` -------------------------------- ### PascalCase Naming for Constants and Enum Values Source: https://android.googlesource.com/platform/frameworks/support/%2B/androidx-main/compose/docs/compose-api-guidelines.md Demonstrates the recommended PascalCase naming convention for constants, immutable values, sealed class objects, and enum class values in Jetpack Compose framework development. This applies to top-level vals, companion objects, and enum entries. ```kotlin const val DefaultKeyName = "__defaultKey" val StructurallyEqual: ComparisonPolicy = StructurallyEqualsImpl(...) object ReferenceEqual : ComparisonPolicy { // ... } sealed class LoadResult { object Loading : LoadResult() class Done(val result: T) : LoadResult() class Error(val cause: Throwable) : LoadResult() } enum class Status { Idle, Busy } ```