Flet: Dismissible

Outro controle de Layout

Controle Dismissible

Dismissible é um controle que pode ser arrastado em uma direção especificada. Quando arrastado ele gera eventos que podem ser capturados para efetuar ações sobre o aplicativo. O conteúdo desenhado dentro do controle permanece visível e acompanha o arrasto do controle que o contém. O controle é dispensado (eliminado) do aplicativo quando atinge um deslocamento mínimo determinado em código.

Nota: chamaremos de dispensa a ação de dismiss do controle. Antes da dispensa o controle encolhe em tamanho, gerando os eventos descritos abaixo.

Um exemplo bem reduzido de uso do dismiss é mostrado abaixo.

import flet as ft
import copy

def main(page):

    dm1=ft.Dismissible(content=ft.Text("Primeira Linha", size=20, bgcolor=ft.colors.AMBER_100))
    (dm2 := copy.deepcopy(dm1)).content.value="Segunda Linha"
    (dm3 := copy.deepcopy(dm1)).content.value="Terceira Linha"

    page.add(ft.Column([dm1, dm2, dm3]))
ft.app(main)

Esse código gera o seguinte aplicativo, mostrado na figura 1 em três estados:

Figura 1: Três linhas de controle Dismissible: estado original, arrasto para a direita, linha 1 eliminada.

Nesse exemplo criamos um controle dm1=ft.Dismissible() e o copiamos, usando deepcopy para dois outros controles. O método copy.deepcopy é necessário para que controles internos sejam copiados e a cópia seja feita por valor. Isso significa que dm2 e dm3 são novos objetos e não apenas uma nova referência para dm1. Com isso podemos mudar dm2.content.value="Segunda Linha" (o valor do conteúdo interno).

Nota: O operador walrus (:=) de atribuição tem o efeito de fazer uma atribuição e já retornar o objeto para operações posteriores. Portanto:

    (dm2 := copy.deepcopy(dm1)).content.value="Segunda Linha"
    # é o mesmo que
    dm2 = copy.deepcopy(dm1)
    dm2.content.value="Segunda Linha"

Em um caso mais elaborado podemos inserir linhas formatadas (por exemplo inserindo o texto dentro de containers), capturar outros eventos e definir outras propriedades. A figura 2 mostra um caso um pouco mais elaborado e o exemplo de uso de Dismissible no final desse texto mostra um código mais completo.

Figura 2: linhas formatadas com container: arrasto da linha 1 para ambos os lados e exclusão da linha 1.

Propriedades de Dismissible

Dismissible tem as seguintes propriedades:

Propriedade Descrição
background um controle que fica por trás do controle principal que exibe conteúdo.
Se secondary_background também for especificado esse controle secundário aparece como fundo quando o conteúdo é arrastado para os lados ou verticalmente.
content um controle filho que fica exibido dentro do Dismissible e se move com ele.
cross_axis_end_offset especifica um valor para deslocamento vertical quando o controle é movido horizontalmente, fazendo o controle se mover na diagonal, para baixo se o valor for positivo, para cima se negativo.
direction direção usada para dispensar o controle. Os valores possível estão no enum DismissDirection:

  • DismissDirection.UP
  • DismissDirection.DOWN
  • DismissDirection.LEFT
  • DismissDirection.RIGHT
  • DismissDirection.START_TO_END
  • DismissDirection.END_TO_START
dismiss_thresholds o valor mínimo de deslocamento considerado para dispensar o controle. A partir desse valor o controle é removido do aplicativo. Por exemplo, se o valor é de 0.4 (o default) a barra tem que ser movida de 40% ou mais de sua extensão para ser dispensada. Seu valor é especificado como um dicionário tendo como chaves um objeto DismissDirection e o valor um valor numérico entre 0.0 e 1.0. O bloco de código mostra isso:

    ft.Dismissible(
    # ...
    dismiss_thresholds={
        ft.DismissDirection.VERTICAL: 0.1,
        ft.DismissDirection.START_TO_END: 0.7
    }
)

Esse dicionário define que os valores mínimos (thresholds) são 10% na vertical e 70% na horizontal, se o movimento for do início para o final (para a direita).

movement_duration a duração do movimento para que o controle seja dispensado ou retorne para sua posição original.
resize_duration a duração do movimento de contração antes que o evento de dispensa seja acionado.
secondary_background um controle desenhado por tras do conteúdo principal, que é exposto quando o conteúdo é arrastado para os lados. Só pode ser especificado se o background também for definido.

Eventos de Dismissible

Evento Descrição
on_confirm_dismiss um evento que ocorre antes que o controle seja dispensado, dando a oportunidade de se confirmar ou negar essa operação. O controle não pode ser arrastado novamente até que essa pendência seja resolvida.

Para resolver essa pendência deve ser chamado o método confirm_dismiss(dismiss) passando um booleano: True para confirmar, provocando a dispensa do controle, False para negar, o que faz com que ele volte para sua posição original. Uma possibilidade consiste em apresentar a pergunta em uma caixa como AlertDialog.

on_dismiss dispara quando o controle é dispensado. Antes da dispensa o controle é contraído, diminuindo de tamanho.
on_resize dispara quando o controle muda de tamanho, o que ocorre, por exemplo, na contração ao ser dispensado.
on_update dispara quando o controle é arrastado, independentemente de ser ou não dispensado.

Método de Dismissible

Método Descrição
confirm_dismiss(dismiss: bool) resolve a pendência quando a dispensa é requerida. Esse evento pode ser chamado quando se trata do evento on_confirm_dismiss.

Exemplo de uso de Dismissible

Um exemplo mais completo de código está listado abaixo. Para manter a simplicidade e não usar caixas de diálogo (ainda não consideradas nessas notas) o evento on_confirm_dismiss=informe_dispensa apenas aciona a função informe_dispensa que registra na caixa de informações as ações de remoção dos controles pela direita ou pela esquerda, sem pedir a confirmação.

import flet as ft

def main(page):

    def informe_dispensa(e: ft.DismissibleDismissEvent):
        if e.direction == ft.DismissDirection.END_TO_START:
            msg.value += "\nSaída pela direita!"
        else:
           msg.value += "\nSaída pela esquerda!"
        e.control.confirm_dismiss(True)
        page.update()

    def dispensar(e):
        coluna.controls.remove(e.control)
        page.update()

    def atualizar(e: ft.DismissibleUpdateEvent):
        if e.direction == ft.DismissDirection.END_TO_START:
            icone.name=ft.icons.ARROW_BACK
        else:
            icone.name=ft.icons.ARROW_FORWARD
        texto.value=f"Progresso: {e.progress}"
        page.update()

    cor = [ft.colors.AMBER_100,ft.colors.BLUE_GREY_100, ft.colors.BLUE_50]
    array=[
        ft.Dismissible(
            content=ft.Container(
                ft.Text(f"Esta é a linha {i+1}", size=15),
                height=35, width=380, 
                border = ft.border.all(2, ft.colors.BLACK54),
                bgcolor= cor[i%2],
                padding = ft.padding.all(6),
                margin = ft.margin.all(2)
            ),
            dismiss_direction=ft.DismissDirection.HORIZONTAL,
            background=ft.Container(bgcolor=ft.colors.CYAN_100),
            secondary_background=ft.Container(bgcolor=ft.colors.BROWN_50),
            dismiss_thresholds={
                ft.DismissDirection.END_TO_START: 0.2,
                ft.DismissDirection.START_TO_END: 0.2
            },
            on_dismiss=dispensar, 
            on_update=atualizar,
            on_confirm_dismiss=informe_dispensa
        ) for i in range(10)
    ]

    coluna=ft.Column(array, spacing=1)
    icone=ft.Icon(name=ft.icons.KEYBOARD, color=ft.colors.BLUE, size=30)
    texto=ft.Text("Posição dos controles", size=18)
    linha_info = ft.Row([icone, texto])
    msg = ft.Text(f"Eventos capturados (Dispensar):\n" , size=15)
    ver_acao = ft.Container(
        msg,
        height=100, width=380, 
        expand=True,
        border = ft.border.all(2, ft.colors.BLACK54),
        bgcolor= cor[2],
        padding = ft.padding.all(12),
        margin = ft.margin.all(3)
    )

    page.add(coluna, linha_info, ver_acao)

ft.app(main)
Figura 3: (a) o estado inicial do aplicativo; (b) arrasto da linha 1 para a direita; (c) arrasto da linha 2 para a esquerda; (d) removidas linhas 1 e 3.

A operação array=[(objeto(i)) for i in range(10)] cria uma lista com 10 objetos objeto(i) passando o valor de i internamente para o objeto em cada loop.

A função informa_dispensa(e) recebe o evento e que contém uma descrição da direção do evento de arrastar. Ela atualiza a variável msg que é exibida dentro do container ver_acao.

A função dispensar(e) usa a referência ao controle e.control para removê-lo, usando coluna.controls.remove(e.control).

A função atualizar(e) escolhe um ícone de direção e insere texto mostrando a porcentagem de arrasto a cada momento, atualizando a variável texto que está em linha_lnfo.

Flet: Exemplo 4

Controles de Layout

Card, Tabs, SafeArea, Divider, VerticalDivider, ExpansionPanelList são outros controles que facilitam a divisão, exibição ou ocultação de conteúdo dentro de uma página de aplicativo.

Seguem-se alguns exemplos de códigos usando esses controles.

Exemplo de uso de Card

Leia sobre a descrição do controle, suas propriedades e evento em Controles de Layout: Card.
O código abaixo exibe a construção de uma página com 3 cards.

import flet as ft

def main(page):
    page.title = "Exemplo usando \"card\""

    listitulo = ft.ListTile(leading=ft.Icon(ft.icons.ACCESS_ALARM,  size=50, color="#D3143E"),
                            title=ft.Text("Compromisso: 28 de junho de 2024"),
                            subtitle=ft.Text("Aniversário de seu filho. Não esqueça o presente!"),)
    
    def apagar(e):
        listitulo.title= ft.Text("")
        listitulo.subtitle=ft.Text("* apagado", color="#aaaaaa")
        cartao.color="WHITE"
        page.update()

    def proximo(e):
        listitulo.title=ft.Text("Compromisso: 01 de agosto de 2024")
        listitulo.subtitle=ft.Text("Viagem para São Paulo", color="#000000")
        cartao.color="#CBEBA8"
        page.update()

    bt1 = ft.TextButton("Apagar", on_click=apagar)
    bt2 = ft.TextButton("Próximo compromisso", on_click=proximo)

    linha = ft.Row([bt1, bt2], alignment=ft.MainAxisAlignment.END)
    coluna = ft.Column([listitulo, linha,])
    contem = ft.Container(coluna, width=500, padding=20,)
    cartao = ft.Card(contem, color="YELLOW", shadow_color="white", elevation=8.0)

    page.add(cartao)

ft.app(target=main)

Na figura 1 o resultado desse código, quando executado.

Figura 1: O Card amarelo mostra o estado incial do app. Card verde, se for clicado “Próximo compromisso”. Último Card, se o clique for no botão “Apagar”.

Tabs

Leia sobre a descrição do controle, suas propriedades e evento em Controle Tabs.

O controle Tabs, usualmente chamado de guias ou abas em português, é usado para separar conteúdo de categorias diferentes, facilitando a navegação do usuário. Ele simula a marcação de bookmarks em um livro físico, para indicar setores de conteúdos diversos, sendo um padrão de desenho com o qual os usuários estão acostumados, no desktop ou web.

Exemplo de uso de Tabs

O código abaixo exibe a construção de uma página o uso de Tabs.

import flet as ft

def main(page: ft.Page):
    page.bgcolor=ft.colors.BROWN_200
    
    ct_interno=ft.Container(content=ft.Text("Conteúdo do Tab 1 dentro de um Card",
                            size=20, weight=ft.FontWeight.BOLD), padding=45)
    
    card = ft.Card(content= ct_interno, elevation=20, expand=1)

    ct_tab1 = ft.Container(content=card, bgcolor="#BAE3ED", alignment=ft.alignment.center)

    tab1 = ft.Tab(text="Tab 1", content=ct_tab1)

    ct_tab2 = ft.Container(content=ft.Text("Conteúdo de texto do Tab 2", size=30),
                           bgcolor="#BBF3EF",
                           margin = ft.margin.all(10.0),
                           padding=ft.padding.all(1.0),
                           alignment=ft.alignment.top_right,
                           border=ft.border.all(2, "#55555"),
                           border_radius=20)

    tab2 = ft.Tab(text="Tab 2", icon=ft.icons.SEARCH, content=ct_tab2)

    ct_tab3 = ft.Container(content=ft.Text("Conteúdo de texto do Tab 3", size=30),
                           bgcolor="#FCF5CC", alignment=ft.alignment.bottom_left)

    tab3 = ft.Tab(text="Tab 3", icon=ft.icons.SETTINGS, content=ct_tab3)

    tabs = ft.Tabs(tabs=[tab1, tab2, tab3],
                   selected_index=0,
                   divider_color="#ABCAD0",
                   label_color="WHITE",
                   unselected_label_color="BLACK",
                   overlay_color = "YELLOW",
                   indicator_padding=10,
                   indicator_color="RED")

    page.add(ft.Container(tabs, bgcolor=ft.colors.BROWN_200, expand=1))

ft.app(target=main)

O resultado desse código está mostrado na figura 2.

Figura 2: Tabs 1, 2 e 3 exibidos, com cliques nas respectivas guias ou abas (tab).

O primeiro tab exibe um card com texto interno, centralizado. O segundo tab tem um container formatado com bordas arredondadas e texto no alto, à direita. O terceiro tab tem texto em baixo, à esquerda.

Observe que no código acima, e na maioria dos blocos de código nessas notas, os controles são montados por partes, separando controles que serão depois inseridos dentro de outros. Para montar o tab1, criamos ct_interno, um container com um texto e o inserimos dentro de card, que é inserido em ct_tab1 e finalmente em tab1. Isso é feito por motívo didático, para que o código fique mais fácil de ser entendido, e não tem efeito no resultado no momento da execução. Esse estilo é uma preferência do programador. Uma forma alternativa para a construção do mesmo controle pode ser vista abaixo.

    tab1 = ft.Tab(
        text="Tab 1",
        content=ft.Container(
            content=ft.Card(
                content=ft.Container(
                    content=ft.Text("Conteúdo do Tab 1 dentro de um Card",
                    size=20, weight=ft.FontWeight.BOLD), padding=45
                ),
                elevation=20, expand=1
            ),
            bgcolor="#BAE3ED",
            alignment=ft.alignment.center
        )
    )

De qualquer forma é importante estabelecer um critério de indentação e se manter coerente com ele.

SafeArea

SafeArea insere conteúdo com preenchimento (padding, as distâncias entre as margens internas do container com as bordas externas do controle) de modo a impedir que os elementos de interface do sistema operacional se sobreponham às do aplicativo. Leia sobre a descrição do controle, suas propriedades em Controles de Layout: SafeAreaCard.

Um exemplo é mostrado no código abaixo.

import flet as ft

class Contador:
    contagem = 0

def main(page: ft.Page):    
    def add_click(e):
        cliques.contagem += 1
        contador.value = f"Contador: {cliques.contagem}"
        contador.update()

    page.horizontal_alignment=ft.CrossAxisAlignment.CENTER
    page.vertical_alignment=ft.MainAxisAlignment.CENTER
    page.floating_action_button = ft.FloatingActionButton(icon=ft.icons.ADD, on_click=add_click)

    cliques = Contador()

    contador = ft.Text("Contador: 0", size=30, color="WHITE", weight=ft.FontWeight.BOLD)

    page.add(
        ft.SafeArea(
            expand=False,
            content=ft.Container(
                content=contador,
                padding=35, width=300, height=190,
                alignment=ft.alignment.center,
                bgcolor="BLUE",
                gradient=ft.LinearGradient(colors=[ft.colors.BLACK, ft.colors.BLUE_900]),
                border=ft.border.all(3, ft.colors.BLUE_100),
                border_radius=ft.border_radius.all(10)
            )
        )
    )

ft.app(main)

A execução desse código gera um aplicativo mostrado na figura 3.

Figura 3: Controle SafeArea tendo um container no seu interior.

Observe que, para manter um contador global foi criada a classe e propriedade State.counter. O objeto cliques = Contador() tem, portanto a propriedade cliques.contagem, que é incrementada a cada clique no botão floating_action_button.

Divider e VerticalDivider

São controles usados para desenhar uma linha divisória, horizontal e vertical, respectivamente. Leia sobre a descrição dos controles e suas propriedades em Controle Divider e VerticalDivider.

import flet as ft

def main(page: ft.Page):
    ct1 = ft.Container(bgcolor="#797EF6", expand=True)
    ct2 = ft.Container(bgcolor="#4ADEDE", expand=True)
    divisorVertical = ft.VerticalDivider(width=50, thickness=10, color="#960cc9")
    ct3 = ft.Container(bgcolor="#1AA7EC", expand=True)
    ct4 = ft.Container(bgcolor="#1E2F97", expand=True)
    divisorHorizontal= ft.Divider(height=50, thickness=40, color="#F6E750")

    page.add(
        ft.Row([ct1, divisorVertical, ct2], spacing=0, expand=True),
        ft.Column([ct3, divisorHorizontal, ct4], spacing=0, expand=True)
    )

ft.app(target=main)

O código executado é renderizado como a figura 4.

Figura 4: Divider e VerticalDivider.

A única diferença entre propriedades de Divider e VerticalDivider são as propriedades Divider.height e VerticalDivider.width.

ExpansionPanel e ExpansionPanelList

ExpansionPanelList é o controle usado para criar painéis expansíveis, que podem ser contraídos até um mínimo, expandidos para exibir conteúdo, ou removidos da página. ExpansionPanelsList recebe uma lista de controles ExpansionPanel. Leia sobre a descrição dos controles e suas propriedades em Controle ExpansionPanelList e ExpansionPanel.

import flet as ft

class Painel(ft.ExpansionPanel):
    def __init__(self, cabecalho, titulo, texto, cor, botao=None, indice=0):
        super().__init__()
        self.can_tap_header=True
        self.bgcolor=cor
        self.header=ft.Text("  " + cabecalho, size=22, weight=ft.FontWeight.BOLD)
        self.titulo=ft.Text(titulo, size=18)
        self.texto=ft.Text(texto, size=17)
        self.conteudo=ft.ListTile(
            title=self.titulo,
            subtitle=self.texto,
            trailing=botao
        )
        self.content=self.conteudo
        self.indice = indice

def main(page: ft.Page):
    cor = [ft.colors.GREEN_100, ft.colors.BLUE_100, ft.colors.RED_100,
           ft.colors.WHITE10, ft.colors.BLUE_500, ft.colors.WHITE70]
    
    def pegar_painel(index):
        for pn in panel.controls:
            if index == pn.indice: return pn
        return None    
            

    def mudou(e: ft.ControlEvent):
        if int(e.data)==4: return
        atividade.conteudo.subtitle.value += f"\nO painel {e.data} foi modificado"
        page.update()

    def apagar(e: ft.ControlEvent):
        pn = pegar_painel(e.control.data)
        if pn:
            panel.controls.remove(pn)
            atividade.conteudo.subtitle.value += f"\nFoi apagado o Painel {e.control.data}"
        page.update()

    panel = ft.ExpansionPanelList(
        elevation=8,
        spacing=6,
        expand_icon_color=ft.colors.AMBER_100,
        divider_color=ft.colors.CYAN_ACCENT_100,
        on_change=mudou
    )
    panel.controls.append(
        Painel("Atividades Online", "Lista das ações realizadas no aplicativo.", "",
               cor[4], None,0
        )
    )
  
    panel.controls.append(
        Painel(
            "Painel 1", "Conteúdo do painel 1", "Clique no ícone para apagar o painel 1.", cor[0],
            ft.ElevatedButton("Apagar", icon=ft.icons.DELETE, width=150, height=30, data=1,
            on_click=apagar), 1
        )
    )

    panel.controls.append(
        Painel(
            "Painel 2", "Conteúdo do painel 2", "Clique no ícone para apagar o painel 2.", cor[1],
            ft.ElevatedButton("Apagar", icon=ft.icons.DELETE, width=150, height=30, data=2,
            on_click=apagar), 2
        )
    )

    panel.controls.append(
        Painel(
            "Painel 3", "Conteúdo do painel 3", "Clique no ícone para apagar o painel 3.", cor[2],
            ft.ElevatedButton("Apagar", icon=ft.icons.DELETE, width=150, height=30, data=3,
            on_click=apagar), 3
        )
    )

    atividade = Painel("Resumo de Atividades", "", "Conteúdo de texto da informação de uso", cor[5])
    panel.controls.append(atividade)

    page.add(panel)

ft.app(target=main)

O resultado pode ser visto na figura 5.

Figura 5: à esquerda o painel com todos os painéis recolhidos. À direita o estado do app após algumas operações de expandir e apagar.

Como são gerados no código vários painéis flet.ExpansionPanel é interessante criar a classe Class Painel que herda de ExpansionPanel, personalizando na inicialização cada caso a ser inserido. O último dos painéis exibidos, atividade é usado para armazenar e exibir as ações feitas no aplicativo, no campo atividade.conteudo.subtitle.value. Essas ações são executadas nos eventos ExpansionPanelList.on_change e pelo clique nos botões ElevatedButton colocados em cada painel, exceto o último, e pela ação de apagar um painel. Os painéis são identificados pela propriedade painel.indice, usada para encontrar e deletar um deles.

Outros controles de Layout


Outros controles de montagem de Layout

Nessa seção veremos os controles Card, Tabs, SafeArea, Divider, VerticalDivider, ExpansionPanelList, todos eles widgets que facilitam a divisão, exibição ou ocultação de conteúdo dentro de uma página de aplicativo.

Card

Um card no flet é um painel de bordas arredondadas e com um sombreado colorido simulando uma elevação sobre o fundo onde está desenhado.

Propriedades de Card

Propriedade Descrição
color indica a cor de fundo do card.
content o controle a ser exibido pelo controle. card só pode ter um filho. Para inserir mais de um widget dentro de card insira nele controles que podem ter vários filhos, como Row, Column ou Stack.
elevation: controla o tamanho da sombra abaixo do card, simulando que esse esteja elevado sobre sua base. O valor default é 1.0.
margin distância entre as bordas e a borda de seu container.
shadow_color cor da sombra abaixo do card.
shape a forma do card. Esse valor é uma instância de uma das implementações:

  • StadiumBorder
  • RoundedRectangleBorder
    • radius: raio da borda, uma instância da classe BorderRadius ou um número.
  • CircleBorder
  • BeveledRectangleBorder
    • radius: raio da borda, uma instância da classe BorderRadius ou um número.
  • ContinuousRectangleBorder
    • radius: raio da borda, uma instância da classe BorderRadius ou um número.

O formato default é RoundedRectangleBorder com radius=4.0.

surface_tint_color cor usada como sobreposição de cor para indicar elevação.
Se ajustada para None, nenhuma sobreposição será aplicada. Caso contrário, esta cor será composta sobre a cor de fundo com opacidade relacionada à elevação e usada para desenhar fundo do card. O default é None.
Não consegui usar essa propriedade. Se algum leitor tem essa informação peço que me envie.

Exemplo de uso de Card

Um exemplo mais completo pode ser encontrado em Uso de Card.

import flet as ft

def main(page):
    icon=ft.Icon(ft.icons.ADB_ROUNDED, color=ft.colors.BLUE, size=50 )
    texto1 = ft.Text("Título do Card", size=30)
    texto2 = ft.Text("Conteúdo de texto do Card")
    linha = ft.Row([icon, ft.Column([texto1, texto2])])
    cartao = ft.Card(ft.Container(linha, width=300, padding=20))
    page.add(cartao)

ft.app(target=main)



Como vemos, card tem um único filho, um container que, que sua vez, tem uma linha com um ícone e uma coluna (com dois controles de textos).

Tabs

O controle Tabs, usualmente chamado de guias ou abas em protuguês, é usado para separar conteúdo de categorias diferentes, facilitando a navegação do usuário. Ele simula a marcação de bookmarks em um livro físico, para indicar setores de conteúdos diversos, sendo um padrão de desenho com o qual os usuários estão acostumados, no desktop ou web.

Propriedades de Tabs

Propriedade Descrição
animation_duration tempo de duração, em milisegundos, da animação de trocas entre tabs. O default é 50.
divider_color a cor do divisor entre os tabs ou guias.
indicator_border_radius o raio de arredondadmento de cantos do indicador.
indicator_border_side cor e largura da linha horizontal desenhada abaixo da guia selecionada. Essa linha indica que uma guia está em uso.
indicator_color cor da linha exibida abaixo da guia selecionada.
indicator_padding localiza a linha sublinhada abaixo da guia selecionada, relativa a sua borda. A propriedade indicator_tab_size pode ser usada para indicar a localização das bordas. A propriedade é Falsa para centralizar o conteúdo ou True para usar toda a extensão do tab.
indicator_tab_size booleano, True para que o indicador ocupe todo o tab.
label_color cor dos rótulos das guias selecionadas.
overlay_color define a resposta da cor inicial quando o controle está em foco, hover ou splash. Quando especificada essa propriedade usa MaterialState.FOCUSED, MaterialState.HOVERED ou MaterialState.PRESSED.
selected_index índice do tab selecionado. Pode ser ajustado programaticamente ou selecionado pelo usuário.
scrollable booleano, indica se a barra de tab pode ser rolada horizontalmente. Se scrollable=True cada tab tem a largura necessária para comportar seu rótulo e o controle inteiro é rolável. Se scrollable=False o espaço disponível é distribuído igualmente entre os tabs.
tab_alignment especifica o alinhamento horizontal dos tabs dentro do controle Tabs.
A propriedade TabAlignment é um enum com os valores:

  • NONE
  • START (é o default se scrollable=True)
  • START_OFFSET
  • FILL (é o default se scrollable=False)
  • CENTER
