Tamanho de fonte
Alto contraste
Altura de linha
Entrar Cadastrar
Kivy Language Python

Aplicativo com Python: design moderno com Kivy e KivyMD

Avatar de Heitor Tasso Heitor Tasso
23 minutos de leitura 21/11/2025 • Atualizado 3 dias atrás 5xp

Você criou sua lista de lembretes com Kivy e agora quer ir além? Perfeito! Chegou o momento de transformar aquele app funcional em algo verdadeiramente profissional utilizando Python e KivyMD, que é derivado do Kivy.

Atualmente, seu código Kivy provavelmente mistura lógica de programação com definições de interface. Além disso, a aparência ainda lembra os primeiros apps dos anos 2000. Isso é completamente normal no começo.

No entanto, quando você decide evoluir o app, duas coisas ficam evidentes: primeiro, o código se torna cada vez mais difícil de manter. Segundo, a interface simplesmente não impressiona ninguém.

Comparação visual entre interface Kivy básica com botões simples e versão moderna usando Material Design

A solução envolve duas ferramentas poderosas: Kivy Language Python para organizar seu código e KivyMD para criar interfaces modernas. Consequentemente, você terá o mesmo app, porém com código profissional e visual impactante.

Neste artigo, você descobrirá como migrar sua lista de lembretes para essa nova arquitetura. Portanto, prepare-se para dar um salto de qualidade significativo.

O problema da organização: quando Python não basta

Todo desenvolvedor Kivy passa por essa fase. Primeiramente, você cria um app simples que funciona. Em seguida, adiciona mais funcionalidades. Posteriormente, o código começa a ficar bagunçado.

Sintomas de código desorganizado

Reconhece algum desses sinais na sua aplicação Kivy?

  • Arquivos Python com mais de 200 linhas misturando interface e lógica;
  • Dificuldade para encontrar onde definiu as cores de um botão específico;
  • Medo de mexer no código porque “se funciona, não mexe”;
  • Demora excessiva para implementar mudanças simples na interface.

Esses são sinais clássicos de que chegou a hora de organizar melhor sua aplicação.

O dilema do desenvolvedor Kivy

Por um lado, Python funciona perfeitamente para apps simples. Por outro lado, conforme a complexidade aumenta, surgem problemas reais de manutenibilidade.

Considere este exemplo real de como o código cresce naturalmente:

# Versão inicial - 50 linhas
class MeuApp(App):
    def build(self):
        return Button(text="Clique")

# Após algumas funcionalidades - 200+ linhas
class MeuApp(App):
    def build(self):
        layout = BoxLayout(orientation="vertical")
        # ... 150 linhas de definições de interface
        # ... misturado com lógica de negócio

Evidentemente, essa abordagem não escala bem.

Por que separar interface e lógica no Kivy/Python

A separação entre interface e lógica traz benefícios fundamentais:

✅ Vantagens da organização

  • Código Python e Kivy mais limpo visualmente;
  • Interface definida de forma declarativa;
  • Facilidade para fazer alterações visuais;
  • Colaboração mais eficiente em equipes.

⚠️ Sem organização

  • Código gigante e confuso;
  • Alterações do Kivy requerem muita lógica;
  • Dificuldade para debugar problemas;
  • Manutenção cada vez mais complexa.

Felizmente, existe uma solução elegante: Kivy Language Python. Esta ferramenta permite separar completamente a definição da interface da lógica do aplicativo.

Kivy Language: separando o que é o quê

O Kivy Language Python funciona como HTML para web, mas específico para Kivy. Basicamente, você define toda a interface em arquivos .kv, enquanto mantém apenas a lógica nos arquivos .py.

Sintaxe básica em 5 minutos

A sintaxe do Kivy é surpreendentemente simples. Veja esta comparação prática:

Vamos migrar um pedaço da nossa lista de lembretes para Kivy. Primeiramente, observe como era no Python:

def build(self):
    layout = BoxLayout(orientation="vertical")
    titulo = Label(text="Meus Lembretes", font_size="24sp")
    layout.add_widget(titulo)
    return layout

