{% setvar book_path %}/reference/kotlin/androidx/_book.yaml{% endsetvar %} {% include "_shared/_reference-head-tags.html" %}

androidx.compose.foundation

Interfaces

Indication

Indication represents visual effects that occur when certain interactions happens.

Cmn
IndicationInstance

IndicationInstance is a specific instance of an Indication that draws visual effects on certain interactions, such as press or focus.

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

Classes

AtomicLong
Cmn
AtomicReference
Cmn
BorderStroke

Class to specify the stroke to draw border with.

Cmn
MagnifierStyle

Specifies how a magnifier should create the underlying Magnifier widget.

android
MarqueeAnimationMode

Specifies when the basicMarquee animation runs.

Cmn
MutatorMutex

Mutual exclusion for UI state mutation over time.

Cmn
OverscrollConfiguration

Metadata for overscroll effects for android platform.

android
ScrollState

State of the scroll.

Cmn

Annotations

ExperimentalFoundationApi
Cmn
InternalFoundationApi
Cmn

Enums

MutatePriority

Priorities for performing mutation on state.

Cmn

Top-level functions summary

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
@ExperimentalFoundationApi
@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

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
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: Float)

Draws shape with brush behind the content.

Cmn
Modifier
@ExperimentalFoundationApi
Modifier.basicMarquee(
    iterations: Int,
    animationMode: MarqueeAnimationMode,
    delayMillis: 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
@ExperimentalFoundationApi
Modifier.combinedClickable(
    enabled: Boolean,
    onClickLabel: String?,
    role: Role?,
    onLongClickLabel: String?,
    onLongClick: (() -> Unit)?,
    onDoubleClick: (() -> Unit)?,
    onClick: () -> Unit
)

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

Cmn
Modifier
@ExperimentalFoundationApi
Modifier.combinedClickable(
    interactionSource: MutableInteractionSource,
    indication: Indication?,
    enabled: Boolean,
    onClickLabel: String?,
    role: Role?,
    onLongClickLabel: String?,
    onLongClick: (() -> Unit)?,
    onDoubleClick: (() -> Unit)?,
    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.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
@ExperimentalFoundationApi
Modifier.magnifier(
    sourceCenter: Density.() -> Offset,
    magnifierCenter: Density.() -> Offset,
    zoom: Float,
    style: MagnifierStyle,
    onSizeChanged: ((DpSize) -> Unit)?
)

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

Renders overscroll from the provided overscrollEffect.

Cmn
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: 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

Top-level properties summary

Int
Cmn
Int
Cmn
MarqueeSpacing
Cmn
Dp
Cmn
ProvidableCompositionLocal<Indication>

CompositionLocal that provides an Indication through the hierarchy.

Cmn
ProvidableCompositionLocal<OverscrollConfiguration?>

Composition local to provide configuration for scrolling containers down the hierarchy.

android

Top-level functions

BorderStroke

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

Canvas

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

Component that allow you to specify an area on the screen and perform canvas drawing on this area. You MUST specify size with modifier, whether with exact sizes via Modifier.size modifier, or relative to parent, via Modifier.fillMaxSize, ColumnScope.weight, etc. If parent wraps this child, only exact sizes must be specified.

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.inset
import androidx.compose.ui.unit.dp

Canvas(modifier = Modifier.size(100.dp)) {
    drawRect(Color.Magenta)
    inset(10.0f) {
        drawLine(
            color = Color.Red,
            start = Offset.Zero,
            end = Offset(size.width, size.height),
            strokeWidth = 5.0f
        )
    }
}
Parameters
modifier: Modifier

mandatory modifier to specify size strategy for this composable

onDraw: DrawScope.() -> Unit

lambda that will be called to perform drawing. Note that this lambda will be called during draw stage, you have no access to composition scope, meaning that Composable function invocation inside it will result to runtime exception

Canvas

@ExperimentalFoundationApi
@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

Image

@Composable
@NonRestartableComposable
fun Image(
    imageVector: ImageVector,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null
): Unit

A composable that lays out and draws a given ImageVector. This will attempt to size the composable according to the ImageVector's given width and height. However, an optional Modifier parameter can be provided to adjust sizing or draw additional content (ex. background). Any unspecified dimension will leverage the ImageVector's size as a minimum constraint.

Parameters
imageVector: ImageVector

The 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

Image

@Composable
fun Image(
    painter: Painter,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null
): Unit

Creates a composable that lays out and draws a given Painter. This will attempt to size the composable according to the Painter's intrinsic size. However, an optional Modifier parameter can be provided to adjust sizing or draw additional content (ex. background)

NOTE a Painter might not have an intrinsic size, so if no LayoutModifier is provided as part of the Modifier chain this might size the Image composable to a width and height of zero and will not draw any content. This can happen for Painter implementations that always attempt to fill the bounds like ColorPainter

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp

val customPainter = remember {
    object : Painter() {

        override val intrinsicSize: Size
            get() = Size(100.0f, 100.0f)

        override fun DrawScope.onDraw() {
            drawRect(color = Color.Cyan)
        }
    }
}

Image(
    painter = customPainter,
    contentDescription = "Localized description",
    modifier = Modifier.size(100.dp, 100.dp)
)
Parameters
painter: Painter

to draw

contentDescription: String?

text used by accessibility services to describe what this image represents. This should always be provided unless this image is used for decorative purposes, and does not represent a meaningful action that a user can take. This text should be localized, such as by using androidx.compose.ui.res.stringResource or similar

modifier: Modifier = Modifier

Modifier used to adjust the layout algorithm or draw decoration content (ex. background)

alignment: Alignment = Alignment.Center

Optional alignment parameter used to place the 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

Image

@Composable
@NonRestartableComposable
fun Image(
    bitmap: ImageBitmap,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null,
    filterQuality: FilterQuality = DefaultFilterQuality
): Unit

A composable that lays out and draws a given ImageBitmap. This will attempt to size the composable according to the ImageBitmap's given width and height. However, an optional Modifier parameter can be provided to adjust sizing or draw additional content (ex. background). Any unspecified dimension will leverage the ImageBitmap's size as a minimum constraint.

The following sample shows basic usage of an Image composable to position and draw an ImageBitmap on screen

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size
import androidx.compose.ui.graphics.ImageBitmap

val ImageBitmap = createTestImage()
// Lays out and draws an image sized to the dimensions of the ImageBitmap
Image(bitmap = ImageBitmap, contentDescription = "Localized description")

For use cases that require drawing a rectangular subset of the ImageBitmap consumers can use overload that consumes a Painter parameter shown in this sample

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize

val ImageBitmap = createTestImage()
// Lays out and draws an image sized to the rectangular subsection of the ImageBitmap
Image(
    painter = BitmapPainter(
        ImageBitmap,
        IntOffset(10, 12),
        IntSize(50, 60)
    ),
    contentDescription = "Localized description"
)
Parameters
bitmap: ImageBitmap

The 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

MarqueeSpacing

@ExperimentalFoundationApi
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'.

rememberScrollState

@Composable
fun rememberScrollState(initial: Int = 0): ScrollState

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

Learn how to control the state of Modifier.verticalScroll or Modifier.horizontalScroll:

import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Text
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier

// Create ScrollState to own it and be able to control scroll behaviour of scrollable Row below
val scrollState = rememberScrollState()
val scope = rememberCoroutineScope()
Column {
    Row(Modifier.horizontalScroll(scrollState)) {
        repeat(1000) { index ->
            Square(index)
        }
    }
    // Controls for scrolling
    Row(verticalAlignment = Alignment.CenterVertically) {
        Text("Scroll")
        Button(
            onClick = {
                scope.launch { scrollState.scrollTo(scrollState.value - 1000) }
            }
        ) {
            Text("< -")
        }
        Button(
            onClick = {
                scope.launch { scrollState.scrollBy(10000f) }
            }
        ) {
            Text("--- >")
        }
    }
    Row(verticalAlignment = Alignment.CenterVertically) {
        Text("Smooth Scroll")
        Button(
            onClick = {
                scope.launch { scrollState.animateScrollTo(scrollState.value - 1000) }
            }
        ) {
            Text("< -")
        }
        Button(
            onClick = {
                scope.launch { scrollState.animateScrollBy(10000f) }
            }
        ) {
            Text("--- >")
        }
    }
}
Parameters
initial: Int = 0

initial scroller position to start with

Extension functions

background

fun Modifier.background(color: Color, shape: Shape = RectangleShape): Modifier

Draws shape with a solid color behind the content.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

Text(
    "Text with background",
    Modifier.background(color = Color.Magenta).padding(10.dp)
)
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: 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: 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.

basicMarquee

@ExperimentalFoundationApi
fun Modifier.basicMarquee(
    iterations: Int = DefaultMarqueeIterations,
    animationMode: MarqueeAnimationMode = Immediately,
    delayMillis: Int = DefaultMarqueeDelayMillis,
    initialDelayMillis: Int = if (animationMode == Immediately) delayMillis else 0,
    spacing: MarqueeSpacing = DefaultMarqueeSpacing,
    velocity: Dp = DefaultMarqueeVelocity
): 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:

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.

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())
}

