As 20 perguntas mais frequentes sobre Jetpack Compose em entrevistas (2026)
As 20 perguntas de entrevista sobre Jetpack Compose mais comuns: recomposição, gerenciamento de estado, navegação, performance e padrões de arquitetura.

Jetpack Compose se consolidou como o toolkit de UI padrão para desenvolvimento Android. Entrevistas técnicas avaliam regularmente o domínio de Compose, desde os mecanismos de recomposição até o gerenciamento de estado e a otimização de performance. A seguir estão as 20 perguntas mais frequentes, com respostas detalhadas e exemplos de código.
Cada pergunta inclui uma resposta estruturada e um exemplo de código. As perguntas estão organizadas por dificuldade crescente: fundamentos, intermediário e avançado.
Fundamentos do Jetpack Compose
1. Qual é a diferença entre Compose e o sistema de views XML?
Compose utiliza um paradigma declarativo: a UI é descrita como uma função do estado, e o framework cuida das atualizações automaticamente. O sistema XML tradicional é imperativo, exigindo manipulação manual de views via findViewById ou View Binding.
// Compose: UI updates automatically when count changes
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) } // Reactive state
Button(onClick = { count++ }) { // UI declaration
Text("Clicks: $count") // Recomposed automatically
}
}Com Compose, não é necessário buscar uma referência a um TextView e atualizá-lo manualmente. A recomposição cuida de tudo.
2. O que é recomposição?
Recomposição é o processo pelo qual o Compose re-invoca funções @Composable quando o estado delas muda. Apenas as funções cujos parâmetros foram alterados são re-executadas, o que otimiza a performance.
@Composable
fun UserCard(name: String, age: Int) {
Column {
Text("Name: $name") // Recomposed only if name changes
Text("Age: $age") // Recomposed only if age changes
StaticBadge() // Not recomposed if its inputs remain the same
}
}
@Composable
fun StaticBadge() {
Text("Static badge") // Compose knows this function is stable
}Ponto-chave para entrevistas: a recomposição é otimista (o Compose assume que pode ser cancelada) e não ordenada (a ordem de execução dos composables não é garantida).
3. O que o remember faz?
remember preserva um valor entre recomposições. Sem remember, cada recomposição redefiniria a variável para seu valor inicial.
@Composable
fun InputField() {
// ✅ Value survives recompositions
var text by remember { mutableStateOf("") }
// ❌ Without remember, text resets to "" on every recomposition
// var text by mutableStateOf("")
TextField(
value = text,
onValueChange = { text = it }, // Triggers recomposition
label = { Text("Enter text") }
)
}4. Qual é a diferença entre remember e rememberSaveable?
remember preserva valores entre recomposições, mas os perde em mudanças de configuração (rotação de tela). rememberSaveable persiste os valores através de mudanças de configuração usando o mecanismo SavedInstanceState.
@Composable
fun SearchBar() {
// Lost after screen rotation
var query by remember { mutableStateOf("") }
// Preserved after screen rotation
var savedQuery by rememberSaveable { mutableStateOf("") }
TextField(
value = savedQuery,
onValueChange = { savedQuery = it },
placeholder = { Text("Search...") }
)
}Gerenciamento de estado no Compose
5. O que é state hoisting?
State hoisting consiste em mover o estado de um composable para o componente pai. O composable filho se torna sem estado: recebe o estado como parâmetros e notifica mudanças via callbacks. Esse padrão é essencial para construir componentes reutilizáveis e testáveis.
// ✅ Stateless composable, easy to test and reuse
@Composable
fun EmailInput(
email: String, // State provided by parent
onEmailChange: (String) -> Unit, // Callback to parent
modifier: Modifier = Modifier
) {
TextField(
value = email,
onValueChange = onEmailChange,
label = { Text("Email") },
modifier = modifier
)
}
// Parent manages the state
@Composable
fun LoginForm() {
var email by remember { mutableStateOf("") }
EmailInput(
email = email,
onEmailChange = { email = it } // Parent controls state
)
}Esse padrão é fundamental no Compose e aparece com frequência em entrevistas.
6. Como o derivedStateOf funciona?
derivedStateOf cria um estado derivado que só dispara recomposição quando o resultado do cálculo muda, não a cada modificação da fonte.
@Composable
fun FilteredList(items: List<String>) {
var searchQuery by remember { mutableStateOf("") }
// Recalculated only when the filtered result actually changes
val filteredItems by remember(items) {
derivedStateOf {
items.filter { it.contains(searchQuery, ignoreCase = true) }
}
}
Column {
TextField(value = searchQuery, onValueChange = { searchQuery = it })
LazyColumn {
items(filteredItems) { item -> Text(item) }
}
}
}Esse mecanismo é útil quando um estado muda frequentemente, mas o resultado derivado muda raramente (por exemplo, uma lista filtrada ou um botão habilitado/desabilitado com base na validade de um formulário).
7. Qual é a diferença entre StateFlow e State<T> do Compose?
StateFlow (Kotlin coroutines) é um fluxo reativo proveniente do ViewModel. State<T> é o mecanismo nativo do Compose para disparar recomposições. Na prática, StateFlow é coletado em um composable via collectAsStateWithLifecycle(). Para uma compreensão mais profunda dos fluxos de coroutines, consultar o guia completo de Kotlin coroutines.
class UserViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UserUiState())
val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
}
@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
// Converts StateFlow to State<T> for Compose
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
Text("Hello, ${uiState.userName}")
}A recomendação é usar collectAsStateWithLifecycle() (em vez de collectAsState()) porque respeita o ciclo de vida e para a coleta quando a tela não está mais visível.
Side effects e ciclo de vida
8. Quais são os principais side effects no Compose?
Os side effects permitem executar código não-composable (chamadas de rede, logging, navegação) de forma controlada. Os principais side effects incluem LaunchedEffect, DisposableEffect, SideEffect, e o novo Keyed SideEffect introduzido no Compose 1.12.
@Composable
fun AnalyticsScreen(screenName: String) {
// LaunchedEffect: runs once when screenName changes
LaunchedEffect(screenName) {
analyticsTracker.logScreenView(screenName) // Suspended call
}
// DisposableEffect: with cleanup (like useEffect with cleanup)
DisposableEffect(Unit) {
val listener = onScrollListener()
scrollView.addListener(listener)
onDispose {
scrollView.removeListener(listener) // Cleanup guaranteed
}
}
// SideEffect: runs after every successful recomposition
SideEffect {
logger.log("Screen recomposed") // Non-suspended code
}
}Atualização Compose 1.12: Keyed SideEffect fornece uma nova sobrecarga que suporta argumentos de chave para side effects de disparo único quando chaves específicas mudam. Segundo os benchmarks oficiais, pode ser até 90% mais rápido que LaunchedEffect para operações não suspensas.
9. Quando usar LaunchedEffect vs rememberCoroutineScope?
LaunchedEffect está vinculado à composição: a coroutine é cancelada quando o composable sai da composição ou quando a chave muda. rememberCoroutineScope fornece um scope controlado pelo usuário, útil para ações disparadas por interação (cliques de botão).
@Composable
fun DataScreen(userId: String) {
// ✅ LaunchedEffect: automatic loading tied to lifecycle
LaunchedEffect(userId) {
loadUserData(userId) // Re-launched if userId changes
}
// ✅ rememberCoroutineScope: one-off user action
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch { refreshData() } // Triggered manually
}) {
Text("Refresh")
}
}Pronto para mandar bem nas entrevistas de Android?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Layouts e componentes avançados
10. Como o LazyColumn funciona e como se diferencia do RecyclerView?
LazyColumn é o equivalente do RecyclerView no Compose. Apenas compõe os elementos visíveis na tela e recicla os composables que saem da janela visível.
@Composable
fun UserList(users: List<User>) {
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp) // Spacing between items
) {
items(
items = users,
key = { it.id } // Stable key to optimize recompositions
) { user ->
UserCard(user)
}
}
}Ponto importante: sempre fornecer um parâmetro key estável para evitar recomposições desnecessárias ao ordenar ou remover elementos.
11. Como criar um layout personalizado?
Compose permite criar layouts personalizados através da função Layout. Isso substitui as implementações de ViewGroup personalizadas do sistema de views.
@Composable
fun OverlappingRow(
overlapOffset: Dp = (-16).dp, // Negative offset for overlap
content: @Composable () -> Unit
) {
Layout(content = content) { measurables, constraints ->
val placeables = measurables.map { it.measure(constraints) }
val width = placeables.sumOf { it.width } + (overlapOffset.roundToPx() * (placeables.size - 1))
val height = placeables.maxOf { it.height }
layout(width, height) {
var xOffset = 0
placeables.forEach { placeable ->
placeable.placeRelative(xOffset, 0)
xOffset += placeable.width + overlapOffset.roundToPx()
}
}
}
}12. Como implementar temas personalizados com MaterialTheme?
O sistema de temas no Compose é baseado em CompositionLocal. MaterialTheme fornece valores de cores, tipografia e formas acessíveis em toda a árvore de composables.
// Custom color definitions
private val DarkColorScheme = darkColorScheme(
primary = Color(0xFF6200EE),
secondary = Color(0xFF03DAC6),
background = Color(0xFF121212)
)
@Composable
fun AppTheme(content: @Composable () -> Unit) {
MaterialTheme(
colorScheme = DarkColorScheme,
typography = AppTypography, // Custom typography
content = content
)
}
// Usage in a composable
@Composable
fun ThemedCard() {
Card(colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface // Theme access
)) {
Text(
text = "Content",
style = MaterialTheme.typography.bodyLarge // Theme typography
)
}
}Navegação no Compose
13. Como funciona a navegação no Compose?
Compose Navigation utiliza um NavHost com rotas declaradas como tipos serializáveis (Navigation 2.8+). Desde o Navigation 2.9, a biblioteca suporta value classes e List<Enum> como tipos de argumento sem exigir implementações personalizadas de NavType.
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") {
HomeScreen(onNavigateToDetail = { id ->
navController.navigate("detail/$id") // Navigation with argument
})
}
composable(
route = "detail/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType })
) { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId") ?: ""
DetailScreen(userId = userId)
}
}
}14. Como passar dados entre telas?
Argumentos simples (String, Int) são passados diretamente pela rota. Para objetos complexos, a recomendação atual é usar um ViewModel compartilhado ou passar apenas um identificador e carregar os dados na tela de destino. O Navigation 2.9 introduziu CollectionNavType<T> para argumentos baseados em coleções como listas e arrays.
// Type-safe navigation with Kotlin Serialization (Navigation 2.8+)
@Serializable
data class ProfileRoute(val userId: String, val tab: String = "info")
// Declaration
composable<ProfileRoute> { backStackEntry ->
val route = backStackEntry.toRoute<ProfileRoute>()
ProfileScreen(userId = route.userId, tab = route.tab)
}
// Navigation
navController.navigate(ProfileRoute(userId = "123", tab = "stats"))Nunca passar objetos complexos serializados na rota. O correto é passar um ID e deixar a tela de destino carregar os dados através do ViewModel. Para orientação sobre arquitetura, consultar MVVM vs MVI: Qual arquitetura escolher.
Performance e otimização
15. Como prevenir recomposições desnecessárias?
Três estratégias principais para minimizar recomposições desnecessárias:
// 1. Use stable classes (data class with immutable properties)
@Stable // Tells Compose this class is stable
data class UserState(
val name: String,
val avatar: String
)
// 2. Extract lambdas with remember
@Composable
fun OptimizedList(onItemClick: (String) -> Unit) {
val stableCallback = remember(onItemClick) { onItemClick }
LazyColumn {
items(100) { index ->
ItemRow(onClick = { stableCallback("item_$index") })
}
}
}
// 3. Use key() to help Compose identify elements
@Composable
fun UserTabs(users: List<User>) {
Column {
users.forEach { user ->
key(user.id) { // Stable identity
UserRow(user)
}
}
}
}16. Como fazer profiling de performance em uma aplicação Compose?
O Layout Inspector do Android Studio exibe os contadores de recomposição por composable. O Compose 1.13 alpha introduz o RecompositionTracer para registrar fluxos de invalidação de recomposição. A flag debugInspectorInfo também auxilia no diagnóstico.
// Enable recomposition counters in debug
@Composable
fun DebugRecomposition(tag: String, content: @Composable () -> Unit) {
val recompositionCount = remember { mutableIntStateOf(0) }
SideEffect {
recompositionCount.intValue++ // Incremented on every recomposition
Log.d("Recomposition", "$tag: ${recompositionCount.intValue} times")
}
content()
}
// Usage
DebugRecomposition("UserCard") {
UserCard(user)
}Além disso, o Compose Compiler Metrics gera um relatório detalhado de funções skippable, restartable e classes estáveis/instáveis.
17. O que é o Modifier e por que é importante?
Modifier é uma cadeia ordenada de instruções que modifica a aparência e o comportamento de um composable. A ordem dos modifiers impacta diretamente a renderização.
@Composable
fun ModifierOrderDemo() {
// ❌ Padding THEN background = padding not colored
Text(
text = "Hello",
modifier = Modifier
.padding(16.dp)
.background(Color.Red)
)
// ✅ Background THEN padding = padding is colored
Text(
text = "Hello",
modifier = Modifier
.background(Color.Red)
.padding(16.dp)
)
}Boa prática: sempre aceitar um parâmetro modifier: Modifier = Modifier em composables reutilizáveis para permitir customização pelo componente pai.
Arquitetura e padrões avançados
18. Como estruturar uma tela Compose com ViewModel?
O padrão recomendado separa o estado da UI em um data class, os eventos em uma sealed interface, e o ViewModel gerencia a lógica de negócio.
// UI State
data class ProfileUiState(
val user: User? = null,
val isLoading: Boolean = false,
val error: String? = null
)
// User events
sealed interface ProfileEvent {
data object Refresh : ProfileEvent
data class UpdateName(val name: String) : ProfileEvent
}
// ViewModel
class ProfileViewModel(private val repo: UserRepository) : ViewModel() {
private val _uiState = MutableStateFlow(ProfileUiState(isLoading = true))
val uiState = _uiState.asStateFlow()
fun onEvent(event: ProfileEvent) {
when (event) {
is ProfileEvent.Refresh -> loadProfile()
is ProfileEvent.UpdateName -> updateName(event.name)
}
}
}
// Compose screen
@Composable
fun ProfileScreen(viewModel: ProfileViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
ProfileContent(
uiState = uiState,
onEvent = viewModel::onEvent // Event delegation
)
}19. Como testar composables?
Compose fornece uma biblioteca de testes com ComposeTestRule para testes de UI e asserções semânticas. Desde o Compose 1.11, as APIs de teste v2 são as padrão, com a v1 deprecada. A mudança principal é a troca de UnconfinedTestDispatcher (execução imediata) para StandardTestDispatcher (execução enfileirada), o que significa que os testes agora refletem melhor o comportamento em produção.
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun counter_incrementsOnClick() {
composeTestRule.setContent {
Counter() // The composable under test
}
// Verify initial state
composeTestRule.onNodeWithText("Clicks: 0").assertIsDisplayed()
// Simulate a click
composeTestRule.onNodeWithText("Clicks: 0").performClick()
// With v2 APIs, advance the clock explicitly
composeTestRule.waitForIdle()
// Verify new state
composeTestRule.onNodeWithText("Clicks: 1").assertIsDisplayed()
}Para testes unitários de composables sem estado, testar o ViewModel separadamente com testes padrão de JUnit/Turbine costuma ser mais eficiente.
20. Como integrar Compose em uma aplicação existente baseada em XML?
A interoperabilidade é bidirecional: ComposeView permite inserir Compose em XML, e AndroidView utiliza views clássicas dentro do Compose.
// Compose in XML (in a Fragment or Activity)
class ProfileFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
setContent {
AppTheme { ProfileScreen() }
}
}
}
}
// XML View in Compose
@Composable
fun LegacyMapView() {
AndroidView(
factory = { context -> MapView(context).apply { onCreate(null) } },
update = { mapView -> mapView.getMapAsync { /* config */ } }
)
}Migrar tela por tela, começando pelas mais simples. Cada nova tela deve ser inteiramente em Compose, enquanto as telas existentes são migradas progressivamente.
Pronto para mandar bem nas entrevistas de Android?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Sources
- What's new in the Jetpack Compose August '26 release - Compose 1.12 release with Keyed SideEffect, BOM 2026.08.00
- What's new in the Jetpack Compose April '26 release - Testing v2 APIs, shared element debugging
- Navigation releases - Navigation 2.9 with value class and CollectionNavType support
- Side effects in Compose - Official side effects documentation
Pontos-chave para entrevistas de Jetpack Compose
Essas 20 perguntas cobrem os fundamentos que todo desenvolvedor Android precisa dominar para uma entrevista sobre Jetpack Compose. Para praticar de forma interativa, experimentar o módulo de perguntas de entrevista de Jetpack Compose.
- ✅ Compreender a recomposição e seu comportamento otimista
- ✅ Dominar
remember,rememberSaveableederivedStateOf - ✅ Aplicar state hoisting sistematicamente
- ✅ Conhecer os side effects (
LaunchedEffect,DisposableEffect,SideEffect, e o novo Keyed SideEffect) - ✅ Otimizar a performance (classes estáveis, keys, lambdas)
- ✅ Estruturar telas com ViewModel + UiState + Events
- ✅ Testar composables com
ComposeTestRuleusando as APIs de teste v2 - ✅ Dominar a interoperabilidade Compose/Views
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Você saberia encontrar o bug em Android?
Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Escrito por
Anthony Fillion-MailletFundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 20 de agosto de 2026
Tags
Compartilhar
Artigos relacionados

Jetpack Compose: Animações Avançadas Passo a Passo
Guia completo de animações avançadas no Compose: transições, AnimatedVisibility, Animatable, gestos e desempenho para interfaces Android fluidas.

Kotlin Flow vs StateFlow vs SharedFlow: perguntas de entrevista Android em 2026
As perguntas sobre Kotlin Flow vs StateFlow vs SharedFlow que os entrevistadores de Android fazem em 2026, com respostas claras, tabela comparativa e código pronto para produção.

Modularização Android em 2026: Arquitetura Multi-Módulo e Perguntas de Entrevista
Guia completo sobre modularização Android em 2026. Aprenda a estruturar uma arquitetura multi-módulo, usar catálogos de versões Gradle e dominar as perguntas essenciais de entrevista técnica.