
Exemplos 2: Referências a controles
Variáveis são referências
Widgets são classes comuns do Python, com suas propriedades e métodos. Para atuar sobre os objetos criados fazemos uma referência a eles, por meio dos nomes de variáveis. Vale lembrar que no Python toda variável é uma refeerência a algum objeto. Por exemplo, no código abaixo, um objeto flet.Text foi atribuído à variável t.
1 import flet as ft
2
3 def main(page: ft.Page):
4
5 def mudar_texto(e):
6 t.value="Um texto em azul!"
7 t.color="blue"
8 page.update()
9
10 t = ft.Text(value="Um texto em vermelho!", color="red", size=20)
11 bt=ft.ElevatedButton("Mudar texto", on_click=mudar_texto)
12 page.add(t,bt)
13
14 ft.app(target=main)

A linha 10 contém o código de inicialização do objeto Text onde atribuimos valor a duas propriedades, respectivamente value (o texto que será exibido) e color (a cor da fonte usada). Com um clique no botão bt as propriedades t.value e t.color são alteradas, usando a variável como referência ao objeto. A figura 1 mostra o estado inical do aplicativo, com o texto em vermelho, e após o clique no botão Mudar texto.
Obs.: para exibir t e bt na mesma linha podemos inserir uma linha na página contendo os dois widgets: page.add(ft.Row([t,bt])). Nesse caso o controle Row (em flet.Row(<lista-de-controles>))) serve de container para os controles na lista <lista-de-controles>.
Vimos que page recebe uma lista (de controles) no parâmetro controls. Essa lista pode ser tratada como qualquer outra lista do Python. Por exemplo, ela possui o método pop(n).
1 for i in range(6):
2 page.controls.append(ft.Text(f"Exibindo a linha {i}"))
3 page.update()
4
5 page.controls.pop()
6 page.update()

Esse código insere e exibe 6 linhas de texto na página. Em seguida ela exclui a última linha com page.controls.pop(). Assim com o fazemos com outras listas, page.controls.pop(n) excluiria a n-ésima linha.
A figura 2 mostra as 5 linhas restantes após a ação de page.controls.pop().
Referências para controles com a Classe Ref
Outro exemplo explora a mesma possibilidade manipulação das variáveis como referências aos objetos de controle. Os objetos TextField são criados com seus textos vazios, preenchidos pelo usuário, usados para inserir a linha 9de cumprimento com o clique do botão. O comando page.update() atualiza na tela apenas as campos que foram alterados. A propriedade autofocus=True na linha 4 garante que o cursor voltará para o objeto nome após a execução da função fala_oi.
1 import flet as ft
2
3 def main(page):
4 nome = ft.TextField(label="Nome", autofocus=True)
5 sobrenome = ft.TextField(label="Sobrenome")
6 coluna_ola = ft.Column()
7
8 def fala_oi(e):
9 coluna_ola.controls.append(ft.Text(f"Olá, {nome.value} {sobrenome.value}!"))
10 nome.value = ""
11 sobrenome.value = ""
12 page.update()
13 nome.focus()
14
15 page.add(
16 nome,
17 sobrenome,
18 ft.ElevatedButton("Diga oi!", on_click=fala_oi),
19 coluna_ola,
20 )
21
22 ft.app(target=main)
Em códigos mais longos e complexos um número maior de controles é adicionado, juntamente com suas propriedades e funções que manipulam seus eventos. Fica cada vez mais difícil, para o programador, se lembrar de todas as definições de nomes, por mais organizado que ele seja. A inserção de controles na página, efetuada nas linhas 15 a 20 não deixa muito clara a estrutura de controles inseridos.
Para isso o Flet incluiu uma classe utilitária chamada Ref, usada para fazer uma referência a um controle que pode ser usada em gerenciadores de eventos (event handlers) e, posteriormente, ser conectada a um controle real quando se constroi a árvore do aplicativo.
Para demontrar o uso dessa classe vamos alterar o código do exemplo acima usando Refs. Listamos abaixo como definir uma nova referência, como usar essa referência para inserir valores e, finalmente, como atribuir um controle real a essa referência, aproveitando todas as suas propriedades já definidas.
1 # criar uma referência a um TextField (por exemplo) 2 nome = ft.Ref[ft.TextField]() 3 4 # acessar o controle referenciado 5 nome.current.value = "José Ninguém" 6 7 # atribuindo a referência um controle real, que pode ser incluído na página 8 page.add( 9 ft.TextField(ref=nome, label="Nome", autofocus=True) 10 )
Vemos que objetos da classe Ref possuem a propriedade current que, por sua vez, contém as mesmas propriedades e métodos do pbjeto referenciado. Para ligar essa referência a um controle utilizamos a propriedade Control.ref do controle. Com essas definições a aplicativo pode ser escrito como:
1 import flet as ft
2
3 def main(page):
4 nome = ft.Ref[ft.TextField]()
5 sobrenome = ft.Ref[ft.TextField]()
6 coluna_ola = ft.Ref[ft.Column]()
7
8 def fala_oi(e):
9 coluna_ola.current.controls.append(
10 ft.Text(f"Olá, {nome.current.value} {sobrenome.current.value}!")
11 )
12 nome.current.value = ""
13 sobrenome.current.value = ""
14 page.update()
15 nome.current.focus()
16
17 page.add(
18 ft.TextField(ref=nome, label="Nome", autofocus=True),
19 ft.TextField(ref=sobrenome, label="Sobrenome"),
20 ft.ElevatedButton("Diga oi!", on_click=fala_oi),
21 ft.Column(ref=coluna_ola),
22 )
23
24 ft.app(target=main)