Agora, veja como fica usando Kivy Language Python:

arquivo: main.py

class ListaLembretesApp(App):
    def build(self):
        return Builder.load_file("interface.kv")

arquivo: interface.kv

BoxLayout:
    orientation: "vertical"
    
    Label:
        text: "Meus Lembretes"
        font_size: "24sp"
        size_hint_y: 0.1

Como você pode ver, a sintaxe Kivy é mais limpa e declarativa. Além disso, elimina a necessidade de criar variáveis para cada widget. Percebe a diferença? O arquivo Python agora tem apenas 3 linhas! Consequentemente, toda a complexidade visual foi movida para o arquivo Kivy.

Organizando arquivos Kivy

Para projetos maiores, recomendo essa estrutura de organização:

meu_app/
├── main.py                 # Lógica principal
├── interfaces/
│   ├── main.kv            # Interface principal
│   ├── lembretes.kv       # Tela de lembretes
│   └── configuracoes.kv   # Tela de configurações
└── models/
    └── lembrete.py        # Classes de dados

Esta organização permite que você mantenha cada tela em seu próprio arquivo. Portanto, fica muito mais fácil encontrar e modificar elementos específicos.

Depurando problemas comuns

Durante a migração para Kivy Language Python, você provavelmente encontrará alguns erros típicos:

Problema 1: Arquivo Kivy não encontrado

FileNotFoundError: [Errno 2] No such file or directory: 'interface.kv'

Solução: verifique se o arquivo .kv está na pasta correta e se está sendo carregado pelo Builder dentro de algum código Python.

Problema 2: Propriedades não reconhecidas

AttributeError: 'Button' object has no attribute 'texto'

Solução: propriedades KV devem estar de acordo com a documentação, e nesse caso, o correto seria text em inglês.

Visual moderno com KivyMD: apps que impressionam

Agora que você organizou o código, chegou a hora de elevar o nível visual. Apps Kivy padrão funcionam, mas definitivamente não impressionam visualmente.

O problema visual do Kivy padrão

Por padrão, Kivy usa elementos visuais que remetem aos anos 2000. Embora funcionais, esses elementos passam uma impressão amadora. Consequentemente, isso pode prejudicar a percepção do seu trabalho.

Compare estes elementos visuais:

Kivy Padrão

  • Botões com bordas duras
  • Cores básicas (cinza, azul simples)
  • Sem transições ou animações
  • Visual “quadrado” e rígido

KivyMD Profissional

  • Material Design nativo
  • Paleta de cores harmoniosa
  • Animações fluidas incluídas
  • Visual moderno e polido

A solução: Material Design com KivyMD

KivyMD implementa completamente o Material Design do Google. Como resultado, você obtém componentes visuais profissionais sem esforço adicional.

Primeiramente, instale o KivyMD:

pip install kivymd

Em seguida, faça a migração básica da sua aplicação:

from kivymd.app import MDApp
from kivymd.uix.label import MDLabel

class MeuApp(MDApp):
    def build(self):
        return MDLabel(text="Hello World!", halign="center")

MeuApp().run()

Instantaneamente, você terá uma aplicação com visual Material Design. Além disso, todos os componentes seguirão automaticamente as diretrizes de design do Google.

Componentes essenciais do KivyMD

Estes são os componentes que farão a maior diferença visual na sua aplicação:

MDCard – Para agrupar conteúdo:

MDCard:
    elevation: 3
    shadow_softness: 12
    md_bg_color: app.theme_cls.surface_color

MDRaisedButton – Botões com Material Design:

MDRaisedButton:
    text: "Clique aqui"
    theme_icon_color: "Custom"
    md_bg_color: app.theme_cls.primary_color

MDTextField – Campos de entrada modernos:

MDTextField:
    hint_text: "Digite seu lembrete"
    helper_text: "Máximo 100 caracteres"
    max_text_length: 100

Configuração de cores e estilos

O KivyMD permite personalizar facilmente toda a paleta de cores:

