🧱 Semana 4 – Widgets Básicos (Text, TextField, Buttons) e Layout (Row/Column/Container)

📝 Tópico 1 – Text: títulos, estilos e ícones

O controle Text exibe textos. Podemos ajustar tamanho, peso da fonte e cor. Ícones podem ser usados com ft.Icon ou diretamente nos botões.

import flet as ft

def main(page: ft.Page):
    page.title = "Semana 4 - Text"
    titulo = ft.Text("Widgets básicos", size=24, weight=ft.FontWeight.BOLD)
    subtitulo = ft.Text("Trabalhando com Text, estilos e ícones", size=16, italic=True)
    linha = ft.Row([ft.Icon(ft.icons.INFO, color="blue"), ft.Text("Exemplo com ícone e texto")])
    page.add(titulo, subtitulo, linha)

ft.app(target=main)
  • size: tamanho; weight: espessura (ex.: FontWeight.BOLD); color: cor do texto.
  • Ícones em ft.icons (ex.: ft.icons.HOME, ft.icons.ADD).
⌨️ Tópico 2 – TextField: rótulos, dicas e validação

TextField captura entrada do usuário. Podemos exibir rótulo, dica (placeholder), ícones e mensagens de erro (error_text).

import flet as ft

def main(page: ft.Page):
    page.title = "Semana 4 - TextField"
    nome = ft.TextField(label="Seu nome", hint_text="Digite seu nome", prefix_icon=ft.icons.PERSON)
    email = ft.TextField(label="E-mail", hint_text="exemplo@empresa.com", prefix_icon=ft.icons.EMAIL)
    mensagem = ft.Text(value="Preencha e clique em Enviar.")

    def enviar(e):
        ok = True
        if not nome.value:
            nome.error_text = "Informe o nome."
            ok = False
        else:
            nome.error_text = None
        if not email.value or "@" not in email.value:
            email.error_text = "E-mail inválido."
            ok = False
        else:
            email.error_text = None

        if ok:
            mensagem.value = f"Obrigado, {nome.value}! Verifique seu e-mail: {email.value}"
            page.snack_bar = ft.SnackBar(ft.Text("Dados enviados!"))
            page.snack_bar.open = True
        page.update()

    page.add(ft.Column([nome, email, ft.ElevatedButton("Enviar", on_click=enviar), mensagem], width=420))

ft.app(target=main)
  • Use error_text para feedback de validação na própria caixa de texto.
  • Senha: TextField(password=True, can_reveal_password=True).
🖱️ Tópico 3 – Buttons: Elevated, Outlined, Text e IconButton

Botões disparam ações por eventos. Cada tipo tem estilo próprio:

  • ElevatedButton: destaque primário.
  • OutlinedButton: com borda.
  • TextButton: sem borda/preenchimento.
  • IconButton: apenas ícone.
import flet as ft

def main(page: ft.Page):
    resultado = ft.Text("Aperte um botão...")
    contador = 0

    def clicar(e, msg):
        nonlocal contador
        contador += 1
        resultado.value = f"{msg} (total cliques: {contador})"
        page.update()

    page.add(
        ft.Row(
            [
                ft.ElevatedButton("Salvar", icon=ft.icons.SAVE, on_click=lambda e: clicar(e, "Salvar clicado")),
                ft.OutlinedButton("Cancelar", icon=ft.icons.CLOSE, on_click=lambda e: clicar(e, "Cancelar clicado")),
                ft.TextButton("Ajuda", on_click=lambda e: clicar(e, "Ajuda clicado")),
                ft.IconButton(icon=ft.icons.ADD, tooltip="Adicionar", on_click=lambda e: clicar(e, "Adicionar clicado")),
            ],
            wrap=True, spacing=10
        ),
        resultado
    )

ft.app(target=main)
📐 Tópico 4 – Layout com Row e Column (alinhamentos e espaço)

Row organiza na horizontal; Column na vertical. Controle o alinhamento no eixo principal e no eixo cruzado.

import flet as ft

def main(page: ft.Page):
    linha = ft.Row(
        [
            ft.Container(ft.Text("A"), bgcolor="#E3F2FD", padding=10),
            ft.Container(ft.Text("B"), bgcolor="#BBDEFB", padding=10),
            ft.Container(ft.Text("C"), bgcolor="#90CAF9", padding=10),
        ],
        alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
        vertical_alignment=ft.CrossAxisAlignment.CENTER,
    )

    coluna = ft.Column(
        [ft.Text("Topo"), ft.Text("Meio"), ft.Text("Base")],
        horizontal_alignment=ft.CrossAxisAlignment.CENTER,
        spacing=10
    )

    page.add(
        ft.Column(
            [
                ft.Text("Row: SPACE_BETWEEN", weight=ft.FontWeight.BOLD),
                linha,
                ft.Divider(),
                ft.Text("Column centralizada", weight=ft.FontWeight.BOLD),
                coluna,
            ],
            expand=True
        )
    )

ft.app(target=main)
  • spacing adiciona espaçamento entre filhos.
  • expand=True faz o controle ocupar o espaço disponível.
📦 Tópico 5 – Container: padding, borda, cor e “card” simples

Container permite estilizar blocos: fundo, borda, raio e espaçamentos. Útil para “cards”.

import flet as ft

def make_card(titulo: str, subtitulo: str) -> ft.Control:
    return ft.Container(
        content=ft.Column(
            [ft.Text(titulo, weight=ft.FontWeight.BOLD), ft.Text(subtitulo, size=12, color="grey")],
            spacing=4
        ),
        padding=16,
        bgcolor="#FAFAFA",
        border=ft.border.all(1, "#E0E0E0"),
        border_radius=10,
        ink=True
    )

def main(page: ft.Page):
    cards = [make_card(f"Item {i}", "Descrição do item") for i in range(1, 5)]
    page.add(ft.Row(cards, spacing=12, wrap=True))

ft.app(target=main)

Outros recursos do Container: margin, alignment e gradient (quando suportado pelo tema).

💬 Tópico 6 – Feedback: SnackBar e AlertDialog

Para mensagens rápidas use SnackBar; para confirmações importantes, AlertDialog.

import flet as ft

def main(page: ft.Page):
    status = ft.Text()

    def show_snack(e):
        page.snack_bar = ft.SnackBar(ft.Text("Operação realizada!"))
        page.snack_bar.open = True
        page.update()

    dlg = ft.AlertDialog(
        modal=True,
        title=ft.Text("Confirmação"),
        content=ft.Text("Deseja realmente excluir?"),
        actions=[
            ft.TextButton("Cancelar", on_click=lambda e: close_dialog(False)),
            ft.ElevatedButton("Excluir", on_click=lambda e: close_dialog(True)),
        ],
        actions_alignment=ft.MainAxisAlignment.END,
    )

    def open_dialog(e):
        page.dialog = dlg
        dlg.open = True
        page.update()

    def close_dialog(confirmado: bool):
        dlg.open = False
        status.value = "Excluído!" if confirmado else "Cancelado."
        page.update()

    page.add(
        ft.Row(
            [
                ft.ElevatedButton("SnackBar", on_click=show_snack),
                ft.OutlinedButton("Confirmar exclusão", icon=ft.icons.DELETE, on_click=open_dialog),
                status,
            ],
            spacing=10
        )
    )

ft.app(target=main)

Padrão de UX: SnackBar para feedback leve; Dialog para ações destrutivas ou confirmações.

🛠️ Prática Guiada – Semana 4
  1. Crie uma tela “Cadastro Rápido” com Nome e E-mail (2 TextField com label, hint_text e prefix_icon).
  2. Adicione: Salvar (ElevatedButton), Limpar (OutlinedButton) e um IconButton com ft.icons.INFO para ajuda.
  3. Validação: Nome obrigatório; E-mail precisa de “@” (use error_text).
  4. Ao salvar com sucesso, mostre SnackBar. Ao excluir (adicione um botão), abra AlertDialog pedindo confirmação.
  5. Organize com Column e Row. Use Container como “card”.
  6. Tire prints da tela com validação, SnackBar e Dialog (anexe na entrega).
📝 Tarefas / Entrega – Semana 4
  • Envie um PDF chamado Desktop_Semana04_NomeSobrenome.pdf contendo:
    • Print da tela “Cadastro Rápido” com o “card” (Container) e os campos.
    • Print do erro de validação (error_text) funcionando.
    • Print do SnackBar após salvar com sucesso.
    • Print do AlertDialog de confirmação.
    • Trecho do código que monta a UI (Row/Column/Container) e os handlers.
  • Prazo: domingo 23:59.
📏 Rubrica (0–10)
Critério Descrição Pontos
Formulário TextFields com label/hint/ícone + card 0–3,0
Validação Regras com error_text 0–2,0
Feedback SnackBar e AlertDialog 0–2,0
Layout Row/Column bem alinhados 0–2,0
Clareza do código Handlers limpos 0–1,0
Total 10,0