Flet: Flutter para Python
Baseado no Flutter (veja nota abaixo) foi desenvolvida a biblioteca Flet, um framework que permite a construção de aplicações web, desktop e mobile multiusuário interativas usando o Python. O Flet empacota os widgets do Flutter e adiciona algumas combinações próprias de widgets menores, ocultando complexidades e facilitando o uso de boas práticas de construção da interface do usuário. Ele pode ser usado para construir aplicativos que rodam do lado do servidor, eliminando a necessidade de uso de HTML, CSS e Javascrip, e também aplicativos para celulares e desktop. Seu uso exige o conhecimento prévio de Python e um pouco de POO (programação orientada a objetos).
Atualmente (em julho de 2023) o Flet está na versão 0.7.4 e em rápido processo de desenvolvimento.
Flutter e Widgets
Instalando o Flet
Para executar código do python com flet no Linux é necessário ter as bibliotecas do GStreamer instaladas, o que já ocorre na maioria das instalações. Se a mensagem de erro abaixo for emitida instale o GStreamer.
# mensagem de erro ao executar python com flet error while loading shared libraries: libgstapp-1.0.so.0: cannot open shared object file: No such file or directory # GStreamer pode ser instalado com (no fedora) $ sudo dnf update $ sudo dnf install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio # no ubuntu $ sudo apt-get update $ sudo apt-get (mesmo string acima)
Flet exige Python 3.7 ou superior. Para instalar o módulo podemos usar o pip. Como sempre, é recomendado (mas não obrigatório) instalar a nova biblioteca dentro de um ambiente virtual é recomendado (embora não obrigatório).
# criamos um ambiente virtual com o comando $ python3 -m venv ~/Projetos/.venv # para ativar o ambiente virtual $ cd ~/Projetos/.venv $ source bin/activate # agora podemos instalar o flet $ pip install flet # para upgrade do flet, se já instalado $ pip install flet --upgrade
Outras instruções de instalação podem ser encontradas na documentação do Flet, ou a instalação com Anaconda.
Feito isso podemos escrever nosso primeiro código flet, apenas com o esqueleto de um aplicativo. Ao ser executado ele apenas abre uma janela sem conteúdo. Abra um editor de texto, ou sua IDE preferida, e grave o seguinte arquivo, com nome flet1.py
:
import flet as fl def main(page: fl.Page): # add/update controls on Page pass fl.app(target=main)
Ao executar python flet1.py
veremos apenas uma janela vazia, que pode ser fechada com os controles usuais de janela (ou CTRL-F4). O programa termina com flet.app(target=main)
, recebendo no parâmetro target a função que apenas recebe o objeto fleet.Page, main (podia ter outro nome qualquer). O objeto Page é como um Canvas onde, mais tarde, inseriremos outros widgets.
Da forma como escrevemos esse código, uma janela nativa do sistema operacional será aberta no desktop. Para abrir o mesmo aplicativo no browser padrão trocamos a última linha por:
fl.app(target=main, view=fl.AppView.WEB_BROWSER)
Inserindo Widgets
Para obter alguma funcionalidade em nosso aplicativo temos que inserir nele controles, também chamados de widgets. Controles são inseridas na Page, o widget de nível mais alto, ou aninhados dentro de outros controles. Por exemplo, para inserir texto diretamente na page fazemos:
import flet as fl def main(page= fl.Page): txt = fl.Text(value="Olá mundo!", color="blue") page.controls.append(txt) page.update() fl.app(target=main)
Widgets são objetos do python com uma representação visual, com características controladas por seus parâmetros. value e color são parâmetros que recebem strings, esse último declarado no formato de cor do HTML. São válidas as cores, por exemplo: navyblue
, #ff0000
(vermelho), etc. O objeto page possui uma lista controls, à qual adicionamos o elemento txt.
No código seguinte usamos um atalho: page.add(t)
significa o mesmo que page.controls.append(t)
seguido de page.update()
. Também importamos o módulo time para provocar uma pausa na execução do código em time.sleep(1)
.
import flet as fl import time def main(page= fl.Page): t = fl.Text() page.add(t) cidades = ["Belo Horizonte","Curitiba","São Paulo","Salvador","** fim **"] for i in range(5): t.value = cidades[i] page.update() time.sleep(1) fl.app(target=main)
Ao ser executado as quatro cidades armazenadas na lista cidades são exibidas e o loop é terminado com a exibição de ** fim **.
Alguns controles, como Row e Line são containers, podendo conter dentro deles outros widgets, da mesma forma que Page. Por exemplo, inicializamos abaixo uma linha (um objeto ft.Row) contendo três outros objetos que são strings, e a inserimos em page.
import flet as ft import time def main(page= ft.Page): linha = ft.Row(controls=[ft.Text("Estas são"), ft.Text("cidades"), ft.Text("brasileiras")]) page.add(linha) t = ft.Text() page.add(t) # it's a shortcut for page.controls.append(t) and then page.update() cidades = ["Belo Horizonte","Curitiba","São Paulo","Salvador","** fim **"] for i in range(5): t.value = cidades[i] page.update() time.sleep(1) ft.app(target=main)
O resultado é exibido na figura 1, com a cidade sendo substituída a cada momento.

