flet
flet copied to clipboard
Client storage requests do not work on web (static)
Duplicate Check
- [X] I have searched the opened issues and there are no duplicates
Describe the bug
Attempting to access any of the client storage requests on web clients results in a TimeoutError (see log). I do not think this is the real problem, but I have not been able to find out more details. The example below tries to access it in the main method, but the problem occurs when used anywhere else.
It's not browser specific, it happens in Firefox/Chrome on Debian as well as in Safari on iOS ( also when used as a PWA).
The same code works fine when using flet run or even when using flet run --web. The Linux builds also just work, the others I have not tried yet. (Edit: Android build works flawless as well)
Code sample
Code
import flet as ft
def main(page: ft.Page):
text = ft.Text("Test")
page.add(text)
res = page.client_storage.contains_key("key")
if res == False:
text.value = "Key not found"
page.update()
ft.app(main)
To reproduce
- Build code sample with
flet build weborflet publish - Run the result on a local or external server
- Access the URL in a browser
Expected behavior
No error in log and the displayed text should become "Key not found".
Screenshots / Videos
Captures
[Upload media here]
Operating System
Linux
Operating system details
Debian sid
Flet version
0.23.2
Regression
I'm not sure / I don't know
Suggestions
No response
Logs
Logs
From browser console:
Loading from existing service worker. [flutter.js:3:660](http://localhost:8000/flutter.js)
Service worker already active. [flutter.js:3:908](http://localhost:8000/flutter.js)
Injecting <script> tag. Using callback. [flutter.js:1:1458](http://localhost:8000/flutter.js)
Source-Map-Fehler: Error: request failed with status 404
Ressourcen-Adresse: http://localhost:8000/flutter.js
Source-Map-Adresse: flutter.js.map
WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. 2 [canvaskit.js:14:383](https://www.gstatic.com/flutter-canvaskit/edd8546116457bdf1c5bdfb13ecb9463d2bb5ed4/canvaskit.js)
TypeError: flet_js.send is not a function
[python-worker.js:39:17](http://localhost:8000/python-worker.js)
TypeError: flet_js.send is not a function
[python-worker.js:39:17](http://localhost:8000/python-worker.js)
Python engine initialized! [python.js:23:13](http://localhost:8000/python.js)
Unhandled error processing page session : Traceback (most recent call last): [pyodide.asm.js:9:98748](https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js)
File "/tmp/serious_python_tempELXFMW/__pypackages__/flet/flet.py", line 71, in on_session_created [pyodide.asm.js:9:98748](https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js)
File "/tmp/serious_python_tempELXFMW/main.py", line 7, in main [pyodide.asm.js:9:98748](https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js)
File "/tmp/serious_python_tempELXFMW/__pypackages__/flet_core/client_storage.py", line 46, in contains_key [pyodide.asm.js:9:98748](https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js)
File "/tmp/serious_python_tempELXFMW/__pypackages__/flet_core/page.py", line 1342, in _invoke_method [pyodide.asm.js:9:98748](https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js)
TimeoutError: Timeout waiting for invokeMethod clientStorage:containskey({'key': 'key'}) call [pyodide.asm.js:9:98748](https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js)
<empty string>
Additional details
Edit: There are the similar issues #2948 and #3613 but they seem to happen in different circumstances (i.e. not only failing on web).
Unfortunately, I still have the problem (flet 0.24.1) , but I would like to add an small additional discovery:
When using page.client_storage.set("key", "value") the key does actually get set in local storage (can be found out with the browser devs tools) but the function call nevertheless fails with a "Timeout Error". Also there are no changes when trying get or contains_key after "successfully" (well or howerever I should call it) setting the key, still a "TimeoutError" and no value returned.
But even more interesting when doing this and enabling the debug logs there correct value is actually printed there:
[...]
TimeoutError: Timeout waiting for invokeMethod clientStorage:get({'key': 'testkey'}) call pyodide.asm.js:9:115597
<empty string>
DEBUG:flet_core:_process_command: error ["There was an error while processing your request: Timeout waiting for invokeMethod clientStorage:get({'key': 'testkey'}) call"] {} pyodide.asm.js:9:115701
DEBUG:flet:__send: {"action":"sessionCrashed","payload":{"message":"There was an error while processing your request: Timeout waiting for invokeMethod clientStorage:get({'key': 'testkey'}) call"}} pyodide.asm.js:9:115701
DEBUG:flet:Sending data from JavaScript to Python: {"action":"pageEventFromWeb","payload":{"eventTarget":"page","eventName":"invoke_method_result","eventData":"{\"method_id\":\"4864b0e26aec4c54b751497fdf9e9094\",\"result\":\"\\\"\\\\\\\"correct_value\\\\\\\"\\\"\",\"error\":null}"}} pyodide.asm.js:9:115701
DEBUG:flet:_on_message: {"action":"pageEventFromWeb","payload":{"eventTarget":"page","eventName":"invoke_method_result","eventData":"{\"method_id\":\"4864b0e26aec4c54b751497fdf9e9094\",\"result\":\"\\\"\\\\\\\"correct_value\\\\\\\"\\\"\",\"error\":null}"}} pyodide.asm.js:9:115701
DEBUG:flet_core:page.on_event_async: page invoke_method_result {"method_id":"4864b0e26aec4c54b751497fdf9e9094","result":"\"\\\"correct_value\\\"\"","error":null} pyodide.asm.js:9:115701
DEBUG:flet:Sending data from JavaScript to Python: {"action":"pageEventFromWeb","payload":{"eventTarget":"page","eventName":"app_lifecycle_state_change","eventData":"inactive"}} pyodide.asm.js:9:115701
DEBUG:flet:_on_message: {"action":"pageEventFromWeb","payload":{"eventTarget":"page","eventName":"app_lifecycle_state_change","eventData":"inactive"}} pyodide.asm.js:9:115701
So flet seems to be able to get the data from the browser but something fails after that.
For everyone else having the problem as well I'll take the liberty of quoting a workaround from Discord (thanks to @k3nz0):
I have the same issue using client_storage methods like described here https://github.com/flet-dev/flet/issues/3783 ? Can you help me with that? When i call set i see my data is stored into local storage but my code throws timeout error. The only way i made it work calling lower level protected methods
token = await page._invoke_method_async( # noqa: WPS437 method_name="clientStorage:get", arguments={"key": "mock.access_token"}, wait_timeout=10, wait_for_result=True, )
or
await self.page._invoke_method_async( # noqa: WPS437 "clientStorage:set", {"key": "mock.access_token", "value": self.user_client.auth_token}, wait_timeout=10, )
If it is ok for you that setting a key takes a while you can just keep using page.client_storage.set("key", "value") for that and catch the exception while using the workaround for getting the values.
It's not a perfect solution, but it works for now.
I encountered the same issue, so I followed your solution:
# I replaced the following:
page.client_storage.set(f"{PREFIX}user-settings", user_settings)
# With your command:
await page._invoke_method_async( # noqa: WPS437
"clientStorage:set",
{"key": f"{PREFIX}user-settings", "value": user_settings},
wait_timeout=10,
)
When calling the function, I handled the exception, which solved the error. However, I noticed that nothing is saved in the browser's local storage. Back to square one. I've decided not to use page.client_storage until the Flet dev guys sort this out. But what intrigues me is that on simple pages without views, only with main.py in the test below, Client Storage works perfectly without error, in synchronous mode without having to be asynchronous.
import flet as ft
import uuid
"""
Test: Armazenar informações no lado do cliente (navegador)
usando o client_storage. do Flet.
"""
def main(page: ft.Page):
page.title = "Gerar e Armazenar ID Único"
# Chave para armazenar o token no client_storage
STORAGE_KEY = "estoque.rapido.user_session"
# Função para gerar um novo ID e armazená-lo
def generate_id(e):
# Gera um UUID único
unique_id = str(uuid.uuid4())
# Armazena no client_storage (persiste no navegador)
page.client_storage.set(STORAGE_KEY, unique_id)
# Atualiza a interface com o ID gerado
page.add(ft.Text(f"ID Gerado: {unique_id}"))
page.update()
# Função para recuperar o ID armazenado
def retrieve_id(e):
sessionid_text.value = f"ID da Sessão: {page.session_id}"
# Recupera o ID do client_storage
stored_id = page.client_storage.get(STORAGE_KEY)
if stored_id:
page.add(ft.Text(f"ID Armazenado: {stored_id}"))
else:
page.add(ft.Text("Nenhum ID encontrado. Gere um novo ID."))
page.update()
# Interface com botões para gerar e recuperar o ID
sessionid_text = ft.Text(f"ID da Sessão: {page.session_id}")
page.add(
sessionid_text,
ft.ElevatedButton("Gerar ID", on_click=generate_id),
ft.ElevatedButton("Recuperar ID", on_click=retrieve_id),
)
# Executa a aplicação como um aplicativo web
ft.app(target=main, view=ft.AppView.WEB_BROWSER)
# Executa o aplicativo
# ft.app(target=main, view=ft.WEB_BROWSER)
Over time, I realized that a simple example works because it operates synchronously, without being inside an asynchronous function. However, when used in asynchronous functions, a timeout occurs because access to client_storage in web and mobile environments depends on IndexedDB, which sometimes exceeds the standard 1-second limit. Therefore, we should not call this method within an asynchronous function, as there is an incompatibility.
When I encountered the problem the first time I experimented quit a bit around and if I remember correctly I also tried to set time limits up to quit large values and it did not fix anything for me. It just took longer for the error to appear. But I'm not completely sure anymore what I tried and what not.
I'm not sure why the workaround is not working for you, I'm actively using the page._invoke_method_async and the values are getting stored by the browser.
To work in web browser environment (Pyodide) you should use _async methods. See here: https://github.com/flet-dev/flet/issues/5264#issuecomment-2856846040
The code that works on all platforms:
import flet as ft
async def main(page: ft.Page):
text = ft.Text("Test")
page.add(text)
res = await page.client_storage.contains_key_async("key")
if res == False:
text.value = "Key not found"
page.update()
ft.app(main)