Observe que, fazendo isso, a estrutura da árvore de controles se torna mais claramente visível quando os controles são adicionados à página, na linha 17 e seguintes.
A figura 3 mostra os campos preenchidos e o resultado do clique no botão Diga oi.
Interação com o usuário
Claro que a maioria dos aplicativos demandam algum tipo de interação com o usuário, seja para escolher um ítem de busca, inserir novos dados ou escolher entre opções. Os exemplos acima usam de flet.TextField para a inserção de textos e ft.ElevatedButton para receber cliques e acionar uma função. Claro que existem muitas outras formas de interação que podem ser simples como um clique em botão ou sofisticadas quanto capturar a posição na tela de um clique, dentro de algum objeto desenhado na página.
Caixas de checagens e “dropdowns”
Vimos vários exemplos de uso das caixas de texto puro flet.Text(“Texto a exibir!”), usadas para exibir texto, e caixas interativas flet.TextField onde o usuário pode digitar dados e inserí-los no aplicativo.
As caixas de checagem, flet.Checkbox são úteis quando a escolha de dados é binária, geralmente sim/não.
1 import flet as ft
2
3 def main(page):
4 def checkbox_mudou(e):
5 texto_exibido.value = f"A caixa de checagem está {'marcada' if check.value else 'desmarcada'}."
6 page.update()
7
8 texto_exibido = ft.Text()
9 check = ft.Checkbox(label="Clique na caixa de checagem...", value=False, on_change=checkbox_mudou)
10 page.add(check, texto_exibido)
11
12 ft.app(target=main)

Esse código produz o resultado mostrado na figura, dependendo da caixa estar marcada ou não. O evento capturado nesse caso foi o flet.Checkbox.on_change que envia o evento para a função selecionada. Na função é lida a propriedade booleana check.value. A figura 4 mostra os dois estados possíveis do aplicativo.
Foi usado o operador trinário valor_se_verdadeiro if condição else valor_se_falso.
Dropdowns, flet.Dropdown são úteis quando várias escolhas de dados estão disponíveis. O conjunto permitido de escolhas pode ser inserido durante o desenvolvimento ou alterado pelo usuário.
1 import flet as ft
2
3 def main(page: ft.Page):
4 def dropdown_mudou(e):
5 escolhido.value = f"Você afirmou que prefere {dpd_linguagem.value}"
6 page.update()
7
8 escolhido = ft.Text()
9 itens_escolher = [
10 ft.dropdown.Option("Python"),
11 ft.dropdown.Option("Java"),
12 ft.dropdown.Option("Javascript"),
13 ft.dropdown.Option("C, C+"),
14 ft.dropdown.Option("Rust"),
15 ft.dropdown.Option("Ruby"),
16 ]
17 dpd_linguagem = ft.Dropdown(
18 label="Linguagem de programação",
19 width=100,
20 options=itens_escolher,
21 on_change=dropdown_mudou,
22 )
23 page.add(dpd_linguagem, escolhido)
24
25 ft.app(target=main)

