Skip to content

Implement optional use of per-page cache #39

@Daxexs

Description

@Daxexs
  • Documentation

The use of cache is very useful for applications where changing pages does not change the processes that are being carried out, that is, if you return to the page the controls remain running in the background without stopping when changing pages (route). An example would be the use of a counter when initializing and moving to another page (route) and returning to the counter page, the counter would have increased its value without stopping the process.

Features:

  • When returning to the same page the View is not reloaded, the controls remain as they were, with the same processes carried out.

  • It is not necessary to use the page_run_task method to run processes, but it is recommended.

How to use?

To activate the use of the page cache, you only need to activate it from the cache parameter, which by default is cache=False

example:

import asyncio
import random
import flet as ft
import flet_easy as fs

app = fs.FletEasy()


# remove animation on route change
@app.config
def config(page: ft.Page):
    page.theme = ft.Theme(
        page_transitions=ft.PageTransitionsTheme(
            windows=ft.PageTransitionTheme.NONE,
            android=ft.PageTransitionTheme.NONE,
            ios=ft.PageTransitionTheme.NONE,
            macos=ft.PageTransitionTheme.NONE,
            linux=ft.PageTransitionTheme.NONE,
        ),
    )


# use in 'data.view'
@app.view
def config_view(data: fs.Datasy):
    def change_color(e: ft.ControlEvent):
        colors = [ft.Colors.RED, ft.Colors.GREEN, ft.Colors.BLUE, ft.Colors.YELLOW]
        appbar.bgcolor = random.choice(colors)
        data.page.update()

    appbar = ft.AppBar(
        bgcolor=ft.Colors.BLACK45,
        actions=[
            ft.IconButton(
                icon=ft.Icons.RESTART_ALT_ROUNDED,
                icon_color=ft.Colors.WHITE,
                on_click=change_color,
            )
        ],
    )

    def index_bar_route(e: ft.ControlEvent):
        route = e.control.selected_index

        if route == 0:
            data.go("/")()
        elif route == 1:
            data.go("/test2")()
        elif route == 2:
            data.go("/test3")()

    return ft.View(
        appbar=appbar,
        navigation_bar=ft.NavigationBar(
            destinations=[
                ft.NavigationBarDestination(icon=ft.Icons.CODE, label="counter 1"),
                ft.NavigationBarDestination(icon=ft.Icons.SYNC_DISABLED, label="counter 2"),
                ft.NavigationBarDestination(
                    icon=ft.Icons.CODE,
                    label="counter 3",
                ),
            ],
            on_change=index_bar_route,
        ),
    )


# control custom
class Counter(ft.Container):
    def __init__(self, update, color: str):
        super().__init__()
        self.update = update

        self.number = ft.TextField(value="0", text_size=50, text_align="center")
        self.content = ft.Column(
            controls=[
                self.number,
                ft.FilledButton("start", on_click=self.start, height=50),
            ],
            horizontal_alignment="center",
        )
        self.width = 400
        self.bgcolor = color
        self.border_radius = 10
        self.padding = 20

    async def start(self, e):
        while True:
            self.number.value = str(int(self.number.value) + 1)
            self.update()
            await asyncio.sleep(1)


# add cache to the page
@app.page("/", title="Test 1", cache=True)
def index_page(data: fs.Datasy):
    page = data.page
    data.view.appbar.title = ft.Text("Test 1")

    return ft.View(
        controls=[
            ft.Text("Counter 1", size=50),
            Counter(page.update, ft.Colors.RED),
        ],
        appbar=data.view.appbar,
        navigation_bar=data.view.navigation_bar,
        horizontal_alignment="center",
        vertical_alignment="center",
    )


@app.page("/test2", title="Test 2")
def test_page(data: fs.Datasy):
    page = data.page
    data.view.appbar.title = ft.Text("Test 2")

    return ft.View(
        controls=[
            ft.Text("Counter 2 - (cache disabled)", size=50),
            Counter(page.update, ft.Colors.BLUE),
        ],
        appbar=data.view.appbar,
        navigation_bar=data.view.navigation_bar,
        vertical_alignment="center",
        horizontal_alignment="center",
    )


# add cache to the page
@app.page("/test3", title="Test 3", cache=True)
async def test2_page(data: fs.Datasy):
    page = data.page
    data.view.appbar.title = ft.Text("Test 3")

    return ft.View(
        controls=[
            ft.Text("Counter 3", size=50),
            Counter(page.update, ft.Colors.GREEN),
        ],
        appbar=data.view.appbar,
        navigation_bar=data.view.navigation_bar,
        horizontal_alignment="center",
        vertical_alignment="center",
    )

app.run()

🎬Demo

use-cache-page-1.mp4

data.dynamic_control

This feature is particularly useful when you need to maintain the dynamic state of a control while navigating between different pages. When you enable the cache option on a page, shared controls will keep their state even if they are modified on other pages.

It works perfectly with controls shared through the view decorator of the FletEasy instance, allowing a consistent user experience throughout the application.

data.dynamic_control : Is a method for dynamic UI control updates.

Parameters:

  • control: The UI control to modify.
  • func_update: A function that defines the update, supports async.

Example:

import asyncio
import random
import flet as ft
import flet_easy as fs

app = fs.FletEasy(route_init="/")


# remove animation on route change
@app.config
def config(page: ft.Page):
    page.theme = ft.Theme(
        page_transitions=ft.PageTransitionsTheme(
            windows=ft.PageTransitionTheme.NONE,
            android=ft.PageTransitionTheme.NONE,
            ios=ft.PageTransitionTheme.NONE,
            macos=ft.PageTransitionTheme.NONE,
            linux=ft.PageTransitionTheme.NONE,
        ),
    )

# use in 'data.view'
@app.view
def config_view(data: fs.Datasy):
    def change_color(e: ft.ControlEvent):
        colors = [ft.Colors.RED, ft.Colors.GREEN, ft.Colors.BLUE, ft.Colors.YELLOW]
        appbar.bgcolor = random.choice(colors)
        data.page.update()

    appbar = ft.AppBar(
        bgcolor=ft.Colors.BLACK45,
        actions=[
            ft.IconButton(
                icon=ft.Icons.RESTART_ALT_ROUNDED,
                icon_color=ft.Colors.WHITE,
                on_click=change_color,
            )
        ],
    )

    return ft.View(
        appbar=appbar,
        navigation_bar=ft.NavigationBar(
            destinations=[
                ft.NavigationBarDestination(icon=ft.Icons.CODE, label="counter 1"),
                ft.NavigationBarDestination(icon=ft.Icons.SYNC_DISABLED, label="counter 2"),
                ft.NavigationBarDestination(
                    icon=ft.Icons.CODE,
                    label="counter 3",
                ),
            ],
            on_change=data.go_navigation_bar,
        ),
    )

# control custom
class Counter(ft.Container):
    def __init__(self, update, color: str):
        super().__init__()
        self.update = update

        self.number = ft.TextField(value="0", text_size=50, text_align="center")
        self.content = ft.Column(
            controls=[
                self.number,
                ft.FilledButton("start", on_click=self.start, height=50),
            ],
            horizontal_alignment="center",
        )
        self.width = 400
        self.bgcolor = color
        self.border_radius = 10
        self.padding = 20

    async def start(self, e):
        while True:
            self.number.value = str(int(self.number.value) + 1)
            self.update()
            await asyncio.sleep(1)

# add cache to the page
@app.page("/", title="Test 1", index=0, cache=True)
def index_page(data: fs.Datasy):
    page = data.page
    appbar = data.view.appbar

    async def update_title(e: ft.AppBar):
        e.title = ft.Text("Test 1")

    data.dynamic_control(appbar, update_title)

    return ft.View(
        controls=[
            ft.Text("Counter 1", size=50),
            Counter(page.update, ft.Colors.RED),
        ],
        appbar=appbar,
        navigation_bar=data.view.navigation_bar,
        horizontal_alignment="center",
        vertical_alignment="center",
    )

@app.page("/test2", title="Test 2", index=1)
def test_page(data: fs.Datasy):
    page = data.page
    appbar = data.view.appbar

    appbar.title = ft.Text("Test 2")

    return ft.View(
        controls=[
            ft.Text("Counter 2 - (cache disabled)", size=50),
            Counter(page.update, ft.Colors.BLUE),
        ],
        appbar=appbar,
        navigation_bar=data.view.navigation_bar,
        vertical_alignment="center",
        horizontal_alignment="center",
    )

# add cache to the page
@app.page("/test3", title="Test 3", index=2, cache=True)
async def test2_page(data: fs.Datasy):
    page = data.page
    appbar = data.view.appbar

    data.dynamic_control(appbar, func_update=lambda e: setattr(e, "title", ft.Text("Test 3")))

    return ft.View(
        controls=[
            ft.Text("Counter 3", size=50),
            Counter(page.update, ft.Colors.GREEN),
        ],
        appbar=appbar,
        navigation_bar=data.view.navigation_bar,
        horizontal_alignment="center",
        vertical_alignment="center",
    )

app.run()

🎬Demo

use-cache-page-2.mp4

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

Status

In review

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions