Skip to content

BogdanovychA/flet-storage

Repository files navigation

Flet Storage

Made in Ukraine

DOI PyPI Downloads PyPI - License PyPI - Version

🌐 Translations: 🇺🇦 Українська

A lightweight, asynchronous, namespaced storage utility for Flet applications.

FletStorage is a powerful wrapper around Flet's built-in SharedPreferences. It simplifies client-side data persistence by adding automatic JSON serialization, allowing you to store and retrieve complex Python objects without manual conversion, while keeping your data organized and isolated.

Features

  • Automatic JSON serialization: Store and retrieve dict, list, int, bool, str, and set directly without manual conversion.
  • Namespaced storage: Automatically prefixes keys with app_name to prevent data collisions between different applications on the same device.
  • Asynchronous and parallel: Fully asynchronous API with parallel deletion in the clear() method for maximum performance.
  • Robust error handling: Clear KeyError and ValueError exceptions for predictable data management.
  • Set support: Python sets are automatically preserved during serialization and deserialization.

Installation

pip install flet-storage

Quick Start

import flet as ft
from flet_storage import FletStorage


async def main(page: ft.Page):
    # Initialize storage with a unique namespace
    storage = FletStorage("my_app")

    # Save data
    await storage.set("user", {"name": "Ivan", "age": 25})
    await storage.set("settings", {"theme": "dark", "language": "en"})

    # Retrieve data
    user = await storage.get("user")
    print(user)  # {'name': 'Ivan', 'age': 25}

    # Get with default value
    config = await storage.get_or_default("config", {"version": "1.0"})
    print(config)  # {'version': '1.0'}

    # Check if key exists
    exists = await storage.contains_key("user")
    print(exists)  # True

    # Get all keys
    keys = await storage.get_keys()
    print(keys)  # ['user', 'settings']

    # Remove a key
    await storage.remove("settings")

    # Clear entire storage
    await storage.clear()

ft.run(main)

Supported Data Types

FletStorage automatically serializes and deserializes the following Python types:

Type Description Example
dict Dictionaries {"key": "value"}
list Lists [1, 2, 3]
set Sets (preserved) {"a", "b", "c"}
str Strings "hello"
int, float Numbers 42, 3.14
bool Booleans True, False
None None value None

Working with Sets

Sets are automatically preserved during storage and retrieval:

# Storing a set
tags = {"python", "flet", "async"}
await storage.set("tags", tags)

# Retrieving (returns a set, not a list!)
tags = await storage.get("tags")
print(type(tags))  # <class 'set'> ✅
print(tags)  # {'python', 'flet', 'async'}

# Sets in nested structures work too
data = {
    "user": "Ivan",
    "tags": {"web", "mobile"},
    "categories": ["tech", "programming"]
}
await storage.set("profile", data)
profile = await storage.get("profile")
# profile["tags"] is a set ✅
# profile["categories"] is a list ✅

Technical Note: Sets are stored internally as {"__type__": "set", "values": [...]}. If you need to store a dictionary with a "__type__" key that equals "set", it may be incorrectly interpreted as a set marker during deserialization.

API Reference

__init__(app_name: str)

Initializes the storage with a unique namespace.

Parameters:

  • app_name (str): The unique namespace for the application.

Example:

storage = FletStorage("todo_app")

async set(key: str, obj: object) -> bool

Serializes an object to JSON and stores it under a namespaced key.

Parameters:

  • key (str): The unique key identifier (without namespace).
  • obj (object): Any JSON-serializable object (dict, list, set, str, int, etc.).

Returns:

  • bool: True if the operation was successful.

Example:

await storage.set("preferences", {"notifications": True})
await storage.set("tags", {"python", "flet"})  # Sets are supported!

async get(key: str) -> Any

Retrieves and deserializes an object by its key.

Parameters:

  • key (str): The key identifier to look up.

Returns:

  • Any: The deserialized Python object.

Raises:

  • KeyError: If the key does not exist in the storage.
  • ValueError: If the stored data is not valid JSON.

Example:

try:
    data = await storage.get("preferences")
except KeyError:
    print("Key not found")

