androidx.compose.foundation

Interfaces

AndroidExternalSurfaceScope

AndroidExternalSurfaceScope is a scoped environment provided when an AndroidExternalSurface or AndroidEmbeddedExternalSurface is first initialized.

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 Modifier.Node instances to render visual effects that occur when certain interactions happens.

Cmn
MarqueeSpacing

Defines a calculateSpacing method that determines the space after the end of basicMarquee content before drawing the content again.

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 OverscrollEffects.

Cmn
SurfaceCoroutineScope

SurfaceCoroutineScope is a scoped environment provided by AndroidExternalSurface and AndroidEmbeddedExternalSurface when a new Surface is created.

android
SurfaceScope

SurfaceScope is a scoped environment provided by AndroidExternalSurface and AndroidEmbeddedExternalSurface to handle Surface lifecycle events.

android

Classes

AndroidExternalSurfaceZOrder

Defines the z-order of an AndroidExternalSurface.

android
BorderStroke

Class to specify the stroke to draw border with.

Cmn
MarqueeAnimationMode

Specifies when the basicMarquee animation runs.

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 basicMarquee parameters.

Cmn

Annotations

Enums

MutatePriority

Priorities for performing mutation on state.

Cmn

Top-level functions summary

Unit
@Composable
AndroidEmbeddedExternalSurface(
    modifier: Modifier,
    isOpaque: Boolean,
    surfaceSize: IntSize,
    transform: Matrix?,
    onInit: AndroidExternalSurfaceScope.() -> Unit
)

Provides a dedicated drawing Surface embedded directly in the UI hierarchy.

android
Unit
@Composable
AndroidExternalSurface(
    modifier: Modifier,
    isOpaque: Boolean,
    surfaceSize: IntSize,
    zOrder: AndroidExternalSurfaceZOrder,
    isSecure: Boolean,
    onInit: AndroidExternalSurfaceScope.() -> Unit
)

Provides a dedicated drawing Surface as a separate layer positioned by default behind the window holding the AndroidExternalSurface composable.

android
Unit
@Composable
@ExperimentalFoundationApi
BasicTooltipBox(
    positionProvider: PopupPositionProvider,
    tooltip: @Composable () -> Unit,
    state: BasicTooltipState,
    modifier: Modifier,
    focusable: Boolean,
    enableUserInput: Boolean,
    content: @Composable () -> Unit
)

BasicTooltipBox that wraps a composable with a tooltip.

Cmn
android
BasicTooltipState
@ExperimentalFoundationApi
BasicTooltipState(
    initialIsVisible: Boolean,
    isPersistent: Boolean,
    mutatorMutex: MutatorMutex
)

Constructor extension function for BasicTooltipState

Cmn
BorderStroke
BorderStroke(width: Dp, color: Color)

Create BorderStroke class with width and Color

Cmn
Unit
@Composable
Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit)

Component that allow you to specify an area on the screen and perform canvas drawing on this area.

Cmn
Unit
@Composable
Canvas(modifier: Modifier, contentDescription: String, onDraw: DrawScope.() -> Unit)

Component that allow you to specify an area on the screen and perform canvas drawing on this area.

Cmn
Unit
@Composable
@NonRestartableComposable
Image(
    imageVector: ImageVector,
    contentDescription: String?,
    modifier: Modifier,
    alignment: Alignment,
    contentScale: ContentScale,
    alpha: Float,
    colorFilter: ColorFilter?
)

A composable that lays out and draws a given ImageVector.

Cmn
Unit
@Composable
Image(
    painter: Painter,
    contentDescription: String?,
    modifier: Modifier,
    alignment: Alignment,
    contentScale: ContentScale,
    alpha: Float,
    colorFilter: ColorFilter?
)

Creates a composable that lays out and draws a given Painter.

Cmn
Unit
@Composable
@NonRestartableComposable
Image(
    bitmap: ImageBitmap,
    contentDescription: String?,
    modifier: Modifier,
    alignment: Alignment,
    contentScale: ContentScale,
    alpha: Float,
    colorFilter: ColorFilter?,
    filterQuality: FilterQuality
)