class MeuApp(MDApp):
    def build(self):
        self.theme_cls.primary_palette = "Blue"
        self.theme_cls.theme_style = "Dark"
        return Builder.load_file("interface.kv")

Desta forma, você pode adaptar completamente o visual às suas preferências ou identidade da marca.

Projeto prático: evoluindo sua lista de lembretes

Vamos transformar sua lista de lembretes básica em uma aplicação profissional. Aplicaremos tudo que aprendemos sobre Kivy Language Python e KivyMD de forma incremental.

Resultado anterior

Aplicação antiga de lista de lembretes com design simples implementado usando Kivy normal

Resultado esperado

Antes de começarmos a codar, veja exatamente o que vamos criar:

Aplicação final de lista de lembretes com design Material Design implementado usando KivyMD

Teremos uma aplicação com:

  • Cabeçalho com ícone e título estilizado;
  • Campo de entrada com botão FAB (Floating Action Button);
  • Lista de tarefas com ícones e botões de remoção;
  • Tema claro/escuro alternável;
  • Persistência automática de dados.

Estrutura base com KivyMD

Problema real: nossa lista atual usa componentes Kivy básicos que parecem desatualizados;

Solução: migrar para KivyMD mantendo a funcionalidade, mas com visual moderno;

Como funciona: é como reformar uma casa – mantemos a estrutura (funcionalidade), mas trocamos o acabamento (visual).

Implementação passo a passo:

Passo 1 – App base com KivyMD:

from kivymd.app import MDApp
from kivy.lang import Builder

class ListaLembretesApp(MDApp):
    def build(self):
        self.theme_cls.theme_style = "Dark"
        self.theme_cls.primary_palette = "DeepPurple"
        return Builder.load_file("interfaces/main.kv")

ListaLembretesApp().run()

Passo 2 – Interface KV com cabeçalho estilizado:

MDScreen:
    MDBoxLayout:
        orientation: "vertical"
        spacing: dp(8)
        padding: dp(16)
        
        # Cabeçalho com gradiente visual
        MDCard:
            size_hint_y: None
            height: dp(100)
            elevation: 4
            md_bg_color: app.theme_cls.primary_color
            radius: [15, 15, 15, 15]
            
            MDBoxLayout:
                orientation: "horizontal"
                padding: dp(20)
                spacing: dp(10)
                
                MDIcon:
                    icon: "playlist-check"
                    size_hint_x: None
                    width: dp(40)
                    theme_icon_color: "Custom"
                    icon_color: 1, 1, 1, 1
                    pos_hint: {"center_y": 0.5}
                
                MDLabel:
                    text: "To-Do List"
                    font_style: "H4"
                    theme_text_color: "Custom"
                    text_color: 1, 1, 1, 1
                    bold: True
                    valign: "center"
                
                MDIconButton:
                    icon: "theme-light-dark"
                    theme_icon_color: "Custom"
                    icon_color: 1, 1, 1, 1
                    pos_hint: {"center_y": 0.5}
                    on_press: app.alternar_tema()

Campo de entrada profissional

Problema real: campo de texto simples não condiz com design moderno.

Solução: implementar campo com ícone e Floating Action Button.

Implementação passo a passo:

Passo 3 – Adicionar campo estilizado ao Kivy:

MDScreen:
    MDBoxLayout:
        MDCard:
            ...
        # Adicionar após o cabeçalho
        # Campo de entrada estilizado
        MDCard:
            size_hint_y: None
            height: dp(80)
            elevation: 2
            radius: [12, 12, 12, 12]
            
            MDBoxLayout:
                orientation: "horizontal"
                padding: dp(16)
                spacing: dp(12)
                    
                MDTextField:
                    id: campo_lembrete
                    hint_text: "Add New Task"
                    multiline: False
                    font_size: dp(16)
                    on_text_validate: app.adicionar_lembrete()
                    
                MDFloatingActionButton:
                    icon: "plus"
                    md_bg_color: app.theme_cls.primary_color
                    size_hint: None, None
                    size: dp(45), dp(45)
                    pos_hint: {"center_y": 0.5}
                    elevation: 3
                    on_press: app.adicionar_lembrete()

