flet-dev / flet

Flet enables developers to easily build realtime web, mobile and desktop apps in Python. No frontend experience required.
https://flet.dev
Apache License 2.0
11.33k stars 439 forks source link

Multiselect Dropdown #1741

Open Xynonners opened 1 year ago

Xynonners commented 1 year ago

Please Describe The Problem To Be Solved

Multiselect dropdown. Something like the gradio multiselect dropdown or https://pub.dev/packages/multi_select_flutter would be useful to have in flet.

signup2k commented 5 months ago

@Xynonners @FeodorFitsner I used some controls in flet, created a demo of multi-selector. It is far from perfect, though, work as a workaround.

import flet as ft

def create_multi_selector(page: ft.Page, options: list = []):
    all_items = options
    selected_items = []

    # Search bar
    search_bar = ft.Row(
        [
            ft.TextField(label="Search", expand=False,
                         on_change=lambda e: update_filtered_items(e.control.value),
                         on_focus=lambda e: set_focus_state(True, e.control.value),
                         icon=ft.icons.SEARCH),
            ft.ElevatedButton("Submit", on_click=lambda e: [print(selected_items), set_focus_state(False, '')]),
        ])

    # Container for filtered items
    filtered_items = ft.Column(scroll="auto", height=200, visible=False)  # Set a fixed height and enable scrolling

    # Container for chips
    chips = ft.Row(wrap=True)

    def set_focus_state(focused, query):
        if focused:
            if query != '':
                update_filtered_items(query)
            filtered_items.visible = True
        else:
            filtered_items.visible = False
        filtered_items.update()

    def update_filtered_items(search_query):
        matched_items = [item for item in all_items if search_query.lower() in item.lower()][:30]  # Limit to 30 items
        filtered_items.controls = [create_item_control(item) for item in matched_items]
        filtered_items.update()

    def create_item_control(item):
        is_selected = item in selected_items
        item_control = ft.Checkbox(
            label=item,
            value=is_selected,
            on_change=lambda e, item=item: toggle_item(e, item),
        )
        return item_control

    def toggle_item(event, item):
        if event.control.value:
            if item not in selected_items:
                selected_items.append(item)
        else:
            if item in selected_items:
                selected_items.remove(item)
        update_chips()

    def update_chips():
        chip_controls = [ft.Chip(label=ft.Text(item), on_click=lambda e, item=item: remove_chip(item)) for item in selected_items]
        if selected_items:
            chip_controls.append(ft.ElevatedButton("Clear All", on_click=lambda e: clear_all()))
        chips.controls = chip_controls
        chips.update()

    def remove_chip(item):
        if item in selected_items:
            selected_items.remove(item)
        update_filtered_items(search_bar.controls[0].value)
        update_chips()

    def clear_all():
        selected_items.clear()
        update_filtered_items(search_bar.controls[0].value)
        update_chips()

    # Create the control first
    control = ft.Column([
        search_bar,
        chips,
        filtered_items
    ])

    # Add to page first
    page.add(control)
    # Initialize filtered items
    update_filtered_items("")

def main(page: ft.Page):
    page.title = "Multi-Selector in Flet"
    page.theme_mode = "dark"
    page.vertical_alignment = "top"
    # Data source
    options = [f'value {i}' for i in range(100)]

    # Create and add multi-selector to the page
    create_multi_selector(page, options)

ft.app(target=main)

recording