A composable that lays out and draws a given ImageBitmap.

Cmn
MarqueeSpacing
MarqueeSpacing(spacing: Dp)

A MarqueeSpacing with a fixed size.

Cmn
Unit
checkScrollableContainerConstraints(
    constraints: Constraints,
    orientation: Orientation
)
Cmn
Boolean

This function should be used to help build responsive UIs that follow the system setting, to avoid harsh contrast changes when switching between applications.

Cmn
BasicTooltipState
@Composable
@ExperimentalFoundationApi
rememberBasicTooltipState(
    initialIsVisible: Boolean,
    isPersistent: Boolean,
    mutatorMutex: MutatorMutex
)

Create and remember the default BasicTooltipState.

Cmn
OverscrollEffect?

Returns a remembered OverscrollEffect created from the current value of LocalOverscrollFactory.

Cmn
OverscrollFactory
@Composable
rememberPlatformOverscrollFactory(
    glowColor: Color,
    glowDrawPadding: PaddingValues
)

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.

android
ScrollState

Create and remember the ScrollState based on the currently appropriate scroll configuration to allow changing scroll position or observing scroll behavior.

Cmn

Extension functions summary

Modifier
Modifier.background(color: Color, shape: Shape)

Draws shape with a solid color behind the content.

Cmn
Modifier
Modifier.background(
    brush: Brush,
    shape: Shape,
    alpha: @FloatRange(from = 0.0, to = 1.0) Float
)

Draws shape with brush behind the content.

Cmn
Modifier
Modifier.basicMarquee(
    iterations: Int,
    animationMode: MarqueeAnimationMode,
    repeatDelayMillis: Int,
    initialDelayMillis: Int,
    spacing: MarqueeSpacing,
    velocity: Dp
)

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 border and a shape and clip it.

Cmn
Modifier
Modifier.border(width: Dp, brush: Brush, shape: Shape)

Modify element to add border with appearance specified with a width, a brush and a shape and clip it.

Cmn
Modifier
Modifier.border(width: Dp, color: Color, shape: Shape)

Modify element to add border with appearance specified with a width, a color and a shape and clip it.

Cmn
Modifier
Modifier.clickable(
    enabled: Boolean,
    onClickLabel: String?,
    role: Role?,
    onClick: () -> Unit
)

Configure component to receive clicks via input or accessibility "click" event.

Cmn
Modifier
Modifier.clickable(
    interactionSource: MutableInteractionSource?,
    indication: Indication?,
    enabled: Boolean,
    onClickLabel: String?,
    role: Role?,
    onClick: () -> Unit
)

Configure component to receive clicks via input or accessibility "click" event.

Cmn
Modifier

Clips bounds of scrollable container on main axis while leaving space for background effects (like shadows) on cross axis.

Cmn
Modifier
Modifier.combinedClickable(
    enabled: Boolean,
    onClickLabel: String?,
    role: Role?,
    onLongClickLabel: String?,
    onLongClick: (() -> Unit)?,
    onDoubleClick: (() -> Unit)?,
    hapticFeedbackEnabled: Boolean,
    onClick: () -> Unit
)

Configure component to receive clicks, double clicks and long clicks via input or accessibility "click" event.

Cmn
Modifier
Modifier.combinedClickable(
    interactionSource: MutableInteractionSource?,
    indication: Indication?,
    enabled: Boolean,
    onClickLabel: String?,
    role: Role?,
    onLongClickLabel: String?,
    onLongClick: (() -> Unit)?,
    onDoubleClick: (() -> Unit)?,
    hapticFeedbackEnabled: Boolean,
    onClick: () -> Unit
)

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

This function is deprecated. Use systemGestureExclusion

android
Modifier

Creates a focus group or marks this component as a focus group.

Cmn
Modifier
Modifier.focusable(
    enabled: Boolean,
    interactionSource: MutableInteractionSource?
)

Configure component to be focusable via focus system or accessibility "focus" event.

