O objeto page é o primeiro conteiner na construção da árvore de controles do Flet, sendo o único que não está ligado a um conteiner pai. Ele contém o objeto View que, por sua vez abriga todos os outros controles. Uma instância de page e uma view primária são criadas automaticamente quando uma sessão é iniciada.
Vamos primeiro fazer uma descrição dessas propriedades, métodos e eventos, e depois mostrar exemplos de uso. Muitas vezes a mera descrição do elemento é suficiente para que as apliquemos no código. Em alguns casos notas adicionais estão linkadas.
Propriedades de page
Observação: Os itens marcados com o ícone tem disponibilidade restrita:
🌎 só disponíveis na Web,
🖥️ só disponíveis no desktop,
📱 só disponíveis em celulares,
📝 há nota explicativa sobre o controle, (clique)
🅾 Read Only (Somente leitura).
Propriedade
Descrição
auto_scroll
True se a barra de scroll deve ser automaticamente posicionada no final se seu filho é atualizado. Necessariamente auto_scroll = False para que o método scroll_to() funcione.
appbar
page.appbar recebe uma barra de controle do aplicativo no alto da page (diferente da barra da janela).
banner
Controle Banner no alto da página.
bgcolor
Cor de fundo da página.
client_ip
🌎 Endereço de IP do usuário conectado.
client_user_agent
🌎 Detalhes do navegador do usuário conectado.
controls
📝 Lista de controles a exigir na página. Ex.: page.controls.append(ft.Text("Olá!")); page.update() é o mesmo que page.add(ft.Text("Olá!")). Para remover o último controle da página: page.controls.pop(); page.update().
dark_theme
Indica a instância do tema usado.
dialog
Um controle AlertDialog para ser exibido.
floating_action_button
Um controle FloatingActionButton para exibir no alto da página.
fonts
📝 Importa fontes para uso em Text.font_family ou aplica fonte no aplicativo inteiro com theme.font_family. Flet admite o uso de fontes .ttc, .ttf, .otf.
height
🅾 Altura da página, geralmente usada junto com o evento page.on_resize.
horizontal_alignment
📝 Alinhamento horizontal do controle filho em relação a seu conteiner.
on_scroll_interval
Passos em milisegundo para o evento on_scroll event. Default é 10.
overlay
Lista de controles exibidos em pilha sobre o conteúdo da página.
padding
Espaço entre as bordas do controle e as do conteiner. Default é page.padding = 10.
platform
Sistema operacional onde o aplicativo está sendo executado. Pode ser: ios, android, macos, linux ou, windows.
pubsub
Implementação para transferência de mensagens entre sessões do aplicativo.
subscribe(handler)
Registra a sessão atual para emissão de mensagens. handler é uma função ou método contendo uma mensagem.
subscribe_topic(topic, handler)
Inscreve a sessão para recebimento de mensagens sobre tópico específico.
send_all(message)
Envia mensagem a todos os inscritos.
send_all_on_topic(topic, message)
Envia mensagem para todos os inscritos em um tópico específico.
send_others(message)
Envia mensagem para todos, exceto para quem envia.
send_others_on_topic(topic, message)
Envia mensagem para todos em um tópico específico, exceto para quem envia.
unsubscribe()
Remove inscrição da sessão atual de todas as mensagens enviadas.
unsubscribe_topic(topic)
Remove inscrição da sessão de todas as mensagens sobre o tópico.
unsubscribe_all()
Remove as inscrições da sessão de todas as mensagens.
Uma lista de controles View para armazenar histórico de navegação. A última View é a página atual, a primeira é “root”, que não pode ser excluída.
web
True se a aplicativo está rodando no navegador.
width
🅾 Largura da página web ou janela de aplicativo, usualmente usada com o evento page.on_resize.
window_always_on_top
🖥️ Ajusta de a janela deve estar sempre acima das demais janelas. Default: False.
window_bgcolor
🖥️ Cor de fundo da janela do aplicativo.Pode ser usado com page.bgcolor para tornar a hjanela toda transparente.
window_focused
🖥️ True para trazer o foco para essa janela.
window_frameless
🖥️ True para eliminar as bordas das janelas.
window_full_screen
🖥️ True para usar tela cheia. Default: False.
window_height
🖥️ Leitura ou ajuste da altura da janela.
window_left
🖥️ Leitura ou ajuste da posição horizontal da janela, em relação à borda esquerda da tela.
window_maximizable
🖥️ False para ocultar botões de “Maximizar”. Default: True.
window_maximized
🖥️ True se a janela do aplicativo está maximizada. Pode ser ajustada programaticamente.
window_max_height
🖥️ Leitura ou ajuste da altura máxima da janela do aplicativo.
window_max_width
🖥️ Leitura ou ajuste da largura máxima da janela do aplicativo.
window_minimizable
🖥️ Se False o botão de “Minimizar” fica oculto. Default: True.
window_minimized
🖥️ True se a janela está minimizada. Pode ser ajustada programaticamente.
window_min_height
🖥️ Leitura ou ajuste da altura mínima da janela do aplicativo.
window_min_width
🖥️ Leitura ou ajuste da largura mínima da janela do aplicativo.
window_movable
🖥️ macOS apenas. Se False impede a movimentação da janela. Default: True.
window_opacity
🖥️ Ajuste da opacidade da janela. 0.0 (transparente) and 1.0 (opaca).
window_resizable
🖥️ Marque como False para impedir redimensionamento da janela. Default: True.
window_title_bar_hidden
🖥️ True para ocultar a barra de título da janela. O controle WindowDragArea permite a movimentação de janelas de barra de título.
window_title_bar_buttons_hidden
🖥️ apenas macOS. True para ocultar butões de ação da janela quando a barra de título está invisível.
window_top
🖥️ Leitura ou ajuste da posição da distância da janela (em pixeis) ao alto da tela.
window_prevent_close
🖥️ True para interceptar o sinal nativo de fechamente de janela. Pode ser usado com page.on_window_event ou page.window_destroy() para forçar uma confirmação do usuário.
window_progress_bar
🖥️ Exibe uma barra de progresso com valor de 0.0 a 1.0 na barra de tarefas (Windows) ou Dock (macOS).
window_skip_task_bar
🖥️ True para ocultar a barra de tarefas (Windows) ou Dock (macOS).
window_visible
🖥️ True para tornar o aplicativo visível. Usado quando o app é iniciado em janela oculta.
window_width
🖥️ Leitura ou ajuste da largura da janela do aplicativo.
Métodos de page
Método
Ação
can_launch_url(url)
📝 Verifica se a url pode ser acessada pelo aplicativo.
close_banner()
Fecha o banner ativo.
close_bottom_sheet()
Fecha o pé de página.
close_dialog()
Fecha a caixa de diálogo ativa.
close_in_app_web_view()
📱 Fecha visualização de página web iniciada com launch_url() de dentro do aplicativo.
get_clipboard()
Recupera o último texto salvo no clipboard do lado do cliente.
go(route)
Um método auxiliar para atualizar page.route. Ele aciona o evento page.on_route_change seguido de page.update().
🖥️ Move a janela do aplicativo para o centro da tela.
window_close()
🖥️ Fecha a janela do aplicativo.
window_destroy()
🖥️ Força o fechamento da janela. Pode ser usado com page.window_prevent_close = True para exigir a confirmação do usuário para encerramento do aplicativo. 📝
window_to_front()
🖥️ Traz a janela do aplicativo para o primeiro plano.
Eventos de page
Evento
Dispara quando
on_close
uma sessão expirou por um tempo configurado. Default: 60 minutos.
on_connect
o usuário da web conecta ou reconect uma sessão. Não dispara na primeira exibição de uma página mas quando ela é atualizada. Detecta a presença do usuário online.
on_disconnect
o usuário se desconecta de uma sessão, fechando o navegador ou a aba da página.on_error ocorre um erro não tratado.
Estritamente dizendo deveríamos iniciar nosso estudo do Flet considerando os objetos mais básicos, no sentido de serem containeres de outros objetos. No entanto já vimos vários casos de pequenos aplicativos que usam botões. É difícil sequer exibir exemplos de código de GUI sem botões. Por isso vamos descrever aqui o uso dos botões, deixando para depois uma descrição dos controles de layout.
Alguns controles tem a função principal de obter informações do usuário, como botões, menus dropdown ou caixas de texto, para inserção de dados. Outros servem para a exibição de informações calculadas pelo código, mostrando gráficos, figuras ou textos. As caixas de textos podem ser usadas em ambos os casos.
Buttons (botões)
Os seguintes tipos de botões estão disponíveis (e ilustrados na figura 1), contendo as propriedades, métodos e respondendo a eventos:
Vamos usar alguns exemplos para ilustrar as propriedades e métodos desses objetos.
import flet as ft
def main(page: ft.Page):
def mudar(e):
bt2.disabled = not bt2.disabled
bt2.text = "Desabilitado" if bt2.disabled else "Habilitado"
page.update()
bt1 = ft.FilledButton(text="FilledButton", on_click=mudar, width=200)
bt2 = ft.ElevatedButton("Habilitado", disabled=False, width=200)
page.add(bt1, bt2)
ft.app(target=main)
Nesse caso um FilledButton aciona a função mudar que alterna a propriedade disabled do botão elevado. Observe que um botão com disabled=True não reage ao clique, nem à nenhum outro evento. O operador ternário foi usado: valor_se_true if condicao else valor_se_false.
Dois novos eventos são mostrados a seguir: on_hover, que é disparado quando o cursor se move sobre o botão (sem necessidade de clicar) e on_long_press, disparado com um clique longo. Um ícone é inserido nos botões ElevatedButton, cujas cores são alteradas pelos eventos descritos.
Os botões assumem os estados mostrados na figura 2.
Figura 2: Execução do código acima.
Alguns Ícones pré-definidos do Flet
Uma grande quantidade de ícones está disponível e pode ser pesquisada em Flet.dev: Icons Browser.
Botões possuem a propriedade data que pode armazenar um objeto a ser passado para as funções acionadas por eventos. As propriedades dos widgets funcionam como variáveis globais. No exemplo abaixo temos uma caixa de texto, que exibe a propriedade data. O botão elevado também tem uma propridade data que não é exibida mas serve para armazenar quantas vezes o botão foi clicado. Ele serve de container para um Row contendo 3 ícones (ft.Icon(ft.icons.NOME_DO_ICONE)).
import flet as ft
def main(page: ft.Page):
def bt_clicou(e):
bt.data += 1
txt.value = f"O botão foi clicado {bt.data} {'vezes' if bt.data >1 else 'vêz'}"
page.update()
txt = ft.Text("O botão não foi clicado", size=25, color="navyblue", italic=True)
bt = ft.ElevatedButton("Clica!",
content=ft.Row(
[
ft.Icon(ft.icons.CHAIR, color="red"),
ft.Icon(ft.icons.COTTAGE, color="green"),
ft.Icon(ft.icons.AGRICULTURE, color="blue"),
], alignment=ft.MainAxisAlignment.SPACE_AROUND,),
on_click=bt_clicou, data=0, width=150,
)
page.add(txt, bt)
ft.app(target=main)
Se o nome do ícone não for fornecido como primeiro parâmetro o nome do parâmetro deve ser nomeado: ft.Icon(color="red", name=ft.icons.CHAIR). E execução do código resulta nas janelas exibidas na figura 3.
Figura 3
As caixas de texto podem receber formatações diversas como size (tamanho da fonte), color (cor da fonte) bgcolor (cor do fundo), italic (itálico), weight (peso da fonte). A propriedade selectable informa se o texto exibido pode ser selecionado, e estilos diversos podem ser atribuídos em style. A página recebe a propriedade page.scroll = "adaptive" para que possa apresentar uma barra de scroll.
O resultado é mostrado na figura 4. A janela foi dimensionada para ser menor que o texto existente, mostrando a barra de scroll.
Figura 4
O número máximo de linhas exibidas, max_lines, ou largura e altura do texto, width e height são úteis quando se apresenta texto logo em uma janela.
import flet as ft
def main(page: ft.Page):
page.scroll = "adaptive"
texto1 = (
"René Descartes (La Haye en Touraine, 31 de março de 1596, Estocolmo, 11 de"
"fevereiro de 1650) foi um filósofo, físico e matemático francês. Durante a"
"Idade Moderna, também era conhecido por seu nome latino Renatus Cartesius."
)
texto2 = (
"Descartes, por vezes chamado de o fundador da filosofia moderna e o pai da"
"matemática moderna, é considerado um dos pensadores mais importantes e"
"influentes da História do Pensamento Ocidental. Inspirou contemporâneos e"
"várias gerações de filósofos posteriores; boa parte da filosofia escrita "
"a partir de então foi uma reação às suas obras ou a autores supostamente"
"influenciados por ele. Muitos especialistas afirmam que, a partir de"
"Descartes, inaugurou-se o racionalismo da Idade Moderna."
)
t7 = ft.Text("Limita texto longo a 1 linha, com elipses", style=ft.TextThemeStyle.HEADLINE_SMALL)
t8 = ft.Text(texto1, max_lines=1, overflow="ellipsis")
t9 = ft.Text("Limita texto longo a 2 linhas", style=ft.TextThemeStyle.HEADLINE_SMALL)
t10 = ft.Text(texto2, max_lines=2)
t11 = ft.Text("Limita largura e altura do texto longo", style=ft.TextThemeStyle.HEADLINE_SMALL)
t12 = ft.Text(texto2, width=700, height=100)
page.add(t7, t8, t9, t10, t11, t12)
ft.app(target=main)
Figura 5
Resultando na janela mostrada na figura 5. O parâmetro overflow="ellipsis" mostra uma elipses onde on texto foi quebrado. As definições de texto1 e texto2 correspondem a uma das formas de declarar strings longas no python.
Existem estilos pré-definidos. código abaixo usamos o texto do widget igual ao nome do estilo, para facilitar a visualização: style=ft.TextThemeStyle.NOME_DO_ESTILO. Apenas as definições estão mostradas e o resultado está na figura 6.
Propriedades dos controles pode ser alterados dinamicamente por meio de inputs recebidos do usuário (ou outra origem qualquer). No próximo exemplo o tamanho da fonte é controlado por um flet.Slider.
import flet as ft
def main(page: ft.Page):
def font_size(e):
t.size = int(sl.value)
t.value = f"Texto escrito com fonte Bookerly, size={t.size}"
t.update()
t = ft.Text("Texto escrito com fonte Bookerly, size=10", size=10, font_family="Bookerly")
sl = ft.Slider(min=0, max=100, divisions=10, value=10, label="{value}", width=500, on_change=font_size)
page.add(t, sl)
ft.app(target=main)
Figura 7
O app gerado está na figura 7. Observe que a propriedade do Slider.label="value" exibe acima do cursor (como um tooltip) o valor do controle. O tamanho da fonte é ajustado de acordo com esse valor.
Se a fonte está instalada localmente basta usar font_family="Nome_da_Fonte". Para fontes remotas podemos definir uma ou várias fontes a serem usadas. page.fonts recebe um dicionário com nomes e locais das fontes.
page.fonts = {
"Kanit": "https://raw.githubusercontent.com/google/fonts/master/ofl/kanit/Kanit-Bold.ttf",
"Open Sans": "fonts/OpenSans-Regular.ttf",
}
page.theme = Theme(font_family="Kanit")
page.add(
ft.Text("Esse texto é renderizado com fonte Kanit"),
ft.Text("Esse texto é renderizado com fonte 'Open Sans'", font_family="Open Sans"),
Ícones
O objeto fleet.Icon pode ser inserido em vários conteineres. Ele possui as propriedades (entre outras) color, name, size e tooltip. O tamanho default é size=24 enquanto tooltip é o texto exibido quando o cursor está sobre o ícone.
O código ilustra esse uso:
Figura 8: Resultado do código de exibição de ícones.
O nome do ícone pode ser dado como name=ft.icons.ZOOM_IN ou uma string name="child_care", onde o nome pode ser pesquisado no Icons Browser. Note que ft.icons contém ENUMS predefinidos e name=ft.icons.AIR_SHARP é o mesmo que name="air_sharp".
Ícones personalizados podem ser inseridos como imagens, como em flet.Image(src=f"img/Search.png"), onde o caminho pode ser absoluto ou relativo, em referência ao diretório onde está o aplicativo. Isso significa que a imagem da lupa exibida na figura está armazenada em pasta_do_aplicativo/img/Search.png.
Vimos no primeiro artigo dessa série que um aplicativo Python com Flet consiste em código Python para a realização da lógica do aplicativo, usando o Flet como camada de exibição. Mais tarde nos preocuparemos em fazer uma separação explícita das camadas. Por enquanto basta notar que o Flet cria uma árvore de widgets cujas propriedades são controladas pelo código. Widgets também podem estar associados à ações ligadas a funções. Portanto, para construir aplicativos com Flet, precisamos conhecer esses widgets, suas propriedades e eventos que respondem.
Alguns controles tem a função principal de obter informações do usuário, como botões, menus dropdown ou caixas de texto, para inserção de dados. Outros servem para a exibição de informações calculadas pelo código, mostrando gráficos, figuras ou textos. As caixas de textos podem ser usadas em ambos os casos.
Figura 10: Estrutura de árvore de um aplicativo Flet
A interface do Flet é montada como uma composição de controles, arranjados em uma hierarquia sob forma de árvore que se inicia com uma Page. Dentro dela são dispostos os demais controles, sendo que alguns deles são também conteineres, abrigando outros controles. Todos os controles, portanto, possuem um pai, exceto a Page. Controles como Column, Row e Dropdown podem conter controles filhos, como mostrado na figura 10.
Categorias de Controles
🔘 Controles
Categoria
Itens
🔘 Layout
diagramação
16 itens
🔘 Navigation
navegação
3 itens
🔘 Information Displays
exibição de informação
8 itens
🔘 Buttons
botões
8 itens
🔘 Input and Selections
entrada e seleções
6 itens
🔘 Dialogs, Alerts, Panels
dialogo e paineis
4 itens
🔘 Charts
gráficos
5 itens
🔘 Animations
animações
1 item
🔘 Utility
utilidades
13 itens
Propriedades comuns a vários controles
Algumas propriedades são comuns a todos (ou quase todos) os controles. Vamos primeiro listá-las e depois apresentar alguns exemplos de uso. As propriedades marcadas com o sinal ≜ só são válidas quando estão dentro de uma controle Stack, que será descrito após a tabela.
bottom
≜
Distância entre a borda inferior do filho e a borda inferior do Stack.
data
Um campo auxiliar de dados arbitrários que podem ser armazenados junto a um controle.
disabled
Desabilitação do controle. Por padrão disabled = False. Um controle desabilitado fica sombreado e não reage a nenhum evento. Todos os filhos de um controle desabilitado ficam desabilitados.
expand
Um controle dentro de uma coluna ou linha pode ser expandido para preencher o espaço disponível. expand=valor, onde valor pode ser booleano ou inteiro, um fator de expansão, usado para dividir o espaço entre vários controles.
hight
Altura do controle, em pixeis.
left
≜
Distância da borda esquerda do filho à borda esquerda do Stack.
right
≜
Distância da borda direita do filho à borda direita do Stack.
top
≜
Distância da borda superior do filho ao topo do Stack.
visible
Visibilidade do controle e seus filhos. vivible = True por padrão. Controles invisíveis não recebem foco, não podem ser selecionados nem respondem a eventos.
widht
Largura de controle em pixeis.
Um Stack é um controle usado para posicionar controles em cima de outros (empilhados). Veremos mais sobre ele na seção sobre layouts.
Transformações (Transformations)
Transformações são operações realizadas sobre os controles
offset
É uma translação aplicada sobre um controle, antes que ele seja renderizado. A translação é dada em uma escala relativa ao tamanho do controle. Um deslocamento de 0,25 realizará uma translação horizontal de 1/4 da largura do controle. Ex,: ft.Container(..., offset=ft.transform.Offset(-1, -1).
opacity
Torna o controle parcialmente transparente. O default é opacity=1.0, nenhuma transparência. Se opacity=0.0controle é 100% transparente.
rotate
Aplica uma rotação no controle em torno de seu centro. O parâmetro rotate pode receber um número, que é interpretado com o ângulo, em radianos, de rotação anti-horária. A rotação também pode ser especificada por meio de transform.Rotate, que permite estabelecer ângulo, alinhamento e posição de centro de rotação. Ex,: ft.Image(..., rotate=Rotate(angle=0.25 * pi, alignment=ft.alignment.center_left) representa uma rotação de 45° (π/4).
scale
Controla a escala ao longo do plano 2D. O fator de escala padrão é 1,0. Por ex.: ft.Image(..., scale=Scale(scale_x=2, scale_y=0.5) multiplica as dimensões em x por 2 e em y por .5. Alternativamente Scale(scale=3) multiplica por 3 nas duas direções.
bt6 ⇾ torna a cor mais translúcida (ct.opacity -= .1),
bt7 ⇾ retorna a imagem para o estado inicial,
Figura 12: Propriedades e Transformações
onde ct = ft.Container, é o container de cor vermelha, mostrado no figura 12.
Atalhos de Teclado
Qualquer pessoa que faz uso intensivo do computador sabe da importância dos Atalhos de Teclado (Keyboard shortcuts). A possibilidade de não mover a mão do teclado para acionar o mouse pode significar melhor usabilidade e aumento de produtividade. Para isso o Flet oferece para o programador a possibilidade de inserir atalhos em seus aplicativos para que seu usuário possa dinamizar seu uso.
Para capturar eventos produzidos pelo teclado o objeto page implementa o método on_keyboard_event, que gerencia o pressionamento de teclas de caracter, em combinação com teclas de controle. Esse evento passa o parâmetro eque é uma instância da classe KeyboardEvent, e tem as seguintes propriedades:
e.key
Representação textual da tecla pressionada. Veja nota†.
e.shift
Booleano: True se a tecla “Shift” foi pressionada.
e.ctrl
Booleano: True se a tecla “Control” foi pressionada.
e.alt
Booleano: True se a tecla “Alt” foi pressionada.
e.meta
Booleano: True se a tecla “Meta” foi pressionada††.
Nota†: Além dos caracteres A … Z (todos apresentados em maiúsculas) também são representadas as teclas Enter, Backspace, F1 … F12, Escape, Insert … Page Down, Pause, etc. Alguns teclados permitem a reconfiguração de teclas, por exemplo fazendo F1 = Help, etc. Nota††: A tecla Meta é representada em geral no Windows como tecla Windows e no Mac como tecla Command.
O seguinte código ilustra esse uso. A linha page.on_keyboard_event = on_teclado faz com que eventos de teclado acionem a função on_teclado. O objeto e leva as propriedades booleanas e.ctrl, e.alt, e.shift, e.meta e o texto e.key.
O resultado desse código, quando executado e após o pressionamento simultaneo das teclas CTRL-ALT-SHIFT-J, está mostrado na figura 13.
O exemplo acima ilustra ainda uma característica da POO (programação orientada a objetos). Como temos que criar 5 caixas de texto iguais, exceto pelo seu valor, criamos uma classe BtControle (herdando de ft.TextField) e criamos cada botão como instância dessa classe. No código manipulamos a visibilidade desses botões.
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 é um framework para o desenvolvimento (um SDK) de interface do usuário de software de código aberto criado pelo Google, e lançado em maio de 2017. Em outras palavras ele serve para a construção de GUIs (Interfaces Gráficas de Usuários), e é usado para desenvolver aplicativos em diversas plataforma usando um único código base.
A primeira versão do Flutter (Flutter Sky) rodava no sistema operacional Android e, segundo seus desenvolvedores, podia renderizar 120 quadros por segundo. O Flutter 1.0, a primeira versão estável do framework, foi lançado em 2018. Em 2020 surgiu o kit de desenvolvimento de software Dart (SDK) versão 2.8 com o Flutter 1.17.0, em que foi adicionado suporte para API que melhora o desempenho em dispositivos iOS, juntamente com novos widgets de materiais e ferramentas rastreamento em rede.
O Flutter 2 foi lançado pelo Google em 2021, incluindo um novo renderizador Canvas Kit para aplicativos baseados na web e aperfeiçoamento no suporte de aplicativos web e desktop para Windows, macOS e Linux. Em setembro de 2021, o Dart 2.14 e o Flutter 2.5 foram lançados, com melhorias para o modo de tela cheia do Android e a versão mais recente do Material Design do Google. Em 2022 o Flutter foi lançado expandindo o suporte a plataformas, com versões estáveis para Linux e macOS em arquiteturas diversas. O Flutter 3.3 trouxe interoperabilidade com Objective-C e Swift e uma versão preliminar de um novo mecanismo de renderização chamado “Impeller”. Em janeiro de 2023 foi anunciado o Flutter 3.7.
Aplicativos elaborados com Flutter são baseados em Widgets. Widgets são pequenos blocos de aplicativo com representação gráfica, que podem ser inseridos dentro de ambientes gráficos mais gerais , usados em aplicativos web ou desktop. Eles aparecem na forma de botões, caixas de texto, relógios e calendários selecionáveis, menus drop-down, etc, e servem basicamente para a interação com o usuário, ou recebendo inputs, como um clique em um botão, ou exibindo resultados, como um texto de resposta ou um gráfico.
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
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:
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.
Figura 1
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)
Figura 2
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.
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.
Entidades HTML são códigos usados para a exibição de símbolos que não estão diretamente acessíveis pelo teclado, dentro de uma página de HTML (páginas da WEB). Eles podem representar caracteres técnicos, matemáticos, símbolos de moedas, sinais gráficos e muitos outros. A tabela lista os códigos htmlhexadecimal e decimal. Por exemplo, a página:
<html>
<body>
<h1>Exibindo html </h1>
<p>Símbolo de um naipe espada de baralho: ♣ - ♣ - &♣; - ♣.</p>
<p>Símbolo matemático da integral de contorno: ∮ - ∮ - ∮ - ∮.
</body>
</html>
seria exibida da seguinte forma (dependendo, é claro, do browser e das instruções css da página):
Exibindo html
Símbolo de um naipe espada de baralho: ♣ – ♣ – ♣ – ♣.
Símbolo matemático, integral de contorno: ∮ – ∮ – ∮ – ∮.
Nem todos os browsers podem exibir corretamente todos os símbolos. Um código na primeira coluna da tabela representa uma falha de exibição. Passe o mouse sobre os caracteres da 1ª coluna para ampliar.
Uma plataforma WEB é uma coleção de tecnologias desenvolvidas para a criação, desenvolvimento e distribuição de páginas e aplicativos na internet.
Front End
Elas envolvem tecnologias de níveis variados de complexidade sendo que as mais básicas, e mais amplamente empregadas, são a linguagem HTML, Hyper Text Markup Language e CSS, Cascade Style Sheets. HTML é responsável pela estrutura de uma página, marcando títulos, parágrafos, links, blocos de texto, inserção de imagens, etc. CSS são arquivos de formatação, usados para definir tamanhos e tipos de fontes, cores, espaçamentos, etc. A linguagem javascript é usada pela maioria dos sites para executar ações do lado do navegador, tais como atualização dinâmica de conteúdo, envio de respostas do usuário para o servidor e controle de arquivos multimídia. Diversas novas tecnologias são baseadas no javascript, como AngularJS, Node.js e React.
Back End
Além das tecnologias usadas no navegador quase sempre é necessário usar linguagens de programação que alteram a construção de páginas e aplicativos no lado do servidor. O acesso a um banco de dados é a utilização mais comum. As mais empregadas são o PHP, Java, Python, Ruby on Rails e ASP.Net. Cada uma delas oferece um número de plataformas, cada em com suas vantagens e desvantagens, como agilidade de desenvolvimento e velocidade de resposta das páginas. As plataformas são basicamente ambientes desenvolvidos para resolver problemas determinados. Por ex., o PHP é a base de CMSs como o WordPress, útil para a construção de blogs e exibição de textos, ou desenvolvimento de sites de vendas online. O Python é usado na plataforma Django, criado e muito empregado nos sites de jornalismo, onde as atualizações devem ser rápidas e constantes. Todas essas tecnologias demandam algum tempo de aprendizado.
A Arquitetura básica da Web
Partindo do usuário, a web é vista primeiro em um navegador, como o Firefox, Chrome ou Safari. O navegador envia uma URL para a rede e recebe em resposta páginas HTML, arquivos de estilo CSS, arquivos Javascript e de multimídia, como áudios e vídeos. O navegador é um aplicativo que pode agrupar esse conteúdo e apresentá-los de forma compreensível. Navegadores modernos podem exibir outros formatos de arquivo, como pdf e planilhas, nativamente ou por meio de plugins. Além disso eles podem interpretar e executar código javascript, também recebidos do servidor.
A requisição do cliente (o navegador) contém o endereço do servidor que são computadores dedicados à esse serviço em diversas partes do mundo. O servidor recebe a solicitação e monta uma resposta. Para isso ele pode usar diversas tecnologias diferentes, acesso a banco de dados, código de frameworks montadores de páginas, etc. O servidor pode, por sua vez, acessar outros servidores para montar o conteúdo que será enviado para o cliente. O resultado será uma página em formato HTML gerada pelo servidor e apropriada para ser exibida.
Para a comunicação entre cliente e servidor é usado o HTTP, um protocolo de transferência de hipertexto que define como dados devem trafegar na rede. O HTTPS é um aperfeiçoamento que adiciona uma camada de segurança entre a emissão e resposta. Páginas HTML enviadas pelo servidor podem ser estáticas, enviadas como estão gravadas, sem processamento. O mais comum hoje é que as páginas sejam dinâmicas, montadas por solicitação, processadas por código de programa, geralmente ASP ou .NET, Java, Rails ou Python. Um servidor de aplicativos é o mecanismo que executa a lógica de negócios no lado do servidor que, em geral, está separado do servidor de páginas.
As bibliotecas gráficas do Python, Matplotlib, Seaborn e Bokeh aceitam cores no sistema css e a maioria dos nomes ou shortcuts. O código hexadecimal é formado por 3 números, de 0 até 255 (ou 00 até FF), para daterminar a intensidade das cores R, G, B (vermelho, verde, azul). Dessa forma FFFFFF representa (255, 255, 255), ou branco; 000000 representa (0, 0, 0) ou preto. FF0000 é vermelho puro, 00FF00 é verde puro, 0000FF é azul puro. Além disso se pode utilizar o parâmetro alpha=a, onde 0 ≤ a ≤1 para a opacidade da cor, sendo 1 opacidade máxima.
Cores podem ser definidas como:
no formato hexadecimal (como #123456), com strings do CSS4 rgb(), rgba() ou hsl(): como rgb(0 127 0 / 1.0), rgba(255, 0, 127, 0.6) ou hsl(60deg 100% 50% / 1.0).
como tupla de inteiros (r, g, b), onde r, g, b são inteiros entre 0 e 255.
como a tuple (r, g, b, a), onde r, g, b são inteiros entre 0 e 255 e a é número de ponto flutuante entre 0 e 1.
um inteiro sem sinal representando RGBA values no padrão 0xRRGGBBAA, 0xffff00ff ou 0xff0000ff.
Segue uma tabela com as cores em código hexadecimal e seu nome.