- name
- android-compose-components
- description
- Complete catalog of Android Jetpack Compose and Material Design 3 components.
Covers M3 Expressive (2025), gesture system, graphics/drawing, navigation patterns,
theming, and mapping guidance for Crystal cross-platform UI integration via JNI.
- user-invocable
- true
- allowed-tools
- Read, Write, Edit, Glob, Grep, Bash, WebSearch, WebFetch
- version
- 1.0.0
# Android Jetpack Compose / Material 3 Components Reference
This document is the authoritative reference for Android Jetpack Compose and Material
Design 3 UI components as of 2025 (compose-material3 1.4.0 stable, 1.5.x alpha series).
It covers Material 3 Expressive, the full gesture system, Canvas/graphics APIs, adaptive
navigation, theming tokens, and platform-specific Android APIs. It provides implementation
priority guidance for the asset_pipeline Crystal library's `Android::Renderer` (JNI bridge).
The asset_pipeline maps Crystal `UI::View` types to native Android elements at compile
time. The `Android::Renderer` (in `src/ui/renderers/android_renderer.cr`) calls through
the JNI bridge (`jni_bridge.c`) to Kotlin/Java, which creates the actual View or Composable
objects. The JNI collection bridge (`src/ui/native/jni_collections.cr`) provides typed
wrappers for JNI references, ViewGroup batch operations, and string marshaling.
---
## 1. Overview
### Material Design 3 and M3 Expressive
Material Design 3 (M3) is the current Google design system, launched alongside Android 12
(Material You). As of 2025 the system has evolved into M3 Expressive, announced at
Google I/O 2025, which adds:
- Five new component families: ButtonGroup, SplitButtonLayout, FloatingActionButtonMenu,
LoadingIndicator/WavyProgressIndicator, and Floating Toolbars
- 35 new shape definitions and shape-morphing animations
- A physics-based spring motion system replacing fixed easing curves
- Enhanced typography expressiveness
- Research backed by 46 studies and 18,000+ participants
The Jetpack Compose implementation ships in `androidx.compose.material3` (latest stable
1.4.0, released September 24 2025). Experimental and expressive APIs continue in the
1.5.x alpha series.
### Jetpack Compose vs Traditional Android Views
| Aspect | Jetpack Compose | Traditional Android Views |
|--------|----------------|--------------------------|
| Paradigm | Declarative (describe what, not how) | Imperative (XML + Java/Kotlin mutations) |
| State | `State<T>`, `MutableState<T>`, `remember {}` | ViewModel + LiveData/StateFlow observers |
| Layout | Compose layout pass (intrinsics-based) | Measure/layout/draw pass on View hierarchy |
| Thread | Main thread, coroutines for side effects | Main thread for UI; background via Handler |
| Styling | Modifier chains, MaterialTheme | XML attributes, style resources, themes |
| Crystal JNI | Lower priority — hard to drive from native | Higher priority — `jobject` ViewGroup refs |
### Crystal Integration: JNI Bridge Pattern
```
Crystal UI::View tree
|
v
Android::Renderer (src/ui/renderers/android_renderer.cr)
visit(label) / visit(button) / ...
|
v [Crystal -> C via @[Link] fun declarations]
jni_bridge.c (C wrapper functions)
|
v [C -> JVM via JNIEnv* calls]
Kotlin/Java Activity or Fragment
- Inflates Views or creates Composables
- Returns jobject back to C
|
v [back to Crystal]
NativeHandle (UI::JNI.global(env, local_ref))
stored in NativeView node
```
The JNI collection bridge (`src/ui/native/jni_collections.cr`) provides:
- `UI::JNI::JString` — Crystal String <-> jstring marshaling
- `UI::JNI::ObjectArray` — Crystal Array <-> jobjectArray
- `UI::JNI::ArrayList` — Crystal Array <-> java.util.ArrayList
- `UI::JNI.viewgroup_add_views(env, parent, children)` — batch addView()
- `UI::JNI.local_frame(env, capacity)` — scoped local reference tables
**Critical JNI rule:** Local refs are only valid within the current native method call.
Use `UI::JNI.global(env, local_ref)` to promote any View reference that Crystal must
hold past the return boundary, then `NativeHandle` tracks the lifetime.
### API Level Requirements Summary
| Feature | Min API |
|---------|---------|
| All Compose basics | 21 (Android 5.0) |
| Dynamic Color (Material You) | 31 (Android 12) |
| RenderEffect / blur | 31 (Android 12) |
| RuntimeShader / AGSL | 33 (Android 13) |
| Predictive back gesture | 34 (Android 14) |
| Compose BOM 2024+ | 21 |
| WideNavigationRail | 21 (M3 Expressive, lib-based) |
| FloatingActionButtonMenu | 21 (M3 Expressive, lib-based) |
| WavyLinearProgressIndicator | 21 (M3 Expressive, lib-based) |
| Picture-in-Picture | 26 (Android 8.0) |
| Glance Widgets | 23 (Android 6.0) |
---
## 2. Material 3 Components by Category
All components require:
```kotlin
implementation("androidx.compose.material3:material3:1.4.0")
```
Experimental APIs require `@OptIn(ExperimentalMaterial3Api::class)`.
Expressive APIs (1.5.x alpha) additionally require `@OptIn(ExperimentalMaterial3ExpressiveApi::class)`.
---
### 2.1 Buttons and FABs
#### Standard Buttons
| Compose Function | Emphasis | Traditional View | Crystal UI::View Equiv. | Priority |
|-----------------|----------|-----------------|------------------------|----------|
| `Button` | High — filled, primary color | `MaterialButton` | `UI::Button` (maps here) | P0 (done) |
| `FilledButton` | High — alias for Button | `MaterialButton` | Same as Button | P0 |
| `ElevatedButton` | Medium — elevated surface | `MaterialButton` (elevation style) | — | P1 |
| `FilledTonalButton` | Medium — secondary container fill | `MaterialButton` (tonal style) | — | P1 |
| `OutlinedButton` | Low — bordered, transparent fill | `MaterialButton` (outlined style) | — | P1 |
| `TextButton` | Lowest — text only | `MaterialButton` (text style) | — | P2 |
All button parameters:
```kotlin
Button(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = ButtonDefaults.shape, // RoundedCornerShape(50%) by default
colors: ButtonColors = ButtonDefaults.buttonColors(),
elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
border: BorderStroke? = null,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
interactionSource: MutableInteractionSource? = null,
content: @Composable RowScope.() -> Unit
)
```
#### Icon Buttons
| Compose Function | Traditional View | Priority |
|-----------------|-----------------|----------|
| `IconButton` | `ImageButton` | P1 |
| `FilledIconButton` | `MaterialButton` (icon, filled) | P2 |
| `FilledTonalIconButton` | `MaterialButton` (icon, tonal) | P2 |
| `OutlinedIconButton` | `MaterialButton` (icon, outlined) | P2 |
| `IconToggleButton` | `ImageButton` + toggle state | P2 |
#### Floating Action Buttons (FABs)
| Compose Function | Size | Traditional View | Priority |
|-----------------|------|-----------------|----------|
| `FloatingActionButton` | Standard (56dp) | `FloatingActionButton` | P1 |
| `SmallFloatingActionButton` | Small (40dp) | `FloatingActionButton` (mini) | P2 |
| `LargeFloatingActionButton` | Large (96dp) | `FloatingActionButton` (large) | P2 |
| `ExtendedFloatingActionButton` | Pill with text | `ExtendedFloatingActionButton` | P2 |
FAB parameters:
```kotlin
FloatingActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
shape: Shape = FloatingActionButtonDefaults.shape,
containerColor: Color = FloatingActionButtonDefaults.containerColor,
contentColor: Color = contentColorFor(containerColor),
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
interactionSource: MutableInteractionSource? = null,
content: @Composable () -> Unit
)
```
#### NEW: M3 Expressive Button Components (1.4.x / 1.5.x-alpha)
| Compose Function | Description | Traditional View Equiv. | Priority |
|-----------------|-------------|------------------------|----------|
| `ButtonGroup` | Horizontal or vertical group of semantically related buttons with connected styling | `LinearLayout` of `MaterialButton` | P2 |
| `SplitButtonLayout` | Two-zone button: action zone + dropdown trigger zone | Custom compound `MaterialButton` | P2 |
| `FloatingActionButtonMenu` | FAB that expands into a menu of secondary actions | Custom `CoordinatorLayout` pattern | P2 |
| `ToggleFloatingActionButton` | FAB with toggled (expanded/collapsed) state, used as anchor for FABMenu | `FloatingActionButton` | P2 |
| `FloatingActionButtonMenuItem` | Individual item inside a FABMenu | `MaterialButton` in overlay | P2 |
```kotlin
// ButtonGroup usage (M3 Expressive)
ButtonGroup(
modifier: Modifier = Modifier,
horizontalArrangement: Arrangement.Horizontal = ButtonGroupDefaults.horizontalArrangement,
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
content: @Composable ButtonGroupScope.() -> Unit
)
// Inside ButtonGroupScope, use Modifier.align() per-button
// FloatingActionButtonMenu usage
FloatingActionButtonMenu(
expanded: Boolean,
button: @Composable () -> Unit, // Typically a ToggleFloatingActionButton
modifier: Modifier = Modifier,
content: @Composable FloatingActionButtonMenuScope.() -> Unit
) {
FloatingActionButtonMenuItem(
onClick = { /* action */ },
icon = { Icon(Icons.Default.Share, contentDescription = null) },
text = { Text("Share") }
)
}
```
---
### 2.2 Selection Controls
#### Checkbox
| Compose Function | Traditional View | Parameters | Priority |
|-----------------|-----------------|------------|----------|
| `Checkbox` | `CheckBox` | `checked`, `onCheckedChange`, `enabled`, `colors` | P1 |
| `TriStateCheckbox` | `CheckBox` (partial state) | `state: ToggleableState`, `onClick`, `enabled`, `colors` | P2 |
```kotlin
Checkbox(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: CheckboxColors = CheckboxDefaults.colors(),
interactionSource: MutableInteractionSource? = null
)
```
#### RadioButton
| Compose Function | Traditional View | Priority |
|-----------------|-----------------|----------|
| `RadioButton` | `RadioButton` | P1 |
RadioButton has no built-in group; you track selection state in the parent composable.
Traditional equivalent for a group: `RadioGroup` containing `RadioButton` views.
```kotlin
RadioButton(
selected: Boolean,
onClick: (() -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: RadioButtonColors = RadioButtonDefaults.colors()
)
```
#### Switch
| Compose Function | Traditional View | Crystal UI::View Equiv. | Priority |
|-----------------|-----------------|------------------------|----------|
| `Switch` | `SwitchCompat` / `MaterialSwitch` | — | P1 |
```kotlin
Switch(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
thumbContent: (@Composable () -> Unit)? = null, // Icon inside thumb
enabled: Boolean = true,
colors: SwitchColors = SwitchDefaults.colors(),
interactionSource: MutableInteractionSource? = null
)
```
#### Chips
All chips require `@OptIn(ExperimentalMaterial3Api::class)`.
| Compose Function | Use Case | Traditional View | Priority |
View on GitHub