To only animate when the composable is focused, specify animationMode and make the composable focusable.

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()
    )
}

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.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)
)
Parameters
iterations: Int = DefaultMarqueeIterations

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.

delayMillis: Int = DefaultMarqueeDelayMillis

The duration to wait before starting each subsequent iteration, in millis.

initialDelayMillis: Int = if (animationMode == Immediately) delayMillis 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 delayMillis.

spacing: MarqueeSpacing = DefaultMarqueeSpacing

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

velocity: Dp = DefaultMarqueeVelocity

The speed of the animation in dps / second.

border

fun Modifier.border(border: BorderStroke, shape: Shape = RectangleShape): Modifier

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

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

Text(
    "Text with  square border",
    modifier = Modifier
        .border(4.dp, Color.Magenta)
        .padding(10.dp)
)

()

Parameters
border: BorderStroke

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

shape: Shape = RectangleShape

shape of the border

border

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

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

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.unit.dp

val gradientBrush = Brush.horizontalGradient(
    colors = listOf(Color.Red, Color.Blue, Color.Green),
    startX = 0.0f,
    endX = 500.0f,
    tileMode = TileMode.Repeated
)
Text(
    "Text with gradient border",
    modifier = Modifier
        .border(width = 2.dp, brush = gradientBrush, shape = CircleShape)
        .padding(10.dp)
)

