1 /*
2 * Copyright 2022 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.material
18
19 import androidx.compose.foundation.BorderStroke
20 import androidx.compose.foundation.background
21 import androidx.compose.foundation.interaction.Interaction
22 import androidx.compose.foundation.interaction.MutableInteractionSource
23 import androidx.compose.foundation.layout.Arrangement
24 import androidx.compose.foundation.layout.Box
25 import androidx.compose.foundation.layout.IntrinsicSize
26 import androidx.compose.foundation.layout.Row
27 import androidx.compose.foundation.layout.RowScope
28 import androidx.compose.foundation.layout.Spacer
29 import androidx.compose.foundation.layout.defaultMinSize
30 import androidx.compose.foundation.layout.padding
31 import androidx.compose.foundation.layout.requiredSize
32 import androidx.compose.foundation.layout.width
33 import androidx.compose.foundation.shape.CircleShape
34 import androidx.compose.foundation.shape.CornerSize
35 import androidx.compose.runtime.Composable
36 import androidx.compose.runtime.CompositionLocalProvider
37 import androidx.compose.runtime.Immutable
38 import androidx.compose.runtime.Stable
39 import androidx.compose.runtime.State
40 import androidx.compose.runtime.getValue
41 import androidx.compose.runtime.rememberUpdatedState
42 import androidx.compose.ui.Alignment
43 import androidx.compose.ui.Modifier
44 import androidx.compose.ui.draw.clip
45 import androidx.compose.ui.graphics.Color
46 import androidx.compose.ui.graphics.Shape
47 import androidx.compose.ui.graphics.compositeOver
48 import androidx.compose.ui.semantics.Role
49 import androidx.compose.ui.semantics.role
50 import androidx.compose.ui.semantics.semantics
51 import androidx.compose.ui.unit.dp
52
53 /**
54 * [Material Design action chip](https://material.io/components/chips#action-chips).
55 *
56 * Action chips offer actions related to primary content. They should appear dynamically and
57 * contextually in a UI.
58 *
59 * @sample androidx.compose.material.samples.ChipSample
60 *
61 * You can create an [outlined action chip](https://material.io/components/chips#action-chips) using
62 * [ChipDefaults.outlinedChipColors] and [ChipDefaults.outlinedBorder]
63 *
64 * @sample androidx.compose.material.samples.OutlinedChipWithIconSample
65 *
66 * Action chips should appear in a set and can be horizontally scrollable
67 *
68 * @sample androidx.compose.material.samples.ChipGroupSingleLineSample
69 *
70 * Alternatively, use [androidx.compose.foundation.layout.FlowRow] to wrap chips to a new line.
71 *
72 * @sample androidx.compose.material.samples.ChipGroupReflowSample
73 * @param onClick called when the chip is clicked.
74 * @param modifier Modifier to be applied to the chip
75 * @param enabled When disabled, chip will not respond to user input. It will also appear visually
76 * disabled and disabled to accessibility services.
77 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
78 * emitting [Interaction]s for this chip. You can use this to change the chip's appearance or
79 * preview the chip in different states. Note that if `null` is provided, interactions will still
80 * happen internally.
81 * @param shape defines the chip's shape as well as its shadow
82 * @param border Border to draw around the chip. Pass `null` here for no border.
83 * @param colors [ChipColors] that will be used to resolve the background and content color for this
84 * chip in different states. See [ChipDefaults.chipColors].
85 * @param leadingIcon Optional icon at the start of the chip, preceding the content text.
86 * @param content the content of this chip
87 */
88 @ExperimentalMaterialApi
89 @Composable
Chipnull90 fun Chip(
91 onClick: () -> Unit,
92 modifier: Modifier = Modifier,
93 enabled: Boolean = true,
94 interactionSource: MutableInteractionSource? = null,
95 shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
96 border: BorderStroke? = null,
97 colors: ChipColors = ChipDefaults.chipColors(),
98 leadingIcon: @Composable (() -> Unit)? = null,
99 content: @Composable RowScope.() -> Unit
100 ) {
101 val contentColor by colors.contentColor(enabled)
102 Surface(
103 onClick = onClick,
104 modifier = modifier.semantics { role = Role.Button },
105 enabled = enabled,
106 shape = shape,
107 color = colors.backgroundColor(enabled).value,
108 contentColor = contentColor.copy(1.0f),
109 border = border,
110 interactionSource = interactionSource,
111 ) {
112 CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
113 ProvideTextStyle(value = MaterialTheme.typography.body2) {
114 Row(
115 Modifier.defaultMinSize(minHeight = ChipDefaults.MinHeight)
116 .padding(
117 start =
118 if (leadingIcon == null) {
119 HorizontalPadding
120 } else 0.dp,
121 end = HorizontalPadding,
122 ),
123 horizontalArrangement = Arrangement.Start,
124 verticalAlignment = Alignment.CenterVertically
125 ) {
126 if (leadingIcon != null) {
127 Spacer(Modifier.width(LeadingIconStartSpacing))
128 val leadingIconContentColor by colors.leadingIconContentColor(enabled)
129 CompositionLocalProvider(
130 LocalContentColor provides leadingIconContentColor,
131 LocalContentAlpha provides leadingIconContentColor.alpha,
132 content = leadingIcon
133 )
134 Spacer(Modifier.width(LeadingIconEndSpacing))
135 }
136 content()
137 }
138 }
139 }
140 }
141 }
142
143 /**
144 * [Material Design filter chip](https://material.io/components/chips#filter-chips)
145 *
146 * Filter chips use tags or descriptive words to filter a collection. They are a good alternative to
147 * toggle buttons or checkboxes.
148 *
149 * @sample androidx.compose.material.samples.FilterChipSample
150 *
151 * A filter chip with leading icon and selected icon looks like:
152 *
153 * @sample androidx.compose.material.samples.FilterChipWithLeadingIconSample
154 *
155 * You can create an [outlined filter chip](https://material.io/components/chips#action-chips) using
156 * [ChipDefaults.outlinedFilterChipColors] and [ChipDefaults.outlinedBorder]
157 *
158 * @sample androidx.compose.material.samples.OutlinedFilterChipSample
159 * @param selected boolean state for this chip: either it is selected or not
160 * @param onClick will be called when the user clicks the chip
161 * @param modifier Modifier to be applied to the chip
162 * @param enabled controls the enabled state of the chip. When `false`, this chip will not be
163 * clickable
164 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
165 * emitting [Interaction]s for this chip. You can use this to change the chip's appearance or
166 * preview the chip in different states. Note that if `null` is provided, interactions will still
167 * happen internally.
168 * @param shape defines the chip's shape as well as its shadow
169 * @param border border to draw around the chip
170 * @param colors [SelectableChipColors] that will be used to resolve the background and content
171 * color for this chip in different states. See [ChipDefaults.filterChipColors].
172 * @param leadingIcon Optional icon at the start of the chip, preceding the content text.
173 * @param selectedIcon Icon used to indicate a chip's selected state, it is commonly a
174 * [androidx.compose.material.icons.Icons.Filled.Done]. By default, if a leading icon is also
175 * provided, the leading icon will be obscured by a circle overlay and then the selected icon.
176 * @param trailingIcon Optional icon at the end of the chip, following the content text. Filter
177 * chips commonly do not display any trailing icon.
178 * @param content the content of this chip
179 */
180 @ExperimentalMaterialApi
181 @Composable
FilterChipnull182 fun FilterChip(
183 selected: Boolean,
184 onClick: () -> Unit,
185 modifier: Modifier = Modifier,
186 enabled: Boolean = true,
187 interactionSource: MutableInteractionSource? = null,
188 shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
189 border: BorderStroke? = null,
190 colors: SelectableChipColors = ChipDefaults.filterChipColors(),
191 leadingIcon: @Composable (() -> Unit)? = null,
192 selectedIcon: @Composable (() -> Unit)? = null,
193 trailingIcon: @Composable (() -> Unit)? = null,
194 content: @Composable RowScope.() -> Unit
195 ) {
196 // TODO(b/113855296): Animate transition between unselected and selected
197 val contentColor = colors.contentColor(enabled, selected)
198 Surface(
199 selected = selected,
200 onClick = onClick,
201 modifier = modifier.semantics { role = Role.Checkbox },
202 enabled = enabled,
203 shape = shape,
204 color = colors.backgroundColor(enabled, selected).value,
205 contentColor = contentColor.value.copy(1.0f),
206 interactionSource = interactionSource,
207 border = border,
208 ) {
209 CompositionLocalProvider(LocalContentAlpha provides contentColor.value.alpha) {
210 ProvideTextStyle(value = MaterialTheme.typography.body2) {
211 Row(
212 Modifier.width(IntrinsicSize.Max)
213 .defaultMinSize(minHeight = ChipDefaults.MinHeight)
214 .padding(
215 start =
216 if (leadingIcon != null || (selected && selectedIcon != null)) {
217 0.dp
218 } else {
219 HorizontalPadding
220 },
221 end =
222 if (trailingIcon == null) {
223 HorizontalPadding
224 } else {
225 0.dp
226 }
227 ),
228 horizontalArrangement = Arrangement.Start,
229 verticalAlignment = Alignment.CenterVertically
230 ) {
231 if (leadingIcon != null || (selected && selectedIcon != null)) {
232 Spacer(Modifier.width(LeadingIconStartSpacing))
233 Box {
234 if (leadingIcon != null) {
235 val leadingIconColor = colors.leadingIconColor(enabled, selected)
236 CompositionLocalProvider(
237 LocalContentColor provides leadingIconColor.value,
238 LocalContentAlpha provides leadingIconColor.value.alpha,
239 content = leadingIcon
240 )
241 }
242 if (selected && selectedIcon != null) {
243 var overlayModifier: Modifier = Modifier
244 var iconColor = contentColor.value
245 if (leadingIcon != null) {
246 overlayModifier =
247 Modifier.requiredSize(SelectedIconContainerSize)
248 .background(
249 color = contentColor.value,
250 shape = CircleShape
251 )
252 .clip(CircleShape)
253
254 iconColor = colors.backgroundColor(enabled, selected).value
255 }
256 Box(
257 modifier = overlayModifier,
258 contentAlignment = Alignment.Center
259 ) {
260 CompositionLocalProvider(
261 LocalContentColor provides iconColor,
262 content = selectedIcon
263 )
264 }
265 }
266 }
267 Spacer(Modifier.width(LeadingIconEndSpacing))
268 }
269 Row(
270 modifier = Modifier.weight(1f),
271 horizontalArrangement = Arrangement.Start,
272 verticalAlignment = Alignment.CenterVertically,
273 content = content,
274 )
275 if (trailingIcon != null) {
276 Spacer(Modifier.width(TrailingIconSpacing))
277 trailingIcon()
278 Spacer(Modifier.width(TrailingIconSpacing))
279 }
280 }
281 }
282 }
283 }
284 }
285
286 /**
287 * Represents the background and content colors used in a clickable chip in different states.
288 *
289 * See [ChipDefaults.chipColors] for the default colors used in a filled [Chip]. See
290 * [ChipDefaults.outlinedChipColors] for the default colors used in a outlined [Chip],
291 */
292 @Stable
293 @ExperimentalMaterialApi
294 interface ChipColors {
295 /**
296 * Represents the background color for this chip, depending on [enabled].
297 *
298 * @param enabled whether the chip is enabled
299 */
backgroundColornull300 @Composable fun backgroundColor(enabled: Boolean): State<Color>
301
302 /**
303 * Represents the content color for this chip, depending on [enabled], see
304 * [leadingIconContentColor].
305 *
306 * @param enabled whether the chip is enabled
307 */
308 @Composable fun contentColor(enabled: Boolean): State<Color>
309
310 /**
311 * Represents the leading icon's content color for this chip, depending on [enabled].
312 *
313 * @param enabled whether the chip is enabled
314 */
315 @Composable fun leadingIconContentColor(enabled: Boolean): State<Color>
316 }
317
318 // TODO(b/182821022): Add links choice and input chip colors.
319 /**
320 * Represents the background and content colors used in a selectable chip in different states.
321 * [FilterChip], choice chip and input chip are types of selectable chips. See
322 * [ChipDefaults.filterChipColors] for the default colors used in a filled [FilterChip]. See
323 * [ChipDefaults.outlinedFilterChipColors] for the default colors used in a outlined [FilterChip].
324 */
325 @ExperimentalMaterialApi
326 interface SelectableChipColors {
327 /**
328 * Represents the background color for this chip, depending on [enabled] and [selected].
329 *
330 * @param enabled whether the chip is enabled
331 * @param selected whether the chip is selected
332 */
333 @Composable fun backgroundColor(enabled: Boolean, selected: Boolean): State<Color>
334
335 /**
336 * Represents the content color for this chip, depending on [enabled] and [selected].
337 *
338 * @param enabled whether the chip is enabled
339 * @param selected whether the chip is selected
340 */
341 @Composable fun contentColor(enabled: Boolean, selected: Boolean): State<Color>
342
343 /**
344 * Represents the leading icon color for this chip, depending on [enabled] and [selected].
345 *
346 * @param enabled whether the chip is enabled
347 * @param selected whether the chip is selected
348 */
349 @Composable fun leadingIconColor(enabled: Boolean, selected: Boolean): State<Color>
350 }
351
352 /** Contains the baseline values used by chips. */
353 @ExperimentalMaterialApi
354 object ChipDefaults {
355 /**
356 * The min height applied for a chip. Note that you can override it by applying Modifier.height
357 * directly on a chip.
358 */
359 val MinHeight = 32.dp
360
361 /**
362 * Creates a [ChipColors] that represents the default background and content colors used in a
363 * filled [Chip].
364 *
365 * @param backgroundColor the background color of this chip when enabled
366 * @param contentColor the content color of this chip when enabled, there is a separate param
367 * for icon colors
368 * @param leadingIconContentColor the color of this chip's start icon when enabled
369 * @param disabledBackgroundColor the background color of this chip when not enabled
370 * @param disabledContentColor the content color of this chip when not enabled
371 * @param disabledLeadingIconContentColor the color of this chip's start icon when not enabled
372 */
373 @Composable
chipColorsnull374 fun chipColors(
375 backgroundColor: Color =
376 MaterialTheme.colors.onSurface
377 .copy(alpha = SurfaceOverlayOpacity)
378 .compositeOver(MaterialTheme.colors.surface),
379 contentColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentOpacity),
380 leadingIconContentColor: Color = contentColor.copy(alpha = LeadingIconOpacity),
381 disabledBackgroundColor: Color =
382 MaterialTheme.colors.onSurface
383 .copy(alpha = ContentAlpha.disabled * SurfaceOverlayOpacity)
384 .compositeOver(MaterialTheme.colors.surface),
385 disabledContentColor: Color =
386 contentColor.copy(alpha = ContentAlpha.disabled * ContentOpacity),
387 disabledLeadingIconContentColor: Color =
388 leadingIconContentColor.copy(alpha = ContentAlpha.disabled * LeadingIconOpacity),
389 ): ChipColors =
390 DefaultChipColors(
391 backgroundColor = backgroundColor,
392 contentColor = contentColor,
393 leadingIconContentColor = leadingIconContentColor,
394 disabledBackgroundColor = disabledBackgroundColor,
395 disabledContentColor = disabledContentColor,
396 disabledLeadingIconContentColor = disabledLeadingIconContentColor,
397 )
398
399 /**
400 * Creates a [ChipColors] that represents the default background and content colors used in an
401 * outlined [Chip].
402 *
403 * @param backgroundColor the background color of this chip when enabled
404 * @param contentColor the content color of this chip when enabled, there is a separate param
405 * for icon colors
406 * @param leadingIconContentColor the leading icon content color of this chip when enabled
407 * @param disabledBackgroundColor the background color of this chip when not enabled
408 * @param disabledContentColor the content color of this chip when not enabled
409 * @param disabledLeadingIconContentColor the color of this chip's start icon when not enabled
410 * @para leadingIconContentColor the color of this chip's start icon when enabled
411 */
412 @Composable
413 fun outlinedChipColors(
414 backgroundColor: Color = MaterialTheme.colors.surface,
415 contentColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentOpacity),
416 leadingIconContentColor: Color = contentColor.copy(alpha = LeadingIconOpacity),
417 disabledBackgroundColor: Color = backgroundColor,
418 disabledContentColor: Color =
419 contentColor.copy(alpha = ContentAlpha.disabled * ContentOpacity),
420 disabledLeadingIconContentColor: Color =
421 leadingIconContentColor.copy(alpha = ContentAlpha.disabled * LeadingIconOpacity),
422 ): ChipColors =
423 chipColors(
424 backgroundColor = backgroundColor,
425 contentColor = contentColor,
426 leadingIconContentColor = leadingIconContentColor,
427 disabledBackgroundColor = disabledBackgroundColor,
428 disabledContentColor = disabledContentColor,
429 disabledLeadingIconContentColor = disabledLeadingIconContentColor
430 )
431
432 /**
433 * Creates a [SelectableChipColors] that represents the default background and content colors
434 * used in a filled [FilterChip].
435 *
436 * @param backgroundColor the background color of this chip when enabled
437 * @param contentColor the content color of this chip when enabled
438 * @param leadingIconColor the color of this chip's start icon when enabled
439 * @param disabledBackgroundColor the background color of this chip when not enabled
440 * @param disabledContentColor the content color of this chip when not enabled
441 * @param disabledLeadingIconColor the color of this chip's start icon when not enabled
442 * @param selectedBackgroundColor the background color of this chip when selected
443 * @param selectedContentColor the content color of this chip when selected
444 * @param selectedLeadingIconColor the color of this chip's start icon when selected
445 */
446 @Composable
447 fun filterChipColors(
448 backgroundColor: Color =
449 MaterialTheme.colors.onSurface
450 .copy(alpha = SurfaceOverlayOpacity)
451 .compositeOver(MaterialTheme.colors.surface),
452 contentColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentOpacity),
453 leadingIconColor: Color = contentColor.copy(LeadingIconOpacity),
454 disabledBackgroundColor: Color =
455 MaterialTheme.colors.onSurface
456 .copy(alpha = ContentAlpha.disabled * SurfaceOverlayOpacity)
457 .compositeOver(MaterialTheme.colors.surface),
458 disabledContentColor: Color =
459 contentColor.copy(alpha = ContentAlpha.disabled * ContentOpacity),
460 disabledLeadingIconColor: Color =
461 leadingIconColor.copy(alpha = ContentAlpha.disabled * LeadingIconOpacity),
462 selectedBackgroundColor: Color =
463 MaterialTheme.colors.onSurface
464 .copy(alpha = SurfaceOverlayOpacity)
465 .compositeOver(backgroundColor),
466 selectedContentColor: Color =
467 MaterialTheme.colors.onSurface
468 .copy(alpha = SelectedOverlayOpacity)
469 .compositeOver(contentColor),
470 selectedLeadingIconColor: Color =
471 MaterialTheme.colors.onSurface
472 .copy(alpha = SelectedOverlayOpacity)
473 .compositeOver(leadingIconColor)
474 ): SelectableChipColors =
475 DefaultSelectableChipColors(
476 backgroundColor = backgroundColor,
477 contentColor = contentColor,
478 leadingIconColor = leadingIconColor,
479 disabledBackgroundColor = disabledBackgroundColor,
480 disabledContentColor = disabledContentColor,
481 disabledLeadingIconColor = disabledLeadingIconColor,
482 selectedBackgroundColor = selectedBackgroundColor,
483 selectedContentColor = selectedContentColor,
484 selectedLeadingIconColor = selectedLeadingIconColor
485 )
486
487 /**
488 * Creates a [ChipColors] that represents the default background and content colors used in a
489 * selectable outlined [FilterChip].
490 *
491 * @param backgroundColor the background color of this chip when enabled
492 * @param contentColor the content color of this chip when enabled
493 * @param leadingIconColor the color of this chip's start icon when enabled
494 * @param disabledBackgroundColor the background color of this chip when not enabled
495 * @param disabledContentColor the content color of this chip when not enabled
496 * @param disabledLeadingIconColor the color of this chip's start icon when not enabled
497 * @param selectedBackgroundColor the background color of this chip when selected
498 * @param selectedContentColor the content color of this chip when selected
499 * @param selectedLeadingIconColor the color of this chip's start icon when selected
500 */
501 @Composable
502 fun outlinedFilterChipColors(
503 backgroundColor: Color = MaterialTheme.colors.surface,
504 contentColor: Color = MaterialTheme.colors.onSurface.copy(ContentOpacity),
505 leadingIconColor: Color = contentColor.copy(LeadingIconOpacity),
506 disabledBackgroundColor: Color = backgroundColor,
507 disabledContentColor: Color =
508 contentColor.copy(alpha = ContentAlpha.disabled * ContentOpacity),
509 disabledLeadingIconColor: Color =
510 leadingIconColor.copy(alpha = ContentAlpha.disabled * LeadingIconOpacity),
511 selectedBackgroundColor: Color =
512 MaterialTheme.colors.onSurface
513 .copy(alpha = SelectedOverlayOpacity)
514 .compositeOver(backgroundColor),
515 selectedContentColor: Color =
516 MaterialTheme.colors.onSurface
517 .copy(alpha = SelectedOverlayOpacity)
518 .compositeOver(contentColor),
519 selectedLeadingIconColor: Color =
520 MaterialTheme.colors.onSurface
521 .copy(alpha = SelectedOverlayOpacity)
522 .compositeOver(leadingIconColor)
523 ): SelectableChipColors =
524 DefaultSelectableChipColors(
525 backgroundColor = backgroundColor,
526 contentColor = contentColor,
527 leadingIconColor = leadingIconColor,
528 disabledBackgroundColor = disabledBackgroundColor,
529 disabledContentColor = disabledContentColor,
530 disabledLeadingIconColor = disabledLeadingIconColor,
531 selectedBackgroundColor = selectedBackgroundColor,
532 selectedContentColor = selectedContentColor,
533 selectedLeadingIconColor = selectedLeadingIconColor
534 )
535
536 /** The border used by all types of outlined chips */
537 val outlinedBorder: BorderStroke
538 @Composable
539 get() =
540 BorderStroke(
541 OutlinedBorderSize,
542 MaterialTheme.colors.onSurface.copy(alpha = OutlinedBorderOpacity)
543 )
544
545 /** The color opacity used for chip's leading icon color */
546 const val LeadingIconOpacity = 0.54f
547
548 /** The color opacity used for chip's content color */
549 const val ContentOpacity = 0.87f
550
551 /** The color opacity used for the outlined chip's border color */
552 const val OutlinedBorderOpacity = 0.12f
553
554 /** The outlined chip's border size */
555 val OutlinedBorderSize = 1.dp
556
557 /** The size of a chip's leading icon */
558 val LeadingIconSize = 20.dp
559
560 /** The size of a standalone selected icon */
561 val SelectedIconSize = 18.dp
562 }
563
564 /** Default [ChipColors] implementation. */
565 @ExperimentalMaterialApi
566 @Immutable
567 private class DefaultChipColors(
568 private val backgroundColor: Color,
569 private val contentColor: Color,
570 private val leadingIconContentColor: Color,
571 private val disabledBackgroundColor: Color,
572 private val disabledContentColor: Color,
573 private val disabledLeadingIconContentColor: Color
574 // TODO(b/113855296): Support other states: hover, focus, drag
575 ) : ChipColors {
576 @Composable
577 override fun backgroundColor(enabled: Boolean): State<Color> {
578 return rememberUpdatedState(if (enabled) backgroundColor else disabledBackgroundColor)
579 }
580
581 @Composable
582 override fun contentColor(enabled: Boolean): State<Color> {
583 return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
584 }
585
586 @Composable
587 override fun leadingIconContentColor(enabled: Boolean): State<Color> {
588 return rememberUpdatedState(
589 if (enabled) leadingIconContentColor else disabledLeadingIconContentColor
590 )
591 }
592
593 override fun equals(other: Any?): Boolean {
594 if (this === other) return true
595 if (other == null || this::class != other::class) return false
596
597 other as DefaultChipColors
598
599 if (backgroundColor != other.backgroundColor) return false
600 if (contentColor != other.contentColor) return false
601 if (leadingIconContentColor != other.leadingIconContentColor) return false
602 if (disabledBackgroundColor != other.disabledBackgroundColor) return false
603 if (disabledContentColor != other.disabledContentColor) return false
604 if (disabledLeadingIconContentColor != other.disabledLeadingIconContentColor) return false
605
606 return true
607 }
608
609 override fun hashCode(): Int {
610 var result = backgroundColor.hashCode()
611 result = 31 * result + contentColor.hashCode()
612 result = 31 * result + leadingIconContentColor.hashCode()
613 result = 31 * result + disabledBackgroundColor.hashCode()
614 result = 31 * result + disabledContentColor.hashCode()
615 result = 31 * result + disabledLeadingIconContentColor.hashCode()
616
617 return result
618 }
619 }
620
621 /** Default [SelectableChipColors] implementation. */
622 @ExperimentalMaterialApi
623 @Immutable
624 private class DefaultSelectableChipColors(
625 private val backgroundColor: Color,
626 private val contentColor: Color,
627 private val leadingIconColor: Color,
628 private val disabledBackgroundColor: Color,
629 private val disabledContentColor: Color,
630 private val disabledLeadingIconColor: Color,
631 private val selectedBackgroundColor: Color,
632 private val selectedContentColor: Color,
633 private val selectedLeadingIconColor: Color
634 // TODO(b/113855296): Support other states: hover, focus, drag
635 ) : SelectableChipColors {
636 @Composable
backgroundColornull637 override fun backgroundColor(enabled: Boolean, selected: Boolean): State<Color> {
638 val target =
639 when {
640 !enabled -> disabledBackgroundColor
641 !selected -> backgroundColor
642 else -> selectedBackgroundColor
643 }
644 return rememberUpdatedState(target)
645 }
646
647 @Composable
contentColornull648 override fun contentColor(enabled: Boolean, selected: Boolean): State<Color> {
649 val target =
650 when {
651 !enabled -> disabledContentColor
652 !selected -> contentColor
653 else -> selectedContentColor
654 }
655 return rememberUpdatedState(target)
656 }
657
658 @Composable
leadingIconColornull659 override fun leadingIconColor(enabled: Boolean, selected: Boolean): State<Color> {
660 val target =
661 when {
662 !enabled -> disabledLeadingIconColor
663 !selected -> leadingIconColor
664 else -> selectedLeadingIconColor
665 }
666 return rememberUpdatedState(target)
667 }
668
equalsnull669 override fun equals(other: Any?): Boolean {
670 if (this === other) return true
671 if (other == null || this::class != other::class) return false
672
673 other as DefaultSelectableChipColors
674
675 if (backgroundColor != other.backgroundColor) return false
676 if (contentColor != other.contentColor) return false
677 if (leadingIconColor != other.leadingIconColor) return false
678 if (disabledBackgroundColor != other.disabledBackgroundColor) return false
679 if (disabledContentColor != other.disabledContentColor) return false
680 if (disabledLeadingIconColor != other.disabledLeadingIconColor) return false
681 if (selectedBackgroundColor != other.selectedBackgroundColor) return false
682 if (selectedContentColor != other.selectedContentColor) return false
683 if (selectedLeadingIconColor != other.selectedLeadingIconColor) return false
684
685 return true
686 }
687
hashCodenull688 override fun hashCode(): Int {
689 var result = backgroundColor.hashCode()
690 result = 31 * result + contentColor.hashCode()
691 result = 31 * result + leadingIconColor.hashCode()
692 result = 31 * result + disabledBackgroundColor.hashCode()
693 result = 31 * result + disabledContentColor.hashCode()
694 result = 31 * result + disabledLeadingIconColor.hashCode()
695 result = 31 * result + selectedBackgroundColor.hashCode()
696 result = 31 * result + selectedContentColor.hashCode()
697 result = 31 * result + selectedLeadingIconColor.hashCode()
698
699 return result
700 }
701 }
702
703 /**
704 * The content padding used by a chip. Used as start padding when there's leading icon, used as eng
705 * padding when there's no trailing icon.
706 */
707 private val HorizontalPadding = 12.dp
708
709 /** The size of the spacing before the leading icon when they used inside a chip. */
710 private val LeadingIconStartSpacing = 4.dp
711
712 /** The size of the spacing between the leading icon and a text inside a chip. */
713 private val LeadingIconEndSpacing = 8.dp
714
715 /** The size of the horizontal spacing before and after the trailing icon inside an InputChip. */
716 private val TrailingIconSpacing = 8.dp
717
718 /** The color opacity used for chip's surface overlay. */
719 private const val SurfaceOverlayOpacity = 0.12f
720
721 /** The color opacity used for a selected chip's leading icon overlay. */
722 private const val SelectedOverlayOpacity = 0.16f
723
724 /**
725 * The size of a circle used to obscure the leading icon before a selected icon is displayed on top.
726 */
727 private val SelectedIconContainerSize = 24.dp
728