Outros controles de Layout: ListTile e ListView
Controle ListTile
ListTile é um controle que exibe uma linha de altura fixa que pode conter outros controles, geralmente imagens, ícones, textos, botões e menus popups. Ele admite ícones à direita ou à esquerda do título, podendo seus objetos interiores responder a certos eventos.
Um exemplo mínimo de código aparece listado abaixo, com a ilustração de seu resultado ao ser executado. Um menu popup foi incluído porque esses controles são usados frequentemente juntos com ListTile.
import flet as ft def main(page): lTile1=ft.ListTile(title=ft.Text("Você gosta de animais?", size=18)) pop_menu=ft.PopupMenuButton( icon=ft.icons.MORE_VERT, items=[ft.PopupMenuItem(text="Jacaré"), ft.PopupMenuItem(text="Leão")] ) lTile2=ft.ListTile( title=ft.Text("Qual é o seu animal favorito?"), subtitle=ft.Text("Selecione no menu popup"), trailing=pop_menu ) page.add(ft.Card(ft.Column([lTile1, lTile2]), width=300)) ft.app(target=main)
A figura 1 mostra o resultado desse código, renderizado no desktop.
Propriedades de ListTile
Propriedade | Descrição |
---|---|
autofocus | booleano, autofocus=True se o controle receberá o foco inicial (o cursor está sobre o controle). Se mais de um controle tiver esse ajuste o primeiro deles (na ordem da página) receberá o foco. |
content_padding | Espaçamento interno (padding) do controle. Separação entre os controles internos: leading, title, subtitle, e trailing. O default é padding.symmetric(horizontal=16) . Veja Container.padding para maiores informações. |
dense | define se o controle tem exibição densa, com menor altura e caracteres compactos. |
is_three_line | booleano, se is_three_line=True o controle ListTile deve exibir 3 linhas de texto, e o subtitulo nao pode ser Null, uma vez que ele devera conter as linhas 2 e 3 de texto.Se is_three_line=False o ListTile deve conter uma linhas se subtitle=Null e duas linhas caso contrário.
Se um controle flet.Text for usado para title ou subtitle é possível estabelecer um limite máximo para o número de linhas com Text.max_lines. |
leading | controle a ser exibido antes (à esquerda) do título. |
selected | booleano; se selected=True para um tile então os ícones e textos terão a mesma cor. Por default a cor dos elementos em selected é a cor primária do tema. |
subtitle | Linhas (ou linhas de texto) a serem exibidas abaixo do título. Em geral um flet.Text .Se is_three_line=False o subtítulo não quebrará em linhas longas. Caso contrário deve ser configurado o número máximo de linhas (por exemplo com Text.max_lines). |
title | controle a ser exibido como conteúdo principal ou título, em geral um flet.Text . Linhas de título não são quebradas e um limite para essa linha única pode ser ajustado com Text.max_lines. |
toggle_inputs | booleano; define se um clique na ListTile deve abrir (ou fechar) uma caixa de Radio, Checkbox ou Switch. O default é toggle_inputs=False . |
trailing | controle a ser exibido após (à direita ) do título. Em geral é usado um controle de ícone. |
url | a URL a ser aberta quando o ListTile é clicado. O evento on_click, se definido, será acionado. |
url_target | a URL a ser aberta, no modo Web.
|
Eventos de ListTile
Evento | Descrição |
---|---|
on_click | dispara com um clique (ou toque em touch screens) no controle ListTile. |
on_long_press | dispara com um clique longo (ou toque longo em touch screens) no controle ListTile |
Exemplo de uso de ListTile
import flet as ft def main(page): page.title = "Exemplo de uso do Widget ListTile" page.horizontal_alignment=ft.CrossAxisAlignment.CENTER def clk(e): lt1.data +=1 lt1.title.value=f"Você clicou {lt1.data} {'vez' if lt1.data==1 else 'vezes'} no título" page.update() def clicado(e): ct_info.content.value=f"Foi clicado o item: {e.control.text}" page.update() class PI(ft.PopupMenuItem): def __init__(self, texto): super().__init__() self.text=texto self.on_click=clicado lt1=ft.ListTile(title=ft.Text("Linha única no título", size=18), data=0, on_click=clk) lt2=ft.ListTile(title=ft.Text("Uma linha densa nessa listTile"), dense=True) lt3=ft.ListTile( leading=ft.Icon(ft.icons.DELETE_FOREVER_ROUNDED), title=ft.Text("Essa linha abre selecionada"), selected=True ) lt4=ft.ListTile( leading=ft.ElevatedButton(text="X", bgcolor="#cc8866", on_click=clicado), title=ft.Text("Linha com controle anterior"), subtitle=ft.Text("Clique para apagar caixa de info") ) lt5=ft.ListTile( title=ft.Text("Linha com controle posterior"), trailing=ft.PopupMenuButton( icon=ft.icons.MORE_VERT, items=[PI("Popup 1.1"), PI("Popup 1.2")] ) ) lt6=ft.ListTile( leading=ft.Icon(ft.icons.BATTERY_1_BAR), title=ft.Text("Linha com controles anterior e posterior"), trailing=ft.PopupMenuButton( icon=ft.icons.MORE_VERT, items=[PI("Popup 2.1"), PI("Popup 2.2")] ) ) lt7=ft.ListTile( leading=ft.Icon(ft.icons.MENU_BOOK), title=ft.Text("Título e subtítulo com controles anterior e posterior"), subtitle=ft.Text("Aqui vai um subtítulo"), trailing=ft.PopupMenuButton( icon=ft.icons.MORE_VERT, items=[PI("Popup 3.1"), PI("Popup 3.2")] ) ) ct_info=ft.Container( content=ft.Text("Aqui você verá os itens clicados!"), width=500, height=40, bgcolor=ft.colors.AMBER_100, alignment=ft.alignment.center, border=ft.border.all(1, ft.colors.BLUE_100), border_radius = ft.border_radius.all(10) ) titulo=ft.Tooltip( message="Exemplo de tooltip", content=ft.Text("Informações de cliques:"), padding=10 ) page.add( ft.Container( content=ft.Column( [lt1, lt2, lt3, lt4, lt5, lt6, lt7, titulo, ct_info], spacing=20 ), width=500, bgcolor=ft.colors.BLUE_50, border=ft.border.all(1, ft.colors.BLUE_500), border_radius = ft.border_radius.all(15), padding=ft.padding.all(15), shadow=ft.BoxShadow( blur_radius=15, color=ft.colors.BLUE_GREY_500, offset=ft.Offset(0, 0), blur_style=ft.ShadowBlurStyle.OUTER ) ) ) ft.app(target=main)
A figura 2 exibe o código em execução.
Um clique no controle lt1 aciona a função clk(e)
que incrementa o valor de lt1.data
e o exibe em lt1.title.value
.
Os controles lt5, lt6 e lt7 recebem em ListTile.trailing
um menu popup PopupMenuButton. Cada um deles contém PopupMenuButton.items=[PI("Texto 1"), PI("Texto 2")]
, onde PI herda da classe PI(ft.PopupMenuItem)
. Isso cria menus popups que abrem ao serem clicados o ícone à direita, icon=ft.icons.MORE_VERT (três pontos verticais). Todos esses ítens respondem ao evento click abrindo a função clicado
. Essa função recebe exibe o texto do controle que enviou o evento em ct_info.content.value
. O texto é capturado em e.control.text
.
Observe que ct_info.content.value
é o texto dentro do container ct_info, um texto e não um objeto flet.Text. O nome do controle clicado está em e.control.text
.
Como exemplo foi inserido um Tooltip (ferramenta de dicas) no controle titulo, responsável por exibir uma messagem quando o cursor percorre a área do controle.
Controle ListView
Ainda que seja possível adicionar listas longas nos controles Column e Row esses controles serão pouco eficazes e lentos nessa exibição, principalmente porque adicionam seu conteúdo de uma só vez, mesmo que não estejam visíveis. A solução para essa questão consiste em usar os controles ListView e GridView.
ListView é um controle especialmente útil para a exibição de conteúdo longo. Ele insere controle sucessivamente na direção estabelecida para o rolamento, permitindo percorrer sua extensão automaticamente (com o autoscroll). Controles podem ser adicionados na vertical (o default) ou na horizontal.
Apesar de que ListView insere gradualmente seu conteúdo e renderiza seus filhos com eficiência, o rolamento pode ser melhorado ajustando uma altura fixa para todos os ítens filhos no rolamento vertical, ou largura fixa no rolamento horizontal. Para fazer isso podemos determinar uma altura absoluta na propriedade item_extent ou, alternativamente marcando a propriedade first_item_prototype = True
no primeiro filho, o que obriga todos os demais a ter a mesma extensão.
O seguinte exemplo mostra como se pode renderizar rapidamente 5000 linhas de texto com esse controle.
import flet as ft def main(page: ft.Page): lv = ft.ListView(expand=True, spacing=10) for i in range(5000): lv.controls.append(ft.Text(f"Linha {i+1}")) page.add(lv) ft.app(target=main)
Nesse caso o rolamento não é automático. Para que a tela role junto com inserção de controle modificamos a definição do controle usando:
lv = ft.ListView(expand=True, auto_scroll=True, spacing=10)
.
Note que foi usado expand=True
no construtor de ListView. Para que essa propriedade funcione corretamente temos que ajustar a altura (height), ou largura (width) no rolamento horizontal. Podemos marcar ListView(height=300, spacing=10)
, por exemplo. No exemplo acima forçamos o controle a usar todo o espaço disponível com ListView.expand=True
.
Propriedades de ListView
Propriedade | Descrição |
---|---|
auto_scroll | booleano, auto_scroll=True se a barra de rolamento (scrollbar) deve ser mover automaticamente até o final do último filho acrescentado. Para que o método scroll_to() funcione esse controle deve ser ajustado como auto_scroll=False . |
controls | A lista de controles a serem exibidos dentro do ListView. |
divider_thickness | Se esse valor for maior que zero um controle Divider é usado no espaçamento entre os ítens. O default é divider_thickness=0 . |
first_item_prototype | booleano, first_item_prototype=True para que as dimensões do primeiro filho devem ser usadas como “protótipo” para os demais itens inseridos. Nesse caso todos os controles terão a mesma altura ou largura que o primeiro filho. O default é False. |
horizontal | booleano, horizontal=True para que os itens sejam dispostos horizontalmente. |
item_extent | uma altura (ou largura, no rolamento horizontal) fixa para que um item tenha renderização otimizada. |
on_scroll_interval | passo ou salto em milisegundos para o disparo do evento on_scroll. Default é on_scroll_interval=10 . |
padding | espaçamento interno para posicionamento dos fihos. Veja a propriedade Container.padding. |
reverse | booleano, define a direção do rolamento. Default é reverse=False . |
spacing | altura do controle Divider entre itens do ListView. Espaçamento zero é usado se esse valor não é especificado. |
Método de ListView
scroll_to(offset, delta, key, duration, curve) | Move a posição da barra de rolagem (e, portanto, da posição visível dentro do controle) para uma marca absoluta, um salto relativo ou para uma chave especificada.
Veja Column.scroll_to() para detalhes. |
Evento de ListView
on_scroll | dispara quando a posição da barra de rolamento é alterada pelo usuário.
Veja Column.scroll_to() para detalhes. |
Exemplo de uso de ListView
O exemplo mostra a montagem de conteúdo dentro de um ListView, com intervalo de tempo de .1 segundo
provocado por sleep(.1)
.
from time import sleep import flet as ft def main(page: ft.Page): page.title = "Use ListView com auto-rolagem" arr=[ft.Text("<h1>Título do conteúdo da Web</h1> ", size=20, bgcolor="#aabbff")] lv = ft.ListView( controls=[ft.Text("<h1>Título do conteúdo da Web</h1> ", size=20)], expand=True, spacing=1, padding=40, auto_scroll=True, divider_thickness=1 ) ct=ft.Container( content=lv, width=500, height=500, bgcolor=ft.colors.AMBER_50, alignment=ft.alignment.center, border=ft.border.all(1, ft.colors.BLUE_100), border_radius = ft.border_radius.all(10) ) page.add(ct) def escreve_texto(texto): sleep(.1) lv.controls.append(ft.Text(texto)) page.update() escreve_texto("<p>Podemos construir uma lista não-ordenada:</p>") escreve_texto("<ul>") for i in range(0, 3): escreve_texto(f" <li>Exibindo conteúdo da linha {i}</li>") escreve_texto("</ul>") escreve_texto("<p>Podemos construir uma tabela</p>") escreve_texto("<table>") escreve_texto("<tr><th>Palavra</th><th>Número</th>") escreve_texto("<th>Palavra</th><th>Número</th>") escreve_texto("<th>Palavra</th><th>Número</th></tr>") for i in range(0, 9, 3): escreve_texto("<tr>") escreve_texto(f" <td>palavra</td><td>{i}</td>") escreve_texto(f" <td>palavra</td><td>{i+1}</td>") escreve_texto(f" <td>palavra</td><td>{i+2}</td>") escreve_texto("</tr>") escreve_texto("</table>") ft.app(target=main)
O controle ListView é criado e inserido na página com apenas um controle (flet.Text). Controles filhos são adicionados depois por meio da função escreve_texto(texto)
. A figura 3 mostra o aplicativo resultante da execução desse código.
O texto gerado é parte de uma página HTML, algo usado apenas como uma demonstração da montagem gradual no controle, sem um significado especial pois esse controle não pode renderizar texto com marcação HTML. O texto gerado seria representado em um navegador como mostrado na figura 4, incluído aqui apenas por completeza.