tabs uma lista com os controles tab a serem exibidos.
unselected_label_color a cor das guias (tabs) não selecionadas.

Evento de Tab

Evento Descrição
on_change Dispara quando o índice da guia selecionada (selected_index) é alterado.

Um exemplo de uso de tabs pode ser visto em Uso de Tabs.

Propriedades de Tab

Propriedade Descrição
content o controle a ser exibido quando o Tab é selecionado.
icon ícone a ser exibido à esquerda do texto de Tab.
tab_content um controle com o conteúdo personalizado a substituir o texto e o ícone.
text o nome ou rótulo do Tab.

SafeArea


Outro controle voltado para a diagramação das páginas de aplicativos, SafeArea insere conteúdo com preenchimento (padding, as distâncias entre as margens internas do container com as bordas externas do controle) de modo a impedir que os elementos de interface do sistema operacional se sobreponham às do aplicativo. Ele recua o conteúdo interno evitando sobreposição pela barra de status no alto da tela.

Em particular esse recurso permite o recuo do conteúdo para evitar obstrução pelo “entalhe” no iPhone, (notch) ou outras modificações na geometria das telas em aparelhos menores.

Propriedades de SafeArea

Propriedade Descrição
bottom booleano, se o aplicativo deve evitar intrusão do SO na parte inferior da tela. Default é True.
content recebe o controle filho a ser exibido dentro SafeArea.
left booleano, se o aplicativo deve evitar intrusão do SO na parte esquerda da tela. Default é True.
maintain_bottom_view_padding booleano, default é False. Se True o controle SafeArea deve manter em exibição o MediaQueryData.viewPadding no parte inferior da tela, em vez do MediaQueryData.padding (que é o default).

Por exemplo, caso um teclado na tela seja exibido acima da SafeArea, o preenchimento interno (padding) pode ser mantido abaixo da obstrução sem ser ocultado. Pode ser útil quando o layout contém controles flexíveis que podem ser movidos na interface do usuário. Definir SafeArea=True evita alteração na IU.

minimum preenchimento (padding) mínimo a ser aplicado. Quando o valor de minimum é especificado ele será adotado como padding exceto se esse valor não for suficiente para garantir o preenchimento da área segura.
right booleano, se o aplicativo deve evitar intrusão do SO na parte direita da tela. Default é True.
right booleano, se o aplicativo deve evitar intrusão do SO na parte superior da tela, que em geral contém uma barra de status. Default é True.
Nota: Denotamos nessa tabela, SO = sistema operacional, IU = interface de usuário (ou interface gráfica)

Um exemplo de uso do controle pode ser visto em Uso de SafeArea.

Divider


Um controle voltado para a diagramação das páginas de aplicativos, desenhando uma linha horizontal externa sobre o container pai, com uma linha interna de mesma extensão mas altura possível diferente. Ele admite preenchimento (padding) em ambas as bordas. Pode ter cor, comprimento e espessura definidas.

Propriedades de Divider

Propriedade Descrição
color cor da linha.
height altura da linha, no sentido horizontal. O centro da linha, desenhada no container, fica no centro de sua altura horizontal. O default é 16.0 se esse valor for null.
thickness espessura da linha desenhada dentro do Divider. Se thickness=0.0 a altura será de 1 pixel. Se esse valor for null esse será 0.0.

Exemplos de uso dos controles Vertical e VerticalDivider podem ser vistos em Uso de Divider.

VerticalDivider

Um controle voltado para a diagramação das páginas de aplicativos, desenhando uma linha vertical sobre o container pai, com exatamente análoga à Divider mas com propriedades cor, largura e espessura. A única propriedade diferente é width que define a largura no sentido horizontal da linha externa.

color cor da linha.
width largura linha externa. O centro da linha, desenhada no container, fica no centro de sua altura horizontal. O default é 16.0 se esse valor for null.
thickness espessura da linha dentro do VerticalDivider. Se thickness=0.0 a altura será de 1 pixel. Se esse valor for null esse será 0.0.

ExpansionPanel e ExpansionPanelList

O controle ExpansionPanelList permite a criação de painéis expansíveis, ou seja, podem ser contraídos até um mínimo, expandidos para exibir conteúdo, ou removidos da página. Como organizador de conteúdo na diagramação de uma interface gráfica ele é similar aos Tabs, mas separando o espaço da página em faixas horizontais. Além disso vários painéis podem ficar simultaneamente abertos.

O controle ExpansionPanelsList recebe uma lista de controles ExpansionPanel.

Propriedades de ExpansionPanelList

Propriedade Descrição
controls uma lista de controles ExpansionPanel a serem exibidos dentro do controle pai.
divider_color a cor do divisor quando ExpansionPanel.expanded=False.
elevation define a elevação dos controles filhos ExpansionPanel quando extendido. O valor default é 2.
expanded_header_padding define o espaçamento interno (padding) em torno da cabeçalho quando extendido. O valor default é padding.symmetric(vertical=16.0). Leia mais sobre padding em Propriedades de Containers.
expanded_icon_color cor do ícone. Default é colors.BLACK_54 em modo de tema claro e colors.WHITE_60 em modo escuro.
spacing tamanho do intervalo entre os ExpansionPanels quando extendidos.

Evento de ExpansionPanelList

on_change dispara quando um ExpansionPanel muda de estado entre expandido e colapsado. A propriedade de evento e.data contem o índice do painel ExpansionPanel que causou o evento.

Propriedades de ExpansionPanel

Os objetos ExpansionPanel são os filhos diretos de ExpansionPanelList, que serão representados na página como painéis extendíveis.

bgcolor a cor de fundo do painel.
content os controles que aparecerão dentro desse painel. Esse conteúdo aparece abaixo do cabeçalho (header) quando o painel está expandido. Se content=None o painel terá um espaço reservado para Texto como conteúdo.
can_tap_header booleano. Se can_tap_header=True um toque sobre o cabeçalho do painel provocará sua expansão ou colapso. Defaults é False.
expanded booleano. Define o estado do painel como expandido ou colapsado. Defaults é False (colapsado).
header o controle a ser apresentado dentro do cabeçalho (header). Se header=None o ExpansionPanel terá terá um espaço reservado para Texto no cabeçalho.

Um exemplo de uso dos controles ExpansionPanelList e ExpansionPanel pode ser visto em Uso de ExpansionPanel.

Bibliografia

Todas as URLs foram visitadas em janeiro de 2024.

Flet: Cores e Temas

Cores e Temas

É claro que uma escolha adequada de cores para o seu aplicativo é um passo essencial para a produção de um produto agradável de se olhar e usar. Essa escolha faz parte dos esforços de construir apps com boa usabilidade. O estudo completo de usabilidade e uso de cores é complexo. Algumas referências podem ser encontradas na bibliografia.

O Flet apresenta uma proposta de construção de interfaces gráficas que, por definição, devem ter boa aparência. Para isso podemos aplicar cores, transparências e sombreados aos controles.

Valores de Cores

Cores no Flet podem ser definidas por seu valor hexadecimal ou valores nomeados, idênticos aos usados com HTML e CSS. Em formato hexadecimal a cor é expressa por uma string com o seguinte formato:

Hexadecimal: #aarrggbb (0xaarrggbb) or #rrggbb (0xeeggbb).

Nesse código as letras significam: a opacidade, r vermelho, g verde, b azul; todas em notação hexadecimal.

Se o número de opacidade é omitido ele é ajustado para o valor máximo ff (255, totalmente opaco).

c1 = ft.Container(bgcolor='#ff0000')    # container de fundo vermelho
c2 = ft.Container(bgcolor='#00ff00')    # container de fundo verde
c3 = ft.Container(bgcolor='#0000ff')    # container de fundo azul

c4 = ft.Container(bgcolor='#80ff0000')  # fundo vermelho, 50% transparente

Valores nomeados: são strings que representam as cores e podem ser interpretadas pelos controles do Flet. Alternativamente elas podem ser passadas como propriedades do módulo flet.colors.

c5 = ft.Container(bgcolor=ft.colors.BLUE)
c6 = ft.Container(bgcolor='blue')

Opacidade: A opacidade (o inverso de transparência) pode ser ajustada com o método with_opacity, com valor entre 0.0 (100% transparente) e 1.0 (0% transparente).

color = ft.colors.with_opacity(0.5, ft.colors.PRIMARY)
color = ft.colors.with_opacity(0.5, '#ff6666')
color = ft.colors.with_opacity(0.5, '#ff6666')
color = "#80fff6666"
color = "blue, 0.5" #  50% azul

Cores de Tema

Esse esquema é baseado no ColorScheme do Flutter que, por sua vêz é baseado nas especificações do Material Design.

Material Design é uma linguagem de design desenvolvida pelo Google e apresentada em 2014. Ele layouts baseados em grade, animações e transições responsivas, e profundidade efeitos de iluminação e sombras. Seu principal objetivo é o estabelecimento de uma linguagem visual combinando princípios de bom design com inovação técnica e científica.

Existem 30 cores de temas nomeadas em theme.color_scheme que são geradas de acordo com a semente (seed) pela propriedade color_scheme_seed, sendo o azul a cor de seed default.

Figura 1: Cores em ft.colors. Por exemplo: ft.colors.INDIGO_ACCENT_200

Cores dos controles

Muitos controles do Flet recebem cores default que dependem do tema escolhido (color_scheme), que define as cores de todos os controles em todos os níveis do aplicativo. Essas cores podem ser sobrescritas em diversos níveis, tais como atribuindo a cor de fundo ou destaque (background e foreground) de um container ou botão, personalizando os componentes de uma barra de rolagem (scrollbar) ou de guias (tabs).

Cores locais nos controles
Para definir a cor local (“control level”) de um controle específico, usamos:

ctl = ft.Container(width=100, height=70, bgcolor=ft.colors.BLUE_400)
bt = ft.ElevatedButton("Go", icon="arrow_circle_left", bgcolor="white", width=150, height=30)
ctl2 = ft.Container(width=50, height=50,
                    border=ft.border.all(1, "#293B4A"),
                    border_radius=ft.border_radius.all(10),
                    bgcolor="#F1EBC5")

Cores dos controles com temas
Alguns controles não podem ter sua cor ajustada localmente e suas cores são definidas apenas pelo tema. É o caso de FilledButton cuja cor default primária (“primary” color) é definida pelo tema de seu controle pai (seu conteiner).

