1 /*
<lambda>null2 * Copyright 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package androidx.compose.material3
18
19 import androidx.compose.animation.core.Animatable
20 import androidx.compose.animation.core.FiniteAnimationSpec
21 import androidx.compose.animation.core.VectorConverter
22 import androidx.compose.foundation.BorderStroke
23 import androidx.compose.foundation.interaction.FocusInteraction
24 import androidx.compose.foundation.interaction.HoverInteraction
25 import androidx.compose.foundation.interaction.Interaction
26 import androidx.compose.foundation.interaction.InteractionSource
27 import androidx.compose.foundation.interaction.MutableInteractionSource
28 import androidx.compose.foundation.interaction.PressInteraction
29 import androidx.compose.foundation.interaction.collectIsPressedAsState
30 import androidx.compose.foundation.layout.Arrangement
31 import androidx.compose.foundation.layout.PaddingValues
32 import androidx.compose.foundation.layout.Row
33 import androidx.compose.foundation.layout.RowScope
34 import androidx.compose.foundation.layout.defaultMinSize
35 import androidx.compose.foundation.layout.padding
36 import androidx.compose.foundation.shape.CornerBasedShape
37 import androidx.compose.foundation.shape.RoundedCornerShape
38 import androidx.compose.material3.internal.ProvideContentColorTextStyle
39 import androidx.compose.material3.internal.animateElevation
40 import androidx.compose.material3.internal.rememberAnimatedShape
41 import androidx.compose.material3.tokens.BaselineButtonTokens
42 import androidx.compose.material3.tokens.ButtonLargeTokens
43 import androidx.compose.material3.tokens.ButtonMediumTokens
44 import androidx.compose.material3.tokens.ButtonSmallTokens
45 import androidx.compose.material3.tokens.ButtonXLargeTokens
46 import androidx.compose.material3.tokens.ButtonXSmallTokens
47 import androidx.compose.material3.tokens.ColorSchemeKeyTokens
48 import androidx.compose.material3.tokens.ElevatedButtonTokens
49 import androidx.compose.material3.tokens.FilledButtonTokens
50 import androidx.compose.material3.tokens.FilledTonalButtonTokens
51 import androidx.compose.material3.tokens.MotionSchemeKeyTokens
52 import androidx.compose.material3.tokens.OutlinedButtonTokens
53 import androidx.compose.material3.tokens.TextButtonTokens
54 import androidx.compose.runtime.Composable
55 import androidx.compose.runtime.Immutable
56 import androidx.compose.runtime.LaunchedEffect
57 import androidx.compose.runtime.Stable
58 import androidx.compose.runtime.State
59 import androidx.compose.runtime.getValue
60 import androidx.compose.runtime.key
61 import androidx.compose.runtime.mutableStateListOf
62 import androidx.compose.runtime.remember
63 import androidx.compose.ui.Alignment
64 import androidx.compose.ui.Modifier
65 import androidx.compose.ui.geometry.Offset
66 import androidx.compose.ui.graphics.Color
67 import androidx.compose.ui.graphics.Shape
68 import androidx.compose.ui.graphics.takeOrElse
69 import androidx.compose.ui.semantics.Role
70 import androidx.compose.ui.semantics.role
71 import androidx.compose.ui.semantics.semantics
72 import androidx.compose.ui.text.TextStyle
73 import androidx.compose.ui.unit.Dp
74 import androidx.compose.ui.unit.dp
75
76 /**
77 * [Material Design button](https://m3.material.io/components/buttons/overview)
78 *
79 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
80 * post.
81 *
82 * 
84 *
85 * Filled buttons are high-emphasis buttons. Filled buttons have the most visual impact after the
86 * [FloatingActionButton], and should be used for important, final actions that complete a flow,
87 * like "Save", "Join now", or "Confirm".
88 *
89 * @sample androidx.compose.material3.samples.ButtonSample
90 * @sample androidx.compose.material3.samples.ButtonWithIconSample
91 *
92 * Button that uses a square shape instead of the default round shape:
93 *
94 * @sample androidx.compose.material3.samples.SquareButtonSample
95 *
96 * Button that utilizes the small design content padding:
97 *
98 * @sample androidx.compose.material3.samples.SmallButtonSample
99 *
100 * [Button] uses the small design spec as default. For a [Button] that uses the design for extra
101 * small, medium, large, or extra large buttons:
102 *
103 * @sample androidx.compose.material3.samples.XSmallButtonWithIconSample
104 * @sample androidx.compose.material3.samples.MediumButtonWithIconSample
105 * @sample androidx.compose.material3.samples.LargeButtonWithIconSample
106 * @sample androidx.compose.material3.samples.XLargeButtonWithIconSample
107 *
108 * Choose the best button for an action based on the amount of emphasis it needs. The more important
109 * an action is, the higher emphasis its button should be.
110 * - See [OutlinedButton] for a medium-emphasis button with a border.
111 * - See [ElevatedButton] for an [FilledTonalButton] with a shadow.
112 * - See [TextButton] for a low-emphasis button with no border.
113 * - See [FilledTonalButton] for a middle ground between [OutlinedButton] and [Button].
114 *
115 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
116 *
117 * @param onClick called when this button is clicked
118 * @param modifier the [Modifier] to be applied to this button
119 * @param enabled controls the enabled state of this button. When `false`, this component will not
120 * respond to user input, and it will appear visually disabled and disabled to accessibility
121 * services.
122 * @param shape defines the shape of this button's container, border (when [border] is not null),
123 * and shadow (when using [elevation])
124 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
125 * states. See [ButtonDefaults.buttonColors].
126 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
127 * states. This controls the size of the shadow below the button. See
128 * [ButtonElevation.shadowElevation].
129 * @param border the border to draw around the container of this button
130 * @param contentPadding the spacing values to apply internally between the container and the
131 * content
132 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
133 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
134 * preview the button in different states. Note that if `null` is provided, interactions will
135 * still happen internally.
136 * @param content The content displayed on the button, expected to be text, icon or image.
137 */
138 @Composable
139 fun Button(
140 onClick: () -> Unit,
141 modifier: Modifier = Modifier,
142 enabled: Boolean = true,
143 shape: Shape = ButtonDefaults.shape,
144 colors: ButtonColors = ButtonDefaults.buttonColors(),
145 elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
146 border: BorderStroke? = null,
147 contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
148 interactionSource: MutableInteractionSource? = null,
149 content: @Composable RowScope.() -> Unit
150 ) {
151 @Suppress("NAME_SHADOWING")
152 val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
153 val containerColor = colors.containerColor(enabled)
154 val contentColor = colors.contentColor(enabled)
155 val shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp
156 Surface(
157 onClick = onClick,
158 modifier = modifier.semantics { role = Role.Button },
159 enabled = enabled,
160 shape = shape,
161 color = containerColor,
162 contentColor = contentColor,
163 shadowElevation = shadowElevation,
164 border = border,
165 interactionSource = interactionSource
166 ) {
167 ProvideContentColorTextStyle(
168 contentColor = contentColor,
169 textStyle = MaterialTheme.typography.labelLarge
170 ) {
171 Row(
172 Modifier.defaultMinSize(
173 minWidth = ButtonDefaults.MinWidth,
174 minHeight = ButtonDefaults.MinHeight
175 )
176 .padding(contentPadding),
177 horizontalArrangement = Arrangement.Center,
178 verticalAlignment = Alignment.CenterVertically,
179 content = content
180 )
181 }
182 }
183 }
184
185 // TODO add link to image of pressed button
186 /**
187 * [Material Design button](https://m3.material.io/components/buttons/overview)
188 *
189 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
190 * post. It also morphs between the shapes provided in [shapes] depending on the state of the
191 * interaction with the button as long as the shapes provided our [CornerBasedShape]s. If a shape in
192 * [shapes] isn't a [CornerBasedShape], then button will change between the [ButtonShapes] according
193 * to user interaction.
194 *
195 * 
197 *
198 * Filled buttons are high-emphasis buttons. Filled buttons have the most visual impact after the
199 * [FloatingActionButton], and should be used for important, final actions that complete a flow,
200 * like "Save", "Join now", or "Confirm".
201 *
202 * @sample androidx.compose.material3.samples.ButtonWithAnimatedShapeSample
203 *
204 * Choose the best button for an action based on the amount of emphasis it needs. The more important
205 * an action is, the higher emphasis its button should be.
206 * - See [OutlinedButton] for a medium-emphasis button with a border.
207 * - See [ElevatedButton] for an [FilledTonalButton] with a shadow.
208 * - See [TextButton] for a low-emphasis button with no border.
209 * - See [FilledTonalButton] for a middle ground between [OutlinedButton] and [Button].
210 *
211 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
212 *
213 * @param onClick called when this button is clicked
214 * @param shapes the [ButtonShapes] that this button with morph between depending on the user's
215 * interaction with the button.
216 * @param modifier the [Modifier] to be applied to this button
217 * @param enabled controls the enabled state of this button. When `false`, this component will not
218 * respond to user input, and it will appear visually disabled and disabled to accessibility
219 * services.
220 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
221 * states. See [ButtonDefaults.buttonColors].
222 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
223 * states. This controls the size of the shadow below the button. See
224 * [ButtonElevation.shadowElevation].
225 * @param border the border to draw around the container of this button
226 * @param contentPadding the spacing values to apply internally between the container and the
227 * content
228 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
229 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
230 * preview the button in different states. Note that if `null` is provided, interactions will
231 * still happen internally.
232 * @param content The content displayed on the button, expected to be text, icon or image.
233 */
234 @Composable
235 @ExperimentalMaterial3ExpressiveApi
Buttonnull236 fun Button(
237 onClick: () -> Unit,
238 shapes: ButtonShapes,
239 modifier: Modifier = Modifier,
240 enabled: Boolean = true,
241 colors: ButtonColors = ButtonDefaults.buttonColors(),
242 elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
243 border: BorderStroke? = null,
244 contentPadding: PaddingValues = ButtonDefaults.contentPaddingFor(ButtonDefaults.MinHeight),
245 interactionSource: MutableInteractionSource? = null,
246 content: @Composable RowScope.() -> Unit
247 ) {
248 @Suppress("NAME_SHADOWING")
249 val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
250 // TODO Load the motionScheme tokens from the component tokens file
251 // MotionSchemeKeyTokens.DefaultEffects is intentional here to prevent
252 // any bounce in this component.
253 val defaultAnimationSpec = MotionSchemeKeyTokens.DefaultEffects.value<Float>()
254 val pressed by interactionSource.collectIsPressedAsState()
255 val containerColor = colors.containerColor(enabled)
256 val contentColor = colors.contentColor(enabled)
257 val shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp
258 val buttonShape = shapeByInteraction(shapes, pressed, defaultAnimationSpec)
259
260 Surface(
261 onClick = onClick,
262 modifier = modifier.semantics { role = Role.Button },
263 enabled = enabled,
264 shape = buttonShape,
265 color = containerColor,
266 contentColor = contentColor,
267 shadowElevation = shadowElevation,
268 border = border,
269 interactionSource = interactionSource
270 ) {
271 ProvideContentColorTextStyle(
272 contentColor = contentColor,
273 textStyle = MaterialTheme.typography.labelLarge
274 ) {
275 Row(
276 Modifier.defaultMinSize(
277 minWidth = ButtonDefaults.MinWidth,
278 minHeight = ButtonDefaults.MinHeight
279 )
280 .padding(contentPadding),
281 horizontalArrangement = Arrangement.Center,
282 verticalAlignment = Alignment.CenterVertically,
283 content = content
284 )
285 }
286 }
287 }
288
289 /**
290 * [Material Design elevated button](https://m3.material.io/components/buttons/overview)
291 *
292 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
293 * post.
294 *
295 * 
297 *
298 * Elevated buttons are high-emphasis buttons that are essentially [FilledTonalButton]s with a
299 * shadow. To prevent shadow creep, only use them when absolutely necessary, such as when the button
300 * requires visual separation from patterned container.
301 *
302 * @sample androidx.compose.material3.samples.ElevatedButtonSample
303 *
304 * Choose the best button for an action based on the amount of emphasis it needs. The more important
305 * an action is, the higher emphasis its button should be.
306 * - See [Button] for a high-emphasis button without a shadow, also known as a filled button.
307 * - See [FilledTonalButton] for a middle ground between [OutlinedButton] and [Button].
308 * - See [OutlinedButton] for a medium-emphasis button with a border.
309 * - See [TextButton] for a low-emphasis button with no border.
310 *
311 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
312 *
313 * @param onClick called when this button is clicked
314 * @param modifier the [Modifier] to be applied to this button
315 * @param enabled controls the enabled state of this button. When `false`, this component will not
316 * respond to user input, and it will appear visually disabled and disabled to accessibility
317 * services.
318 * @param shape defines the shape of this button's container, border (when [border] is not null),
319 * and shadow (when using [elevation])
320 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
321 * states. See [ButtonDefaults.elevatedButtonColors].
322 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
323 * states. This controls the size of the shadow below the button. Additionally, when the container
324 * color is [ColorScheme.surface], this controls the amount of primary color applied as an
325 * overlay. See [ButtonDefaults.elevatedButtonElevation].
326 * @param border the border to draw around the container of this button
327 * @param contentPadding the spacing values to apply internally between the container and the
328 * content
329 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
330 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
331 * preview the button in different states. Note that if `null` is provided, interactions will
332 * still happen internally.
333 * @param content The content displayed on the button, expected to be text, icon or image.
334 */
335 @Composable
ElevatedButtonnull336 fun ElevatedButton(
337 onClick: () -> Unit,
338 modifier: Modifier = Modifier,
339 enabled: Boolean = true,
340 shape: Shape = ButtonDefaults.elevatedShape,
341 colors: ButtonColors = ButtonDefaults.elevatedButtonColors(),
342 elevation: ButtonElevation? = ButtonDefaults.elevatedButtonElevation(),
343 border: BorderStroke? = null,
344 contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
345 interactionSource: MutableInteractionSource? = null,
346 content: @Composable RowScope.() -> Unit
347 ) =
348 Button(
349 onClick = onClick,
350 modifier = modifier,
351 enabled = enabled,
352 shape = shape,
353 colors = colors,
354 elevation = elevation,
355 border = border,
356 contentPadding = contentPadding,
357 interactionSource = interactionSource,
358 content = content
359 )
360
361 // TODO add link to image of pressed elevated button
362 /**
363 * [Material Design elevated button](https://m3.material.io/components/buttons/overview)
364 *
365 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
366 * post. It also morphs between the shapes provided in [shapes] depending on the state of the
367 * interaction with the button as long as the shapes provided our [CornerBasedShape]s. If a shape in
368 * [shapes] isn't a [CornerBasedShape], then button will change between the [ButtonShapes] according
369 * to user interaction.
370 *
371 * 
373 *
374 * Elevated buttons are high-emphasis buttons that are essentially [FilledTonalButton]s with a
375 * shadow. To prevent shadow creep, only use them when absolutely necessary, such as when the button
376 * requires visual separation from patterned container.
377 *
378 * @sample androidx.compose.material3.samples.ElevatedButtonWithAnimatedShapeSample
379 *
380 * Choose the best button for an action based on the amount of emphasis it needs. The more important
381 * an action is, the higher emphasis its button should be.
382 * - See [Button] for a high-emphasis button without a shadow, also known as a filled button.
383 * - See [FilledTonalButton] for a middle ground between [OutlinedButton] and [Button].
384 * - See [OutlinedButton] for a medium-emphasis button with a border.
385 * - See [TextButton] for a low-emphasis button with no border.
386 *
387 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
388 *
389 * @param onClick called when this button is clicked
390 * @param shapes the [ButtonShapes] that this button with morph between depending on the user's
391 * interaction with the button.
392 * @param modifier the [Modifier] to be applied to this button
393 * @param enabled controls the enabled state of this button. When `false`, this component will not
394 * respond to user input, and it will appear visually disabled and disabled to accessibility
395 * services.
396 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
397 * states. See [ButtonDefaults.elevatedButtonColors].
398 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
399 * states. This controls the size of the shadow below the button. Additionally, when the container
400 * color is [ColorScheme.surface], this controls the amount of primary color applied as an
401 * overlay. See [ButtonDefaults.elevatedButtonElevation].
402 * @param border the border to draw around the container of this button
403 * @param contentPadding the spacing values to apply internally between the container and the
404 * content
405 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
406 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
407 * preview the button in different states. Note that if `null` is provided, interactions will
408 * still happen internally.
409 * @param content The content displayed on the button, expected to be text, icon or image.
410 */
411 @Composable
412 @ExperimentalMaterial3ExpressiveApi
413 fun ElevatedButton(
414 onClick: () -> Unit,
415 shapes: ButtonShapes,
416 modifier: Modifier = Modifier,
417 enabled: Boolean = true,
418 colors: ButtonColors = ButtonDefaults.elevatedButtonColors(),
419 elevation: ButtonElevation? = ButtonDefaults.elevatedButtonElevation(),
420 border: BorderStroke? = null,
421 contentPadding: PaddingValues = ButtonDefaults.contentPaddingFor(ButtonDefaults.MinHeight),
422 interactionSource: MutableInteractionSource? = null,
423 content: @Composable RowScope.() -> Unit
424 ) =
425 Button(
426 onClick = onClick,
427 shapes = shapes,
428 modifier = modifier,
429 enabled = enabled,
430 colors = colors,
431 elevation = elevation,
432 border = border,
433 contentPadding = contentPadding,
434 interactionSource = interactionSource,
435 content = content
436 )
437
438 /**
439 * [Material Design filled tonal button](https://m3.material.io/components/buttons/overview)
440 *
441 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
442 * post.
443 *
444 * 
446 *
447 * Filled tonal buttons are medium-emphasis buttons that is an alternative middle ground between
448 * default [Button]s (filled) and [OutlinedButton]s. They can be used in contexts where
449 * lower-priority button requires slightly more emphasis than an outline would give, such as "Next"
450 * in an onboarding flow. Tonal buttons use the secondary color mapping.
451 *
452 * @sample androidx.compose.material3.samples.FilledTonalButtonSample
453 *
454 * Choose the best button for an action based on the amount of emphasis it needs. The more important
455 * an action is, the higher emphasis its button should be.
456 * - See [Button] for a high-emphasis button without a shadow, also known as a filled button.
457 * - See [ElevatedButton] for a [FilledTonalButton] with a shadow.
458 * - See [OutlinedButton] for a medium-emphasis button with a border.
459 * - See [TextButton] for a low-emphasis button with no border.
460 *
461 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
462 *
463 * @param onClick called when this button is clicked
464 * @param modifier the [Modifier] to be applied to this button
465 * @param enabled controls the enabled state of this button. When `false`, this component will not
466 * respond to user input, and it will appear visually disabled and disabled to accessibility
467 * services.
468 * @param shape defines the shape of this button's container, border (when [border] is not null),
469 * and shadow (when using [elevation])
470 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
471 * states. See [ButtonDefaults.filledTonalButtonColors].
472 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
473 * states. This controls the size of the shadow below the button. Additionally, when the container
474 * color is [ColorScheme.surface], this controls the amount of primary color applied as an
475 * overlay.
476 * @param border the border to draw around the container of this button
477 * @param contentPadding the spacing values to apply internally between the container and the
478 * content
479 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
480 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
481 * preview the button in different states. Note that if `null` is provided, interactions will
482 * still happen internally.
483 * @param content The content displayed on the button, expected to be text, icon or image.
484 */
485 @Composable
486 fun FilledTonalButton(
487 onClick: () -> Unit,
488 modifier: Modifier = Modifier,
489 enabled: Boolean = true,
490 shape: Shape = ButtonDefaults.filledTonalShape,
491 colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
492 elevation: ButtonElevation? = ButtonDefaults.filledTonalButtonElevation(),
493 border: BorderStroke? = null,
494 contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
495 interactionSource: MutableInteractionSource? = null,
496 content: @Composable RowScope.() -> Unit
497 ) =
498 Button(
499 onClick = onClick,
500 modifier = modifier,
501 enabled = enabled,
502 shape = shape,
503 colors = colors,
504 elevation = elevation,
505 border = border,
506 contentPadding = contentPadding,
507 interactionSource = interactionSource,
508 content = content
509 )
510
511 // TODO add link to image of pressed filled tonal button
512 /**
513 * [Material Design filled tonal button](https://m3.material.io/components/buttons/overview)
514 *
515 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
516 * post. It also morphs between the shapes provided in [shapes] depending on the state of the
517 * interaction with the button as long as the shapes provided our [CornerBasedShape]s. If a shape in
518 * [shapes] isn't a [CornerBasedShape], then button will change between the [ButtonShapes] according
519 * to user interaction.
520 *
521 * 
523 *
524 * Filled tonal buttons are medium-emphasis buttons that is an alternative middle ground between
525 * default [Button]s (filled) and [OutlinedButton]s. They can be used in contexts where
526 * lower-priority button requires slightly more emphasis than an outline would give, such as "Next"
527 * in an onboarding flow. Tonal buttons use the secondary color mapping.
528 *
529 * @sample androidx.compose.material3.samples.FilledTonalButtonWithAnimatedShapeSample
530 *
531 * Choose the best button for an action based on the amount of emphasis it needs. The more important
532 * an action is, the higher emphasis its button should be.
533 * - See [Button] for a high-emphasis button without a shadow, also known as a filled button.
534 * - See [ElevatedButton] for a [FilledTonalButton] with a shadow.
535 * - See [OutlinedButton] for a medium-emphasis button with a border.
536 * - See [TextButton] for a low-emphasis button with no border.
537 *
538 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
539 *
540 * @param onClick called when this button is clicked
541 * @param shapes the [ButtonShapes] that this button with morph between depending on the user's
542 * interaction with the button.
543 * @param modifier the [Modifier] to be applied to this button
544 * @param enabled controls the enabled state of this button. When `false`, this component will not
545 * respond to user input, and it will appear visually disabled and disabled to accessibility
546 * services.
547 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
548 * states. See [ButtonDefaults.filledTonalButtonColors].
549 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
550 * states. This controls the size of the shadow below the button. Additionally, when the container
551 * color is [ColorScheme.surface], this controls the amount of primary color applied as an
552 * overlay.
553 * @param border the border to draw around the container of this button
554 * @param contentPadding the spacing values to apply internally between the container and the
555 * content
556 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
557 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
558 * preview the button in different states. Note that if `null` is provided, interactions will
559 * still happen internally.
560 * @param content The content displayed on the button, expected to be text, icon or image.
561 */
562 @Composable
563 @ExperimentalMaterial3ExpressiveApi
564 fun FilledTonalButton(
565 onClick: () -> Unit,
566 shapes: ButtonShapes,
567 modifier: Modifier = Modifier,
568 enabled: Boolean = true,
569 colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
570 elevation: ButtonElevation? = ButtonDefaults.filledTonalButtonElevation(),
571 border: BorderStroke? = null,
572 contentPadding: PaddingValues = ButtonDefaults.contentPaddingFor(ButtonDefaults.MinHeight),
573 interactionSource: MutableInteractionSource? = null,
574 content: @Composable RowScope.() -> Unit
575 ) =
576 Button(
577 onClick = onClick,
578 shapes = shapes,
579 modifier = modifier,
580 enabled = enabled,
581 colors = colors,
582 elevation = elevation,
583 border = border,
584 contentPadding = contentPadding,
585 interactionSource = interactionSource,
586 content = content
587 )
588
589 /**
590 * [Material Design outlined button](https://m3.material.io/components/buttons/overview)
591 *
592 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
593 * post.
594 *
595 * 
597 *
598 * Outlined buttons are medium-emphasis buttons. They contain actions that are important, but are
599 * not the primary action in an app. Outlined buttons pair well with [Button]s to indicate an
600 * alternative, secondary action.
601 *
602 * @sample androidx.compose.material3.samples.OutlinedButtonSample
603 *
604 * Choose the best button for an action based on the amount of emphasis it needs. The more important
605 * an action is, the higher emphasis its button should be.
606 * - See [Button] for a high-emphasis button without a shadow, also known as a filled button.
607 * - See [FilledTonalButton] for a middle ground between [OutlinedButton] and [Button].
608 * - See [OutlinedButton] for a medium-emphasis button with a border.
609 * - See [TextButton] for a low-emphasis button with no border.
610 *
611 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
612 *
613 * @param onClick called when this button is clicked
614 * @param modifier the [Modifier] to be applied to this button
615 * @param enabled controls the enabled state of this button. When `false`, this component will not
616 * respond to user input, and it will appear visually disabled and disabled to accessibility
617 * services.
618 * @param shape defines the shape of this button's container, border (when [border] is not null),
619 * and shadow (when using [elevation]).
620 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
621 * states. See [ButtonDefaults.outlinedButtonColors].
622 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
623 * states. This controls the size of the shadow below the button. Additionally, when the container
624 * color is [ColorScheme.surface], this controls the amount of primary color applied as an
625 * overlay.
626 * @param border the border to draw around the container of this button. Pass `null` for no border.
627 * @param contentPadding the spacing values to apply internally between the container and the
628 * content
629 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
630 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
631 * preview the button in different states. Note that if `null` is provided, interactions will
632 * still happen internally.
633 * @param content The content displayed on the button, expected to be text, icon or image.
634 */
635 @Composable
636 fun OutlinedButton(
637 onClick: () -> Unit,
638 modifier: Modifier = Modifier,
639 enabled: Boolean = true,
640 shape: Shape = ButtonDefaults.outlinedShape,
641 colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
642 elevation: ButtonElevation? = null,
643 border: BorderStroke? = ButtonDefaults.outlinedButtonBorder(enabled),
644 contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
645 interactionSource: MutableInteractionSource? = null,
646 content: @Composable RowScope.() -> Unit
647 ) =
648 Button(
649 onClick = onClick,
650 modifier = modifier,
651 enabled = enabled,
652 shape = shape,
653 colors = colors,
654 elevation = elevation,
655 border = border,
656 contentPadding = contentPadding,
657 interactionSource = interactionSource,
658 content = content
659 )
660
661 // TODO add link to image of pressed outlined button
662 /**
663 * [Material Design outlined button](https://m3.material.io/components/buttons/overview)
664 *
665 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
666 * post. It also morphs between the shapes provided in [shapes] depending on the state of the
667 * interaction with the button as long as the shapes provided our [CornerBasedShape]s. If a shape in
668 * [shapes] isn't a [CornerBasedShape], then button will change between the [ButtonShapes] according
669 * to user interaction.
670 *
671 * 
673 *
674 * Outlined buttons are medium-emphasis buttons. They contain actions that are important, but are
675 * not the primary action in an app. Outlined buttons pair well with [Button]s to indicate an
676 * alternative, secondary action.
677 *
678 * @sample androidx.compose.material3.samples.OutlinedButtonWithAnimatedShapeSample
679 *
680 * Choose the best button for an action based on the amount of emphasis it needs. The more important
681 * an action is, the higher emphasis its button should be.
682 * - See [Button] for a high-emphasis button without a shadow, also known as a filled button.
683 * - See [FilledTonalButton] for a middle ground between [OutlinedButton] and [Button].
684 * - See [OutlinedButton] for a medium-emphasis button with a border.
685 * - See [TextButton] for a low-emphasis button with no border.
686 *
687 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
688 *
689 * @param onClick called when this button is clicked
690 * @param shapes the [ButtonShapes] that this button with morph between depending on the user's
691 * interaction with the button.
692 * @param modifier the [Modifier] to be applied to this button
693 * @param enabled controls the enabled state of this button. When `false`, this component will not
694 * respond to user input, and it will appear visually disabled and disabled to accessibility
695 * services.
696 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
697 * states. See [ButtonDefaults.outlinedButtonColors].
698 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
699 * states. This controls the size of the shadow below the button. Additionally, when the container
700 * color is [ColorScheme.surface], this controls the amount of primary color applied as an
701 * overlay.
702 * @param border the border to draw around the container of this button. Pass `null` for no border.
703 * @param contentPadding the spacing values to apply internally between the container and the
704 * content
705 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
706 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
707 * preview the button in different states. Note that if `null` is provided, interactions will
708 * still happen internally.
709 * @param content The content displayed on the button, expected to be text, icon or image.
710 */
711 @Composable
712 @ExperimentalMaterial3ExpressiveApi
713 fun OutlinedButton(
714 onClick: () -> Unit,
715 shapes: ButtonShapes,
716 modifier: Modifier = Modifier,
717 enabled: Boolean = true,
718 colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
719 elevation: ButtonElevation? = null,
720 border: BorderStroke? = ButtonDefaults.outlinedButtonBorder(enabled),
721 contentPadding: PaddingValues = ButtonDefaults.contentPaddingFor(ButtonDefaults.MinHeight),
722 interactionSource: MutableInteractionSource? = null,
723 content: @Composable RowScope.() -> Unit
724 ) =
725 Button(
726 onClick = onClick,
727 shapes = shapes,
728 modifier = modifier,
729 enabled = enabled,
730 colors = colors,
731 elevation = elevation,
732 border = border,
733 contentPadding = contentPadding,
734 interactionSource = interactionSource,
735 content = content
736 )
737
738 /**
739 * [Material Design text button](https://m3.material.io/components/buttons/overview)
740 *
741 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
742 * post.
743 *
744 * 
746 *
747 * Text buttons are typically used for less-pronounced actions, including those located in dialogs
748 * and cards. In cards, text buttons help maintain an emphasis on card content. Text buttons are
749 * used for the lowest priority actions, especially when presenting multiple options.
750 *
751 * @sample androidx.compose.material3.samples.TextButtonWithAnimatedShapeSample
752 *
753 * Choose the best button for an action based on the amount of emphasis it needs. The more important
754 * an action is, the higher emphasis its button should be.
755 * - See [Button] for a high-emphasis button without a shadow, also known as a filled button.
756 * - See [ElevatedButton] for a [FilledTonalButton] with a shadow.
757 * - See [FilledTonalButton] for a middle ground between [OutlinedButton] and [Button].
758 * - See [OutlinedButton] for a medium-emphasis button with a border.
759 *
760 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
761 *
762 * @param onClick called when this button is clicked
763 * @param modifier the [Modifier] to be applied to this button
764 * @param enabled controls the enabled state of this button. When `false`, this component will not
765 * respond to user input, and it will appear visually disabled and disabled to accessibility
766 * services.
767 * @param shape defines the shape of this button's container, border (when [border] is not null),
768 * and shadow (when using [elevation])
769 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
770 * states. See [ButtonDefaults.textButtonColors].
771 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
772 * states. This controls the size of the shadow below the button. Additionally, when the container
773 * color is [ColorScheme.surface], this controls the amount of primary color applied as an
774 * overlay. A TextButton typically has no elevation, and the default value is `null`. See
775 * [ElevatedButton] for a button with elevation.
776 * @param border the border to draw around the container of this button
777 * @param contentPadding the spacing values to apply internally between the container and the
778 * content
779 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
780 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
781 * preview the button in different states. Note that if `null` is provided, interactions will
782 * still happen internally.
783 * @param content The content displayed on the button, expected to be text.
784 */
785 @Composable
786 fun TextButton(
787 onClick: () -> Unit,
788 modifier: Modifier = Modifier,
789 enabled: Boolean = true,
790 shape: Shape = ButtonDefaults.textShape,
791 colors: ButtonColors = ButtonDefaults.textButtonColors(),
792 elevation: ButtonElevation? = null,
793 border: BorderStroke? = null,
794 contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
795 interactionSource: MutableInteractionSource? = null,
796 content: @Composable RowScope.() -> Unit
797 ) =
798 Button(
799 onClick = onClick,
800 modifier = modifier,
801 enabled = enabled,
802 shape = shape,
803 colors = colors,
804 elevation = elevation,
805 border = border,
806 contentPadding = contentPadding,
807 interactionSource = interactionSource,
808 content = content
809 )
810
811 // TODO add link to image of pressed text button
812 /**
813 * [Material Design text button](https://m3.material.io/components/buttons/overview)
814 *
815 * Buttons help people initiate actions, from sending an email, to sharing a document, to liking a
816 * post. It also morphs between the shapes provided in [shapes] depending on the state of the
817 * interaction with the button as long as the shapes provided our [CornerBasedShape]s. If a shape in
818 * [shapes] isn't a [CornerBasedShape], then button will change between the [ButtonShapes] according
819 * to user interaction.
820 *
821 * 
823 *
824 * Text buttons are typically used for less-pronounced actions, including those located in dialogs
825 * and cards. In cards, text buttons help maintain an emphasis on card content. Text buttons are
826 * used for the lowest priority actions, especially when presenting multiple options.
827 *
828 * @sample androidx.compose.material3.samples.TextButtonSample
829 *
830 * Choose the best button for an action based on the amount of emphasis it needs. The more important
831 * an action is, the higher emphasis its button should be.
832 * - See [Button] for a high-emphasis button without a shadow, also known as a filled button.
833 * - See [ElevatedButton] for a [FilledTonalButton] with a shadow.
834 * - See [FilledTonalButton] for a middle ground between [OutlinedButton] and [Button].
835 * - See [OutlinedButton] for a medium-emphasis button with a border.
836 *
837 * The default text style for internal [Text] components will be set to [Typography.labelLarge].
838 *
839 * @param onClick called when this button is clicked
840 * @param shapes the [ButtonShapes] that this button with morph between depending on the user's
841 * interaction with the button.
842 * @param modifier the [Modifier] to be applied to this button
843 * @param enabled controls the enabled state of this button. When `false`, this component will not
844 * respond to user input, and it will appear visually disabled and disabled to accessibility
845 * services.
846 * @param colors [ButtonColors] that will be used to resolve the colors for this button in different
847 * states. See [ButtonDefaults.textButtonColors].
848 * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
849 * states. This controls the size of the shadow below the button. Additionally, when the container
850 * color is [ColorScheme.surface], this controls the amount of primary color applied as an
851 * overlay. A TextButton typically has no elevation, and the default value is `null`. See
852 * [ElevatedButton] for a button with elevation.
853 * @param border the border to draw around the container of this button
854 * @param contentPadding the spacing values to apply internally between the container and the
855 * content
856 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
857 * emitting [Interaction]s for this button. You can use this to change the button's appearance or
858 * preview the button in different states. Note that if `null` is provided, interactions will
859 * still happen internally.
860 * @param content The content displayed on the button, expected to be text.
861 */
862 @ExperimentalMaterial3ExpressiveApi
863 @Composable
864 fun TextButton(
865 onClick: () -> Unit,
866 shapes: ButtonShapes,
867 modifier: Modifier = Modifier,
868 enabled: Boolean = true,
869 colors: ButtonColors = ButtonDefaults.textButtonColors(),
870 elevation: ButtonElevation? = null,
871 border: BorderStroke? = null,
872 contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
873 interactionSource: MutableInteractionSource? = null,
874 content: @Composable RowScope.() -> Unit
875 ) =
876 Button(
877 onClick = onClick,
878 shapes = shapes,
879 modifier = modifier,
880 enabled = enabled,
881 colors = colors,
882 elevation = elevation,
883 border = border,
884 contentPadding = contentPadding,
885 interactionSource = interactionSource,
886 content = content
887 )
888
889 // TODO(b/201341237): Use token values for 0 elevation?
890 // TODO(b/201341237): Use token values for null border?
891 // TODO(b/201341237): Use token values for no color (transparent)?
892 /**
893 * Contains the default values used by all 5 button types.
894 *
895 * Default values that apply to all buttons types are [MinWidth], [MinHeight], [IconSize], and
896 * [IconSpacing].
897 *
898 * A default value that applies only to [Button], [ElevatedButton], [FilledTonalButton], and
899 * [OutlinedButton] is [ContentPadding].
900 *
901 * Default values that apply only to [Button] are [buttonColors] and [buttonElevation]. Default
902 * values that apply only to [ElevatedButton] are [elevatedButtonColors] and
903 * [elevatedButtonElevation]. Default values that apply only to [FilledTonalButton] are
904 * [filledTonalButtonColors] and [filledTonalButtonElevation]. A default value that applies only to
905 * [OutlinedButton] is [outlinedButtonColors]. Default values that apply only to [TextButton] are
906 * [TextButtonContentPadding] and [textButtonColors].
907 */
908 object ButtonDefaults {
909
910 private val ButtonLeadingSpace = BaselineButtonTokens.LeadingSpace
911 private val ButtonTrailingSpace = BaselineButtonTokens.TrailingSpace
912 private val ButtonWithIconStartpadding = 16.dp
913 private val SmallStartPadding = ButtonSmallTokens.LeadingSpace
914 private val SmallEndPadding = ButtonSmallTokens.TrailingSpace
915 private val ButtonVerticalPadding = 8.dp
916
917 /**
918 * The default content padding used by [Button], [ElevatedButton], [FilledTonalButton], and
919 * [OutlinedButton] buttons.
920 * - See [TextButtonContentPadding] or [TextButtonWithIconContentPadding] for content padding
921 * used by [TextButton].
922 * - See [ButtonWithIconContentPadding] for content padding used by [Button] that contains
923 * [Icon].
924 */
925 val ContentPadding =
926 PaddingValues(
927 start = ButtonLeadingSpace,
928 top = ButtonVerticalPadding,
929 end = ButtonTrailingSpace,
930 bottom = ButtonVerticalPadding
931 )
932
933 /** The default content padding used by [Button] that contains an [Icon]. */
934 val ButtonWithIconContentPadding =
935 PaddingValues(
936 start = ButtonWithIconStartpadding,
937 top = ButtonVerticalPadding,
938 end = ButtonTrailingSpace,
939 bottom = ButtonVerticalPadding
940 )
941
942 /** The default content padding used for small [Button] */
943 @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN)
944 @ExperimentalMaterial3ExpressiveApi
945 val SmallButtonContentPadding
946 get() =
947 PaddingValues(
948 start = SmallStartPadding,
949 top = ButtonVerticalPadding,
950 end = SmallEndPadding,
951 bottom = ButtonVerticalPadding
952 )
953
954 /** The default content padding used for small [Button] */
955 @ExperimentalMaterial3ExpressiveApi
956 val SmallContentPadding
957 get() =
958 PaddingValues(
959 start = SmallStartPadding,
960 top = ButtonVerticalPadding,
961 end = SmallEndPadding,
962 bottom = ButtonVerticalPadding
963 )
964
965 /** Default content padding for an extra small button. */
966 @ExperimentalMaterial3ExpressiveApi
967 val ExtraSmallContentPadding
968 get() =
969 PaddingValues(
970 // TODO update with the value from ButtonXSmallTokens.kt once it's been corrected
971 start = 12.dp,
972 end = 12.dp,
973 top = 6.dp,
974 bottom = 6.dp
975 )
976
977 /** Default content padding for a medium button. */
978 @ExperimentalMaterial3ExpressiveApi
979 val MediumContentPadding
980 get() =
981 PaddingValues(
982 start = ButtonMediumTokens.LeadingSpace,
983 end = ButtonMediumTokens.TrailingSpace,
984 top = 16.dp,
985 bottom = 16.dp
986 )
987
988 /** Default content padding for a large button. */
989 @ExperimentalMaterial3ExpressiveApi
990 val LargeContentPadding
991 get() =
992 PaddingValues(
993 start = ButtonLargeTokens.LeadingSpace,
994 end = ButtonLargeTokens.TrailingSpace,
995 top = 32.dp,
996 bottom = 32.dp
997 )
998
999 /** Default content padding for an extra large button. */
1000 @ExperimentalMaterial3ExpressiveApi
1001 val ExtraLargeContentPadding
1002 get() =
1003 PaddingValues(
1004 start = ButtonXLargeTokens.LeadingSpace,
1005 end = ButtonXLargeTokens.TrailingSpace,
1006 top = 48.dp,
1007 bottom = 48.dp
1008 )
1009
1010 private val TextButtonHorizontalPadding = 12.dp
1011
1012 /**
1013 * The default content padding used by [TextButton].
1014 * - See [TextButtonWithIconContentPadding] for content padding used by [TextButton] that
1015 * contains [Icon].
1016 */
1017 val TextButtonContentPadding =
1018 PaddingValues(
1019 start = TextButtonHorizontalPadding,
1020 top = ContentPadding.calculateTopPadding(),
1021 end = TextButtonHorizontalPadding,
1022 bottom = ContentPadding.calculateBottomPadding()
1023 )
1024
1025 private val TextButtonWithIconHorizontalEndPadding = 16.dp
1026
1027 /** The default content padding used by [TextButton] that contains an [Icon]. */
1028 val TextButtonWithIconContentPadding =
1029 PaddingValues(
1030 start = TextButtonHorizontalPadding,
1031 top = ContentPadding.calculateTopPadding(),
1032 end = TextButtonWithIconHorizontalEndPadding,
1033 bottom = ContentPadding.calculateBottomPadding()
1034 )
1035
1036 /**
1037 * The default min width applied for small buttons. Note that you can override it by applying
1038 * Modifier.widthIn directly on the button composable.
1039 */
1040 val MinWidth = 58.dp
1041
1042 /**
1043 * The default min height applied for small buttons. Note that you can override it by applying
1044 * Modifier.heightIn directly on the button composable.
1045 */
1046 val MinHeight = ButtonSmallTokens.ContainerHeight
1047
1048 /** The default height for a extra small button container. */
1049 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1050 @get:ExperimentalMaterial3ExpressiveApi
1051 @ExperimentalMaterial3ExpressiveApi
1052 val ExtraSmallContainerHeight = ButtonXSmallTokens.ContainerHeight
1053
1054 /** The default height for a medium button container. */
1055 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1056 @get:ExperimentalMaterial3ExpressiveApi
1057 @ExperimentalMaterial3ExpressiveApi
1058 val MediumContainerHeight = ButtonMediumTokens.ContainerHeight
1059
1060 /** The default height for a large button container. */
1061 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1062 @get:ExperimentalMaterial3ExpressiveApi
1063 @ExperimentalMaterial3ExpressiveApi
1064 val LargeContainerHeight = ButtonLargeTokens.ContainerHeight
1065
1066 /** The default height for a extra large button container. */
1067 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1068 @get:ExperimentalMaterial3ExpressiveApi
1069 @ExperimentalMaterial3ExpressiveApi
1070 val ExtraLargeContainerHeight = ButtonXLargeTokens.ContainerHeight
1071
1072 /** The default size of the icon when used inside a small button. */
1073 // TODO update with the correct value in BaselineButtonTokens when available
1074 val IconSize = 18.dp
1075
1076 /** The default size of the icon used inside of a extra small button. */
1077 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1078 @get:ExperimentalMaterial3ExpressiveApi
1079 @ExperimentalMaterial3ExpressiveApi
1080 val ExtraSmallIconSize = ButtonXSmallTokens.IconSize
1081
1082 /** The expressive size of the icon used inside a small button. */
1083 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1084 @get:ExperimentalMaterial3ExpressiveApi
1085 @ExperimentalMaterial3ExpressiveApi
1086 val SmallIconSize = ButtonSmallTokens.IconSize
1087
1088 /** The default size of the icon used inside of a medium button. */
1089 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1090 @get:ExperimentalMaterial3ExpressiveApi
1091 @ExperimentalMaterial3ExpressiveApi
1092 val MediumIconSize = ButtonMediumTokens.IconSize
1093
1094 /** The default size of the icon used inside of a large button. */
1095 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1096 @get:ExperimentalMaterial3ExpressiveApi
1097 @ExperimentalMaterial3ExpressiveApi
1098 val LargeIconSize = ButtonLargeTokens.IconSize
1099
1100 /** The default size of the icon used inside of a extra large button. */
1101 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1102 @get:ExperimentalMaterial3ExpressiveApi
1103 @ExperimentalMaterial3ExpressiveApi
1104 val ExtraLargeIconSize = ButtonXLargeTokens.IconSize
1105
1106 /**
1107 * The default size of the spacing between an icon and a text when they used inside a small
1108 * button.
1109 */
1110 val IconSpacing = ButtonSmallTokens.IconLabelSpace
1111
1112 /**
1113 * The default spacing between an icon and a text when they used inside any extra small button.
1114 */
1115 // TODO use the value from ButtonXSmallTokens.kt once it's been corrected
1116 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1117 @get:ExperimentalMaterial3ExpressiveApi
1118 @ExperimentalMaterial3ExpressiveApi
1119 val ExtraSmallIconSpacing = 4.dp
1120
1121 /** The default spacing between an icon and a text when they used inside any medium button. */
1122 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1123 @get:ExperimentalMaterial3ExpressiveApi
1124 @ExperimentalMaterial3ExpressiveApi
1125 val MediumIconSpacing = ButtonMediumTokens.IconLabelSpace
1126
1127 /** The default spacing between an icon and a text when they used inside any large button. */
1128 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1129 @get:ExperimentalMaterial3ExpressiveApi
1130 @ExperimentalMaterial3ExpressiveApi
1131 val LargeIconSpacing = ButtonLargeTokens.IconLabelSpace
1132
1133 /**
1134 * The default spacing between an icon and a text when they used inside any extra large button.
1135 */
1136 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1137 @get:ExperimentalMaterial3ExpressiveApi
1138 @ExperimentalMaterial3ExpressiveApi
1139 val ExtraLargeIconSpacing = ButtonXLargeTokens.IconLabelSpace
1140
1141 /** Square shape for default buttons. */
1142 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1143 @get:ExperimentalMaterial3ExpressiveApi
1144 @ExperimentalMaterial3ExpressiveApi
1145 val squareShape: Shape
1146 @Composable get() = ButtonSmallTokens.ContainerShapeSquare.value
1147
1148 /** Pressed shape for default buttons. */
1149 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1150 @get:ExperimentalMaterial3ExpressiveApi
1151 @ExperimentalMaterial3ExpressiveApi
1152 val pressedShape: Shape
1153 @Composable get() = ButtonSmallTokens.PressedContainerShape.value
1154
1155 /** Pressed shape for extra small buttons. */
1156 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1157 @get:ExperimentalMaterial3ExpressiveApi
1158 @ExperimentalMaterial3ExpressiveApi
1159 val extraSmallPressedShape: Shape
1160 @Composable get() = ButtonXSmallTokens.PressedContainerShape.value
1161
1162 /** Pressed shape for medium buttons. */
1163 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1164 @get:ExperimentalMaterial3ExpressiveApi
1165 @ExperimentalMaterial3ExpressiveApi
1166 val mediumPressedShape: Shape
1167 @Composable get() = ButtonMediumTokens.PressedContainerShape.value
1168
1169 /** Pressed shape for large buttons. */
1170 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1171 @get:ExperimentalMaterial3ExpressiveApi
1172 @ExperimentalMaterial3ExpressiveApi
1173 val largePressedShape: Shape
1174 @Composable get() = ButtonLargeTokens.PressedContainerShape.value
1175
1176 /** Pressed shape for extra large buttons. */
1177 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1178 @get:ExperimentalMaterial3ExpressiveApi
1179 @ExperimentalMaterial3ExpressiveApi
1180 val extraLargePressedShape: Shape
1181 @Composable get() = ButtonXLargeTokens.PressedContainerShape.value
1182
1183 /** Default shape for a button. */
1184 val shape: Shape
1185 @Composable get() = ButtonSmallTokens.ContainerShapeRound.value
1186
1187 /** Default shape for an elevated button. */
1188 val elevatedShape: Shape
1189 @Composable get() = ButtonSmallTokens.ContainerShapeRound.value
1190
1191 /** Default shape for a filled tonal button. */
1192 val filledTonalShape: Shape
1193 @Composable get() = ButtonSmallTokens.ContainerShapeRound.value
1194
1195 /** Default shape for an outlined button. */
1196 val outlinedShape: Shape
1197 @Composable get() = ButtonSmallTokens.ContainerShapeRound.value
1198
1199 /** Default shape for a text button. */
1200 val textShape: Shape
1201 @Composable get() = ButtonSmallTokens.ContainerShapeRound.value
1202
1203 /**
1204 * Creates a [ButtonShapes] that represents the default shape and pressed shape used in a
1205 * button.
1206 */
1207 @ExperimentalMaterial3ExpressiveApi
1208 @Composable
1209 fun shapes() = MaterialTheme.shapes.defaultButtonShapes
1210
1211 /**
1212 * Creates a [ButtonShapes] that represents the default shape and pressedShape used in a
1213 * [Button] and its variants.
1214 *
1215 * @param shape the unchecked shape for [ButtonShapes]
1216 * @param pressedShape the unchecked shape for [ButtonShapes]
1217 */
1218 @Composable
1219 @ExperimentalMaterial3ExpressiveApi
1220 fun shapes(
1221 shape: Shape? = null,
1222 pressedShape: Shape? = null,
1223 ): ButtonShapes =
1224 MaterialTheme.shapes.defaultButtonShapes.copy(shape = shape, pressedShape = pressedShape)
1225
1226 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
1227 internal val Shapes.defaultButtonShapes: ButtonShapes
1228 get() {
1229 return defaultButtonShapesCached
1230 ?: ButtonShapes(
1231 shape = fromToken(ButtonSmallTokens.ContainerShapeRound),
1232 pressedShape = fromToken(ButtonSmallTokens.PressedContainerShape)
1233 )
1234 .also { defaultButtonShapesCached = it }
1235 }
1236
1237 /**
1238 * Creates a [ButtonColors] that represents the default container and content colors used in a
1239 * [Button].
1240 */
1241 @Composable fun buttonColors() = MaterialTheme.colorScheme.defaultButtonColors
1242
1243 /**
1244 * Creates a [ButtonColors] that represents the default container and content colors used in a
1245 * [Button].
1246 *
1247 * @param containerColor the container color of this [Button] when enabled.
1248 * @param contentColor the content color of this [Button] when enabled.
1249 * @param disabledContainerColor the container color of this [Button] when not enabled.
1250 * @param disabledContentColor the content color of this [Button] when not enabled.
1251 */
1252 @Composable
1253 fun buttonColors(
1254 containerColor: Color = Color.Unspecified,
1255 contentColor: Color = Color.Unspecified,
1256 disabledContainerColor: Color = Color.Unspecified,
1257 disabledContentColor: Color = Color.Unspecified,
1258 ): ButtonColors =
1259 MaterialTheme.colorScheme.defaultButtonColors.copy(
1260 containerColor = containerColor,
1261 contentColor = contentColor,
1262 disabledContainerColor = disabledContainerColor,
1263 disabledContentColor = disabledContentColor
1264 )
1265
1266 internal val ColorScheme.defaultButtonColors: ButtonColors
1267 get() {
1268 return defaultButtonColorsCached
1269 ?: ButtonColors(
1270 containerColor = fromToken(FilledButtonTokens.ContainerColor),
1271 contentColor = fromToken(FilledButtonTokens.LabelTextColor),
1272 disabledContainerColor =
1273 fromToken(FilledButtonTokens.DisabledContainerColor)
1274 .copy(alpha = FilledButtonTokens.DisabledContainerOpacity),
1275 disabledContentColor =
1276 fromToken(FilledButtonTokens.DisabledLabelTextColor)
1277 .copy(alpha = FilledButtonTokens.DisabledLabelTextOpacity)
1278 )
1279 .also { defaultButtonColorsCached = it }
1280 }
1281
1282 /**
1283 * Creates a [ButtonColors] that represents the default container and content colors used in an
1284 * [ElevatedButton].
1285 */
1286 @Composable fun elevatedButtonColors() = MaterialTheme.colorScheme.defaultElevatedButtonColors
1287
1288 /**
1289 * Creates a [ButtonColors] that represents the default container and content colors used in an
1290 * [ElevatedButton].
1291 *
1292 * @param containerColor the container color of this [ElevatedButton] when enabled
1293 * @param contentColor the content color of this [ElevatedButton] when enabled
1294 * @param disabledContainerColor the container color of this [ElevatedButton] when not enabled
1295 * @param disabledContentColor the content color of this [ElevatedButton] when not enabled
1296 */
1297 @Composable
1298 fun elevatedButtonColors(
1299 containerColor: Color = Color.Unspecified,
1300 contentColor: Color = Color.Unspecified,
1301 disabledContainerColor: Color = Color.Unspecified,
1302 disabledContentColor: Color = Color.Unspecified,
1303 ): ButtonColors =
1304 MaterialTheme.colorScheme.defaultElevatedButtonColors.copy(
1305 containerColor = containerColor,
1306 contentColor = contentColor,
1307 disabledContainerColor = disabledContainerColor,
1308 disabledContentColor = disabledContentColor
1309 )
1310
1311 internal val ColorScheme.defaultElevatedButtonColors: ButtonColors
1312 get() {
1313 return defaultElevatedButtonColorsCached
1314 ?: ButtonColors(
1315 containerColor = fromToken(ElevatedButtonTokens.ContainerColor),
1316 contentColor = fromToken(ElevatedButtonTokens.LabelTextColor),
1317 disabledContainerColor =
1318 fromToken(ElevatedButtonTokens.DisabledContainerColor)
1319 .copy(alpha = ElevatedButtonTokens.DisabledContainerOpacity),
1320 disabledContentColor =
1321 fromToken(ElevatedButtonTokens.DisabledLabelTextColor)
1322 .copy(alpha = ElevatedButtonTokens.DisabledLabelTextOpacity)
1323 )
1324 .also { defaultElevatedButtonColorsCached = it }
1325 }
1326
1327 /**
1328 * Creates a [ButtonColors] that represents the default container and content colors used in an
1329 * [FilledTonalButton].
1330 */
1331 @Composable
1332 fun filledTonalButtonColors() = MaterialTheme.colorScheme.defaultFilledTonalButtonColors
1333
1334 /**
1335 * Creates a [ButtonColors] that represents the default container and content colors used in an
1336 * [FilledTonalButton].
1337 *
1338 * @param containerColor the container color of this [FilledTonalButton] when enabled
1339 * @param contentColor the content color of this [FilledTonalButton] when enabled
1340 * @param disabledContainerColor the container color of this [FilledTonalButton] when not
1341 * enabled
1342 * @param disabledContentColor the content color of this [FilledTonalButton] when not enabled
1343 */
1344 @Composable
1345 fun filledTonalButtonColors(
1346 containerColor: Color = Color.Unspecified,
1347 contentColor: Color = Color.Unspecified,
1348 disabledContainerColor: Color = Color.Unspecified,
1349 disabledContentColor: Color = Color.Unspecified,
1350 ): ButtonColors =
1351 MaterialTheme.colorScheme.defaultFilledTonalButtonColors.copy(
1352 containerColor = containerColor,
1353 contentColor = contentColor,
1354 disabledContainerColor = disabledContainerColor,
1355 disabledContentColor = disabledContentColor
1356 )
1357
1358 internal val ColorScheme.defaultFilledTonalButtonColors: ButtonColors
1359 get() {
1360 return defaultFilledTonalButtonColorsCached
1361 ?: ButtonColors(
1362 containerColor = fromToken(FilledTonalButtonTokens.ContainerColor),
1363 contentColor = fromToken(FilledTonalButtonTokens.LabelTextColor),
1364 disabledContainerColor =
1365 fromToken(FilledTonalButtonTokens.DisabledContainerColor)
1366 .copy(alpha = FilledTonalButtonTokens.DisabledContainerOpacity),
1367 disabledContentColor =
1368 fromToken(FilledTonalButtonTokens.DisabledLabelTextColor)
1369 .copy(alpha = FilledTonalButtonTokens.DisabledLabelTextOpacity)
1370 )
1371 .also { defaultFilledTonalButtonColorsCached = it }
1372 }
1373
1374 /**
1375 * Creates a [ButtonColors] that represents the default container and content colors used in an
1376 * [OutlinedButton].
1377 */
1378 @Composable fun outlinedButtonColors() = MaterialTheme.colorScheme.defaultOutlinedButtonColors
1379
1380 /**
1381 * Creates a [ButtonColors] that represents the default container and content colors used in an
1382 * [OutlinedButton].
1383 *
1384 * @param containerColor the container color of this [OutlinedButton] when enabled
1385 * @param contentColor the content color of this [OutlinedButton] when enabled
1386 * @param disabledContainerColor the container color of this [OutlinedButton] when not enabled
1387 * @param disabledContentColor the content color of this [OutlinedButton] when not enabled
1388 */
1389 @Composable
1390 fun outlinedButtonColors(
1391 containerColor: Color = Color.Unspecified,
1392 contentColor: Color = Color.Unspecified,
1393 disabledContainerColor: Color = Color.Unspecified,
1394 disabledContentColor: Color = Color.Unspecified,
1395 ): ButtonColors =
1396 MaterialTheme.colorScheme.defaultOutlinedButtonColors.copy(
1397 containerColor = containerColor,
1398 contentColor = contentColor,
1399 disabledContainerColor = disabledContainerColor,
1400 disabledContentColor = disabledContentColor
1401 )
1402
1403 internal val ColorScheme.defaultOutlinedButtonColors: ButtonColors
1404 get() {
1405 return defaultOutlinedButtonColorsCached
1406 ?: ButtonColors(
1407 containerColor = Color.Transparent,
1408 contentColor = fromToken(OutlinedButtonTokens.LabelTextColor),
1409 disabledContainerColor = Color.Transparent,
1410 disabledContentColor =
1411 fromToken(OutlinedButtonTokens.DisabledLabelTextColor)
1412 .copy(alpha = OutlinedButtonTokens.DisabledLabelTextOpacity)
1413 )
1414 .also { defaultOutlinedButtonColorsCached = it }
1415 }
1416
1417 /**
1418 * Creates a [ButtonColors] that represents the default container and content colors used in a
1419 * [TextButton].
1420 */
1421 @Composable fun textButtonColors() = MaterialTheme.colorScheme.defaultTextButtonColors
1422
1423 /**
1424 * Creates a [ButtonColors] that represents the default container and content colors used in a
1425 * [TextButton].
1426 *
1427 * @param containerColor the container color of this [TextButton] when enabled
1428 * @param contentColor the content color of this [TextButton] when enabled
1429 * @param disabledContainerColor the container color of this [TextButton] when not enabled
1430 * @param disabledContentColor the content color of this [TextButton] when not enabled
1431 */
1432 @Composable
1433 fun textButtonColors(
1434 containerColor: Color = Color.Unspecified,
1435 contentColor: Color = Color.Unspecified,
1436 disabledContainerColor: Color = Color.Unspecified,
1437 disabledContentColor: Color = Color.Unspecified,
1438 ): ButtonColors =
1439 MaterialTheme.colorScheme.defaultTextButtonColors.copy(
1440 containerColor = containerColor,
1441 contentColor = contentColor,
1442 disabledContainerColor = disabledContainerColor,
1443 disabledContentColor = disabledContentColor
1444 )
1445
1446 internal val ColorScheme.defaultTextButtonColors: ButtonColors
1447 get() {
1448 return defaultTextButtonColorsCached
1449 ?: ButtonColors(
1450 containerColor = Color.Transparent,
1451 // TODO replace with the token value once it's corrected
1452 contentColor = fromToken(ColorSchemeKeyTokens.Primary),
1453 disabledContainerColor = Color.Transparent,
1454 disabledContentColor =
1455 fromToken(TextButtonTokens.DisabledLabelColor)
1456 .copy(alpha = TextButtonTokens.DisabledLabelOpacity)
1457 )
1458 .also { defaultTextButtonColorsCached = it }
1459 }
1460
1461 /**
1462 * Creates a [ButtonElevation] that will animate between the provided values according to the
1463 * Material specification for a [Button].
1464 *
1465 * @param defaultElevation the elevation used when the [Button] is enabled, and has no other
1466 * [Interaction]s.
1467 * @param pressedElevation the elevation used when this [Button] is enabled and pressed.
1468 * @param focusedElevation the elevation used when the [Button] is enabled and focused.
1469 * @param hoveredElevation the elevation used when the [Button] is enabled and hovered.
1470 * @param disabledElevation the elevation used when the [Button] is not enabled.
1471 */
1472 @Composable
1473 fun buttonElevation(
1474 defaultElevation: Dp = FilledButtonTokens.ContainerElevation,
1475 pressedElevation: Dp = FilledButtonTokens.PressedContainerElevation,
1476 focusedElevation: Dp = FilledButtonTokens.FocusedContainerElevation,
1477 hoveredElevation: Dp = FilledButtonTokens.HoveredContainerElevation,
1478 disabledElevation: Dp = FilledButtonTokens.DisabledContainerElevation,
1479 ): ButtonElevation =
1480 ButtonElevation(
1481 defaultElevation = defaultElevation,
1482 pressedElevation = pressedElevation,
1483 focusedElevation = focusedElevation,
1484 hoveredElevation = hoveredElevation,
1485 disabledElevation = disabledElevation,
1486 )
1487
1488 /**
1489 * Creates a [ButtonElevation] that will animate between the provided values according to the
1490 * Material specification for a [ElevatedButton].
1491 *
1492 * @param defaultElevation the elevation used when the [ElevatedButton] is enabled, and has no
1493 * other [Interaction]s.
1494 * @param pressedElevation the elevation used when this [ElevatedButton] is enabled and pressed.
1495 * @param focusedElevation the elevation used when the [ElevatedButton] is enabled and focused.
1496 * @param hoveredElevation the elevation used when the [ElevatedButton] is enabled and hovered.
1497 * @param disabledElevation the elevation used when the [ElevatedButton] is not enabled.
1498 */
1499 @Composable
1500 fun elevatedButtonElevation(
1501 defaultElevation: Dp = ElevatedButtonTokens.ContainerElevation,
1502 pressedElevation: Dp = ElevatedButtonTokens.PressedContainerElevation,
1503 focusedElevation: Dp = ElevatedButtonTokens.FocusedContainerElevation,
1504 hoveredElevation: Dp = ElevatedButtonTokens.HoveredContainerElevation,
1505 disabledElevation: Dp = ElevatedButtonTokens.DisabledContainerElevation
1506 ): ButtonElevation =
1507 ButtonElevation(
1508 defaultElevation = defaultElevation,
1509 pressedElevation = pressedElevation,
1510 focusedElevation = focusedElevation,
1511 hoveredElevation = hoveredElevation,
1512 disabledElevation = disabledElevation
1513 )
1514
1515 /**
1516 * Creates a [ButtonElevation] that will animate between the provided values according to the
1517 * Material specification for a [FilledTonalButton].
1518 *
1519 * @param defaultElevation the elevation used when the [FilledTonalButton] is enabled, and has
1520 * no other [Interaction]s.
1521 * @param pressedElevation the elevation used when this [FilledTonalButton] is enabled and
1522 * pressed.
1523 * @param focusedElevation the elevation used when the [FilledTonalButton] is enabled and
1524 * focused.
1525 * @param hoveredElevation the elevation used when the [FilledTonalButton] is enabled and
1526 * hovered.
1527 * @param disabledElevation the elevation used when the [FilledTonalButton] is not enabled.
1528 */
1529 @Composable
1530 fun filledTonalButtonElevation(
1531 defaultElevation: Dp = FilledTonalButtonTokens.ContainerElevation,
1532 pressedElevation: Dp = FilledTonalButtonTokens.PressedContainerElevation,
1533 focusedElevation: Dp = FilledTonalButtonTokens.FocusContainerElevation,
1534 hoveredElevation: Dp = FilledTonalButtonTokens.HoverContainerElevation,
1535 disabledElevation: Dp = 0.dp
1536 ): ButtonElevation =
1537 ButtonElevation(
1538 defaultElevation = defaultElevation,
1539 pressedElevation = pressedElevation,
1540 focusedElevation = focusedElevation,
1541 hoveredElevation = hoveredElevation,
1542 disabledElevation = disabledElevation
1543 )
1544
1545 /** The default [BorderStroke] used by [OutlinedButton]. */
1546 val outlinedButtonBorder: BorderStroke
1547 @Composable
1548 @Deprecated(
1549 message =
1550 "Please use the version that takes an `enabled` param to get the " +
1551 "`BorderStroke` with the correct opacity",
1552 replaceWith = ReplaceWith("outlinedButtonBorder(enabled)")
1553 )
1554 get() =
1555 BorderStroke(
1556 width = ButtonSmallTokens.OutlinedOutlineWidth,
1557 color = OutlinedButtonTokens.OutlineColor.value,
1558 )
1559
1560 /**
1561 * The default [BorderStroke] used by [OutlinedButton].
1562 *
1563 * @param enabled whether the button is enabled
1564 */
1565 @Composable
1566 fun outlinedButtonBorder(enabled: Boolean = true): BorderStroke =
1567 BorderStroke(
1568 width = ButtonSmallTokens.OutlinedOutlineWidth,
1569 color =
1570 if (enabled) {
1571 OutlinedButtonTokens.OutlineColor.value
1572 } else {
1573 OutlinedButtonTokens.OutlineColor.value.copy(
1574 alpha = OutlinedButtonTokens.DisabledContainerOpacity
1575 )
1576 }
1577 )
1578
1579 /**
1580 * Recommended [ButtonShapes] for a provided button height.
1581 *
1582 * @param buttonHeight The height of the button
1583 */
1584 @Composable
1585 @ExperimentalMaterial3ExpressiveApi
1586 fun shapesFor(buttonHeight: Dp): ButtonShapes {
1587 val xSmallHeight = ExtraSmallContainerHeight
1588 val smallHeight = MinHeight
1589 val mediumHeight = MediumContainerHeight
1590 val largeHeight = LargeContainerHeight
1591 val xLargeHeight = ExtraLargeContainerHeight
1592 return when {
1593 buttonHeight <= (xSmallHeight + smallHeight) / 2 ->
1594 shapes(shape = shape, pressedShape = extraSmallPressedShape)
1595 buttonHeight <= (smallHeight + mediumHeight) / 2 -> shapes()
1596 buttonHeight <= (mediumHeight + largeHeight) / 2 ->
1597 shapes(shape = shape, pressedShape = mediumPressedShape)
1598 buttonHeight <= (largeHeight + xLargeHeight) / 2 ->
1599 shapes(shape = shape, pressedShape = largePressedShape)
1600 else -> shapes(shape = shape, pressedShape = extraLargePressedShape)
1601 }
1602 }
1603
1604 /**
1605 * Recommended [PaddingValues] for a provided button height.
1606 *
1607 * @param buttonHeight The height of the button
1608 */
1609 @ExperimentalMaterial3ExpressiveApi
1610 fun contentPaddingFor(buttonHeight: Dp): PaddingValues {
1611 val smallHeight = MinHeight
1612 val mediumHeight = MediumContainerHeight
1613 val largeHeight = LargeContainerHeight
1614 val xLargeHeight = ExtraLargeContainerHeight
1615 return when {
1616 buttonHeight < smallHeight -> ExtraSmallContentPadding
1617 buttonHeight < mediumHeight -> SmallContentPadding
1618 buttonHeight < largeHeight -> MediumContentPadding
1619 buttonHeight < xLargeHeight -> LargeContentPadding
1620 else -> ExtraLargeContentPadding
1621 }
1622 }
1623
1624 /**
1625 * Recommended Icon size for a provided button height.
1626 *
1627 * @param buttonHeight The height of the button
1628 */
1629 @ExperimentalMaterial3ExpressiveApi
1630 fun iconSizeFor(buttonHeight: Dp): Dp {
1631 val smallHeight = MinHeight
1632 val mediumHeight = MediumContainerHeight
1633 val largeHeight = LargeContainerHeight
1634 val xLargeHeight = ExtraLargeContainerHeight
1635 return when {
1636 buttonHeight < smallHeight -> ExtraSmallIconSize
1637 buttonHeight < mediumHeight -> SmallIconSize
1638 buttonHeight < largeHeight -> MediumIconSize
1639 buttonHeight < xLargeHeight -> LargeIconSize
1640 else -> ExtraLargeIconSize
1641 }
1642 }
1643
1644 /**
1645 * Recommended spacing after an [Icon] for a provided button height.
1646 *
1647 * @param buttonHeight The height of the button
1648 */
1649 @ExperimentalMaterial3ExpressiveApi
1650 fun iconSpacingFor(buttonHeight: Dp): Dp {
1651 val smallHeight = MinHeight
1652 val mediumHeight = MediumContainerHeight
1653 val largeHeight = LargeContainerHeight
1654 val xLargeHeight = ExtraLargeContainerHeight
1655 return when {
1656 buttonHeight < smallHeight -> ExtraSmallIconSpacing
1657 buttonHeight < mediumHeight -> IconSpacing
1658 buttonHeight < largeHeight -> MediumIconSpacing
1659 buttonHeight < xLargeHeight -> LargeIconSpacing
1660 else -> ExtraLargeIconSpacing
1661 }
1662 }
1663
1664 /**
1665 * Recommended [TextStyle] for a [Text] provided a button height.
1666 *
1667 * @param buttonHeight The height of the button
1668 */
1669 @Composable
1670 @ExperimentalMaterial3ExpressiveApi
1671 fun textStyleFor(buttonHeight: Dp): TextStyle {
1672 val mediumHeight = MediumContainerHeight
1673 val largeHeight = LargeContainerHeight
1674 val xLargeHeight = ExtraLargeContainerHeight
1675 return when {
1676 buttonHeight < mediumHeight -> MaterialTheme.typography.labelLarge
1677 buttonHeight < largeHeight -> MaterialTheme.typography.titleMedium
1678 buttonHeight < xLargeHeight -> MaterialTheme.typography.headlineSmall
1679 else -> MaterialTheme.typography.headlineLarge
1680 }
1681 }
1682 }
1683
1684 /**
1685 * Represents the elevation for a button in different states.
1686 * - See [ButtonDefaults.buttonElevation] for the default elevation used in a [Button].
1687 * - See [ButtonDefaults.elevatedButtonElevation] for the default elevation used in a
1688 * [ElevatedButton].
1689 */
1690 @Stable
1691 class ButtonElevation
1692 internal constructor(
1693 private val defaultElevation: Dp,
1694 private val pressedElevation: Dp,
1695 private val focusedElevation: Dp,
1696 private val hoveredElevation: Dp,
1697 private val disabledElevation: Dp,
1698 ) {
1699 /**
1700 * Represents the shadow elevation used in a button, depending on its [enabled] state and
1701 * [interactionSource].
1702 *
1703 * Shadow elevation is used to apply a shadow around the button to give it higher emphasis.
1704 *
1705 * @param enabled whether the button is enabled
1706 * @param interactionSource the [InteractionSource] for this button
1707 */
1708 @Composable
shadowElevationnull1709 internal fun shadowElevation(
1710 enabled: Boolean,
1711 interactionSource: InteractionSource
1712 ): State<Dp> {
1713 return animateElevation(enabled = enabled, interactionSource = interactionSource)
1714 }
1715
1716 @Composable
animateElevationnull1717 private fun animateElevation(
1718 enabled: Boolean,
1719 interactionSource: InteractionSource
1720 ): State<Dp> {
1721 val interactions = remember { mutableStateListOf<Interaction>() }
1722 LaunchedEffect(interactionSource) {
1723 interactionSource.interactions.collect { interaction ->
1724 when (interaction) {
1725 is HoverInteraction.Enter -> {
1726 interactions.add(interaction)
1727 }
1728 is HoverInteraction.Exit -> {
1729 interactions.remove(interaction.enter)
1730 }
1731 is FocusInteraction.Focus -> {
1732 interactions.add(interaction)
1733 }
1734 is FocusInteraction.Unfocus -> {
1735 interactions.remove(interaction.focus)
1736 }
1737 is PressInteraction.Press -> {
1738 interactions.add(interaction)
1739 }
1740 is PressInteraction.Release -> {
1741 interactions.remove(interaction.press)
1742 }
1743 is PressInteraction.Cancel -> {
1744 interactions.remove(interaction.press)
1745 }
1746 }
1747 }
1748 }
1749
1750 val interaction = interactions.lastOrNull()
1751
1752 val target =
1753 if (!enabled) {
1754 disabledElevation
1755 } else {
1756 when (interaction) {
1757 is PressInteraction.Press -> pressedElevation
1758 is HoverInteraction.Enter -> hoveredElevation
1759 is FocusInteraction.Focus -> focusedElevation
1760 else -> defaultElevation
1761 }
1762 }
1763
1764 val animatable = remember { Animatable(target, Dp.VectorConverter) }
1765
1766 LaunchedEffect(target) {
1767 if (animatable.targetValue != target) {
1768 if (!enabled) {
1769 // No transition when moving to a disabled state
1770 animatable.snapTo(target)
1771 } else {
1772 val lastInteraction =
1773 when (animatable.targetValue) {
1774 pressedElevation -> PressInteraction.Press(Offset.Zero)
1775 hoveredElevation -> HoverInteraction.Enter()
1776 focusedElevation -> FocusInteraction.Focus()
1777 else -> null
1778 }
1779 animatable.animateElevation(
1780 from = lastInteraction,
1781 to = interaction,
1782 target = target
1783 )
1784 }
1785 }
1786 }
1787
1788 return animatable.asState()
1789 }
1790
equalsnull1791 override fun equals(other: Any?): Boolean {
1792 if (this === other) return true
1793 if (other == null || other !is ButtonElevation) return false
1794
1795 if (defaultElevation != other.defaultElevation) return false
1796 if (pressedElevation != other.pressedElevation) return false
1797 if (focusedElevation != other.focusedElevation) return false
1798 if (hoveredElevation != other.hoveredElevation) return false
1799 if (disabledElevation != other.disabledElevation) return false
1800
1801 return true
1802 }
1803
hashCodenull1804 override fun hashCode(): Int {
1805 var result = defaultElevation.hashCode()
1806 result = 31 * result + pressedElevation.hashCode()
1807 result = 31 * result + focusedElevation.hashCode()
1808 result = 31 * result + hoveredElevation.hashCode()
1809 result = 31 * result + disabledElevation.hashCode()
1810 return result
1811 }
1812 }
1813
1814 /**
1815 * Represents the container and content colors used in a button in different states.
1816 *
1817 * @param containerColor the container color of this [Button] when enabled.
1818 * @param contentColor the content color of this [Button] when enabled.
1819 * @param disabledContainerColor the container color of this [Button] when not enabled.
1820 * @param disabledContentColor the content color of this [Button] when not enabled.
1821 * @constructor create an instance with arbitrary colors.
1822 * - See [ButtonDefaults.buttonColors] for the default colors used in a [Button].
1823 * - See [ButtonDefaults.elevatedButtonColors] for the default colors used in a [ElevatedButton].
1824 * - See [ButtonDefaults.textButtonColors] for the default colors used in a [TextButton].
1825 */
1826 @Immutable
1827 class ButtonColors
1828 constructor(
1829 val containerColor: Color,
1830 val contentColor: Color,
1831 val disabledContainerColor: Color,
1832 val disabledContentColor: Color,
1833 ) {
1834 /**
1835 * Returns a copy of this ButtonColors, optionally overriding some of the values. This uses the
1836 * Color.Unspecified to mean “use the value from the source”
1837 */
copynull1838 fun copy(
1839 containerColor: Color = this.containerColor,
1840 contentColor: Color = this.contentColor,
1841 disabledContainerColor: Color = this.disabledContainerColor,
1842 disabledContentColor: Color = this.disabledContentColor
1843 ) =
1844 ButtonColors(
1845 containerColor.takeOrElse { this.containerColor },
<lambda>null1846 contentColor.takeOrElse { this.contentColor },
<lambda>null1847 disabledContainerColor.takeOrElse { this.disabledContainerColor },
<lambda>null1848 disabledContentColor.takeOrElse { this.disabledContentColor },
1849 )
1850
1851 /**
1852 * Represents the container color for this button, depending on [enabled].
1853 *
1854 * @param enabled whether the button is enabled
1855 */
1856 @Stable
containerColornull1857 internal fun containerColor(enabled: Boolean): Color =
1858 if (enabled) containerColor else disabledContainerColor
1859
1860 /**
1861 * Represents the content color for this button, depending on [enabled].
1862 *
1863 * @param enabled whether the button is enabled
1864 */
1865 @Stable
1866 internal fun contentColor(enabled: Boolean): Color =
1867 if (enabled) contentColor else disabledContentColor
1868
1869 override fun equals(other: Any?): Boolean {
1870 if (this === other) return true
1871 if (other == null || other !is ButtonColors) return false
1872
1873 if (containerColor != other.containerColor) return false
1874 if (contentColor != other.contentColor) return false
1875 if (disabledContainerColor != other.disabledContainerColor) return false
1876 if (disabledContentColor != other.disabledContentColor) return false
1877
1878 return true
1879 }
1880
hashCodenull1881 override fun hashCode(): Int {
1882 var result = containerColor.hashCode()
1883 result = 31 * result + contentColor.hashCode()
1884 result = 31 * result + disabledContainerColor.hashCode()
1885 result = 31 * result + disabledContentColor.hashCode()
1886 return result
1887 }
1888 }
1889
1890 /**
1891 * The shapes that will be used in buttons. Button will morph between these shapes depending on the
1892 * interaction of the button, assuming all of the shapes are [CornerBasedShape]s.
1893 *
1894 * @property shape is the active shape.
1895 * @property pressedShape is the pressed shape.
1896 */
1897 @ExperimentalMaterial3ExpressiveApi
1898 @Immutable
1899 class ButtonShapes(val shape: Shape, val pressedShape: Shape) {
1900 /** Returns a copy of this ButtonShapes, optionally overriding some of the values. */
copynull1901 fun copy(
1902 shape: Shape? = this.shape,
1903 pressedShape: Shape? = this.pressedShape,
1904 ) =
1905 ButtonShapes(
1906 shape = shape.takeOrElse { this.shape },
<lambda>null1907 pressedShape = pressedShape.takeOrElse { this.pressedShape }
1908 )
1909
takeOrElsenull1910 internal fun Shape?.takeOrElse(block: () -> Shape): Shape = this ?: block()
1911
1912 override fun equals(other: Any?): Boolean {
1913 if (this === other) return true
1914 if (other == null || other !is ButtonShapes) return false
1915
1916 if (shape != other.shape) return false
1917 if (pressedShape != other.pressedShape) return false
1918
1919 return true
1920 }
1921
hashCodenull1922 override fun hashCode(): Int {
1923 var result = shape.hashCode()
1924 result = 31 * result + pressedShape.hashCode()
1925
1926 return result
1927 }
1928 }
1929
1930 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
1931 internal val ButtonShapes.hasRoundedCornerShapes: Boolean
1932 get() = shape is RoundedCornerShape && pressedShape is RoundedCornerShape
1933
1934 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
1935 @Composable
shapeByInteractionnull1936 private fun shapeByInteraction(
1937 shapes: ButtonShapes,
1938 pressed: Boolean,
1939 animationSpec: FiniteAnimationSpec<Float>
1940 ): Shape {
1941 val shape =
1942 if (pressed) {
1943 shapes.pressedShape
1944 } else {
1945 shapes.shape
1946 }
1947
1948 if (shapes.hasRoundedCornerShapes)
1949 return key(shapes) {
1950 rememberAnimatedShape(
1951 shape as RoundedCornerShape,
1952 animationSpec,
1953 )
1954 }
1955
1956 return shape
1957 }
1958