| name | flet-storage |
| description | Provides instructions and examples for using the flet-storage package, an asynchronous wrapper around Flet's SharedPreferences for persistent data management. Make sure to activate this skill whenever the user mentions saving user preferences, configuration, session tokens, cached data, localStorage, or local state in Flet, or if they encounter JSON serialization errors (especially with Python sets) when persisting Flet data. |
| metadata | {"version":"1.0.6","repository":"https://github.com/BogdanovychA/flet-storage"} |
Flet Storage Skill
This skill provides context, examples, and best practices for integrating and working with the flet-storage package in Python Flet applications.
What is flet-storage?
flet-storage is an asynchronous Python library built on top of Flet's SharedPreferences. It simplifies data persistence by providing:
- Automatic JSON Serialization: Transparently handles Python data types like
dict, list, set, str, int, float, bool, and None. Sets are automatically preserved during serialization and deserialization.
- Namespaced Storage: Automatically prefixes keys with an
app_name to prevent data collisions between different Flet applications on the same device or web domain.
- Asynchronous API: Offers non-blocking data operations for modern async Flet apps.
- Robust API methods: Includes
get_or_default(), contains_key(), and safe parallelized clear() operations.
How to Guide the User
1. Installation
If the user hasn't installed it, suggest installing the package via pip:
pip install flet-storage
2. Basic Setup and Usage Example
Provide this typical usage pattern when users ask how to initialize and use the library:
import flet as ft
from flet_storage import FletStorage
async def main(page: ft.Page):
storage = FletStorage(app_name="my_awesome_app")
await storage.set("user_settings", {"theme": "dark", "notifications": True})
await storage.set("login_attempts", 3)
await storage.set("favorite_tags", {"python", "flet", "async"})
tags = await storage.get("favorite_tags")
settings = await storage.get("user_settings")
attempts = await storage.get_or_default("login_attempts", 0)
if await storage.contains_key("user_settings"):
print("Settings found!")
await storage.remove("login_attempts")
await storage.clear()
page.add(ft.Text(f"Settings loaded: {settings}"))
page.add(ft.Text(f"Tags loaded (type: {type(tags).__name__}): {tags}"))
ft.run(main)
3. Key Methods to Remember
await storage.set(key: str, value: Any): Serializes and saves a value. Supports set directly.
await storage.get(key: str): Retrieves and deserializes a value. Reconstructs set objects automatically. Raises KeyError if the key does not exist.
await storage.get_or_default(key: str, default: Any): Retrieves a value or returns the provided default if the key is missing.
await storage.contains_key(key: str): Returns True if the key exists, otherwise False.
await storage.remove(key: str): Deletes the specific key from storage.
await storage.get_keys(): Retrieves a list of all keys belonging to the current application namespace.
await storage.clear(): Clears all stored data that belongs to the initialized app_name namespace.
4. Storage Limitations
Important to know when designing your app:
- Web (localStorage): ~5-10MB typical limit
- Desktop/Mobile: More generous, but still intended for configuration and small datasets
- Use case: Perfect for user preferences, auth tokens, small lists, UI state, cached data
- Not suitable for: Large datasets (>1-5MB), media files, database-like operations
For large datasets or complex queries, recommend using SQLite (sqlite3 module) or backend APIs instead.
5. Error Handling
Guide users on proper exception handling:
try:
user_data = await storage.get("user")
except KeyError:
print("User data not found, using defaults")
user_data = {"name": "Guest"}
try:
settings = await storage.get("settings")
except ValueError as e:
print(f"Corrupted data: {e}")
await storage.remove("settings")
user_data = await storage.get_or_default("user", {"name": "Guest"})
6. Working with Sets (Advanced Example)
async def manage_tags(storage: FletStorage):
tags = await storage.get_or_default("tags", set())
tags.add("python")
tags.add("flet")
await storage.set("tags", tags)
tags.discard("python")
await storage.set("tags", tags)
if "flet" in tags:
print("Flet tag exists!")
return tags
Important: Sets are stored internally as {"__type__": "set", "values": [...]}. If you store a dict with key "__type__" equal to "set", it may be misinterpreted during deserialization.
7. Data Caching Pattern
Show users how to implement time-based cache expiration:
import time
async def cache_data(storage: FletStorage, key: str, data: Any, ttl: int = 3600):
"""Cache data with time-to-live in seconds."""
cache_entry = {
"data": data,
"expires_at": time.time() + ttl
}
await storage.set(f"cache_{key}", cache_entry)
async def get_cached_data(storage: FletStorage, key: str) -> Any | None:
"""Retrieve cached data if not expired."""
try:
cache_entry = await storage.get(f"cache_{key}")
if time.time() < cache_entry["expires_at"]:
return cache_entry["data"]
else:
await storage.remove(f"cache_{key}")
return None
except KeyError:
return None
8. Cross-Platform Compatibility
flet-storage works across all Flet platforms:
- ✅ Web: Uses browser localStorage
- ✅ Windows/macOS/Linux: Uses platform-specific preferences storage
- ✅ Android/iOS: Uses native storage APIs
Tested platforms:
- Android (production apps)
- Web applications
- Linux (compiled binaries)
- Windows (development environment)
Note: iOS/macOS community testing welcome - underlying Flet implementation should work seamlessly.
Best Practices and Caveats
-
Always Use Namespaces: Strongly recommend initializing FletStorage with a unique app_name. Web browsers and some desktop environments share local storage per domain/user, and omitting a namespace can lead to apps overwriting each other's data.
-
Always Use ft.run() to Launch the App: Modern Flet uses ft.run(main) — never ft.app(target=main), which is deprecated. All generated code examples must use ft.run().
-
Async Environment: Remind the user that flet-storage functions are async. They must be awaited, and the Flet main function (or event handlers using it) must be asynchronous (async def).
-
Security: Stored data (where flet-storage saves information) is not encrypted. Warn the user against storing sensitive data like clear-text passwords or high-privilege API keys unless they manually encrypt it first.
-
Use get_or_default() for Optional Data: Instead of try-except blocks, prefer get_or_default() for cleaner code when dealing with optional configuration.
-
Regular Cleanup: For apps with temporary data (caches, session data), implement periodic cleanup to prevent storage bloat:
async def cleanup_old_cache(storage: FletStorage):
keys = await storage.get_keys()
for key in keys:
if key.startswith("cache_"):
await storage.remove(key)
-
Structure Your Data: Store related data together in dictionaries for better organization and fewer storage operations.
-
Use Sets for Unique Collections: Sets are automatically preserved and are perfect for storing unique items like tags, favorites, or categories.
Common Pitfalls to Avoid
- Forgetting to await:
storage.set("key", "value")
await storage.set("key", "value")
- Not using async function:
def save_data(storage):
await storage.set("key", "value")
async def save_data(storage):
await storage.set("key", "value")
- Storing too much data:
await storage.set("all_users", huge_list_of_10000_users)
await storage.set("cached_recent_users", recent_10_users)
- Not handling exceptions:
user = await storage.get("user")
user = await storage.get_or_default("user", {"name": "Guest"})
try:
user = await storage.get("user")
except KeyError:
user = {"name": "Guest"}
- Using deprecated
ft.app() to launch the app:
ft.app(target=main)
ft.run(main)
Data Migration Pattern
When updating app structure, show how to safely migrate data:
async def migrate_storage(storage: FletStorage):
"""Migrate storage schema from v1 to v2."""
version = await storage.get_or_default("schema_version", 1)
if version == 1:
old_settings = await storage.get_or_default("settings", {})
new_settings = {
"ui": {
"theme": old_settings.get("theme", "light"),
"language": old_settings.get("language", "en")
},
"notifications": old_settings.get("notifications", True),
"version": 2
}
await storage.set("settings", new_settings)
await storage.set("schema_version", 2)
print("Migrated to schema v2")
How to Work with This Skill
- Auto-Update Check: ALWAYS run
scripts/check_update.py before providing assistance.
- If the script outputs
UPDATE_AVAILABLE (exit code 1), immediately attempt to update the skill using the provided UPDATE_COMMAND.
- If the update succeeds, re-read the updated
SKILL.md and continue.
- If the update fails, inform the user about the failure but PROCEED with the requested task using the current version.
- Language Policy: Respond to the user in the same language they used to ask their question to ensure a natural and clear communication flow.
Important Notes
- Requires Python >= 3.10.
- Requires Flet >= 0.80.0.