Flet: GridView e ResponsiveRow

Outros controles de Layout: GridView e ResponsiveRow

Controle GridView

GridView, assim como ListView, é um controle apropriado para apresentar listas longas (milhares de ítens). Esses controles devem ser escolhidos ao invés de Column ou Row para se obter um rolamento suave de conteúdo. O Navegador de Ícones (Icons Brower) do Flet é construído com um GridView.

Um exemplo curto do uso do controle é mostrado abaixo. Nesse código um array (uma lista) de 8 containeres com cores de fundo escolhidas entre os valores da lista, cores. Essa lista é passada em flet.GridView.controls = array.

import flet as ft

cores = {0:"black",  1:"#1D74FF", 2:"#C51709", 3:"#FFE000",
         4:"#00CB4F",5:"#A866DC", 6:"#FF6600", 7:"#145375"}

def main(page: ft.Page):
    array = []
    for i in range(0, 8):
        array.append(
            ft.Container(
                bgcolor=cores[i],
                border=ft.border.all(1, "BLACK"),
                border_radius=ft.border_radius.all(15),
            )
        )

    grid_imagens = ft.GridView(
        controls = array,
        expand=1,
        runs_count=15,
        max_extent=100,
        child_aspect_ratio=1.0,
        spacing=2,
        run_spacing=2,
    )
    page.add(grid_imagens)

ft.app(target=main)

A execução desse código resulta na tela mostrada na figura 1.

Figura 1: oito containeres de cores diferentes adicionados a um GridView

Propriedades de GridView

Propriedade Descrição
auto_scroll booleano, auto_scroll=True se a barra de rolamento se move automaticamente para a posição onde o último filho foi inserido. Essa propriedade deve ser False para que o método scroll_to() funcione.
child_aspect_ratio proporção entre dimensões vertical e horizontal (cross-axis e main-axis) de cada filho.
controls lista de controles a serem renderizados dentro do GridView.
horizontal booleano, horizontal=True para que a grade empilhe itens horizontalmente.
max_extent largura ou altura máxima de cada ítem na grade.
on_scroll_interval intervalo em milisegundos para o disparo do evento on_scroll. Default é 10.
padding espaço interno de separação entre os filhos e a grade.

Veja a propriedade Container.padding para maiores informações.

reverse booleano, reverse=True faz com que o rolamento se dê invertido, de baixo para cima. Default é False.
run_spacing espaçamento entre cada filho em pixeis, ao longo do eixo vertical.
runs_count o número de controles filhos a serem apresentados na vertical.
spacing espaçamento entre cada filho em pixeis, ao longo do eixo horizontal.

Método de GridView

scroll_to(offset, delta, key, duration, curve) move a barra de rolamento para a posição definida, em termos absoluto, relativo ou salto para uma chave especificada.

Veja o método Column.scroll_to() para maiores informações.

Evento de GridView

on_scroll Dispara quando a posição de rolamento é alterada pelo usuário.Veja o evento Column.on_scroll para maiores informações.

Exemplo de uso de GridView

No exemplo abaixo usamos o controle flet.Image(src, fit, repeat, border_radius) que ainda não descrevemos nessas notas. Seu objetivo é o de inserir imagens, no caso atual lidas no repositório Image Gallery localizada em picsum.photos.

import flet as ft

def main(page: ft.Page):
    page.title = "GridView Example"
    page.theme_mode = ft.ThemeMode.DARK
    page.padding = 10
    
    txt = ft.Text("Controle GridView com Imagens", size = 28)
    titulo=ft.Container(txt, data=0)
    
    def incrementa(e):
        titulo.data+=12
        carregar_imagens()

    bt_mais=ft.ElevatedButton(
        "Carregar mais imagens",
        icon=ft.icons.BEACH_ACCESS,
        color="WHITE",
        bgcolor="#6E432B", width=300, height=40,
        on_click=incrementa
    )

    grid_imagens = ft.GridView(
        expand=1,
        runs_count=15,
        max_extent=150,
        child_aspect_ratio=1.0,
        spacing=5,
        run_spacing=5,
    )

    def carregar_imagens():
        grid_imagens.controls=[]
        for i in range(titulo.data, titulo.data+12):
            grid_imagens.controls.append(
                ft.Image(
                    src=f"https://picsum.photos/150/150?{i}",
                    fit=ft.ImageFit.NONE,
                    repeat=ft.ImageRepeat.NO_REPEAT,
                    border_radius=ft.border_radius.all(10),
                )
            )
        page.update()

    carregar_imagens()
    page.add(ft.Column([ft.Row([titulo,  bt_mais]), grid_imagens]))

ft.app(target=main)
Figura 2: Execução do código de exemplo de GridView

Nesse código o controle flet.GridView é criado com o array de controles vazio e depois populado pela função carregar_imagens() que insere 12 imagens na propriedade GridView.controls. A propriedade titulo.data armazena a posição das imagens baixadas e é incrementada de 12 a cada disparo do botão bt_mais.

Controle ResponsiveRow

ResponsiveRow traz para o Flet o mesmo conceito de layout de grade usado no framework Bootstrap. Com ele se pode alinhar os controles filhos em colunas virtuais que podem ser redimensionadas. Por padrão uma grade possui 12 colunas, número que pode ser modificado na propriedade ResponsiveRow.columns.

