Skip to main content

Popup menu component for Flet

Project description

DISPONIBLE EN INGLES Y ESPAÑOL

(VERSION INGLES)

Flet Popup Menu

A customizable and ready-to-use popup menu component for Flet applications.
It provides edit/delete dialogs, dynamic form generation for editing items, API communication,
and a flexible callback system for UI updates across modules.


🚀 Installation

pip install flet_popupmenu

Import

from flet_popupmenu import PopupMenuButton

Use

  • Initialize function called "PopupMenuButton" whose arguments are:
def PopupMenuButton(
    page: ft.Page,
    id: int,
    item_to_edit: dict = None,
    alias: str = "item",
    request_url: dict = None,
    callback=None,
    callbacks: dict = {}
):

🔍 ARGUMENTS EXPLAINED

Below is exactly what each argument does, and which ones are optional.

🔵 Required Arguments

✔ page: ft.Page

Current Flet page — required for dialogs, updates, and UI rendering.

✔ id: int

Unique identifier of the item being edited or deleted.

🟡 Optional Arguments (EXPLAINED IN DETAIL)

🟡 item_to_edit: dict = None (optional)

If provided → the popup shows a form for editing the item.

If omitted → the “edit” option is disabled automatically.

Structure example:

item_to_edit = {
    "title":  { "value": "Task A", "type": "text" },
    "state":  {
        "value": 1,
        "type": "dropdown",
        "options": { 0: "Pending", 1: "Completed" }
    }
}

If your item has no editable fields → simply pass None

🟡 alias: str = "item" (optional)

Name used in dialog messages. Examples:

  • "Task"

  • "User"

  • "Product"

If not set → defaults to "item".

🟡 request_url: dict = None (optional but RECOMMENDED)

Defines URLs for:

  • deleting the item

  • editing the item

Structure:

request_url = {
    "delete": {
        "url": "https://api.com/items?id=3",
        "headers": headers
    },
    "edit": {
        "url": "https://api.com/items?id=3",
        "headers": headers
    }
}

If omitted:

  • delete button will do nothing

  • edit button will do nothing

  • no errors will be thrown

🟡 callback=None (optional)

Function executed after a successful delete/edit.

Use it for UI refresh:

callback = refresh_tasks

⚠️ IMPORTANT: The callback must NOT require arguments. It must be a simple callable:

def refresh_tasks():
    ...

🟡 callbacks: dict = {}   (optional, advanced)

Allows triggering multiple auxiliary functions across your app.

This is useful if deleting/editing a task also needs to:

  • reload categories

  • refresh dashboards

  • reload summary counters

callbacks={
    "load_categories": {
        "function": refresh_categories,
        "args": []
    },
    "update_dashboard": {
        "function": refresh_dashboard,
        "args": []
    }
}

If omitted → only the main callback (if any) will run.

🟡 Layout: dict = {}   (optional, advanced)

Allows setting layout of popup container. Values are: top, left, right, bottom, alignment, border_radius, bg_color

layout = {
    "top": int | 8,
    "left": int | 0,
    "right": int | 8,
    "bottom": int | 0,
    "alignment": "[top_right | top_center | top_left | right | center | left | bottom_right | bottom_center | bottom_left]" | ft.Alignment.top_right,
    "border_radius": int # In case exists, will set width amd heght as 40x40 to cover image according background
}

🌟 FULL EXAMPLE (RECOMMENDED)

🟦 STRUCTURE

2 files:

app.py         → main module
tasks.py       → module where PopupMenuButton is used

🟩 1️⃣ app.py — MAIN FILE (DEFINE CALLBACKS)

# app.py
import flet as ft
from tasks import render_tasks_list

# ------- CALLBACKS DEFINED HERE IN -------
def refresh_tasks():
    """
    Refreshes the task list. Gets executed after deleting/editing.
    IMPORTANT: Must not require args.
    """
    print("✔ refresh_tasks() executed (UI update here).")

def refresh_categories():
    """
    Example of a secondary function fired from callbacks argument.
    It can reload categories, counters, dashboards, etc.
    """
    print("✔ refresh_categories() executed (extra UI update).")


def main(page: ft.Page):
    page.title = "Callback Example"
    page.vertical_alignment = ft.MainAxisAlignment.START

    # ------- PASS CALLBACKS TO MODULE -------
    content = render_tasks_list(
        page=page,
        callback_main=refresh_tasks,
        callbacks_extra={
            "reload_categories": {
                "function": refresh_categories,
                "args": []
            }
        }
    )

    page.add(content)


ft.app(target=main)

🟩 2️⃣ tasks.py — Where is used PopupMenuButton (RECIVES CALLBACKS)

# tasks.py
import flet as ft
from flet_popupmenu import PopupMenuButton


def render_tasks_list(page: ft.Page, callback_main=None, callbacks_extra=None):
    """
    callback_main: main refresh function (optional)
    callbacks_extra: dict with extra functions to run (optional)
    """

    # Simulated API task
    task = {
        "id": 3,
        "title": "Example Task",
        "description": "This is a sample description",
        "state": 1,
    }

    # Editable fields
    item_to_edit = {
        "id": { "value": task["id"], "type": "identifier", "disabled": True },
        "title": { "value": task["title"], "type": "text" },
        "description": { "value": task["description"], "type": "text" },
        "state": {
            "value": task["state"],
            "type": "dropdown",
            "options": {
                0: "Pending",
                1: "Completed",
                2: "In progress"
            }
        }
    }

    # Example headers
    headers = {
        "Content-Type": "application/json",
        "Authorization": "Bearer <token>"
    }

    popup = PopupMenuButton(
        page=page,
        id=task["id"],
        item_to_edit=item_to_edit,
        alias="Task",
        request_url={
            "delete": {
                "url": f"https://example.com/tasks?id={task['id']}",
                "headers": headers
            },
            "edit": {
                "url": f"https://example.com/tasks?id={task['id']}",
                "headers": headers
            }
        },

        # 🔥 MAIN CALLBACK (typically refresh list)
        callback=callback_main,

        # 🔥 EXTRA CALLBACKS (optional)
        callbacks=callbacks_extra
    )

    return ft.Column(
        controls=[
            ft.Text("Task List Example", size=20, weight="bold"),
            ft.ListTile(
                leading=ft.Icon(ft.icons.TASK),
                title=ft.Text(task["title"]),
                subtitle=ft.Text(task["description"]),
                trailing=popup
            )
        ]
    )

🟧 What exacly happens when the user deletes or edits:

    1. Popup sends a delete or an edit request to your API
    2. If API responses OK → PopupMenuButton runs:

callback_main()
callbacks_extra["reload_categories"]["function"](*args)

It called by example:

✔ refresh_tasks() → to reload the list ✔ refresh_categories() → to update other system parts

    3. Defenitely, the UI turns into completelly optimized.

⭐ His finallity:

  • How to separate logic across multiple files

  • How to pass a function from the main file

  • How to receive and use it within the component

  • How to use advanced callbacks

  • How to write functions without arguments

  • How to refresh the UI after actions

⚠️ COMMON MISTAKES TO AVOID

  • Do not pass callbacks that expect parameters. All functions must be def func(): ...

  • Ensure your API ends with a valid JSON response.

  • If item_to_edit is None, editing will be disabled.

  • If request_url is None, delete/edit buttons will do nothing.

  • The options key is only for dropdowns.

🧾 License

MIT License.

❤️ Contributions Welcome

Open a pull request or issue on GitHub. Suggestions and improvements are welcome!


🚀 ¿Do you even want badges for PyPI too?

Can add:

  • ✔ PyPI version
  • ✔ Python versions
  • ✔ Wheel status
  • ✔ Download counts
  • ✔ License badge
  • ✔ Supported OS

Only say:

👉 “Put badges too

(VERSIÓN EN ESPAÑOL)

Menú Emergente Flet

Un componente de menú emergente personalizable y listo para usar para aplicaciones Flet.
Proporciona diálogos de edición/eliminación, generación dinámica de formularios para editar elementos, comunicación con API
y un sistema flexible de callbacks para actualizar la interfaz de usuario en todos los módulos.


🚀 Instalación

pip install flet_popupmenu

Importación

from flet_popupmenu import PopupMenuButton

Uso

  • Inicializa la función "PopupMenuButton" cuyos argumentos son:
def PopupMenuButton(
    page: ft.Page,
    id: int,
    item_to_edit: dict = None,
    alias: str = "item",
    request_url: dict = None,
    callback=None,
    callbacks: dict = {}
):

🔍 EXPLICACIÓN DE ARGUMENTOS

A continuación se detalla para qué sirve cada argumento y cuáles son opcionales.