Cmn
Modifier
Modifier.horizontalScroll(
    state: ScrollState,
    enabled: Boolean,
    flingBehavior: FlingBehavior?,
    reverseScrolling: Boolean
)

Modify element to allow to scroll horizontally when width of the content is bigger than max constraints allow.

Cmn
Modifier
Modifier.horizontalScroll(
    state: ScrollState,
    overscrollEffect: OverscrollEffect?,
    enabled: Boolean,
    flingBehavior: FlingBehavior?,
    reverseScrolling: Boolean
)

Modify element to allow to scroll horizontally when width of the content is bigger than max constraints allow.

Cmn
Modifier
Modifier.hoverable(
    interactionSource: MutableInteractionSource,
    enabled: Boolean
)

Configure component to be hoverable via pointer enter/exit events.

Cmn
Modifier
Modifier.indication(
    interactionSource: InteractionSource,
    indication: Indication?
)

Draws visual effects for this component when interactions occur.

Cmn
Modifier
Modifier.magnifier(
    sourceCenter: Density.() -> Offset,
    magnifierCenter: (Density.() -> Offset)?,
    onSizeChanged: ((DpSize) -> Unit)?,
    zoom: Float,
    size: DpSize,
    cornerRadius: Dp,
    elevation: Dp,
    clip: Boolean
)

Shows a Magnifier widget that shows an enlarged version of the content at sourceCenter relative to the current layout node.

android
Modifier

Calls onPositioned whenever the bounds of the currently-focused area changes.

Cmn
Modifier
Modifier.overscroll(overscrollEffect: OverscrollEffect?)

Renders overscroll from the provided overscrollEffect.

Cmn
Modifier

Mark the layout rectangle as preferring to stay clear of floating windows.

android
Modifier

Mark a rectangle within the local layout coordinates preferring to stay clear of floating windows.

android
Modifier

Contains the semantics required for an indeterminate progress indicator, that represents the fact of the in-progress operation.

Cmn
Modifier
Modifier.progressSemantics(
    value: Float,
    valueRange: ClosedFloatingPointRange<Float>,
    steps: @IntRange(from = 0) Int
)

Contains the semantics required for a determinate progress indicator or the progress part of a slider, that represents progress within valueRange.

Cmn
Modifier

Excludes the layout rectangle from the system gesture.

android
Modifier

Excludes a rectangle within the local layout coordinates from the system gesture.

android
Modifier
Modifier.verticalScroll(
    state: ScrollState,
    enabled: Boolean,
    flingBehavior: FlingBehavior?,
    reverseScrolling: Boolean
)

Modify element to allow to scroll vertically when height of the content is bigger than max constraints allow.

Cmn
Modifier
Modifier.verticalScroll(
    state: ScrollState,
    overscrollEffect: OverscrollEffect?,
    enabled: Boolean,
    flingBehavior: FlingBehavior?,
    reverseScrolling: Boolean
)

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 this with an empty OverscrollEffect.node that will not draw / render, but will still handle events.

Cmn
OverscrollEffect

Returns a wrapped version of this that will not handle events / consume values provided through OverscrollEffect.applyToScroll / OverscrollEffect.applyToFling, but will still render / attach OverscrollEffect.node.

Cmn

Top-level properties summary

ProvidableCompositionLocal<Indication>

CompositionLocal that provides an Indication through the hierarchy.

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 OverscrollFactory through the hierarchy.

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 AndroidExternalSurface

isOpaque: Boolean = true

Whether the managed surface should be opaque or transparent. If transparent and isMediaOverlay is false, the surface will be positioned above the parent window.

surfaceSize: IntSize = IntSize.Zero

Sets the surface size independently of the layout size of this AndroidExternalSurface. If set to IntSize.Zero, the surface size will be equal to the AndroidExternalSurface layout size.

transform: Matrix? = null

Sets the transform to apply to the Surface. Some transforms might prevent the content from drawing all the pixels contained within this Composable's bounds. In such situations, make sure to set isOpaque to false.

onInit: AndroidExternalSurfaceScope.() -> Unit

