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.AnimatedVisibility
20 import androidx.compose.animation.core.Animatable
21 import androidx.compose.animation.core.AnimationSpec
22 import androidx.compose.animation.core.VectorConverter
23 import androidx.compose.animation.core.animateFloat
24 import androidx.compose.animation.core.updateTransition
25 import androidx.compose.animation.expandHorizontally
26 import androidx.compose.animation.fadeIn
27 import androidx.compose.animation.fadeOut
28 import androidx.compose.animation.shrinkHorizontally
29 import androidx.compose.foundation.interaction.FocusInteraction
30 import androidx.compose.foundation.interaction.HoverInteraction
31 import androidx.compose.foundation.interaction.Interaction
32 import androidx.compose.foundation.interaction.InteractionSource
33 import androidx.compose.foundation.interaction.MutableInteractionSource
34 import androidx.compose.foundation.interaction.PressInteraction
35 import androidx.compose.foundation.layout.Arrangement
36 import androidx.compose.foundation.layout.Box
37 import androidx.compose.foundation.layout.Row
38 import androidx.compose.foundation.layout.RowScope
39 import androidx.compose.foundation.layout.Spacer
40 import androidx.compose.foundation.layout.defaultMinSize
41 import androidx.compose.foundation.layout.padding
42 import androidx.compose.foundation.layout.sizeIn
43 import androidx.compose.foundation.layout.width
44 import androidx.compose.material3.internal.ProvideContentColorTextStyle
45 import androidx.compose.material3.internal.animateElevation
46 import androidx.compose.material3.tokens.ElevationTokens
47 import androidx.compose.material3.tokens.ExtendedFabLargeTokens
48 import androidx.compose.material3.tokens.ExtendedFabMediumTokens
49 import androidx.compose.material3.tokens.ExtendedFabPrimaryTokens
50 import androidx.compose.material3.tokens.ExtendedFabSmallTokens
51 import androidx.compose.material3.tokens.FabBaselineTokens
52 import androidx.compose.material3.tokens.FabLargeTokens
53 import androidx.compose.material3.tokens.FabMediumTokens
54 import androidx.compose.material3.tokens.FabPrimaryContainerTokens
55 import androidx.compose.material3.tokens.FabSmallTokens
56 import androidx.compose.material3.tokens.MotionSchemeKeyTokens
57 import androidx.compose.material3.tokens.TypographyKeyTokens
58 import androidx.compose.runtime.Composable
59 import androidx.compose.runtime.LaunchedEffect
60 import androidx.compose.runtime.Stable
61 import androidx.compose.runtime.State
62 import androidx.compose.runtime.derivedStateOf
63 import androidx.compose.runtime.remember
64 import androidx.compose.ui.Alignment
65 import androidx.compose.ui.Modifier
66 import androidx.compose.ui.draw.CacheDrawModifierNode
67 import androidx.compose.ui.geometry.Offset
68 import androidx.compose.ui.geometry.Size
69 import androidx.compose.ui.graphics.Color
70 import androidx.compose.ui.graphics.Shape
71 import androidx.compose.ui.graphics.drawscope.inset
72 import androidx.compose.ui.graphics.graphicsLayer
73 import androidx.compose.ui.graphics.layer.drawLayer
74 import androidx.compose.ui.layout.layout
75 import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
76 import androidx.compose.ui.node.DelegatingNode
77 import androidx.compose.ui.node.ModifierNodeElement
78 import androidx.compose.ui.node.currentValueOf
79 import androidx.compose.ui.platform.InspectorInfo
80 import androidx.compose.ui.semantics.Role
81 import androidx.compose.ui.semantics.clearAndSetSemantics
82 import androidx.compose.ui.semantics.role
83 import androidx.compose.ui.semantics.semantics
84 import androidx.compose.ui.text.TextStyle
85 import androidx.compose.ui.unit.Dp
86 import androidx.compose.ui.unit.IntOffset
87 import androidx.compose.ui.unit.IntSize
88 import androidx.compose.ui.unit.dp
89 import androidx.compose.ui.unit.toIntSize
90 import androidx.compose.ui.unit.toOffset
91 import androidx.compose.ui.util.lerp
92 import kotlin.math.roundToInt
93 import kotlinx.coroutines.launch
94 
95 /**
96  * [Material Design floating action
97  * button](https://m3.material.io/components/floating-action-button/overview)
98  *
99  * The FAB represents the most important action on a screen. It puts key actions within reach.
100  *
101  * ![FAB image](https://developer.android.com/images/reference/androidx/compose/material3/fab.png)
102  *
103  * FAB typically contains an icon, for a FAB with text and an icon, see
104  * [ExtendedFloatingActionButton].
105  *
106  * @sample androidx.compose.material3.samples.FloatingActionButtonSample
107  * @param onClick called when this FAB is clicked
108  * @param modifier the [Modifier] to be applied to this FAB
109  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
110  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
111  *   have no color.
112  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
113  *   matching content color for [containerColor], or to the current [LocalContentColor] if
114  *   [containerColor] is not a color from the theme.
115  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
116  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
117  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
118  *   an overlay. See also: [Surface].
119  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
120  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
121  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
122  *   happen internally.
123  * @param content the content of this FAB, typically an [Icon]
124  */
125 @Composable
126 fun FloatingActionButton(
127     onClick: () -> Unit,
128     modifier: Modifier = Modifier,
129     shape: Shape = FloatingActionButtonDefaults.shape,
130     containerColor: Color = FloatingActionButtonDefaults.containerColor,
131     contentColor: Color = contentColorFor(containerColor),
132     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
133     interactionSource: MutableInteractionSource? = null,
134     content: @Composable () -> Unit,
135 ) =
136     FloatingActionButton(
137         onClick,
138         ExtendedFabPrimaryTokens.LabelTextFont.value,
139         FabBaselineTokens.ContainerWidth,
140         FabBaselineTokens.ContainerHeight,
141         modifier,
142         shape,
143         containerColor,
144         contentColor,
145         elevation,
146         interactionSource,
147         content
148     )
149 
150 @Composable
151 private fun FloatingActionButton(
152     onClick: () -> Unit,
153     textStyle: TextStyle,
154     minWidth: Dp,
155     minHeight: Dp,
156     modifier: Modifier = Modifier,
157     shape: Shape = FloatingActionButtonDefaults.shape,
158     containerColor: Color = FloatingActionButtonDefaults.containerColor,
159     contentColor: Color = contentColorFor(containerColor),
160     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
161     interactionSource: MutableInteractionSource? = null,
162     content: @Composable () -> Unit,
163 ) {
164     @Suppress("NAME_SHADOWING")
165     val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
166     Surface(
167         onClick = onClick,
168         modifier = modifier.semantics { role = Role.Button },
169         shape = shape,
170         color = containerColor,
171         contentColor = contentColor,
172         tonalElevation = elevation.tonalElevation(),
173         shadowElevation = elevation.shadowElevation(interactionSource = interactionSource).value,
174         interactionSource = interactionSource
175     ) {
176         ProvideContentColorTextStyle(contentColor = contentColor, textStyle = textStyle) {
177             Box(
178                 modifier =
179                     Modifier.defaultMinSize(
180                         minWidth = minWidth,
181                         minHeight = minHeight,
182                     ),
183                 contentAlignment = Alignment.Center,
184             ) {
185                 content()
186             }
187         }
188     }
189 }
190 
191 /**
192  * [Material Design small floating action
193  * button](https://m3.material.io/components/floating-action-button/overview)
194  *
195  * The FAB represents the most important action on a screen. It puts key actions within reach.
196  *
197  * ![Small FAB
198  * image](https://developer.android.com/images/reference/androidx/compose/material3/small-fab.png)
199  *
200  * @sample androidx.compose.material3.samples.SmallFloatingActionButtonSample
201  *
202  * FABs can also be shown and hidden with an animation when the main content is scrolled:
203  *
204  * @sample androidx.compose.material3.samples.AnimatedFloatingActionButtonSample
205  * @param onClick called when this FAB is clicked
206  * @param modifier the [Modifier] to be applied to this FAB
207  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
208  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
209  *   have no color.
210  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
211  *   matching content color for [containerColor], or to the current [LocalContentColor] if
212  *   [containerColor] is not a color from the theme.
213  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
214  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
215  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
216  *   an overlay. See also: [Surface].
217  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
218  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
219  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
220  *   happen internally.
221  * @param content the content of this FAB, typically an [Icon]
222  */
223 @Composable
SmallFloatingActionButtonnull224 fun SmallFloatingActionButton(
225     onClick: () -> Unit,
226     modifier: Modifier = Modifier,
227     shape: Shape = FloatingActionButtonDefaults.smallShape,
228     containerColor: Color = FloatingActionButtonDefaults.containerColor,
229     contentColor: Color = contentColorFor(containerColor),
230     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
231     interactionSource: MutableInteractionSource? = null,
232     content: @Composable () -> Unit,
233 ) {
234     FloatingActionButton(
235         onClick = onClick,
236         modifier =
237             modifier.sizeIn(
238                 minWidth = FabSmallTokens.ContainerWidth,
239                 minHeight = FabSmallTokens.ContainerHeight,
240             ),
241         shape = shape,
242         containerColor = containerColor,
243         contentColor = contentColor,
244         elevation = elevation,
245         interactionSource = interactionSource,
246         content = content,
247     )
248 }
249 
250 /**
251  * [Material Design medium floating action
252  * button](https://m3.material.io/components/floating-action-button/overview)
253  *
254  * The FAB represents the most important action on a screen. It puts key actions within reach.
255  *
256  * @sample androidx.compose.material3.samples.MediumFloatingActionButtonSample
257  *
258  * FABs can also be shown and hidden with an animation when the main content is scrolled:
259  *
260  * @sample androidx.compose.material3.samples.AnimatedFloatingActionButtonSample
261  * @param onClick called when this FAB is clicked
262  * @param modifier the [Modifier] to be applied to this FAB
263  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
264  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
265  *   have no color.
266  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
267  *   matching content color for [containerColor], or to the current [LocalContentColor] if
268  *   [containerColor] is not a color from the theme.
269  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
270  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
271  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
272  *   an overlay. See also: [Surface].
273  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
274  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
275  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
276  *   happen internally.
277  * @param content the content of this FAB, typically an [Icon]
278  */
279 @ExperimentalMaterial3ExpressiveApi
280 @Composable
MediumFloatingActionButtonnull281 fun MediumFloatingActionButton(
282     onClick: () -> Unit,
283     modifier: Modifier = Modifier,
284     shape: Shape = FloatingActionButtonDefaults.mediumShape,
285     containerColor: Color = FloatingActionButtonDefaults.containerColor,
286     contentColor: Color = contentColorFor(containerColor),
287     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
288     interactionSource: MutableInteractionSource? = null,
289     content: @Composable () -> Unit,
290 ) {
291     FloatingActionButton(
292         onClick = onClick,
293         modifier =
294             modifier.sizeIn(
295                 minWidth = FabMediumTokens.ContainerWidth,
296                 minHeight = FabMediumTokens.ContainerHeight,
297             ),
298         shape = shape,
299         containerColor = containerColor,
300         contentColor = contentColor,
301         elevation = elevation,
302         interactionSource = interactionSource,
303         content = content,
304     )
305 }
306 
307 /**
308  * [Material Design large floating action
309  * button](https://m3.material.io/components/floating-action-button/overview)
310  *
311  * The FAB represents the most important action on a screen. It puts key actions within reach.
312  *
313  * ![Large FAB
314  * image](https://developer.android.com/images/reference/androidx/compose/material3/large-fab.png)
315  *
316  * @sample androidx.compose.material3.samples.LargeFloatingActionButtonSample
317  *
318  * FABs can also be shown and hidden with an animation when the main content is scrolled:
319  *
320  * @sample androidx.compose.material3.samples.AnimatedFloatingActionButtonSample
321  * @param onClick called when this FAB is clicked
322  * @param modifier the [Modifier] to be applied to this FAB
323  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
324  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
325  *   have no color.
326  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
327  *   matching content color for [containerColor], or to the current [LocalContentColor] if
328  *   [containerColor] is not a color from the theme.
329  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
330  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
331  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
332  *   an overlay. See also: [Surface].
333  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
334  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
335  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
336  *   happen internally.
337  * @param content the content of this FAB, typically an [Icon]
338  */
339 @Composable
LargeFloatingActionButtonnull340 fun LargeFloatingActionButton(
341     onClick: () -> Unit,
342     modifier: Modifier = Modifier,
343     shape: Shape = FloatingActionButtonDefaults.largeShape,
344     containerColor: Color = FloatingActionButtonDefaults.containerColor,
345     contentColor: Color = contentColorFor(containerColor),
346     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
347     interactionSource: MutableInteractionSource? = null,
348     content: @Composable () -> Unit,
349 ) {
350     FloatingActionButton(
351         onClick = onClick,
352         modifier =
353             modifier.sizeIn(
354                 minWidth = FabLargeTokens.ContainerWidth,
355                 minHeight = FabLargeTokens.ContainerHeight,
356             ),
357         shape = shape,
358         containerColor = containerColor,
359         contentColor = contentColor,
360         elevation = elevation,
361         interactionSource = interactionSource,
362         content = content,
363     )
364 }
365 
366 // TODO link to image
367 /**
368  * [Material Design small extended floating action
369  * button](https://m3.material.io/components/extended-fab/overview)
370  *
371  * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
372  * label and larger target area.
373  *
374  * The other small extended floating action button overload supports a text label and icon.
375  *
376  * @sample androidx.compose.material3.samples.SmallExtendedFloatingActionButtonTextSample
377  * @param onClick called when this FAB is clicked
378  * @param modifier the [Modifier] to be applied to this FAB
379  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
380  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
381  *   have no color.
382  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
383  *   matching content color for [containerColor], or to the current [LocalContentColor] if
384  *   [containerColor] is not a color from the theme.
385  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
386  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
387  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
388  *   an overlay. See also: [Surface].
389  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
390  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
391  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
392  *   happen internally.
393  * @param content the content of this FAB, typically a [Text] label
394  */
395 @ExperimentalMaterial3ExpressiveApi
396 @Composable
SmallExtendedFloatingActionButtonnull397 fun SmallExtendedFloatingActionButton(
398     onClick: () -> Unit,
399     modifier: Modifier = Modifier,
400     shape: Shape = FloatingActionButtonDefaults.smallExtendedFabShape,
401     containerColor: Color = FloatingActionButtonDefaults.containerColor,
402     contentColor: Color = contentColorFor(containerColor),
403     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
404     interactionSource: MutableInteractionSource? = null,
405     content: @Composable RowScope.() -> Unit,
406 ) {
407     FloatingActionButton(
408         onClick = onClick,
409         textStyle = SmallExtendedFabTextStyle.value,
410         minWidth = SmallExtendedFabMinimumWidth,
411         minHeight = SmallExtendedFabMinimumHeight,
412         modifier = modifier,
413         shape = shape,
414         containerColor = containerColor,
415         contentColor = contentColor,
416         elevation = elevation,
417         interactionSource = interactionSource,
418     ) {
419         Row(
420             modifier =
421                 Modifier.padding(
422                     start = SmallExtendedFabPaddingStart,
423                     end = SmallExtendedFabPaddingEnd
424                 ),
425             horizontalArrangement = Arrangement.Center,
426             verticalAlignment = Alignment.CenterVertically,
427             content = content,
428         )
429     }
430 }
431 
432 // TODO link to image
433 /**
434  * [Material Design medium extended floating action
435  * button](https://m3.material.io/components/extended-fab/overview)
436  *
437  * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
438  * label and larger target area.
439  *
440  * The other medium extended floating action button overload supports a text label and icon.
441  *
442  * @sample androidx.compose.material3.samples.MediumExtendedFloatingActionButtonTextSample
443  * @param onClick called when this FAB is clicked
444  * @param modifier the [Modifier] to be applied to this FAB
445  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
446  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
447  *   have no color.
448  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
449  *   matching content color for [containerColor], or to the current [LocalContentColor] if
450  *   [containerColor] is not a color from the theme.
451  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
452  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
453  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
454  *   an overlay. See also: [Surface].
455  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
456  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
457  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
458  *   happen internally.
459  * @param content the content of this FAB, typically a [Text] label
460  */
461 @ExperimentalMaterial3ExpressiveApi
462 @Composable
MediumExtendedFloatingActionButtonnull463 fun MediumExtendedFloatingActionButton(
464     onClick: () -> Unit,
465     modifier: Modifier = Modifier,
466     shape: Shape = FloatingActionButtonDefaults.mediumExtendedFabShape,
467     containerColor: Color = FloatingActionButtonDefaults.containerColor,
468     contentColor: Color = contentColorFor(containerColor),
469     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
470     interactionSource: MutableInteractionSource? = null,
471     content: @Composable RowScope.() -> Unit,
472 ) {
473     FloatingActionButton(
474         onClick = onClick,
475         textStyle = MediumExtendedFabTextStyle.value,
476         minWidth = MediumExtendedFabMinimumWidth,
477         minHeight = MediumExtendedFabMinimumHeight,
478         modifier = modifier,
479         shape = shape,
480         containerColor = containerColor,
481         contentColor = contentColor,
482         elevation = elevation,
483         interactionSource = interactionSource,
484     ) {
485         Row(
486             modifier =
487                 Modifier.padding(
488                     start = MediumExtendedFabPaddingStart,
489                     end = MediumExtendedFabPaddingEnd
490                 ),
491             horizontalArrangement = Arrangement.Center,
492             verticalAlignment = Alignment.CenterVertically,
493             content = content,
494         )
495     }
496 }
497 
498 // TODO link to image
499 /**
500  * [Material Design large extended floating action
501  * button](https://m3.material.io/components/extended-fab/overview)
502  *
503  * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
504  * label and larger target area.
505  *
506  * The other large extended floating action button overload supports a text label and icon.
507  *
508  * @sample androidx.compose.material3.samples.LargeExtendedFloatingActionButtonTextSample
509  * @param onClick called when this FAB is clicked
510  * @param modifier the [Modifier] to be applied to this FAB
511  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
512  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
513  *   have no color.
514  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
515  *   matching content color for [containerColor], or to the current [LocalContentColor] if
516  *   [containerColor] is not a color from the theme.
517  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
518  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
519  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
520  *   an overlay. See also: [Surface].
521  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
522  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
523  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
524  *   happen internally.
525  * @param content the content of this FAB, typically a [Text] label
526  */
527 @ExperimentalMaterial3ExpressiveApi
528 @Composable
LargeExtendedFloatingActionButtonnull529 fun LargeExtendedFloatingActionButton(
530     onClick: () -> Unit,
531     modifier: Modifier = Modifier,
532     shape: Shape = FloatingActionButtonDefaults.largeExtendedFabShape,
533     containerColor: Color = FloatingActionButtonDefaults.containerColor,
534     contentColor: Color = contentColorFor(containerColor),
535     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
536     interactionSource: MutableInteractionSource? = null,
537     content: @Composable RowScope.() -> Unit,
538 ) {
539     FloatingActionButton(
540         onClick = onClick,
541         textStyle = LargeExtendedFabTextStyle.value,
542         minWidth = LargeExtendedFabMinimumWidth,
543         minHeight = LargeExtendedFabMinimumHeight,
544         modifier = modifier,
545         shape = shape,
546         containerColor = containerColor,
547         contentColor = contentColor,
548         elevation = elevation,
549         interactionSource = interactionSource,
550     ) {
551         Row(
552             modifier =
553                 Modifier.padding(
554                     start = LargeExtendedFabPaddingStart,
555                     end = LargeExtendedFabPaddingEnd
556                 ),
557             horizontalArrangement = Arrangement.Center,
558             verticalAlignment = Alignment.CenterVertically,
559             content = content,
560         )
561     }
562 }
563 
564 /**
565  * [Material Design extended floating action
566  * button](https://m3.material.io/components/extended-fab/overview)
567  *
568  * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
569  * label and larger target area.
570  *
571  * ![Extended FAB
572  * image](https://developer.android.com/images/reference/androidx/compose/material3/extended-fab.png)
573  *
574  * The other extended floating action button overload supports a text label and icon.
575  *
576  * @sample androidx.compose.material3.samples.ExtendedFloatingActionButtonTextSample
577  * @param onClick called when this FAB is clicked
578  * @param modifier the [Modifier] to be applied to this FAB
579  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
580  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
581  *   have no color.
582  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
583  *   matching content color for [containerColor], or to the current [LocalContentColor] if
584  *   [containerColor] is not a color from the theme.
585  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
586  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
587  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
588  *   an overlay. See also: [Surface].
589  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
590  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
591  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
592  *   happen internally.
593  * @param content the content of this FAB, typically a [Text] label
594  */
595 @Composable
ExtendedFloatingActionButtonnull596 fun ExtendedFloatingActionButton(
597     onClick: () -> Unit,
598     modifier: Modifier = Modifier,
599     shape: Shape = FloatingActionButtonDefaults.extendedFabShape,
600     containerColor: Color = FloatingActionButtonDefaults.containerColor,
601     contentColor: Color = contentColorFor(containerColor),
602     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
603     interactionSource: MutableInteractionSource? = null,
604     content: @Composable RowScope.() -> Unit,
605 ) {
606     FloatingActionButton(
607         onClick = onClick,
608         modifier = modifier,
609         shape = shape,
610         containerColor = containerColor,
611         contentColor = contentColor,
612         elevation = elevation,
613         interactionSource = interactionSource,
614     ) {
615         Row(
616             modifier =
617                 Modifier.sizeIn(minWidth = ExtendedFabMinimumWidth)
618                     .padding(horizontal = ExtendedFabTextPadding),
619             horizontalArrangement = Arrangement.Center,
620             verticalAlignment = Alignment.CenterVertically,
621             content = content,
622         )
623     }
624 }
625 
626 /**
627  * [Material Design small extended floating action
628  * button](https://m3.material.io/components/extended-fab/overview)
629  *
630  * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
631  * label and larger target area.
632  *
633  * The other small extended floating action button overload is for FABs without an icon.
634  *
635  * Default content description for accessibility is extended from the extended fabs icon. For custom
636  * behavior, you can provide your own via [Modifier.semantics].
637  *
638  * @sample androidx.compose.material3.samples.SmallExtendedFloatingActionButtonSample
639  * @sample androidx.compose.material3.samples.SmallAnimatedExtendedFloatingActionButtonSample
640  * @param text label displayed inside this FAB
641  * @param icon icon for this FAB, typically an [Icon]
642  * @param onClick called when this FAB is clicked
643  * @param modifier the [Modifier] to be applied to this FAB
644  * @param expanded controls the expansion state of this FAB. In an expanded state, the FAB will show
645  *   both the [icon] and [text]. In a collapsed state, the FAB will show only the [icon].
646  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
647  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
648  *   have no color.
649  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
650  *   matching content color for [containerColor], or to the current [LocalContentColor] if
651  *   [containerColor] is not a color from the theme.
652  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
653  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
654  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
655  *   an overlay. See also: [Surface].
656  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
657  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
658  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
659  *   happen internally.
660  */
661 @ExperimentalMaterial3ExpressiveApi
662 @Composable
SmallExtendedFloatingActionButtonnull663 fun SmallExtendedFloatingActionButton(
664     text: @Composable () -> Unit,
665     icon: @Composable () -> Unit,
666     onClick: () -> Unit,
667     modifier: Modifier = Modifier,
668     expanded: Boolean = true,
669     shape: Shape = FloatingActionButtonDefaults.smallExtendedFabShape,
670     containerColor: Color = FloatingActionButtonDefaults.containerColor,
671     contentColor: Color = contentColorFor(containerColor),
672     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
673     interactionSource: MutableInteractionSource? = null,
674 ) =
675     ExtendedFloatingActionButton(
676         text = text,
677         icon = icon,
678         onClick = onClick,
679         textStyle = SmallExtendedFabTextStyle.value,
680         minWidth = SmallExtendedFabMinimumWidth,
681         minHeight = SmallExtendedFabMinimumHeight,
682         startPadding = SmallExtendedFabPaddingStart,
683         endPadding = SmallExtendedFabPaddingEnd,
684         iconPadding = SmallExtendedFabIconPadding,
685         modifier = modifier,
686         expanded = expanded,
687         shape = shape,
688         containerColor = containerColor,
689         contentColor = contentColor,
690         elevation = elevation,
691         interactionSource = interactionSource,
692     )
693 
694 /**
695  * [Material Design medium extended floating action
696  * button](https://m3.material.io/components/extended-fab/overview)
697  *
698  * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
699  * label and larger target area.
700  *
701  * The other medium extended floating action button overload is for FABs without an icon.
702  *
703  * Default content description for accessibility is extended from the extended fabs icon. For custom
704  * behavior, you can provide your own via [Modifier.semantics].
705  *
706  * @sample androidx.compose.material3.samples.MediumExtendedFloatingActionButtonSample
707  * @sample androidx.compose.material3.samples.MediumAnimatedExtendedFloatingActionButtonSample
708  * @param text label displayed inside this FAB
709  * @param icon icon for this FAB, typically an [Icon]
710  * @param onClick called when this FAB is clicked
711  * @param modifier the [Modifier] to be applied to this FAB
712  * @param expanded controls the expansion state of this FAB. In an expanded state, the FAB will show
713  *   both the [icon] and [text]. In a collapsed state, the FAB will show only the [icon].
714  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
715  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
716  *   have no color.
717  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
718  *   matching content color for [containerColor], or to the current [LocalContentColor] if
719  *   [containerColor] is not a color from the theme.
720  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
721  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
722  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
723  *   an overlay. See also: [Surface].
724  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
725  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
726  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
727  *   happen internally.
728  */
729 @ExperimentalMaterial3ExpressiveApi
730 @Composable
731 fun MediumExtendedFloatingActionButton(
732     text: @Composable () -> Unit,
733     icon: @Composable () -> Unit,
734     onClick: () -> Unit,
735     modifier: Modifier = Modifier,
736     expanded: Boolean = true,
737     shape: Shape = FloatingActionButtonDefaults.mediumExtendedFabShape,
738     containerColor: Color = FloatingActionButtonDefaults.containerColor,
739     contentColor: Color = contentColorFor(containerColor),
740     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
741     interactionSource: MutableInteractionSource? = null,
742 ) =
743     ExtendedFloatingActionButton(
744         text = text,
745         icon = icon,
746         onClick = onClick,
747         textStyle = MediumExtendedFabTextStyle.value,
748         minWidth = MediumExtendedFabMinimumWidth,
749         minHeight = MediumExtendedFabMinimumHeight,
750         startPadding = MediumExtendedFabPaddingStart,
751         endPadding = MediumExtendedFabPaddingEnd,
752         iconPadding = MediumExtendedFabIconPadding,
753         modifier = modifier,
754         expanded = expanded,
755         shape = shape,
756         containerColor = containerColor,
757         contentColor = contentColor,
758         elevation = elevation,
759         interactionSource = interactionSource,
760     )
761 
762 /**
763  * [Material Design large extended floating action
764  * button](https://m3.material.io/components/extended-fab/overview)
765  *
766  * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
767  * label and larger target area.
768  *
769  * The other large extended floating action button overload is for FABs without an icon.
770  *
771  * Default content description for accessibility is extended from the extended fabs icon. For custom
772  * behavior, you can provide your own via [Modifier.semantics].
773  *
774  * @sample androidx.compose.material3.samples.LargeExtendedFloatingActionButtonSample
775  * @sample androidx.compose.material3.samples.LargeAnimatedExtendedFloatingActionButtonSample
776  * @param text label displayed inside this FAB
777  * @param icon icon for this FAB, typically an [Icon]
778  * @param onClick called when this FAB is clicked
779  * @param modifier the [Modifier] to be applied to this FAB
780  * @param expanded controls the expansion state of this FAB. In an expanded state, the FAB will show
781  *   both the [icon] and [text]. In a collapsed state, the FAB will show only the [icon].
782  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
783  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
784  *   have no color.
785  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
786  *   matching content color for [containerColor], or to the current [LocalContentColor] if
787  *   [containerColor] is not a color from the theme.
788  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
789  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
790  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
791  *   an overlay. See also: [Surface].
792  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
793  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
794  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
795  *   happen internally.
796  */
797 @ExperimentalMaterial3ExpressiveApi
798 @Composable
799 fun LargeExtendedFloatingActionButton(
800     text: @Composable () -> Unit,
801     icon: @Composable () -> Unit,
802     onClick: () -> Unit,
803     modifier: Modifier = Modifier,
804     expanded: Boolean = true,
805     shape: Shape = FloatingActionButtonDefaults.largeExtendedFabShape,
806     containerColor: Color = FloatingActionButtonDefaults.containerColor,
807     contentColor: Color = contentColorFor(containerColor),
808     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
809     interactionSource: MutableInteractionSource? = null,
810 ) =
811     ExtendedFloatingActionButton(
812         text = text,
813         icon = icon,
814         onClick = onClick,
815         textStyle = LargeExtendedFabTextStyle.value,
816         minWidth = LargeExtendedFabMinimumWidth,
817         minHeight = LargeExtendedFabMinimumHeight,
818         startPadding = LargeExtendedFabPaddingStart,
819         endPadding = LargeExtendedFabPaddingEnd,
820         iconPadding = LargeExtendedFabIconPadding,
821         modifier = modifier,
822         expanded = expanded,
823         shape = shape,
824         containerColor = containerColor,
825         contentColor = contentColor,
826         elevation = elevation,
827         interactionSource = interactionSource,
828     )
829 
830 /**
831  * [Material Design extended floating action
832  * button](https://m3.material.io/components/extended-fab/overview)
833  *
834  * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
835  * label and larger target area.
836  *
837  * ![Extended FAB
838  * image](https://developer.android.com/images/reference/androidx/compose/material3/extended-fab.png)
839  *
840  * The other extended floating action button overload is for FABs without an icon.
841  *
842  * Default content description for accessibility is extended from the extended fabs icon. For custom
843  * behavior, you can provide your own via [Modifier.semantics].
844  *
845  * @sample androidx.compose.material3.samples.ExtendedFloatingActionButtonSample
846  * @sample androidx.compose.material3.samples.AnimatedExtendedFloatingActionButtonSample
847  * @param text label displayed inside this FAB
848  * @param icon icon for this FAB, typically an [Icon]
849  * @param onClick called when this FAB is clicked
850  * @param modifier the [Modifier] to be applied to this FAB
851  * @param expanded controls the expansion state of this FAB. In an expanded state, the FAB will show
852  *   both the [icon] and [text]. In a collapsed state, the FAB will show only the [icon].
853  * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
854  * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
855  *   have no color.
856  * @param contentColor the preferred color for content inside this FAB. Defaults to either the
857  *   matching content color for [containerColor], or to the current [LocalContentColor] if
858  *   [containerColor] is not a color from the theme.
859  * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
860  *   different states. This controls the size of the shadow below the FAB. Additionally, when the
861  *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
862  *   an overlay. See also: [Surface].
863  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
864  *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
865  *   preview the FAB in different states. Note that if `null` is provided, interactions will still
866  *   happen internally.
867  */
868 @Composable
869 fun ExtendedFloatingActionButton(
870     text: @Composable () -> Unit,
871     icon: @Composable () -> Unit,
872     onClick: () -> Unit,
873     modifier: Modifier = Modifier,
874     expanded: Boolean = true,
875     shape: Shape = FloatingActionButtonDefaults.extendedFabShape,
876     containerColor: Color = FloatingActionButtonDefaults.containerColor,
877     contentColor: Color = contentColorFor(containerColor),
878     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
879     interactionSource: MutableInteractionSource? = null,
880 ) {
881     FloatingActionButton(
882         onClick = onClick,
883         modifier = modifier,
884         shape = shape,
885         containerColor = containerColor,
886         contentColor = contentColor,
887         elevation = elevation,
888         interactionSource = interactionSource,
889     ) {
890         val startPadding = if (expanded) ExtendedFabStartIconPadding else 0.dp
891         val endPadding = if (expanded) ExtendedFabTextPadding else 0.dp
892 
893         Row(
894             modifier =
895                 Modifier.sizeIn(
896                         minWidth =
897                             if (expanded) {
898                                 ExtendedFabMinimumWidth
899                             } else {
900                                 FabBaselineTokens.ContainerWidth
901                             }
902                     )
903                     .padding(start = startPadding, end = endPadding),
904             verticalAlignment = Alignment.CenterVertically,
905             horizontalArrangement = if (expanded) Arrangement.Start else Arrangement.Center
906         ) {
907             icon()
908             AnimatedVisibility(
909                 visible = expanded,
910                 enter = extendedFabExpandAnimation(),
911                 exit = extendedFabCollapseAnimation(),
912             ) {
913                 Row(Modifier.clearAndSetSemantics {}) {
914                     Spacer(Modifier.width(ExtendedFabEndIconPadding))
915                     text()
916                 }
917             }
918         }
919     }
920 }
921 
922 @Composable
ExtendedFloatingActionButtonnull923 private fun ExtendedFloatingActionButton(
924     text: @Composable () -> Unit,
925     icon: @Composable () -> Unit,
926     onClick: () -> Unit,
927     textStyle: TextStyle,
928     minWidth: Dp,
929     minHeight: Dp,
930     startPadding: Dp,
931     endPadding: Dp,
932     iconPadding: Dp,
933     modifier: Modifier = Modifier,
934     expanded: Boolean = true,
935     shape: Shape = FloatingActionButtonDefaults.extendedFabShape,
936     containerColor: Color = FloatingActionButtonDefaults.containerColor,
937     contentColor: Color = contentColorFor(containerColor),
938     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
939     interactionSource: MutableInteractionSource? = null,
940 ) {
941     FloatingActionButton(
942         onClick = onClick,
943         textStyle = textStyle,
944         minWidth = Dp.Unspecified,
945         minHeight = Dp.Unspecified,
946         modifier = modifier,
947         shape = shape,
948         containerColor = containerColor,
949         contentColor = contentColor,
950         elevation = elevation,
951         interactionSource = interactionSource,
952     ) {
953         val expandTransition = updateTransition(if (expanded) 1f else 0f, label = "expanded state")
954         // TODO Load the motionScheme tokens from the component tokens file
955         val sizeAnimationSpec = MotionSchemeKeyTokens.FastSpatial.value<Float>()
956         val opacityAnimationSpec = MotionSchemeKeyTokens.FastEffects.value<Float>()
957         val expandedWidthProgress =
958             expandTransition.animateFloat(transitionSpec = { sizeAnimationSpec }) { it }
959         val expandedAlphaProgress =
960             expandTransition.animateFloat(transitionSpec = { opacityAnimationSpec }) { it }
961         Row(
962             modifier =
963                 Modifier.layout { measurable, constraints ->
964                         val expandedWidth = measurable.maxIntrinsicWidth(constraints.maxHeight)
965                         val width =
966                             lerp(minWidth.roundToPx(), expandedWidth, expandedWidthProgress.value)
967                         val placeable = measurable.measure(constraints)
968                         layout(width, placeable.height) { placeable.place(0, 0) }
969                     }
970                     .sizeIn(minWidth = minWidth, minHeight = minHeight)
971                     .padding(start = startPadding, end = endPadding),
972             verticalAlignment = Alignment.CenterVertically,
973         ) {
974             icon()
975             val fullyCollapsed =
976                 remember(expandTransition) {
977                     derivedStateOf {
978                         expandTransition.currentState == 0f && !expandTransition.isRunning
979                     }
980                 }
981             if (!fullyCollapsed.value) {
982                 Row(
983                     Modifier.clearAndSetSemantics {}
984                         .graphicsLayer { alpha = expandedAlphaProgress.value }
985                 ) {
986                     Spacer(Modifier.width(iconPadding))
987                     text()
988                 }
989             }
990         }
991     }
992 }
993 
994 /** Contains the default values used by [FloatingActionButton] */
995 object FloatingActionButtonDefaults {
996     internal val ShowHideTargetScale = 0.2f
997 
998     /** The recommended size of the icon inside a [MediumFloatingActionButton]. */
999     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1000     @get:ExperimentalMaterial3ExpressiveApi
1001     @ExperimentalMaterial3ExpressiveApi
1002     val MediumIconSize = FabMediumTokens.IconSize
1003 
1004     /** The recommended size of the icon inside a [LargeFloatingActionButton]. */
1005     val LargeIconSize = 36.dp // TODO: FabLargeTokens.IconSize is incorrect
1006 
1007     /** Default shape for a floating action button. */
1008     val shape: Shape
1009         @Composable get() = FabBaselineTokens.ContainerShape.value
1010 
1011     /** Default shape for a small floating action button. */
1012     val smallShape: Shape
1013         @Composable get() = FabSmallTokens.ContainerShape.value
1014 
1015     /** Default shape for a medium floating action button. */
1016     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1017     @get:ExperimentalMaterial3ExpressiveApi
1018     @ExperimentalMaterial3ExpressiveApi
1019     val mediumShape: Shape
1020         @Composable get() = ShapeDefaults.LargeIncreased // TODO: update to use token
1021 
1022     /** Default shape for a large floating action button. */
1023     val largeShape: Shape
1024         @Composable get() = FabLargeTokens.ContainerShape.value
1025 
1026     /** Default shape for an extended floating action button. */
1027     val extendedFabShape: Shape
1028         @Composable get() = ExtendedFabPrimaryTokens.ContainerShape.value
1029 
1030     /** Default shape for a small extended floating action button. */
1031     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1032     @get:ExperimentalMaterial3ExpressiveApi
1033     @ExperimentalMaterial3ExpressiveApi
1034     val smallExtendedFabShape: Shape
1035         @Composable get() = ExtendedFabSmallTokens.ContainerShape.value
1036 
1037     /** Default shape for a medium extended floating action button. */
1038     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1039     @get:ExperimentalMaterial3ExpressiveApi
1040     @ExperimentalMaterial3ExpressiveApi
1041     val mediumExtendedFabShape: Shape
1042         @Composable get() = ShapeDefaults.LargeIncreased // TODO: update to use token
1043 
1044     /** Default shape for a large extended floating action button. */
1045     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1046     @get:ExperimentalMaterial3ExpressiveApi
1047     @ExperimentalMaterial3ExpressiveApi
1048     val largeExtendedFabShape: Shape
1049         @Composable get() = ExtendedFabLargeTokens.ContainerShape.value
1050 
1051     /** Default container color for a floating action button. */
1052     val containerColor: Color
1053         @Composable get() = FabPrimaryContainerTokens.ContainerColor.value
1054 
1055     /**
1056      * Creates a [FloatingActionButtonElevation] that represents the elevation of a
1057      * [FloatingActionButton] in different states. For use cases in which a less prominent
1058      * [FloatingActionButton] is possible consider the [loweredElevation].
1059      *
1060      * @param defaultElevation the elevation used when the [FloatingActionButton] has no other
1061      *   [Interaction]s.
1062      * @param pressedElevation the elevation used when the [FloatingActionButton] is pressed.
1063      * @param focusedElevation the elevation used when the [FloatingActionButton] is focused.
1064      * @param hoveredElevation the elevation used when the [FloatingActionButton] is hovered.
1065      */
1066     @Composable
elevationnull1067     fun elevation(
1068         defaultElevation: Dp = FabPrimaryContainerTokens.ContainerElevation,
1069         pressedElevation: Dp = FabPrimaryContainerTokens.PressedContainerElevation,
1070         focusedElevation: Dp = FabPrimaryContainerTokens.FocusedContainerElevation,
1071         hoveredElevation: Dp = FabPrimaryContainerTokens.HoveredContainerElevation,
1072     ): FloatingActionButtonElevation =
1073         FloatingActionButtonElevation(
1074             defaultElevation = defaultElevation,
1075             pressedElevation = pressedElevation,
1076             focusedElevation = focusedElevation,
1077             hoveredElevation = hoveredElevation,
1078         )
1079 
1080     /**
1081      * Use this to create a [FloatingActionButton] with a lowered elevation for less emphasis. Use
1082      * [elevation] to get a normal [FloatingActionButton].
1083      *
1084      * @param defaultElevation the elevation used when the [FloatingActionButton] has no other
1085      *   [Interaction]s.
1086      * @param pressedElevation the elevation used when the [FloatingActionButton] is pressed.
1087      * @param focusedElevation the elevation used when the [FloatingActionButton] is focused.
1088      * @param hoveredElevation the elevation used when the [FloatingActionButton] is hovered.
1089      */
1090     @Composable
1091     fun loweredElevation(
1092         defaultElevation: Dp = ElevationTokens.Level1,
1093         pressedElevation: Dp = ElevationTokens.Level1,
1094         focusedElevation: Dp = ElevationTokens.Level1,
1095         hoveredElevation: Dp = ElevationTokens.Level2,
1096     ): FloatingActionButtonElevation =
1097         FloatingActionButtonElevation(
1098             defaultElevation = defaultElevation,
1099             pressedElevation = pressedElevation,
1100             focusedElevation = focusedElevation,
1101             hoveredElevation = hoveredElevation,
1102         )
1103 
1104     /**
1105      * Use this to create a [FloatingActionButton] that represents the default elevation of a
1106      * [FloatingActionButton] used for [BottomAppBar] in different states.
1107      *
1108      * @param defaultElevation the elevation used when the [FloatingActionButton] has no other
1109      *   [Interaction]s.
1110      * @param pressedElevation the elevation used when the [FloatingActionButton] is pressed.
1111      * @param focusedElevation the elevation used when the [FloatingActionButton] is focused.
1112      * @param hoveredElevation the elevation used when the [FloatingActionButton] is hovered.
1113      */
1114     fun bottomAppBarFabElevation(
1115         defaultElevation: Dp = 0.dp,
1116         pressedElevation: Dp = 0.dp,
1117         focusedElevation: Dp = 0.dp,
1118         hoveredElevation: Dp = 0.dp
1119     ): FloatingActionButtonElevation =
1120         FloatingActionButtonElevation(
1121             defaultElevation,
1122             pressedElevation,
1123             focusedElevation,
1124             hoveredElevation
1125         )
1126 }
1127 
1128 /**
1129  * Apply this modifier to a [FloatingActionButton] to show or hide it with an animation, typically
1130  * based on the app's main content scrolling.
1131  *
1132  * @param visible whether the FAB should be shown or hidden with an animation
1133  * @param alignment the direction towards which the FAB should be scaled to and from
1134  * @param targetScale the initial scale value when showing the FAB and the final scale value when
1135  *   hiding the FAB
1136  * @param scaleAnimationSpec the [AnimationSpec] to use for the scale part of the animation, if null
1137  *   the Fast Spatial spring spec from the [MotionScheme] will be used
1138  * @param alphaAnimationSpec the [AnimationSpec] to use for the alpha part of the animation, if null
1139  *   the Fast Effects spring spec from the [MotionScheme] will be used
1140  * @sample androidx.compose.material3.samples.AnimatedFloatingActionButtonSample
1141  */
1142 @ExperimentalMaterial3ExpressiveApi
1143 fun Modifier.animateFloatingActionButton(
1144     visible: Boolean,
1145     alignment: Alignment,
1146     targetScale: Float = FloatingActionButtonDefaults.ShowHideTargetScale,
1147     scaleAnimationSpec: AnimationSpec<Float>? = null,
1148     alphaAnimationSpec: AnimationSpec<Float>? = null
1149 ): Modifier {
1150     return this.then(
1151         FabVisibleModifier(
1152             visible = visible,
1153             alignment = alignment,
1154             targetScale = targetScale,
1155             scaleAnimationSpec = scaleAnimationSpec,
1156             alphaAnimationSpec = alphaAnimationSpec
1157         )
1158     )
1159 }
1160 
1161 internal data class FabVisibleModifier(
1162     private val visible: Boolean,
1163     private val alignment: Alignment,
1164     private val targetScale: Float,
1165     private val scaleAnimationSpec: AnimationSpec<Float>? = null,
1166     private val alphaAnimationSpec: AnimationSpec<Float>? = null
1167 ) : ModifierNodeElement<FabVisibleNode>() {
1168 
createnull1169     override fun create(): FabVisibleNode =
1170         FabVisibleNode(
1171             visible = visible,
1172             alignment = alignment,
1173             targetScale = targetScale,
1174             scaleAnimationSpec = scaleAnimationSpec,
1175             alphaAnimationSpec = alphaAnimationSpec,
1176         )
1177 
1178     override fun update(node: FabVisibleNode) {
1179         node.updateNode(
1180             visible = visible,
1181             alignment = alignment,
1182             targetScale = targetScale,
1183             scaleAnimationSpec = scaleAnimationSpec,
1184             alphaAnimationSpec = alphaAnimationSpec,
1185         )
1186     }
1187 
inspectablePropertiesnull1188     override fun InspectorInfo.inspectableProperties() {
1189         // Show nothing in the inspector.
1190     }
1191 }
1192 
1193 internal class FabVisibleNode(
1194     visible: Boolean,
1195     private var alignment: Alignment,
1196     private var targetScale: Float,
1197     private var scaleAnimationSpec: AnimationSpec<Float>? = null,
1198     private var alphaAnimationSpec: AnimationSpec<Float>? = null,
1199 ) : DelegatingNode(), CompositionLocalConsumerModifierNode {
1200 
1201     private val scaleAnimatable = Animatable(if (visible) 1f else 0f)
1202     private val alphaAnimatable = Animatable(if (visible) 1f else 0f)
1203 
1204     init {
1205         delegate(
<lambda>null1206             CacheDrawModifierNode {
1207                 val layer = obtainGraphicsLayer()
1208                 // Use a larger layer size to make sure the elevation shadow doesn't get clipped
1209                 // and offset via layer.topLeft and DrawScope.inset to preserve the visual
1210                 // position of the FAB.
1211                 val layerInsetSize = 16.dp.toPx()
1212                 val layerSize =
1213                     Size(size.width + layerInsetSize * 2f, size.height + layerInsetSize * 2f)
1214                         .toIntSize()
1215                 val nodeSize = size.toIntSize()
1216 
1217                 layer.apply {
1218                     topLeft = IntOffset(-layerInsetSize.roundToInt(), -layerInsetSize.roundToInt())
1219 
1220                     alpha = alphaAnimatable.value
1221 
1222                     // Scale towards the direction of the provided alignment
1223                     val alignOffset = alignment.align(IntSize(1, 1), nodeSize, layoutDirection)
1224                     pivotOffset = alignOffset.toOffset() + Offset(layerInsetSize, layerInsetSize)
1225                     scaleX = lerp(targetScale, 1f, scaleAnimatable.value)
1226                     scaleY = lerp(targetScale, 1f, scaleAnimatable.value)
1227 
1228                     record(size = layerSize) {
1229                         inset(layerInsetSize, layerInsetSize) { this@record.drawContent() }
1230                     }
1231                 }
1232 
1233                 onDrawWithContent { drawLayer(layer) }
1234             }
1235         )
1236     }
1237 
1238     @OptIn(ExperimentalMaterial3ExpressiveApi::class)
updateNodenull1239     fun updateNode(
1240         visible: Boolean,
1241         alignment: Alignment,
1242         targetScale: Float,
1243         scaleAnimationSpec: AnimationSpec<Float>?,
1244         alphaAnimationSpec: AnimationSpec<Float>?
1245     ) {
1246         this.alignment = alignment
1247         this.targetScale = targetScale
1248         this.scaleAnimationSpec = scaleAnimationSpec
1249         this.alphaAnimationSpec = alphaAnimationSpec
1250 
1251         coroutineScope.launch {
1252             // TODO Load the motionScheme tokens from the component tokens file
1253             scaleAnimatable.animateTo(
1254                 targetValue = if (visible) 1f else 0f,
1255                 animationSpec =
1256                     scaleAnimationSpec ?: currentValueOf(LocalMotionScheme).fastSpatialSpec()
1257             )
1258         }
1259 
1260         coroutineScope.launch {
1261             // TODO Load the motionScheme tokens from the component tokens file
1262             alphaAnimatable.animateTo(
1263                 targetValue = if (visible) 1f else 0f,
1264                 animationSpec =
1265                     alphaAnimationSpec ?: currentValueOf(LocalMotionScheme).fastEffectsSpec()
1266             )
1267         }
1268     }
1269 }
1270 
1271 /**
1272  * Represents the tonal and shadow elevation for a floating action button in different states.
1273  *
1274  * See [FloatingActionButtonDefaults.elevation] for the default elevation used in a
1275  * [FloatingActionButton] and [ExtendedFloatingActionButton].
1276  */
1277 @Stable
1278 open class FloatingActionButtonElevation
1279 internal constructor(
1280     private val defaultElevation: Dp,
1281     private val pressedElevation: Dp,
1282     private val focusedElevation: Dp,
1283     private val hoveredElevation: Dp,
1284 ) {
1285     @Composable
shadowElevationnull1286     internal fun shadowElevation(interactionSource: InteractionSource): State<Dp> {
1287         return animateElevation(interactionSource = interactionSource)
1288     }
1289 
tonalElevationnull1290     internal fun tonalElevation(): Dp {
1291         return defaultElevation
1292     }
1293 
1294     @Composable
animateElevationnull1295     private fun animateElevation(interactionSource: InteractionSource): State<Dp> {
1296         val animatable =
1297             remember(interactionSource) {
1298                 FloatingActionButtonElevationAnimatable(
1299                     defaultElevation = defaultElevation,
1300                     pressedElevation = pressedElevation,
1301                     hoveredElevation = hoveredElevation,
1302                     focusedElevation = focusedElevation
1303                 )
1304             }
1305 
1306         LaunchedEffect(this) {
1307             animatable.updateElevation(
1308                 defaultElevation = defaultElevation,
1309                 pressedElevation = pressedElevation,
1310                 hoveredElevation = hoveredElevation,
1311                 focusedElevation = focusedElevation
1312             )
1313         }
1314 
1315         LaunchedEffect(interactionSource) {
1316             val interactions = mutableListOf<Interaction>()
1317             interactionSource.interactions.collect { interaction ->
1318                 when (interaction) {
1319                     is HoverInteraction.Enter -> {
1320                         interactions.add(interaction)
1321                     }
1322                     is HoverInteraction.Exit -> {
1323                         interactions.remove(interaction.enter)
1324                     }
1325                     is FocusInteraction.Focus -> {
1326                         interactions.add(interaction)
1327                     }
1328                     is FocusInteraction.Unfocus -> {
1329                         interactions.remove(interaction.focus)
1330                     }
1331                     is PressInteraction.Press -> {
1332                         interactions.add(interaction)
1333                     }
1334                     is PressInteraction.Release -> {
1335                         interactions.remove(interaction.press)
1336                     }
1337                     is PressInteraction.Cancel -> {
1338                         interactions.remove(interaction.press)
1339                     }
1340                 }
1341                 val targetInteraction = interactions.lastOrNull()
1342                 launch { animatable.animateElevation(to = targetInteraction) }
1343             }
1344         }
1345 
1346         return animatable.asState()
1347     }
1348 
equalsnull1349     override fun equals(other: Any?): Boolean {
1350         if (this === other) return true
1351         if (other == null || other !is FloatingActionButtonElevation) return false
1352 
1353         if (defaultElevation != other.defaultElevation) return false
1354         if (pressedElevation != other.pressedElevation) return false
1355         if (focusedElevation != other.focusedElevation) return false
1356         return hoveredElevation == other.hoveredElevation
1357     }
1358 
hashCodenull1359     override fun hashCode(): Int {
1360         var result = defaultElevation.hashCode()
1361         result = 31 * result + pressedElevation.hashCode()
1362         result = 31 * result + focusedElevation.hashCode()
1363         result = 31 * result + hoveredElevation.hashCode()
1364         return result
1365     }
1366 }
1367 
1368 private class FloatingActionButtonElevationAnimatable(
1369     private var defaultElevation: Dp,
1370     private var pressedElevation: Dp,
1371     private var hoveredElevation: Dp,
1372     private var focusedElevation: Dp
1373 ) {
1374     private val animatable = Animatable(defaultElevation, Dp.VectorConverter)
1375 
1376     private var lastTargetInteraction: Interaction? = null
1377     private var targetInteraction: Interaction? = null
1378 
Interactionnull1379     private fun Interaction?.calculateTarget(): Dp {
1380         return when (this) {
1381             is PressInteraction.Press -> pressedElevation
1382             is HoverInteraction.Enter -> hoveredElevation
1383             is FocusInteraction.Focus -> focusedElevation
1384             else -> defaultElevation
1385         }
1386     }
1387 
updateElevationnull1388     suspend fun updateElevation(
1389         defaultElevation: Dp,
1390         pressedElevation: Dp,
1391         hoveredElevation: Dp,
1392         focusedElevation: Dp
1393     ) {
1394         this.defaultElevation = defaultElevation
1395         this.pressedElevation = pressedElevation
1396         this.hoveredElevation = hoveredElevation
1397         this.focusedElevation = focusedElevation
1398         snapElevation()
1399     }
1400 
snapElevationnull1401     private suspend fun snapElevation() {
1402         val target = targetInteraction.calculateTarget()
1403         if (animatable.targetValue != target) {
1404             try {
1405                 animatable.snapTo(target)
1406             } finally {
1407                 lastTargetInteraction = targetInteraction
1408             }
1409         }
1410     }
1411 
animateElevationnull1412     suspend fun animateElevation(to: Interaction?) {
1413         val target = to.calculateTarget()
1414         // Update the interaction even if the values are the same, for when we change to another
1415         // interaction later
1416         targetInteraction = to
1417         try {
1418             if (animatable.targetValue != target) {
1419                 animatable.animateElevation(target = target, from = lastTargetInteraction, to = to)
1420             }
1421         } finally {
1422             lastTargetInteraction = to
1423         }
1424     }
1425 
asStatenull1426     fun asState(): State<Dp> = animatable.asState()
1427 }
1428 
1429 private val SmallExtendedFabMinimumWidth = ExtendedFabSmallTokens.ContainerHeight
1430 private val SmallExtendedFabMinimumHeight = ExtendedFabSmallTokens.ContainerHeight
1431 private val SmallExtendedFabPaddingStart = ExtendedFabSmallTokens.LeadingSpace
1432 private val SmallExtendedFabPaddingEnd = ExtendedFabSmallTokens.TrailingSpace
1433 private val SmallExtendedFabIconPadding = ExtendedFabSmallTokens.IconLabelSpace
1434 private val SmallExtendedFabTextStyle = TypographyKeyTokens.TitleMedium
1435 
1436 private val MediumExtendedFabMinimumWidth = ExtendedFabMediumTokens.ContainerHeight
1437 private val MediumExtendedFabMinimumHeight = ExtendedFabMediumTokens.ContainerHeight
1438 private val MediumExtendedFabPaddingStart = ExtendedFabMediumTokens.LeadingSpace
1439 private val MediumExtendedFabPaddingEnd = ExtendedFabMediumTokens.TrailingSpace
1440 // TODO: ExtendedFabMediumTokens.IconLabelSpace is incorrect
1441 private val MediumExtendedFabIconPadding = 12.dp
1442 private val MediumExtendedFabTextStyle = TypographyKeyTokens.TitleLarge
1443 
1444 private val LargeExtendedFabMinimumWidth = ExtendedFabLargeTokens.ContainerHeight
1445 private val LargeExtendedFabMinimumHeight = ExtendedFabLargeTokens.ContainerHeight
1446 private val LargeExtendedFabPaddingStart = ExtendedFabLargeTokens.LeadingSpace
1447 private val LargeExtendedFabPaddingEnd = ExtendedFabLargeTokens.TrailingSpace
1448 // TODO: ExtendedFabLargeTokens.IconLabelSpace is incorrect
1449 private val LargeExtendedFabIconPadding = 16.dp
1450 private val LargeExtendedFabTextStyle = TypographyKeyTokens.HeadlineSmall
1451 
1452 private val ExtendedFabStartIconPadding = 16.dp
1453 
1454 private val ExtendedFabEndIconPadding = 12.dp
1455 
1456 private val ExtendedFabTextPadding = 20.dp
1457 
1458 private val ExtendedFabMinimumWidth = 80.dp
1459 
1460 @Composable
1461 private fun extendedFabCollapseAnimation() =
1462     fadeOut(
1463         // TODO Load the motionScheme tokens from the component tokens file
1464         animationSpec = MotionSchemeKeyTokens.FastEffects.value()
1465     ) +
1466         shrinkHorizontally(
1467             animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value(),
1468             shrinkTowards = Alignment.Start,
1469         )
1470 
1471 @Composable
1472 private fun extendedFabExpandAnimation() =
1473     fadeIn(
1474         // TODO Load the motionScheme tokens from the component tokens file
1475         animationSpec = MotionSchemeKeyTokens.DefaultEffects.value(),
1476     ) +
1477         expandHorizontally(
1478             animationSpec = MotionSchemeKeyTokens.FastSpatial.value(),
1479             expandFrom = Alignment.Start,
1480         )
1481