Cada coluna inserida em um ResponsiveRow possui a propriedade col especificando quantas colunas cada controle deve preencher, de modo análogo ao uso da propriedade expand. Por exemplo, para criar um layout que contém duas colunas abrangendo 6 colunas virtuais cada usamos:

import flet as ft

ft.ResponsiveRow([
    ft.Column(col=6, controls=[ft.Text("Coluna 1")]),
    ft.Column(col=6, controls=[ft.Text("Coluna 2")])
])

O controle é dito “responsivo” porque pode ajustar o tamanho de seus filhos à geometrias variáveis de telas, que pode ser a página, a janela, etc. No exemplo acima a propriedade col é um número constante, significando que o filho ocupará 6 colunas para qualquer tamanho de tela. Se a propriedade col de um filho não for especificada ele terá o número máximo de colunas.

A propriedade col pode ser configurada para ter um valor diferente para “pontos de interrupção” (breakpoints) específicos, de acordo com o tamanho da tela. Os pontos de interrupção, como intervalos de dimensão, recebem nomes:

Por exemplo, no código abaixo o conteúdo é colapsado em apenas uma coluna em uma tela pequena (como a de um telefone celular) e assume 2 colunas em telas grandes:

import flet as ft
ft.ResponsiveRow([
    ft.Column(col={"sm": 6}, controls=[ft.Text("Column 1")]),
    ft.Column(col={"sm": 6}, controls=[ft.Text("Column 2")])
])

Propriedades de ResponsiveRow

Propriedade Descrição
alignment alinhamento horizontal dos controles filhos. Por exemplo, MainAxisAlignment.START (que é o default) posiciona os filhos à esquerda da linha.

Os valores possíveis da propriedade estão no enum MainAxisAlignment com os valores:

  • START (default)
  • END
  • CENTER
  • SPACE_BETWEEN
  • SPACE_AROUND
  • SPACE_EVENLY
columns o número de colunas virtuais a usar no layout dos filhos. Default é columns=12.
controls uma lista de controles a serem exibidos dentro do ResponsiveRow.
run_spacing espaçamento entre as linhas quando o conteúdo está quebrado em múltiplas linhas. Default run_spacing=10.
spacing espaçamento entre os controles em uma linha. Default é spacing=10 (pixeis virtuais). Esse espaçamento só é aplicado quando o alinhamento (alignment) é start, end ou center.
vertical_alignment como os controles filhos são posicionados verticalmente.

Os valores possíveis da propriedade estão no enum CrossAxisAlignment com os valores:

  • START (default)
  • CENTER
  • END
  • STRETCH
  • BASELINE

Exemplo de uso de ResponsiveRow

No exemplo abaixo tanto as funções coluna() como linha() retornam containeres com um texto. Esses controles preenchem as posições de coluna do ResponsiveRow, a primeira delas especificando os pontos de quebra em
col={"sm": 6, "md": 4, "xl": 2}, a segunda em col={"md": 4}. Observe que Container não tem a propriedade col, exceto quando dentro de uma “linha responsiva”.

import flet as ft

def main(page: ft.Page):

    def coluna(texto, cor):
        # retorna um Container com um texto
        return ft.Container(
            ft.Text(texto),
            padding=15,
            bgcolor=cor,
            col={"sm": 6, "md": 4, "xl": 2}
        )

    def linha(texto, cor):
        # retorna um Container com um texto
        return ft.Container(
            ft.Text(texto, size=18),
            width=200, height=60, padding=15,
            border_radius=10,
            bgcolor=cor,
            col={"md": 4}
        )

    def page_resize(e):
        info.value = f"{page.width} px"
        info.update()

    page.on_resize = page_resize
    info = ft.Text(size=28)

    page.overlay.append(
        ft.Container(
            info,
            width=200, height=70, padding=10, border_radius=15,
            bottom=50, right=50, bgcolor="#acdeff",
        )
    )

    page.add(
        ft.ResponsiveRow(
            [
                coluna("Coluna 1", ft.colors.YELLOW),
                coluna("Coluna 2", ft.colors.GREEN),
                coluna("Coluna 3", ft.colors.BLUE),
                coluna("Coluna 4", ft.colors.RED),
            ],
        ),
        ft.ResponsiveRow(
            [
                linha("texto 1", ft.colors.BLUE_50),
                linha("texto 2", ft.colors.RED_50),
                linha("texto 3", ft.colors.GREEN_50),
            ],
            run_spacing={"xs": 10},
        ),
    )
    page_resize(None)

ft.app(target=main)

A propriedade de largura da página page.width é exibida dentro de um Container inserido na camada page.overlay para flutuar em cima dos controles da página. Esse controle está definido em espaçamento fixo bottom=50 e right=50, podendo se sobrepor aos controles da camada abaixo. A atualização é disparada no evento page.on_resize.

Observe que a atualização realizada no redimensionamento altera info.value e a renderização é exposta na página com info.update(). O mesmo efeito seria obtido com page.update() pois esse método cuida de atualizar apenas as partes modificadas da página.

O resultado da execução do código aparece nas figuras 3 e 4.

Figura 3: estado inicial do aplicativo com tela de largura superior a 1200 px.
Figura 4: estado do aplicativo com tela redimensionada para 567 px, depois 288px.

Leave a Reply

Your email address will not be published. Required fields are marked *