Lambda invoked on first composition. This lambda can be used to declare a AndroidExternalSurfaceScope.onSurface callback that will be invoked when a surface is available.

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:

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 AndroidExternalSurface

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 AndroidExternalSurface. If set to IntSize.Zero, the surface size will be equal to the AndroidExternalSurface layout size.

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 AndroidExternalSurfaceScope.onSurface callback that will be invoked when a surface is available.

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

PopupPositionProvider that will be used to place the tooltip relative to the anchor content.

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 Modifier to be applied to this BasicTooltipBox.

focusable: Boolean = true

Boolean that determines if the tooltip is focusable. When true, the tooltip will consume touch events while it's shown and will have accessibility focus move to the first element of the component. When false, the tooltip won't consume touch events while it's shown but assistive-tech users will need to swipe or drag to get to the first element of the component.

enableUserInput: Boolean = true

Boolean which determines if this BasicTooltipBox will handle long press and mouse hover to trigger the tooltip through the state provided.

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

Boolean that determines if the tooltip associated with this will be persistent or not. If isPersistent is true, then the tooltip will only be dismissed when the user clicks outside the bounds of the tooltip or if BasicTooltipState.dismiss is called. When isPersistent is false, the tooltip will dismiss after a short duration. Ideally, this should be set to true when there is actionable content being displayed within a tooltip.

mutatorMutex: MutatorMutex = BasicTooltipDefaults.GlobalMutatorMutex

MutatorMutex used to ensure that for all of the tooltips associated with the mutator mutex, only one will be shown on the screen at any time.

fun BorderStroke(width: Dp, color: Color): BorderStroke

Create BorderStroke class with width and Color

Parameters
width: Dp

width of the border in Dp. Use Dp.Hairline for one-pixel border.

color: Color

color to paint the border with

@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 Composable function invocation inside it will result to runtime exception

@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 Composable function invocation inside it will result to runtime exception

@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 ImageVector 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 ImageVector in the given bounds defined by the width and height

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 ImageVector

alpha: Float = DefaultAlpha

Optional opacity to be applied to the ImageVector when it is rendered onscreen

colorFilter: ColorFilter? = null

Optional ColorFilter to apply for the ImageVector when it is rendered onscreen

@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 Painter in the given bounds defined by the width and height.

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 Painter

alpha: Float = DefaultAlpha

Optional opacity to be applied to the Painter when it is rendered onscreen the default renders the Painter completely opaque

colorFilter: ColorFilter? = null

Optional colorFilter to apply for the Painter when it is rendered onscreen

@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 ImageBitmap 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 ImageBitmap in the given bounds defined by the width and height

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 ImageBitmap

alpha: Float = DefaultAlpha

Optional opacity to be applied to the ImageBitmap when it is rendered onscreen

colorFilter: ColorFilter? = null

Optional ColorFilter to apply for the ImageBitmap when it is rendered onscreen

filterQuality: FilterQuality = DefaultFilterQuality

Sampling algorithm applied to the bitmap when it is scaled and drawn into the destination. The default is FilterQuality.Low which scales using a bilinear sampling algorithm

fun MarqueeSpacing(spacing: Dp): MarqueeSpacing

A MarqueeSpacing with a fixed size.

checkScrollableContainerConstraints

fun checkScrollableContainerConstraints(
    constraints: Constraints,
    orientation: Orientation
): Unit
Parameters
constraints: Constraints

Constraints used to measure the scrollable container

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

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 also recommended to provide user accessible overrides in your application, so users can choose to force an always-light or always-dark theme. To do this, you should provide the current theme value in a CompositionLocal or similar to components further down your hierarchy, only calling this effect once at the top level if no user override has been set. This also helps avoid multiple calls to this effect, which can be expensive as it queries system configuration.

For example, to draw a white rectangle when in dark theme, and a black rectangle when in light theme:

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

true if the system is considered to be in 'dark theme'.

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