async get_or_default(key: str, default: Any = None) -> Any

Gets the value for the given key or returns a default value if not found.

Parameters:

  • key (str): The key identifier to look up.
  • default (Any): The value to return if the key does not exist. Defaults to None.

Returns:

  • Any: The deserialized object if found, otherwise the default value.

Raises:

  • ValueError: If the stored data is not valid JSON.

Example:

config = await storage.get_or_default("config", {"version": "1.0"})

async contains_key(key: str) -> bool

Checks if a specific key exists within the application namespace.

Parameters:

  • key (str): The key identifier to check.

Returns:

  • bool: True if the key exists, False otherwise.

Example:

if await storage.contains_key("user_token"):
    token = await storage.get("user_token")

async remove(key: str) -> bool

Removes a specific key and its value from the storage.

Parameters:

  • key (str): The key identifier to remove.

Returns:

  • bool: True if the operation was successful.

Example:

await storage.remove("temp_data")

async get_keys() -> list[str]

Retrieves all keys belonging to the current application namespace.

Returns:

  • list[str]: A list of keys with the app_name. prefix removed.

Example:

all_keys = await storage.get_keys()
print(f"Stored {len(all_keys)} keys")

async clear() -> None

Deletes all keys and values associated with the current application namespace. Other namespaces in SharedPreferences remain untouched.

Example:

await storage.clear()  # Removes all app data

Usage Examples

Saving User Settings

async def save_user_settings(storage: FletStorage, settings: dict):
    await storage.set("settings", settings)
    print("Settings saved")

async def load_user_settings(storage: FletStorage) -> dict:
    return await storage.get_or_default("settings", {
        "theme": "light",
        "language": "en",
        "notifications": True
    })

Managing a Todo List

async def add_todo(storage: FletStorage, task: str):
    todos = await storage.get_or_default("todos", [])
    todos.append({"task": task, "completed": False})
    await storage.set("todos", todos)

async def get_all_todos(storage: FletStorage) -> list:
    return await storage.get_or_default("todos", [])

async def clear_completed_todos(storage: FletStorage):
    todos = await storage.get_or_default("todos", [])
    active_todos = [t for t in todos if not t["completed"]]
    await storage.set("todos", active_todos)

Working with Tags (Sets)

async def add_tag(storage: FletStorage, tag: str):
    tags = await storage.get_or_default("tags", set())
    tags.add(tag)
    await storage.set("tags", tags)

async def remove_tag(storage: FletStorage, tag: str):
    tags = await storage.get_or_default("tags", set())
    tags.discard(tag)
    await storage.set("tags", tags)

async def get_all_tags(storage: FletStorage) -> set:
    return await storage.get_or_default("tags", set())

Data Caching

import time

async def cache_data(storage: FletStorage, key: str, data: Any, ttl: int = 3600):
    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:
    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

Error Handling

async def safe_get_data(storage: FletStorage, key: str):
    try:
        data = await storage.get(key)
        return data
    except KeyError:
        print(f"Key '{key}' not found")
        return None
    except ValueError as e:
        print(f"JSON error: {e}")
        return None

Best Practices

  1. Use descriptive namespace names: Choose a unique app_name to avoid conflicts with other applications.

  2. Handle exceptions: Always handle KeyError and ValueError when working with get().

  3. Use get_or_default(): For optional data, this is more convenient than exception handling.

  4. Structure your data: Store related data together in dictionaries for better organization.

  5. Clean up stale data: Regularly remove unnecessary keys to keep storage clean.

  6. Use sets for unique collections: Sets are automatically preserved and are perfect for storing unique items like tags or categories.

AI Agent Skill

This repository contains a specialised skill for AI agents. It helps the agent provide expert assistance in writing Python code and integrating this library into your projects.

To install the skill, run:

npx skills add https://github.com/BogdanovychA/flet-storage --skill flet-storage

License

MIT License

Contributing

Contributions are welcome! Please submit pull requests on GitHub.

Support

If you encounter any issues or have questions:


Made with ❤️ for the Flet community

About

A lightweight utility for simplified client-side storage management in Flet applications.

Topics

Resources

License

Stars

9 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages