Introdução a biblioteca Kivy
O Kivy é uma biblioteca/framework Python para criar aplicações gráficas. Ela permite criar interfaces para desktop, Android, iOS, Linux, macOS, Windows e até alguns ambientes embarcados usando uma base de código em Python. A própria documentação oficial descreve Kivy como uma biblioteca open source para desenvolvimento rápido de aplicações com interfaces modernas, incluindo interfaces multi-touch.
Em termos simples, Kivy serve para criar programas com janela, botões, textos, campos de entrada, listas, telas, animações e eventos de toque ou clique. Apesar de todos os benefícios do Kivy, ele não é recomendado para todos os cenários de uso. Abaixo deixo alguns exemplos onde Kivy talvez não seja a melhor escolha:
- Aplicativo que precisa parecer 100% nativo no Android ou iOS
- Aplicativo com UI muito parecida com apps modernos de banco
- Sistema web acessado por navegador
- Aplicação corporativa grande com muitos formulários complexos
- App que depende fortemente de APIs nativas específicas do Android/iOS
O Kivy não é apenas uma biblioteca de widgets. Ele é um framework completo com loop de eventos, sistema gráfico, linguagem declarativa própria, widgets, propriedades reativas, sistema de entrada, gerenciamento de telas e ferramentas de empacotamento.
Funcionamento interno
Uma aplicação Kivy não funciona como um script comum que executa linha por linha e termina. Uma aplicação gráfica funciona baseada em eventos, por isso, o loop é chamado de event loop.
No Kivy, vários objetos importantes são baseados no conceito de eventos. Ele permite registrar tipos de eventos e dispará-los para objetos. Isso significa que, internamente, Kivy trabalha assim:
- Usuário toca no botão
- Kivy recebe evento de entrada
- Kivy identifica qual widget foi atingido
- Kivy dispara evento on_press
- Seu código é chamado
- Você altera uma propriedade
- Kivy percebe a alteração
- A interface é atualizada
Um ponto central é o sistema de propriedades. Em Python comum seria mais ou menos assim:
self.nome = "fulano"
Isso apenas guarda um valor. Mas em em Kivy, o correto é declarar assim:
from kivy.properties import StringProperty
nome = StringProperty("fulano")
Isso cria uma propriedade observável. Quando o valor muda, Kivy consegue disparar eventos automaticamente. A documentação oficial explica que as Properties do Kivy implementam o padrão Observer, permitindo usar bind() para chamar funções quando um valor muda.
Esse detalhe é muito importante, já que o Kivy não atualiza a tela porque "adivinha" tudo. Ele tem um sistema de propriedades observáveis que conecta dados, eventos e interface.
Criar ambiente
Vamos ver como iniciar um ambiente para começar a trabalhar com Kivy.
# Crie o diretório do projeto:
mkdir -p ~/kivy/
# Entre no diretório criado:
cd ~/kivy/
# Crie o ambiente virtual chamado '.env':
python3 -m venv financy
# Para ativar o ambiente, podemos usar o comando abaixo:
source financy/bin/activate
# Agora instale a biblioteca do Kivy:
pip install kivy
Se quiser verificar a versão do Kivy, pode usar o comando abaixo:
python -c "import kivy; print(kivy.__version__)"
[INFO ] [Logger ] Record log in /home/fulano/.kivy/logs/kivy_26-05-31_0.txt
[INFO ] [Kivy ] v2.3.1
[INFO ] [Kivy ] Installed at "/home/fulano/kivy/financy/lib/python3.12/site-packages/kivy/__init__.py"
[INFO ] [Python ] v3.12.3 (main, Mar 23 2026, 19:04:32) [GCC 13.3.0]
[INFO ] [Python ] Interpreter at "/home/fulano/kivy/financy/bin/python"
[INFO ] [Logger ] Purge log fired. Processing...
[INFO ] [Logger ] Purge finished!
2.3.1
Nivelamento
Para aproveitar melhor o aprendizado de Kivy, é importante que você já tenha alguns conhecimentos prévios. Ao longo do conteúdo, vou apresentar uma breve introdução a esses conceitos, mas sem entrar em muitos detalhes, pois o foco principal será o uso do Kivy.
Básico de orientação a Objetos
Você pode conferir sobre Orientação a Objetos clicando aqui.
Básico de orientação a eventos em Python
Quando você escreve um programa simples em Python, normalmente ele segue uma sequência direta, exemplo:
nome = input("Digite seu nome: ")
print("Olá,", nome)
Esse fluxo é previsível, já que o código é executado, pede o nome, espera o usuário digitar, mostra a mensagem e termina. Esse tipo de programa é chamado, de forma geral, de programa sequencial.
Agora pense em uma interface gráfica, como um app feito com Kivy. O programa não pode simplesmente começar, executar tudo e terminar. Ele precisa ficar vivo esperando ações do usuário.
O usuário pode clicar em um botão, digitar em um campo, mudar de tela, arrastar algo, fechar a janela, alterar uma informação dentre outras tarefas possíveis. O programa não sabe antecipadamente qual dessas coisas vai acontecer primeiro.
Então o modelo muda, sendo mais ou menos assim:
- inicie
- mostre a interface
- espere algo acontecer
- quando acontecer, reaja
- volte a esperar
Esse é o coração da orientação a eventos. Um evento é algo que acontece no sistema e que pode gerar uma reação do programa. A orientação a eventos é um estilo de programação em que o programa é organizado em torno desses acontecimentos.
Você precisa entender que o programa não roda mais apenas de cima para baixo como se fosse um script. Em uma aplicação orientada a eventos, boa parte do seu código fica "parado", esperando ser chamado quando algo acontecer.
O lado bom é que em frameworks gráficos, o próprio framework já possui um loop de eventos. No Kivy, temos uma mistura de dois mundos, a mistura de orientação a objetos com orientação a eventos.
Outra observação importante é que em um aplicativo Kivy, o modelo mais comum é ter uma classe principal responsável por iniciar a aplicação e outras classes menores representando cada tela gráfica do sistema.
A classe principal, que herda de App, funciona como o ponto de entrada do programa. Ela é responsável por iniciar o Kivy e carregar a estrutura principal da interface. Quando o aplicativo possui várias telas, normalmente usamos o ScreenManager para controlar qual tela será exibida no momento. Cada tela é representada por uma classe própria, geralmente herdando de Screen.
Dessa forma, cada tela fica responsável apenas pelo seu próprio comportamento. Por exemplo, uma tela de resumo mostra os dados gerais, uma tela de renda trata o cadastro da renda, uma tela de gastos fixos trata os gastos recorrentes e assim por diante.
Esse modelo mistura orientação a objetos com orientação a eventos. As classes organizam as partes do aplicativo, enquanto os eventos definem o que acontece quando o usuário interage com a interface, como clicar em um botão, preencher um campo ou mudar de tela.
Essa separação deixa o código mais organizado, facilita a manutenção e evita que toda a lógica do aplicativo fique concentrada em uma única classe grande.
Básico sobre Layouts
Em Kivy, o layout é o componente responsável por organizar outros widgets na tela. Já um Widget é qualquer elemento visual, como:
- Label
- Button
- TextInput
- Image
- Slider
- Screen
- BoxLayout
- GridLayout
Um botão é um widget, um texto é um widget, um campo de entrada é um widget e um layout também é um widget, mas com uma função especial. A função do layout é responder a pergunta: "Onde cada coisa deve aparecer na tela?".
Sem layout, você teria que posicionar tudo manualmente, informando coordenadas, largura e altura. Isso até funciona, mas é ruim para telas diferentes, como desktop, celular pequeno, celular grande e tablet. O layout resolve esse problema organizando os elementos automaticamente.
Primeiro exemplo absoluto
Vamos começar com um app mínimo usando BoxLayout.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class TelaPrincipal(BoxLayout):
pass
class MeuApp(App):
def build(self):
return TelaPrincipal()
MeuApp().run()
<TelaPrincipal>:
orientation: "vertical"
Label:
text: "Título"
Button:
text: "Clique aqui"
Resultado aproximado:
+-----------------------------+
| Título |
| |
+-----------------------------+
| Clique aqui |
| |
+-----------------------------+
O BoxLayout dividiu a tela entre o Label e o Button.
.kv?Nós podemos ter um único .kv ou vários arquivos .kv. Os dois modelos são válidos, o que muda é a complexidade e a preferência do desenvolvedor.
Para começar, o mais simples é usar um único arquivo .kv. Depois, quando o projeto crescer, você pode separar por tela ou por componente.
E como o Kivy sabe qual .kv usar? Porque o Kivy tem uma regra automática para definir isso. Ele sempre sabe o nome do arquivo porque ele usa a classe principal como base. Imagine que temos uma classe igual ao exemplo abaixo:
class FinancyApp(App):
pass
o Kivy procura automaticamente um arquivo chamado financy.kv. Para isso, ele segue a seguinte ordem:
- Obtém o nome da classe
FinancyApp; - Remove o
Appdo nome da classe; - Nesse ponto, fica apenas com
Financy. - Converte tudo para minúsculo;
- Adiciona a extensão
.kve procura pelo arquivofinancy.kv.
Na classe TelaPrincipal, dissemos que ela é um BoxLayout, ou seja, nossa classe herdou os atributos e métodos da classe BoxLayout. Com isso, a TelaPrincipal agora é um layout.
class TelaPrincipal(BoxLayout):
pass
Já no .kv, dissemos que os elementos dentro dela serão organizados na vertical.
<TelaPrincipal>:
orientation: "vertical"
Depois adicionamos um Label:
Label:
text: "Título"
E por fim, um botão:
Button:
text: "Clique aqui"
Como a orientação é vertical, o Kivy coloca um embaixo do outro.
BoxLayout
O BoxLayout é o layout mais importante para começar. Ele organiza widgets em uma única direção, podendo ser: vertical ou horizontal.
Vertical
<TelaPrincipal>:
orientation: "vertical"
Button:
text: "Botão 1"
Button:
text: "Botão 2"
Button:
text: "Botão 3"
O resultado é mais ou menos assim:
+----------------+
| Botão 1 |
+----------------+
| Botão 2 |
+----------------+
| Botão 3 |
+----------------+
Horizontal
<TelaPrincipal>:
orientation: "horizontal"
Button:
text: "Botão 1"
Button:
text: "Botão 2"
Button:
text: "Botão 3"
O resultado é mais ou menos assim:
+----------+----------+----------+
| Botão 1 | Botão 2 | Botão 3 |
+----------+----------+----------+
Quando usar BoxLayout?
A ideia é usar ele quando você quer organizar elementos em linha ou coluna. Como:
- formulário vertical
- rodapé com botões lado a lado
- cabeçalho com ícone e texto
- tela com conteúdo em cima e menu embaixo
Padding
O padding é a margem interna do layout, exemplo sem padding:
<TelaPrincipal>:
orientation: "vertical"
Button:
text: "Salvar"
O resultado é um botão que encosta nas bordas:
+-----------------------------+
| Salvar |
+-----------------------------+
Agora, se adicionarmos um padding:
<TelaPrincipal>:
orientation: "vertical"
padding: 20
Button:
text: "Salvar"
O resultado é um layout com um espaço interno de 20 pixels em volta do conteúdo:
+-----------------------------+
| |
| +---------------------+ |
| | Salvar | |
| +---------------------+ |
| |
+-----------------------------+
Você também pode definir valores separados:
padding: 20, 10, 20, 10
A ordem é:
esquerda, topo, direita, baixo
Spacing
O spacing é o espaço entre os widgets, exemplo sem spacing:
<TelaPrincipal>:
orientation: "vertical"
Button:
text: "Botão 1"
Button:
text: "Botão 2"
Resultado:
+----------------+
| Botão 1 |
+----------------+
| Botão 2 |
+----------------+
Exemplo com spacing:
<TelaPrincipal>:
orientation: "vertical"
spacing: 10
Button:
text: "Botão 1"
Button:
text: "Botão 2"
Resultado:
+----------------+
| Botão 1 |
+----------------+
+----------------+
| Botão 2 |
+----------------+
Size_hint
Agora vem uma parte muito importante de projeto com o Kivy, que é o tamanho dos widgets. Esse tamanho normalmente é controlado por size_hint, ele diz quanto do espaço disponível o widget deve ocupar proporcionalmente, exemplo:
Button:
text: "Salvar"
size_hint: 1, 1
Isso significa: ocupe 100% da largura disponível e ocupe 100% da altura disponível. Mas normalmente usamos size_hint_x e size_hint_y para que fique visualmente bonito.
Size_hint_y
Imagine o seguinte:
<TelaPrincipal>:
orientation: "vertical"
Label:
text: "Título"
Button:
text: "Salvar"
Por padrão, os dois dividem o espaço vertical. Agora imagine que você quer o título com altura fixa.
<TelaPrincipal>:
orientation: "vertical"
Label:
text: "Título"
size_hint_y: None
height: 60
Button:
text: "Salvar"
Aqui você está dizendo:
- Label não deve usar altura proporcional
- Label deve ter altura fixa de 60
- Button fica com o espaço restante
Mas, por que usar size_hint_y: None? Isso é muito comum porque se você definir apenas height: 60, o Kivy pode ignorar essa altura, porque o size_hint_y ainda está controlando o tamanho. Então, quando quiser altura fixa, use:
size_hint_y: None
height: 60
Quando quiser largura fixa:
size_hint_x: None
width: 120
Essa é uma das armadilhas mais comuns do Kivy.
Exemplo de layout com cabeçalho, conteúdo e rodapé
Vamos ver um modelo que é muito usado em app mobile. Ele precisa ter cabeçalho, conteúdo e rodapé, como no desenho abaixo:
+-----------------------------+
| Cabeçalho |
+-----------------------------+
| |
| Conteúdo |
| |
+-----------------------------+
| Rodapé |
+-----------------------------+
No Kivy, devemos fazer assim:
<LayoutPrincipal>:
orientation: "vertical"
BoxLayout:
size_hint_y: None
height: 70
Label:
text: "Financy"
BoxLayout:
orientation: "vertical"
Label:
text: "Conteúdo da tela"
BoxLayout:
size_hint_y: None
height: 70
Button:
text: "Resumo"
Button:
text: "Renda"
Button:
text: "Fixos"
Button:
text: "Periódicos"
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class LayoutPrincipal(BoxLayout):
pass
class FinancyApp(App):
def build(self):
return LayoutPrincipal()
FinancyApp().run()
Esse é um padrão real de aplicativo.
Layouts dentro de layouts
Em Kivy, normalmente é montado telas combinando layouts, exemplo:
BoxLayout vertical
├── Cabeçalho horizontal
├── Conteúdo vertical
└── Rodapé horizontal
Em .kv, seria mais ou menos assim:
<LayoutPrincipal>:
orientation: "vertical"
BoxLayout:
orientation: "horizontal"
size_hint_y: None
height: 70
Label:
text: "Ícone"
Label:
text: "Financy"
BoxLayout:
orientation: "vertical"
padding: 20
spacing: 10
Label:
text: "Saldo disponível"
Label:
text: "R$ 409,80"
Button:
text: "Adicionar gasto"
BoxLayout:
orientation: "horizontal"
size_hint_y: None
height: 70
Button:
text: "Resumo"
Button:
text: "Renda"
Button:
text: "Fixos"
Button:
text: "Periódicos"
Essa composição é normal. Você não precisa achar um layout que resolva tudo, para isso, você monta blocos pequenos.
GridLayout
O GridLayout organiza widgets em linhas e colunas, exemplo:
<TelaPrincipal>:
cols: 2
Button:
text: "1"
Button:
text: "2"
Button:
text: "3"
Button:
text: "4"
Resultado:
+-------+-------+
| 1 | 2 |
+-------+-------+
| 3 | 4 |
+-------+-------+
Para usar no Python é preciso importar o GridLayout:
from kivy.uix.gridlayout import GridLayout
class TelaPrincipal(GridLayout):
pass
Ou dentro de outro layout:
BoxLayout:
orientation: "vertical"
GridLayout:
cols: 2
Button:
text: "Renda"
Button:
text: "Gastos"
Button:
text: "Fixos"
Button:
text: "Resumo"
AnchorLayout
O AnchorLayout serve para ancorar um widget em uma região, exemplo:
- centro
- topo
- baixo
- esquerda
- direita
No kivy seria mais ou menos assim:
<TelaPrincipal>:
anchor_x: "center"
anchor_y: "center"
Button:
text: "Centralizado"
size_hint: None, None
size: 200, 60
Resultado:
+-----------------------------+
| |
| |
| [ Centralizado ] |
| |
| |
+-----------------------------+
FloatLayout
O FloatLayout permite posicionar widgets de forma mais livre, usando proporções ou posições, exemplo:
<TelaPrincipal>:
Button:
text: "Botão"
size_hint: 0.5, 0.1
pos_hint: {"center_x": 0.5, "center_y": 0.5}
Aqui o size_hint: 0.5, 0.1, significa que a largura é de 50% da tela e a altura é de 10% da tela. Já o pos_hint: {"center_x": 0.5, "center_y": 0.5}, significa que deve ficar centralizado no meio da tela.
RelativeLayout
O RelativeLayout parece com o FloatLayout, mas trabalha com coordenadas relativas ao próprio layout. Ele é útil em casos mais específicos, principalmente quando você quer posicionar elementos em relação a um container. Para começar, os exemplos com BoxLayout, GridLayout, AnchorLayout e FloatLayout resolvem quase 100% do casos.
StackLayout
O StackLayout coloca os widgets em sequência e quebra linha quando não cabe mais. É parecido com o comportamento de elementos que vão "empilhando", exemplo:
<TelaPrincipal>:
orientation: "lr-tb"
Button:
text: "Item 1"
size_hint: None, None
size: 120, 50
Button:
text: "Item 2"
size_hint: None, None
size: 120, 50
Button:
text: "Item 3"
size_hint: None, None
size: 120, 50
Button:
text: "Item 4"
size_hint: None, None
size: 120, 50
Resultado:
+----------+----------+
| Item 1 | Item 2 |
+----------+----------+
| Item 3 | Item 4 |
+----------+----------+
Se couberem três por linha, ele coloca três. Se couberem dois, coloca dois.
PageLayout
O PageLayout permite alternar entre páginas, como se fossem telas que você arrasta. Hoje, para a maioria dos apps, especialmente com navegação clara, você provavelmente vai usar ScreenManager em vez de PageLayout. Imagine o exemplo abaixo:
ScreenManager
para telas do app
PageLayout
para páginas arrastáveis, casos específicos
ScreenManager
Embora o ScreenManager seja um widget que gerencia telas, na prática ele organiza qual tela aparece. Você usa quando tem múltiplas telas, exemplo:
ScreenManager:
id: telas
TelaResumo:
name: "resumo"
TelaRenda:
name: "renda"
E muda a tela assim:
Button:
text: "Renda"
on_press: telas.current = "renda"
Deixando a interface do app mais bonita
Depois que a estrutura básica do app está funcionando, o próximo passo é melhorar a aparência da interface. No Kivy, isso normalmente é feito ajustando cores, espaçamentos, tamanhos, fontes, cards, botões e organização visual.
É importante entender uma coisa desde o começo, o Kivy puro entrega componentes funcionais, mas o visual padrão é simples e feio. Para deixar o app com aparência mais moderna, você precisa construir um estilo próprio ou usar uma biblioteca complementar, como o KivyMD.
Quando você cria uma tela simples assim:
BoxLayout:
orientation: "vertical"
Label:
text: "Resumo"
Button:
text: "Adicionar"
O Kivy usa o visual padrão dos widgets. Isso é suficiente para aprender, testar eventos e validar a lógica e ver se nada está quebrando, mas no fim, não parece um app moderno. O Kivy não tenta imitar automaticamente o Android, iOS ou Material Design. Ele desenha os próprios elementos gráficos. Por isso, se você quiser uma interface mais bonita, precisa definir melhor o estilo.
O que melhora a aparência de uma tela?
Uma tela começa a parecer mais profissional quando você cuida de pontos como: cores, espaçamento, tamanho dos elementos, alinhamento, contraste, hierarquia visual, componentes reutilizáveis, bordas arredondadas, ícones, consistência entre telas e muitos outros detalhes.
Criando uma identidade visual simples
Antes de sair colorindo tudo, defina algumas cores principais, exemplo:
- Fundo principal: azul quase preto
- Cards: azul escuro
- Cor de destaque: roxo
- Texto principal: branco suave
- Texto secundário: cinza claro
- Valor positivo: verde
- Valor negativo: vermelho
No Kivy, as cores usam valores entre 0 e 1, exemplo:
Color:
rgba: 0.05, 0.08, 0.14, 1
Isso representa uma cor escura, e a ordem é: vermelho, verde, azul, transparência.
Pintando o fundo da tela
Para mudar o fundo de um layout, usamos canvas.before, exemplo:
<TelaResumo>:
BoxLayout:
orientation: "vertical"
padding: 20
spacing: 12
canvas.before:
Color:
rgba: 0.05, 0.08, 0.14, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: "Resumo"
font_size: 28
bold: True
color: 1, 1, 1, 1
O Color: é usado para definir a cor usada no desenho e Rectangle: para desenhar um retângulo ocupando o mesmo tamanho do layout, essa técnica cria o fundo da tela.
Criando um card
Os Cards são blocos visuais usados para agrupar informações, exemplo:
+-----------------------------+
| Saldo disponível |
| R$ 409,80 |
| Renda: R$ 743,60 |
| Gastos: R$ 333,80 |
+-----------------------------+
No Kivy, um card pode ser um BoxLayout com fundo arredondado:
BoxLayout:
orientation: "vertical"
padding: 20
spacing: 10
size_hint_y: None
height: 180
canvas.before:
Color:
rgba: 0.27, 0.13, 0.72, 1
RoundedRectangle:
pos: self.pos
size: self.size
radius: [22]
Label:
text: "Saldo disponível"
font_size: 18
color: 0.85, 0.85, 0.90, 1
Label:
text: "R$ 409,80"
font_size: 38
bold: True
color: 1, 1, 1, 1
Aqui o ponto principal é o RoundedRectangle:, ele cria o fundo arredondado do card.
Criando componentes reutilizáveis
Em vez de repetir o mesmo estilo várias vezes, você pode criar um componente no .kv, exemplo:
<CardEscuro@BoxLayout>:
orientation: "vertical"
padding: 18
spacing: 10
canvas.before:
Color:
rgba: 0.10, 0.14, 0.21, 1
RoundedRectangle:
pos: self.pos
size: self.size
radius: [18]
Agora você pode usar:
CardEscuro:
Label:
text: "Gastos fixos"
color: 1, 1, 1, 1
Label:
text: "R$ 120,00"
color: 1, 1, 1, 1
Isso deixa o .kv mais limpo.
Criando botões mais bonitos
O botão padrão do Kivy costuma parecer simples demais, mas podemos criar um botão personalizado:
<BotaoPrimario@Button>:
background_normal: ""
background_down: ""
background_color: 0, 0, 0, 0
color: 1, 1, 1, 1
font_size: 16
bold: True
canvas.before:
Color:
rgba: 0.42, 0.39, 0.95, 1
RoundedRectangle:
pos: self.pos
size: self.size
radius: [14]
O uso seria assim:
BotaoPrimario:
text: "Adicionar gasto"
size_hint_y: None
height: 52
O background_normal: "" e background_down: "" é usado para remover o fundo padrão do botão. Já o background_color: 0, 0, 0, 0 é usado para deixar o fundo original transparente.
Depois usamos canvas.before para desenhar nosso próprio botão.
Melhorando o rodapé
Um rodapé simples pode ser assim:
BoxLayout:
size_hint_y: None
height: 70
Button:
text: "Resumo"
Button:
text: "Renda"
Mas um rodapé mais bonito pode ter fundo escuro, botões transparentes e destaque na tela atual.
<BotaoRodape@Button>:
background_normal: ""
background_down: ""
background_color: 0, 0, 0, 0
color: 0.55, 0.60, 0.70, 1
font_size: 13
bold: True
BoxLayout:
size_hint_y: None
height: 76
padding: 8
spacing: 4
canvas.before:
Color:
rgba: 0.09, 0.13, 0.20, 1
Rectangle:
pos: self.pos
size: self.size
BotaoRodape:
text: "Resumo"
color: 0.52, 0.50, 1, 1
on_press: telas.current = "resumo"
BotaoRodape:
text: "Renda"
on_press: telas.current = "renda"
BotaoRodape:
text: "Fixos"
on_press: telas.current = "gastos_fixos"
BotaoRodape:
text: "Periódicos"
on_press: telas.current = "gastos_periodicos"
O botão ativo pode usar uma cor diferente para indicar onde o usuário está.