Boolean that determines if the tooltip associated with this will be persistent or not. If isPersistent is true, then the tooltip will only be dismissed when the user clicks outside the bounds of the tooltip or if BasicTooltipState.dismiss is called. When isPersistent is false, the tooltip will dismiss after a short duration. Ideally, this should be set to true when there is actionable content being displayed within a tooltip.

mutatorMutex: MutatorMutex = BasicTooltipDefaults.GlobalMutatorMutex

MutatorMutex used to ensure that for all of the tooltips associated with the mutator mutex, only one will be shown on the screen at any time.

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

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))
Parameters
color: Color

color to paint background with

shape: Shape = RectangleShape

desired shape of the background

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 brush, with 0 being completely transparent and 1 being completely opaque. The value must be between 0 and 1.

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. Int.MAX_VALUE will repeat forever, and 0 will disable animation.

animationMode: MarqueeAnimationMode = Immediately

Whether the marquee should start animating Immediately or only WhileFocused. In WhileFocused mode, the modified node or the content must be made focusable. Note that the initialDelayMillis is part of the animation, so this parameter determines when that initial delay starts counting down, not when the content starts to actually scroll.

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 animationMode is WhileFocused, otherwise the initial delay will be repeatDelayMillis.

spacing: MarqueeSpacing = Spacing

A MarqueeSpacing that specifies how much space to leave at the end of the content before showing the beginning again.

velocity: Dp = Velocity

The speed of the animation in dps / second.

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

BorderStroke class that specifies border appearance, such as size and color

shape: Shape = RectangleShape

shape of the 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 Dp.Hairline for a hairline border.

brush: Brush

brush to paint the border with

shape: Shape

shape of the 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 Dp.Hairline for a hairline border.

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,
    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 version has no MutableInteractionSource or Indication parameters, the default indication from LocalIndication will be used. To specify MutableInteractionSource or Indication, use the other overload.

If you are only creating this clickable modifier inside composition, consider using the other overload and explicitly passing LocalIndication.current for improved performance. For more information see the documentation on the other overload.

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 false, onClick, and this modifier will appear disabled for accessibility services

onClickLabel: String? = null

semantic / accessibility label for the onClick action

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

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?

MutableInteractionSource that will be used to dispatch PressInteraction.Press when this clickable is pressed. If null, an internal MutableInteractionSource will be created if needed.

indication: Indication?

indication to be shown when modified element is pressed. By default, indication from LocalIndication will be used. Pass null to show no indication, or current value from LocalIndication to show theme default

enabled: Boolean = true

Controls the enabled state. When false, onClick, and this modifier will appear disabled for accessibility services

onClickLabel: String? = null

semantic / accessibility label for the onClick action

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,
    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 version has no MutableInteractionSource or Indication parameters, the default indication from LocalIndication will be used. To specify MutableInteractionSource or Indication, use the other overload.

If you are only creating this combinedClickable modifier inside composition, consider using the other overload and explicitly passing LocalIndication.current for improved performance. For more information see the documentation on the other overload.

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 false, onClick, onLongClick or onDoubleClick won't be invoked

onClickLabel: String? = null

semantic / accessibility label for the onClick action

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 action

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 HapticFeedback behavior

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?

MutableInteractionSource that will be used to emit PressInteraction.Press when this clickable is pressed. If null, an internal MutableInteractionSource will be created if needed.

indication: Indication?

indication to be shown when modified element is pressed. By default, indication from LocalIndication will be used. Pass null to show no indication, or current value from LocalIndication to show theme default

enabled: Boolean = true

Controls the enabled state. When false, onClick, onLongClick or onDoubleClick won't be invoked

onClickLabel: String? = null

semantic / accessibility label for the onClick action

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 action

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 HapticFeedback behavior

onClick: () -> Unit

will be called when user clicks on the element

excludeFromSystemGesture

fun Modifier.excludeFromSystemGesture(): Modifier

Excludes the layout rectangle from the system gesture.

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.

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 false, element won't participate in the focus

interactionSource: MutableInteractionSource? = null

