androidx.compose.foundation
Interfaces
AndroidExternalSurfaceScope |
|
android
|
BasicTooltipState |
The state that is associated with an instance of a tooltip. |
Cmn
|
Indication |
Indication represents visual effects that occur when certain interactions happens. |
Cmn
|
IndicationInstance |
This interface is deprecated. IndicationInstance has been deprecated along with the rememberUpdatedInstance that returns it. |
Cmn
|
IndicationNodeFactory |
IndicationNodeFactory is an Indication that creates |
Cmn
|
MarqueeSpacing |
Defines a |
Cmn
|
OverscrollEffect |
An OverscrollEffect represents a visual effect that displays when the edges of a scrolling container have been reached with a scroll or fling. |
Cmn
|
OverscrollFactory |
A factory for creating |
Cmn
|
ScrollIndicatorFactory |
A factory for creating a scroll indicator, such as a scrollbar, that can be attached to a scrollable component. |
Cmn
|
ScrollIndicatorState |
Represents the state required to draw a scroll indicator (e.g., a scrollbar), which is typically provided by a scrollable component. |
Cmn
|
SurfaceCoroutineScope |
|
android
|
SurfaceScope |
|
android
|
Classes
AndroidExternalSurfaceZOrder |
Defines the z-order of an |
android
|
BorderStroke |
Class to specify the stroke to draw border with. |
Cmn
|
MarqueeAnimationMode |
Specifies when the |
Cmn
|
MutatorMutex |
Mutual exclusion for UI state mutation over time. |
Cmn
|
OverscrollConfiguration |
This class is deprecated. Providing `OverscrollConfiguration` through `LocalOverscrollConfiguration` to disable / configure overscroll has been replaced with `LocalOverscrollFactory` and `rememberPlatformOverscrollFactory`. |
android
|
ScrollState |
State of the scroll. |
Cmn
|
Objects
BasicTooltipDefaults |
BasicTooltip defaults that contain default values for tooltips created. |
Cmn
|
ComposeFoundationFlags |
This is a collection of flags which are used to guard against regressions in some of the "riskier" refactors or new feature support that is added to this module. |
Cmn
|
MarqueeDefaults |
Namespace for constants representing the default values for various |
Cmn
|
Annotations
Enums
MutatePriority |
Priorities for performing mutation on state. |
Cmn
|
Top-level functions summary
Unit |
@ComposableProvides a dedicated drawing |
android
|
Unit |
@ComposableProvides a dedicated drawing |
android
|
Unit |
@ComposableBasicTooltipBox that wraps a composable with a tooltip. |
Cmn
|
BasicTooltipState |
@ExperimentalFoundationApiConstructor extension function for |
Cmn
|
BorderStroke |
BorderStroke(width: Dp, color: Color)Create |
Cmn
|
Unit |
@ComposableComponent that allow you to specify an area on the screen and perform canvas drawing on this area. |
Cmn
|
Unit |
Component that allow you to specify an area on the screen and perform canvas drawing on this area. |
Cmn
|
Unit |
@ComposableA composable that lays out and draws a given |
Cmn
|
Unit |
@ComposableCreates a composable that lays out and draws a given |
Cmn
|
Unit |
@ComposableA composable that lays out and draws a given |
Cmn
|
MarqueeSpacing |
MarqueeSpacing(spacing: Dp)A |
Cmn
|
Unit |
checkScrollableContainerConstraints( |
Cmn
|
Boolean |
Returns whether the operating system is in dark theme. |
Cmn
|
BasicTooltipState |
@ComposableCreate and remember the default |
Cmn
|
OverscrollEffect? |
Returns a remembered |
Cmn
|
OverscrollFactory |
@ComposableCreates and remembers an instance of the platform |
android
|
ScrollState |
@ComposableCreate and |
Cmn
|
Extension functions summary
Modifier |
Modifier.background(color: Color, shape: Shape) |
Cmn
|
Modifier |
Modifier.background( |
Cmn
|
Modifier |
Modifier.basicMarquee(Applies an animated marquee effect to the modified content if it's too wide to fit in the available space. |
Cmn
|
Modifier |
Modifier.border(border: BorderStroke, shape: Shape)Modify element to add border with appearance specified with a |
Cmn
|
Modifier |
Modify element to add border with appearance specified with a |
Cmn
|
Modifier |
Modify element to add border with appearance specified with a |
Cmn
|
Modifier |
Modifier.clickable(Configure component to receive clicks via input or accessibility "click" event. |
Cmn
|
Modifier |
Modifier.clickable(Configure component to receive clicks via input or accessibility "click" event. |
Cmn
|
Modifier |
Modifier.clipScrollableContainer(orientation: Orientation)Clips bounds of scrollable container on main axis while leaving space for background effects (like shadows) on cross axis. |
Cmn
|
Modifier |
Modifier.combinedClickable(Configure component to receive clicks, double clicks and long clicks via input or accessibility "click" event. |
Cmn
|
Modifier |
Modifier.combinedClickable(Configure component to receive clicks, double clicks and long clicks via input or accessibility "click" event. |
Cmn
|
Modifier |
This function is deprecated. Use systemGestureExclusion |
android
|
Modifier |
Modifier.This function is deprecated. Use systemGestureExclusion |
android
|
Modifier |
Creates a focus group or marks this component as a focus group. |
Cmn
|
Modifier |
Modifier.focusable(Configure component to be focusable via focus system or accessibility "focus" event. |
Cmn
|
Modifier |
Modifier.horizontalScroll(Modify element to allow to scroll horizontally when width of the content is bigger than max constraints allow. |
Cmn
|
Modifier |
Modifier.horizontalScroll(Modify element to allow to scroll horizontally when width of the content is bigger than max constraints allow. |
Cmn
|
Modifier |
Modifier.hoverable(Configure component to be hoverable via pointer enter/exit events. |
Cmn
|
Modifier |
Modifier.indication(Draws visual effects for this component when interactions occur. |
Cmn
|
Modifier |
Modifier.magnifier(Shows a |
android
|
Modifier |
Modifier.onFocusedBoundsChanged(Calls |
Cmn
|
Modifier |
Modifier.overscroll(overscrollEffect: OverscrollEffect?)Renders overscroll from the provided |
Cmn
|
Modifier |
Mark the layout rectangle as preferring to stay clear of floating windows. |
android
|
Modifier |
Modifier.preferKeepClear(rectProvider: (LayoutCoordinates) -> Rect)Mark a rectangle within the local layout coordinates preferring to stay clear of floating windows. |
android
|
Modifier |
Contains the |
Cmn
|
Modifier |
Modifier.progressSemantics(Contains the |
Cmn
|
Modifier |
Modifier.scrollIndicator(A modifier that draws and handles interactions for a scroll indicator (e.g., a scrollbar) defined by the provided |
Cmn
|
Modifier |
Modifier.scrollableArea(Configure a component to act as a scrollable area. |
Cmn
|
Modifier |
Modifier.scrollableArea(Configure a component to act as a scrollable area. |
Cmn
|
Modifier |
Excludes the layout rectangle from the system gesture. |
android
|
Modifier |
Modifier.systemGestureExclusion(exclusion: (LayoutCoordinates) -> Rect)Excludes a rectangle within the local layout coordinates from the system gesture. |
android
|
Modifier |
Modifier.verticalScroll(Modify element to allow to scroll vertically when height of the content is bigger than max constraints allow. |
Cmn
|
Modifier |
Modifier.verticalScroll(Modify element to allow to scroll vertically when height of the content is bigger than max constraints allow. |
Cmn
|
OverscrollEffect |
Returns a wrapped version of |
Cmn
|
OverscrollEffect |
Returns a wrapped version of |
Cmn
|
Top-level properties summary
ProvidableCompositionLocal<Indication> |
CompositionLocal that provides an |
Cmn
|
ProvidableCompositionLocal<OverscrollConfiguration?> |
This property is deprecated. Providing `OverscrollConfiguration` through `LocalOverscrollConfiguration` to disable / configure overscroll has been replaced with `LocalOverscrollFactory` and `rememberPlatformOverscrollFactory`. |
android
|
ProvidableCompositionLocal<OverscrollFactory?> |
CompositionLocal that provides an |
Cmn
|
Top-level functions
AndroidEmbeddedExternalSurface
@Composable
fun AndroidEmbeddedExternalSurface(
modifier: Modifier = Modifier,
isOpaque: Boolean = true,
surfaceSize: IntSize = IntSize.Zero,
transform: Matrix? = null,
onInit: AndroidExternalSurfaceScope.() -> Unit
): Unit
Provides a dedicated drawing Surface embedded directly in the UI hierarchy. Unlike AndroidExternalSurface, AndroidEmbeddedExternalSurface positions its surface as a regular element inside the composable hierarchy. This means that graphics composition is handled like any other UI widget, using the GPU. This can lead to increased power and memory bandwidth usage compared to AndroidExternalSurface. It is therefore recommended to use AndroidExternalSurface over AndroidEmbeddedExternalSurface whenever possible.
AndroidEmbeddedExternalSurface can however be useful when interactions with other widgets is necessary, for instance if the surface needs to be "sandwiched" between two other widgets, or if it must participate in visual effects driven by a Modifier.graphicsLayer{}.
The Surface provided can be used to present content that's external to Compose, such as a video stream (from a camera or a media player), OpenGL, Vulkan... The provided Surface can be rendered into using a thread different from the main thread.
The drawing surface is opaque by default, which can be controlled with the isOpaque parameter.
To start rendering, the caller must first acquire the Surface when it's created. This is achieved by providing the onInit lambda, which allows the caller to register an appropriate AndroidExternalSurfaceScope.onSurface callback. The onInit lambda can also be used to initialize/cache resources needed once a surface is available.
After acquiring a surface, the caller can start rendering into it. Rendering into a surface can be done from any thread.
It is recommended to register the SurfaceScope.onChanged and SurfaceScope.onDestroyed callbacks to properly handle the lifecycle of the surface and react to dimension changes. You must ensure that the rendering thread stops interacting with the surface when the SurfaceScope.onDestroyed callback is invoked.
If a surfaceSize is specified (set to non-IntSize.Zero), the surface will use the specified size instead of the layout size of this composable. The surface will be stretched at render time to fit the layout size. This can be used for instance to render at a lower resolution for performance reasons.
import androidx.compose.foundation.AndroidEmbeddedExternalSurface import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.runtime.withFrameNanos import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.lerp import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.dp AndroidEmbeddedExternalSurface(modifier = Modifier.fillMaxWidth().height(400.dp)) { // Resources can be initialized/cached here // A surface is available, we can start rendering onSurface { surface, width, height -> var w = width var h = height // Initial draw to avoid a black frame surface.lockCanvas(Rect(0, 0, w, h)).apply { drawColor(Color.Yellow.toArgb()) surface.unlockCanvasAndPost(this) } // React to surface dimension changes surface.onChanged { newWidth, newHeight -> w = newWidth h = newHeight } // Cleanup if needed surface.onDestroyed {} // Render loop, automatically cancelled on surface destruction while (true) { withFrameNanos { time -> surface.lockCanvas(Rect(0, 0, w, h)).apply { val timeMs = time / 1_000_000L val t = 0.5f + 0.5f * sin(timeMs / 1_000.0f) drawColor(lerp(Color.Yellow, Color.Red, t).toArgb()) surface.unlockCanvasAndPost(this) } } } } }
| Parameters | |
|---|---|
modifier: Modifier = Modifier |
Modifier to be applied to the |
isOpaque: Boolean = true |
Whether the managed surface should be opaque or transparent. If transparent and isMediaOverlay is |
surfaceSize: IntSize = IntSize.Zero |
Sets the surface size independently of the layout size of this |
transform: Matrix? = null |
Sets the transform to apply to the |
onInit: AndroidExternalSurfaceScope.() -> Unit |
Lambda invoked on first composition. This lambda can be used to declare a |
AndroidExternalSurface
@Composable
fun AndroidExternalSurface(
modifier: Modifier = Modifier,
isOpaque: Boolean = true,
surfaceSize: IntSize = IntSize.Zero,
zOrder: AndroidExternalSurfaceZOrder = AndroidExternalSurfaceZOrder.Behind,
isSecure: Boolean = false,
onInit: AndroidExternalSurfaceScope.() -> Unit
): Unit
Provides a dedicated drawing Surface as a separate layer positioned by default behind the window holding the AndroidExternalSurface composable. Because AndroidExternalSurface uses a separate window layer, graphics composition is handled by the system compositor which can bypass the GPU and provide better performance and power usage characteristics compared to AndroidEmbeddedExternalSurface. It is therefore recommended to use AndroidExternalSurface over AndroidEmbeddedExternalSurface whenever possible.
The Surface provided can be used to present content that's external to Compose, such as a video stream (from a camera or a media player), OpenGL, Vulkan...The provided Surface can be rendered into using a thread different from the main thread.
The z-ordering of the surface can be controlled using the zOrder parameter:
-
AndroidExternalSurfaceZOrder.Behind: positions the surface behind the window -
AndroidExternalSurfaceZOrder.MediaOverlay: positions the surface behind the window but above otherAndroidExternalSurfaceZOrder.Behindsurfaces -
AndroidExternalSurfaceZOrder.OnTop: positions the surface above the window
The drawing surface is opaque by default, which can be controlled with the isOpaque parameter. When the surface is transparent, you may need to change the z-order to see something behind the surface.
To start rendering, the caller must first acquire the Surface when it's created. This is achieved by providing the onInit lambda, which allows the caller to register an appropriate AndroidExternalSurfaceScope.onSurface callback. The onInit lambda can also be used to initialize/cache resources needed once a surface is available.
After acquiring a surface, the caller can start rendering into it. Rendering into a surface can be done from any thread.
It is recommended to register the SurfaceScope.onChanged and SurfaceScope.onDestroyed callbacks to properly handle the lifecycle of the surface and react to dimension changes. You must ensure that the rendering thread stops interacting with the surface when the SurfaceScope.onDestroyed callback is invoked.
If a surfaceSize is specified (set to non-IntSize.Zero), the surface will use the specified size instead of the layout size of this composable. The surface will be stretched at render time to fit the layout size. This can be used for instance to render at a lower resolution for performance reasons.
import androidx.compose.foundation.AndroidExternalSurface import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.runtime.withFrameNanos import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.lerp import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.dp AndroidExternalSurface(modifier = Modifier.fillMaxWidth().height(400.dp)) { // Resources can be initialized/cached here // A surface is available, we can start rendering onSurface { surface, width, height -> var w = width var h = height // Initial draw to avoid a black frame surface.lockCanvas(Rect(0, 0, w, h)).apply { drawColor(Color.Blue.toArgb()) surface.unlockCanvasAndPost(this) } // React to surface dimension changes surface.onChanged { newWidth, newHeight -> w = newWidth h = newHeight } // Cleanup if needed surface.onDestroyed {} // Render loop, automatically cancelled on surface destruction while (true) { withFrameNanos { time -> surface.lockCanvas(Rect(0, 0, w, h)).apply { val timeMs = time / 1_000_000L val t = 0.5f + 0.5f * sin(timeMs / 1_000.0f) drawColor(lerp(Color.Blue, Color.Green, t).toArgb()) surface.unlockCanvasAndPost(this) } } } } }
| Parameters | |
|---|---|
modifier: Modifier = Modifier |
Modifier to be applied to the |
isOpaque: Boolean = true |
Whether the managed surface should be opaque or transparent. |
surfaceSize: IntSize = IntSize.Zero |
Sets the surface size independently of the layout size of this |
zOrder: AndroidExternalSurfaceZOrder = AndroidExternalSurfaceZOrder.Behind |
Sets the z-order of the surface relative to its parent window. |
isSecure: Boolean = false |
Control whether the surface view's content should be treated as secure, preventing it from appearing in screenshots or from being viewed on non-secure displays. |
onInit: AndroidExternalSurfaceScope.() -> Unit |
Lambda invoked on first composition. This lambda can be used to declare a |
BasicTooltipBox
@Composable
@ExperimentalFoundationApi
fun BasicTooltipBox(
positionProvider: PopupPositionProvider,
tooltip: @Composable () -> Unit,
state: BasicTooltipState,
modifier: Modifier = Modifier,
focusable: Boolean = true,
enableUserInput: Boolean = true,
content: @Composable () -> Unit
): Unit
BasicTooltipBox that wraps a composable with a tooltip.
Tooltip that provides a descriptive message for an anchor. It can be used to call the users attention to the anchor.
| Parameters | |
|---|---|
positionProvider: PopupPositionProvider |
|
tooltip: @Composable () -> Unit |
the composable that will be used to populate the tooltip's content. |
state: BasicTooltipState |
handles the state of the tooltip's visibility. |
modifier: Modifier = Modifier |
the |
focusable: Boolean = true |
|
enableUserInput: Boolean = true |
|
content: @Composable () -> Unit |
the composable that the tooltip will anchor to. |
BasicTooltipState
@ExperimentalFoundationApi
fun BasicTooltipState(
initialIsVisible: Boolean = false,
isPersistent: Boolean = true,
mutatorMutex: MutatorMutex = BasicTooltipDefaults.GlobalMutatorMutex
): BasicTooltipState
Constructor extension function for BasicTooltipState
| Parameters | |
|---|---|
initialIsVisible: Boolean = false |
the initial value for the tooltip's visibility when drawn. |
isPersistent: Boolean = true |
|
mutatorMutex: MutatorMutex = BasicTooltipDefaults.GlobalMutatorMutex |
|
BorderStroke
fun BorderStroke(width: Dp, color: Color): BorderStroke
Create BorderStroke class with width and Color
| Parameters | |
|---|---|
width: Dp |
width of the border in |
color: Color |
color to paint the border with |
Canvas
@Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit): Unit
Component that allow you to specify an area on the screen and perform canvas drawing on this area. You MUST specify size with modifier, whether with exact sizes via Modifier.size modifier, or relative to parent, via Modifier.fillMaxSize, ColumnScope.weight, etc. If parent wraps this child, only exact sizes must be specified.
import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.size import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.inset import androidx.compose.ui.unit.dp Canvas(modifier = Modifier.size(100.dp)) { drawRect(Color.Magenta) inset(10.0f) { drawLine( color = Color.Red, start = Offset.Zero, end = Offset(size.width, size.height), strokeWidth = 5.0f, ) } }
| Parameters | |
|---|---|
modifier: Modifier |
mandatory modifier to specify size strategy for this composable |
onDraw: DrawScope.() -> Unit |
lambda that will be called to perform drawing. Note that this lambda will be called during draw stage, you have no access to composition scope, meaning that |
Canvas
@Composable
fun Canvas(modifier: Modifier, contentDescription: String, onDraw: DrawScope.() -> Unit): Unit
Component that allow you to specify an area on the screen and perform canvas drawing on this area. You MUST specify size with modifier, whether with exact sizes via Modifier.size modifier, or relative to parent, via Modifier.fillMaxSize, ColumnScope.weight, etc. If parent wraps this child, only exact sizes must be specified.
import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.size import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp Canvas( contentDescription = "Pie chart: 80% apples, 20% bananas (localized string)", modifier = Modifier.size(300.dp), ) { // Apples (80%) drawCircle(color = Color.Red, radius = size.width / 2) // Bananas (20%) drawArc( color = Color.Yellow, startAngle = 0f, sweepAngle = 360f * 0.20f, useCenter = true, topLeft = Offset(0f, (size.height - size.width) / 2f), size = Size(size.width, size.width), ) }
| Parameters | |
|---|---|
modifier: Modifier |
mandatory modifier to specify size strategy for this composable |
contentDescription: String |
text used by accessibility services to describe what this canvas represents. This should be provided unless the canvas is used for decorative purposes or as part of a larger entity already described in some other way. This text should be localized, such as by using androidx.compose.ui.res.stringResource |
onDraw: DrawScope.() -> Unit |
lambda that will be called to perform drawing. Note that this lambda will be called during draw stage, you have no access to composition scope, meaning that |
Image
@Composable
@NonRestartableComposable
fun Image(
imageVector: ImageVector,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null
): Unit
A composable that lays out and draws a given ImageVector. This will attempt to size the composable according to the ImageVector's given width and height. However, an optional Modifier parameter can be provided to adjust sizing or draw additional content (ex. background). Any unspecified dimension will leverage the ImageVector's size as a minimum constraint.
| Parameters | |
|---|---|
imageVector: ImageVector |
The |
contentDescription: String? |
text used by accessibility services to describe what this image represents. This should always be provided unless this image is used for decorative purposes, and does not represent a meaningful action that a user can take. This text should be localized, such as by using androidx.compose.ui.res.stringResource or similar |
modifier: Modifier = Modifier |
Modifier used to adjust the layout algorithm or draw decoration content (ex. background) |
alignment: Alignment = Alignment.Center |
Optional alignment parameter used to place the |
contentScale: ContentScale = ContentScale.Fit |
Optional scale parameter used to determine the aspect ratio scaling to be used if the bounds are a different size from the intrinsic size of the |
alpha: Float = DefaultAlpha |
Optional opacity to be applied to the |
colorFilter: ColorFilter? = null |
Optional ColorFilter to apply for the |
Image
@Composable
fun Image(
painter: Painter,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null
): Unit
Creates a composable that lays out and draws a given Painter. This will attempt to size the composable according to the Painter's intrinsic size. However, an optional Modifier parameter can be provided to adjust sizing or draw additional content (ex. background)
NOTE a Painter might not have an intrinsic size, so if no LayoutModifier is provided as part of the Modifier chain this might size the Image composable to a width and height of zero and will not draw any content. This can happen for Painter implementations that always attempt to fill the bounds like ColorPainter
import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Paint import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.dp val customPainter = remember { object : Painter() { override val intrinsicSize: Size get() = Size(100.0f, 100.0f) override fun DrawScope.onDraw() { drawRect(color = Color.Cyan) } } } Image( painter = customPainter, contentDescription = "Localized description", modifier = Modifier.size(100.dp, 100.dp), )
| Parameters | |
|---|---|
painter: Painter |
to draw |
contentDescription: String? |
text used by accessibility services to describe what this image represents. This should always be provided unless this image is used for decorative purposes, and does not represent a meaningful action that a user can take. This text should be localized, such as by using androidx.compose.ui.res.stringResource or similar |
modifier: Modifier = Modifier |
Modifier used to adjust the layout algorithm or draw decoration content (ex. background) |
alignment: Alignment = Alignment.Center |
Optional alignment parameter used to place the |
contentScale: ContentScale = ContentScale.Fit |
Optional scale parameter used to determine the aspect ratio scaling to be used if the bounds are a different size from the intrinsic size of the |
alpha: Float = DefaultAlpha |
Optional opacity to be applied to the |
colorFilter: ColorFilter? = null |
Optional colorFilter to apply for the |
Image
@Composable
@NonRestartableComposable
fun Image(
bitmap: ImageBitmap,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DefaultFilterQuality
): Unit
A composable that lays out and draws a given ImageBitmap. This will attempt to size the composable according to the ImageBitmap's given width and height. However, an optional Modifier parameter can be provided to adjust sizing or draw additional content (ex. background). Any unspecified dimension will leverage the ImageBitmap's size as a minimum constraint.
The following sample shows basic usage of an Image composable to position and draw an ImageBitmap on screen
import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size import androidx.compose.ui.graphics.ImageBitmap val ImageBitmap = createTestImage() // Lays out and draws an image sized to the dimensions of the ImageBitmap Image(bitmap = ImageBitmap, contentDescription = "Localized description")
For use cases that require drawing a rectangular subset of the ImageBitmap consumers can use overload that consumes a Painter parameter shown in this sample
import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.Paint import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize val ImageBitmap = createTestImage() // Lays out and draws an image sized to the rectangular subsection of the ImageBitmap Image( painter = BitmapPainter(ImageBitmap, IntOffset(10, 12), IntSize(50, 60)), contentDescription = "Localized description", )
| Parameters | |
|---|---|
bitmap: ImageBitmap |
The |
contentDescription: String? |
text used by accessibility services to describe what this image represents. This should always be provided unless this image is used for decorative purposes, and does not represent a meaningful action that a user can take. This text should be localized, such as by using androidx.compose.ui.res.stringResource or similar |
modifier: Modifier = Modifier |
Modifier used to adjust the layout algorithm or draw decoration content (ex. background) |
alignment: Alignment = Alignment.Center |
Optional alignment parameter used to place the |
contentScale: ContentScale = ContentScale.Fit |
Optional scale parameter used to determine the aspect ratio scaling to be used if the bounds are a different size from the intrinsic size of the |
alpha: Float = DefaultAlpha |
Optional opacity to be applied to the |
colorFilter: ColorFilter? = null |
Optional ColorFilter to apply for the |
filterQuality: FilterQuality = DefaultFilterQuality |
Sampling algorithm applied to the |
checkScrollableContainerConstraints
fun checkScrollableContainerConstraints(
constraints: Constraints,
orientation: Orientation
): Unit
| Parameters | |
|---|---|
constraints: Constraints |
|
orientation: Orientation |
orientation of the scrolling |
| Throws | |
|---|---|
kotlin.IllegalStateException |
if the container was measured with the infinity constraints in the direction of scrolling. This usually means nesting scrollable in the same direction containers which is a performance issue and is discouraged. |
isSystemInDarkTheme
@Composable
fun isSystemInDarkTheme(): Boolean
Returns whether the operating system is in dark theme.
This function should be used to help build responsive UIs that follow the system setting, to avoid harsh contrast changes when switching between applications.
It is recommended for this to be used at the top level of the application, to create theming objects such as a set of colors that can be provided through the hierarchy. This way, screens and components don't need to be aware of the system theme setting, they just consume properties from the theming object. This also makes it easier to support user-defined overrides, such as forcing light / dark theme regardless of the system setting.
import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp val dark = isSystemInDarkTheme() val color = if (dark) Color.White else Color.Black Box { Box(Modifier.size(50.dp).background(color = color)) }
| Returns | |
|---|---|
Boolean |
|
rememberBasicTooltipState
@Composable
@ExperimentalFoundationApi
fun rememberBasicTooltipState(
initialIsVisible: Boolean = false,
isPersistent: Boolean = true,
mutatorMutex: MutatorMutex = BasicTooltipDefaults.GlobalMutatorMutex
): BasicTooltipState
Create and remember the default BasicTooltipState.
| Parameters | |
|---|---|
initialIsVisible: Boolean = false |
the initial value for the tooltip's visibility when drawn. |
isPersistent: Boolean = true |
|
mutatorMutex: MutatorMutex = BasicTooltipDefaults.GlobalMutatorMutex |
|
rememberOverscrollEffect
@Composable
fun rememberOverscrollEffect(): OverscrollEffect?
Returns a remembered OverscrollEffect created from the current value of LocalOverscrollFactory. If LocalOverscrollFactory changes, a new OverscrollEffect will be returned. Returns null if null is provided to LocalOverscrollFactory.
rememberPlatformOverscrollFactory
@Composable
fun rememberPlatformOverscrollFactory(
glowColor: Color = DefaultGlowColor,
glowDrawPadding: PaddingValues = DefaultGlowPaddingValues
): OverscrollFactory
Creates and remembers an instance of the platform OverscrollFactory, with the provided glowColor and glowDrawPadding values - these values will be used on platform versions where glow overscroll is used.
The OverscrollFactory returned from this function should be provided near the top of your application to LocalOverscrollFactory, in order to apply this across all components in your application.
| Parameters | |
|---|---|
glowColor: Color = DefaultGlowColor |
color for the glow effect if the platform effect is a glow effect, otherwise ignored. |
glowDrawPadding: PaddingValues = DefaultGlowPaddingValues |
the amount of padding to apply from the overscroll bounds to the effect before drawing it if the platform effect is a glow effect, otherwise ignored. |
rememberScrollState
@Composable
fun rememberScrollState(initial: Int = 0): ScrollState
Create and remember the ScrollState based on the currently appropriate scroll configuration to allow changing scroll position or observing scroll behavior.
Learn how to control the state of Modifier.verticalScroll or Modifier.horizontalScroll:
import androidx.compose.foundation.gestures.animateScrollBy import androidx.compose.foundation.gestures.scrollBy import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.rememberScrollState import androidx.compose.material.Text import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier // Create ScrollState to own it and be able to control scroll behaviour of scrollable Row below val scrollState = rememberScrollState() val scope = rememberCoroutineScope() Column { Row(Modifier.horizontalScroll(scrollState)) { repeat(1000) { index -> Square(index) } } // Controls for scrolling Row(verticalAlignment = Alignment.CenterVertically) { Text("Scroll") Button(onClick = { scope.launch { scrollState.scrollTo(scrollState.value - 1000) } }) { Text("< -") } Button(onClick = { scope.launch { scrollState.scrollBy(10000f) } }) { Text("--- >") } } Row(verticalAlignment = Alignment.CenterVertically) { Text("Smooth Scroll") Button( onClick = { scope.launch { scrollState.animateScrollTo(scrollState.value - 1000) } } ) { Text("< -") } Button(onClick = { scope.launch { scrollState.animateScrollBy(10000f) } }) { Text("--- >") } } }
| Parameters | |
|---|---|
initial: Int = 0 |
initial scroller position to start with |
Extension functions
background
fun Modifier.background(color: Color, shape: Shape = RectangleShape): Modifier
Draws shape with a solid color behind the content.
import androidx.compose.foundation.background import androidx.compose.foundation.layout.padding import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp Text("Text with background", Modifier.background(color = Color.Magenta).padding(10.dp))
background
fun Modifier.background(
brush: Brush,
shape: Shape = RectangleShape,
alpha: @FloatRange(from = 0.0, to = 1.0) Float = 1.0f
): Modifier
Draws shape with brush behind the content.
import androidx.compose.foundation.background import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CutCornerShape import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp val gradientBrush = Brush.horizontalGradient( colors = listOf(Color.Red, Color.Blue, Color.Green), startX = 0.0f, endX = 500.0f, ) Text( "Text with gradient back", Modifier.background(brush = gradientBrush, shape = CutCornerShape(8.dp)).padding(10.dp), )
| Parameters | |
|---|---|
brush: Brush |
brush to paint background with |
shape: Shape = RectangleShape |
desired shape of the background |
alpha: @FloatRange(from = 0.0, to = 1.0) Float = 1.0f |
Opacity to be applied to the |
basicMarquee
fun Modifier.basicMarquee(
iterations: Int = Iterations,
animationMode: MarqueeAnimationMode = Immediately,
repeatDelayMillis: Int = RepeatDelayMillis,
initialDelayMillis: Int = if (animationMode == Immediately) repeatDelayMillis else 0,
spacing: MarqueeSpacing = Spacing,
velocity: Dp = Velocity
): Modifier
Applies an animated marquee effect to the modified content if it's too wide to fit in the available space. This modifier has no effect if the content fits in the max constraints. The content will be measured with unbounded width.
When the animation is running, it will restart from the initial state any time:
-
any of the parameters to this modifier change, or
-
the content or container size change.
The animation only affects the drawing of the content, not its position. The offset returned by the LayoutCoordinates of anything inside the marquee is undefined relative to anything outside the marquee, and may not match its drawn position on screen. This modifier also does not currently support content that accepts position-based input such as pointer events.
To only animate when the composable is focused, specify animationMode and make the composable focusable. This modifier does not add any visual effects aside from scrolling, but you can add your own by placing modifiers before this one.
import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.width import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp // Marquee only animates when the content doesn't fit in the max width. Column(Modifier.width(30.dp)) { Text("hello world", Modifier.basicMarquee()) }
import androidx.compose.foundation.MarqueeSpacing import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp val edgeWidth = 32.dp fun ContentDrawScope.drawFadedEdge(leftEdge: Boolean) { val edgeWidthPx = edgeWidth.toPx() drawRect( topLeft = Offset(if (leftEdge) 0f else size.width - edgeWidthPx, 0f), size = Size(edgeWidthPx, size.height), brush = Brush.horizontalGradient( colors = listOf(Color.Transparent, Color.Black), startX = if (leftEdge) 0f else size.width, endX = if (leftEdge) edgeWidthPx else size.width - edgeWidthPx, ), blendMode = BlendMode.DstIn, ) } Text( "the quick brown fox jumped over the lazy dogs", Modifier.widthIn(max = edgeWidth * 4) // Rendering to an offscreen buffer is required to get the faded edges' alpha to be // applied only to the text, and not whatever is drawn below this composable (e.g. the // window). .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen } .drawWithContent { drawContent() drawFadedEdge(leftEdge = true) drawFadedEdge(leftEdge = false) } .basicMarquee( // Animate forever. iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(0.dp), ) .padding(start = edgeWidth), )
import androidx.compose.foundation.MarqueeAnimationMode import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.clickable import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.width import androidx.compose.material.Text import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.unit.dp val focusRequester = remember { FocusRequester() } // Marquee only animates when the content doesn't fit in the max width. Column(Modifier.width(30.dp)) { Text( "hello world", Modifier.clickable { focusRequester.requestFocus() } .basicMarquee(animationMode = MarqueeAnimationMode.WhileFocused) .focusRequester(focusRequester) .focusable(), ) }
| Parameters | |
|---|---|
iterations: Int = Iterations |
The number of times to repeat the animation. |
animationMode: MarqueeAnimationMode = Immediately |
Whether the marquee should start animating |
repeatDelayMillis: Int = RepeatDelayMillis |
The duration to wait before starting each subsequent iteration, in millis. |
initialDelayMillis: Int = if (animationMode == Immediately) repeatDelayMillis else 0 |
The duration to wait before starting the first iteration of the animation, in millis. By default, there will be no initial delay if |
spacing: MarqueeSpacing = Spacing |
A |
velocity: Dp = Velocity |
The speed of the animation in dps / second. A positive velocity means that the marquee will animate in the direction of the current |
border
fun Modifier.border(border: BorderStroke, shape: Shape = RectangleShape): Modifier
Modify element to add border with appearance specified with a border and a shape and clip it.
import androidx.compose.foundation.border import androidx.compose.foundation.layout.padding import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp Text("Text with square border", modifier = Modifier.border(4.dp, Color.Magenta).padding(10.dp))
| Parameters | |
|---|---|
border: BorderStroke |
|
shape: Shape = RectangleShape |
shape of the border |
border
fun Modifier.border(width: Dp, brush: Brush, shape: Shape): Modifier
Modify element to add border with appearance specified with a width, a brush and a shape and clip it.
import androidx.compose.foundation.border import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TileMode import androidx.compose.ui.unit.dp val gradientBrush = Brush.horizontalGradient( colors = listOf(Color.Red, Color.Blue, Color.Green), startX = 0.0f, endX = 500.0f, tileMode = TileMode.Repeated, ) Text( "Text with gradient border", modifier = Modifier.border(width = 2.dp, brush = gradientBrush, shape = CircleShape).padding(10.dp), )
import androidx.compose.foundation.border import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CutCornerShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TileMode import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp val widthRange = (1..10) var width by remember { mutableStateOf((widthRange.random()).dp) } val shapes = remember { listOf(CutCornerShape(8.dp), CircleShape, RoundedCornerShape(20)) } var selectedShape by remember { mutableStateOf(shapes.random()) } val colors = listOf( Color.Black, Color.DarkGray, Color.Gray, Color.LightGray, Color.White, Color.Red, Color.Blue, Color.Green, Color.Yellow, Color.Cyan, Color.Magenta, ) var gradientBrush by remember { mutableStateOf( Brush.horizontalGradient( colors = listOf(colors.random(), colors.random(), colors.random()), startX = 0.0f, endX = 500.0f, tileMode = TileMode.Repeated, ) ) } Column(Modifier.padding(2.dp)) { Text(text = "Update border with buttons") Row { Button( modifier = Modifier.width(60.dp), onClick = { width = (widthRange.random()).dp }, ) { Text(fontSize = 8.sp, text = "width") } Button( modifier = Modifier.width(60.dp), onClick = { gradientBrush = Brush.horizontalGradient( colors = listOf(colors.random(), colors.random(), colors.random()), startX = 0.0f, endX = 500.0f, tileMode = TileMode.Repeated, ) }, ) { Text(fontSize = 8.sp, text = "brush") } Button( modifier = Modifier.width(60.dp), onClick = { selectedShape = shapes.random() }, ) { Text(fontSize = 8.sp, text = "shape") } } Text( "Dynamic border", modifier = Modifier.border(width = width, brush = gradientBrush, shape = selectedShape) .padding(10.dp), ) }
| Parameters | |
|---|---|
width: Dp |
width of the border. Use |
brush: Brush |
brush to paint the border with |
shape: Shape |
shape of the border |
border
fun Modifier.border(width: Dp, color: Color, shape: Shape = RectangleShape): Modifier
Modify element to add border with appearance specified with a width, a color and a shape and clip it.
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.border import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CutCornerShape import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp Text( "Text with gradient border", modifier = Modifier.border(border = BorderStroke(2.dp, Color.Blue), shape = CutCornerShape(8.dp)) .padding(10.dp), )
| Parameters | |
|---|---|
width: Dp |
width of the border. Use |
color: Color |
color to paint the border with |
shape: Shape = RectangleShape |
shape of the border |
clickable
fun Modifier.clickable(
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
interactionSource: MutableInteractionSource? = null,
onClick: () -> Unit
): Modifier
Configure component to receive clicks via input or accessibility "click" event.
Add this modifier to the element to make it clickable within its bounds and show a default indication when it's pressed.
This overload will use the Indication from LocalIndication. Use the other overload to explicitly provide an Indication instance. Note that this overload only supports IndicationNodeFactory instances provided through LocalIndication - it is strongly recommended to migrate to IndicationNodeFactory, but you can use the other overload if you still need to support Indication instances that are not IndicationNodeFactory.
If interactionSource is null, an internal MutableInteractionSource will be lazily created only when needed. This reduces the performance cost of clickable during composition, as creating the indication can be delayed until there is an incoming androidx.compose.foundation.interaction.Interaction. If you are only passing a remembered MutableInteractionSource and you are never using it outside of clickable, it is recommended to instead provide null to enable lazy creation. If you need the Indication to be created eagerly, provide a remembered MutableInteractionSource.
If you need to support double click or long click alongside the single click, consider using combinedClickable.
Note Any removal operations on Android Views from clickable should wrap onClick in a post { } block to guarantee the event dispatch completes before executing the removal. (You do not need to do this when removing a composable because Compose guarantees it completes via the snapshot state system.)
import androidx.compose.foundation.clickable import androidx.compose.material.Text import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier val count = remember { mutableStateOf(0) } // content that you want to make clickable Text(text = count.value.toString(), modifier = Modifier.clickable { count.value += 1 })
| Parameters | |
|---|---|
enabled: Boolean = true |
Controls the enabled state. When |
onClickLabel: String? = null |
semantic / accessibility label for the |
role: Role? = null |
the type of user interface element. Accessibility services might use this to describe the element or do customizations |
interactionSource: MutableInteractionSource? = null |
|
onClick: () -> Unit |
will be called when user clicks on the element |
clickable
fun Modifier.clickable(
interactionSource: MutableInteractionSource?,
indication: Indication?,
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
onClick: () -> Unit
): Modifier
Configure component to receive clicks via input or accessibility "click" event.
Add this modifier to the element to make it clickable within its bounds and show an indication as specified in indication parameter.
If interactionSource is null, and indication is an IndicationNodeFactory, an internal MutableInteractionSource will be lazily created along with the indication only when needed. This reduces the performance cost of clickable during composition, as creating the indication can be delayed until there is an incoming androidx.compose.foundation.interaction.Interaction. If you are only passing a remembered MutableInteractionSource and you are never using it outside of clickable, it is recommended to instead provide null to enable lazy creation. If you need indication to be created eagerly, provide a remembered MutableInteractionSource.
If indication is not an IndicationNodeFactory, and instead implements the deprecated Indication.rememberUpdatedInstance method, you should explicitly pass a remembered MutableInteractionSource as a parameter for interactionSource instead of null, as this cannot be lazily created inside clickable.
If you need to support double click or long click alongside the single click, consider using combinedClickable.
Note Any removal operations on Android Views from clickable should wrap onClick in a post { } block to guarantee the event dispatch completes before executing the removal. (You do not need to do this when removing a composable because Compose guarantees it completes via the snapshot state system.)
import androidx.compose.foundation.clickable import androidx.compose.material.Text import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier val count = remember { mutableStateOf(0) } // content that you want to make clickable Text(text = count.value.toString(), modifier = Modifier.clickable { count.value += 1 })
| Parameters | |
|---|---|
interactionSource: MutableInteractionSource? |
|
indication: Indication? |
indication to be shown when modified element is pressed. By default, indication from |
enabled: Boolean = true |
Controls the enabled state. When |
onClickLabel: String? = null |
semantic / accessibility label for the |
role: Role? = null |
the type of user interface element. Accessibility services might use this to describe the element or do customizations |
onClick: () -> Unit |
will be called when user clicks on the element |
clipScrollableContainer
fun Modifier.clipScrollableContainer(orientation: Orientation): Modifier
Clips bounds of scrollable container on main axis while leaving space for background effects (like shadows) on cross axis.
| Parameters | |
|---|---|
orientation: Orientation |
orientation of the scrolling |
combinedClickable
fun Modifier.combinedClickable(
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
onLongClickLabel: String? = null,
onLongClick: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
hapticFeedbackEnabled: Boolean = true,
interactionSource: MutableInteractionSource? = null,
onClick: () -> Unit
): Modifier
Configure component to receive clicks, double clicks and long clicks via input or accessibility "click" event.
Add this modifier to the element to make it clickable within its bounds.
If you need only click handling, and no double or long clicks, consider using clickable
This overload will use the Indication from LocalIndication. Use the other overload to explicitly provide an Indication instance. Note that this overload only supports IndicationNodeFactory instances provided through LocalIndication - it is strongly recommended to migrate to IndicationNodeFactory, but you can use the other overload if you still need to support Indication instances that are not IndicationNodeFactory.
If interactionSource is null, an internal MutableInteractionSource will be lazily created only when needed. This reduces the performance cost of combinedClickable during composition, as creating the indication can be delayed until there is an incoming androidx.compose.foundation.interaction.Interaction. If you are only passing a remembered MutableInteractionSource and you are never using it outside of combinedClickable, it is recommended to instead provide null to enable lazy creation. If you need the Indication to be created eagerly, provide a remembered MutableInteractionSource.
Note, if the modifier instance gets re-used between a key down and key up events, the ongoing input will be aborted.
Note Any removal operations on Android Views from clickable should wrap onClick in a post { } block to guarantee the event dispatch completes before executing the removal. (You do not need to do this when removing a composable because Compose guarantees it completes via the snapshot state system.)
import androidx.compose.foundation.clickable import androidx.compose.material.Text import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier val count = remember { mutableStateOf(0) } // content that you want to make clickable Text(text = count.value.toString(), modifier = Modifier.clickable { count.value += 1 })
| Parameters | |
|---|---|
enabled: Boolean = true |
Controls the enabled state. When |
onClickLabel: String? = null |
semantic / accessibility label for the |
role: Role? = null |
the type of user interface element. Accessibility services might use this to describe the element or do customizations |
onLongClickLabel: String? = null |
semantic / accessibility label for the |
onLongClick: (() -> Unit)? = null |
will be called when user long presses on the element |
onDoubleClick: (() -> Unit)? = null |
will be called when user double clicks on the element |
hapticFeedbackEnabled: Boolean = true |
whether to use the default |
interactionSource: MutableInteractionSource? = null |
|
onClick: () -> Unit |
will be called when user clicks on the element |
combinedClickable
fun Modifier.combinedClickable(
interactionSource: MutableInteractionSource?,
indication: Indication?,
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
onLongClickLabel: String? = null,
onLongClick: (() -> Unit)? = null,
onDoubleClick: (() -> Unit)? = null,
hapticFeedbackEnabled: Boolean = true,
onClick: () -> Unit
): Modifier
Configure component to receive clicks, double clicks and long clicks via input or accessibility "click" event.
Add this modifier to the element to make it clickable within its bounds.
If you need only click handling, and no double or long clicks, consider using clickable.
Add this modifier to the element to make it clickable within its bounds.
If interactionSource is null, and indication is an IndicationNodeFactory, an internal MutableInteractionSource will be lazily created along with the indication only when needed. This reduces the performance cost of clickable during composition, as creating the indication can be delayed until there is an incoming androidx.compose.foundation.interaction.Interaction. If you are only passing a remembered MutableInteractionSource and you are never using it outside of clickable, it is recommended to instead provide null to enable lazy creation. If you need indication to be created eagerly, provide a remembered MutableInteractionSource.
If indication is not an IndicationNodeFactory, and instead implements the deprecated Indication.rememberUpdatedInstance method, you should explicitly pass a remembered MutableInteractionSource as a parameter for interactionSource instead of null, as this cannot be lazily created inside clickable.
Note, if the modifier instance gets re-used between a key down and key up events, the ongoing input will be aborted.
Note Any removal operations on Android Views from clickable should wrap onClick in a post { } block to guarantee the event dispatch completes before executing the removal. (You do not need to do this when removing a composable because Compose guarantees it completes via the snapshot state system.)
import androidx.compose.foundation.clickable import androidx.compose.material.Text import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier val count = remember { mutableStateOf(0) } // content that you want to make clickable Text(text = count.value.toString(), modifier = Modifier.clickable { count.value += 1 })
| Parameters | |
|---|---|
interactionSource: MutableInteractionSource? |
|
indication: Indication? |
indication to be shown when modified element is pressed. By default, indication from |
enabled: Boolean = true |
Controls the enabled state. When |
onClickLabel: String? = null |
semantic / accessibility label for the |
role: Role? = null |
the type of user interface element. Accessibility services might use this to describe the element or do customizations |
onLongClickLabel: String? = null |
semantic / accessibility label for the |
onLongClick: (() -> Unit)? = null |
will be called when user long presses on the element |
onDoubleClick: (() -> Unit)? = null |
will be called when user double clicks on the element |
hapticFeedbackEnabled: Boolean = true |
whether to use the default |
onClick: () -> Unit |
will be called when user clicks on the element |
excludeFromSystemGesture
fun Modifier.excludeFromSystemGesture(): Modifier
Excludes the layout rectangle from the system gesture.
| See also | |
|---|---|
setSystemGestureExclusionRects |
excludeFromSystemGesture
fun Modifier.excludeFromSystemGesture(
exclusion: (LayoutCoordinates) -> Rect
): Modifier
Excludes a rectangle within the local layout coordinates from the system gesture. After layout, exclusion is called to determine the Rect to exclude from the system gesture area.
The LayoutCoordinates of the Modifier's location in the layout is passed as passed as exclusion's parameter.
| See also | |
|---|---|
setSystemGestureExclusionRects |
focusGroup
fun Modifier.focusGroup(): Modifier
Creates a focus group or marks this component as a focus group. This means that when we move focus using the keyboard or programmatically using FocusManager.moveFocus(), the items within the focus group will be given a higher priority before focus moves to items outside the focus group.
In the sample below, each column is a focus group, so pressing the tab key will move focus to all the buttons in column 1 before visiting column 2.
import androidx.compose.foundation.focusGroup import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.ui.Modifier Row { Column(Modifier.focusGroup()) { Button({}) { Text("Row1 Col1") } Button({}) { Text("Row2 Col1") } Button({}) { Text("Row3 Col1") } } Column(Modifier.focusGroup()) { Button({}) { Text("Row1 Col2") } Button({}) { Text("Row2 Col2") } Button({}) { Text("Row3 Col2") } } }
Note: The focusable children of a focusable parent automatically form a focus group. This modifier is to be used when you want to create a focus group where the parent is not focusable. If you encounter a component that uses a focusGroup internally, you can make it focusable by using a focusable modifier. In the second sample here, the LazyRow is a focus group that is not itself focusable. But you can make it focusable by adding a focusable modifier.
import androidx.compose.foundation.border import androidx.compose.foundation.focusable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.foundation.layout.Row import androidx.compose.foundation.lazy.LazyRow import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color.Companion.Black import androidx.compose.ui.graphics.Color.Companion.Red import androidx.compose.ui.unit.dp val interactionSource = remember { MutableInteractionSource() } LazyRow( Modifier.focusable(interactionSource = interactionSource) .border(1.dp, if (interactionSource.collectIsFocusedAsState().value) Red else Black) ) { repeat(10) { item { Button({}) { Text("Button$it") } } } }
focusable
fun Modifier.focusable(
enabled: Boolean = true,
interactionSource: MutableInteractionSource? = null
): Modifier
Configure component to be focusable via focus system or accessibility "focus" event.
Add this modifier to the element to make it focusable within its bounds.
import androidx.compose.foundation.focusable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.foundation.layout.Column import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester // initialize focus reference to be able to request focus programmatically val focusRequester = remember { FocusRequester() } // MutableInteractionSource to track changes of the component's interactions (like "focused") val interactionSource = remember { MutableInteractionSource() } // text below will change when we focus it via button click val isFocused = interactionSource.collectIsFocusedAsState().value val text = if (isFocused) { "Focused! tap anywhere to free the focus" } else { "Bring focus to me by tapping the button below!" } Column { // this Text will change it's text parameter depending on the presence of a focus Text( text = text, modifier = Modifier // add focusRequester modifier before the focusable (or even in the parent) .focusRequester(focusRequester) .focusable(interactionSource = interactionSource), ) Button(onClick = { focusRequester.requestFocus() }) { Text("Bring focus to the text above") } }
| Parameters | |
|---|---|
enabled: Boolean = true |
Controls the enabled state. When |
interactionSource: MutableInteractionSource? = null |
|
horizontalScroll
fun Modifier.horizontalScroll(
state: ScrollState,
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false
): Modifier
Modify element to allow to scroll horizontally when width of the content is bigger than max constraints allow.
import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TileMode import androidx.compose.ui.unit.dp val scrollState = rememberScrollState() val gradient = Brush.horizontalGradient( listOf(Color.Red, Color.Blue, Color.Green), 0.0f, 10000.0f, TileMode.Repeated, ) Box( Modifier.horizontalScroll(scrollState) .size(width = 10000.dp, height = 200.dp) .background(brush = gradient) )
In order to use this modifier, you need to create and own ScrollState
See the other overload in order to provide a custom OverscrollEffect
| Parameters | |
|---|---|
state: ScrollState |
state of the scroll |
enabled: Boolean = true |
whether or not scrolling via touch input is enabled |
flingBehavior: FlingBehavior? = null |
logic describing fling behavior when drag has finished with velocity. If |
reverseScrolling: Boolean = false |
reverse the direction of scrolling, when |
| See also | |
|---|---|
rememberScrollState |
horizontalScroll
fun Modifier.horizontalScroll(
state: ScrollState,
overscrollEffect: OverscrollEffect?,
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false
): Modifier
Modify element to allow to scroll horizontally when width of the content is bigger than max constraints allow.
import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TileMode import androidx.compose.ui.unit.dp val scrollState = rememberScrollState() val gradient = Brush.horizontalGradient( listOf(Color.Red, Color.Blue, Color.Green), 0.0f, 10000.0f, TileMode.Repeated, ) Box( Modifier.horizontalScroll(scrollState) .size(width = 10000.dp, height = 200.dp) .background(brush = gradient) )
In order to use this modifier, you need to create and own ScrollState
| Parameters | |
|---|---|
state: ScrollState |
state of the scroll |
overscrollEffect: OverscrollEffect? |
the |
enabled: Boolean = true |
whether or not scrolling via touch input is enabled |
flingBehavior: FlingBehavior? = null |
logic describing fling behavior when drag has finished with velocity. If |
reverseScrolling: Boolean = false |
reverse the direction of scrolling, when |
| See also | |
|---|---|
rememberScrollState |
hoverable
fun Modifier.hoverable(
interactionSource: MutableInteractionSource,
enabled: Boolean = true
): Modifier
Configure component to be hoverable via pointer enter/exit events.
import androidx.compose.foundation.background import androidx.compose.foundation.hoverable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsHoveredAsState import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp // MutableInteractionSource to track changes of the component's interactions (like "hovered") val interactionSource = remember { MutableInteractionSource() } val isHovered by interactionSource.collectIsHoveredAsState() // the color will change depending on the presence of a hover Box( modifier = Modifier.size(128.dp) .background(if (isHovered) Color.Red else Color.Blue) .hoverable(interactionSource = interactionSource), contentAlignment = Alignment.Center, ) { Text(if (isHovered) "Hovered" else "Unhovered") }
| Parameters | |
|---|---|
interactionSource: MutableInteractionSource |
|
enabled: Boolean = true |
Controls the enabled state. When |
indication
fun Modifier.indication(
interactionSource: InteractionSource,
indication: Indication?
): Modifier
Draws visual effects for this component when interactions occur.
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.clickable import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredHeight import androidx.compose.material.Text import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp val interactionSource = remember { MutableInteractionSource() } Column { Text( text = "Click me and my neighbour will indicate as well!", modifier = Modifier // clickable will dispatch events using MutableInteractionSource .clickable( interactionSource = interactionSource, indication = LocalIndication.current, ) { /** do something */ } .padding(10.dp), ) Spacer(Modifier.requiredHeight(10.dp)) Text( text = "I'm neighbour and I indicate when you click the other one", modifier = Modifier // this element doesn't have a click, but will show default indication from the // CompositionLocal as it accepts the same MutableInteractionSource .indication(interactionSource, LocalIndication.current) .padding(10.dp), ) }
| Parameters | |
|---|---|
interactionSource: InteractionSource |
|
indication: Indication? |
|
magnifier
fun Modifier.magnifier(
sourceCenter: Density.() -> Offset,
magnifierCenter: (Density.() -> Offset)? = null,
onSizeChanged: ((DpSize) -> Unit)? = null,
zoom: Float = Float.NaN,
size: DpSize = DpSize.Unspecified,
cornerRadius: Dp = Dp.Unspecified,
elevation: Dp = Dp.Unspecified,
clip: Boolean = true
): Modifier
Shows a Magnifier widget that shows an enlarged version of the content at sourceCenter relative to the current layout node.
This function returns a no-op modifier on API levels below P (28), since the framework does not support the Magnifier widget on those levels. However, even on higher API levels, not all magnifier features are supported on all platforms. Please refer to parameter explanations below to learn more about supported features on different platform versions.
This function does not allow configuration of source bounds since the magnifier widget does not support constraining to the bounds of composables.
import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.magnifier import androidx.compose.material.Text import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.input.pointer.pointerInput // When the magnifier center position is Unspecified, it is hidden. // Hide the magnifier until a drag starts. var magnifierCenter by remember { mutableStateOf(Offset.Unspecified) } if (Build.VERSION.SDK_INT < 28) { Text("Magnifier is not supported on this platform.") } else { Box( Modifier.magnifier(sourceCenter = { magnifierCenter }, zoom = 2f) .pointerInput(Unit) { detectDragGestures( // Show the magnifier at the original pointer position. onDragStart = { magnifierCenter = it }, // Make the magnifier follow the finger while dragging. onDrag = { _, delta -> magnifierCenter += delta }, // Hide the magnifier when the finger lifts. onDragEnd = { magnifierCenter = Offset.Unspecified }, onDragCancel = { magnifierCenter = Offset.Unspecified }, ) } .drawBehind { // Some concentric circles to zoom in on. for (diameter in 2 until size.maxDimension.toInt() step 10) { drawCircle(color = Color.Black, radius = diameter / 2f, style = Stroke()) } } ) }
| Parameters | |
|---|---|
sourceCenter: Density.() -> Offset |
The offset of the center of the magnified content. Measured in pixels from the top-left of the layout node this modifier is applied to. This offset is passed to |
magnifierCenter: (Density.() -> Offset)? = null |
The offset of the magnifier widget itself, where the magnified content is rendered over the original content. Measured in density-independent pixels from the top-left of the layout node this modifier is applied to. If left null or returns an |
onSizeChanged: ((DpSize) -> Unit)? = null |
An optional callback that will be invoked when the magnifier widget is initialized to report on its actual size. This can be useful when |
zoom: Float = Float.NaN |
See |
size: DpSize = DpSize.Unspecified |
See |
cornerRadius: Dp = Dp.Unspecified |
See |
elevation: Dp = Dp.Unspecified |
See |
clip: Boolean = true |
See |
onFocusedBoundsChanged
fun Modifier.onFocusedBoundsChanged(
onPositioned: (LayoutCoordinates?) -> Unit
): Modifier
Calls onPositioned whenever the bounds of the currently-focused area changes. If a child of this node has focus, onPositioned will be called immediately with a non-null LayoutCoordinates that can be queried for the focused bounds, and again every time the focused child changes or is repositioned. When a child loses focus, onPositioned will be passed null.
When an event occurs, it is bubbled up from the focusable node, so the nearest parent gets the event first, and then its parent, etc.
Note that there may be some cases where the focused bounds change but the callback is not invoked, but the last LayoutCoordinates will always return the most up-to-date bounds.
overscroll
fun Modifier.overscroll(overscrollEffect: OverscrollEffect?): Modifier
Renders overscroll from the provided overscrollEffect.
This modifier attaches the provided overscrollEffect's OverscrollEffect.node to the hierarchy, which renders the actual effect. Note that this modifier is only responsible for the visual part of overscroll - on its own it will not handle input events. In addition to using this modifier you also need to propagate events to the overscrollEffect, most commonly by using a androidx.compose.foundation.gestures.scrollable.
Alternatively, you can use a higher level API such as verticalScroll or androidx.compose.foundation.lazy.LazyColumn and provide a custom OverscrollEffect - these components will both render and provide events to the OverscrollEffect, so you do not need to manually render the effect with this modifier.
import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.spring import androidx.compose.foundation.OverscrollEffect import androidx.compose.foundation.background import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.rememberScrollableState import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.overscroll import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult import androidx.compose.ui.layout.MeasureScope import androidx.compose.ui.node.DelegatableNode import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp // our custom offset overscroll that offset the element it is applied to when we hit the bound // on the scrollable container. class OffsetOverscrollEffect(val scope: CoroutineScope) : OverscrollEffect { private val overscrollOffset = Animatable(0f) override fun applyToScroll( delta: Offset, source: NestedScrollSource, performScroll: (Offset) -> Offset, ): Offset { // in pre scroll we relax the overscroll if needed // relaxation: when we are in progress of the overscroll and user scrolls in the // different direction = substract the overscroll first val sameDirection = sign(delta.y) == sign(overscrollOffset.value) val consumedByPreScroll = if (abs(overscrollOffset.value) > 0.5 && !sameDirection) { val prevOverscrollValue = overscrollOffset.value val newOverscrollValue = overscrollOffset.value + delta.y if (sign(prevOverscrollValue) != sign(newOverscrollValue)) { // sign changed, coerce to start scrolling and exit scope.launch { overscrollOffset.snapTo(0f) } Offset(x = 0f, y = delta.y + prevOverscrollValue) } else { scope.launch { overscrollOffset.snapTo(overscrollOffset.value + delta.y) } delta.copy(x = 0f) } } else { Offset.Zero } val leftForScroll = delta - consumedByPreScroll val consumedByScroll = performScroll(leftForScroll) val overscrollDelta = leftForScroll - consumedByScroll // if it is a drag, not a fling, add the delta left to our over scroll value if (abs(overscrollDelta.y) > 0.5 && source == NestedScrollSource.UserInput) { scope.launch { // multiply by 0.1 for the sake of parallax effect overscrollOffset.snapTo(overscrollOffset.value + overscrollDelta.y * 0.1f) } } return consumedByPreScroll + consumedByScroll } override suspend fun applyToFling( velocity: Velocity, performFling: suspend (Velocity) -> Velocity, ) { val consumed = performFling(velocity) // when the fling happens - we just gradually animate our overscroll to 0 val remaining = velocity - consumed overscrollOffset.animateTo( targetValue = 0f, initialVelocity = remaining.y, animationSpec = spring(), ) } override val isInProgress: Boolean get() = overscrollOffset.value != 0f // Create a LayoutModifierNode that offsets by overscrollOffset.value override val node: DelegatableNode = object : Modifier.Node(), LayoutModifierNode { override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints, ): MeasureResult { val placeable = measurable.measure(constraints) return layout(placeable.width, placeable.height) { val offsetValue = IntOffset(x = 0, y = overscrollOffset.value.roundToInt()) placeable.placeRelativeWithLayer(offsetValue.x, offsetValue.y) } } } } val offset = remember { mutableStateOf(0f) } val scope = rememberCoroutineScope() // Create the overscroll controller val overscroll = remember(scope) { OffsetOverscrollEffect(scope) } // let's build a scrollable that scroll until -512 to 512 val scrollStateRange = (-512f).rangeTo(512f) Box( Modifier.size(150.dp) .scrollable( orientation = Orientation.Vertical, state = rememberScrollableState { delta -> // use the scroll data and indicate how much this element consumed. val oldValue = offset.value // coerce to our range offset.value = (offset.value + delta).coerceIn(scrollStateRange) offset.value - oldValue // indicate that we consumed what's needed }, // pass the overscroll to the scrollable so the data is updated overscrollEffect = overscroll, ) .background(Color.LightGray), contentAlignment = Alignment.Center, ) { Text( offset.value.roundToInt().toString(), style = TextStyle(fontSize = 32.sp), modifier = Modifier // show the overscroll only on the text, not the containers (just for fun) .overscroll(overscroll), ) }
| Parameters | |
|---|---|
overscrollEffect: OverscrollEffect? |
the |
preferKeepClear
fun Modifier.preferKeepClear(): Modifier
Mark the layout rectangle as preferring to stay clear of floating windows.
This Modifier only has an effect on SDK 33 and above.
| See also | |
|---|---|
setPreferKeepClearRects |
preferKeepClear
fun Modifier.preferKeepClear(rectProvider: (LayoutCoordinates) -> Rect): Modifier
Mark a rectangle within the local layout coordinates preferring to stay clear of floating windows. After layout, rectProvider is called to determine the Rect to mark as keep clear.
The LayoutCoordinates of the Modifier's location in the layout is passed as rectProvider's parameter.
This Modifier only has an effect on SDK 33 and above.
| See also | |
|---|---|
setPreferKeepClearRects |
progressSemantics
fun Modifier.progressSemantics(): Modifier
Contains the semantics required for an indeterminate progress indicator, that represents the fact of the in-progress operation.
If you need determinate progress 0.0 to 1.0, consider using overload with the progress parameter.
import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.progressSemantics import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color Box(Modifier.progressSemantics().background(color = Color.Cyan)) { Text("Operation is on progress") }
progressSemantics
fun Modifier.progressSemantics(
value: Float,
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
steps: @IntRange(from = 0) Int = 0
): Modifier
Contains the semantics required for a determinate progress indicator or the progress part of a slider, that represents progress within valueRange. value outside of this range will be coerced into this range.
import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.progressSemantics import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp val progress = 0.5f // emulate progress from some state Box( Modifier.progressSemantics(progress) .size((progress * 100).dp, 4.dp) .background(color = Color.Cyan) )
| Parameters | |
|---|---|
value: Float |
current value of the ProgressIndicator/Slider. If outside of |
valueRange: ClosedFloatingPointRange<Float> = 0f..1f |
range of values that value can take. Passed |
steps: @IntRange(from = 0) Int = 0 |
if greater than 0, specifies the amounts of discrete values, evenly distributed between across the whole value range. If 0, any value from the range specified is allowed. Must not be negative. |
scrollIndicator
fun Modifier.scrollIndicator(
factory: ScrollIndicatorFactory,
state: ScrollIndicatorState,
orientation: Orientation
): Modifier
A modifier that draws and handles interactions for a scroll indicator (e.g., a scrollbar) defined by the provided factory.
The ScrollIndicatorFactory is responsible for creating the UI and behavior of the indicator, while the state provides the necessary data, like scroll offset and content size, to correctly represent the scroll position.
import androidx.compose.foundation.ScrollIndicatorFactory import androidx.compose.foundation.ScrollIndicatorState import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.scrollIndicator import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.node.DelegatableNode import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp // A basic implementation of ScrollIndicator that draws a simple visual scrollbar. data class BasicVisualScrollbarFactory( val thumbThickness: Dp = 8.dp, val padding: Dp = 4.dp, val thumbColor: Color = Color.Gray, val thumbAlpha: Float = 0.5f, ) : ScrollIndicatorFactory { // The node is the core of the ScrollIndicator, handling the drawing logic. override fun createNode( state: ScrollIndicatorState, orientation: Orientation, ): DelegatableNode { return object : Modifier.Node(), DrawModifierNode { override fun ContentDrawScope.draw() { // Draw the original content. drawContent() // Don't draw the scrollbar if the content fits within the viewport. if (state.contentSize <= state.viewportSize) return val visibleContentRatio = state.viewportSize.toFloat() / state.contentSize // Calculate the thumb's size and position along the scrolling axis. val thumbLength = state.viewportSize * visibleContentRatio val thumbPosition = state.scrollOffset * visibleContentRatio val thumbThicknessPx = thumbThickness.toPx() val paddingPx = padding.toPx() // Determine the scrollbar size and thumb position based on the orientation. val (topLeft, size) = when (orientation) { Orientation.Vertical -> { val x = size.width - thumbThicknessPx - paddingPx Offset(x, thumbPosition) to Size(thumbThicknessPx, thumbLength) } Orientation.Horizontal -> { val y = size.height - thumbThicknessPx - paddingPx Offset(thumbPosition, y) to Size(thumbLength, thumbThicknessPx) } } // Draw the scrollbar thumb. drawRect(color = thumbColor, topLeft = topLeft, size = size, alpha = thumbAlpha) } } } } val scrollState = rememberScrollState() val scrollbarFactory = remember { BasicVisualScrollbarFactory() } val scrollbarModifier = scrollState.scrollIndicatorState?.let { Modifier.scrollIndicator( factory = scrollbarFactory, state = it, orientation = Orientation.Vertical, ) } ?: Modifier Column(modifier = Modifier.fillMaxSize().then(scrollbarModifier).verticalScroll(scrollState)) { repeat(50) { Text(text = "Item ${it + 1}", modifier = Modifier.padding(8.dp)) } }
| Parameters | |
|---|---|
factory: ScrollIndicatorFactory |
The |
state: ScrollIndicatorState |
The |
orientation: Orientation |
The scrolling orientation of the container. The indicator will be drawn and interact based on this orientation. |
scrollableArea
fun Modifier.scrollableArea(
state: ScrollableState,
orientation: Orientation,
enabled: Boolean = true,
reverseScrolling: Boolean = false,
flingBehavior: FlingBehavior? = null,
interactionSource: MutableInteractionSource? = null,
bringIntoViewSpec: BringIntoViewSpec? = null
): Modifier
Configure a component to act as a scrollable area. A scrollable area clips its content to its bounds, renders overscroll, and handles scroll gestures such that the content, not the viewport, moves with the user's gestures.
This modifier is a building block for creating custom scrollable containers, and serves as a higher-level abstraction over androidx.compose.foundation.gestures.scrollable. For simpler use cases, prefer higher-level components that are built with scrollableArea, such as verticalScroll and androidx.compose.foundation.lazy.LazyColumn. For example, verticalScroll offsets the content in the viewport out of the box to have scrollable container behavior.
The primary distinction between scrollable and scrollableArea is in how scroll deltas are handled. scrollableArea inverts the deltas to provide a natural "content-moving" experience. For instance, dragging a finger up results in a positive scroll delta, which accommodates content moving upwards within the layout. In contrast, the lower-level scrollable provides raw, un-inverted deltas, which is useful for custom gesture handling that isn't directly tied to content scrolling.
The direction of scrolling is automatically adjusted based on the orientation, the current androidx.compose.ui.platform.LocalLayoutDirection, and the reverseScrolling flag. Setting reverseScrolling to true is useful for layouts that grow from the end of the container to the beginning, like a chat feed. In such cases, the content within the container should also be laid out in reverse. The following table summarizes the resulting scroll delta for a user's drag gesture:
orientation |
LayoutDirection |
reverseScrolling |
User Gesture | Scroll Delta |
Vertical |
Ltr and Rtl |
false |
Drag Up | Positive |
Vertical |
Ltr and Rtl |
true |
Drag Up | Negative |
Horizontal |
Ltr |
false |
Drag Left | Positive |
Horizontal |
Ltr |
true |
Drag Left | Negative |
Horizontal |
Rtl |
false |
Drag Left | Negative |
Horizontal |
Rtl |
true |
Drag Left | Positive |
This scrollableArea overload uses overscroll provided through LocalOverscrollFactory by default. See the other overload to manually provide an OverscrollEffect instance, or disable overscroll.
import androidx.compose.foundation.background import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.ScrollableState import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.scrollableArea import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastCoerceIn // This sample demonstrates how to create custom scrollable containers using the scrollableArea // modifier. // This ScrollableAreaSampleScrollState holds the scroll position and other relevant // information. It implements the ScrollableState interface, making it compatible with the // scrollableArea modifier, and is similar in function to the ScrollState used with // Modifier.verticalScroll. val scrollState = rememberScrollableAreaSampleScrollState() // For lists with many items, consider using a LazyLayout instead Layout( modifier = Modifier.size(150.dp) .scrollableArea(scrollState, Orientation.Vertical) .background(Color.LightGray), content = { repeat(40) { Text( modifier = Modifier.padding(vertical = 2.dp), text = "Item $it", fontSize = 24.sp, textAlign = TextAlign.Center, ) } }, ) { measurables, constraints -> var totalHeight = 0 val childConstraints = constraints.copy(minWidth = 0, minHeight = 0) val placeables = measurables.map { measurable -> val placeable = measurable.measure(childConstraints) totalHeight += placeable.height placeable } val viewportHeight = constraints.maxHeight // Update the maximum scroll value to not scroll beyond limits and stop when scroll // reaches the end. scrollState.maxValue = (totalHeight - viewportHeight).coerceAtLeast(0) // Position the children within the layout. layout(constraints.maxWidth, viewportHeight) { // The current vertical scroll position, in pixels. val scrollY = scrollState.value val viewportCenterY = scrollY + viewportHeight / 2 var placeableLayoutPositionY = 0 placeables.forEach { placeable -> // This sample applies a scaling effect to items based on their distance // from the center, creating a wheel-like effect. val itemCenterY = placeableLayoutPositionY + placeable.height / 2 val distanceFromCenter = abs(itemCenterY - viewportCenterY) val normalizedDistance = (distanceFromCenter / (viewportHeight / 2f)).fastCoerceIn(0f, 1f) // Items scale between 0.4 at the edges of the viewport and 1 at the center. val scaleFactor = 1f - (normalizedDistance * 0.6f) // Place the item horizontally centered with a layer transformation for // scaling to achieve wheel-like effect. placeable.placeRelativeWithLayer( x = constraints.maxWidth / 2 - placeable.width / 2, // Offset y by the scroll position to make placeable visible in the viewport. y = placeableLayoutPositionY - scrollY, ) { scaleX = scaleFactor scaleY = scaleFactor } // Move to the next item's vertical position. placeableLayoutPositionY += placeable.height } } }
| Parameters | |
|---|---|
state: ScrollableState |
The |
orientation: Orientation |
The |
enabled: Boolean = true |
Whether scrolling is enabled. |
reverseScrolling: Boolean = false |
reverses the direction of scrolling. This is useful for experiences where new items appear at the end and the list grows backwards. When |
flingBehavior: FlingBehavior? = null |
logic describing fling behavior when drag has finished with velocity. If |
interactionSource: MutableInteractionSource? = null |
an optional hoisted |
bringIntoViewSpec: BringIntoViewSpec? = null |
The configuration that this scrollable area should use to perform scrolling when scroll requests are received from the focus system. If |
scrollableArea
fun Modifier.scrollableArea(
state: ScrollableState,
orientation: Orientation,
overscrollEffect: OverscrollEffect?,
enabled: Boolean = true,
reverseScrolling: Boolean = false,
flingBehavior: FlingBehavior? = null,
interactionSource: MutableInteractionSource? = null,
bringIntoViewSpec: BringIntoViewSpec? = null
): Modifier
Configure a component to act as a scrollable area. A scrollable area clips its content to its bounds, renders overscroll, and handles scroll gestures such that the content, not the viewport, moves with the user's gestures.
This modifier is a building block for creating custom scrollable containers, and serves as a higher-level abstraction over androidx.compose.foundation.gestures.scrollable. For simpler use cases, prefer higher-level components that are built with scrollableArea, such as verticalScroll and androidx.compose.foundation.lazy.LazyColumn. For example, verticalScroll offsets the content in the viewport out of the box to have scrollable container behavior.
The primary distinction between scrollable and scrollableArea is in how scroll deltas are handled. scrollableArea inverts the deltas to provide a natural "content-moving" experience. For instance, dragging a finger up results in a positive scroll delta, which accommodates content moving upwards within the layout. In contrast, the lower-level scrollable provides raw, un-inverted deltas, which is useful for custom gesture handling that isn't directly tied to content scrolling.
The direction of scrolling is automatically adjusted based on the orientation, the current androidx.compose.ui.platform.LocalLayoutDirection, and the reverseScrolling flag. Setting reverseScrolling to true is useful for layouts that grow from the end of the container to the beginning, like a chat feed. In such cases, the content within the container should also be laid out in reverse. The following table summarizes the resulting scroll delta for a user's drag gesture:
orientation |
LayoutDirection |
reverseScrolling |
User Gesture | Scroll Delta |
Vertical |
Ltr and Rtl |
false |
Drag Up | Positive |
Vertical |
Ltr and Rtl |
true |
Drag Up | Negative |
Horizontal |
Ltr |
false |
Drag Left | Positive |
Horizontal |
Ltr |
true |
Drag Left | Negative |
Horizontal |
Rtl |
false |
Drag Left | Negative |
Horizontal |
Rtl |
true |
Drag Left | Positive |
This overload allows providing OverscrollEffect that will be rendered within the scrollable area. See the other overload of scrollableArea in order to use a default OverscrollEffect provided by LocalOverscrollFactory.
import androidx.compose.foundation.background import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.ScrollableState import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.scrollableArea import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastCoerceIn // This sample demonstrates how to create custom scrollable containers using the scrollableArea // modifier. // This ScrollableAreaSampleScrollState holds the scroll position and other relevant // information. It implements the ScrollableState interface, making it compatible with the // scrollableArea modifier, and is similar in function to the ScrollState used with // Modifier.verticalScroll. val scrollState = rememberScrollableAreaSampleScrollState() // For lists with many items, consider using a LazyLayout instead Layout( modifier = Modifier.size(150.dp) .scrollableArea(scrollState, Orientation.Vertical) .background(Color.LightGray), content = { repeat(40) { Text( modifier = Modifier.padding(vertical = 2.dp), text = "Item $it", fontSize = 24.sp, textAlign = TextAlign.Center, ) } }, ) { measurables, constraints -> var totalHeight = 0 val childConstraints = constraints.copy(minWidth = 0, minHeight = 0) val placeables = measurables.map { measurable -> val placeable = measurable.measure(childConstraints) totalHeight += placeable.height placeable } val viewportHeight = constraints.maxHeight // Update the maximum scroll value to not scroll beyond limits and stop when scroll // reaches the end. scrollState.maxValue = (totalHeight - viewportHeight).coerceAtLeast(0) // Position the children within the layout. layout(constraints.maxWidth, viewportHeight) { // The current vertical scroll position, in pixels. val scrollY = scrollState.value val viewportCenterY = scrollY + viewportHeight / 2 var placeableLayoutPositionY = 0 placeables.forEach { placeable -> // This sample applies a scaling effect to items based on their distance // from the center, creating a wheel-like effect. val itemCenterY = placeableLayoutPositionY + placeable.height / 2 val distanceFromCenter = abs(itemCenterY - viewportCenterY) val normalizedDistance = (distanceFromCenter / (viewportHeight / 2f)).fastCoerceIn(0f, 1f) // Items scale between 0.4 at the edges of the viewport and 1 at the center. val scaleFactor = 1f - (normalizedDistance * 0.6f) // Place the item horizontally centered with a layer transformation for // scaling to achieve wheel-like effect. placeable.placeRelativeWithLayer( x = constraints.maxWidth / 2 - placeable.width / 2, // Offset y by the scroll position to make placeable visible in the viewport. y = placeableLayoutPositionY - scrollY, ) { scaleX = scaleFactor scaleY = scaleFactor } // Move to the next item's vertical position. placeableLayoutPositionY += placeable.height } } }
| Parameters | |
|---|---|
state: ScrollableState |
The |
orientation: Orientation |
The |
overscrollEffect: OverscrollEffect? |
the |
enabled: Boolean = true |
Whether scrolling is enabled. |
reverseScrolling: Boolean = false |
reverses the direction of scrolling. This is useful for experiences where new items appear at the end and the list grows backwards. When |
flingBehavior: FlingBehavior? = null |
logic describing fling behavior when drag has finished with velocity. If |
interactionSource: MutableInteractionSource? = null |
an optional hoisted |
bringIntoViewSpec: BringIntoViewSpec? = null |
The configuration that this scrollable area should use to perform scrolling when scroll requests are received from the focus system. If |
systemGestureExclusion
fun Modifier.systemGestureExclusion(): Modifier
Excludes the layout rectangle from the system gesture.
| See also | |
|---|---|
setSystemGestureExclusionRects |
systemGestureExclusion
fun Modifier.systemGestureExclusion(exclusion: (LayoutCoordinates) -> Rect): Modifier
Excludes a rectangle within the local layout coordinates from the system gesture. After layout, exclusion is called to determine the Rect to exclude from the system gesture area.
The LayoutCoordinates of the Modifier's location in the layout is passed as passed as exclusion's parameter.
| See also | |
|---|---|
setSystemGestureExclusionRects |
verticalScroll
fun Modifier.verticalScroll(
state: ScrollState,
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false
): Modifier
Modify element to allow to scroll vertically when height of the content is bigger than max constraints allow.
import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TileMode import androidx.compose.ui.unit.dp val scrollState = rememberScrollState() val gradient = Brush.verticalGradient( listOf(Color.Red, Color.Blue, Color.Green), 0.0f, 10000.0f, TileMode.Repeated, ) Box( Modifier.verticalScroll(scrollState) .fillMaxWidth() .requiredHeight(10000.dp) .background(brush = gradient) )
In order to use this modifier, you need to create and own ScrollState
See the other overload in order to provide a custom OverscrollEffect
| Parameters | |
|---|---|
state: ScrollState |
state of the scroll |
enabled: Boolean = true |
whether or not scrolling via touch input is enabled |
flingBehavior: FlingBehavior? = null |
logic describing fling behavior when drag has finished with velocity. If |
reverseScrolling: Boolean = false |
reverse the direction of scrolling, when |
| See also | |
|---|---|
rememberScrollState |
verticalScroll
fun Modifier.verticalScroll(
state: ScrollState,
overscrollEffect: OverscrollEffect?,
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false
): Modifier
Modify element to allow to scroll vertically when height of the content is bigger than max constraints allow.
import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TileMode import androidx.compose.ui.unit.dp val scrollState = rememberScrollState() val gradient = Brush.verticalGradient( listOf(Color.Red, Color.Blue, Color.Green), 0.0f, 10000.0f, TileMode.Repeated, ) Box( Modifier.verticalScroll(scrollState) .fillMaxWidth() .requiredHeight(10000.dp) .background(brush = gradient) )
In order to use this modifier, you need to create and own ScrollState
| Parameters | |
|---|---|
state: ScrollState |
state of the scroll |
overscrollEffect: OverscrollEffect? |
the |
enabled: Boolean = true |
whether or not scrolling via touch input is enabled |
flingBehavior: FlingBehavior? = null |
logic describing fling behavior when drag has finished with velocity. If |
reverseScrolling: Boolean = false |
reverse the direction of scrolling, when |
| See also | |
|---|---|
rememberScrollState |
withoutEventHandling
fun OverscrollEffect.withoutEventHandling(): OverscrollEffect
Returns a wrapped version of this that will not handle any incoming events. This means that calls to OverscrollEffect.applyToScroll / OverscrollEffect.applyToFling will directly execute the provided performScroll / performFling lambdas, without the OverscrollEffect ever seeing the incoming values. OverscrollEffect.node will still be attached, so that overscroll can render.
This can be useful if you want to render an OverscrollEffect in a different component that normally provides events to overscroll, such as a androidx.compose.foundation.lazy.LazyColumn. Use this along with withoutVisualEffect to create two wrapped instances: one that does not handle events, and one that does not draw, so you can ensure that the overscroll effect is only rendered once, and only receives events from one source.
| See also | |
|---|---|
withoutVisualEffect |
withoutVisualEffect
fun OverscrollEffect.withoutVisualEffect(): OverscrollEffect
Returns a wrapped version of this with an empty OverscrollEffect.node. This prevents the overscroll effect from applying any visual effect, but it will still handle events.
This can be used along with withoutEventHandling in cases where you wish to change where overscroll is rendered for a given component. Pass this wrapped instance that doesn't render to the component that handles events (such as androidx.compose.foundation.lazy.LazyColumn) to prevent it from drawing the overscroll effect. Then to separately render the original overscroll effect, you can directly pass it to Modifier.overscroll (since that modifier only renders, and does not handle events). If instead you want to draw the overscroll in another component that handles events, such as a different lazy list, you need to first wrap the original overscroll effect with withoutEventHandling to prevent it from also dispatching events.
import androidx.compose.foundation.OverscrollEffect import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.overscroll import androidx.compose.foundation.rememberOverscrollEffect import androidx.compose.foundation.withoutVisualEffect import androidx.compose.material.Text import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.unit.dp val items = remember { (1..100).toList() } val state = rememberLazyListState() val overscroll = rememberOverscrollEffect() // Create a wrapped version of the above overscroll effect that does not apply a visual effect. // This will be used inside LazyColumn to provide events to overscroll, without letting // LazyColumn render the overscroll effect internally. val overscrollWithoutVisualEffect = overscroll?.withoutVisualEffect() LazyColumn( content = { items(items) { Text("Item $it") } }, state = state, modifier = Modifier.size(300.dp) .clip(RectangleShape) // Manually render the overscroll on top of the lazy list _and_ the 'decorations' we // are // manually drawing, to make sure they will also be included in the overscroll // effect. .overscroll(overscroll) .drawBehind { state.layoutInfo.visibleItemsInfo.drop(1).forEach { info -> val verticalOffset = info.offset.toFloat() drawLine( color = Color.Red, start = Offset(0f, verticalOffset), end = Offset(size.width, verticalOffset), ) } }, // Pass the overscroll effect that does not apply a visual effect inside the LazyList to // receive overscroll events overscrollEffect = overscrollWithoutVisualEffect, )
| See also | |
|---|---|
withoutEventHandling |
Top-level properties
LocalIndication
val LocalIndication: ProvidableCompositionLocal<Indication>
CompositionLocal that provides an Indication through the hierarchy. This Indication will be used by default to draw visual effects for interactions such as press and drag in components such as clickable.
By default this will provide a debug indication, this should always be replaced.
LocalOverscrollConfiguration
@ExperimentalFoundationApi
val LocalOverscrollConfiguration: ProvidableCompositionLocal<OverscrollConfiguration?>
Composition local to provide configuration for scrolling containers down the hierarchy. null means there will be no overscroll at all.
LocalOverscrollFactory
val LocalOverscrollFactory: ProvidableCompositionLocal<OverscrollFactory?>
CompositionLocal that provides an OverscrollFactory through the hierarchy. This will be used by default by scrolling components, so you can provide an OverscrollFactory here to override the overscroll used by components within a hierarchy.
See rememberOverscrollEffect to remember an OverscrollEffect from the current provided value.