🔵 Argumentos obligatorios

✔ page: ft.Page

Página actual de Flet — necesaria para diálogos, actualizaciones y renderizado de la UI.

✔ id: int

Identificador único del elemento a editar o eliminar.

🟡 Argumentos opcionales (EXPLICADOS EN DETALLE)

🟡 item_to_edit: dict = None (opcional)

Si se proporciona → el menú muestra un formulario para editar el elemento.

Si se omite → la opción “editar” se desactiva automáticamente.

Ejemplo de estructura:

item_to_edit = {
    "title":  { "value": "Tarea A", "type": "text" },
    "state":  {
        "value": 1,
        "type": "dropdown",
        "options": { 0: "Pendiente", 1: "Completada" }
    }
}

Si tu elemento no tiene campos editables → simplemente pasa None.

🟡 alias: str = "item" (opcional)

Nombre usado en los mensajes de diálogo. Ejemplos:

  • "Tarea"
  • "Usuario"
  • "Producto"

Si no se define → por defecto es "item".

🟡 request_url: dict = None (opcional pero RECOMENDADO)

Define URLs para:

  • eliminar el elemento
  • editar el elemento

Estructura:

request_url = {
    "delete": {
        "url": "https://api.com/items?id=3",
        "headers": headers
    },
    "edit": {
        "url": "https://api.com/items?id=3",
        "headers": headers
    }
}

Si se omite:

  • el botón eliminar no hará nada
  • el botón editar no hará nada
  • no se lanzarán errores

🟡 callback=None (opcional)

Función ejecutada tras eliminar/editar exitosamente.

Úsala para refrescar la UI:

callback = refresh_tasks

⚠️ IMPORTANTE: El callback NO debe requerir argumentos. Debe ser una función simple:

def refresh_tasks():
    ...

🟡 callbacks: dict = {}   (opcional, avanzado)

Permite disparar múltiples funciones auxiliares en tu app.

Útil si al eliminar/editar una tarea también necesitas:

  • recargar categorías
  • refrescar dashboards
  • recargar contadores resumen
callbacks={
    "load_categories": {
        "function": refresh_categories,
        "args": []
    },
    "update_dashboard": {
        "function": refresh_dashboard,
        "args": []
    }
}

Si se omite → solo se ejecuta el callback principal (si existe).

🌟 EJEMPLO COMPLETO (RECOMENDADO)

🟦 ESTRUCTURA

2 archivos:

app.py         → módulo principal
tasks.py       → módulo donde se usa PopupMenuButton

🟩 1️⃣ app.py — ARCHIVO PRINCIPAL (DEFINE CALLBACKS)

# app.py
import flet as ft
from tasks import render_tasks_list

# ------- CALLBACKS DEFINIDOS AQUÍ -------
def refresh_tasks():
    """
    Refresca la lista de tareas. Se ejecuta tras eliminar/editar.
    IMPORTANTE: No debe requerir argumentos.
    """
    print("✔ refresh_tasks() ejecutado (actualización de UI).")

def refresh_categories():
    """
    Ejemplo de función secundaria disparada desde callbacks.
    Puede recargar categorías, contadores, dashboards, etc.
    """
    print("✔ refresh_categories() ejecutado (actualización extra de UI).")


def main(page: ft.Page):
    page.title = "Ejemplo de Callback"
    page.vertical_alignment = ft.MainAxisAlignment.START

    # ------- PASA CALLBACKS AL MÓDULO -------
    content = render_tasks_list(
        page=page,
        callback_main=refresh_tasks,
        callbacks_extra={
            "reload_categories": {
                "function": refresh_categories,
                "args": []
            }
        }
    )

    page.add(content)


ft.app(target=main)

🟩 2️⃣ tasks.py — Donde se usa PopupMenuButton (RECIBE CALLBACKS)

# tasks.py
import flet as ft
from flet_popupmenu import PopupMenuButton