()

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

val widthRange = (1..10)
var width by remember { mutableStateOf((widthRange.random()).dp) }

val shapes = remember {
    listOf(CutCornerShape(8.dp), CircleShape, RoundedCornerShape(20))
}
var selectedShape by remember { mutableStateOf(shapes.random()) }

val colors = listOf(
    Color.Black,
    Color.DarkGray,
    Color.Gray,
    Color.LightGray,
    Color.White,
    Color.Red,
    Color.Blue,
    Color.Green,
    Color.Yellow,
    Color.Cyan,
    Color.Magenta
)
var gradientBrush by remember {
    mutableStateOf(
        Brush.horizontalGradient(
            colors = listOf(colors.random(), colors.random(), colors.random()),
            startX = 0.0f,
            endX = 500.0f,
            tileMode = TileMode.Repeated
        )
    )
}

Column(Modifier.padding(2.dp)) {
    Text(text = "Update border with buttons")
    Row {
        Button(
            modifier = Modifier.width(60.dp),
            onClick = {

                width = (widthRange.random()).dp
            }
        ) {
            Text(
                fontSize = 8.sp,
                text = "width"
            )
        }
        Button(
            modifier = Modifier.width(60.dp),
            onClick = {
                gradientBrush = Brush.horizontalGradient(
                    colors = listOf(colors.random(), colors.random(), colors.random()),
                    startX = 0.0f,
                    endX = 500.0f,
                    tileMode = TileMode.Repeated
                )
            }
        ) {
            Text(
                fontSize = 8.sp,
                text = "brush"
            )
        }
        Button(
            modifier = Modifier.width(60.dp),
            onClick = {
                selectedShape = shapes.random()
            }
        ) {
            Text(
                fontSize = 8.sp,
                text = "shape"
            )
        }
    }
    Text(
        "Dynamic border",
        modifier = Modifier
            .border(
                width = width,
                brush = gradientBrush,
                shape = selectedShape
            )
            .padding(10.dp)
    )
}