Passo 4 – Conectar lógica de adição:

class ListaLembretesApp(MDApp):
    ...
    # Adicionar método à classe ListaLembretesApp
    def adicionar_lembrete(self):
        campo = self.root.ids.campo_lembrete
        lista = self.root.ids.lista_lembretes

        texto = campo.text.strip()
        if texto:
            item = LembreteItem(texto=texto)
            lista.add_widget(item)
            campo.text = ""
            self.salvar_lembretes()

Lista de tarefas interativa

Problema real: precisamos exibir e gerenciar os lembretes de forma visual.

Solução: implementar lista com ícones e botões de remoção usando componentes KivyMD.

Implementação passo a passo:

Passo 5 – Criar classe para itens da lista:

from kivymd.uix.list import OneLineAvatarIconListItem, IconLeftWidget, IconRightWidget

class LembreteItem(OneLineAvatarIconListItem):
    def __init__(self, texto="", feito=False, **kwargs):
        super().__init__(**kwargs)
        self.texto_original = texto
        self.feito = feito

        # Ícone de tarefa à esquerda (checkbox)
        self.icone = IconLeftWidget(
            icon="checkbox-marked-circle-outline" if self.feito else "checkbox-blank-circle-outline"
        )
        self.icone.bind(on_release=self.toggle_feito)
        self.add_widget(self.icone)

        # Botão de lixeira (caso ainda queira manter exclusão manual)
        botao = IconRightWidget(icon="delete-outline")
        botao.bind(on_release=self.remover_item)
        self.add_widget(botao)

        self.atualizar_texto()

Passo 6 – Adicionar área da lista ao Kivy:

MDScreen:
    MDBoxLayout:
        MDCard:
            ...
        MDCard:
            ...
        # Adicionar Lista de lembretes com estilo
        MDCard:
            elevation: 2
            radius: [12, 12, 12, 12]
            
            ScrollView:
                MDList:
                    id: lista_lembretes
                    spacing: dp(2)

Passo 7 – Completar lógica de adição e remoção:

# Adicionar método de remover item
class LembreteItem(OneLineAvatarIconListItem):
    ...
    def atualizar_texto(self):
        """Atualiza o texto com ou sem risquinho"""
        if self.feito:
            self.text = f"[s]{self.texto_original}[/s]"
            self.markup = True  # permite usar tags [s][/s]
        else:
            self.text = self.texto_original
            self.markup = False

    def toggle_feito(self, *args):
        """Alterna entre concluído/não concluído"""
        self.feito = not self.feito
        self.icone.icon = (
            "checkbox-marked-circle-outline" if self.feito else "checkbox-blank-circle-outline"
        )
        self.atualizar_texto()
        MDApp.get_running_app().salvar_lembretes()

    def remover_item(self, *args):
        """Remove completamente a tarefa"""
        if self.parent:
            self.parent.remove_widget(self)
            MDApp.get_running_app().salvar_lembretes()

Persistência e funcionalidades extras

Problema real: dados se perdem ao fechar o app, falta alternância de tema.

Solução: salvar dados em JSON e implementar toggle de tema.

Implementação passo a passo:

Passo 8 – Adicionar persistência:

import json
import os

class ListaLembretesApp(MDApp):
    ...
    # Adicionar métodos à classe principal
    def salvar_lembretes(self):
        lista = self.root.ids.lista_lembretes
        lembretes = [
            {"texto": item.texto_original, "feito": item.feito}
            for item in reversed(lista.children)
        ]
        with open("lembretes.json", "w", encoding="utf-8") as f:
            json.dump(lembretes, f, ensure_ascii=False, indent=2)

    def on_start(self):
        """Carrega os lembretes salvos, se existirem"""
        if os.path.exists("lembretes.json"):
            with open("lembretes.json", "r", encoding="utf-8") as f:
                for dic in json.load(f):
                    self.root.ids.lista_lembretes.add_widget(
                        LembreteItem(texto=dic["texto"], feito=dic["feito"])
                    )

