---
title: "Glance Widgets no Android com Kotlin em 2026 | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/glance-widgets-android-kotlin-2026/"
markdown_url: "https://kotlin.dev.br/blog/glance-widgets-android-kotlin-2026.MD"
description: "Aprenda a criar widgets Android modernos com Kotlin, Jetpack Glance, Compose, WorkManager, estados, atualização periódica e boas práticas de produto."
date: "2026-06-15"
author: "Karina Melo"
---

# Glance Widgets no Android com Kotlin em 2026 | Kotlin Brasil

Aprenda a criar widgets Android modernos com Kotlin, Jetpack Glance, Compose, WorkManager, estados, atualização periódica e boas práticas de produto.


Widgets Android parecem pequenos detalhes de interface, mas costumam ser uma das formas mais fortes de retenção quando o produto tem informação recorrente: tarefas do dia, saldo, previsão, treino, entrega, hábito, checklist, métricas ou status de sincronização. Em 2026, criar widgets com Kotlin ficou mais natural graças ao **Jetpack Glance**, que aproxima a experiência de desenvolvimento do modelo declarativo do Compose sem exigir que você escreva tudo diretamente com `RemoteViews`.

Glance não é "Compose completo na tela inicial". Ele é uma camada declarativa para gerar widgets compatíveis com o sistema de AppWidget do Android. Isso significa que você escreve código com uma aparência familiar para quem usa Jetpack Compose, mas ainda precisa respeitar as limitações da tela inicial: poucas interações, atualização controlada, layout mais restrito, tamanho variável e cuidado grande com bateria.

Este guia mostra como pensar um widget Android moderno com Kotlin: quando ele faz sentido, como organizar módulos, como guardar estado, como atualizar dados, onde entra o WorkManager e quais erros costumam quebrar a experiência. Se você está montando a base do app, leia também [WorkManager com Kotlin no Android](/blog/workmanager-kotlin-android-2026/), [DataStore Preferences com Kotlin](/tutoriais/datastore-preferences-kotlin/) e [Modularização Android com Kotlin e Compose](/blog/modularizacao-android-kotlin-compose-2026/).

## Quando um widget vale a pena

O erro comum é criar widget porque o app "precisa de presença" na tela inicial. Presença sozinha não sustenta uso. Um widget vale a pena quando ele responde a uma pergunta frequente sem exigir que a pessoa abra o app.

Bons casos de uso incluem:

- próxima tarefa ou próximo evento;
- resumo financeiro simples;
- status de entrega ou pedido;
- previsão do tempo resumida;
- hábito do dia;
- contador de estudos, treino ou foco;
- atalhos para ações recorrentes;
- métrica operacional de um app interno.

Casos ruins incluem widgets que apenas repetem a home do aplicativo, banners promocionais sem utilidade ou telas que dependem de muita navegação. A tela inicial não é lugar para fluxo complexo. Ela funciona melhor como painel de contexto e porta de entrada para uma ação específica.

## Estrutura básica com Glance

Um widget Glance normalmente combina três peças: uma classe `GlanceAppWidget`, um receiver `GlanceAppWidgetReceiver` e alguma fonte de estado. A classe do widget descreve o conteúdo. O receiver conecta o widget ao sistema Android. A fonte de estado alimenta o layout.

Um exemplo mínimo fica assim:

```kotlin
class HabitoDoDiaWidget : GlanceAppWidget() {
    override suspend fun provideGlance(context: Context, id: GlanceId) {
        provideContent {
            GlanceTheme {
                Column(
                    modifier = GlanceModifier
                        .fillMaxSize()
                        .padding(16.dp),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalAlignment = Alignment.CenterHorizontally,
                ) {
                    Text(
                        text = "Hábito de hoje",
                        style = TextStyle(fontWeight = FontWeight.Bold),
                    )
                    Spacer(GlanceModifier.height(8.dp))
                    Text(text = "Beber água antes das 10h")
                }
            }
        }
    }
}

class HabitoDoDiaWidgetReceiver : GlanceAppWidgetReceiver() {
    override val glanceAppWidget: GlanceAppWidget = HabitoDoDiaWidget()
}
```

O manifesto aponta para o receiver e para o arquivo de configuração do AppWidget. Essa parte continua parecida com widgets tradicionais porque o Android precisa descobrir o widget antes de executar seu código Kotlin.

