Theming¶
Every colour and text style is a parameter. The library has no Material dependency and no theme of its own — it reads what you pass and nothing else.
Colors¶
DataTable(
// ...
colors = DataTableDefaults.colors(
container = Color.White,
header = Color(0xFFF5F5F5),
selectedRow = Color(0x4D1976D2),
hoveredRow = Color(0x1A000000),
rowAlternate = Color(0xFFFAFAFA),
divider = Color(0xFFE0E0E0),
checkboxChecked = Color(0xFF1976D2),
focusedRowBorder = Color(0xFF1976D2),
),
)
| Token | Applies to |
|---|---|
container |
Table background |
header |
Header row and footer background |
divider |
Row separators, freeze boundary, grouped-header underlines |
selectedRow |
Selected row background |
hoveredRow |
Hovered row background, and icon-button hover highlights |
rowAlternate |
Every other row. Color.Transparent (the default) disables striping |
expandedRow |
Background behind expandContent |
focusedRowBorder |
Leading edge marker on the keyboard-focused row |
focusedCellBorder |
Outline around the focused cell, once cellNavigation is on |
selectedCell |
Wash over cells in a selected range. Translucent, and the cursor cell is left unwashed |
editingCell |
Background behind an open cell editor |
invalidCellBorder |
Replaces focusedCellBorder while an editor's value has been rejected |
draggedColumn |
Wash over the header being dragged, once reorderableColumns is on |
columnDropIndicator |
The line marking the edge a dragged column will land against |
checkboxChecked / checkboxUnchecked / checkboxCheckmark |
Selection checkboxes |
iconTint |
Sort arrows, expand chevrons, pagination arrows |
disabledContent |
Disabled pagination arrows |
onSurface |
The caret in an open cell editor. Also available to custom cell content |
onSurfaceSecondary |
Muted content colour, for custom cell content |
focusedCellBorder, selectedCell, editingCell, and invalidCellBorder only ever draw under
cell navigation or editing — a table that opts into neither can ignore them.
Text styles¶
DataTable(
// ...
textStyles = DataTableDefaults.textStyles(
headerCell = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold),
bodyCell = TextStyle(fontSize = 14.sp),
footer = TextStyle(fontSize = 12.sp),
pagination = TextStyle(fontSize = 14.sp),
loading = TextStyle(fontSize = 14.sp),
noData = TextStyle(fontSize = 16.sp),
cellEditor = TextStyle(fontSize = 14.sp),
),
)
Text colour lives on the TextStyle, not in DataTableColors — so a dark palette means setting
both.
cellEditor styles the text inside an open cell editor. It matches bodyCell by default, which
is what stops a cell jumping as it goes into edit mode — if you restyle one, restyle the other.
Density¶
| Density | Vertical padding | Horizontal padding | Default row height |
|---|---|---|---|
DEFAULT |
16.dp | 16.dp | 52.dp |
COMFORTABLE |
12.dp | 16.dp | 44.dp |
COMPACT |
8.dp | 12.dp | 36.dp |
Density sets the padding, and the default row height follows from it:
DataTableDefaults.rowHeight(density) is that padding either side of one line of body text. Pass
DataTable a rowHeight of your own for cells that need more room, or rowHeight = null for
rows that size to their content — see
Row height.
A dark palette¶
There is no built-in dark preset. Both halves have to move together:
val darkColors = DataTableDefaults.colors(
container = Color(0xFF1E1E1E),
header = Color(0xFF2A2A2A),
divider = Color(0xFF3A3A3A),
selectedRow = Color(0x664F8CC9),
hoveredRow = Color(0x14FFFFFF),
rowAlternate = Color(0xFF242424),
iconTint = Color(0xFFBBBBBB),
disabledContent = Color(0xFF5A5A5A),
checkboxChecked = Color(0xFF4F8CC9),
checkboxUnchecked = Color(0xFF888888),
focusedRowBorder = Color(0xFF4F8CC9),
focusedCellBorder = Color(0xFF4F8CC9),
selectedCell = Color(0x334F8CC9),
editingCell = Color(0xFF2F2F2F),
onSurface = Color(0xFFEDEDED),
)
val darkText = DataTableDefaults.textStyles(
headerCell = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold, color = Color(0xFFEDEDED)),
bodyCell = TextStyle(fontSize = 14.sp, color = Color(0xFFDDDDDD)),
footer = TextStyle(fontSize = 12.sp, color = Color(0xFFBBBBBB)),
pagination = TextStyle(fontSize = 14.sp, color = Color(0xFFDDDDDD)),
cellEditor = TextStyle(fontSize = 14.sp, color = Color(0xFFDDDDDD)),
)
editingCell is white until you move it
It defaults to Color(0xFFFFFFFF) — the one token that will flash a white box in an otherwise
dark table the first time someone opens an editor. onSurface paints the caret in that editor,
so it has to move with it or the cursor is invisible.
Driving it from Material is just a matter of reading the scheme:
colors = DataTableDefaults.colors(
container = MaterialTheme.colorScheme.surface,
header = MaterialTheme.colorScheme.surfaceVariant,
divider = MaterialTheme.colorScheme.outlineVariant,
selectedRow = MaterialTheme.colorScheme.primary.copy(alpha = 0.2f),
)
Loading and empty states¶
DataTable(
// ...
loading = isLoading,
loadingContent = { CircularProgressIndicator() }, // optional
noDataContent = { Text("No results found") }, // optional
)
Both have plain built-in defaults. loading = true replaces the body entirely — there is no
overlay-while-showing-stale-rows mode.
The empty state shows whenever there are no rows to display, including an out-of-range page.
Custom header and footer¶
DataTable(
// ...
hideDefaultHeader = true,
headerContent = { /* your header composable */ },
hideDefaultFooter = true,
footerContent = { /* your footer composable */ },
)
Supplying headerContent replaces the built-in header entirely — including sorting, resizing, and
the select-all checkbox, which all live there. Reach for it when you want a toolbar above the
table rather than a replacement for the column headers.