Passo 9 – Botão de alternância de tema:

class ListaLembretesApp(MDApp):
    ...
    # Método para alternar tema
    def alternar_tema(self):
        self.theme_cls.theme_style = (
            "Dark" if self.theme_cls.theme_style == "Light" else "Light"
        )

Validação: execute o app, adicione lembretes, feche e reabra – os dados devem persistir. O botão de tema deve alternar entre claro e escuro.

Checkpoint final

Agora você tem uma aplicação completamente profissional que:

  • Visual: Design Material com cores harmoniosas;
  • Funcional: adiciona, remove e persiste lembretes;
  • Organizada: código separado em Python e Kivy;
  • Moderna: componentes KivyMD e funcionalidades extras.

O mesmo conceito de lista de lembretes, mas com implementação de nível profissional.

Próximos passos: seu roadmap de evolução

Parabéns! Você transformou com sucesso um app básico em uma aplicação profissional. Atualmente, você domina Kivy Language Python e KivyMD, duas ferramentas fundamentais para desenvolvimento Kivy sério.

Suas conquistas atuais

Neste momento, você sabe como:

  • Organizar código separando interface de lógica;
  • Criar interfaces modernas com Material Design;
  • Estruturar projetos Kivy de forma profissional;
  • Implementar funcionalidades básicas de persistência.

Recursos para continuar evoluindo

Para aprofundar ainda mais seus conhecimentos:

Documentação oficial:

Próximos desafios técnicos:

  • Navegação entre telas com MDNavigationDrawer;
  • Integração com APIs externas;
  • Empacotamento com Buildozer para Android.

Trilha Python Office

Automatize tudo com Python e torne seu escritório muito mais eficiente! Manipule arquivos, crie relatórios automatizados e construa sistemas 100% personalizados usando apenas Python.

Comece agora

Lembre-se: a jornada de um desenvolvedor Python nunca para. Cada projeto ensina algo novo, e cada desafio superado abre portas para possibilidades ainda maiores.

Arquivo main.py completo:

from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.list import OneLineAvatarIconListItem, IconLeftWidget, IconRightWidget
import json
import os

class LembreteItem(OneLineAvatarIconListItem):
    def __init__(self, texto="", feito=False, **kwargs):
        super().__init__(**kwargs)
        self.texto_original = texto
        self.feito = feito

        # Ícone de tarefa à esquerda (checkbox)
        self.icone = IconLeftWidget(
            icon="checkbox-marked-circle-outline" if self.feito else "checkbox-blank-circle-outline"
        )
        self.icone.bind(on_release=self.toggle_feito)
        self.add_widget(self.icone)

        # Botão de lixeira (caso ainda queira manter exclusão manual)
        botao = IconRightWidget(icon="delete-outline")
        botao.bind(on_release=self.remover_item)
        self.add_widget(botao)

        self.atualizar_texto()
    
    def atualizar_texto(self):
        """Atualiza o texto com ou sem risquinho"""
        if self.feito:
            self.text = f"[s]{self.texto_original}[/s]"
            self.markup = True  # permite usar tags [s][/s]
        else:
            self.text = self.texto_original
            self.markup = False

    def toggle_feito(self, *args):
        """Alterna entre concluído/não concluído"""
        self.feito = not self.feito
        self.icone.icon = (
            "checkbox-marked-circle-outline" if self.feito else "checkbox-blank-circle-outline"
        )
        self.atualizar_texto()
        MDApp.get_running_app().salvar_lembretes()

    def remover_item(self, *args):
        """Remove completamente a tarefa"""
        if self.parent:
            self.parent.remove_widget(self)
            MDApp.get_running_app().salvar_lembretes()

