How to update a full layout in Flet

634 Views Asked by At

In the code below, i created a Flet app in which the two text controls should update their number based on the amount of clicks on the button. Why is this not happening?

A little bit of background: this is a simplified version of my actual code in which i generate a rather complex layout (a grid) based on the values of a dictionary, depending on the dictionary this layout has more or less rows so i want to update the whole layout and the values within at once.

I tried numerous things and looked at the docs but i can't find any information on updating a layout.

import flet as ft


class MyWidget(ft.UserControl):
    def __init__(self):
        super().__init__()
        self.data = 0
        self.layout = []

    def build_function(self):
        self.layout = ft.Row(
            [
                ft.Text(f"Button clicked {self.data} time(s)"),
                ft.Text(f"Button clicked {self.data} time(s)"),
            ]
        )
        print(self.layout)

    def button_clicked(self, e):
        self.data += 1
        self.build_function()
        print("data = ", self.data)
        self.update()

    def build(self):
        self.build_function()
        return ft.Column(
            [
                ft.ElevatedButton(
                    "Button with 'click' event", on_click=self.button_clicked
                ),
                self.layout,
            ]
        )


def main(page: ft.Page):
    page.add(MyWidget())


ft.app(target=main)
2

There are 2 best solutions below

0
On

You cannot update the controls that are already been placed on the layout, but you can rebuild them. If you update the ini and build_function like below, it should work:

def __init__(self):
    super().__init__()
    self.data = 0
    self.layout = []
    self.btn_list = []

def build_function(self):
    self.btn_list.clear()
    self.btn_list.append(ft.Text(f"Button clicked {self.data} time(s)"))
    self.btn_list.append(ft.Text(f"Button clicked {self.data} time(s)"))
    self.layout = ft.Row(self.btn_list)
    """
        [
            #ft.Text(f"Button clicked {self.data} time(s)"),
            #ft.Text(f"Button clicked {self.data} time(s)"),
        ]
       
    )
    print(self.layout)
    """
0
On

Directly updating the layout variable in the widget doesn't change the content of the column it is attached in the build function. The code below works, it limits the update to the text box and calling refresh on it.

import flet as ft


class MyWidget(ft.UserControl):
    def __init__(self,root: ft.Page):
        super().__init__()
        self.data = 0
        self.layout = []

    def build_function(self):
        self.layout = ft.Row(
            [
                ft.Text(f"Button clicked {self.data} time(s)"),
                ft.Text(f"Button clicked {self.data} time(s)"),
            ]
        )
        print(self.layout)

    def button_clicked(self, e):
        self.data += 1
        print("data = ", self.data)
        for i in self.layout.controls:
            i.value=f"Button clicked {self.data} time(s)"
            i.update()
        #self.update()

    def build(self):
        self.build_function()
        self.col=ft.Column(
            [
                ft.ElevatedButton(
                    "Button with 'click' event", on_click=self.button_clicked
                ),
                self.layout,
            ]
        )
        return self.col


def main(page: ft.Page):
    page.add(MyWidget(page))


ft.app(target=main)

I haven't looked at efficiency, the simpler optimization to call update on the widget once causes causes an Exception in page.py and using async ( the trace hints at a lock as potential issue) crashes encoding the update because of recursive objects. I

Here is the async code:

import flet as ft


class MyWidget(ft.UserControl):
    def __init__(self):
        super().__init__()
        self.data = 0
        self.layout = []

    def build_function(self):
        self.layout = ft.Row(
            [
                ft.Text(f"Button clicked {self.data} time(s)"),
                ft.Text(f"Button clicked {self.data} time(s)"),
            ]
        )
        print(self.layout)

    async def button_clicked(self, e):
        self.data += 1
        #self.build_function()
        print("data = ", self.data)
        for i in self.layout.controls:
            i.value=f"Button clicked {self.data} time(s)"
            await i.update_async()
        #await self.update_async()

    def build(self):
        self.build_function()
        self.col=ft.Column(
            [
                ft.ElevatedButton(
                    "Button with 'click' event", on_click=self.button_clicked
                ),
                self.layout,
            ]
        )
        return self.col


async def main(page: ft.Page):
    await page.add_async(MyWidget())


ft.app(target=main)