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 — as views precisam ser manipuladas manualmente 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.
// ✅ 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().
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 três principais:
@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
}
}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 strings (ou tipos serializáveis a partir do Navigation 2.8+).
@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.
// 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.
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. A flag debugInspectorInfo e o CompositionTracer auxiliam 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.
@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()
// 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.
Conclusão
Essas 20 perguntas cobrem os fundamentos que todo desenvolvedor Android precisa dominar para uma entrevista sobre Jetpack Compose. A seguir, um checklist de revisão:
- ✅ Compreender a recomposição e seu comportamento otimista
- ✅ Dominar
remember,rememberSaveableederivedStateOf - ✅ Aplicar state hoisting sistematicamente
- ✅ Conhecer os side effects (
LaunchedEffect,DisposableEffect,SideEffect) - ✅ Otimizar a performance (classes estáveis, keys, lambdas)
- ✅ Estruturar telas com ViewModel + UiState + Events
- ✅ Testar composables com
ComposeTestRule - ✅ Dominar a interoperabilidade Compose/Views
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Tags
Compartilhar
Artigos relacionados

MVVM vs MVI no Android: Qual Arquitetura Escolher em 2026?
Comparação aprofundada entre MVVM e MVI no Android: vantagens, limitações, casos de uso e um guia prático para escolher a arquitetura certa em 2026.

Dominando Kotlin Coroutines: Guia Completo 2026
Aprenda a dominar Kotlin coroutines para desenvolvimento Android: funções suspend, scopes, dispatchers e padrões avançados.

SQL para Analistas de Dados: Window Functions, CTEs e Consultas Avancadas
Guia completo de SQL avancado com window functions, CTEs e padroes de consulta analitica. Tecnicas essenciais para entrevistas de analise de dados e projetos reais.