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  * ![Filled button
83  * image](https://developer.android.com/images/reference/androidx/compose/material3/filled-button.png)
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  * ![Filled button
196  * image](https://developer.android.com/images/reference/androidx/compose/material3/filled-button.png)
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  * ![Elevated button
296  * image](https://developer.android.com/images/reference/androidx/compose/material3/elevated-button.png)
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  * ![Elevated button
372  * image](https://developer.android.com/images/reference/androidx/compose/material3/elevated-button.png)
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  * ![Filled tonal button
445  * image](https://developer.android.com/images/reference/androidx/compose/material3/filled-tonal-button.png)
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  * ![Filled tonal button
522  * image](https://developer.android.com/images/reference/androidx/compose/material3/filled-tonal-button.png)
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  * ![Outlined button
596  * image](https://developer.android.com/images/reference/androidx/compose/material3/outlined-button.png)
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  * ![Outlined button
672  * image](https://developer.android.com/images/reference/androidx/compose/material3/outlined-button.png)
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  * ![Text button
745  * image](https://developer.android.com/images/reference/androidx/compose/material3/text-button.png)
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  * ![Text button
822  * image](https://developer.android.com/images/reference/androidx/compose/material3/text-button.png)
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