MutableInteractionSource that will be used to emit FocusInteraction.Focus when this element is being focused.

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 null, default from ScrollableDefaults.flingBehavior will be used.

reverseScrolling: Boolean = false

reverse the direction of scrolling, when true, 0 ScrollState.value will mean right, when false, 0 ScrollState.value will mean left

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 OverscrollEffect that will be used to render overscroll for this modifier. Note that the OverscrollEffect.node will be applied internally as well - you do not need to use Modifier.overscroll separately.

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 null, default from ScrollableDefaults.flingBehavior will be used.

reverseScrolling: Boolean = false

reverse the direction of scrolling, when true, 0 ScrollState.value will mean right, when false, 0 ScrollState.value will mean left

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

MutableInteractionSource that will be used to emit HoverInteraction.Enter when this element is being hovered.

enabled: Boolean = true

Controls the enabled state. When false, hover events will be ignored.

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

InteractionSource that will be used by indication to draw visual effects - this InteractionSource represents the stream of Interactions for this component.

indication: Indication?

Indication used to draw visual effects. If null, no visual effects will be shown for this component.

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 Magnifier.show.

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 unspecified value, the magnifier widget will be placed at a default offset relative to sourceCenter. The value of that offset is specified by the system.

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 size parameter is left unspecified.

zoom: Float = Float.NaN

See Magnifier.setZoom. Only supported on API 29+.

size: DpSize = DpSize.Unspecified

See Magnifier.Builder.setSize. Only supported on API 29+.

cornerRadius: Dp = Dp.Unspecified

See Magnifier.Builder.setCornerRadius. Only supported on API 29+.

elevation: Dp = Dp.Unspecified

See Magnifier.Builder.setElevation. Only supported on API 29+.

clip: Boolean = true

See Magnifier.Builder.setClippingEnabled. Only supported on API 29+.

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 OverscrollEffect to render

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.

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.

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 provided, value will be coerced to this range. Must not be NaN.

valueRange: ClosedFloatingPointRange<Float> = 0f..1f

range of values that value can take. Passed value will be coerced to this range

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.

systemGestureExclusion

fun Modifier.systemGestureExclusion(): Modifier

Excludes the layout rectangle from the system gesture.

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.

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 null, default from ScrollableDefaults.flingBehavior will be used.

reverseScrolling: Boolean = false

reverse the direction of scrolling, when true, 0 ScrollState.value will mean bottom, when false, 0 ScrollState.value will mean top

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 OverscrollEffect that will be used to render overscroll for this modifier. Note that the OverscrollEffect.node will be applied internally as well - you do not need to use Modifier.overscroll separately.

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 null, default from ScrollableDefaults.flingBehavior will be used.

reverseScrolling: Boolean = false

reverse the direction of scrolling, when true, 0 ScrollState.value will mean bottom, when false, 0 ScrollState.value will mean top

withoutDrawing

fun OverscrollEffect.withoutDrawing(): OverscrollEffect

Returns a wrapped version of this with an empty OverscrollEffect.node that will not draw / render, but 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.withoutDrawing
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 draw. This will be
// used inside LazyColumn to provide events to overscroll, without letting LazyColumn draw the
// overscroll effect internally.
val overscrollWithoutDrawing = overscroll?.withoutDrawing()
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 draw inside the LazyList to receive overscroll
    // events
    overscrollEffect = overscrollWithoutDrawing
)

withoutEventHandling

fun OverscrollEffect.withoutEventHandling(): OverscrollEffect

Returns a wrapped version of this that will not handle events / consume values provided through OverscrollEffect.applyToScroll / OverscrollEffect.applyToFling, but will still render / attach OverscrollEffect.node.

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 withoutDrawing 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
withoutDrawing

Top-level properties

LocalIndication

val LocalIndicationProvidableCompositionLocal<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 LocalOverscrollConfigurationProvidableCompositionLocal<OverscrollConfiguration?>

Composition local to provide configuration for scrolling containers down the hierarchy. null means there will be no overscroll at all.

LocalOverscrollFactory

val LocalOverscrollFactoryProvidableCompositionLocal<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.