Vemos que Row recebe no parâmetro controls uma lista com 3 widgets de texto.
Claro que muitos outros controles pode ser inseridos. Com o bloco abaixo podemos inserir um campo de texto, que pode ser editado pelo usuário, e um botão.
page.add( ft.Row(controls=[ ft.TextField(label="Sua cidade"), ft.ElevatedButton(text="Clique aqui para inserir o nome de sua cidade!") ]) )
Podemos também criar novas entradas de texto e as inserir consecutivamente em page.
import flet as ft import time def main(page= ft.Page): page.add( ft.Row(controls=[ ft.Text("Estas são"), ft.Text("cidades"), ft.Text("brasileiras") ]) ) page.add( ft.Row(controls=[ ft.TextField(label="Sua cidade"), ft.ElevatedButton(text="Clique aqui para inserir o nome de sua cidade!") ]) ) cidades = ["Belo Horizonte","Curitiba","São Paulo","Salvador","** fim **"] for i in range(5): t = ft.Text() t.value = cidades[i] page.add(t) page.update() time.sleep(1) ft.app(target=main)

Nesse caso não estamos substituindo o conteúdo de um objeto de texto fixo na página, e sim inserindo novas linhas (figura 2). Observe que nenhum procedimento foi designado a esse botão que, no momento, não executa nenhuma tarefa.
O comando page.update()
, que pode estar embutido em page.add()
, envia para a página renderizada apenas as alterações feitas desde sua última execução.
Para incluir alguma ação útil em nosso projeto usamos a capacidade de alguns controles de lidar com eventos (os chamados event handlers). Botões podem executar tarefas quando são clicados usando o evento on_click.
import flet as ft def main(page: ft.Page): def button_clicked(e): page.add(ft.Text("Clicou")) page.add(ft.ElevatedButton(text="Clica aqui", on_click=button_clicked)) ft.app(target=main)
Esse código associa a função button_clicked() com o evento on_click do botão. A cada clique um novo elemento de texto é colocado na página.
Várias outras propriedades podem ser usadas para alterar a aparência dos controles. Vamos usar width (largura) no código abaixo, além do controle Checkbox, uma caixa de texto que pode ser marcada. A função entrar_tarefa() verifica se há conteúdo em nova_tarefa, um TextField e, se houver, cria e insere na página uma nova Checkbox.
Depois limpa o valor de nova_tarefa. O comando nova_tarefa.focus()
coloca no comando de texto o foco da ação dentro da página, independente de ela ter ou não sido usada no if.
import flet as ft def main(page): def entrar_tarefa(e): if nova_tarefa.value: page.add(ft.Checkbox(label=nova_tarefa.value)) nova_tarefa.value = "" nova_tarefa.focus() nova_tarefa = ft.TextField(hint_text="Digite sua nova tarefa...", width=400) page.add(ft.Row([nova_tarefa, ft.ElevatedButton("Inserir tarefa", on_click=entrar_tarefa, width=300)])) nova_tarefa.focus() ft.app(target=main)
É claro que muitas outras ações podem ser inseridas nesse pequeno programa, tal como testar se uma entrada já existe, eliminar espaços em branco desnecessários ou gravar as tarefas em um banco de dados.
Outro Exemplo: Controles e propriedades
É comum os tutoriais do Flet apresentarem um pequeno bloco ilustrativo de código com a operação de somar e subtrair uma unidade a um número em uma caixa de texto. Mostramos aqui um código um pouco mais elaborado para apresentar propriedades adicionais. Usamos page.title = "Soma e subtrai"
para inserir um título na barra de tarefas (ou na aba do navegador, se o codigo for executado no modo web), as propriedades de alignment. Além disso declaramos os botões e caixas de texto separadamente e depois os inserimos nas linhas.
import flet as ft def main(page: ft.Page): def operar(e): numero = int(txt_number.value) + int(e.control.text) txt_number.value = str(numero) txt_operacao.value = "soma" page.update() def mult(e): numero =int(txt_number.value) * int(e.control.text.replace("*","")) txt_number.value = str(numero) txt_operacao.value = "multiplica" page.update() page.title = "Soma, subtrai, multiplica" txt_number = ft.TextField(value="0", text_align=ft.TextAlign.RIGHT, width=100) txt_info = ft.Text("Você pode somar ou subtrair 1 ou 10, multiplicar por 2, 3, 5, 7") txt_operacao = ft.TextField(value="", text_align=ft.TextAlign.RIGHT, width=100) bt1 = ft.ElevatedButton("-10", on_click=operar, width=100) bt2 = ft.ElevatedButton("-1", on_click=operar, width=100) bt3 = ft.ElevatedButton("+1", on_click=operar, width=100) bt4 = ft.ElevatedButton("+10", on_click=operar, width=100) bt5 = ft.ElevatedButton("*2", on_click=mult, width=100) bt6 = ft.ElevatedButton("*3", on_click=mult, width=100) bt7 = ft.ElevatedButton("*5", on_click=mult, width=100) bt8 = ft.ElevatedButton("*7", on_click=mult, width=100) page.add(ft.Row([txt_info], alignment=ft.MainAxisAlignment.CENTER)) page.add(ft.Row([bt1, bt2, txt_number, bt3, bt4], alignment=ft.MainAxisAlignment.CENTER)) page.add(ft.Row([bt5, bt6, txt_operacao, bt7, bt8], alignment=ft.MainAxisAlignment.CENTER)) ft.app(main)
O resultado do código é mostrado na figura 3.