```xml
<receiver
    android:name=".widgets.HabitoDoDiaWidgetReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/habito_do_dia_widget_info" />
</receiver>
```

## Layout não é tela Compose normal

A maior armadilha para quem vem do Compose é esquecer que Glance gera um widget de tela inicial, não uma `Activity`. Você não deve esperar a mesma liberdade de layout, animação, navegação, gesto ou estado efêmero.

Em vez de tentar reproduzir uma tela inteira, desenhe uma versão reduzida:

- uma informação principal;
- uma informação secundária;
- uma ação clara;
- um estado de erro ou vazio;
- um visual legível em tamanhos pequenos.

Pense em blocos como `Column`, `Row`, `Text`, `Image`, `Button` e `Spacer`. Evite densidade excessiva. Um widget pequeno precisa ser entendido em menos de dois segundos.

Para apps que já usam design system em Compose, vale criar uma camada de tokens compartilhados: nomes de cores, espaçamentos, textos e decisões de conteúdo. Mas não tente reutilizar todos os componentes Compose diretamente. O ideal é compartilhar intenção visual, não acoplar o widget ao mesmo componente da tela interna.

## Estado com DataStore ou banco local

Widget bom precisa carregar rápido e funcionar mesmo quando o app não está aberto. Por isso, ele deve ler de uma fonte local simples. Para preferências e pequenos resumos, DataStore costuma ser suficiente. Para listas, histórico ou dados estruturados, Room ou SQLDelight fazem mais sentido.

Um padrão saudável é manter um `WidgetStateRepository` separado da UI principal:

```kotlin
data class ResumoWidget(
    val titulo: String,
    val detalhe: String,
    val atualizadoEm: Instant,
)

class WidgetStateRepository(
    private val dataStore: DataStore<Preferences>,
) {
    val resumo: Flow<ResumoWidget> = dataStore.data.map { prefs ->
        ResumoWidget(
            titulo = prefs[stringPreferencesKey("widget_titulo")] ?: "Sem dados",
            detalhe = prefs[stringPreferencesKey("widget_detalhe")] ?: "Abra o app para configurar",
            atualizadoEm = Instant.fromEpochMilliseconds(
                prefs[longPreferencesKey("widget_atualizado_em")] ?: 0L,
            ),
        )
    }
}
```

Esse repositório não deve depender de `Activity`, `NavController` nem estado de tela. Ele precisa ser fácil de chamar a partir do app, de um worker ou do próprio widget. Em projetos modularizados, uma organização comum é `:feature:widget` dependendo de `:core:model`, `:core:datastore` e, quando necessário, `:core:sync`.

## Atualização com WorkManager

Widgets não devem acordar rede a todo momento. O sistema limita atualizações e o usuário percebe quando um app consome bateria por causa de painel decorativo. A regra prática é sincronizar dados no app ou em background controlado, salvar um resumo local e pedir atualização do widget depois.

WorkManager encaixa bem quando o dado pode ser atualizado em janela flexível:

```kotlin
class AtualizarWidgetWorker(
    context: Context,
    params: WorkerParameters,
    private val repository: WidgetStateRepository,
) : CoroutineWorker(context, params) {
    override suspend fun doWork(): Result {
        return runCatching {
            repository.sincronizarResumoRemoto()
            HabitoDoDiaWidget().updateAll(applicationContext)
        }.fold(
            onSuccess = { Result.success() },
            onFailure = { Result.retry() },
        )
    }
}
```

Para dados urgentes, como entrega em rota ou alerta crítico, avalie notificações em vez de forçar widgets como canal de tempo real. Widget é melhor para contexto persistente; notificação é melhor para evento que exige atenção.

## Clique e navegação

A interação mais importante de muitos widgets é abrir o app já na tela certa. Em Glance, você pode usar ações para iniciar uma Activity ou enviar um broadcast. O widget não deve exigir que o usuário abra a home e procure manualmente o próximo passo.

```kotlin
Text(
    text = "Ver detalhes",
    modifier = GlanceModifier.clickable(
        actionStartActivity<MainActivity>(
            parameters = actionParametersOf(
                ActionParameters.Key<String>("destino") to "habitos",
            ),
        ),
    ),
)
```

