O Material Design sempre foi uma linguagem visual competente, mas durante anos carregou uma crítica justa entre times de produto Android: ele era “plano demais”, geométrico e, em muitos apps, indistinguível. O Google respondeu a isso com o Material 3 Expressive, uma evolução lançada em meados de 2025 que amplifica expressividade visual sem quebrar o contrato do Material 3 que já tínhamos em produção. Em 2026, com a androidx.compose.material3 madura e o Android 16, adotar o Expressive deixou de ser experimento e virou a configuração padrão recomendada para apps novos.
Neste guia você implementa o Material 3 Expressive com Jetpack Compose e Kotlin em 2026, do zero à migração de um tema existente, cobrindo as novas formas (Shape), tipografia expressiva, fonte variável, motion com Spring e SharedTransition, layouts adaptativos por WindowSizeClass e testes visuais. Se você ainda não dominou o Compose, vale revisar antes o guia de Jetpack Compose e o artigo sobre edge-to-edge no Android 16, porque o Expressive pressupõe uma base sólida de theming e janela.
1. O que muda do Material 3 para o Material 3 Expressive
O Expressive não é o Material 4. É uma expansão do mesmo material3 que você já usa, ativada por API e por tokens de tema. As mudanças centrais são:
- Shapes mais vivos: além do
Shapesclássico (extra-small a large), o Expressive introduz oLargeShapes,MediumShapeseSmallShapescom cantos malhosos (RoundedCornerShape,SquircleShape) e dimensões maiores, dando personalidade a cards, botões e sheets. - Tipografia expressiva: tipografia com tamanhos e pesos mais contrastantes, pensada para display. Em vez de só
headlineLarge, o tema expõe umTypographycom variações ópticas e suporte de primeira classe a fonte variável (variable font), permitindoFontVariation.Settingspor token. - Motion como fundação: animações baseadas em
Spring(não mais só tweens lineares),SharedTransitionLayoutpara transições entre telas eexpressiveMotionScheme()que entrega curvas consistentes. - Layouts adaptativos nativos: o
WindowSizeClasspassa a guiar não só colunas, mas densidade, spacing e tipo de navegação (bottom bar vs. rail vs. drawer) de forma declarativa. - Cores tonais mais ricas: a paleta dinâmica (
dynamicColor) continua, mas o Expressive adiciona harmonias de cor e o conceito de “cor de destaque” que se adapta ao conteúdo.
A boa notícia para quem já tem app em produção: a migração é incremental. Você pode ativar Expressive por componente, sem reescrever o tema inteiro.
2. Dependências e versões
No libs.versions.toml (padrão que vale seguir — veja o artigo sobre Gradle Version Catalog):
[versions]
androidxComposeBom = "2026.03.00"
androidxMaterial3Expressive = "1.4.0"
[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
androidx-material3-expressive = { group = "androidx.compose.material3", name = "material3", version.ref = "androidxMaterial3Expressive" }
No app/build.gradle.kts:
dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.material3.expressive)
}
Em 2026 a material3 já traz o Expressive embutido — não há um artifact separado material3-expressive. O que existe é um scheme (expressiveColorScheme, expressiveMotionScheme) e componentes novos (FlexibleBottomSheet, LoadingIndicator) que você opta por usar.
3. Esquema de cores expressivo
O ponto de entrada é o MaterialExpressiveTheme, que sobrescreve o MaterialTheme clássico injetando os esquemas expressivos:
@Composable
fun KotlinBrTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val context = LocalContext.current
val baseScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ->
if (darkTheme) dynamicDarkColorScheme(context)
else dynamicLightColorScheme(context)
darkTheme -> DarkColors
else -> LightColors
}
val expressiveScheme = if (darkTheme) {
expressiveDarkColorScheme(baseScheme)
} else {
expressiveLightColorScheme(baseScheme)
}
MaterialExpressiveTheme(
colorScheme = expressiveScheme,
typography = KotlinTypography,
shapes = KotlinShapes(),
motionScheme = expressiveMotionScheme(),
content = content
)
}
O expressiveLightColorScheme deriva variações extra (tons 60, 70, 80) a partir do baseScheme, expondo cores como colorScheme.containerHighest e colorScheme.onAccentContainer. Quando dynamicColor está ativo (Android 12+), o esquema herda o papel de parede — comportamento idêntico ao Material 3 clássico, mas com mais graus de cor disponíveis.
4. Shapes e formas expressivas
A maior mudança visual fica nas formas. O Expressive introduz Shapes() com mais níveis e cantos maiores:
@Composable
fun KotlinShapes(): Shapes = Shapes(
extraSmall = RoundedCornerShape(8.dp),
small = RoundedCornerShape(12.dp),
medium = RoundedCornerShape(20.dp),
large = RoundedCornerShape(32.dp),
extraLarge = RoundedCornerShape(48.dp)
)
Para componentes de destaque — FAB, cards hero, bottom sheets — o Expressive oferece SquircleShape (canto “superelíptico”, o mesmo do iOS) e o MaterialShapes com formas matemáticas nomeadas (Cookie9Sided, Slanted). Use com parcimônia: formas exóticas funcionam em pontos focais, não em listas inteiras.
FloatingActionButton(
onClick = { /* ... */ },
shape = MaterialShapes.Cookie9Sided.toShape(),
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
) {
Icon(Icons.Default.Add, contentDescription = "Adicionar")
}
5. Tipografia expressiva com fonte variável
A tipografia expressiva ganha mais contraste. Um Typography típico:
val KotlinTypography = Typography(
displayLarge = TextStyle(
fontFamily = VariableFont,
fontWeight = FontWeight.W700,
fontSize = 57.sp,
lineHeight = 64.sp,
letterSpacing = (-0.25).sp
),
headlineLarge = TextStyle(
fontFamily = VariableFont,
fontWeight = FontWeight.W600,
fontSize = 32.sp,
lineHeight = 40.sp
),
bodyLarge = TextStyle(
fontFamily = VariableFont,
fontWeight = FontWeight.W400,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
)
Para usar fonte variável, carregue via FontVariation:
val VariableFont = FontFamily(
Font(
R.font.inter_variable,
variationSettings = FontVariation.Settings(
FontVariation.weight(600),
FontVariation.slant(0f),
FontVariation.opticalSize(16f)
)
)
)
A vantagem é performance e consistência: um único arquivo .ttf cobre toda a faixa de peso (100–900), eliminando o bundle de múltiplas fontes e garantindo transições suaves em animações que interpolam fontWeight.
6. Motion com Spring e SharedTransition
O expressiveMotionScheme() entrega curvas pré-configuradas, mas o real ganho do Expressive está no uso direto de Spring e SharedTransitionLayout. Um exemplo de transição entre um item de lista e seu detalhe:
SharedTransitionLayout {
NavHost(navController, startDestination = "lista") {
composable("lista") {
ListaScreen(
onItemClick = { id -> navController.navigate("detalhe/$id") },
animatedVisibilityScope = this@composable,
sharedTransitionScope = this@SharedTransitionLayout
)
}
composable("detalhe/{id}") { backStackEntry ->
DetalheScreen(
id = backStackEntry.arguments?.getString("id").orEmpty(),
animatedVisibilityScope = this@composable,
sharedTransitionScope = this@SharedTransitionLayout
)
}
}
}
Dentro do item compartilhado, marque-o com Modifier.sharedElement():
Image(
painter = rememberAsyncImagePainter(post.imagemUrl),
contentDescription = post.titulo,
modifier = Modifier
.size(120.dp)
.sharedElement(
rememberSharedContentState(key = "imagem-${post.id}"),
animatedVisibilityScope = animatedVisibilityScope
)
)
O resultado é a imagem “voando” da lista para o detalhe com interpolação automática de tamanho e posição — sem biblioteca third-party, sem boilerplate de Transition.
Para microinterações, prefira Spring a tween:
val scale by animateFloatAsState(
targetValue = if (pressed) 0.95f else 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
),
label = "scale"
)
7. Layouts adaptativos com WindowSizeClass
O Expressive formaliza a adaptação por tamanho de janela, algo essencial em 2026 com foldables e tablets rodando Android 16. O WindowSizeClass classifica a largura em compact (<600dp), medium (600–840dp) e expanded (>840dp):
val windowSize = currentWindowAdaptiveInfo().windowSizeClass
when (windowSize.windowWidthSizeClass) {
WindowWidthSizeClass.Compact -> {
// Smartphone em retrato: bottom bar
Scaffold(bottomBar = { BottomBar() }) { padding ->
FeedCompact(contentPadding = padding)
}
}
WindowWidthSizeClass.Medium -> {
// Tablet pequeno / foldable aberto: rail
Scaffold { FeedMedium() }
}
else -> {
// Tablet grande / desktop: lista-detalhe
TwoPaneLayout()
}
}
Esse padrão também melhora a performance de scroll em telas largas e habilita corretamente o edge-to-edge no Android 16, evitando o clássico bug de conteúdo colado na barra do sistema.
8. Componentes novos que valem a pena
O Expressive trouxe componentes que resolvem dores reais:
LoadingIndicator: substitui oCircularProgressIndicatorcom um spinner que “respira” e pode ganhar cor de destaque, alinhado ao motion expressivo.FlexibleBottomSheet: bottom sheet que respeita gestos e se adapta ao conteúdo sem ocupar a tela toda, ideal para contextos rápidos.SplitToggleChip: chip de filtro com área de toque separada da seleção, melhorando acessibilidade.Carousel: carrossel acessível com snap e suporte a itens de largura variável, substituindo oHorizontalPagerpara casos de showcase.
LoadingIndicator(
modifier = Modifier.padding(24.dp),
color = MaterialTheme.colorScheme.primary
)
9. Migração gradual de um app existente
Para um app já em Material 3 clássico, a migração pode ser feita em três passos reversíveis:
- Substituir
MaterialThemeporMaterialExpressiveThemeno nível raiz. Como ambos expõem o mesmocolorSchemeetypography, os componentes existentes continuam funcionando sem alteração. - Ativar
expressiveMotionScheme()e trocar tweens críticos porSpringonde há feedback tátil (botões, FAB, chips). - Adotar
WindowSizeClassem telas-chave (lista, detalhe, configurações) antes de tocar em componentes novos.
Cada passo é um commit isolado, testável e revertível. Em apps grandes, vale orquestrar isso junto com a modularização Android em Compose para que cada feature adopte Expressive no seu ritmo.
10. Performance, acessibilidade e testes
O Expressive aumenta o uso de animação e fontes variáveis, então alguns cuidados:
- Honorar “Remover animações”: o Compose expõe
LocalAccessibilityManager.currentReducedMotion()(ouSettings.Global.ANIMATOR_DURATION_SCALE). DesligueSpringbouncy eSharedTransitionquando o usuário solicitar movimento reduzido. - Baseline profiles: animações e
SharedTransitionse beneficiam de AOT. Gere profiles com o Macrobenchmark para manter 60fps em dispositivos low-end. - Contraste: o esquema expressivo deriva muitos tons; valide contraste WCAG AA nos textos sobre
containerHighest. - Glance e widgets: widgets de homescreen ainda usam o
glance(veja o guia de Glance Widgets) e não herdamMaterialExpressiveTheme— use oColorSchemedo app como referência visual, mas mapeie paraColorProvidersdo Glance.
Para testes visuais, o snapshot testing em Compose é o gate natural: capture a versão Material 3 clássica, migre e compare os diffs conscientemente.
11. Quando NÃO adotar Expressive
Expressivo é uma escolha de produto, não uma obrigação técnica. Evite quando:
- O app tem marca muito forte com design system próprio (bancos, fintechs) — o Expressive compete visualmente com sua identidade.
- A audiência é majoritariamente acessibilidade-first e prefere UIs sóbrias (apps governamentais, saúde) — o contraste extra pode distrair.
- O time não tem banda para testar animações em diversos dispositivos — motion mal calibrado vira jank em Android low-end, pior que sem animação.
Conclusão
O Material 3 Expressive é o primeiro “Material” que se sente vivo desde o lançamento do Material You. Com Jetpack Compose e Kotlin em 2026, ele entrega ganhos reais de percepção de qualidade, motion fluido e adaptação a foldables — sem exigir reescrita. Aplique de forma incremental, meça performance com baseline profiles, honoreie preferências de acessibilidade e você terá um app que parece de 2026, não de 2021.
Para ir além, combine Expressive com os widgets de homescreen com Glance e uma arquitetura modular em Compose — o resultado é uma base Android moderna, performática e pronta para escalar junto com o ecossistema Kotlin.