Para os controles sem definição de cores locais, como a barra de rolagem (ScrollBar), suas cores serão obtidas a partir de seu ancestral mais próximo, se eles possuem a definição de tema para essas barras. Isso também ocorre com Tabs ou Text.
Nota: ScrollBars são (usadas nos controles Page, View, Column, Row, ListView and GridView, Tabs e Text. Para personalizar um controle específico (ScrollBar, Text ou Tabs control) podemos envolvê-lo em um container e personalizar o tema respectivo (scrollbar_theme, text_theme ou tabs_theme) no tema desse container.

Níveis de temas
O Flet procura o tema definido na antecessor mais próximo, usando aquele ColorScheme. Por exemplo, o código abaixo define um FilledButton dentro de um Container, que é seu antecessor mais próximo. A cor primária do botão será obtida no tema do Container. Por exemplo, o código:

import flet as ft

def main(page: ft.Page):
    container = ft.Container(
        width=200,
        height=200,
        bgcolor="#E3DA2D",
        border=ft.border.all(5, ft.colors.INDIGO_ACCENT_200), 
        content=ft.FilledButton("Cor Primária\n(Primary color)"),
        theme=ft.Theme(color_scheme=ft.ColorScheme(primary=ft.colors.DEEP_PURPLE_100)))

    page.add(container)

ft.app(target=main)
Figura 2: resultado do código

Esse código gera a imagem na figura 2.
Caso a propriedade de cor não esteja definida localmente no controle ou por tema, nem no antecessor mais próximo, a cor será buscada no próximo antecessor disponível, que pode ser a página (page) com seu color_scheme definido (ou default).

Tema nas páginas

Temas são atribuídos às páginas (page) através da propriedade page.theme, que recebe uma instância da classe theme.Theme. No presente estado de desenvolvimento do Flet um tema só pode ser gerado automaticamente por meio de uma cor semente (seed). Por exemplo, para gerar um tema claro baseado na cor verde usamos:

page.theme = theme.Theme(color_scheme_seed="green")
page.update()
Figura 3: tema default e com seed

A figura mostra a renderização da interface com o mesmo código do exemplo na figura anterior, destituído de cores. Os objetos assumeme suas cores com o tema default. A imagem à direita mostra o efeito de page.theme = theme.Theme(color_scheme_seed=”green”).

Além disso a classe Theme possui outras propriedades:

Propriedade Descrição
color_scheme_seed cor usada pelo algoritmo para construir as demais cores do tema.
color_scheme instância da classe ft.ColorScheme para customização do esquema de cores derivado de color_scheme_seed.
text_theme instância da classe ft.TextTheme para customização de estilos de texto contrastando com as cores de card e canvas.
primary_text_theme instância da classe ft.TextTheme que descreve tema para textos em contraste com as cores primárias.
scrollbar_theme instância da classe ft.ScrollbarTheme para customização da aparência de scrollbars.
tabs_theme instância da classe ft.TabsTheme para customização da aparência de Tabs.
font_family a fonte base para todos os elementos gráficos.
use_material3 booleano (default=True) para usar design do Material 3 design. Caso contrário usa Material 2.
visual_density enun ThemeVisualDensity: STANDARD (default), COMPACT, COMFORTABLE, ADAPTIVE_PLATFORM_DENSITY.
page_transitions instância de PageTransitionsTheme para customização de transições de navegação na página para diferente plataformas. Veja a seção sobre transições.

ColorScheme
A classe ColorScheme tem propriedades como:

  • primary: cor primária de telas e componentes
  • secondary: cor de destaque usada em componentes menos proeminentes na interface
  • error: cor usada para validação de campos de entrada, como TextField e error_text
  • background: cor de fundo de conteúdos roláveis (com scroll)
  • shadow: cor das sombras sobre componentes elevados
Figura 4: Propriedades de ColorScheme

Uma lista completa das propriedades pode ser vista abaixo.



Classe TextTheme
É usada para a customização de estilos de texto. TextTheme tem as seguintes propriedades de ft.TextStyle:

Propriedade Descrição
body_large estilo de texto grande usados em passagens longas.
body_medium estilo de texto médio (junto com body_large). É o default da padronização Material.
body_small estilo de texto pequeno.
display_large estilo para o maior texto, reservado para trechos curtos e importantes. Melhor em telas grandes.
display_medium estilo de tamanho médio.
display_small estilo de tamanho pequeno.
headline_large estilo para cabeçalhos grandes. Cabeçalhos (headlines) são menores que display. Adequado para texto curto e telas menores.
headline_medium estilo para cabeçalhos (headlines) medios.
headline_small estilo para cabeçalhos (headlines) pequenos.
label_large estilo para labels grandes. Labels são menores e usados para texto dentro de componentes, como legendas. Úteis em ElevatedButton, TextButton e OutlinedButton.
label_medium Tamanho médio em estilo de labels.
label_small Tamanho pequeno em estilo de labels.
title_large estilo para títulos grandes. Títulos são menores que headlines e devem ser usados para textos curtos de ênfase média.
title_medium estilo para títulos médios.
title_small estilo para títulos pequenos.

Classe ScrollbarTheme
Customiza as cores, espessura e forma de barras de rolagem scrollbars em todo o aplicativo.

A classe ScrollbarTheme tem as seguintes propriedades:

thumb_visibility visibilidade da alça (thumb) da barra de rolagem (scrollbar) mesmo quando não em uso. Se False a scrollbar será exibida apenas quando em uso. A propriedade pode ser um valor booleano ou um dicionário tendo como chaves as propriedades de ft.MaterialState e os valores booleanos.
thickness largura da scrollbar no eixo de rolagem. Pode ser um valor de ponto flutuante ou um dicionário tendo como chaves as propriedades de ft.MaterialState e números como valores.
track_visibility Se o trilho (track) scrollbar deve ser visível. Se True o trilho permanecerá visível enquanto sua alça estiver visível. Se a alça não for visível o trilho também não será. Se a propriedade for ajustada para None essa visibilidade será False e o tema ScrollbarTheme.track_visibility do Theme.scrollbar_theme será usado. O valor default é False se esse último também for None. Essa propriedade também pode ser um booleano ou um dicionário tendo como chaves as propriedades de ft.MaterialState e números como valores.
radius o raio de arredondamento da alça da barra de rolamento.
thumb_color sobrescreve a cor default do Scrollbar thumb. Esse valor pode ser uma cor ou um dicionário de ft.MaterialState.
track_color sobrescreve a cor default do scrollbar track. Esse valor pode ser uma cor ou um dicionário de ft.MaterialState.
track_border_color sobrescreve a cor default da borda da alça. Esse valor pode ser uma cor ou um dicionário de ft.MaterialState.
cross_axis_margin distância entre a alça da barra até a borda mais próxima. A barra de rolamento preenche todo esse espaço. O valor não pode ser none e tem default 0.
main_axis_margin distância entre o início da alça da barra até a borda da caixa onde ele está desenhado. Esse valor afeta a área disponível para a barra. A barra de rolamento preenche todo esse espaço. O valor não pode ser Null e tem defaul 0.
min_thumb_length valor mínimo preferencial para o comprimento da alça da barra quando o comprimento da barra é muito grande.
interactive booleano, se True a barra de rolagem será interativa, repondendo ao arrasto da alça ou clique (ou toque) no trilho. Se False a barra não responderá a eventos de gestos ou hover, mas permitira cliques em toda a sua extensão. Defaults é True se valor = None (exceto no Android onde o default é False.
Figura 6: Tabs (abas ou guias)
Figura 5: Terminologia em Scrollbar

Aqui usamos a terminologia:
scrollbar: barra de rolagem
track: trilho
thumb: alça

tabs: abas ou guias

Classe TabsTheme
Customiza a aparência dos controles de Tabs em todo o aplicativo.

A classe TabsTheme tem as seguintes propriedades:

divider_color cor do divider.
indicator_border_radius raio das bordas do indicador.
indicator_border_side cor and peso (weight) da linha horizontal abaixo do tab selecionado.
indicator_padding indica localização do sublinhado sob o tab selecionado em relação à sua borda. A propriedade indicator_tab_size é marcada como False para definir o indicador centralizado e como True para todo o tab.
indicator_color cor da linha abaixo do tab selecionado.
indicator_tab_size se True o indicador ocupa todo o tab.
label_color cor dos rótulos labels dos tabs selecionados.
unselected_label_color cor dos rótulos labels dos tabs não selecionados.
overlay_color define a resposta de “tinta” sob ação de focus, hover e splash.

Transições de Navegação
theme.page_transitions permite a costumização dos efeitos de transição da página. Esses efeitos são diferentes para as diversas plataformas. Seu valor é uma instância da classe PageTransitionsTheme com as seguintes propriedades opcionais:

  • android (default: FADE_UPWARDS)
  • ios (default: CUPERTINO)
  • macos (default: ZOOM)
  • linux (default: ZOOM)
  • windows (default: ZOOM)

As transições admitidas estão no enum ft.PageTransitionTheme:

  • NONE, nenhum delay, transição sem animação,
  • FADE_UPWARDS, desaparece de baixo para cima,
  • OPEN_UPWARDS, abre de baixo para cima,
  • ZOOM, ampliação
  • CUPERTINO, abre no estilo Cupertino.

Segue um exemplo:

theme = ft.Theme()
theme.page_transitions.android = ft.PageTransitionTheme.OPEN_UPWARDS
theme.page_transitions.ios = ft.PageTransitionTheme.CUPERTINO
theme.page_transitions.macos = ft.PageTransitionTheme.FADE_UPWARDS
theme.page_transitions.linux = ft.PageTransitionTheme.ZOOM
theme.page_transitions.windows = ft.PageTransitionTheme.NONE
page.theme = theme
page.update()

Tema de Página
A propriedade page.theme admite como valor opcional o enum ThemeMode com valores:
SYSTEM (default), LIGHT ou DARK.

Bibliografia

Todas as URLs foram visitadas em janeiro de 2024.

Flet: Stack

Objeto Stack

Em alguns casos queremos colocar controles em cima de outros. No Flet é possível sobrepor vários filhos do controle Stack. Podemos, por exemplo, colocar um texto sobre uma imagem ou um container móvel, como fizemos no exemplo 3.

Propriedades de Stack

O controle Stack possui apenas 2 propriedades:

clip_behavior
Como será cortado (clipped) o conteúdo do objeto.
Os valores possíveis estão armazenados em no enum ClipBehavior com os valores:

  • NONE
  • ANTI_ALIAS
  • ANTI_ALIAS_WITH_SAVE_LAYER
  • HARD_EDGE (default)

controls
Uma lista contendo os controles a serem exibidos dentro de Stack, em ordem de exibição, sendo o último controle exibido por cima dos demais.

Controles dentro de Stack

Diversos controles, ao serem colocados dentro de um Stack passam a admitir propriedades de posição. São elas:

Figura 1: distâncias em Stack

left: distância entre a borda esquerda do controle filho até a borda esquerda do Stack.
right: distância entre a borda direita do controle filho até a borda direita do Stack.
bottom: distância entre a borda inferior do controle filho até a borda inferior do Stack.
top: distância entre a borda superior do controle filho até a borda superior do Stack.

Exemplo de uso do Stack

O exemplo de código a seguir insere 3 controles dentro de um Stack, 1 imagem e 2 linhas. Nessa posição eles admitem as propriedades listadas acima, em particular top e left que são usadas para mover a linha de texto.

1  import flet as ft
2  import time
3  
4  def main(page: ft.Page):
5      def voltar(e):
6          texto.size = 50
7          linha_texto.top=500
8          linha_texto.left=0
9          page.update()
10 
11     def partir(e):
12         while linha_texto.top > 0:
13             time.sleep(.1)
14             texto.size -= 1
15             linha_texto.top-=10
16             linha_texto.left+=10
17             page.update()
18 
19     imagem = ft.Image(src=f"https://phylos.net/wp-content/uploads/2020/02/deepfield.jpg",
20                       width=950, height=556, fit=ft.ImageFit.NONE)
21    
22     texto = ft.Text("Em uma galáxia muito muito distante...",
23                      color="yellow", size=50, weight="bold", opacity=0.3)
24   
25     linha_texto = ft.Row([texto], left = 0, top=500)
26
27     bt_partir = ft.ElevatedButton("Partir!", bgcolor="blue", color="white",
28                                   width=120, height= 35, on_click=partir)
29
30     bt_voltar = ft.ElevatedButton("Voltar", bgcolor="blue", color="white",
31                                    width=120, height= 35, on_click=voltar)
32
33     linha_botoes = ft.Row([bt_partir,bt_voltar], left = 10, top=10)
34
35     page.add(ft.Stack([imagem, linha_texto,linha_botoes], width=950, height=556))
36 
37 ft.app(target=main)
Figura 2: resultado do código com Stack. Apenas 3 frames são mostrados.

Um clique no botão Voltar retorna as propriedades do texto e da linha que o contém para seus valores iniciais. Observe que, se o botão Partir! for acionado antes que o loop na linha 12 tenha terminado a velocidade de subida do texto aumentará pois os incrementos serão aumentados (loops dentro de loops).

Exemplo: sobreposição de controles

Mais um exemplo ajudará a mostrar que, dentro de um Stack, controles desenhados estão dispostos em camadas. Se dois controles são posicionados no mesmo lugar o último deles, na camada superior, ficará visível, cobrindo o controle por baixo.

1  import flet as ft
2  
3  class Ct(ft.Container):
4      def __init__(self, cor, acima, direita, abaixo, esquerda):
5          super().__init__()
6          self.width=20
7          self.height=20
8          self.border_radius=5
9          self.bgcolor=cor
10         self.top=acima
11         self.right=direita
12         self.bottom=abaixo
13         self.left=esquerda
14   
15 def main(page: ft.Page):
16     page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
17     page.vertical_alignment = ft.MainAxisAlignment.CENTER
18
19     def mover(e):
20         e.control.data = (e.control.data % 5) + 1
21         i = e.control.data
22         st.controls.pop()
23         if i==1: ct5=Ct("purple", 0, 60,60,0)
24         elif i==2: ct5=Ct("purple", 0,0,60,60)
25         elif i==3: ct5=Ct("purple", 60,0,0,60)
26         elif i==4: ct5=Ct("purple", 60,60,0,0)
27         else: ct5=Ct("purple", 30,30,30,30)
28         st.controls.append(ct5)
29         st.update()
30
31     ct1 = Ct("red", 0, 60,60,0)
32     ct2 = Ct("yellow", 0,0,60,60)
33     ct3 = Ct("blue", 60,0,0,60)
34     ct4 = Ct("green", 60,60,0,0)
35     ct5 = Ct("purple", 30,30,30,30)
36     ct5.data=0
37     bt_mover = ft.ElevatedButton("Mover", bgcolor="blue", color="white",
38                                   width=120, height= 35, data = 0, on_click=mover)
39
40     st = ft.Stack([ct1, ct2, ct3,ct4, ct5])
41     st.data = 0
42
43     page.add(ft.Container(st, border_radius=8, padding=5,
44                           width=100, height=100, bgcolor="black"), bt_mover)
45
46 ft.app(target=main)
Figura 3: sobreposição de controles com Stack.

O exemplo também ajuda a ilustrar o uso de POO no Flet. Como muitos controles com propriedades semelhantes serão criados, a classe Ct foi criado na linha 3, herdando de um ft.Container. Objetos criados nas linhas 31 a 35 passam para o construtor apenas a cor e a posição do container.

A função mover(), acionada por bt_mover remove o último objeto no Stack na linha 22 e cria um novo container roxo por cima dos demais, circularmente. O container coberto não deixa de existir e reaparece na próxima operação.

Flet: Exemplo 3

Captura de toques de teclado

Atalhos de Teclado

Na seção Widgets: Atalhos de Teclado vimos como capturar a interação do usuário com o aplicativo através de eventos de teclados. A captura de eventos produzidos pelo teclado se dá através do evento page.on_keyboard_event, que gerencia o pressionamento de teclas de letras em combinação com teclas de controle. Esse evento passa o parâmetro e que é uma instância da classe KeyboardEvent, e tem as seguintes propriedades:

e.key Representação textual da tecla pressionada.
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††.

Desta forma conseguimos capturar o pressionamento de uma única letra, além de Enter, Backspace, F1F12, Escape, Insert, Page Down, Pause, etc, ou de combinações como Ctrl-F ou Ctrl-Shift-Enter.

Como exemplo adicional vamos exibir o código de um jogo que captura a interação com o teclado. Uma combinação de teclas como Ctrl-H ou SHIFT-L é exibida em um container que desce do topo da página. Se o usuário digita a mesma combinação ele pontua (10 pontos de cada vez) e nova combinação é exibida.

1   import flet as ft
2   import time, random, string
3
4   cores = ["#FFC6C6", "#A8DCEA", "#BAF3E4", "#F1E9AF",
5            "#CBB1CE", "#B2C9F3", "#F0DCBE", "#F1D7B3"]
6
7   def main(page: ft.Page):
8       def sortear():
9           ct.top = 10        
10          ct.left = random.randrange(100, 1000)
11          ct.height=80
12          t1 = ["Shift", "Alt", "Ctrl"][random.randrange(0, 3)]
13          t2 = random.choice(string.ascii_letters).upper()
14          ct.content = ft.Text(f"{t1}-{t2}", size=20)
15          ct.bgcolor = cores[random.randrange(0, 8)]
16          ct.visible = True
17
18      def fim(e):
19          page.window_destroy()
20
21      def inicio(e):
22          while True:
23              sortear()
24              while ct.top < 400:
25                  time.sleep(.3)
26                  ct.top += 10
27                  page.update()
28              inicio(e)
29
30      def teclou(e):
31          txt = "Shift" if e.shift else "Alt" if e.alt else "Ctrl" if e.ctrl else ""
32          txt = f"{txt}-{e.key}"
33
34          if ct.content.value == txt:
35              pts = int(txtPontos.value.split(":")[1]) + 10
36              txtPontos.value = f"Sua pontuação: {str(pts)}"
37              ct.bgcolor = "red"
38              while ct.top < 400:
39                  time.sleep(.01)
40                  ct.top += 10
41                  ct.height -= 3
42                  page.update()
43
44      ct = ft.Container(padding = 15, width=180, alignment = ft.alignment.center,
45                        border = ft.border.all(2, "black"),
46                        border_radius = ft.border_radius.all(15),
47                        blur=90, visible = False)
48
49      btInicio = ft.ElevatedButton("Começar Jogo", bgcolor="white",
50                                  width=180, height= 40, on_click=inicio)
51      btFim = ft.ElevatedButton("Terminar Jogo", bgcolor="white",
52                                width=180, height= 40, on_click=fim)
53      txtPontos = ft.Text("Sua pontuação: 0", size=27, weight=ft.FontWeight.BOLD)
54      lin = ft.Text("Acerte a combinação de teclas mostrada na tela (Ctrl, Shift, Alt) + Letra",
55                    size=27)
56      linha1 = ft.Row([lin], alignment=ft.MainAxisAlignment.SPACE_EVENLY)
57      linha2 = ft.Row([btInicio, txtPontos, btFim],
58                      alignment=ft.MainAxisAlignment.SPACE_EVENLY)
59
60    page.add(linha1, linha2, ft.Stack([ct]))
61    page.on_keyboard_event = teclou
62
63  ft.app(target=main)

Nesse bloco de código usamos a notação Camel case para dar nomes às variáveis. Ela consiste em usar a primeira letra minúscula e a primeira letra de cada nova palavra subsequente maiúscula. Como exemplo temos casaDaVovo ou notaAluno. O desenvolvedor pode escolher que tipo de notação usar, sendo consistente em seu código e procurando respeitar as convenções da linguagem.

O início do jogo ocorre quando se clica o botão btInicio que dispara a função inicio(). Dentro dela um loop infinito promove várias interações do container descendo a tela (o que é obtido com container.top += 10). Quando o container chega em seu valor máximo estabelecido, no caso ct.top < 400, novos valores são sorteados como suas propriedades e o container e redesenhado no topo.

O sorteio define a posição (top) inicial, uma cor aleatória escolhida dentro de uma lista de cores (linha 15) e monta uma string para representar uma combinação de teclado (linhas 12 a 14). A linha 13 usa random.choice() para escolher aleatoriamente um dos caracteres de string.ascii_letters, e torná-lo maísculo. Esse carater é juntando à um controle. A tecla de controle Meta (Windows) não foi usada pois é comum que ela esteja associada a atalhos globais do computador, em algumas instalações.

A interação do usuário é capturada em teclou que compara a combinação sorteada com aquela teclada pelo usuário. Se ocorreu um acerto ela incrementa a pontuação, derruba o container (acelerando sua queda) e sorteia nova combinação. O loop infinito iniciado na linha 22 só é terminado com o evento btFim.on_click.

Captura de teclas no Python

Claro que podemos capturar o pressionamento de tecla no Python, sem usar os métodos do Flet. Para isso podemos instalar, entre outras escolhas, o módulo keyboard, que funciona em sistemas operacionais Windows e Linux.

# Instale o módulo de teclado usando PIP:
pip install keyboard

# No Python 3 instale usando o comando:
pip3 install keyboard

Para detectar pressionamentos de teclas podemos usar a função is_pressed(). Esse método recebe um caractere como entrada e retorna True se essa tecla foi pressionada. O exemplo seguinte usa um loop que só é terminado com o pressionamento da tecla f.

import keyboard

print("Pressione 'f' para terminar.")
while True:
    if keyboard.is_pressed("a"):
        print("A tecla 'f' foi pressionada!")
        break

A função keyboard.is_pressed("f") retorna False se qualquer outra tecla fopr pressionada.

Também podemos usar a função read_key() para capturar pressionamentos de teclas. Essa função retorna a tecla pressionada pelo usuário.

import keyboard
while True:
    print(keyboard.read_key())
    if keyboard.read_key() == "f":
        print("Você terminou o loop pressionando 'f'.")
        break

Esse código exibe na tela todas as teclas pressionadas até que se tecle “f”.

Outra forma de captura de pressionamento de teclas usa a função wait(), que recebe um caractere como entrada. Quando executado o programa é pausado até que seja pressionada a tecla passada como argumento para a função.

import keyboard
keyboard.wait("f")
print("Você digitou 'f'.")

Nesse último exemplo fica dispensado o uso de um loop para suspender a execução do código.

Bibiografia

Flet: Row e Column


Layout do Flet: Row e Column

Flet: Row

Row (linha) é um controle que serve de container para outros controles e os exibe horizontalmente. Ele possui propriedades, eventos e métodos relativos ao layout e gerenciamento de rolagem da página (scroll) para evitar overflow (quando o conteúdo ocupa áerea maior que a disponível na página).

Propriedades de Row

Propriedades Descrição
alignment alinhamento horizontal dos filhos.
A propriedade MainAxisAlignment recebe um ENUM com os valores:

  • START(default) alinha à esquerda da Linha
  • END
  • CENTER
  • SPACE_BETWEEN
  • SPACE_AROUND
  • SPACE_EVENLY

auto_scroll booleano, auto_scroll=True para mover a posição de scroll para o final quando ocorre atualização dos filhos. Para que o método scroll_to() funcione é necessário ajustar auto_scroll=False.
controls uma lista de controles a serem exibidos na linha.
run_spacing espaçamento entre as várias linhas (runs) quando wrap=True. Default: 10.
Linhas adicionais aparecem quando os controles não cabem dentro de uma linha única.
scroll permite rolagem horizontal da linha para evitar overflow. A propriedade pode receber o ENUM ScrollMode com os valores:

  • None (default): não é rolagem e pode haver overflow.
  • AUTO: rolagem habilitada e a barra de rolagem só aparece quando ocorre o scroll.
  • ADAPTIVE: rolagem habilitada e a barra sempre visível quando aplicativo roda na web ou desktop.
  • ALWAYS: rolagem habilitada e a barra sempre visível.
  • HIDDEN: rolagem habilitada e a barra sempre oculta.
spacing espaçamento entre controles na linha. Default: 10 pixeis. O espaçamento só é aplicado quando alignment é start, end ou center.
on_scroll_interval limitação para o evento on_scroll em milisegundos. Default: 10.
tight booleano, espaço a ser ocupado horizontalmente. Default: tight = False, usar todo o espaço os controles.
vertical_alignment alinhamento vertical. A propriedade pode receber o ENUM CrossAxisAlignment os valores:

  • START (default)
  • CENTER
  • END
  • STRETCH
  • BASELINE
wrap booleano, se wrap=True os controles filhos serão colocados em linhas adicionais (chamadas runs), caso não caibam em uma única linha.

Métodos de Row

Método Descrição
scroll_to(offset, delta, key, duration, curve) move a posição de rolagem para o offset absoluto, para um salto (delta) ou para o controle com key especificada.
Detalhes são idênticos ao do método de Column.scroll_to().

Eventos de Row

Evento Dispara quando
on_scroll a posição de rolagem da linha é alterada por um usuário. Consulte Column.on_scroll() para detalhes e exemplos do evento.

Uso das propriedades de row

No código abaixo são criados 30 controles container numerados que são dispostos em uma linha. Dois controle de deslizar (slide) ajustam as propriedades row.width (o número de colunas em cada linha) e row.spacing (o espaçamento entre cada objeto. Quando o número de objetos em uma linha é maior que o espaço permite, novas linhas são inseridas (runs).

import flet as ft # era 52

def main(page: ft.Page):
    def items(count):
        items = []
        for i in range(1, count + 1):
            items.append(ft.Container(ft.Text(value=str(i), color="white", size=20), alignment=ft.alignment.center,
            width=40, height=40, bgcolor="#40A4D2", border_radius=ft.border_radius.all(10)))
        return items

    def muda_largura(e):
        linha.width = float(e.control.value)
        linha.update()

    def muda_separa(e):
        linha.spacing = int(e.control.value)
        linha.update()

    slid_separa = ft.Slider(min=0, max=50, divisions=50, value=0, label="{value}", on_change=muda_separa,)

    slid_largura = ft.Slider(min=0, max=page.window_width, divisions=20, value=page.window_width,
                             label="{value}", on_change=muda_largura,)

    linha = ft.Row(wrap=True, spacing=10, run_spacing=10, controls=items(30), width=page.window_width,)

    txt1 = ft.Text("O primeiro controle seleciona o número de colunas:")
    txt2 = ft.Text("O segundo controle seleciona espaçamento entre colunas:")
    page.add(ft.Column([txt1, slid_largura,]),ft.Column([txt2, slid_separa,]), linha,)

ft.app(target=main)

O código gera, em algum ajuste dos controles de deslizar, a janela abaixo.

Expandindo controles na linha

A expansão de controles na linha e na coluna são análogas. Veja Expandindo controles na linha e na coluna.

Flet: Column

Column é um controle que serve de container para outros controles e os exibe verticalmente. Ele possui propriedades eventos e métodos relativos ao layout e gerenciamento do rolamento da página (scroll) para evitar overflow (quando o conteúdo ocupa áerea maior que a disponível na página.

Propriedades de Column

Propriedades Descrição
alignment define como os controles filhos devem ser colocados verticalmente.
A propriedade MainAxisAlignment recebe um ENUM com os valores:

  • START(default) alinha à esquerda da Linha
  • END
  • CENTER
  • SPACE_BETWEEN
  • SPACE_AROUND
  • SPACE_EVENLY
auto_scroll auto_scroll=True para mover a posição de scroll o final quando ocorre atualização dos filhos. Para que o método scroll_to() funcione devemos fazer auto_scroll=False.
controls lista de controles a serem exibidos na coluna.
horizontal_alignment posicionamento horizontal dos controles filhos.
A propriedade recebe o ENUM CrossAxisAlignment com os valores:

  • START(default) alinha à esquerda da Linha
  • END
  • CENTER
  • STRETCH
  • BASELINE
on_scroll_interval limita o evento on_scroll (em milisegundos). Default: 10.
scroll habilita a rolagem vertical na coluna para evitar overflow de conteúdo.
A propriedade recebe um ENUM opcional ScrollMode com valores:

  • None (default): coluna não é rolável e pode haver overflow.
  • AUTO: rolagem habilitada e a barra de rolagem só aparece quando a rolagem é necessária.
  • ADAPTIVE: rolagem habilitada e a barra sempre visível em aplicativos na web ou desktop.
  • ALWAYS: rolagem habilitada e a barra sempre vivível.
  • HIDDEN: rolagem habilitada, a barra de rolagem está sempre oculta.
spacing espaçamento entre os controles da coluna. Default: 10 pixeis. O espaçamento só é aplicado quando alignment = start, end, center.
run_spacing espaçamento entre “runs” quando wrap=True. Default: 10.
tight espaçamento vertical. Default: False (alocar todo o espaço para filhos).
wrap se wrap=True a coluna distribuirá os controles filho em colunas adicionais (runs) se não couberem em uma coluna.

Métodos de Column

Método Descrição
scroll_to(
offset, delta,
key, duration,
curve)
move a posição de rolagem para o offset absoluto, para um salto (delta) ou para o controle com key especificada.
Por exemplo:

(1) products.scroll_to(offset=100, duration=1000)
(2) products.scroll_to(offset=-1, duration=1000)
(3) products.scroll_to(delta=50)
(4) products.scroll_to(key="20", duration=1000)

(1) offset é a posição do controle, um valor entre a extensão mínima e máxima do controle de scroll.
(2) offset negativo conta a partir do final do scroll. offset=-1 para posicionar no final.
(3) delta move o scroll relativo à posição atual. Rola para trás, se negativo.
(4) key move o scroll para a posição especificada com key.
A maioria dos controles do Flet tem a propriedade key (equivalente ao global key do Flutter. keydeve ser única para toda a page/view.
duration define a duração da animação de rolagem, em milisegundos. Default: 0 (sem animação).
curve configura a curva de animação. Default: ft.AnimationCurve.EASE.

Eventos de Column

Evento Dispara quando
on_scroll a posição de rolagem é alterada por um usuário.
O argumento do gerenciador de eventos é instância da ft.OnScrollEvent com as propriedades:

  • event_type (str), tipo do evento de rolagem:
    • start: início da rolagem;
    • update: controle de rolagem mudou de posição;
    • end: início da rolagem (parou de rolar);
    • user: usuário mudou a direção da rolagem;
    • over: controle ficou inalterado por estar no início ou final;
  • pixels(float): posição de rolagem atual, em pixeis lógicos.
  • min_scroll_extent (float): valor mínimo no intervalo permitido, em pixeis.
  • max_scroll_extent (float): valor máximo no intervalo permitido, em pixeis.
  • viewport_dimension (float): extensão da viewport.
  • scroll_delta (float): distância rolada, em pixeis. Definido apenas em eventos update.
  • direction (str) : direção da rolagem: idle, forward, reverse. Definido apenas em evento user.
  • overscroll (float): número de pixeis não rolados, por overflow. Definido apenas em eventos over.
  • velocity (float): velocidade na posição de ScrollPosition quando ocorreu overscroll. Definido apenas em eventos over.

Expandindo controles na linha e na coluna

Todos os controles possuem a propriedade expand que serve para expandi-lo para preencher os espaços disponíveis dentro de uma linha. Ela pode receber um booleano ou um inteiro. expand=True significa expandir o controle para preencher todo o espaço. expand=int introduz um “fator de expansão” especificando como dividir um espaço com outros controles expandidos na mesma linha, ou coluna. O código:

ft.Row([
    ft.TextField(hint_text="Esse será expandido", expand=True),
    ft.ElevatedButton(text="Esse não...")
])

cria uma linha contendo um TextField que ocupa o espaço disponível, e um ElevatedButton, sem expansão. A expansão é calculada em termos do tamanho de todos os controle na linha. É possível fornecer fatores que definem a proporção de expansão de cada controle. Por exemplo:

linha = ft.Row([
        ft.Container(expand=1, content=ft.Text("A")),
        ft.Container(expand=3, content=ft.Text("B")),
        ft.Container(expand=1, content=ft.Text("C"))
])

cria uma linha com 3 controles ocupando 1, 3, 1 partes em 5, como exibido na figura,

Percentualmente a largura resultante de um filho é calculada como largura = expand / soma( todas as expands) * 100%.

Da mesma forma um controle filho em uma coluna pode ser expandido para preencher o espaço vertical disponível. Por exemplo, o código abaixo cria uma coluna com um Container que ocupando todo o espaço disponível e um controle Text na parte inferior servindo como uma barra de status:

coluna = ft.Column([
         ft.Container(expand=True, content=ft.Text("Esse será expandido")),
         ft.Text("Um texto usado como label")
])

Assim como no controle das linhas, podemos usar fatores numéricos em expand=n.

col = ft.Column([
      ft.Container(expand=1, content=ft.Text("Acima")),
      ft.Container(expand=3, content=ft.Text("No centro")),
      ft.Container(expand=1, content=ft.Text("Abaixo"))
])


Isso cria uma coluna com 3 containeres com alturas de 20% (1/5), 60% (3/5)e 20% (1/5) respectivamente.

Percentualmente a altura resultante de um filho é calculada como altura = expand / soma( todas as expands) * 100%.

Bibiografia

Flet: View e Container


Layout do Flet: View e Container

Flet: View

A palavra container do inglês é traduzida como contêiner (pt-br), plural contêineres. Para não criar confusão com a palavra técnica nós o chamaremos aqui por container, containers.

Um aplicativo do Flet abre sempre uma page que serve de container para o objeto View. Uma View é criado automaticamente quando uma nova sessão é iniciada. Ela é basicamente uma coluna (column) básica, que abriga todos os demais controles que serão inseridos na página. Dessa forma ele tem comportamento semelhante ao de uma column, e as mesmas propriedades. Uma descrição resumida será apresentada aqui. Para maiores detalhes consulte a descrição de column.

O objeto View é o componente visual de uma página Flet, responsável por renderizar os elementos da UI e gerenciar seu estado. Ele pode abrigar outros objetos como botões, campos de texto, imagens, etc, e organizá-los em uma estrutura hierárquica. Esses elementos são então renderizados na tela. O objeto View também possui métodos para lidar com eventos do usuário, como cliques em botões ou textos digitados nas caixas de texto.

Por exemplo, o código:

page.controls.append(ft.Text("Um texto na página!"))
page.update()
# ou, o que é equivalente
page.add(ft.Text("Um texto na página!"))

insere o texto na View que está diretamente criada sobre page. View possui as seguintes propriedades e métodos.

View: Propriedades

Propriedade Descrição
appbar recebe um controle AppBar para exibir na parte superior da página.
auto_scroll Booleano. True para que a barra de rolagem se mova para o final quando os filhos são atualizados. Para que scroll_to() funcione deve ser atribuído auto_scroll=False.
bgcolor Cor de fundo da página.
controls Lista de controles a serem inseridos na página. O último controle da lista pode se removido com page.controls.pop(); page.update().
fullscreen_dialog Booleano. True se a página atual é um diálogo em tela cheia.
route Rota da visualização (não usada atualmente). Pode ser usada para atualizar page.route em caso de nova visualização.
floating_action_button Recebe um controle FloatingActionButton a ser exibido no alto da página.
horizontal_alignment Alinhamento horizontal dos filhos. Default: horizontal_alignment=CrossAxisAlignment.START.
on_scroll_interval Definição do intervalo de tempo para o evento on_scrollo, em milisegundos. Default: 10.
padding Espaço entre o conteúdo do objeto e suas bordas, em pixeis. Default: 10.
scroll Habilita rolagem (scroll) vertical para a página, evitando overflow. O valor da propriedade está em um ENUM ScrollMode com as possibilidades:

  • None (padrão): nenhum scroll. Pode haver overflow.
  • AUTO: scroll habilitado, a barra só aparece quando a rolagem é necessária.
  • ADAPTIVE: scroll habilitado, a barra de rolagem visível quando aplicativo é web ou desktop.
  • ALWAYS: scroll habilitado, a barra sempre exibida.
  • HIDDEN: scroll habilitado, barra de rolagem invisível.
spacing Espaço vertical entre os controles da página, em pixeis. Default: 10. Só aplicado quando alignment = start, end, center.
vertical_alignment Alinhamento vertical dos filhos. A propriedade está em um ENUM MainAxisAlignmente com as possibilidades:

  • START (padrão)
  • END
  • CENTER
  • SPACE_BETWEEN
  • SPACE_AROUND
  • SPACE_EVENLY

Exemplos:

page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
scroll_to(offset, delta, key, duration, curve) Move a barra de scroll para uma posição absoluta ou salto relativo para chave especificada.

View: Evento

Evento Descrição
on_scroll Dispara quando a posição da barra de rolagem é alterada pelo usuário.

O controle View é útil em situações que se apresenta mais em uma visualização na mesma página e será visto mais tarde com maiores detalhes.

Flet: Container

Um objeto Container é basicamente um auxiliar de layout, um controle onde se pode inserir outros controles, permitindo a decoração de cor de fundo, borda, margem, alinhamento e preenchimento. Ele também pode responder a alguns eventos.

Como exemplo, o código abaixo:

import flet as ft

def main(page: ft.Page):
    page.title = "Contêineres com cores de fundo"

    def cor(e):
        c4 = ft.Container(content=ft.Text("Outro conteiner azul!"), bgcolor=ft.colors.BLUE, padding=5)
        page.add(c4)

    c1 = ft.Container(content=ft.ElevatedButton("Um botão \"Elevated\""),
                      bgcolor=ft.colors.YELLOW, padding=5)

    c2 = ft.Container(content=ft.ElevatedButton("Elevated Button com opacidade=0.5",
                      opacity=0.5), bgcolor=ft.colors.YELLOW, padding=5)

    c3 = ft.Container(content=ft.Text("Coloca outra área azul"),
                      bgcolor=ft.colors.YELLOW, padding=5, on_click=cor)
    page.add(c1, c2, c3)

ft.app(target=main)

gera a janela na figura 1, após 1 clique no botão c3.

Figura 1: Novo container é adicionado ao clicar em “Coloca outra área azul”.

O container c3 reage ao evento clique, adicionando um (ou mais) botão azul à janela.

Container: Propriedades

Figura 2

A figura 2 mostra o esquema de espaçamentos entre controles: a largura e altura (width, height) do container, a margem (margin) entre a caixa de decoração e o container, a borda (border) da caixa e o espaçamento interno (padding) entre o controle e a caixa.

Propriedade Descrição
alignment Alinhamento do controle filho dentro do Container para exibir na parte superior da página. Alignment é uma instância do objeto alignment.Alignment com propriedades x e y que representam a distância do centro de um retângulo.

  • x=0, y=0: o centro do retângulo,
  • x=-1, y=-1: parte superior esquerda do retângulo,
  • x=1.0, y=1.0: parte inferior direita do retângulo.
Figura 3

Constantes de alinhamento pré-definidas no módulo flet.alignment são: top_left, top_center, top_right, center_left, center, center_right, bottom_left, bottom_center, bottom_right. Por exemplo, mostrado na figura 4:

  • container_1.alignment = alignment.center
  • container_2.alignment = alignment.top_left
  • container_3.alignment = alignment.Alignment(-0.5, -0.5)

Figura 4
animate Ativa a animação predefinida do container, alterando valores de suas propriedades de modo gradual. O valor pode ser um dos seguintes tipos:

  • bool: True para ativar a animação do container com curva linear de duração de 1000 milisegundos.
  • int: ajusta tempo em milisegundos, com curva linear.
  • animation: Animation(duration: int, curve: str): ativa animação do container com duração e curva de transição especificadas.

Por exemplo:

import flet as ft

def main(page: ft.Page):

    c = ft.Container(width=200, height=200, bgcolor="red", animate=ft.animation.Animation(1000, "bounceOut"))

    def animar_container(e):
        c.width = 100 if c.width == 200 else 200
        c.height = 100 if c.height == 200 else 200
        c.bgcolor = "blue" if c.bgcolor == "red" else "red"
        c.update()

    page.add(c, ft.ElevatedButton("Animate container", on_click=animar_container))

ft.app(target=main)

O código resulta na animação mostrada abaixo, na figura 5:

Figura 5: animação

bgcolor Cor de fundo do container.
blend_mode modo de mistura (blend) de cores ou gradientes no fundo container.
blur Aplica o efeito de desfoque (blur) gaussiano sobre o container.

O valor desta propriedade pode ser um dos seguintes:

  • um número: especifica o mesmo valor para sigmas horizontais e verticais, por exemplo 10.
  • uma tupla: especifica valores separados para sigmas horizontais e verticais, por exemplo (10, 1).
  • uma instância de ft.Blur: especifica valores para sigmas horizontais e verticais, bem como tile_mode para o filtro. tile_mode é o valor de ft.BlurTileMode tendo como default ft.BlurTileMode.CLAMP.
border Borda desenhada em torno do controle e acima da cor de fundo. Bordas são descritas por uma instância de border.BorderSide, com as propriedades: width (número) e color (string). O valor da propriedade border é instância de border.Borderclasse, descrevendo os 4 lados do retângulo. Métodos auxiliares estão disponíveis para definir estilos de borda:

  • border.all(width, color)
  • border.symmetric(vertical: BorderSide, horizontal: BorderSide)
  • border.only(left: BorderSide, top: BorderSide, right: BorderSide, bottom: BorderSide).

Por exemplo:

container_1.border = ft.border.all(10, ft.colors.PINK_600)
container_1.border = ft.border.only(bottom=ft.border.BorderSide(1, "black"))
border_radius Permite especificar (opcional) o raio de arredondamento das bordas. O raio é instância de border_radius.BorderRadius com as propriedades: top_left, top_right, bottom_left, bottom_right. Esses valores podem ser passados no construtor da instância, ou por meio de métodos auxiliares:

  • border_radius.all(value)
  • border_radius.horizontal(left: float = 0, right: float = 0)
  • border_radius.vertical(top: float = 0, bottom: float = 0)
  • border_radius.only(top_left, top_right, bottom_left, bottom_right)

Por exemplo: container_1.border_radius= ft.border_radius.all(30), fará todas as bordas do container_1 igual 1 30.

clip_behavior Opção para cortar (ou não) o conteúdo do objeto. A propriedade ClipBehavior é um ENUM com valores suportados:

  • NONE
  • ANTI_ALIAS
  • ANTI_ALIAS_WITH_SAVE_LAYER
  • HARD_EDGE

Se border_radius=None o default é ClipBehavior.ANTI_ALIAS. Caso contrário o default é ClipBehavior.HARD_EDGE.

content Define um controle filho desse container.
gradient O gradiente na cor de fundo. O valor deve ser uma instância de uma das classes: LinearGradient, RadialGradient e SweepGradient.


Uma descrição mais detalhada está em Detalhes sobre o gradiente de cores.

image_fit Descrita junto com o objeto image.
image_opacity Define a opacidade da imagem ao mesclar com um plano de fundo: valor entre 0.0 e 1.0.
image_repeat Descrita junto com o objeto image.
image_src Define imagem do plano de fundo.
image_src_base64 Define imagem codificada como string Base-64 como plano de fundo do container.
ink True para efeito de ondulação quando o usuário clica no container. Default: False.
margin Espaço vazio que envolve o controle. margin é uma instância de margin.Margin, definindo a propriedade para os 4 lados do retângulo: left, top, right e bottom. As propriedades podem ser dadas no construtor ou por meio de métodos auxiliares:

  • margin.all(value)
  • margin.symmetric(vertical, horizontal)
  • margin.only(left, top, right, bottom)

Por exemplo:

container_1.margin = margin.all(10)
container_2.margin = 20 # same as margin.all(20)
container_3.margin = margin.symmetric(vertical=10)
container_3.margin = margin.only(left=10)
padding Espaço vazio de decoração entre borda do objeto e seu container. Padding é instância da padding.Padding com propriedades definidas como padding para todos os lados do retângulo: left, top, right e bottom. As propriedades podem ser dadas no construtor ou por meio de métodos auxiliares:

  • padding.all(value: float)
  • padding.symmetric(vertical, horizontal)
  • padding.only(left, top, right, bottom)

Por exemplo:

container_1.padding = ft.padding.all(10)
container_2.padding = 20 # same as ft.padding.all(20)
container_3.padding = ft.padding.symmetric(horizontal=10)
container_4.padding=padding.only(left=10)
shadow Efeito de sombras projetadas pelo container. O valor dessa propriedade é uma instância ou uma lista de ft.BoxShadow, com as seguintes propriedades:

  • spread_radius: espalhamento, quanto a caixa será aumentada antes do desfoque. Default: 0.0.
  • blur_radius: desvio padrão da gaussiano de convolução da forma. Default: 0.0.
  • color: Cor da sombra.
  • offset: Uma instância de ft.Offsetclasse, deslocamentos da sombra, relativos à posição do elemento projetado. Os deslocamentos positivos em x e y deslocam a sombra para a direita e para baixo. Deslocamentos negativos deslocam para a esquerda e para cima. Os deslocamentos são relativos à posição do elemento que o está projetando. Default: ft.Offset(0,0).
  • blur_style: tipo de estilo, ft.BlurStyleque a ser usado na sombra. Default: ft.BlurStyle.NORMAL.

Exemplo:

ft.Container(
    shadow=ft.BoxShadow(
        spread_radius=1,
        blur_radius=15,
        color=ft.colors.BLUE_GREY_300,
        offset=ft.Offset(0, 0),
        blur_style=ft.ShadowBlurStyle.OUTER,
    )
)
shape A forma do conteiner. O valor é ENUM BoxShape: RECTANGLE (padrão), CIRCLE
theme_mode O ajuste do theme_mode redefine o tema usado no container e todos os objetos dentro dele. Se não for definido o tema em theme é válido para o container e seus filhos.
theme Ajuste o tema global e dos filhos na árvore de controle.

Segue um exemplo de uso:

import flet as ft

def main(page: ft.Page):
    page.theme = ft.Theme(color_scheme_seed=ft.colors.RED)

    b1 = ft.ElevatedButton("Botão com tema da página")
    b2 = ft.ElevatedButton("Botão com tema herdado")
    b3= ft.ElevatedButton("Botão com tema dark")

    c1 = ft.Container(
        b1,
        bgcolor=ft.colors.SURFACE_TINT,
        padding=20,
        width=300
    )
    c2 = ft.Container(
        b2,
        theme=ft.Theme(
            color_scheme=ft.ColorScheme(primary=ft.colors.PINK)
        ),
        bgcolor=ft.colors.SURFACE_VARIANT,
        padding=20,
        width=300
    )
    c3 = ft.Container(
        b3,
        theme=ft.Theme(
            color_scheme_seed=ft.colors.INDIGO
        ),
        theme_mode=ft.ThemeMode.DARK,
        bgcolor=ft.colors.SURFACE_VARIANT,
        padding=20,
        width=300
    )

    page.add(c1, c2, c3)

ft.app(main)
Figura 6: Temas

O tema principal da página é definido em page.theme, usando um seed vermelho. Os botões b1 e b2 simnplesmente herdam o tema da página. O botão b3 está no container definido com theme_mode=ft.ThemeMode.DARK, exibindo o tema escuro. O código gera a janela mostrada na figura 6.

Vale lembrar que c1 = ft.Container(b1,...) é equivalente à c1 = ft.Container(content = b1,...) sendo que o content só pode ser omitido se o conteúdo for inserido como primeiro parâmetro.urlDefine a URL a ser abertta quando o container é clicado, disparando o evento on_click.url_target

Define onde abrir URL no modo web:

  • _blank (default): em nova janela ou aba,
  • _self: na mesma janela ou aba aberta.

Container: Eventos

Evento Dispara quando
on_click o usuário clica no container.

class ft.ContainerTapEvent():
    local_x: float
    local_y: float
    global_x: float
    global_y: float

Obs.: O objeto de evento e é uma instância de ContainerTapEvent, exceto se a propriedade ink = True. Nesse caso e será apenas ControlEvent com data vazio.

Um exemplo simples de uso:

import flet as ft

def main(page: ft.Page):

    t = ft.Text()

    def clicou_aqui(e: ft.ContainerTapEvent):
        t.value = (
            f"local_x: {e.local_x}\nlocal_y: {e.local_y}"
            f"\nglobal_x: {e.global_x}\nglobal_y: {e.global_y}"
        )
        t.update()

    c1 = ft.Container(ft.Text("Clique dentro\ndo container"),
                      alignment=ft.alignment.center, bgcolor=ft.colors.TEAL_300, 
                      width=200, height=200, border_radius=10,  on_click=clicou_aqui)
    col = ft.Column([c1, t], horizontal_alignment=ft.CrossAxisAlignment.CENTER)
    page.add(col)

ft.app(target=main)
Figura 8: posição do click.

As propriedades e.local_x e e.local_y se referem à posição dentro do container c1, enquanto e.global_x e e.global_y se referem à posição global, dentro da página.

on_hover o cursor do mouse entra ou abandona a área do container. A propriedade data do evento contém um string (não um booleano) e.data = "true" quando o cursor entra na área, e e.data = "false" quando ele sai.

Um exemplo de um container que altera sua cor de fundo quando o mouse corre sobre ele:

import flet as ft

def main(page: ft.Page):
    def on_hover(e):
        e.control.bgcolor = "blue" if e.data == "true" else "red"
        e.control.update()

    c1 = ft.Container(width=100, height=100, bgcolor="red",
                      ink=False, on_hover=on_hover)
    page.add(c1)

ft.app(target=main)
on_long_press quando o container recebe um click longo (pressionado por um certo tempo).

Detalhes sobre o gradiente de cores

O gradiente na cor de fundo admite como valor uma instância de uma das classes: LinearGradient, RadialGradient e SweepGradient.

Um exmplo de uso está no código abaixo:

import flet as ft
import math

def main(page: ft.Page):
    c1 = ft.Container(
        gradient=ft.LinearGradient(
        begin=ft.alignment.top_center,
        end=ft.alignment.bottom_center,
        colors=[ft.colors.AMBER_900, ft.colors.BLUE],),
        width=150, height=150, border_radius=5,)

    c2 = ft.Container(
         gradient=ft.RadialGradient(colors=[ft.colors.GREY, ft.colors.CYAN_900],),
         width=150, height=150, border_radius=5,)

    c3 = ft.Container(
         gradient=ft.SweepGradient(center=ft.alignment.center,
         start_angle=0.0, end_angle=math.pi * 2,
         colors=[ft.colors.DEEP_PURPLE_800, ft.colors.DEEP_ORANGE_400],),
         width=150, height=150, border_radius=5,)
    
    page.add(ft.Row([c1, c2, c3]))

ft.app(target=main)

O código acima gera as imagens na figura 9:

Figura 9: LinearGradient, a segundo com RadialGradient e a última com SweepGradient

A primeira imagem é gerada com LinearGradient, a segunda com RadialGradient e a última com SweepGradient.

São propriedades da classe LinearGradient:

begin instância de Alignment. Posicionamento inicial (0.0) do gradiente.
end instância de Alignment. Posicionamento final (1.0) do gradiente.
colors cores assumidas pelo gradiente a cada parada. Essa lista deve ter o mesmo tamanho que stops se a lista for não nula. A lista deve ter pelo menos duas cores.
stops lista de valores de 0.0 a 1.0 marcando posições ao longo do gradiente. Se não nula essa lista deve ter o mesmo comprimento que colors. Se o primeiro valor não for 0.0 fica implícita uma parada em 0,0 com cor igual à primeira cor em colors. Se o último valor não for 1.0 fica implícita uma parada em 1.0 e uma cor igual à última cor em colors.
tile_mode como o gradiente deve preencher (tile) a região antes de begin depois de end. O valor é um ENUM GradientTileMode com valores: CLAMP (padrão), DECAL, MIRROR, REPEATED.
rotation rotação do gradiente em radianos, em torno do ponto central de sua caixa container.

Mais Informações:

Gradiente linear na documentação do Flutter.
Unidade de medida de radianos na Wikipedia.

São propriedades da classe RadialGradient:

colors, stops, tile_mode, rotation propriedades idênticas às de LinearGradient.
center instância de Alignment. O centro do gradiente em relação ao objeto que recebe o gradiente. Por exemplo, alinhamento de (0.0, 0.0) coloca o centro do gradiente radial no centro da caixa.
radius raio do gradiente, dado como fração do lado mais curto da caixa. Supondo uma caixa com largura = 100 e altura = 200 pixeis, um raio de 1 no gradiente radial colocará uma parada de 1,0 afastado 100,0 pixeis do centro.
focal ponto focal do gradiente. Se especificado, o gradiente parecerá focado ao longo do vetor do centro até esse ponto focal.
focal_radius raio do ponto focal do gradiente, dado como fração do lado mais curto da caixa. Ex.: um gradiente radial desenhado sobre uma caixa com largura = 100,0 e altura = 200,0 (pixeis), um raio de 1,0 colocará uma parada de 1,0 a 100,0 pixels do ponto focal.

São propriedades da classe SweepGradient:

colors, stops, tile_mode, rotation propriedades idênticas às de LinearGradient.
center centro do gradiente em relação ao objeto que recebe o gradiente. Por exemplo, alinhamento de (0.0, 0.0) coloca o centro do gradiente no centro da caixa.
start_angle ângulo em radianos onde será colocada a parada 0.0 do gradiente. Default: 0.0.
end_angle ângulo em radianos onde será colocada a parada 1.0 do gradiente. Default: math.pi * 2.

Graus e radianos

Figura 10: graus e radianos.

A maiora das medidas angulares na programação do flet (e do python em geral) é dada em radianos. Segue uma breve imagem explicativa do uso de radianos.

Bibiografia


Controles do Flet: Row e Column

Flet: Page, métodos e eventos

Flet, Layout: Page

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.

Métodos de page

Método Ação
can_launch_url(url) Verifica se a url pode ser acessada pelo aplicativo.
Retorna True se é possível gerenciar uma URL com o aplicativo. Caso retorne False isso pode significar que não há como vertificar se o recurso está disponível, talvez devido à falta de permissões. Nas versões mais recentes do Android e iOS esse método sempre retornará False, exceto se o aplicativo for configurado com essa permissão. Na web ele retornará False exceto para esquemas específicos, pois páginas web, por princípio, não devem ter acesso aos aplicativos instalados.
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().
launch_url(url) Abre uma URL em nova janela do navegador.

Abre uma nova página exibindo o recurso descrito na URL.
Admite os argumentos opcionais:

  • web_window_name: nome da janela ou tab de destino. “_self”, na mesma janela/tab, “_blank”, nova janela/tab (ou em aplicativo externo no celular); ou “nome_do_tab”: um nome personalizado.
  • web_popup_window: True para abrir a URL em janela popup. Default: False.
  • window_width – opcional, largura da janela de popup.
  • window_height – opcional, altura da janela de popup.
page.get_upload_url(file_name, expires) Gera URL para armazenamento de upload.
scroll_to(offset, delta, key, duration, curve) Move a posição de scroll para local absoluto ou incremento relativo. Detalhes em scroll_to.
set_clipboard(data) Insere conteúdo no clipboard do lado do cliente (navegador ou desktop). Ex.: page.set_clipboard("Esse valor vai para o clipboard").
show_banner(banner: Banner) Exibe um Banner no pé de página.
show_bottom_sheet(bottom_sheet: BottomSheet) Exibe um BottomSheet no pé de página.
show_dialog(dialog: AlertDialog) Exibe caixa de diálogo.
show_snack_bar(snack_bar: SnackBar) Exibe um SnackBar no pé de página.
window_center() 🖥️ 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.

import flet as ft

def main(page: ft.Page):
    def fechar(e):
        page.dialog = caixa_dialogo
        caixa_dialogo.open = True
        page.update()

    def sim(e):
        page.window_destroy()

    def nao(e):
        caixa_dialogo.open = False
        page.update()

    caixa_dialogo = ft.AlertDialog(
        modal=True,
        title=ft.Text("Confirme..."),
        content=ft.Text("Você quer realmente fechar o aplicativo?"),
        actions=[
            ft.ElevatedButton("Sim", on_click=sim),
            ft.ElevatedButton("Não", on_click=nao),
        ],
        actions_alignment=ft.MainAxisAlignment.END,
    )

    page.add(ft.Text("Feche a janela do aplicativo clicando o botão \"Fechar\"!"))
    page.add(ft.ElevatedButton("Fechar", icon="close", on_click=fechar, width=250))

ft.app(target=main)

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.
on_keyboard_event
on_resize É o evento disparado quando a janela do aplicativo é redimensionada pelo usuário.

def page_resize(e):
    print(f"A página foi redimensionada para: Laugura = {page.window_width}, Altura = {page.window_height})
page.on_resize = page_resize
on_route_change Evento disparado quando a rota da página é alterada no código, pela alteração da URL no navegador ou uso dos botões de avançar ou retroceder. O objeto e passado automaticamente pelo evento é uma instância da classe RouteChangeEvent:

class RouteChangeEvent(ft.ControlEvent):
    route: str     # passando uma nova rota para a página raiz
on_scroll a posição de scroll é alterada pelo usuário. Tem mesmo comportamento de Column.on_scroll.
on_view_pop Evento disparado quando o usuário clica o botão Back na barra de controle AppBar.
O objeto de evento e é uma instância da classe ViewPopEvent.

class ViewPopEvent(ft.ControlEvent):
    view: ft.View

sendo view uma instância do controle View, o conteiner da AppBar.

on_window_event Evento disparado quando a janela do aplicativo tem uma propriedade alterada: position, size, maximized, minimized, etc.
Pode ser usado com window_prevent_close=True (para interceptar sinal de close) e page.window_destroy() para implementar uma lógica de confirmação de término do aplicativo.

Os nomes eventos de janela possíveis são: close, focus, blur, maximize, unmaximize, minimize, restore, resize, resized (macOS e Windows), move, moved (macOS e Windows), enterFullScreen e leaveFullScreen.

Bibiografia

Flet: Propriedades de Page

Flet, Layout: Page

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,
  • 🅾 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 exibir na página. Novos controles pode ser adicionados com o método usual de lista, controls.append(ctrl)). Para remover o último controle inserido na página usamos controls.pop().

# Exemplos:
page.controls.append(ft.Text("Olá!"))
page.update()

# alternativamente
page.add(ft.Text("Olá!"))
# page.add é um shortcut para page.controls.append() seguido de page.update()

# para remover (pop) o último controle da lista
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.
Exemplos: page.fonts
Recebe um dicionário {"nome_da_fonte" : "url_da_fonte"}. Essas fonts podem ser usadas com text.font_family ou theme.font_family, para aplicar a fonte em todo o aplicativo. Podem ser usadas fontes .ttc, .ttf, .otf.

Figura 1: Inserindo fontes

Para um recurso externo a “url_da_fonte” é a URL absoluta, e para fontes instaladas a URL relativa para um diretório assets_dir. Esse diretório deve ser especificado na chamada para flet.app(), podendo ser um caminho relativo ao local onde está main.py ou um caminho absoluto. Por exemplo, suponha a estrutura mostrada na figura 1:

Para usar a fonte “Open Sans” e “Kanit”, importada do GitHub, como default, podemos fazer:

import flet as ft

def main(page: ft.Page):
    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 usa a fonte default Kanit"),
      ft.Text("Esse texto usa a fonte Open Sans", font_family="Open Sans")
    )
ft.app(target=main, assets_dir="assets")
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. Determina como os controles serão posicionados na horizontal. Default é horizontal_alignment=CrossAxisAlignment.START, que coloca os controles à esquerda de page. CrossAxisAlignment é um ENUM com os valores: START (default), CENTER, END, STRETCH e BASELINE.
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.
Notas sobre subscribe, unsubscribe e send. subscribe(handler) inscreve a sessão ativa do aplicativo para recebimento de mensagens. Se topic não for especificado todos os tópicos são enviados. handler é um método com um argumento que é a mensagem.subscribe_topic(topic, handler) especifica o tópico que será enviado.

send_all(message) envia para todos. send_all_on_topic(topic, message) especifica o tópico de envio.

@dataclass
class Message:
    user: str
    text: str

def main(page: ft.Page):

    def on_broadcast_message(message):
        page.add(ft.Text(f"{message.user}: {message.text}"))

    page.pubsub.subscribe(on_broadcast_message)

    def on_send_click(e):
        page.pubsub.send_all(Message("John", "Hello, all!"))

    page.add(ft.ElevatedButton(text="Send message", on_click=on_send_click))

unsubscribe() remove a sessão da lista de subscrição.

@dataclass
class Message:
    user: str
    text: str

def main(page: ft.Page):

    def on_leave_click(e):
        page.pubsub.unsubscribe()

    page.add(ft.ElevatedButton(text="Leave chat", on_click=on_leave_click))

Para remover a sessão da lista de destinatários de mensagens de um tópico use unsubscribe_topic(topic).
Para remover a sessão da lista de destinatários de mensagens de todos os tópicos: unsubscribe_all(), por exemplo quando o usuário fecha a janela:

def main(page: ft.Page):
    def client_exited(e):
        page.pubsub.unsubscribe_all()

    page.on_close = client_exited
pwa 🅾 pwa=True se o aplicativo está rodando como um Progressive Web App (PWA).
route Define ou lê a rota de navegação da página
rtl rtl=True para definir direção de texto da direita para esquerda. Default: False.
scroll Habilita a página para rolagem (scroll), mostrando ou ocultando a barra de rolagem. scroll=ScrollMode.None é o default. ScrollMode é um ENUM com os valores:

  • None ▸ (default), a coluna não pode ser percorrida e o texto pode extrapolar o espaço (overflow).
  • AUTO ▸ a rolagem está ativada e a barra só aparece quando necessária.
  • ADAPTIVE ▸ a rolagem está ativada e barra aparece quando aplicativo está na web ou desktop.
  • ALWAYS ▸ a rolagem está ativada e barra está sempre visível.
  • HIDDEN ▸ a rolagem está ativado mas a barra está oculta.
session_id 🅾 ID único da sessão do usuário.
spacing Espaço vertical entre controles na página. Default: 10 pixeis. Só é aplicado quando o alignment=start, end ou center.
splash Um controle que é exibido sobre o conteúdo da página. Operações lentas podem ser indicadas por meio de um ProgressBar ou ProgressRing.

É exibido em cima da página de conteúdo. Pode ser usado junto com ProgressBar ou ProgressRing para indicar uma operação demorada.

from time import sleep
import flet as ft

def main(page: ft.Page):
    def button_click(e):
        page.splash = ft.ProgressBar()
        btn.disabled = True
        page.update()
        sleep(3)
        page.splash = None
        btn.disabled = False
        page.update()

    btn = ft.ElevatedButton("Do some lengthy task!", on_click=button_click)
    page.add(btn)

ft.app(target=main)
show_semantics_debugger True para a exibição de informações emitidas pelo framework.
theme Essa propriedade recebe uma instância de theme.Theme para personalizar o tema. No estágio atual de desenvolvimento do Flet um theme só pode ser gerado a partir de uma cor “semente” (“seed”). Por exemplo, para um tema claro sobre tom verde:

page.theme = theme.Theme(color_scheme_seed="green")
page.update()

A classe Theme tem as propriedades:

  • color_scheme_seed ▸ cor “semente” para servir de base ao algoritmo de construção do tema.
  • color_scheme ▸ uma instância de ft.ColorScheme para personalização do esquema Material colors derivedo de color_scheme_seed.
  • text_theme ▸ instância de ft.TextTheme para personalização de estilos de texto.
  • primary_text_theme ▸ instância de ft.TextTheme descrevendo o tema de texto que constrasta com a cor primária.
  • scrollbar_theme ▸ instância de ft.ScrollbarTheme para personalizar a aparência da barra de rolagem.
  • tabs_theme ▸ instância de ft.TabsTheme para personalizar a aparência do controle de Tabs.
  • font_family ▸ a fonte base para todos os elementos da UI.
  • use_material3 ▸ True (default) para usar Material 3; caso contrário use Material 2.
  • visual_density ▸ ThemeVisualDensity enum: STANDARD (default), COMPACT, COMFORTABLE, ADAPTIVE_PLATFORM_DENSITY.
  • page_transitions ▸ instância de PageTransitionsTheme para personalizar transições entre páginas ().

† Transições de navegação: (theme.page_transitions personaliza transições entre páginas para diversas plataformas. Seu valor é uma instância de PageTransitionsTheme com as propriedades adicionais:

  • android ▸ default: FADE_UPWARDS
  • ios ▸ default: CUPERTINO
  • macos ▸ default: ZOOM)
  • linux ▸ default: ZOOM)
  • windows ▸ default: ZOOM

Transições suportadas estão em um ENUM ft.PageTransitionTheme: NONE (transição imediata e sem animação), FADE_UPWARDS, OPEN_UPWARDS, ZOOM, CUPERTINO.

São exemplos:

theme = ft.Theme()
theme.page_transitions.android = ft.PageTransitionTheme.OPEN_UPWARDS
theme.page_transitions.ios = ft.PageTransitionTheme.CUPERTINO
theme.page_transitions.macos = ft.PageTransitionTheme.FADE_UPWARDS
theme.page_transitions.linux = ft.PageTransitionTheme.ZOOM
theme.page_transitions.windows = ft.PageTransitionTheme.NONE
page.theme = theme
page.update()
ColorScheme class Um conjunto de 30 cores para configuração de cores dos componentes.
TextTheme class Personalização de estilo de textos.
ScrollbarTheme class Personalização de cor, espessura, forma da barra de scroll.
TabsTheme class Personalização da aparência de controles de TAB.
Navigation transitions Personalização das transições entre páginas de navegação.
theme_mode Tema da página: SYSTEM (default), LIGHT ou DARK
title Título da página ou do navegador.
vertical_alignment Alinhamento vertical dos controles filhos. Admite o ENUM MainAxisAlignment com os seguintes valores:

  • START (default)
  • END
  • CENTER
  • SPACE_BETWEEN
  • SPACE_AROUND
  • SPACE_EVENLY

Por exemplo vertical_alignment=MainAxisAlignment.START posiciona o filho no alto da página.

views 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 janela toda transparente.

Se usada com uma página transparente toda a janela fica transparente: page.window_bgcolor = ft.colors.TRANSPARENT e page.bgcolor = ft.colors.TRANSPARENT.

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, se o app foi iniciado em janela oculta.
window_visible=True torna a janela visível. Se o aplicativo é iniciado com view=ft.AppView.FLET_APP_HIDDENa janela é criada invisível. Para torná-la visível usamos page.window_visible = True.
window_width 🖥️ Leitura ou ajuste da largura da janela do aplicativo.

Bibiografia