Skip to main content

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:

  1. Usuário toca no botão
  2. Kivy recebe evento de entrada
  3. Kivy identifica qual widget foi atingido
  4. Kivy dispara evento on_press
  5. Seu código é chamado
  6. Você altera uma propriedade
  7. Kivy percebe a alteração
  8. 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:

  1. inicie
  2. mostre a interface
  3. espere algo acontecer
  4. quando acontecer, reaja
  5. 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.

main.py
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()

meu.kv
<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.


ter um único .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:

  1. Obtém o nome da classe FinancyApp;
  2. Remove o App do nome da classe;
  3. Nesse ponto, fica apenas com Financy.
  4. Converte tudo para minúsculo;
  5. Adiciona a extensão .kv e procura pelo arquivo financy.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:

  1. Label não deve usar altura proporcional
  2. Label deve ter altura fixa de 60
  3. 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:

financy.kv
<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"

main.py
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á.