class ListaLembretesApp(MDApp):
    # Método para alternar tema
    def alternar_tema(self):
        self.theme_cls.theme_style = (
            "Dark" if self.theme_cls.theme_style == "Light" else "Light"
        )
        
    def salvar_lembretes(self):
        lista = self.root.ids.lista_lembretes
        lembretes = [
            {"texto": item.texto_original, "feito": item.feito}
            for item in reversed(lista.children)
        ]
        with open("lembretes.json", "w", encoding="utf-8") as f:
            json.dump(lembretes, f, ensure_ascii=False, indent=2)

    def on_start(self):
        """Carrega os lembretes salvos, se existirem"""
        if os.path.exists("lembretes.json"):
            with open("lembretes.json", "r", encoding="utf-8") as f:
                for dic in json.load(f):
                    self.root.ids.lista_lembretes.add_widget(
                        LembreteItem(texto=dic["texto"], feito=dic["feito"])
                    )

    def adicionar_lembrete(self):
        campo = self.root.ids.campo_lembrete
        lista = self.root.ids.lista_lembretes

        texto = campo.text.strip()
        if texto:
            item = LembreteItem(texto=texto)
            lista.add_widget(item)
            campo.text = ""
            self.salvar_lembretes()

    def build(self):
        self.theme_cls.theme_style = "Dark"
        self.theme_cls.primary_palette = "DeepPurple"
        return Builder.load_file("interfaces/main.kv")

ListaLembretesApp().run()

Arquivo interfaces/main.kv completo:

MDScreen:
    MDBoxLayout:
        orientation: "vertical"
        spacing: dp(8)
        padding: dp(16)
        
        # Cabeçalho com gradiente visual
        MDCard:
            size_hint_y: None
            height: dp(100)
            elevation: 4
            md_bg_color: app.theme_cls.primary_color
            radius: [15, 15, 15, 15]
            
            MDBoxLayout:
                orientation: "horizontal"
                padding: dp(20)
                spacing: dp(10)
                
                MDIcon:
                    icon: "playlist-check"
                    size_hint_x: None
                    width: dp(40)
                    theme_icon_color: "Custom"
                    icon_color: 1, 1, 1, 1
                    pos_hint: {"center_y": 0.5}
                
                MDLabel:
                    text: "To-Do List"
                    font_style: "H4"
                    theme_text_color: "Custom"
                    text_color: 1, 1, 1, 1
                    bold: True
                    valign: "center"
                
                MDIconButton:
                    icon: "theme-light-dark"
                    theme_icon_color: "Custom"
                    icon_color: 1, 1, 1, 1
                    pos_hint: {"center_y": 0.5}
                    on_press: app.alternar_tema()

        # Campo de entrada estilizado
        MDCard:
            size_hint_y: None
            height: dp(80)
            elevation: 2
            radius: [12, 12, 12, 12]
            
            MDBoxLayout:
                orientation: "horizontal"
                padding: dp(16)
                spacing: dp(12)
                    
                MDTextField:
                    id: campo_lembrete
                    hint_text: "Add New Task"
                    multiline: False
                    font_size: dp(16)
                    on_text_validate: app.adicionar_lembrete()
                    
                MDFloatingActionButton:
                    icon: "plus"
                    md_bg_color: app.theme_cls.primary_color
                    size_hint: None, None
                    size: dp(45), dp(45)
                    pos_hint: {"center_y": 0.5}
                    elevation: 3
                    on_press: app.adicionar_lembrete()

        MDCard:
            elevation: 2
            radius: [12, 12, 12, 12]
            
            ScrollView:
                MDList:
                    id: lista_lembretes
                    spacing: dp(2)
Imagem de um notebook

Cursos de programação gratuitos com certificado

Aprenda a programar e desenvolva soluções para o seu trabalho com Python para alcançar novas oportunidades profissionais. Aqui na Asimov você encontra:

  • Conteúdos gratuitos
  • Projetos práticos
  • Certificados
  • +20 mil alunos e comunidade exclusiva
  • Materiais didáticos e download de código
Inicie agora

Comentários

30xp
Comentar
Faça parte da discussão Crie sua conta gratuita e compartilhe
sua opinião nos comentários
Entre para a Asimov