def render_tasks_list(page: ft.Page, callback_main=None, callbacks_extra=None):
    """
    callback_main: función principal de refresco (opcional)
    callbacks_extra: dict con funciones extra a ejecutar (opcional)
    """

    # Tarea simulada de API
    task = {
        "id": 3,
        "title": "Tarea Ejemplo",
        "description": "Esta es una descripción de muestra",
        "state": 1,
    }

    # Campos editables
    item_to_edit = {
        "id": { "value": task["id"], "type": "identifier", "disabled": True },
        "title": { "value": task["title"], "type": "text" },
        "description": { "value": task["description"], "type": "text" },
        "state": {
            "value": task["state"],
            "type": "dropdown",
            "options": {
                0: "Pendiente",
                1: "Completada",
                2: "En progreso"
            }
        }
    }

    # Ejemplo de headers
    headers = {
        "Content-Type": "application/json",
        "Authorization": "Bearer <token>"
    }

    popup = PopupMenuButton(
        page=page,
        id=task["id"],
        item_to_edit=item_to_edit,
        alias="Tarea",
        request_url={
            "delete": {
                "url": f"https://example.com/tasks?id={task['id']}",
                "headers": headers
            },
            "edit": {
                "url": f"https://example.com/tasks?id={task['id']}",
                "headers": headers
            }
        },

        # 🔥 CALLBACK PRINCIPAL (normalmente refresca la lista)
        callback=callback_main,

        # 🔥 CALLBACKS EXTRA (opcional)
        callbacks=callbacks_extra
    )

    return ft.Column(
        controls=[
            ft.Text("Ejemplo de Lista de Tareas", size=20, weight="bold"),
            ft.ListTile(
                leading=ft.Icon(ft.icons.TASK),
                title=ft.Text(task["title"]),
                subtitle=ft.Text(task["description"]),
                trailing=popup
            )
        ]
    )

🟧 ¿Qué ocurre exactamente al eliminar o editar?

    1. El popup envía una petición de eliminación o edición a tu API
    2. Si la API responde OK → PopupMenuButton ejecuta:

callback_main()
callbacks_extra["reload_categories"]["function"](*args)

Ejemplo de llamada:

✔ refresh_tasks() → para recargar la lista
✔ refresh_categories() → para actualizar otras partes del sistema

    3. Finalmente, la UI queda completamente optimizada.

⭐ Su finalidad:

  • Cómo separar la lógica en varios archivos
  • Cómo pasar una función desde el archivo principal
  • Cómo recibirla y usarla en el componente
  • Cómo usar callbacks avanzados
  • Cómo escribir funciones sin argumentos
  • Cómo refrescar la UI tras acciones

⚠️ ERRORES COMUNES A EVITAR

  • No pases callbacks que requieran parámetros. Todas las funciones deben ser def func(): ...
  • Asegúrate de que tu API devuelva una respuesta JSON válida.
  • Si item_to_edit es None, la edición estará deshabilitada.
  • Si request_url es None, los botones de eliminar/editar no harán nada.
  • La clave options solo es para dropdowns.

🧾 Licencia

Licencia MIT.

❤️ ¡Contribuciones bienvenidas!

Abre un pull request o issue en GitHub. ¡Se aceptan sugerencias y mejoras!


🚀 ¿Quieres también badges para PyPI?

Se pueden añadir:

  • ✔ Versión PyPI
  • ✔ Versiones de Python
  • ✔ Estado Wheel
  • ✔ Contador de descargas
  • ✔ Badge de licencia
  • ✔ SO soportados

Solo di:

👉 “Pon badges también”

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flet_popupmenu-0.0.3.tar.gz (16.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flet_popupmenu-0.0.3-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file flet_popupmenu-0.0.3.tar.gz.

File metadata

  • Download URL: flet_popupmenu-0.0.3.tar.gz
  • Upload date:
  • Size: 16.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for flet_popupmenu-0.0.3.tar.gz
Algorithm Hash digest
SHA256 51f1ed508b3b240acb3ad4516da0f091d46d4862024925a9457f07ae848b6232
MD5 3b3256228f04a915a03f6ab34c1870df
BLAKE2b-256 bb458e892a4fa84c7345301cfacd99c44a36ed1edaf10f38a2e1557d0cb1cf6e

See more details on using hashes here.

File details

Details for the file flet_popupmenu-0.0.3-py3-none-any.whl.

File metadata

  • Download URL: flet_popupmenu-0.0.3-py3-none-any.whl
  • Upload date:
  • Size: 12.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for flet_popupmenu-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 6e45b5620841b12cc4108fcf3d3ffb03d9d017cf60fb928f8a6b6458f7fb705f
MD5 67a23175e8f1db958dd83bf2b305df01
BLAKE2b-256 edf65a53975e260c8819df8deae582e913e3836a867038c0c2e41784ebc3d51a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page