0
# Navigation Components
1
2
Material Design navigation components that integrate seamlessly with iOS navigation patterns and UIKit, providing consistent Material styling while respecting iOS platform conventions.
3
4
## Capabilities
5
6
### Scaffold
7
8
Material design layout structure that provides slots for common screen elements with iOS-specific adaptations.
9
10
```kotlin { .api }
11
/**
12
* Material Design layout structure providing a framework for common Material Design components
13
* Integrates with iOS navigation patterns and safe area handling
14
* @param modifier Modifier to be applied to this Scaffold
15
* @param scaffoldState State of this scaffold containing the DrawerState and SnackbarHostState
16
* @param topBar Top app bar of the screen, typically a TopAppBar
17
* @param bottomBar Bottom bar of the screen, typically a BottomNavigation
18
* @param snackbarHost Component to host Snackbars
19
* @param floatingActionButton Main action button, typically a FloatingActionButton
20
* @param floatingActionButtonPosition Position of the FAB on the screen
21
* @param isFloatingActionButtonDocked Whether the FAB should dock with the bottom bar
22
* @param drawerContent Content of the Drawer sheet that can be pulled from the left side
23
* @param drawerGesturesEnabled Whether or not drawer can be interacted with via gestures
24
* @param drawerShape Shape of the drawer sheet
25
* @param drawerElevation Elevation of the drawer sheet
26
* @param drawerBackgroundColor Background color of the drawer sheet
27
* @param drawerContentColor Color of the content inside the drawer sheet
28
* @param drawerScrimColor Color of the scrim that obscures content when the drawer is open
29
* @param backgroundColor Background color of the scaffold body
30
* @param contentColor Preferred content color provided by scaffold to its children
31
* @param content Content of the screen
32
*/
33
@Composable
34
fun Scaffold(
35
modifier: Modifier = Modifier,
36
scaffoldState: ScaffoldState = rememberScaffoldState(),
37
topBar: @Composable () -> Unit = {},
38
bottomBar: @Composable () -> Unit = {},
39
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
40
floatingActionButton: @Composable () -> Unit = {},
41
floatingActionButtonPosition: FabPosition = FabPosition.End,
42
isFloatingActionButtonDocked: Boolean = false,
43
drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
44
drawerGesturesEnabled: Boolean = true,
45
drawerShape: Shape = MaterialTheme.shapes.large,
46
drawerElevation: Dp = DrawerDefaults.Elevation,
47
drawerBackgroundColor: Color = MaterialTheme.colors.surface,
48
drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
49
drawerScrimColor: Color = DrawerDefaults.scrimColor,
50
backgroundColor: Color = MaterialTheme.colors.background,
51
contentColor: Color = contentColorFor(backgroundColor),
52
content: @Composable (PaddingValues) -> Unit
53
)
54
```
55
56
**Usage Examples:**
57
58
```kotlin
59
// Basic scaffold with iOS-style navigation
60
Scaffold(
61
topBar = {
62
TopAppBar(
63
title = { Text("My App") },
64
navigationIcon = {
65
IconButton(onClick = { /* handle back */ }) {
66
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
67
}
68
}
69
)
70
},
71
floatingActionButton = {
72
FloatingActionButton(
73
onClick = { /* add action */ }
74
) {
75
Icon(Icons.Default.Add, contentDescription = "Add")
76
}
77
}
78
) { paddingValues ->
79
LazyColumn(
80
modifier = Modifier.padding(paddingValues),
81
contentPadding = PaddingValues(16.dp)
82
) {
83
items(items) { item ->
84
// List content
85
}
86
}
87
}
88
89
// Scaffold with drawer (iOS-style sidebar)
90
val scaffoldState = rememberScaffoldState()
91
val scope = rememberCoroutineScope()
92
93
Scaffold(
94
scaffoldState = scaffoldState,
95
topBar = {
96
TopAppBar(
97
title = { Text("With Drawer") },
98
navigationIcon = {
99
IconButton(
100
onClick = {
101
scope.launch {
102
scaffoldState.drawerState.open()
103
}
104
}
105
) {
106
Icon(Icons.Default.Menu, contentDescription = "Menu")
107
}
108
}
109
)
110
},
111
drawerContent = {
112
DrawerContent()
113
}
114
) { paddingValues ->
115
// Main content
116
}
117
118
// Scaffold with bottom navigation
119
Scaffold(
120
bottomBar = {
121
BottomNavigation {
122
val navItems = listOf("Home", "Search", "Profile")
123
navItems.forEachIndexed { index, item ->
124
BottomNavigationItem(
125
icon = { Icon(getIconForTab(index), contentDescription = null) },
126
label = { Text(item) },
127
selected = selectedTab == index,
128
onClick = { selectedTab = index }
129
)
130
}
131
}
132
}
133
) { paddingValues ->
134
// Content for each tab
135
}
136
```
137
138
### TopAppBar
139
140
Material app bar for screen headers with iOS navigation integration and safe area handling.
141
142
```kotlin { .api }
143
/**
144
* Material Design top app bar with iOS-style navigation integration
145
* @param title The title to be displayed in the center of the TopAppBar
146
* @param modifier Modifier to be applied to this TopAppBar
147
* @param navigationIcon The navigation icon displayed at the start of the TopAppBar
148
* @param actions The actions displayed at the end of the TopAppBar
149
* @param backgroundColor The background color for the TopAppBar
150
* @param contentColor The preferred content color provided by this TopAppBar to its children
151
* @param elevation The elevation of this TopAppBar
152
*/
153
@Composable
154
fun TopAppBar(
155
title: @Composable () -> Unit,
156
modifier: Modifier = Modifier,
157
navigationIcon: @Composable (() -> Unit)? = null,
158
actions: @Composable RowScope.() -> Unit = {},
159
backgroundColor: Color = MaterialTheme.colors.primarySurface,
160
contentColor: Color = contentColorFor(backgroundColor),
161
elevation: Dp = AppBarDefaults.TopAppBarElevation
162
)
163
```
164
165
**Usage Examples:**
166
167
```kotlin
168
// Basic top app bar
169
TopAppBar(
170
title = { Text("Screen Title") }
171
)
172
173
// Top app bar with navigation and actions
174
TopAppBar(
175
title = { Text("Messages") },
176
navigationIcon = {
177
IconButton(onClick = { /* navigate back */ }) {
178
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
179
}
180
},
181
actions = {
182
IconButton(onClick = { /* search */ }) {
183
Icon(Icons.Default.Search, contentDescription = "Search")
184
}
185
IconButton(onClick = { /* more options */ }) {
186
Icon(Icons.Default.MoreVert, contentDescription = "More")
187
}
188
}
189
)
190
191
// Custom styled top app bar (iOS-style)
192
TopAppBar(
193
title = {
194
Text(
195
"Custom Title",
196
style = MaterialTheme.typography.h6.copy(fontWeight = FontWeight.Medium)
197
)
198
},
199
backgroundColor = Color.Transparent,
200
contentColor = MaterialTheme.colors.onBackground,
201
elevation = 0.dp
202
)
203
```
204
205
### TopAppBar Defaults and Customization
206
207
TopAppBar defaults and styling configurations for consistent Material Design appearance with iOS adaptations.
208
209
```kotlin { .api }
210
object TopAppBarDefaults {
211
/**
212
* Creates TopAppBarColors with Material 3 theming for iOS optimization
213
*/
214
@Composable
215
fun centerAlignedTopAppBarColors(
216
containerColor: Color = MaterialTheme.colorScheme.surface,
217
scrolledContainerColor: Color = MaterialTheme.colorScheme.applyTonalElevation(
218
backgroundColor = containerColor,
219
elevation = 3.dp
220
),
221
navigationIconContentColor: Color = MaterialTheme.colorScheme.onSurface,
222
titleContentColor: Color = MaterialTheme.colorScheme.onSurface,
223
actionIconContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant
224
): TopAppBarColors
225
226
/**
227
* Creates TopAppBarColors for medium-sized top app bars
228
*/
229
@Composable
230
fun mediumTopAppBarColors(
231
containerColor: Color = MaterialTheme.colorScheme.surface,
232
scrolledContainerColor: Color = MaterialTheme.colorScheme.applyTonalElevation(
233
backgroundColor = containerColor,
234
elevation = 3.dp
235
),
236
navigationIconContentColor: Color = MaterialTheme.colorScheme.onSurface,
237
titleContentColor: Color = MaterialTheme.colorScheme.onSurface,
238
actionIconContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant
239
): TopAppBarColors
240
241
/**
242
* Creates TopAppBarScrollBehavior for iOS-style scroll behavior
243
*/
244
@Composable
245
fun pinnedScrollBehavior(
246
state: TopAppBarState = rememberTopAppBarState(),
247
canScroll: () -> Boolean = { true }
248
): TopAppBarScrollBehavior
249
250
@Composable
251
fun enterAlwaysScrollBehavior(
252
state: TopAppBarState = rememberTopAppBarState(),
253
canScroll: () -> Boolean = { true },
254
snapAnimationSpec: AnimationSpec<Float>? = spring(stiffness = Spring.StiffnessMediumLow),
255
flingAnimationSpec: DecayAnimationSpec<Float>? = rememberSplineBasedDecay()
256
): TopAppBarScrollBehavior
257
}
258
259
interface TopAppBarColors {
260
@Composable
261
fun containerColor(scrollFraction: Float): State<Color>
262
@Composable
263
fun navigationIconContentColor(): State<Color>
264
@Composable
265
fun titleContentColor(): State<Color>
266
@Composable
267
fun actionIconContentColor(): State<Color>
268
}
269
270
interface TopAppBarScrollBehavior {
271
val state: TopAppBarState
272
val isPinned: Boolean
273
val snapAnimationSpec: AnimationSpec<Float>?
274
val flingAnimationSpec: DecayAnimationSpec<Float>?
275
val nestedScrollConnection: NestedScrollConnection
276
}
277
278
@Stable
279
class TopAppBarState(
280
heightOffsetLimit: Float,
281
initialHeightOffset: Float = 0f,
282
initialContentOffset: Float = 0f
283
) {
284
val heightOffset: Float
285
val contentOffset: Float
286
val overlappedFraction: Float
287
}
288
289
@Composable
290
fun rememberTopAppBarState(
291
initialHeightOffsetLimit: Float = -Float.MAX_VALUE,
292
initialHeightOffset: Float = 0f,
293
initialContentOffset: Float = 0f
294
): TopAppBarState
295
```
296
297
**Usage Examples:**
298
299
```kotlin
300
// TopAppBar with custom colors (iOS-style)
301
TopAppBar(
302
title = { Text("My App") },
303
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
304
containerColor = MaterialTheme.colorScheme.primaryContainer,
305
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
306
navigationIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
307
),
308
navigationIcon = {
309
IconButton(onClick = { /* back navigation */ }) {
310
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
311
}
312
}
313
)
314
315
// TopAppBar with scroll behavior
316
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
317
318
Scaffold(
319
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
320
topBar = {
321
TopAppBar(
322
title = { Text("Scrolling App Bar") },
323
scrollBehavior = scrollBehavior
324
)
325
}
326
) { paddingValues ->
327
LazyColumn(
328
modifier = Modifier.fillMaxSize(),
329
contentPadding = paddingValues
330
) {
331
items(100) { index ->
332
Text(
333
text = "Item $index",
334
modifier = Modifier.padding(16.dp)
335
)
336
}
337
}
338
}
339
340
// Medium-sized TopAppBar with custom colors
341
MediumTopAppBar(
342
title = { Text("Large Title") },
343
colors = TopAppBarDefaults.mediumTopAppBarColors(
344
containerColor = MaterialTheme.colorScheme.surface,
345
titleContentColor = MaterialTheme.colorScheme.primary
346
),
347
scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
348
)
349
```
350
351
### BottomNavigation
352
353
Bottom navigation bar container with iOS tab bar styling and behavior.
354
355
```kotlin { .api }
356
/**
357
* Material Design bottom navigation with iOS tab bar adaptations
358
* @param modifier Modifier to be applied to this BottomNavigation
359
* @param backgroundColor The background color for this BottomNavigation
360
* @param contentColor The preferred content color provided by this BottomNavigation to its children
361
* @param elevation The elevation of this BottomNavigation
362
* @param content The content of this BottomNavigation, typically BottomNavigationItems
363
*/
364
@Composable
365
fun BottomNavigation(
366
modifier: Modifier = Modifier,
367
backgroundColor: Color = MaterialTheme.colors.primarySurface,
368
contentColor: Color = contentColorFor(backgroundColor),
369
elevation: Dp = BottomNavigationDefaults.Elevation,
370
content: @Composable RowScope.() -> Unit
371
)
372
```
373
374
### BottomNavigationItem
375
376
Individual bottom navigation items with iOS-style selection states and animations.
377
378
```kotlin { .api }
379
/**
380
* Material Design bottom navigation item with iOS tab styling
381
* @param selected Whether this item is selected
382
* @param onClick Called when this item is clicked
383
* @param icon The icon for this item, typically an Icon
384
* @param modifier Modifier to be applied to this item
385
* @param enabled Controls the enabled state of this item
386
* @param label Optional text label for this item
387
* @param alwaysShowLabel Whether to always show the label for this item
388
* @param selectedContentColor The color of the content when this item is selected
389
* @param unselectedContentColor The color of the content when this item is not selected
390
*/
391
@Composable
392
fun RowScope.BottomNavigationItem(
393
selected: Boolean,
394
onClick: () -> Unit,
395
icon: @Composable () -> Unit,
396
modifier: Modifier = Modifier,
397
enabled: Boolean = true,
398
label: @Composable (() -> Unit)? = null,
399
alwaysShowLabel: Boolean = true,
400
selectedContentColor: Color = LocalContentColor.current,
401
unselectedContentColor: Color = selectedContentColor.copy(alpha = ContentAlpha.medium)
402
)
403
```
404
405
**Usage Examples:**
406
407
```kotlin
408
// iOS-style bottom navigation
409
var selectedTab by remember { mutableStateOf(0) }
410
val tabs = listOf(
411
Triple("Home", Icons.Default.Home, Icons.Outlined.Home),
412
Triple("Search", Icons.Default.Search, Icons.Outlined.Search),
413
Triple("Favorites", Icons.Default.Favorite, Icons.Outlined.FavoriteBorder),
414
Triple("Profile", Icons.Default.Person, Icons.Outlined.Person)
415
)
416
417
BottomNavigation(
418
backgroundColor = MaterialTheme.colors.surface,
419
contentColor = MaterialTheme.colors.primary
420
) {
421
tabs.forEachIndexed { index, (title, selectedIcon, unselectedIcon) ->
422
BottomNavigationItem(
423
icon = {
424
Icon(
425
if (selectedTab == index) selectedIcon else unselectedIcon,
426
contentDescription = null
427
)
428
},
429
label = { Text(title) },
430
selected = selectedTab == index,
431
onClick = { selectedTab = index },
432
selectedContentColor = MaterialTheme.colors.primary,
433
unselectedContentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
434
)
435
}
436
}
437
```
438
439
### BottomAppBar
440
441
Bottom app bar with FAB integration and iOS-style positioning.
442
443
```kotlin { .api }
444
/**
445
* Material Design bottom app bar with iOS-optimized FAB integration
446
* @param modifier Modifier to be applied to this BottomAppBar
447
* @param backgroundColor The background color for this BottomAppBar
448
* @param contentColor The preferred content color provided by this BottomAppBar to its children
449
* @param cutoutShape The shape of the cutout that will be added to the BottomAppBar
450
* @param elevation The elevation of this BottomAppBar
451
* @param content The content of this BottomAppBar
452
*/
453
@Composable
454
fun BottomAppBar(
455
modifier: Modifier = Modifier,
456
backgroundColor: Color = MaterialTheme.colors.primarySurface,
457
contentColor: Color = contentColorFor(backgroundColor),
458
cutoutShape: Shape? = null,
459
elevation: Dp = AppBarDefaults.BottomAppBarElevation,
460
content: @Composable RowScope.() -> Unit
461
)
462
```
463
464
### NavigationRail
465
466
Vertical navigation component for larger iOS devices like iPad.
467
468
```kotlin { .api }
469
/**
470
* Material Design navigation rail optimized for iOS larger screens
471
* @param modifier Modifier to be applied to this NavigationRail
472
* @param backgroundColor The background color for this NavigationRail
473
* @param contentColor The preferred content color provided by this NavigationRail to its children
474
* @param elevation The elevation of this NavigationRail
475
* @param header Optional header content displayed at the top
476
* @param content The content of this NavigationRail, typically NavigationRailItems
477
*/
478
@Composable
479
fun NavigationRail(
480
modifier: Modifier = Modifier,
481
backgroundColor: Color = MaterialTheme.colors.surface,
482
contentColor: Color = contentColorFor(backgroundColor),
483
elevation: Dp = NavigationRailDefaults.Elevation,
484
header: @Composable (ColumnScope.() -> Unit)? = null,
485
content: @Composable ColumnScope.() -> Unit
486
)
487
```
488
489
### Drawer Components
490
491
Material Design drawer components with iOS-style presentation and gestures.
492
493
```kotlin { .api }
494
/**
495
* State of the DrawerLayout composable
496
*/
497
@Stable
498
class DrawerState(
499
initialValue: DrawerValue,
500
confirmStateChange: (DrawerValue) -> Boolean = { true }
501
) {
502
val currentValue: DrawerValue
503
val isAnimationRunning: Boolean
504
val isClosed: Boolean
505
val isOpen: Boolean
506
507
suspend fun open()
508
suspend fun close()
509
suspend fun animateTo(targetValue: DrawerValue, anim: AnimationSpec<Float> = DrawerConstants.defaultAnimationSpec)
510
suspend fun snapTo(targetValue: DrawerValue)
511
}
512
513
/**
514
* Create and remember a DrawerState
515
*/
516
@Composable
517
fun rememberDrawerState(
518
initialValue: DrawerValue,
519
confirmStateChange: (DrawerValue) -> Boolean = { true }
520
): DrawerState
521
522
enum class DrawerValue {
523
Closed, Open
524
}
525
```
526
527
**Usage Examples:**
528
529
```kotlin
530
// Modal drawer with iOS-style presentation
531
@Composable
532
fun ModalDrawerExample() {
533
val drawerState = rememberDrawerState(DrawerValue.Closed)
534
val scope = rememberCoroutineScope()
535
536
ModalDrawer(
537
drawerState = drawerState,
538
drawerShape = RoundedCornerShape(topEnd = 16.dp, bottomEnd = 16.dp),
539
drawerContent = {
540
Column(
541
modifier = Modifier
542
.fillMaxHeight()
543
.width(300.dp)
544
.padding(16.dp)
545
) {
546
Text(
547
"Drawer Header",
548
style = MaterialTheme.typography.h6,
549
modifier = Modifier.padding(bottom = 16.dp)
550
)
551
Divider()
552
// Drawer items
553
repeat(5) { index ->
554
TextButton(
555
onClick = {
556
scope.launch { drawerState.close() }
557
},
558
modifier = Modifier.fillMaxWidth()
559
) {
560
Text("Menu Item ${index + 1}")
561
}
562
}
563
}
564
}
565
) {
566
// Main content
567
Scaffold(
568
topBar = {
569
TopAppBar(
570
title = { Text("Drawer Example") },
571
navigationIcon = {
572
IconButton(
573
onClick = {
574
scope.launch { drawerState.open() }
575
}
576
) {
577
Icon(Icons.Default.Menu, contentDescription = "Menu")
578
}
579
}
580
)
581
}
582
) { paddingValues ->
583
// Screen content
584
}
585
}
586
}
587
```
588
589
## Navigation Defaults and Utilities
590
591
```kotlin { .api }
592
object AppBarDefaults {
593
val TopAppBarElevation: Dp = 4.dp
594
val BottomAppBarElevation: Dp = 8.dp
595
}
596
597
object BottomNavigationDefaults {
598
val Elevation: Dp = 8.dp
599
}
600
601
object NavigationRailDefaults {
602
val Elevation: Dp = 0.dp
603
}
604
605
object DrawerDefaults {
606
val Elevation: Dp = 16.dp
607
val scrimColor: Color = Color.Black.copy(alpha = 0.32f)
608
}
609
610
enum class FabPosition {
611
Center, End
612
}
613
614
@Stable
615
class ScaffoldState(
616
val drawerState: DrawerState,
617
val snackbarHostState: SnackbarHostState
618
)
619
620
@Composable
621
fun rememberScaffoldState(
622
drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
623
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }
624
): ScaffoldState
625
```
626
627
### TabRow
628
629
Material Design tab layout for organizing content into different views with iOS-optimized touch targets.
630
631
```kotlin { .api }
632
/**
633
* Material Design fixed tab row with iOS-optimized spacing and touch targets
634
* @param selectedTabIndex The index of the currently selected tab
635
* @param modifier Modifier to be applied to this tab row
636
* @param backgroundColor The background color for the tab row
637
* @param contentColor The preferred content color provided by this tab row to its children
638
* @param indicator The indicator that represents which tab is currently selected
639
* @param divider The divider displayed at the bottom of the tab row
640
* @param tabs The tabs inside this tab row, typically this will be multiple Tab components
641
*/
642
@Composable
643
fun TabRow(
644
selectedTabIndex: Int,
645
modifier: Modifier = Modifier,
646
backgroundColor: Color = MaterialTheme.colors.primarySurface,
647
contentColor: Color = contentColorFor(backgroundColor),
648
indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
649
TabRowDefaults.Indicator(
650
Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
651
)
652
},
653
divider: @Composable () -> Unit = @Composable {
654
TabRowDefaults.Divider()
655
},
656
tabs: @Composable () -> Unit
657
)
658
659
/**
660
* Material Design scrollable tab row for large numbers of tabs with iOS momentum scrolling
661
* @param selectedTabIndex The index of the currently selected tab
662
* @param modifier Modifier to be applied to this tab row
663
* @param backgroundColor The background color for the tab row
664
* @param contentColor The preferred content color provided by this tab row to its children
665
* @param edgePadding The padding between the starting and ending edge of the scrollable tab row
666
* @param indicator The indicator that represents which tab is currently selected
667
* @param divider The divider displayed at the bottom of the tab row
668
* @param tabs The tabs inside this tab row, typically this will be multiple Tab components
669
*/
670
@Composable
671
fun ScrollableTabRow(
672
selectedTabIndex: Int,
673
modifier: Modifier = Modifier,
674
backgroundColor: Color = MaterialTheme.colors.primarySurface,
675
contentColor: Color = contentColorFor(backgroundColor),
676
edgePadding: Dp = TabRowDefaults.ScrollableTabRowPadding,
677
indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
678
TabRowDefaults.Indicator(
679
Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
680
)
681
},
682
divider: @Composable () -> Unit = @Composable {
683
TabRowDefaults.Divider()
684
},
685
tabs: @Composable () -> Unit
686
)
687
688
/**
689
* Material Design tab with iOS-optimized touch feedback and accessibility
690
* @param selected Whether this tab is currently selected
691
* @param onClick The callback to be invoked when this tab is selected
692
* @param modifier Modifier to be applied to this tab
693
* @param enabled Controls the enabled state of this tab
694
* @param text The text label displayed in this tab
695
* @param icon The optional icon displayed in this tab
696
* @param interactionSource The MutableInteractionSource representing the stream of interactions
697
* @param selectedContentColor The color of the content when this tab is selected
698
* @param unselectedContentColor The color of the content when this tab is not selected
699
*/
700
@Composable
701
fun Tab(
702
selected: Boolean,
703
onClick: () -> Unit,
704
modifier: Modifier = Modifier,
705
enabled: Boolean = true,
706
text: @Composable (() -> Unit)? = null,
707
icon: @Composable (() -> Unit)? = null,
708
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
709
selectedContentColor: Color = LocalContentColor.current,
710
unselectedContentColor: Color = selectedContentColor.copy(alpha = ContentAlpha.medium)
711
)
712
713
/**
714
* LeadingIconTab with both icon and text arranged horizontally
715
*/
716
@Composable
717
fun LeadingIconTab(
718
selected: Boolean,
719
onClick: () -> Unit,
720
text: @Composable () -> Unit,
721
icon: @Composable () -> Unit,
722
modifier: Modifier = Modifier,
723
enabled: Boolean = true,
724
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
725
selectedContentColor: Color = LocalContentColor.current,
726
unselectedContentColor: Color = selectedContentColor.copy(alpha = ContentAlpha.medium)
727
)
728
729
object TabRowDefaults {
730
val ScrollableTabRowPadding: Dp = 52.dp
731
732
@Composable
733
fun Indicator(
734
modifier: Modifier = Modifier,
735
height: Dp = 2.dp,
736
color: Color = LocalContentColor.current
737
)
738
739
@Composable
740
fun Divider(
741
modifier: Modifier = Modifier,
742
thickness: Dp = 1.dp,
743
color: Color = LocalContentColor.current.copy(alpha = 0.12f)
744
)
745
}
746
747
@Stable
748
class TabPosition(val left: Dp, val width: Dp) {
749
val right: Dp get() = left + width
750
}
751
752
fun Modifier.tabIndicatorOffset(
753
currentTabPosition: TabPosition
754
): Modifier
755
```
756
757
**Usage Examples:**
758
759
```kotlin
760
// Basic tab row with text tabs
761
var selectedTab by remember { mutableStateOf(0) }
762
val tabs = listOf("Home", "Search", "Profile")
763
764
Column {
765
TabRow(selectedTabIndex = selectedTab) {
766
tabs.forEachIndexed { index, title ->
767
Tab(
768
selected = selectedTab == index,
769
onClick = { selectedTab = index },
770
text = { Text(title) }
771
)
772
}
773
}
774
775
// Content based on selected tab
776
when (selectedTab) {
777
0 -> HomeContent()
778
1 -> SearchContent()
779
2 -> ProfileContent()
780
}
781
}
782
783
// Tab row with icons and text
784
TabRow(selectedTabIndex = currentTab) {
785
Tab(
786
selected = currentTab == 0,
787
onClick = { currentTab = 0 },
788
text = { Text("Calls") },
789
icon = { Icon(Icons.Default.Call, contentDescription = null) }
790
)
791
Tab(
792
selected = currentTab == 1,
793
onClick = { currentTab = 1 },
794
text = { Text("Messages") },
795
icon = { Icon(Icons.Default.Email, contentDescription = null) }
796
)
797
Tab(
798
selected = currentTab == 2,
799
onClick = { currentTab = 2 },
800
text = { Text("Contacts") },
801
icon = { Icon(Icons.Default.Person, contentDescription = null) }
802
)
803
}
804
805
// Scrollable tab row for many tabs
806
ScrollableTabRow(
807
selectedTabIndex = selectedTabIndex,
808
backgroundColor = MaterialTheme.colors.surface,
809
contentColor = MaterialTheme.colors.onSurface
810
) {
811
categories.forEachIndexed { index, category ->
812
Tab(
813
selected = selectedTabIndex == index,
814
onClick = { selectedTabIndex = index },
815
text = { Text(category.name) }
816
)
817
}
818
}
819
820
// Leading icon tab with horizontal arrangement
821
LeadingIconTab(
822
selected = currentTab == 0,
823
onClick = { currentTab = 0 },
824
text = { Text("Documents") },
825
icon = { Icon(Icons.Default.Description, contentDescription = null) }
826
)
827
```
828
829
### NavigationBar (Material 3)
830
831
Material 3 navigation bar for bottom navigation with modern design language and iOS-optimized behavior.
832
833
```kotlin { .api }
834
/**
835
* Material 3 NavigationBar for bottom navigation with iOS integration
836
* @param modifier Modifier to be applied to this navigation bar
837
* @param containerColor The color used for the background of this navigation bar
838
* @param contentColor The preferred color for content inside this navigation bar
839
* @param tonalElevation When containerColor is ColorScheme.surface, a translucent primary color overlay is applied
840
* @param windowInsets A window insets that the navigation bar will respect
841
* @param content The content of this navigation bar, typically NavigationBarItem components
842
*/
843
@Composable
844
fun NavigationBar(
845
modifier: Modifier = Modifier,
846
containerColor: Color = NavigationBarDefaults.containerColor,
847
contentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
848
tonalElevation: Dp = NavigationBarDefaults.Elevation,
849
windowInsets: WindowInsets = NavigationBarDefaults.windowInsets,
850
content: @Composable RowScope.() -> Unit
851
)
852
853
/**
854
* Material 3 NavigationBarItem with iOS-optimized selection behavior
855
* @param selected Whether this item is currently selected
856
* @param onClick Called when this item is clicked
857
* @param icon The icon for this item
858
* @param modifier Modifier to be applied to this item
859
* @param enabled Controls the enabled state of this item
860
* @param label Optional text label for this item
861
* @param alwaysShowLabel Whether to always show the label for this item
862
* @param colors NavigationBarItemColors that will be used to resolve the colors used for this item
863
* @param interactionSource The MutableInteractionSource representing the stream of interactions
864
*/
865
@Composable
866
fun RowScope.NavigationBarItem(
867
selected: Boolean,
868
onClick: () -> Unit,
869
icon: @Composable () -> Unit,
870
modifier: Modifier = Modifier,
871
enabled: Boolean = true,
872
label: @Composable (() -> Unit)? = null,
873
alwaysShowLabel: Boolean = true,
874
colors: NavigationBarItemColors = NavigationBarItemDefaults.colors(),
875
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
876
)
877
878
object NavigationBarDefaults {
879
val Elevation: Dp = 3.dp
880
val containerColor: Color @Composable get() = MaterialTheme.colorScheme.surface
881
val windowInsets: WindowInsets @Composable get() = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom)
882
}
883
884
object NavigationBarItemDefaults {
885
@Composable
886
fun colors(
887
selectedIconColor: Color = NavigationBarTokens.ActiveIconColor.toColor(),
888
selectedTextColor: Color = NavigationBarTokens.ActiveLabelTextColor.toColor(),
889
indicatorColor: Color = NavigationBarTokens.ActiveIndicatorColor.toColor(),
890
unselectedIconColor: Color = NavigationBarTokens.InactiveIconColor.toColor(),
891
unselectedTextColor: Color = NavigationBarTokens.InactiveLabelTextColor.toColor(),
892
disabledIconColor: Color = NavigationBarTokens.InactiveIconColor.toColor().copy(alpha = 0.38f),
893
disabledTextColor: Color = NavigationBarTokens.InactiveLabelTextColor.toColor().copy(alpha = 0.38f)
894
): NavigationBarItemColors
895
}
896
897
interface NavigationBarItemColors
898
899
interface WindowInsets {
900
companion object {
901
val systemBars: WindowInsets
902
}
903
904
fun only(sides: WindowInsetsSides): WindowInsets
905
}
906
907
enum class WindowInsetsSides {
908
Start, Top, End, Bottom, Horizontal, Vertical, All
909
}
910
```
911
912
**Usage Examples:**
913
914
```kotlin
915
// Basic navigation bar with icons and labels
916
var selectedItem by remember { mutableStateOf(0) }
917
val items = listOf("Home", "Search", "Profile")
918
val icons = listOf(Icons.Default.Home, Icons.Default.Search, Icons.Default.Person)
919
920
Scaffold(
921
bottomBar = {
922
NavigationBar {
923
items.forEachIndexed { index, item ->
924
NavigationBarItem(
925
icon = { Icon(icons[index], contentDescription = null) },
926
label = { Text(item) },
927
selected = selectedItem == index,
928
onClick = { selectedItem = index }
929
)
930
}
931
}
932
}
933
) { innerPadding ->
934
// Screen content
935
}
936
937
// Navigation bar with badges
938
NavigationBar {
939
NavigationBarItem(
940
icon = {
941
BadgedBox(
942
badge = { Badge { Text("3") } }
943
) {
944
Icon(Icons.Default.Email, contentDescription = null)
945
}
946
},
947
label = { Text("Messages") },
948
selected = selectedTab == 0,
949
onClick = { selectedTab = 0 }
950
)
951
952
NavigationBarItem(
953
icon = { Icon(Icons.Default.Home, contentDescription = null) },
954
label = { Text("Home") },
955
selected = selectedTab == 1,
956
onClick = { selectedTab = 1 }
957
)
958
959
NavigationBarItem(
960
icon = {
961
BadgedBox(
962
badge = { Badge() }
963
) {
964
Icon(Icons.Default.Notifications, contentDescription = null)
965
}
966
},
967
label = { Text("Alerts") },
968
selected = selectedTab == 2,
969
onClick = { selectedTab = 2 }
970
)
971
}
972
973
// Custom colors navigation bar
974
NavigationBar(
975
containerColor = MaterialTheme.colorScheme.surfaceVariant
976
) {
977
NavigationBarItem(
978
icon = { Icon(Icons.Default.Home, contentDescription = null) },
979
label = { Text("Home") },
980
selected = currentDestination == "home",
981
onClick = { navigateTo("home") },
982
colors = NavigationBarItemDefaults.colors(
983
selectedIconColor = MaterialTheme.colorScheme.primary,
984
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
985
selectedTextColor = MaterialTheme.colorScheme.primary,
986
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
987
)
988
)
989
}
990
```