Marcadores e estados de erro
Carregamento e falhas tratados com elegância.
Marcadores e estados de erro é uma aula grátis de Android Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Android Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Android Academy inclui 4 aulas no total.
O carregamento não é instantâneo
Imagens da rede levam tempo. Durante esse intervalo, a interface não mostra... nada, a menos que você trate essa situação. E, às vezes, a imagem nunca chega: a URL está incorreta, o servidor está indisponível ou o usuário está offline.
Um aplicativo bem elaborado exibe um placeholder durante o carregamento e um fallback quando algo falha, em vez de deixar um espaço vazio ou causar uma falha. O Coil facilita ambos.
Nesta lição, você adicionará placeholders, imagens de erro e uma interface personalizada para cada estado.
Um placeholder simples
A maneira mais rápida de adicionar um placeholder é passar um drawable para AsyncImage por meio do parâmetro placeholder, usando painterResource. O Coil o exibe até que o carregamento da imagem real termine.
Use um drawable neutro e leve, como uma caixa cinza ou a silhueta de um logotipo.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun ImageWithPlaceholder(url: String) {
AsyncImage(
model = url,
contentDescription = null,
placeholder = painterResource(R.drawable.placeholder_gray)
)
}Adicionando uma imagem de erro
O parâmetro error exibe um drawable alternativo quando a solicitação falha. Há também fallback, usado especificamente quando o model é nulo, ou seja, quando não há dados para carregar.
Definir os três — placeholder, error e fallback — cobre todos os estados visuais com quase nenhum código.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun RobustImage(url: String?) {
AsyncImage(
model = url,
contentDescription = null,
placeholder = painterResource(R.drawable.placeholder_gray),
error = painterResource(R.drawable.image_broken),
fallback = painterResource(R.drawable.no_image)
)
}Placeholder por meio de ImageRequest
Você também pode definir esses estados no próprio ImageRequest. Isso é útil quando você cria a solicitação uma vez e a reutiliza, ou quando precisa definir outras opções da solicitação ao mesmo tempo.
As funções no nível da solicitação recebem identificadores de recursos drawable.
import android.content.Context
import coil3.request.ImageRequest
import coil3.request.crossfade
import coil3.request.error
import coil3.request.placeholder
fun buildRequest(context: Context, url: String) =
ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.placeholder(R.drawable.placeholder_gray)
.error(R.drawable.image_broken)
.build()Interface personalizada com SubcomposeAsyncImage
Drawables são suficientes, mas às vezes você quer um indicador giratório, um efeito de brilho ou um texto para cada estado. SubcomposeAsyncImage permite fornecer componentes reais para loading, error e success.
Ele é mais flexível, mas também um pouco mais pesado; por isso, prefira placeholders drawable em listas longas e reserve a subcomposição para imagens principais.
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import coil3.compose.SubcomposeAsyncImage
@Composable
fun HeroImage(url: String) {
SubcomposeAsyncImage(
model = url,
contentDescription = "Hero",
loading = { CircularProgressIndicator() },
error = { Text("Could not load image") }
)
}Lendo o estado diretamente
Para ter controle total, use a API rememberAsyncImagePainter e examine seu state. O estado é um tipo selado com os casos Loading, Success, Error e Empty.
Isso permite controlar seu próprio layout, suas animações ou suas análises com base no que aconteceu.
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import coil3.compose.AsyncImagePainter
import coil3.compose.rememberAsyncImagePainter
@Composable
fun StateAwareImage(url: String) {
val painter = rememberAsyncImagePainter(model = url)
val state = painter.state.collectAsState().value
when (state) {
is AsyncImagePainter.State.Loading -> { /* show spinner */ }
is AsyncImagePainter.State.Error -> { /* show error UI */ }
else -> Image(painter = painter, contentDescription = null)
}
}Exibindo um placeholder com brilho
Um padrão popular é o efeito de brilho: um gradiente cinza animado que sugere que o conteúdo está a caminho. Você pode criar uma versão simples com uma caixa de cor sólida atrás do estado de carregamento.
Aqui, usamos SubcomposeAsyncImage com uma caixa colorida durante o carregamento. Troque a caixa por uma biblioteca de brilho se quiser o efeito animado.
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import coil3.compose.SubcomposeAsyncImage
@Composable
fun ShimmerImage(url: String) {
SubcomposeAsyncImage(
model = url,
contentDescription = null,
loading = {
androidx.compose.foundation.layout.Box(
Modifier.fillMaxSize()
.background(MaterialTheme.colorScheme.surfaceVariant)
)
}
)
}Lidando com um modelo nulo ou vazio
E se você ainda não tiver uma URL, por exemplo, no caso de um usuário sem foto de perfil? Passar null como modelo ativa o drawable fallback — ou error, se nenhum fallback estiver definido.
Isso evita tratar o valor nulo de forma especial no seu próprio código; deixe o Coil exibir um avatar padrão.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun ProfilePicture(photoUrl: String?) {
// If photoUrl is null, Coil shows the fallback default avatar.
AsyncImage(
model = photoUrl,
contentDescription = "Profile picture",
fallback = painterResource(R.drawable.default_avatar),
error = painterResource(R.drawable.default_avatar)
)
}Escutando erros com um listener
Às vezes, você precisa reagir no código quando um carregamento falha, por exemplo, para registrá-lo ou tentar novamente. ImageRequest.Builder aceita um listener com retornos de chamada de sucesso e de erro.
Mantenha esses retornos de chamada leves; eles são executados na thread principal.
import android.content.Context
import android.util.Log
import coil3.request.ImageRequest
fun loggedRequest(context: Context, url: String) =
ImageRequest.Builder(context)
.data(url)
.listener(
onError = { _, result ->
Log.e("Coil", "Load failed", result.throwable)
},
onSuccess = { _, _ ->
Log.d("Coil", "Loaded $url")
}
)
.build()Aplicando tonalidade e filtros de cor
Drawables de placeholder e de erro geralmente ficam melhores quando recebem uma tonalidade que combina com seu tema. Você pode aplicar um ColorFilter a uma AsyncImage, o que é ótimo para fallbacks de ícones monocromáticos que devem se adaptar aos modos claro e escuro.
Aplique o filtro apenas à aparência de placeholder ou de erro desejada; para fotos reais, normalmente você não o utiliza.
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun TintedFallback(url: String?) {
AsyncImage(
model = url,
contentDescription = null,
error = painterResource(R.drawable.ic_image_placeholder),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant)
)
}Um componente de imagem resiliente
Vamos montar um componente reutilizável que lida corretamente com todos os estados: um plano de fundo durante o carregamento, um fallback de erro, uma transição gradual e recorte. Adicione-o a uma lista e nunca mais se preocupe com espaços vazios.
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade
@Composable
fun ResilientImage(url: String?, size: Int = 96) {
val context = LocalContext.current
AsyncImage(
model = ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
placeholder = painterResource(R.drawable.placeholder_gray),
error = painterResource(R.drawable.image_broken),
fallback = painterResource(R.drawable.default_avatar),
modifier = Modifier.size(size.dp)
)
}Verificação rápida
Você passa model = null para AsyncImage. Qual drawable o Coil exibe por padrão?
Recapitulação
Você tornou o carregamento de imagens mais seguro. Pontos principais:
placeholderé exibido durante o carregamento,errorem caso de falha efallbackquando o modelo é nulo.- Defina-os em
AsyncImageou emImageRequest. SubcomposeAsyncImagepermite fornecer composables reais (indicador de carregamento, efeito de brilho) para cada estado.rememberAsyncImagePainterexpõe ostatebruto para controle total.- Use um
listenerda solicitação para registrar ou reagir aos resultados do carregamento.
A seguir: armazenamento em cache e desempenho, para que as imagens carreguem instantaneamente na segunda vez.
Aprenda Kotlin com um tutor de IA — grátis
Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.
- Cursos
- 36
- Aulas
- 152
Perguntas Frequentes
A aula “Marcadores e estados de erro” é grátis?
Sim — o texto completo de “Marcadores e estados de erro” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Android Academy, atualize para CoddyKit PRO. O curso de Android Academy inclui 4 aulas no total.
O que vou aprender em “Marcadores e estados de erro”?
Carregamento e falhas tratados com elegância. Você pratica Android Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Android Academy?
Nenhuma experiência prévia é necessária. Android Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Marcadores e estados de erro”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Android Academy?
Sim. Cada aula de Android Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Carregando imagens com Coil
- Marcadores e estados de erro
- Cache e desempenho
- Reproduzindo áudio e vídeo