()

Parameters
width: Dp

width of the border. Use Dp.Hairline for a hairline border.

brush: Brush

brush to paint the border with

shape: Shape

shape of the border

border

fun Modifier.border(width: Dp, color: Color, shape: Shape = RectangleShape): Modifier

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

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

Text(
    "Text with gradient border",
    modifier = Modifier
        .border(
            border = BorderStroke(2.dp, Color.Blue),
            shape = CutCornerShape(8.dp)
        )
        .padding(10.dp)
)

()

Parameters
width: Dp

width of the border. Use 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, default indication from LocalIndication will be used. To specify MutableInteractionSource or Indication, use another overload.

If you need to support double click or long click alongside the single click, consider using combinedClickable.

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 you need to support double click or long click alongside the single click, consider using combinedClickable.

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. Only the initial (first) press will be recorded and dispatched with MutableInteractionSource.

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

@ExperimentalFoundationApi
fun Modifier.combinedClickable(
    enabled: Boolean = true,
    onClickLabel: String? = null,
    role: Role? = null,
    onLongClickLabel: String? = null,
    onLongClick: (() -> Unit)? = null,
    onDoubleClick: (() -> Unit)? = null,
    onClick: () -> Unit
): Modifier

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

Add this modifier to the element to make it clickable within its bounds.

If you need only click handling, and no double or long clicks, consider using clickable

This version has no MutableInteractionSource or Indication parameters, default indication from LocalIndication will be used. To specify MutableInteractionSource or Indication, use another overload.

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

onClick: () -> Unit

will be called when user clicks on the element

combinedClickable

@ExperimentalFoundationApi
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,
    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.

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. Only the initial (first) press will be recorded and emitted with MutableInteractionSource.

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

onClick: () -> Unit

will be called when user clicks on the element

excludeFromSystemGesture

fun Modifier.excludeFromSystemGesture(): Modifier

Excludes the layout rectangle from the system gesture.

See also
setSystemGestureExclusionRects

excludeFromSystemGesture

fun Modifier.excludeFromSystemGesture(
    exclusion: (LayoutCoordinates) -> Rect
): Modifier

Excludes a rectangle within the local layout coordinates from the system gesture. After layout, exclusion is called to determine the Rect to exclude from the system gesture area.

The LayoutCoordinates of the Modifier's location in the layout is passed as passed as exclusion's parameter.

See also
setSystemGestureExclusionRects

focusGroup

@ExperimentalFoundationApi
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

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

See also
rememberScrollState

hoverable

fun Modifier.hoverable(
    interactionSource: MutableInteractionSource,
    enabled: Boolean = true
): Modifier

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

import androidx.compose.foundation.background
import androidx.compose.foundation.hoverable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

// MutableInteractionSource to track changes of the component's interactions (like "hovered")
val interactionSource = remember { MutableInteractionSource() }
val isHovered by interactionSource.collectIsHoveredAsState()

