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, andNone. Sets are automatically preserved during serialization and deserialization. - Namespaced Storage: Automatically prefixes keys with an
app_nameto 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 parallelizedclear()operations.
When to Use This Skill
Activate this skill when the user:
- Asks how to save user preferences, session tokens, or local cache in a Flet app.
- Mentions
flet-storageorSharedPreferencesin Flet. - Encounters issues with JSON serialization when storing data in Flet, especially with Python
sets. - Needs to persist data between app sessions.
- Asks about storing configuration, UI state, or small datasets locally.
Language Policy
CRITICAL: When using this skill, ALWAYS communicate with the user in their preferred language (the language they used to ask the question). If the user asks in Ukrainian, respond in Ukrainian. If they ask in English, respond in English, and so on.
How to Guide the User
1. Installation
If the user hasn't installed it, suggest installing the package via pip:
pip install flet-storage2. 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):
# Initialize storage with an app namespace to avoid key collisions
storage = FletStorage(app_name="my_awesome_app")
# Writing data (automatically serializes dicts, lists, sets, etc.)
await storage.set("user_settings", {"theme": "dark", "notifications": True})
await storage.set("login_attempts", 3)
# Working with Python sets (automatically preserved!)
await storage.set("favorite_tags", {"python", "flet", "async"})
tags = await storage.get("favorite_tags") # Returns a set, not a list
# Reading data
settings = await storage.get("user_settings") # Returns a standard Python dict
attempts = await storage.get_or_default("login_attempts", 0)
# Checking for keys
if await storage.contains_key("user_settings"):
print("Settings found!")
# Deleting a specific key
await storage.remove("login_attempts")
# Clearing all data associated with THIS app_name namespace
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. Supportssetdirectly.await storage.get(key: str): Retrieves and deserializes a value. Reconstructssetobjects automatically. RaisesKeyErrorif the key does not exist.await storage.get_or_default(key: str, default: Any): Retrieves a value or returns the provideddefaultif the key is missing.await storage.contains_key(key: str): ReturnsTrueif the key exists, otherwiseFalse.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 initializedapp_namenamespace.
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:
# Handling missing keys
try:
user_data = await storage.get("user")
except KeyError:
print("User data not found, using defaults")
user_data = {"name": "Guest"}
# Handling corrupted JSON data
try:
settings = await storage.get("settings")
except ValueError as e:
print(f"Corrupted data: {e}")
await storage.remove("settings") # Clean up bad data
# Safe pattern using get_or_default (recommended)
user_data = await storage.get_or_default("user", {"name": "Guest"})6. Working with Sets (Advanced Example)
async def manage_tags(storage: FletStorage):
# Initialize with empty set if not exists
tags = await storage.get_or_default("tags", set())
# Add tags
tags.add("python")
tags.add("flet")
await storage.set("tags", tags)
# Remove tag
tags.discard("python")
await storage.set("tags", tags)
# Check membership
if "flet" in tags:
print("Flet tag exists!")
return tagsImportant: 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 None8. 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
FletStoragewith a uniqueapp_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 usesft.run(main)— neverft.app(target=main), which is deprecated. All generated code examples must useft.run(). - Async Environment: Remind the user that
flet-storagefunctions areasync. They must beawaited, and the Fletmainfunction (or event handlers using it) must be asynchronous (async def). - Security: Stored data (where
flet-storagesaves 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, preferget_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:
# Example: Clean up old cached data
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:
# ❌ Wrong - missing await
storage.set("key", "value")
# ✅ Correct
await storage.set("key", "value")- Not using async function:
# ❌ Wrong - sync function
def save_data(storage):
await storage.set("key", "value") # SyntaxError!
# ✅ Correct
async def save_data(storage):
await storage.set("key", "value")- Storing too much data:
# ❌ Bad - large dataset in storage
await storage.set("all_users", huge_list_of_10000_users)
# ✅ Better - use backend/database for large data
await storage.set("cached_recent_users", recent_10_users)- Not handling exceptions:
# ❌ Risky - will crash if key missing
user = await storage.get("user")
# ✅ Safe - using get_or_default
user = await storage.get_or_default("user", {"name": "Guest"})
# ✅ Also safe - explicit error handling
try:
user = await storage.get("user")
except KeyError:
user = {"name": "Guest"}- Using deprecated
ft.app()to launch the app:
# ❌ Wrong - ft.app() is deprecated and should never be used
ft.app(target=main)
# ✅ Correct - always use ft.run() in modern Flet
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:
# Migrate from v1 to v2
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")Additional Resources
- PyPI: https://pypi.org/project/flet-storage/
- GitHub: https://github.com/BogdanovychA/flet-storage
- Issues: https://github.com/BogdanovychA/flet-storage/issues
- Documentation: Full API reference and examples available in the GitHub README
Important Notes
- Requires Python >= 3.10.
- Requires Flet >= 0.80.0.
- MUST run
scripts/check_update.pybefore providing assistance to ensure version alignment.
License
MIT License