Observe que, ao se clicar nos botões de soma, por ex., o evento on_click chama a função operar(e) passando para ela o parâmetro e, que é um objeto de evento. Este objeto tem propriedades que usamos nas funções de chamada. Na soma (e subtração) capturamos e.control.text
, a propriedade de texto exibida nesse botão (-1, +1, etc.). e a usamos para fazer a operação requerida. Os controles possuem diversas propriedades e respondem a eventos específicos, que devemos manipular para contruir a aparência e funcionalidade da interface gráfica.
Vale ainda mencionar que construímos as linhas com a sintaxe ft.Row([bt1, bt2, txt_number, bt3, bt4])
, usando uma lista de controles previamente definidos. Essa lista está na primeira posição, onde se insere o valor do parâmetro nomeado controls. Se esse parâmetro não estiver na primeira entrada seu nome deve ser fornecido, como em: page.add(ft.Row(alignment=ft.MainAxisAlignment.CENTER, controls=[txt_info]))
.
Em uma questão meramente de estilo mas que pode facilitar a organização e leitura do código, podemos definir as linhas separadamente e depois inserí-las na página.
linha1 = ft.Row([txt_info], alignment=ft.MainAxisAlignment.CENTER) linha2 = ft.Row(controls=[bt1, bt2, txt_number, bt3, bt4], alignment=ft.MainAxisAlignment.CENTER) linha3 = ft.Row(controls=[bt5, bt6, txt_operacao, bt7, bt8], alignment=ft.MainAxisAlignment.CENTER) page.add(linha1, linha2, linha3)
Bibiografia
- Flet: Flet Documentation
- Flet: Flet Tutorial
- Pypi.org: Flet 0.8.1
- Youtube: Dev JurisFlet: Aprendendo os conceitos básicos para criação de apps Flutter com Python
- Hackernoon: Building Flutter Apps with Python
- Medium: Build a Markdown Editor Flutter App With the Flet Python Framework
- Medium: Flutter Apps in Python-Flet
- DEV: Build Flutter Apps using Python Flet
Todas as URLs acessadas em Julho de 2023.