No app, trate esse destino como deep link interno. Se você já usa App Links ou Navigation Compose, mantenha uma rota estável para o widget. Isso evita que uma refatoração de navegação quebre atalhos instalados na tela inicial.

## Estados que precisam aparecer

Um widget de produção deve ter pelo menos quatro estados planejados:

1. **Sem configuração:** usuário adicionou o widget, mas ainda não escolheu conta, hábito ou lista.
2. **Com dados:** resumo principal está pronto.
3. **Sem conexão ou erro:** último dado válido existe, mas a atualização falhou.
4. **Conta desconectada:** usuário saiu do app ou perdeu sessão.

Não esconda falha como se fosse dado atualizado. Mostre algo como "Atualizado ontem" ou "Abra o app para reconectar". Transparência evita decisões erradas e reduz suporte.

## Testes e validação prática

Testar widget é menos confortável que testar ViewModel, mas ainda dá para validar bastante coisa. Separe lógica de seleção de dados em classes puras, teste o repositório local, teste o worker e faça uma checklist manual em emulador ou aparelho real.

Antes de publicar, valide:

- widget pequeno, médio e grande;
- tema claro e escuro;
- fonte aumentada nas configurações de acessibilidade;
- usuário deslogado;
- modo avião;
- atualização depois de abrir o app;
- atualização depois de worker;
- clique abrindo a tela correta;
- remoção e adição do widget novamente.

Também vale medir uso. Se o widget existe para aumentar retenção, acompanhe eventos como adição, clique e atualização bem-sucedida. Um app Kotlin que usa analytics já pode padronizar isso em um módulo `:core:analytics` sem espalhar chamadas pela UI.

## Erros comuns

O primeiro erro é colocar informação demais. Widget cheio vira ruído visual. O segundo é buscar rede diretamente durante a renderização, criando lentidão e falhas intermitentes. O terceiro é não pensar em sessão expirada: o widget continua mostrando dado antigo e passa falsa confiança.

Outro erro é tratar Glance como substituto de uma tela Compose. Se você precisa de lista longa, filtros, abas e formulário, abra o app. O widget deve ser o resumo e o atalho, não o produto inteiro.

Por fim, cuidado com compatibilidade. Diferentes launchers podem renderizar detalhes de forma ligeiramente diferente. Teste pelo menos no launcher padrão do emulador e em um aparelho real quando o widget for parte importante da proposta de valor.

## Onde Glance entra na arquitetura

Em um app Android moderno, o widget deve ficar próximo da camada de experiência, mas longe das telas internas. Uma estrutura possível:

```text
:app
:feature:home
:feature:widget
:core:model
:core:datastore
:core:sync
:core:analytics
```

O módulo `:feature:widget` renderiza Glance e traduz estado local para UI de tela inicial. O `:core:sync` busca dados. O `:core:datastore` ou `:core:database` persiste o resumo. O `:core:analytics` mede clique e atualização. Isso evita que o widget dependa de ViewModels de tela ou que o app principal precise conhecer detalhes de AppWidget.

Para times que trabalham também com backend Kotlin, a lógica de resumo pode ser pensada como contrato de produto: o servidor entrega um payload pequeno e estável, e o Android decide como mostrá-lo no widget. Quando o mesmo produto tem jobs, sincronização ou automações fora do Android, vale comparar como outras stacks estruturam workers. O guia de <a href="https://python.dev.br/blog/celery-python-filas-tarefas-background/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Celery em Python para tarefas em background</a> ajuda a pensar em filas e retries do lado servidor sem misturar isso com a camada mobile.

## Conclusão

Glance torna widgets Android muito mais agradáveis para equipes Kotlin que já pensam de forma declarativa, mas a disciplina de produto continua sendo a parte mais importante. Um widget útil não é uma miniatura do app. Ele é uma resposta rápida, confiável e acionável para uma necessidade recorrente.

Comece pequeno: um resumo local, uma ação clara, atualização controlada e estados honestos. Depois conecte com WorkManager, DataStore, analytics e navegação profunda. Se o widget realmente reduz atrito, ele vira um ponto de contato diário com o usuário. Se ele só replica tela, vira decoração. Em Android com Kotlin, essa diferença aparece rápido na retenção, na bateria e na qualidade percebida do produto.