// the color will change depending on the presence of a hover
Box(
    modifier = Modifier
        .size(128.dp)
        .background(if (isHovered) Color.Red else Color.Blue)
        .hoverable(interactionSource = interactionSource),
    contentAlignment = Alignment.Center
) {
    Text(if (isHovered) "Hovered" else "Unhovered")
}
Parameters
interactionSource: MutableInteractionSource

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.material.ripple.rememberRipple
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 and show ripple
            .clickable(
                interactionSource = interactionSource,
                indication = rememberRipple()
            ) {
                /**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.

magnifier

@ExperimentalFoundationApi
fun Modifier.magnifier(
    sourceCenter: Density.() -> Offset,
    magnifierCenter: Density.() -> Offset = { Offset.Unspecified },
    zoom: Float = Float.NaN,
    style: MagnifierStyle = MagnifierStyle.Default,
    onSizeChanged: ((DpSize) -> Unit)? = null
): 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. To check whether a given MagnifierStyle is supported by the current platform, check the MagnifierStyle.isSupported property.

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.MagnifierStyle
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 (!MagnifierStyle.Default.isSupported) {
    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 = { Offset.Unspecified }

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

zoom: Float = Float.NaN

See Magnifier.setZoom. Not supported on SDK levels < Q.

style: MagnifierStyle = MagnifierStyle.Default

The MagnifierStyle to use to configure the magnifier widget.

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 if one of the default MagnifierStyles is used to find out what size the system decided to use for the widget.

onFocusedBoundsChanged

@ExperimentalFoundationApi
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

@ExperimentalFoundationApi
fun Modifier.overscroll(overscrollEffect: OverscrollEffect): Modifier

Renders overscroll from the provided overscrollEffect.

This modifier is a convenience method to call OverscrollEffect.effectModifier, 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.

import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ExperimentalFoundationApi
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.offset
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.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.text.TextStyle
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

@OptIn(ExperimentalFoundationApi::class)
// 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.Drag) {
            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

    // as we're building an offset modifiers, let's offset of our value we calculated
    override val effectModifier: Modifier = Modifier.offset {
        IntOffset(x = 0, y = overscrollOffset.value.roundToInt())
    }
}

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

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

See also
setSystemGestureExclusionRects

systemGestureExclusion

fun Modifier.systemGestureExclusion(exclusion: (LayoutCoordinates) -> Rect): Modifier

Excludes a rectangle within the local layout coordinates from the system gesture. After layout, exclusion is called to determine the Rect to exclude from the system gesture area.

The LayoutCoordinates of the Modifier's location in the layout is passed as passed as exclusion's parameter.

See also
setSystemGestureExclusionRects

verticalScroll

fun Modifier.verticalScroll(
    state: ScrollState,
    enabled: Boolean = true,
    flingBehavior: FlingBehavior? = null,
    reverseScrolling: Boolean = false
): Modifier

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

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.unit.dp

val scrollState = rememberScrollState()
val gradient = Brush.verticalGradient(
    listOf(Color.Red, Color.Blue, Color.Green), 0.0f, 10000.0f, TileMode.Repeated
)
Box(
    Modifier
        .verticalScroll(scrollState)
        .fillMaxWidth()
        .requiredHeight(10000.dp)
        .background(brush = gradient)
)

In order to use this modifier, you need to create and own ScrollState

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

See also
rememberScrollState

Top-level properties

DefaultMarqueeDelayMillis

@ExperimentalFoundationApi
val DefaultMarqueeDelayMillisInt

DefaultMarqueeIterations

@ExperimentalFoundationApi
val DefaultMarqueeIterationsInt

DefaultMarqueeSpacing

@ExperimentalFoundationApi
val DefaultMarqueeSpacingMarqueeSpacing

DefaultMarqueeVelocity

@ExperimentalFoundationApi
val DefaultMarqueeVelocityDp

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

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.