Observe que flet.Dropdown.options é uma lista de objetos dropdown.Option(“nome”). O resultado está mostrado na figura 5.
Além do evento change, as caixas dropdown também podem responder a eventos on_blur, disparado quando o controle perde o foco, e on_focus, quando o controle recebe o foco.
Os ítens na caixa de seleção podem ser inseridos ou apagados dinamicamente, em tempo de execução, como mostramos no código seguinte.
1 import flet as ft
2
3 def main(page: ft.Page):
4 def inserir(e):
5 if txt_insere.value:
6 dp_box.options.append(ft.dropdown.Option(txt_insere.value))
7 dp_box.value, txt_insere.value = txt_insere.value, ""
8 page.update()
9
10 def apagar(e):
11 for item in dp_box.options:
12 if item.key == dp_box.value:
13 dp_box.options.remove(item)
14 page.update()
15
16 dp_box = ft.Dropdown(width=600)
17 txt_insere = ft.TextField(hint_text="Nome a inserir")
18 bt_insere = ft.ElevatedButton("Inserir", on_click=inserir)
19 bt_apaga = ft.OutlinedButton("Apagar item exibido", on_click=apagar)
20 page.add(dp_box, ft.Row(controls=[txt_insere, bt_insere, bt_apaga]))
21
22 ft.app(target=main)

Na linha 6 um item é inserido na caixa dropdown. dp_box.options é uma lista que pode ser acrescida de elementos com append. Essa lista é composta de objetos flet.dropdown.Option(texto). A operação de apagar procura por um item nessa lista e o remove, caso encontrado. A propriedade item.key extrai o texto de cada componente da lista. O texte feito na linha 12 é necessário porque tentar remover um item inexistente de uma lista resulta em erro. O aplicativo executado exibe algo como o representado na figura 6.
Capturando cliques e “hovers”
Um exemplo de captura da posição do cursor do mouse no momento do clique pode ser visto na página Layout do Flet: View e Container na seção Container: Eventos onde um clique dentro de um container dispara um evento para exibir as posições do ponto clicado, relativo ao container e à página.
No exemplo abaixo as ações são iniciadas pelo evento flet.Container.on_hover, disparadas quando o cursor do mouse entra ou sai da área delimitada pelo controle. O evento e passado para a função limpar carrega a propriedade e.data == "true" se o cursor entrou na área, e e.data == "false" se saiu dela. Na saída uma cor é escolhida eleatoriamente entre as cores do dicionário cores.
1 import flet as ft
2 import random
3
4 def main(page: ft.Page):
5 cores = {0:"#000000", 1:"#1D74FF", 2:"#C51709", 3:"#FFE000", 4:"#00CB4F",
6 5:"#A866DC", 6:"#FF6600", 7:"#aaffff", 8:"#145375", 9:"#a4b3c5"}
7
8 def limpar(e):
9 page.controls.pop()
10 page.add(matriz())
11 page.update()
12
13 def on_hover(e):
14 if e.data == "true":
15 e.control.bgcolor = "#efefef"
16 else:
17 e.control.bgcolor = cores[random.randrange(10)]
18 e.control.update()
19
20 def matriz():
21 matr = ft.Column()
22 for i in range(10):
23 lin = []
24 for j in range(10):
25 lin.append(ft.Container(width=50, height=50,
25 border=ft.border.all(1, "#293B4A"),
27 border_radius=ft.border_radius.all(10),
28 bgcolor="#F1EBC5", on_hover=on_hover)
29 )
30 linha = ft.Row(lin)
31 matr.controls.append(linha)
32 return matr
33
34 tit=ft.Text("Passe o cursor sobre os quadrados", size=18, weight=ft.FontWeight.BOLD)
35 bt_limpar=ft.ElevatedButton("Limpar tudo", icon="arrow_circle_left",
36 bgcolor="white", width=150, height=30, on_click=limpar)
37 page.add(ft.Row([tit,bt_limpar]), matriz())
38
39 ft.app(target=main)

O resultado do código está representado na figura 7, após o cursor ter sido passado sobre o objeto matriz(), que é uma coluna.
A construção da matriz de controles container é feita através da função matriz() que retorna 10 linhas, cada uma com 10 containeres, todos respondendo à container.on_hover com a mesma função, que altera a cor de fundo desse objeto.
O laço iniciado na linha 24 constroi um array com 10 objetos Containers (os quadrados). Esse array é inserido em uma linha flet.Row na linha 30. Essas 10 linhas são inseridas na coluna matr na linha 31.
O botão Limpar tudo remove o último controle inserido na página com page.controls.pop(), sendo que esse controle é a matriz de quadrados coloridos. Em seguida ele repõe novos quadrados com as cores originais.
