What You Will Build

A browser-style interface with a Safari-inspired bottom navigation bar featuring a URL input field, navigation arrows, share button, bookmarks, and tab switcher icons. This pattern applies to any app with a bottom toolbar: browsers, document viewers, or media players with transport controls.

Why This Pattern Matters

Bottom-anchored toolbars are a mobile UX best practice because they keep controls within thumb reach. Building a multi-section bottom bar with a text input (URL bar) and icon buttons teaches you Scaffold's bottomBar slot and how to compose complex navigation chrome.

Key Concepts

  • Scaffold bottomBar slot for persistent bottom navigation.
  • Surface with RoundedCornerShape for the floating URL bar.
  • Row with SpaceBetween arrangement for evenly distributed toolbar icons.
  • Dark color scheme for a native browser feel.

The Safari Tab Bar Screen

@Composable
fun SafariTabBarScreen() {
    var urlText by remember { mutableStateOf("apple.com") }

    Scaffold(
        containerColor = MaterialTheme.colorScheme.background,
        bottomBar = {
            Column {
                // URL bar
                Surface(
                    modifier = Modifier.fillMaxWidth()
                        .padding(horizontal = 16.dp, vertical = 4.dp),
                    shape = RoundedCornerShape(12.dp),
                    color = MaterialTheme.colorScheme.surfaceVariant
                ) {
                    Row(
                        modifier = Modifier.padding(
                            horizontal = 12.dp, vertical = 10.dp
                        ),
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Icon(Icons.Default.Lock, null,
                            tint = Color.Gray,
                            modifier = Modifier.size(16.dp))
                        Spacer(Modifier.width(8.dp))
                        Text(urlText,
                            modifier = Modifier.weight(1f),
                            textAlign = TextAlign.Center,
                            color = Color.Gray, fontSize = 14.sp)
                        Icon(Icons.Default.Refresh, null,
                            tint = Color.Gray,
                            modifier = Modifier.size(16.dp))
                    }
                }
                // Safari toolbar icons
                Row(
                    modifier = Modifier.fillMaxWidth()
                        .padding(horizontal = 24.dp, vertical = 8.dp),
                    horizontalArrangement = Arrangement.SpaceBetween
                ) {
                    listOf(
                        Icons.Default.ArrowBack,
                        Icons.Default.ArrowForward,
                        Icons.Default.Share,
                        Icons.Default.MenuBook,
                        Icons.Default.Tab
                    ).forEach { icon ->
                        IconButton(onClick = {}) {
                            Icon(icon, null,
                                tint = Color(0xFF4A90D9))
                        }
                    }
                }
            }
        }
    ) { padding ->
        LazyColumn(
            modifier = Modifier.fillMaxSize().padding(padding),
            contentPadding = PaddingValues(16.dp),
            verticalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            item {
                Text("Safari Tab Bar",
                    fontSize = 28.sp, fontWeight = FontWeight.Bold)
                Spacer(Modifier.height(8.dp))
            }
            items(12) { i ->
                Card(
                    shape = RoundedCornerShape(12.dp),
                    colors = CardDefaults.cardColors(
                        containerColor = MaterialTheme.colorScheme.surfaceVariant
                    )
                ) {
                    Column(modifier = Modifier.padding(16.dp)) {
                        Box(
                            modifier = Modifier.fillMaxWidth()
                                .height(120.dp)
                                .clip(RoundedCornerShape(8.dp))
                                .background(Color(0xFF4A90D9).copy(alpha = 0.15f)),
                            contentAlignment = Alignment.Center
                        ) {
                            Icon(Icons.Default.Web, null,
                                modifier = Modifier.size(40.dp),
                                tint = Color(0xFF4A90D9).copy(alpha = 0.5f))
                        }
                        Spacer(Modifier.height(8.dp))
                        Text("Article ${'$'}{i + 1}",
                            fontWeight = FontWeight.SemiBold)
                        Text("A brief description of web content",
                            fontSize = 13.sp, color = Color.Gray)
                    }
                }
            }
        }
    }
}

Tips and Pitfalls

  • Scaffold bottomBar: The bottomBar slot automatically handles safe area insets for the navigation bar on gesture-navigation devices.
  • URL bar as Surface: Using Surface with a shape gives the rounded pill appearance without needing a TextField, keeping it lightweight for display-only URLs.
  • Icon list pattern: Iterating over a list of icons with forEach keeps the toolbar DRY and easy to modify.
  • Editable URL: Replace the Text with a TextField with singleLine = true and transparent background for an editable address bar.

Minimum SDK: API 21+ with Compose BOM

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.