diff --git a/.codex/skills/create-flet-example-projects/SKILL.md b/.codex/skills/create-flet-example-projects/SKILL.md new file mode 100644 index 0000000000..f1c8845bf0 --- /dev/null +++ b/.codex/skills/create-flet-example-projects/SKILL.md @@ -0,0 +1,146 @@ +--- +name: create-flet-example-projects +description: Use when asked to create Flet example projects from flat .py files with main.py and pyproject.toml metadata for Gallery/MCP indexing. +--- + +## When to use + +Use this skill when a user asks to: +- create one control/example folder (for example `examples/controls/chip`) in the project-per-example format +- migrate existing flat examples to the project-per-example format +- normalize a partially converted folder so all examples follow the same structure + +## Goal + +Ensure each runnable example is a standalone project containing: + +- `main.py` +- `pyproject.toml` with Gallery/MCP metadata +- `assets/` (if the example uses local assets) + +## Workflow + +1. Inspect source folder. +- Detect current state per example: + - flat file: `foo.py` + - project folder: `foo/main.py` + - mixed/partial conversion: both styles present or missing metadata files +- Find candidate flat modules: `*.py` in the target folder (exclude helper files such as `__init__.py`). +- Keep existing `media/` unless an example needs local assets copied into its own `assets/`. + +2. Convert or normalize examples. +- For `foo.py`, create `foo/` and move file to `foo/main.py`. +- If `foo/main.py` already exists, keep it and do not recreate/move files. +- If folder exists but `main.py` is missing, repair structure only when there is a clear source file. +- Do not create `foo/__init__.py`; import example modules directly in tests/docs (for example `import examples.controls.foo.bar.main as bar` or `import examples.controls.foo.bar as bar` when using namespace-package imports). +- When a control folder has been fully converted to project-per-example layout, delete the control-level `examples/controls//__init__.py` too. The converted folders should behave like namespace packages, matching prior migrations such as commit `7e65ad566`. + +3. Add `pyproject.toml` for each example project. +- Infer from path and code. +- Create missing `pyproject.toml` files for existing project folders. +- Update obviously stale metadata when migrating existing examples (for example wrong title/description/categories). +- Verify platform support before adding or omitting `[tool.flet].platforms`: + - Check the local implementation and docs for explicit platform guards, support tables, or platform-specific exceptions. + - If support is limited, add `[tool.flet].platforms` with only the supported platforms. + - If support is broad/all-platform, omit `[tool.flet].platforms`. +- Required fields: + - `[project]`: `name`, `version`, `description`, `requires-python`, `keywords`, `authors`, `dependencies` + - `[dependency-groups].dev`: include `flet-cli`, `flet-desktop`, `flet-web` + - `[tool.flet.gallery].categories` + - `[tool.flet.metadata]`: `title`, `controls`, `layout_pattern`, `complexity`, `features` + - `[tool.flet]`: `org`, `company`, `copyright` +- Add `[tool.flet].platforms` only when the example is platform-limited. +- Add permissions blocks only when code actually needs them. + +4. Infer metadata. +- Title: readable version of file/folder intent. +- Short description: one line of what the example demonstrates. +- `[project].description` must be meaningful and example-specific; avoid generic placeholders like "Example N" or " example for ". +- Description should mention the concrete behavior or interaction shown (for example: hover highlight, live updates, custom axes, event handling). +- Categories: typically control-based, e.g. `Input/Chip`, plus optional `Apps/Basic controls`. +- Tags: from control/topic/behavior words. +- Controls used: list key controls from code. +- Layout pattern: choose closest practical value (e.g. `filter-bar`, `inline-actions`, `dashboard`, `list-detail`). +- Complexity: `basic` unless logic/state/architecture is non-trivial. +- Features: notable behaviors only (click handling, selection, async loading, drag-and-drop, etc.). +- If an example supports exporting or downloading output, include `"save to file"` in `[tool.flet.metadata].features`. +- If an example module contains `async def` handlers or async control flow, append `"async"` to `keywords`. + + +5. Infer dependencies from imports. +- Always include `flet` for standard examples. +- Include extra packages if imported (for example extension packages). +- Do not add unused dependencies. + +6. Make examples mobile-safe. +- If `ft.context.disable_auto_update()` is not used, do not add explicit `page.update()` unless strictly necessary. +- Apply this `page.update()` rule to all examples in the touched folder (new, migrated, and already converted). +- Wrap app content in `ft.SafeArea` so example renders correctly on mobile. +- Add `expand=True` to `ft.SafeArea` only when needed for correct layout/sizing (for example to avoid Infinity/NaN sizing issues), and avoid adding it when not necessary. +- When converting legacy `page.add(a, b, ...)` style examples, wrap the controls in `ft.Column(controls=[...])` inside `ft.SafeArea(content=...)` rather than `ft.Row`, unless the original code explicitly used a row layout. + +- Apply this to all examples in the touched folder (new, migrated, and already converted), not only files changed by moves. +- During validation, confirm every `/main.py` in scope includes a top-level `ft.SafeArea` around rendered content. +- For declarative examples using `@ft.component`, do not pass component instances as regular control children (for example `SafeArea(content=App())`) because this can raise runtime attribute errors. +- In declarative examples, ensure the component itself returns regular controls (including `SafeArea` when needed) and render it at page level with `page.render(App)` in `main()`. + +7. Prefer `@ft.control` for custom controls in examples. +- If an example defines a custom control class inheriting from a Flet control (for example `class MyThing(ft.Column)`), prefer `@ft.control` style. +- Move constructor-style setup to declarative fields + `init()` where practical. +- Keep behavior unchanged and avoid refactors that alter public usage unless needed for compatibility. + +8. Remove deprecated Material 3 toggle usage. +- If `use_material3` appears in example code, remove it and simplify the example to current API usage. +- Remove related Material 3 toggle logic/UI that exists only to switch `use_material3`. +- Update example metadata (`pyproject.toml`) to remove stale Material 3 references when code is changed. + +9. Ensure runnable entrypoint. +- Every example `main.py` should end with: + - `if __name__ == "__main__":` + - ` ft.run(main)` +- Apply this to all examples in the touched folder (new, migrated, and already converted). + +10. Update references. +- Docs code includes: change from `.../example.py` to `.../example/main.py`. +- Inspect the relevant docs pages for each touched control/service/example area (for example `sdk/python/packages/flet/docs/controls/.md`) and update any `--8<--` includes or direct file-path references to the new `main.py` path. +- Tests/imports: use direct module imports and avoid relying on package-level `__init__.py` re-exports. +- For already-converted examples, only update references that are stale; avoid unnecessary churn. +- If removing a control-level `__init__.py`, confirm no remaining imports rely on `from examples.controls. import ...`. + +11. Validate. +- Run `python -m compileall` on changed `main.py` files. +- Run `uv run ruff check` on changed example files and fix violations until it passes (respecting repository `pyproject.toml` under `[tool.ruff]`). +- Search for stale paths to old flat files. +- Search docs and package sources for stale references to the migrated flat example paths and fix any hits in scope. +- Check `git status` to confirm expected moves and edits. +- When integration tests exist for the touched control, run the targeted test file(s). +- Confirm all in-scope `main.py` files include both top-level `ft.SafeArea` wrapping and the `if __name__ == "__main__": ft.run(main)` entrypoint. +- Confirm in-scope `ft.SafeArea` wrappers use `expand=True` only where needed for correct behavior and sizing; avoid forcing it by default. +- Confirm there are no unnecessary `page.update()` calls in in-scope examples (unless explicitly required by isolated-control or non-auto-update behavior). +- Confirm no in-scope examples use `use_material3`. +- Confirm each in-scope `pyproject.toml` has a meaningful, example-specific `[project].description` (not generic or templated text). +- Confirm metadata features include `"save to file"` when the example code supports file export/save behavior. +- Confirm there is no stale control-level `__init__.py` left behind once a touched control folder has been fully converted. +- Confirm the relevant docs pages were updated to reference `main.py` and that no stale doc includes remain for the touched examples. + + +## Code style + +- When writing wrapped controls (`SafeArea`, `Column`, `Row`, `Container`, etc.), keep `content=` or `controls=` as the last named argument in that control call. +- Apply this ordering consistently when creating or refactoring examples. +- Follow code style and linting rules defined in the repository `pyproject.toml` under `[tool.ruff]` for all edits. + +## Command checklist + +- Discover files: `rg --files ` +- Find docs links/imports: `rg -n "" packages examples` +- Syntax check: `python -m compileall ` +- Ruff check: `uv run ruff check ` + +## Output expectations + +Report: +- created example projects +- metadata added +- docs/tests updates +- validation results diff --git a/packages/flet/lib/src/controls/semantics.dart b/packages/flet/lib/src/controls/semantics.dart index eb93e20657..941ac0a3a8 100644 --- a/packages/flet/lib/src/controls/semantics.dart +++ b/packages/flet/lib/src/controls/semantics.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import '../extensions/control.dart'; import '../models/control.dart'; import '../utils/numbers.dart'; +import '../widgets/error.dart'; import 'base_controls.dart'; class SemanticsControl extends StatelessWidget { @@ -12,7 +14,10 @@ class SemanticsControl extends StatelessWidget { @override Widget build(BuildContext context) { debugPrint("Semantics build: ${control.id}"); + final content = control.buildWidget("content"); Semantics semantics = Semantics( + child: content ?? + const ErrorControl("Semantics.content must be provided and visible"), label: control.getString("label"), enabled: !control.disabled, expanded: control.getBool("expanded"), diff --git a/sdk/python/examples/apps/authentication/github_check_auth_results_and_toggle_ui.py b/sdk/python/examples/apps/authentication/github_check_auth_results_and_toggle_ui.py deleted file mode 100644 index 0a8e080d12..0000000000 --- a/sdk/python/examples/apps/authentication/github_check_auth_results_and_toggle_ui.py +++ /dev/null @@ -1,51 +0,0 @@ -# -# Run this example with: -# export GITHUB_CLIENT_ID= -# export GITHUB_CLIENT_SECRET= -# flet run --web --port 8550 github_check_auth_results_and_toggle_ui.py -# -import os - -import flet as ft -from flet.auth.providers import GitHubOAuthProvider - - -def get_env_variable(name: str) -> str: - value = os.getenv(name) - assert value, f"set {name} environment variable" - return value - - -def main(page: ft.Page): - provider = GitHubOAuthProvider( - client_id=get_env_variable("GITHUB_CLIENT_ID"), - client_secret=get_env_variable("GITHUB_CLIENT_SECRET"), - redirect_url="http://127.0.0.1:8550/oauth_callback", - ) - - async def login_button_click(e): - await page.login(provider, scope=["public_repo"]) - - def on_login(e: ft.LoginEvent): - if not e.error: - toggle_login_buttons() - - def logout_button_click(e): - page.logout() - - def on_logout(e): - toggle_login_buttons() - - def toggle_login_buttons(): - login_button.visible = page.auth is None - logout_button.visible = page.auth is not None - - login_button = ft.Button("Login with GitHub", on_click=login_button_click) - logout_button = ft.Button("Logout", on_click=logout_button_click) - toggle_login_buttons() - page.on_login = on_login - page.on_logout = on_logout - page.add(login_button, logout_button) - - -ft.run(main) diff --git a/sdk/python/examples/apps/authentication/github_check_auth_results_and_toggle_ui/main.py b/sdk/python/examples/apps/authentication/github_check_auth_results_and_toggle_ui/main.py new file mode 100644 index 0000000000..e522d44aa7 --- /dev/null +++ b/sdk/python/examples/apps/authentication/github_check_auth_results_and_toggle_ui/main.py @@ -0,0 +1,58 @@ +# +# Run this example with: +# export GITHUB_CLIENT_ID= +# export GITHUB_CLIENT_SECRET= +# flet run --web --port 8550 main.py +# +import os + +import flet as ft +from flet.auth.providers import GitHubOAuthProvider + + +def get_env_variable(name: str) -> str: + value = os.getenv(name) + assert value, f"set {name} environment variable" + return value + + +def main(page: ft.Page): + provider = GitHubOAuthProvider( + client_id=get_env_variable("GITHUB_CLIENT_ID"), + client_secret=get_env_variable("GITHUB_CLIENT_SECRET"), + redirect_url="http://127.0.0.1:8550/oauth_callback", + ) + + async def login_button_click(e): + await page.login(provider, scope=["public_repo"]) + + def on_login(e: ft.LoginEvent): + if not e.error: + toggle_login_buttons() + + def logout_button_click(e): + page.logout() + + def on_logout(e): + toggle_login_buttons() + + def toggle_login_buttons(): + login_button.visible = page.auth is None + logout_button.visible = page.auth is not None + + login_button = ft.Button("Login with GitHub", on_click=login_button_click) + logout_button = ft.Button("Logout", on_click=logout_button_click) + toggle_login_buttons() + page.on_login = on_login + page.on_logout = on_logout + page.add( + ft.SafeArea( + content=ft.Column( + controls=[login_button, logout_button], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/authentication/github_check_auth_results_and_toggle_ui/pyproject.toml b/sdk/python/examples/apps/authentication/github_check_auth_results_and_toggle_ui/pyproject.toml new file mode 100644 index 0000000000..3966b375aa --- /dev/null +++ b/sdk/python/examples/apps/authentication/github_check_auth_results_and_toggle_ui/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-authentication-github-check-auth-results-and-toggle-ui" +version = "1.0.0" +description = "Toggles GitHub sign-in and sign-out buttons based on authentication events." +requires-python = ">=3.10" +keywords = ["apps", "authentication", "github", "oauth", "login", "web", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Authentication"] + +[tool.flet.metadata] +title = "GitHub auth button toggle" +controls = ["SafeArea", "Column", "Button", "GitHubOAuthProvider"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["oauth login", "oauth logout", "auth-aware UI"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/authentication/github_login_same_page.py b/sdk/python/examples/apps/authentication/github_login_same_page.py deleted file mode 100644 index 3645ec1d6e..0000000000 --- a/sdk/python/examples/apps/authentication/github_login_same_page.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Run this example with: -# export GITHUB_CLIENT_ID= -# export GITHUB_CLIENT_SECRET= -# flet run --web --port 8550 github_login_same_page.py -# -import os - -import flet as ft -from flet.auth.providers import GitHubOAuthProvider - - -def get_env_variable(name: str) -> str: - value = os.getenv(name) - assert value, f"set {name} environment variable" - return value - - -def main(page: ft.Page): - provider = GitHubOAuthProvider( - client_id=get_env_variable("GITHUB_CLIENT_ID"), - client_secret=get_env_variable("GITHUB_CLIENT_SECRET"), - redirect_url="http://127.0.0.1:8550/oauth_callback", - ) - - async def login_click(e): - await page.login( - provider, - redirect_to_page=True, - on_open_authorization_url=lambda url: ft.UrlLauncher().launch_url( - ft.Url(url, target=ft.UrlTarget.SELF) - ), - ) - - async def on_login(e): - if e.error: - page.add(ft.Text(f"Login error: {e.error}")) - else: - page.add(ft.Text(f"User ID: {page.auth.user.id}")) - - page.on_login = on_login - page.add(ft.Button("Login with GitHub", on_click=login_click)) - - -ft.run(main) diff --git a/sdk/python/examples/apps/authentication/github_login_same_page/main.py b/sdk/python/examples/apps/authentication/github_login_same_page/main.py new file mode 100644 index 0000000000..b7602104e6 --- /dev/null +++ b/sdk/python/examples/apps/authentication/github_login_same_page/main.py @@ -0,0 +1,52 @@ +# +# Run this example with: +# export GITHUB_CLIENT_ID= +# export GITHUB_CLIENT_SECRET= +# flet run --web --port 8550 main.py +# +import os + +import flet as ft +from flet.auth.providers import GitHubOAuthProvider + + +def get_env_variable(name: str) -> str: + value = os.getenv(name) + assert value, f"set {name} environment variable" + return value + + +def main(page: ft.Page): + provider = GitHubOAuthProvider( + client_id=get_env_variable("GITHUB_CLIENT_ID"), + client_secret=get_env_variable("GITHUB_CLIENT_SECRET"), + redirect_url="http://127.0.0.1:8550/oauth_callback", + ) + + async def login_click(e): + await page.login( + provider, + redirect_to_page=True, + on_open_authorization_url=lambda url: ft.UrlLauncher().launch_url( + ft.Url(url, target=ft.UrlTarget.SELF) + ), + ) + + async def on_login(e): + if e.error: + page.add(ft.Text(f"Login error: {e.error}")) + else: + page.add(ft.Text(f"User ID: {page.auth.user.id}")) + + page.on_login = on_login + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ft.Button("Login with GitHub", on_click=login_click)], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/authentication/github_login_same_page/pyproject.toml b/sdk/python/examples/apps/authentication/github_login_same_page/pyproject.toml new file mode 100644 index 0000000000..0645681806 --- /dev/null +++ b/sdk/python/examples/apps/authentication/github_login_same_page/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "apps-authentication-github-login-same-page" +version = "1.0.0" +description = "Runs GitHub OAuth in the same browser tab and displays the authenticated user ID." +requires-python = ">=3.10" +keywords = ["apps", "authentication", "github", "oauth", "redirect", "web", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Authentication"] + +[tool.flet.metadata] +title = "GitHub login in same page" +controls = ["SafeArea", "Column", "Button", "Text", "GitHubOAuthProvider", "UrlLauncher"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["oauth login", "same-tab redirect"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["web"] diff --git a/sdk/python/examples/apps/authentication/github_minimal.py b/sdk/python/examples/apps/authentication/github_minimal.py deleted file mode 100644 index 058c27cb1e..0000000000 --- a/sdk/python/examples/apps/authentication/github_minimal.py +++ /dev/null @@ -1,43 +0,0 @@ -# -# Run this example with: -# export GITHUB_CLIENT_ID= -# export GITHUB_CLIENT_SECRET= -# flet run --web --port 8550 github_minimal.py -# -import os - -import flet as ft -from flet.auth.providers import GitHubOAuthProvider - - -def get_env_variable(name: str) -> str: - value = os.getenv(name) - assert value, f"set {name} environment variable" - return value - - -def main(page: ft.Page): - provider = GitHubOAuthProvider( - client_id=get_env_variable("GITHUB_CLIENT_ID"), - client_secret=get_env_variable("GITHUB_CLIENT_SECRET"), - redirect_url="http://127.0.0.1:8550/oauth_callback", - ) - - async def login_click(e): - await page.login(provider) - - async def on_login(e): - if e.error: - page.add(ft.Text(f"Login error: {e.error}")) - else: - access_token = (await page.auth.get_token()).access_token - page.add( - ft.Text(f"Access token: {access_token}"), - ft.Text(f"User ID: {page.auth.user.id}"), - ) - - page.on_login = on_login - page.add(ft.Button("Login with GitHub", on_click=login_click)) - - -ft.run(main) diff --git a/sdk/python/examples/apps/authentication/github_minimal/main.py b/sdk/python/examples/apps/authentication/github_minimal/main.py new file mode 100644 index 0000000000..1c08ff2aed --- /dev/null +++ b/sdk/python/examples/apps/authentication/github_minimal/main.py @@ -0,0 +1,50 @@ +# +# Run this example with: +# export GITHUB_CLIENT_ID= +# export GITHUB_CLIENT_SECRET= +# flet run --web --port 8550 main.py +# +import os + +import flet as ft +from flet.auth.providers import GitHubOAuthProvider + + +def get_env_variable(name: str) -> str: + value = os.getenv(name) + assert value, f"set {name} environment variable" + return value + + +def main(page: ft.Page): + provider = GitHubOAuthProvider( + client_id=get_env_variable("GITHUB_CLIENT_ID"), + client_secret=get_env_variable("GITHUB_CLIENT_SECRET"), + redirect_url="http://127.0.0.1:8550/oauth_callback", + ) + + async def login_click(e): + await page.login(provider) + + async def on_login(e): + if e.error: + page.add(ft.Text(f"Login error: {e.error}")) + else: + access_token = (await page.auth.get_token()).access_token + page.add( + ft.Text(f"Access token: {access_token}"), + ft.Text(f"User ID: {page.auth.user.id}"), + ) + + page.on_login = on_login + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ft.Button("Login with GitHub", on_click=login_click)], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/authentication/github_minimal/pyproject.toml b/sdk/python/examples/apps/authentication/github_minimal/pyproject.toml new file mode 100644 index 0000000000..f46f8d54a3 --- /dev/null +++ b/sdk/python/examples/apps/authentication/github_minimal/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-authentication-github-minimal" +version = "1.0.0" +description = "Shows a minimal GitHub OAuth login flow and prints the access token and user ID." +requires-python = ">=3.10" +keywords = ["apps", "authentication", "github", "oauth", "login", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Authentication"] + +[tool.flet.metadata] +title = "Minimal GitHub login" +controls = ["SafeArea", "Column", "Button", "Text", "GitHubOAuthProvider"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["oauth login", "access token display"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/authentication/github_repos_browser.py b/sdk/python/examples/apps/authentication/github_repos_browser.py deleted file mode 100644 index a11ee1a1e4..0000000000 --- a/sdk/python/examples/apps/authentication/github_repos_browser.py +++ /dev/null @@ -1,122 +0,0 @@ -# -# Run this example with: -# export GITHUB_CLIENT_ID= -# export GITHUB_CLIENT_SECRET= -# export MY_APP_SECRET_KEY= -# flet run --web --port 8550 github_repos_browser.py -# -import json -import logging -import os - -import httpx - -import flet as ft -from flet.auth.providers import GitHubOAuthProvider -from flet.security import decrypt, encrypt - -logging.basicConfig(level=logging.INFO) - - -def get_env_variable(name: str) -> str: - value = os.getenv(name) - assert value, f"set {name} environment variable" - return value - - -async def main(page: ft.Page): - # encryption passphrase - secret_key = get_env_variable("MY_APP_SECRET_KEY") - - # configure provider - provider = GitHubOAuthProvider( - client_id=get_env_variable("GITHUB_CLIENT_ID"), - client_secret=get_env_variable("GITHUB_CLIENT_SECRET"), - redirect_url="http://127.0.0.1:8550/oauth_callback", - ) - - # client storage keys - AUTH_TOKEN_KEY = "myapp.auth_token" - - async def perform_login(e): - login_button.disabled = True - login_button.update() - - # perform login - saved_token = None - ejt = await ft.SharedPreferences().get(AUTH_TOKEN_KEY) - if ejt: - saved_token = decrypt(ejt, secret_key) - if e is not None or saved_token is not None: - await page.login( - provider, - redirect_to_page=True, - on_open_authorization_url=lambda url: ft.UrlLauncher().launch_url( - ft.Url(url, target=ft.UrlTarget.SELF) - ), - saved_token=saved_token, - scope=["public_repo"], - ) - - async def on_login(e: ft.LoginEvent): - if e.error: - raise Exception(e.error) - - assert page.auth - - # save token in a client storage - jt = (await page.auth.get_token()).to_json() - ejt = encrypt(jt, secret_key) - await ft.SharedPreferences().set(AUTH_TOKEN_KEY, ejt) - - logged_user.value = f"Hello, {page.auth.user['name']}!" - toggle_login_buttons() - await list_github_repositories() - page.update() - - async def list_github_repositories(): - repos_view.controls.clear() - if page.auth: - headers = { - "User-Agent": "Flet", - "Authorization": f"Bearer {(await page.auth.get_token()).access_token}", - } - async with httpx.AsyncClient() as client: - repos_resp = await client.get( - "https://api.github.com/user/repos", headers=headers - ) - repos_resp.raise_for_status() - user_repos = json.loads(repos_resp.text) - for repo in user_repos: - repos_view.controls.append( - ft.ListTile( - leading=ft.Icon(ft.Icons.FOLDER_ROUNDED), - title=ft.Text(repo["full_name"]), - ) - ) - - async def logout_button_click(e): - await ft.SharedPreferences().remove(AUTH_TOKEN_KEY) - page.logout() - - async def on_logout(e): - toggle_login_buttons() - await list_github_repositories() - - def toggle_login_buttons(): - login_button.visible = page.auth is None - login_button.disabled = False - logged_user.visible = logout_button.visible = page.auth is not None - - logged_user = ft.Text() - login_button = ft.Button("Login with GitHub", on_click=perform_login) - logout_button = ft.Button("Logout", on_click=logout_button_click) - repos_view = ft.ListView(expand=True) - page.on_login = on_login - page.on_logout = on_logout - toggle_login_buttons() - page.add(ft.Row([logged_user, login_button, logout_button]), repos_view) - await perform_login(None) - - -ft.run(main) diff --git a/sdk/python/examples/apps/authentication/github_repos_browser/main.py b/sdk/python/examples/apps/authentication/github_repos_browser/main.py new file mode 100644 index 0000000000..6b5c7a19dc --- /dev/null +++ b/sdk/python/examples/apps/authentication/github_repos_browser/main.py @@ -0,0 +1,130 @@ +# +# Run this example with: +# export GITHUB_CLIENT_ID= +# export GITHUB_CLIENT_SECRET= +# export MY_APP_SECRET_KEY= +# flet run --web --port 8550 main.py +# +import json +import logging +import os + +import httpx + +import flet as ft +from flet.auth.providers import GitHubOAuthProvider +from flet.security import decrypt, encrypt + +logging.basicConfig(level=logging.INFO) + + +def get_env_variable(name: str) -> str: + value = os.getenv(name) + assert value, f"set {name} environment variable" + return value + + +async def main(page: ft.Page): + secret_key = get_env_variable("MY_APP_SECRET_KEY") + + provider = GitHubOAuthProvider( + client_id=get_env_variable("GITHUB_CLIENT_ID"), + client_secret=get_env_variable("GITHUB_CLIENT_SECRET"), + redirect_url="http://127.0.0.1:8550/oauth_callback", + ) + + auth_token_key = "myapp.auth_token" + + async def perform_login(e): + login_button.disabled = True + login_button.update() + + saved_token = None + ejt = await ft.SharedPreferences().get(auth_token_key) + if ejt: + saved_token = decrypt(ejt, secret_key) + if e is not None or saved_token is not None: + await page.login( + provider, + redirect_to_page=True, + on_open_authorization_url=lambda url: ft.UrlLauncher().launch_url( + ft.Url(url, target=ft.UrlTarget.SELF) + ), + saved_token=saved_token, + scope=["public_repo"], + ) + + async def on_login(e: ft.LoginEvent): + if e.error: + raise Exception(e.error) + + assert page.auth + + jt = (await page.auth.get_token()).to_json() + ejt = encrypt(jt, secret_key) + await ft.SharedPreferences().set(auth_token_key, ejt) + + logged_user.value = f"Hello, {page.auth.user['name']}!" + toggle_login_buttons() + await list_github_repositories() + + async def list_github_repositories(): + repos_view.controls.clear() + if page.auth: + headers = { + "User-Agent": "Flet", + "Authorization": f"Bearer {(await page.auth.get_token()).access_token}", + } + async with httpx.AsyncClient() as client: + repos_resp = await client.get( + "https://api.github.com/user/repos", headers=headers + ) + repos_resp.raise_for_status() + user_repos = json.loads(repos_resp.text) + for repo in user_repos: + repos_view.controls.append( + ft.ListTile( + leading=ft.Icon(ft.Icons.FOLDER_ROUNDED), + title=ft.Text(repo["full_name"]), + ) + ) + + async def logout_button_click(e): + await ft.SharedPreferences().remove(auth_token_key) + page.logout() + + async def on_logout(e): + toggle_login_buttons() + await list_github_repositories() + + def toggle_login_buttons(): + login_button.visible = page.auth is None + login_button.disabled = False + logged_user.visible = logout_button.visible = page.auth is not None + + logged_user = ft.Text() + login_button = ft.Button("Login with GitHub", on_click=perform_login) + logout_button = ft.Button("Logout", on_click=logout_button_click) + repos_view = ft.ListView(expand=True) + page.on_login = on_login + page.on_logout = on_logout + toggle_login_buttons() + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.Row( + controls=[logged_user, login_button, logout_button], + ), + repos_view, + ], + ), + ) + ) + await perform_login(None) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/authentication/github_repos_browser/pyproject.toml b/sdk/python/examples/apps/authentication/github_repos_browser/pyproject.toml new file mode 100644 index 0000000000..87bf8f52da --- /dev/null +++ b/sdk/python/examples/apps/authentication/github_repos_browser/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "apps-authentication-github-repos-browser" +version = "1.0.0" +description = "Logs in with GitHub, restores a saved token, and lists the signed-in user's repositories." +requires-python = ">=3.10" +keywords = ["apps", "authentication", "github", "oauth", "repositories", "web", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "httpx"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Authentication"] + +[tool.flet.metadata] +title = "GitHub repos browser" +controls = ["SafeArea", "Column", "Row", "Text", "Button", "ListView", "ListTile", "GitHubOAuthProvider"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["oauth login", "saved token restore", "github api request"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["web"] diff --git a/sdk/python/examples/apps/authentication/linkedin_login.py b/sdk/python/examples/apps/authentication/linkedin_login.py deleted file mode 100644 index b672735508..0000000000 --- a/sdk/python/examples/apps/authentication/linkedin_login.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# Run this example with: -# export LINKEDIN_CLIENT_ID= -# export LINKEDIN_CLIENT_SECRET= -# flet run --web --port 8550 linkedin_login.py -# -import os - -import flet as ft -from flet.auth import OAuthProvider - - -def get_env_variable(name: str) -> str: - value = os.getenv(name) - assert value, f"set {name} environment variable" - return value - - -def main(page: ft.Page): - provider = OAuthProvider( - client_id=get_env_variable("LINKEDIN_CLIENT_ID"), - client_secret=get_env_variable("LINKEDIN_CLIENT_SECRET"), - authorization_endpoint="https://www.linkedin.com/oauth/v2/authorization", - token_endpoint="https://www.linkedin.com/oauth/v2/accessToken", - user_endpoint="https://api.linkedin.com/v2/me", - user_scopes=["r_liteprofile", "r_emailaddress"], - user_id_fn=lambda u: u["id"], - redirect_url="http://127.0.0.1:8550/oauth_callback", - ) - - async def login_click(e): - await page.login(provider) - - async def on_login(e): - if e.error: - page.add(ft.Text(f"Login error: {e.error}")) - else: - access_token = (await page.auth.get_token()).access_token - page.add( - ft.Text(f"Access token: {access_token}"), - ft.Text(f"User ID: {page.auth.user.id}"), - ) - - page.on_login = on_login - page.add(ft.Button("Login with LinkedIn", on_click=login_click)) - - -ft.run(main) diff --git a/sdk/python/examples/apps/authentication/linkedin_login/main.py b/sdk/python/examples/apps/authentication/linkedin_login/main.py new file mode 100644 index 0000000000..5c059b63ce --- /dev/null +++ b/sdk/python/examples/apps/authentication/linkedin_login/main.py @@ -0,0 +1,55 @@ +# +# Run this example with: +# export LINKEDIN_CLIENT_ID= +# export LINKEDIN_CLIENT_SECRET= +# flet run --web --port 8550 main.py +# +import os + +import flet as ft +from flet.auth import OAuthProvider + + +def get_env_variable(name: str) -> str: + value = os.getenv(name) + assert value, f"set {name} environment variable" + return value + + +def main(page: ft.Page): + provider = OAuthProvider( + client_id=get_env_variable("LINKEDIN_CLIENT_ID"), + client_secret=get_env_variable("LINKEDIN_CLIENT_SECRET"), + authorization_endpoint="https://www.linkedin.com/oauth/v2/authorization", + token_endpoint="https://www.linkedin.com/oauth/v2/accessToken", + user_endpoint="https://api.linkedin.com/v2/me", + user_scopes=["r_liteprofile", "r_emailaddress"], + user_id_fn=lambda u: u["id"], + redirect_url="http://127.0.0.1:8550/oauth_callback", + ) + + async def login_click(e): + await page.login(provider) + + async def on_login(e): + if e.error: + page.add(ft.Text(f"Login error: {e.error}")) + else: + access_token = (await page.auth.get_token()).access_token + page.add( + ft.Text(f"Access token: {access_token}"), + ft.Text(f"User ID: {page.auth.user.id}"), + ) + + page.on_login = on_login + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ft.Button("Login with LinkedIn", on_click=login_click)], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/authentication/linkedin_login/pyproject.toml b/sdk/python/examples/apps/authentication/linkedin_login/pyproject.toml new file mode 100644 index 0000000000..aab393c7db --- /dev/null +++ b/sdk/python/examples/apps/authentication/linkedin_login/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-authentication-linkedin-login" +version = "1.0.0" +description = "Authenticates with a custom LinkedIn OAuth provider and displays the token and user ID." +requires-python = ">=3.10" +keywords = ["apps", "authentication", "linkedin", "oauth", "login", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Authentication"] + +[tool.flet.metadata] +title = "LinkedIn login" +controls = ["SafeArea", "Column", "Button", "Text", "OAuthProvider"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["custom oauth provider", "access token display"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/autocomplete_searcher/basic/main.py b/sdk/python/examples/apps/autocomplete_searcher/basic/main.py new file mode 100644 index 0000000000..75fd7dda27 --- /dev/null +++ b/sdk/python/examples/apps/autocomplete_searcher/basic/main.py @@ -0,0 +1,64 @@ +import flet as ft + +NAMES = [ + "Adam", + "William", + "Emma", + "Alexander", + "Julia", + "Elias", + "Hugo", + "Alice", + "Emil", + "Anton", + "Ebba", + "Elin", + "Oliver", + "Axel", + "Maja", + "Ella", + "Alva", + "Liam", + "Albin", + "Elsa", + "Erik", + "Ida", + "Oscar", + "Wilma", +] + + +def main(page: ft.Page): + page.title = "Autocomplete search names" + + def textbox_changed(string): + str_lower = string.control.value.lower() + list_view.controls = ( + [list_items.get(n) for n in NAMES if str_lower in n.lower()] + if str_lower + else [] + ) + + list_items = { + name: ft.ListTile( + title=ft.Text(name), + leading=ft.Icon(ft.Icons.ACCESSIBILITY), + ) + for name in NAMES + } + + text_field = ft.TextField(label="Search name:", on_change=textbox_changed) + list_view = ft.ListView(expand=1, spacing=10, padding=20) + + page.add( + ft.SafeArea( + content=ft.Column( + expand=True, + controls=[text_field, list_view], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/autocomplete_searcher/basic/pyproject.toml b/sdk/python/examples/apps/autocomplete_searcher/basic/pyproject.toml new file mode 100644 index 0000000000..f914ce7d3e --- /dev/null +++ b/sdk/python/examples/apps/autocomplete_searcher/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-autocomplete-searcher-basic" +version = "1.0.0" +description = "Filters a list of names as the user types into an autocomplete-style search field." +requires-python = ">=3.10" +keywords = ["apps", "autocomplete", "search", "list filtering", "text field"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Search"] + +[tool.flet.metadata] +title = "Autocomplete searcher" +controls = ["SafeArea", "Column", "TextField", "ListView", "ListTile", "Text", "Icon"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["live filtering", "search suggestions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/autocomplete_searcher/main.py b/sdk/python/examples/apps/autocomplete_searcher/main.py deleted file mode 100644 index a0fd627ca7..0000000000 --- a/sdk/python/examples/apps/autocomplete_searcher/main.py +++ /dev/null @@ -1,57 +0,0 @@ -import flet as ft - -NAMES = [ - "Adam", - "William", - "Emma", - "Alexander", - "Julia", - "Elias", - "Hugo", - "Alice", - "Emil", - "Anton", - "Ebba", - "Elin", - "Oliver", - "Axel", - "Maja", - "Ella", - "Alva", - "Liam", - "Albin", - "Elsa", - "Erik", - "Ida", - "Oscar", - "Wilma", -] - - -def main(page: ft.Page): - page.title = "Autocomplete search names" - - def textbox_changed(string): - str_lower = string.control.value.lower() - list_view.controls = ( - [list_items.get(n) for n in NAMES if str_lower in n.lower()] - if str_lower - else [] - ) - page.update() - - list_items = { - name: ft.ListTile( - title=ft.Text(name), - leading=ft.Icon(ft.Icons.ACCESSIBILITY), - ) - for name in NAMES - } - - text_field = ft.TextField(label="Search name:", on_change=textbox_changed) - list_view = ft.ListView(expand=1, spacing=10, padding=20) - - page.add(text_field, list_view) - - -ft.run(main) diff --git a/sdk/python/examples/apps/controls_gallery/README.md b/sdk/python/examples/apps/controls_gallery/README.md deleted file mode 100644 index 48e191b88c..0000000000 --- a/sdk/python/examples/apps/controls_gallery/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Flet Gallery app - -Flet Gallery app has been moved to https://github.com/flet-dev/gallery. diff --git a/sdk/python/examples/apps/counter/accessible/main.py b/sdk/python/examples/apps/counter/accessible/main.py new file mode 100644 index 0000000000..77fbed7129 --- /dev/null +++ b/sdk/python/examples/apps/counter/accessible/main.py @@ -0,0 +1,73 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Counter" + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + txt_number = ft.TextField( + value="0", + text_align=ft.TextAlign.RIGHT, + width=100, + label="Counter value", + ) + + def toggle_semantics_debugger(e): + page.show_semantics_debugger = not page.show_semantics_debugger + page.update() + + def minus_click(e): + txt_number.value = str(int(txt_number.value) - 1) + txt_number.label = f"Counter value, {txt_number.value}" + page.update() + + def plus_click(e): + txt_number.value = str(int(txt_number.value) + 1) + txt_number.label = f"Counter value, {txt_number.value}" + page.update() + + page.on_keyboard_event = toggle_semantics_debugger + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Semantics( + label=( + "Press plus button to increase counter, " + "or minus button to decrease counter." + ), + hint_text=( + "Press CONTROL plus ALT plus S to show semantics debugger" + ), + button=True, + content=ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.IconButton( + icon=ft.Icons.REMOVE, + tooltip="Decrease number", + on_click=minus_click, + ), + txt_number, + ft.IconButton( + icon=ft.Icons.ADD, + tooltip="Increase number", + on_click=plus_click, + ), + ], + ), + ), + ft.Text( + value=( + "Press CONTROL plus ALT plus S to show semantics debugger" + ), + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/counter/accessible/pyproject.toml b/sdk/python/examples/apps/counter/accessible/pyproject.toml new file mode 100644 index 0000000000..2cc168af1c --- /dev/null +++ b/sdk/python/examples/apps/counter/accessible/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-counter-accessible" +version = "1.0.0" +description = "Accessible counter app with semantics labels and a keyboard shortcut for the semantics debugger." +requires-python = ">=3.10" +keywords = ["apps", "counter", "accessibility", "semantics", "keyboard"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Accessibility"] + +[tool.flet.metadata] +title = "Accessible counter" +controls = ["SafeArea", "Column", "Row", "IconButton", "TextField", "Semantics", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["accessible semantics", "keyboard shortcut", "counter updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/counter/basic/main.py b/sdk/python/examples/apps/counter/basic/main.py new file mode 100644 index 0000000000..c70e8b234e --- /dev/null +++ b/sdk/python/examples/apps/counter/basic/main.py @@ -0,0 +1,36 @@ +import flet as ft + +ft.context.disable_auto_update() + + +def main(page: ft.Page): + page.title = "Counter" + txt_number = ft.TextField(value="0", text_align=ft.TextAlign.RIGHT, width=100) + + def minus_click(e): + txt_number.value = str(int(txt_number.value) - 1) + page.update() + + def plus_click(e): + txt_number.value = str(int(txt_number.value) + 1) + page.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + controls=[ + ft.IconButton(ft.Icons.REMOVE, on_click=minus_click), + txt_number, + ft.IconButton(ft.Icons.ADD, on_click=plus_click), + ] + ) + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/counter/basic/pyproject.toml b/sdk/python/examples/apps/counter/basic/pyproject.toml new file mode 100644 index 0000000000..9b57164057 --- /dev/null +++ b/sdk/python/examples/apps/counter/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-counter-basic" +version = "1.0.0" +description = "Classic counter app with increment and decrement buttons around a numeric text field." +requires-python = ">=3.10" +keywords = ["apps", "counter", "buttons", "text field", "state"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Basic"] + +[tool.flet.metadata] +title = "Basic counter" +controls = ["SafeArea", "Column", "Row", "IconButton", "TextField"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["counter updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/counter/counter.py b/sdk/python/examples/apps/counter/counter.py deleted file mode 100644 index d8f2926d60..0000000000 --- a/sdk/python/examples/apps/counter/counter.py +++ /dev/null @@ -1,33 +0,0 @@ -import flet as ft - -ft.context.disable_auto_update() - - -def main(page: ft.Page): - page.title = "Flet counter example" - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - txt_number = ft.TextField(value="0", text_align=ft.TextAlign.RIGHT, width=100) - - def minus_click(e): - txt_number.value = str(int(txt_number.value) - 1) - page.update() - - def plus_click(e): - txt_number.value = str(int(txt_number.value) + 1) - page.update() - - page.add( - ft.Row( - [ - ft.IconButton(ft.Icons.REMOVE, on_click=minus_click, key="decrement"), - txt_number, - ft.IconButton(ft.Icons.ADD, on_click=plus_click), - ], - alignment=ft.MainAxisAlignment.CENTER, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/apps/counter/counter_accessible.py b/sdk/python/examples/apps/counter/counter_accessible.py deleted file mode 100644 index f0ab036cb3..0000000000 --- a/sdk/python/examples/apps/counter/counter_accessible.py +++ /dev/null @@ -1,50 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Flet counter example" - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.spacing = 50 - - def on_keyboard(e: ft.KeyboardEvent): - print(e) - if e.key == "S" and e.ctrl: - page.show_semantics_debugger = not page.show_semantics_debugger - page.update() - - page.on_keyboard_event = on_keyboard - - txt_number = ft.TextField( - label="Number", value="0", text_align=ft.TextAlign.RIGHT, width=100 - ) - sem = ft.Semantics(txt_number, label="Current number: 0") - - def button_click(e): - txt_number.value = str( - int(txt_number.value) + (1 if e.control.data == "+" else -1) - ) - sem.label = f"Current number: {txt_number.value}" - page.update() - - page.add( - ft.Row( - [ - ft.IconButton( - ft.Icons.REMOVE, - tooltip="Decrement", - on_click=button_click, - data="-", - ), - sem, - ft.IconButton( - ft.Icons.ADD, tooltip="Increment", on_click=button_click, data="+" - ), - ], - alignment=ft.MainAxisAlignment.CENTER, - ), - ft.Text("Press CTRL+S to toggle semantics debugger"), - ) - - -ft.run(main) diff --git a/sdk/python/examples/apps/counter/requirements.txt b/sdk/python/examples/apps/counter/requirements.txt deleted file mode 100644 index 9f5592458b..0000000000 --- a/sdk/python/examples/apps/counter/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flet>=0.25.1 diff --git a/sdk/python/examples/apps/counter/test_counter_app.py b/sdk/python/examples/apps/counter/test_counter_app.py deleted file mode 100644 index e42d777d81..0000000000 --- a/sdk/python/examples/apps/counter/test_counter_app.py +++ /dev/null @@ -1,42 +0,0 @@ -from pathlib import Path - -import counter as app -import flet as ft -import flet.testing as ftt -import pytest -import pytest_asyncio - - -@pytest_asyncio.fixture(scope="module") -async def flet_app(request): - flet_app = ftt.FletTestApp( - flutter_app_dir=(Path(__file__).parent / "../../../../../client").resolve(), - flet_app_main=app.main, - test_path=request.fspath, - ) - await flet_app.start() - yield flet_app - await flet_app.teardown() - - -@pytest.mark.asyncio(loop_scope="module") -async def test_app(flet_app: ftt.FletTestApp): - tester = flet_app.tester - await tester.pump_and_settle() - zero_text = await tester.find_by_text("0") - assert zero_text.count == 1 - - # tap increment button - increment_btn = await tester.find_by_icon(ft.Icons.ADD) - assert increment_btn.count == 1 - await tester.tap(increment_btn) - await tester.pump_and_settle() - assert (await tester.find_by_text("1")).count == 1 - - # tap decrement button - decrement_button = await tester.find_by_key("decrement") - assert decrement_button.count == 1 - await tester.tap(decrement_button) - await tester.tap(decrement_button) - await tester.pump_and_settle() - assert (await tester.find_by_text("-1")).count == 1 diff --git a/sdk/python/examples/apps/counter_test_ios/Dockerfile b/sdk/python/examples/apps/counter_test_ios/Dockerfile index 2fe01b9780..5bf082ae12 100644 --- a/sdk/python/examples/apps/counter_test_ios/Dockerfile +++ b/sdk/python/examples/apps/counter_test_ios/Dockerfile @@ -2,8 +2,8 @@ FROM python:3-alpine WORKDIR /app -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt +COPY pyproject.toml ./ +RUN pip install --no-cache-dir . COPY . . diff --git a/sdk/python/examples/apps/counter_test_ios/main.py b/sdk/python/examples/apps/counter_test_ios/main.py index c8c4748a43..8dc1980490 100644 --- a/sdk/python/examples/apps/counter_test_ios/main.py +++ b/sdk/python/examples/apps/counter_test_ios/main.py @@ -7,6 +7,7 @@ class State: def main(page: ft.Page): state = State() + counter = ft.Text("0", size=50) def add_click(e): state.counter += 1 @@ -18,13 +19,14 @@ def add_click(e): ) page.add( ft.SafeArea( - ft.Container( - counter := ft.Text("0", size=50), + expand=True, + content=ft.Container( alignment=ft.Alignment.CENTER, + content=counter, ), - expand=True, ) ) -ft.run(main) +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/counter_test_ios/pyproject.toml b/sdk/python/examples/apps/counter_test_ios/pyproject.toml new file mode 100644 index 0000000000..492ce3b348 --- /dev/null +++ b/sdk/python/examples/apps/counter_test_ios/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-counter-test-ios" +version = "1.0.0" +description = "Minimal floating-action-button counter app used for testing Flet on mobile devices." +requires-python = ">=3.10" +keywords = ["apps", "counter", "mobile", "floating action button", "ios"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Basic"] + +[tool.flet.metadata] +title = "Counter test iOS" +controls = ["SafeArea", "Container", "Text", "FloatingActionButton"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["counter updates", "mobile testing"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/counter_test_ios/requirements.txt b/sdk/python/examples/apps/counter_test_ios/requirements.txt deleted file mode 100644 index 3a0e96024e..0000000000 --- a/sdk/python/examples/apps/counter_test_ios/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flet>=0.80.0 diff --git a/sdk/python/examples/apps/custom_controls/custom_buttons.py b/sdk/python/examples/apps/custom_controls/custom_buttons.py deleted file mode 100644 index d7037edb37..0000000000 --- a/sdk/python/examples/apps/custom_controls/custom_buttons.py +++ /dev/null @@ -1,45 +0,0 @@ -from dataclasses import dataclass, field -from typing import Any - -import flet as ft - - -def main(page: ft.Page): - @ft.control - class MyButton(ft.Button): - expand: int = field(default_factory=lambda: 1) - style: ft.ButtonStyle = field( - default_factory=lambda: ft.ButtonStyle( - shape=ft.RoundedRectangleBorder(radius=10) - ) - ) - bgcolor: ft.Colors = ft.Colors.BLUE_ACCENT - icon: Any = ft.Icons.HEADPHONES - - @dataclass - class MyButton2(ft.Button): - expand: Any = 1 - bgcolor: ft.Colors = ft.Colors.GREEN_ACCENT - style: ft.ButtonStyle = field( - default_factory=lambda: ft.ButtonStyle( - shape=ft.RoundedRectangleBorder(radius=20) - ) - ) - icon: ft.IconDataOrControl = ft.Icons.HEADPHONES - - @ft.control - class MyButton3(ft.Button): - def init(self): - self.expand = 1 - self.bgcolor = ft.Colors.RED_ACCENT - self.style = ft.ButtonStyle(shape=ft.RoundedRectangleBorder(radius=30)) - self.icon = ft.Icons.HEADPHONES - - page.add( - ft.Row([MyButton(content="1")]), - ft.Row([MyButton2(content="2")]), - ft.Row([MyButton3(content="3")]), - ) - - -ft.run(main) diff --git a/sdk/python/examples/apps/custom_controls/custom_buttons/main.py b/sdk/python/examples/apps/custom_controls/custom_buttons/main.py new file mode 100644 index 0000000000..ae42752145 --- /dev/null +++ b/sdk/python/examples/apps/custom_controls/custom_buttons/main.py @@ -0,0 +1,52 @@ +from dataclasses import dataclass, field +from typing import Any + +import flet as ft + + +def main(page: ft.Page): + @ft.control + class MyButton(ft.Button): + expand: int = field(default_factory=lambda: 1) + style: ft.ButtonStyle = field( + default_factory=lambda: ft.ButtonStyle( + shape=ft.RoundedRectangleBorder(radius=10) + ) + ) + bgcolor: ft.Colors = ft.Colors.BLUE_ACCENT + icon: Any = ft.Icons.HEADPHONES + + @dataclass + class MyButton2(ft.Button): + expand: Any = 1 + bgcolor: ft.Colors = ft.Colors.GREEN_ACCENT + style: ft.ButtonStyle = field( + default_factory=lambda: ft.ButtonStyle( + shape=ft.RoundedRectangleBorder(radius=20) + ) + ) + icon: ft.IconDataOrControl = ft.Icons.HEADPHONES + + @ft.control + class MyButton3(ft.Button): + def init(self): + self.expand = 1 + self.bgcolor = ft.Colors.RED_ACCENT + self.style = ft.ButtonStyle(shape=ft.RoundedRectangleBorder(radius=30)) + self.icon = ft.Icons.HEADPHONES + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row(controls=[MyButton(content="1")]), + ft.Row(controls=[MyButton2(content="2")]), + ft.Row(controls=[MyButton3(content="3")]), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/custom_controls/custom_buttons/pyproject.toml b/sdk/python/examples/apps/custom_controls/custom_buttons/pyproject.toml new file mode 100644 index 0000000000..d402f8a39b --- /dev/null +++ b/sdk/python/examples/apps/custom_controls/custom_buttons/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-custom-controls-custom-buttons" +version = "1.0.0" +description = "Shows three ways to define reusable custom button controls with predefined styling." +requires-python = ">=3.10" +keywords = ["apps", "custom controls", "buttons", "control decorator", "dataclass"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Custom controls"] + +[tool.flet.metadata] +title = "Custom buttons" +controls = ["SafeArea", "Column", "Row", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["custom controls", "button styling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/component_dialog.py b/sdk/python/examples/apps/declarative/component_dialog.py deleted file mode 100644 index 2aa5df9638..0000000000 --- a/sdk/python/examples/apps/declarative/component_dialog.py +++ /dev/null @@ -1,90 +0,0 @@ -import asyncio -from typing import Callable, Optional, cast - -import httpx - -import flet as ft - - -# ---------- REUSABLE DIALOG HOOK ---------- -def use_dialog(dialog_factory: Callable[[], ft.AlertDialog]) -> Callable[[], None]: - dlg_ref = ft.use_ref(cast(Optional[ft.AlertDialog], None)) - - if dlg_ref.current is None: - dlg_ref.current = dialog_factory() - - def open_dialog(): - if dlg_ref.current: - ft.context.page.show_dialog(dlg_ref.current) - - return open_dialog - - -# ---------- DIALOG COMPONENT ---------- -@ft.component -def UserDialogContent(): - """Component that loads and displays user data""" - loading, set_loading = ft.use_state(True) - name, set_name = ft.use_state("") - email, set_email = ft.use_state("") - error, set_error = ft.use_state("") - - async def load_user(): - set_loading(True) - set_error("") - try: - await asyncio.sleep(2) # Simulate network delay - async with httpx.AsyncClient(timeout=5) as client: - r = await client.get("https://jsonplaceholder.typicode.com/users/1") - r.raise_for_status() - data = r.json() - set_name(data["name"]) - set_email(data["email"]) - except Exception as e: - set_error(str(e)) - finally: - set_loading(False) - - # Load data when component mounts - ft.use_effect(lambda: asyncio.create_task(load_user()), []) - - return ft.Column( - tight=True, - controls=[ - ft.Text("User Panel", weight=ft.FontWeight.BOLD, size=18), - ft.ProgressRing(visible=loading), - ft.Text(f"Name: {name}"), - ft.Text(f"Email: {email}"), - ft.Text(error, color=ft.Colors.RED) if error else ft.Container(), - ], - ) - - -# ---------- PARENT COMPONENT ---------- -@ft.component -def App(): - open_user_dialog = use_dialog( - lambda: ft.AlertDialog( - modal=True, - title=ft.Text("User Information"), - content=UserDialogContent(), - actions=[ft.TextButton("Close", on_click=lambda e: e.page.pop_dialog())], - actions_alignment=ft.MainAxisAlignment.END, - ) - ) - - return ft.Container( - padding=20, - content=ft.Column( - controls=[ - ft.Text("Main App", size=22, weight=ft.FontWeight.BOLD), - ft.ElevatedButton( - "Open User Panel", - on_click=open_user_dialog, - ), - ] - ), - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/component_dialog/main.py b/sdk/python/examples/apps/declarative/component_dialog/main.py new file mode 100644 index 0000000000..8f9c720677 --- /dev/null +++ b/sdk/python/examples/apps/declarative/component_dialog/main.py @@ -0,0 +1,97 @@ +import asyncio +from typing import Callable, Optional, cast + +import httpx + +import flet as ft + + +# ---------- REUSABLE DIALOG HOOK ---------- +def use_dialog(dialog_factory: Callable[[], ft.AlertDialog]) -> Callable[[], None]: + dlg_ref = ft.use_ref(cast(Optional[ft.AlertDialog], None)) + + if dlg_ref.current is None: + dlg_ref.current = dialog_factory() + + def open_dialog(): + if dlg_ref.current: + ft.context.page.show_dialog(dlg_ref.current) + + return open_dialog + + +# ---------- DIALOG COMPONENT ---------- +@ft.component +def UserDialogContent(): + """Component that loads and displays user data""" + loading, set_loading = ft.use_state(True) + name, set_name = ft.use_state("") + email, set_email = ft.use_state("") + error, set_error = ft.use_state("") + + async def load_user(): + set_loading(True) + set_error("") + try: + await asyncio.sleep(2) # Simulate network delay + async with httpx.AsyncClient(timeout=5) as client: + r = await client.get("https://jsonplaceholder.typicode.com/users/1") + r.raise_for_status() + data = r.json() + set_name(data["name"]) + set_email(data["email"]) + except Exception as e: + set_error(str(e)) + finally: + set_loading(False) + + # Load data when component mounts + ft.use_effect(lambda: asyncio.create_task(load_user()), []) + + return ft.Column( + tight=True, + controls=[ + ft.Text("User Panel", weight=ft.FontWeight.BOLD, size=18), + ft.ProgressRing(visible=loading), + ft.Text(f"Name: {name}"), + ft.Text(f"Email: {email}"), + ft.Text(error, color=ft.Colors.RED) if error else ft.Container(), + ], + ) + + +# ---------- PARENT COMPONENT ---------- +@ft.component +def App(): + open_user_dialog = use_dialog( + lambda: ft.AlertDialog( + modal=True, + title=ft.Text("User Information"), + content=UserDialogContent(), + actions=[ft.TextButton("Close", on_click=lambda e: e.page.pop_dialog())], + actions_alignment=ft.MainAxisAlignment.END, + ) + ) + + return ft.SafeArea( + content=ft.Container( + padding=20, + content=ft.Column( + controls=[ + ft.Text("Main App", size=22, weight=ft.FontWeight.BOLD), + ft.Button( + "Open User Panel", + on_click=open_user_dialog, + ), + ] + ), + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/component_dialog/pyproject.toml b/sdk/python/examples/apps/declarative/component_dialog/pyproject.toml new file mode 100644 index 0000000000..f2ce37e917 --- /dev/null +++ b/sdk/python/examples/apps/declarative/component_dialog/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-component-dialog" +version = "1.0.0" +description = "Opens a declarative alert dialog that loads user data asynchronously before rendering it." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "dialog", "async", "httpx"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "httpx"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Component dialog" +controls = ["SafeArea", "Container", "Column", "Text", "ElevatedButton", "AlertDialog", "ProgressRing"] +layout_pattern = "center-stage" +complexity = "intermediate" +features = ["dialog management", "async", "data loading"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/counter.py b/sdk/python/examples/apps/declarative/counter.py deleted file mode 100644 index 6431490953..0000000000 --- a/sdk/python/examples/apps/declarative/counter.py +++ /dev/null @@ -1,24 +0,0 @@ -import flet as ft - - -@ft.component -def App(): - count, set_count = ft.use_state(0) - - return ft.View( - floating_action_button=ft.FloatingActionButton( - icon=ft.Icons.ADD, on_click=lambda: set_count(count + 1) - ), - controls=[ - ft.SafeArea( - ft.Container( - ft.Text(value=f"{count}", size=50), - alignment=ft.Alignment.CENTER, - ), - expand=True, - ) - ], - ) - - -ft.run(lambda page: page.render_views(App)) diff --git a/sdk/python/examples/apps/declarative/counter/main.py b/sdk/python/examples/apps/declarative/counter/main.py new file mode 100644 index 0000000000..ed12ccd2fb --- /dev/null +++ b/sdk/python/examples/apps/declarative/counter/main.py @@ -0,0 +1,29 @@ +import flet as ft + + +@ft.component +def App(): + count, set_count = ft.use_state(0) + + return ft.View( + floating_action_button=ft.FloatingActionButton( + icon=ft.Icons.ADD, on_click=lambda: set_count(count + 1) + ), + controls=[ + ft.SafeArea( + expand=True, + content=ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text(value=f"{count}", size=50), + ), + ) + ], + ) + + +def main(page: ft.Page): + page.render_views(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/counter/pyproject.toml b/sdk/python/examples/apps/declarative/counter/pyproject.toml new file mode 100644 index 0000000000..80f7fee15d --- /dev/null +++ b/sdk/python/examples/apps/declarative/counter/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-counter" +version = "1.0.0" +description = "Declarative counter app that updates a centered value from a floating action button." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "counter", "floating action button", "state"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Declarative counter" +controls = ["View", "SafeArea", "Container", "Text", "FloatingActionButton"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["counter updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/counter_minimal.py b/sdk/python/examples/apps/declarative/counter_minimal.py deleted file mode 100644 index 59129d8124..0000000000 --- a/sdk/python/examples/apps/declarative/counter_minimal.py +++ /dev/null @@ -1,16 +0,0 @@ -import flet as ft - - -@ft.component -def App(): - count, set_count = ft.use_state(0) - - return ft.Row( - controls=[ - ft.Text(value=f"{count}"), - ft.Button("Add", on_click=lambda: set_count(count + 1)), - ], - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/counter_minimal/main.py b/sdk/python/examples/apps/declarative/counter_minimal/main.py new file mode 100644 index 0000000000..453f218e81 --- /dev/null +++ b/sdk/python/examples/apps/declarative/counter_minimal/main.py @@ -0,0 +1,23 @@ +import flet as ft + + +@ft.component +def App(): + count, set_count = ft.use_state(0) + + return ft.SafeArea( + content=ft.Row( + controls=[ + ft.Text(value=f"{count}"), + ft.Button("Add", on_click=lambda: set_count(count + 1)), + ], + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/counter_minimal/pyproject.toml b/sdk/python/examples/apps/declarative/counter_minimal/pyproject.toml new file mode 100644 index 0000000000..a5174dbe89 --- /dev/null +++ b/sdk/python/examples/apps/declarative/counter_minimal/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-counter-minimal" +version = "1.0.0" +description = "Small declarative counter showing the minimum state hook pattern for updating text." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "counter", "minimal", "state hooks"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Counter minimal" +controls = ["SafeArea", "Row", "Text", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["counter updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/drag_and_drop_containers.py b/sdk/python/examples/apps/declarative/drag_and_drop_containers.py deleted file mode 100644 index 4861fab64b..0000000000 --- a/sdk/python/examples/apps/declarative/drag_and_drop_containers.py +++ /dev/null @@ -1,89 +0,0 @@ -from dataclasses import dataclass - -import flet as ft - - -@dataclass -@ft.observable -class TargetState: - bgcolor: ft.Colors = ft.Colors.BLUE_GREY_100 - is_drag_over: bool = False - - -@ft.component -def App(): - target, _ = ft.use_state(lambda: TargetState()) - - def on_will_accept(e: ft.DragWillAcceptEvent): - target.is_drag_over = True - - def on_accept(e: ft.DragTargetEvent): - target.bgcolor = e.src.data - target.is_drag_over = False - - def on_leave(e: ft.DragTargetLeaveEvent): - target.is_drag_over = False - - return ft.Row( - controls=[ - ft.Column( - controls=[ - ft.Draggable( - group="color", - data=ft.Colors.CYAN, - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.CYAN, - border_radius=5, - ), - content_feedback=ft.Container( - width=20, - height=20, - bgcolor=ft.Colors.CYAN, - border_radius=3, - ), - ), - ft.Draggable( - group="color", - data=ft.Colors.YELLOW, - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.YELLOW, - border_radius=5, - ), - ), - ft.Draggable( - group="color", - data=ft.Colors.GREEN, - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.GREEN, - border_radius=5, - ), - ), - ] - ), - ft.Container(width=100), - ft.DragTarget( - group="color", - on_will_accept=on_will_accept, - on_accept=on_accept, - on_leave=on_leave, - content=ft.Container( - width=50, - height=50, - bgcolor=target.bgcolor, - border=ft.Border.all(2, ft.Colors.BLACK45) - if target.is_drag_over - else None, - border_radius=5, - ), - ), - ] - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/drag_and_drop_containers/main.py b/sdk/python/examples/apps/declarative/drag_and_drop_containers/main.py new file mode 100644 index 0000000000..773b307d42 --- /dev/null +++ b/sdk/python/examples/apps/declarative/drag_and_drop_containers/main.py @@ -0,0 +1,96 @@ +from dataclasses import dataclass + +import flet as ft + + +@dataclass +@ft.observable +class TargetState: + bgcolor: ft.Colors = ft.Colors.BLUE_GREY_100 + is_drag_over: bool = False + + +@ft.component +def App(): + target, _ = ft.use_state(lambda: TargetState()) + + def on_will_accept(e: ft.DragWillAcceptEvent): + target.is_drag_over = True + + def on_accept(e: ft.DragTargetEvent): + target.bgcolor = e.src.data + target.is_drag_over = False + + def on_leave(e: ft.DragTargetLeaveEvent): + target.is_drag_over = False + + return ft.SafeArea( + content=ft.Row( + controls=[ + ft.Column( + controls=[ + ft.Draggable( + group="color", + data=ft.Colors.CYAN, + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.CYAN, + border_radius=5, + ), + content_feedback=ft.Container( + width=20, + height=20, + bgcolor=ft.Colors.CYAN, + border_radius=3, + ), + ), + ft.Draggable( + group="color", + data=ft.Colors.YELLOW, + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.YELLOW, + border_radius=5, + ), + ), + ft.Draggable( + group="color", + data=ft.Colors.GREEN, + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.GREEN, + border_radius=5, + ), + ), + ] + ), + ft.Container(width=100), + ft.DragTarget( + group="color", + on_will_accept=on_will_accept, + on_accept=on_accept, + on_leave=on_leave, + content=ft.Container( + width=50, + height=50, + bgcolor=target.bgcolor, + border=ft.Border.all(2, ft.Colors.BLACK_45) + if target.is_drag_over + else None, + border_radius=5, + ), + ), + ] + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/drag_and_drop_containers/pyproject.toml b/sdk/python/examples/apps/declarative/drag_and_drop_containers/pyproject.toml new file mode 100644 index 0000000000..fc218d0acf --- /dev/null +++ b/sdk/python/examples/apps/declarative/drag_and_drop_containers/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-drag-and-drop-containers" +version = "1.0.0" +description = "Drag colored squares onto a target container to update its background color declaratively." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "drag and drop", "containers", "state"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Drag and drop containers" +controls = ["SafeArea", "Row", "Column", "Draggable", "DragTarget", "Container"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["drag and drop", "live state updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/drag_and_drop_ordering.py b/sdk/python/examples/apps/declarative/drag_and_drop_ordering.py deleted file mode 100644 index cb37788abb..0000000000 --- a/sdk/python/examples/apps/declarative/drag_and_drop_ordering.py +++ /dev/null @@ -1,258 +0,0 @@ -import logging -from dataclasses import dataclass, field - -import flet as ft - -logging.basicConfig(level=logging.INFO) -logging.getLogger("flet_object_patch").setLevel(logging.INFO) -logging.getLogger("flet_components").setLevel(logging.INFO) - -ItemID = ft.IdCounter() - - -@ft.observable -@dataclass -class AppState: - groups: list["Group"] = field(default_factory=list) - - def move_group(self, src: "Group", dst: "Group"): - src_index = self.groups.index(src) - dst_index = self.groups.index(dst) - if src_index != dst_index: - print("Move group", src.title, "to position of", dst.title) - self.groups.insert(dst_index, self.groups.pop(src_index)) - - -@ft.observable -@dataclass -class Group: - title: str - color: ft.Colors - items: list["Item"] = field(default_factory=list) - - def add_item(self, text: str): - self.items.append(Item(text=text, group=self)) - - def move_item_into(self, item: "Item"): - print("Move item", item.text, "from", item.group.title, "to", self.title) - item.group.items.remove(item) - item.group = self - self.items.append(item) - - -@ft.observable -@dataclass -class Item: - text: str - group: Group - id: int = field(default_factory=ItemID) - - def move_item_at(self, item: "Item", to_item: "Item"): - if item == to_item: - return - print( - f"Move item {item.text} from {item.group.title} " - f"to {to_item.group.title} at position of {to_item.text}" - ) - item.group.items.remove(item) - item.group = to_item.group - to_index = to_item.group.items.index(to_item) - to_item.group.items.insert(to_index, item) - - -@ft.component -def ItemView(item: Item, **kwargs): - is_item_over, set_is_item_over = ft.use_state(False) - - def on_accept(e: ft.DragTargetEvent): - item.move_item_at(e.src.data, item) - set_is_item_over(False) - - return ft.Column( - spacing=2, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Divider( - color=ft.Colors.BLACK38, - thickness=2, - height=2, - radius=2, - opacity=1.0 if is_item_over else 0.0, - ), - ft.Draggable( - group="items", - data=item, - content=ft.DragTarget( - group="items", - data=item, - on_will_accept=lambda e: set_is_item_over( - e.accept and e.src.data != item - ), - on_accept=on_accept, - on_leave=lambda: set_is_item_over(False), - content=ft.Card( - content=ft.Container( - padding=7, - width=200, - content=ft.Row( - alignment=ft.MainAxisAlignment.START, - controls=[ - ft.Icon(ft.Icons.CIRCLE_OUTLINED), - ft.Text(value=item.text), - ], - ), - ), - ), - ), - ), - ], - ) - - -@ft.component -def GroupView(group: Group, move_group, **kwargs): - is_group_over, set_is_group_over = ft.use_state(False) - is_item_over, set_is_item_over = ft.use_state(False) - new_item_text, set_new_item_text = ft.use_state("") - - def on_item_accept(e: ft.DragTargetEvent): - group.move_item_into(e.src.data) - set_is_item_over(False) - - def on_group_accept(e: ft.DragTargetEvent): - move_group(e.src.data, group) - set_is_group_over(False) - - def on_add_item(self): - if stripped_text := new_item_text.strip(): - group.add_item(stripped_text) - set_new_item_text("") - - return ft.Row( - spacing=4, - intrinsic_height=True, - controls=[ - ft.VerticalDivider( - color=ft.Colors.BLACK54, - width=2, - thickness=2, - radius=2, - leading_indent=15, - trailing_indent=15, - opacity=1.0 if is_group_over else 0.0, - ), - ft.Draggable( - group="groups", - data=group, - content=ft.DragTarget( - group="items", - data=group, - on_will_accept=lambda e: set_is_item_over(e.accept), - on_accept=on_item_accept, - on_leave=lambda: set_is_item_over(False), - content=ft.DragTarget( - group="groups", - data=group, - on_will_accept=lambda e: set_is_group_over( - e.accept and e.src.data != group - ), - on_accept=on_group_accept, - on_leave=lambda: set_is_group_over(False), - content=ft.Container( - border=ft.Border.all(2, ft.Colors.BLACK12) - if not is_group_over - else ft.Border.all(2, ft.Colors.BLACK38), - border_radius=ft.BorderRadius.all(15), - bgcolor=group.color, - padding=ft.Padding.all(20), - width=220, - content=ft.Column( - spacing=4, - controls=[ - ft.Text( - group.title, - theme_style=ft.TextThemeStyle.TITLE_LARGE, - ), - ft.TextField( - label="New item", - bgcolor=ft.Colors.WHITE, - value=new_item_text, - on_change=lambda e: set_new_item_text( - e.control.value - ), - on_submit=on_add_item, - ), - ft.TextButton( - content="Add", - icon=ft.Icons.ADD, - on_click=on_add_item, - ), - ft.Column( - spacing=2, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - *[ - ItemView(item, key=item.id) - for item in group.items - ], - ft.Divider( - color=ft.Colors.BLACK38, - thickness=2, - height=2, - radius=2, - opacity=1.0 if is_item_over else 0.0, - ), - ], - ), - ], - ), - ), - ), - ), - ), - ], - ) - - -@ft.component -def App(): - group_1 = Group(title="Group 1", color=ft.Colors.DEEP_ORANGE_400) - group_1.add_item("Item 1") - group_1.add_item("Item 2") - - group_2 = Group(title="Group 2", color=ft.Colors.PINK_400) - group_2.add_item("Item 3") - - group_3 = Group(title="Group 3", color=ft.Colors.CYAN_400) - group_3.add_item("Item 4") - - # group_4 = Group(title="Group 4", color=ft.Colors.GREEN_400) - # group_4.add_item("Item 5") - - app, _ = ft.use_state( - lambda: AppState( - groups=[ - group_1, - group_2, - group_3, - # group_4, - ] - ) - ) - - def on_mounted(): - ft.context.page.theme_mode = ft.ThemeMode.LIGHT - - ft.on_mounted(on_mounted) - - return ft.Row( - spacing=4, - vertical_alignment=ft.CrossAxisAlignment.START, - controls=[ - GroupView(group, move_group=app.move_group, key=group.title) - for group in app.groups - ], - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/drag_and_drop_ordering/main.py b/sdk/python/examples/apps/declarative/drag_and_drop_ordering/main.py new file mode 100644 index 0000000000..65a6241639 --- /dev/null +++ b/sdk/python/examples/apps/declarative/drag_and_drop_ordering/main.py @@ -0,0 +1,265 @@ +import logging +from dataclasses import dataclass, field + +import flet as ft + +logging.basicConfig(level=logging.INFO) +logging.getLogger("flet_object_patch").setLevel(logging.INFO) +logging.getLogger("flet_components").setLevel(logging.INFO) + +ItemID = ft.IdCounter() + + +@ft.observable +@dataclass +class AppState: + groups: list["Group"] = field(default_factory=list) + + def move_group(self, src: "Group", dst: "Group"): + src_index = self.groups.index(src) + dst_index = self.groups.index(dst) + if src_index != dst_index: + print("Move group", src.title, "to position of", dst.title) + self.groups.insert(dst_index, self.groups.pop(src_index)) + + +@ft.observable +@dataclass +class Group: + title: str + color: ft.Colors + items: list["Item"] = field(default_factory=list) + + def add_item(self, text: str): + self.items.append(Item(text=text, group=self)) + + def move_item_into(self, item: "Item"): + print("Move item", item.text, "from", item.group.title, "to", self.title) + item.group.items.remove(item) + item.group = self + self.items.append(item) + + +@ft.observable +@dataclass +class Item: + text: str + group: Group + id: int = field(default_factory=ItemID) + + def move_item_at(self, item: "Item", to_item: "Item"): + if item == to_item: + return + print( + f"Move item {item.text} from {item.group.title} " + f"to {to_item.group.title} at position of {to_item.text}" + ) + item.group.items.remove(item) + item.group = to_item.group + to_index = to_item.group.items.index(to_item) + to_item.group.items.insert(to_index, item) + + +@ft.component +def ItemView(item: Item, **kwargs): + is_item_over, set_is_item_over = ft.use_state(False) + + def on_accept(e: ft.DragTargetEvent): + item.move_item_at(e.src.data, item) + set_is_item_over(False) + + return ft.Column( + spacing=2, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Divider( + color=ft.Colors.BLACK_38, + thickness=2, + height=2, + radius=2, + opacity=1.0 if is_item_over else 0.0, + ), + ft.Draggable( + group="items", + data=item, + content=ft.DragTarget( + group="items", + data=item, + on_will_accept=lambda e: set_is_item_over( + e.accept and e.src.data != item + ), + on_accept=on_accept, + on_leave=lambda: set_is_item_over(False), + content=ft.Card( + content=ft.Container( + padding=7, + width=200, + content=ft.Row( + alignment=ft.MainAxisAlignment.START, + controls=[ + ft.Icon(ft.Icons.CIRCLE_OUTLINED), + ft.Text(value=item.text), + ], + ), + ), + ), + ), + ), + ], + ) + + +@ft.component +def GroupView(group: Group, move_group, **kwargs): + is_group_over, set_is_group_over = ft.use_state(False) + is_item_over, set_is_item_over = ft.use_state(False) + new_item_text, set_new_item_text = ft.use_state("") + + def on_item_accept(e: ft.DragTargetEvent): + group.move_item_into(e.src.data) + set_is_item_over(False) + + def on_group_accept(e: ft.DragTargetEvent): + move_group(e.src.data, group) + set_is_group_over(False) + + def on_add_item(self): + if stripped_text := new_item_text.strip(): + group.add_item(stripped_text) + set_new_item_text("") + + return ft.Row( + spacing=4, + intrinsic_height=True, + controls=[ + ft.VerticalDivider( + color=ft.Colors.BLACK_54, + width=2, + thickness=2, + radius=2, + leading_indent=15, + trailing_indent=15, + opacity=1.0 if is_group_over else 0.0, + ), + ft.Draggable( + group="groups", + data=group, + content=ft.DragTarget( + group="items", + data=group, + on_will_accept=lambda e: set_is_item_over(e.accept), + on_accept=on_item_accept, + on_leave=lambda: set_is_item_over(False), + content=ft.DragTarget( + group="groups", + data=group, + on_will_accept=lambda e: set_is_group_over( + e.accept and e.src.data != group + ), + on_accept=on_group_accept, + on_leave=lambda: set_is_group_over(False), + content=ft.Container( + border=ft.Border.all(2, ft.Colors.BLACK_12) + if not is_group_over + else ft.Border.all(2, ft.Colors.BLACK_38), + border_radius=ft.BorderRadius.all(15), + bgcolor=group.color, + padding=ft.Padding.all(20), + width=220, + content=ft.Column( + spacing=4, + controls=[ + ft.Text( + group.title, + theme_style=ft.TextThemeStyle.TITLE_LARGE, + ), + ft.TextField( + label="New item", + bgcolor=ft.Colors.WHITE, + value=new_item_text, + on_change=lambda e: set_new_item_text( + e.control.value + ), + on_submit=on_add_item, + ), + ft.TextButton( + content="Add", + icon=ft.Icons.ADD, + on_click=on_add_item, + ), + ft.Column( + spacing=2, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + *[ + ItemView(item, key=item.id) + for item in group.items + ], + ft.Divider( + color=ft.Colors.BLACK_38, + thickness=2, + height=2, + radius=2, + opacity=1.0 if is_item_over else 0.0, + ), + ], + ), + ], + ), + ), + ), + ), + ), + ], + ) + + +@ft.component +def App(): + group_1 = Group(title="Group 1", color=ft.Colors.DEEP_ORANGE_400) + group_1.add_item("Item 1") + group_1.add_item("Item 2") + + group_2 = Group(title="Group 2", color=ft.Colors.PINK_400) + group_2.add_item("Item 3") + + group_3 = Group(title="Group 3", color=ft.Colors.CYAN_400) + group_3.add_item("Item 4") + + # group_4 = Group(title="Group 4", color=ft.Colors.GREEN_400) + # group_4.add_item("Item 5") + + app, _ = ft.use_state( + lambda: AppState( + groups=[ + group_1, + group_2, + group_3, + # group_4, + ] + ) + ) + + def on_mounted(): + ft.context.page.theme_mode = ft.ThemeMode.LIGHT + + ft.on_mounted(on_mounted) + + return ft.SafeArea( + content=ft.Row( + spacing=4, + vertical_alignment=ft.CrossAxisAlignment.START, + controls=[ + GroupView(group, move_group=app.move_group, key=group.title) + for group in app.groups + ], + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/drag_and_drop_ordering/pyproject.toml b/sdk/python/examples/apps/declarative/drag_and_drop_ordering/pyproject.toml new file mode 100644 index 0000000000..017dc3b594 --- /dev/null +++ b/sdk/python/examples/apps/declarative/drag_and_drop_ordering/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-drag-and-drop-ordering" +version = "1.0.0" +description = "Reorders groups and items declaratively with nested drag-and-drop targets." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "drag and drop", "reordering", "kanban"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Drag and drop ordering" +controls = ["SafeArea", "Row", "Column", "Draggable", "DragTarget", "TextField", "TextButton"] +layout_pattern = "dashboard" +complexity = "intermediate" +features = ["drag and drop", "reordering", "nested state"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/edit_form.py b/sdk/python/examples/apps/declarative/edit_form.py deleted file mode 100644 index 2ce0b07518..0000000000 --- a/sdk/python/examples/apps/declarative/edit_form.py +++ /dev/null @@ -1,59 +0,0 @@ -from dataclasses import dataclass -from typing import cast - -import flet as ft - - -@dataclass -@ft.observable -class Form: - first_name: str = "" - last_name: str = "" - - def set_first_name(self, value): - self.first_name = value - - def set_last_name(self, value): - self.last_name = value - - async def submit(self, e: ft.Event[ft.Button]): - e.page.show_dialog( - ft.AlertDialog( - title="Hello", - content=ft.Text(f"{self.first_name} {self.last_name}!"), - ) - ) - - async def reset(self): - self.first_name = "" - self.last_name = "" - - -@ft.component -def App(): - form, _ = ft.use_state(Form()) - - return [ - ft.TextField( - label="First name", - value=form.first_name, - on_change=lambda e: form.set_first_name(e.control.value), - ), - ft.TextField( - label="Last name", - value=form.last_name, - on_change=lambda e: form.set_last_name(e.control.value), - ), - ft.Row( - cast( - list[ft.Control], - [ - ft.FilledButton("Submit", on_click=form.submit), - ft.FilledTonalButton("Reset", on_click=form.reset), - ], - ) - ), - ] - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/edit_form/main.py b/sdk/python/examples/apps/declarative/edit_form/main.py new file mode 100644 index 0000000000..5e4ed8663d --- /dev/null +++ b/sdk/python/examples/apps/declarative/edit_form/main.py @@ -0,0 +1,68 @@ +from dataclasses import dataclass +from typing import cast + +import flet as ft + + +@dataclass +@ft.observable +class Form: + first_name: str = "" + last_name: str = "" + + def set_first_name(self, value): + self.first_name = value + + def set_last_name(self, value): + self.last_name = value + + async def submit(self, e: ft.Event[ft.Button]): + e.page.show_dialog( + ft.AlertDialog( + title="Hello", + content=ft.Text(f"{self.first_name} {self.last_name}!"), + ) + ) + + async def reset(self): + self.first_name = "" + self.last_name = "" + + +@ft.component +def App(): + form, _ = ft.use_state(Form()) + + return ft.SafeArea( + content=ft.Column( + controls=[ + ft.TextField( + label="First name", + value=form.first_name, + on_change=lambda e: form.set_first_name(e.control.value), + ), + ft.TextField( + label="Last name", + value=form.last_name, + on_change=lambda e: form.set_last_name(e.control.value), + ), + ft.Row( + cast( + list[ft.Control], + [ + ft.FilledButton("Submit", on_click=form.submit), + ft.FilledTonalButton("Reset", on_click=form.reset), + ], + ) + ), + ] + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/edit_form/pyproject.toml b/sdk/python/examples/apps/declarative/edit_form/pyproject.toml new file mode 100644 index 0000000000..5b78f63975 --- /dev/null +++ b/sdk/python/examples/apps/declarative/edit_form/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-edit-form" +version = "1.0.0" +description = "Edits a small form declaratively and submits or resets the fields through observable state." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "form", "text fields", "observable state", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Edit form" +controls = ["SafeArea", "Column", "Row", "TextField", "FilledButton", "FilledTonalButton", "AlertDialog"] +layout_pattern = "form" +complexity = "basic" +features = ["form editing", "async"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/minesweeper.py b/sdk/python/examples/apps/declarative/minesweeper.py deleted file mode 100644 index 2954fbdd2e..0000000000 --- a/sdk/python/examples/apps/declarative/minesweeper.py +++ /dev/null @@ -1,347 +0,0 @@ -# Step 1: Basic drag-and-drop of rectangles (cards) within a bounded area. - -import asyncio -import random -from dataclasses import dataclass -from typing import Optional - -import flet as ft - -# ----------- Visual constants ---------- -SQUARE_SIZE = 30 - -LIGHT = ft.Colors.WHITE70 -DARK = ft.Colors.BLACK38 - -BEVEL_RAISED = ft.Border( - bottom=ft.BorderSide(4, DARK), - right=ft.BorderSide(4, DARK), - top=ft.BorderSide(4, LIGHT), - left=ft.BorderSide(4, LIGHT), -) - -BEVEL_SUNKEN = ft.Border( - bottom=ft.BorderSide(4, LIGHT), - right=ft.BorderSide(4, LIGHT), - top=ft.BorderSide(4, DARK), - left=ft.BorderSide(4, DARK), -) - - -# ---------- Model ---------- -@ft.observable -@dataclass -class Square: - top: float = 0 - left: float = 0 - mine: bool = False - revealed: bool = False - flagged: bool = False - # tapped: bool = False - exploded: bool = False - adjacent_mines: int = 0 - - -@ft.observable -@dataclass -class Game: - squares: Optional[list[Square]] = None # to be initialized in __post_init__ - rows: int = 9 - cols: int = 9 - mine_count: int = 10 - mines_left: int = 10 - over = False - won = False - # timer - seconds: int = 0 # elapsed time - running: bool = False # ticking or not - first_click_done: bool = False - - def __post_init__(self): - """Initialize the grid of squares.""" - self.squares = [] - for r in range(self.rows): - for c in range(self.cols): - self.squares.append(Square(left=c * SQUARE_SIZE, top=r * SQUARE_SIZE)) - - # place mines - mine_positions = random.sample(range(len(self.squares)), self.mine_count) - for pos in mine_positions: - self.squares[pos].mine = True - - # calculate adjacent mine counts - for r in range(self.rows): - for c in range(self.cols): - idx = r * self.cols + c - if self.squares[idx].mine: - continue - count = 0 - for dr in (-1, 0, 1): - for dc in (-1, 0, 1): - if dr == 0 and dc == 0: - continue - nr, nc = r + dr, c + dc - if 0 <= nr < self.rows and 0 <= nc < self.cols: - nidx = nr * self.cols + nc - if self.squares[nidx].mine: - count += 1 - self.squares[idx].adjacent_mines = count - - def square_revealed(self, square: Square): - square.revealed = True - # square.tapped = True - if square.mine: - square.exploded = True - self.over = True - for sq in self.squares: - if sq.mine: - sq.revealed = True - print("Game Over!") - elif square.adjacent_mines == 0: - # reveal adjacent squares - r = int(square.top / SQUARE_SIZE) - c = int(square.left / SQUARE_SIZE) - for dr in (-1, 0, 1): - for dc in (-1, 0, 1): - if dr == 0 and dc == 0: - continue - nr, nc = r + dr, c + dc - if 0 <= nr < self.rows and 0 <= nc < self.cols: - nidx = nr * self.cols + nc - nsq = self.squares[nidx] - if not nsq.revealed and not nsq.mine and not nsq.flagged: - self.square_revealed(nsq) - # check for win - if all(sq.revealed or sq.mine for sq in self.squares): - self.over = True - self.won = True - for sq in self.squares: - if sq.mine and not sq.flagged: - sq.flagged = True - print("You Win!") - - def square_flagged(self, square: Square): - if not square.revealed: - square.flagged = not square.flagged - self.mines_left += -1 if square.flagged else 1 - - -# ---------- View (pure) ---------- -@ft.component -def SquareView(square: Square) -> ft.Control: - # Pure view: just render from state - return ft.Container( - bgcolor=( - ft.Colors.RED_900 - # if (square.revealed and square.mine and square.tapped) - if square.exploded - else ft.Colors.GREY_300 - # ft.Colors.GREY_300 - if square.revealed - else ft.Colors.GREY_400 - ), - alignment=ft.Alignment.CENTER, - foreground_decoration=ft.BoxDecoration( - border=BEVEL_RAISED if not square.revealed else None - ), - content=ft.Text( - "💥" - if square.exploded - else "💣" - if square.revealed and square.mine - else "🚩" - if square.flagged - else str(square.adjacent_mines) - if square.revealed and square.adjacent_mines > 0 - else "", - size=SQUARE_SIZE * 0.6, - # text_align=ft.TextAlign.CENTER, - # align=ft.Alignment.CENTER, - weight=ft.FontWeight.BOLD, - color=( - ft.Colors.BLUE - if square.adjacent_mines == 1 - else ft.Colors.GREEN - if square.adjacent_mines == 2 - else ft.Colors.RED - if square.adjacent_mines == 3 - else ft.Colors.ORANGE - if square.adjacent_mines == 4 - else ft.Colors.PURPLE - if square.adjacent_mines == 5 - else ft.Colors.BROWN - if square.adjacent_mines == 6 - else ft.Colors.TEAL - if square.adjacent_mines == 7 - else ft.Colors.BLACK - if square.adjacent_mines == 8 - else ft.Colors.BLACK - ), - ), - left=square.left, - top=square.top, - border=ft.Border.all(1, ft.Colors.GREY_500) if square.revealed else None, - width=SQUARE_SIZE, - height=SQUARE_SIZE, - # on_click=lambda _e: square_revealed(square), - ) - - -# ---------- App ---------- -@ft.component -def App(): - game, set_game = ft.use_state(lambda: Game()) - new_game_tapped, set_new_game_tapped = ft.use_state(False) - - ticker_task, set_ticker_task = ft.use_state(None) - - async def tick_loop(): - try: - while True: - await asyncio.sleep(1) - if not game.running or game.over: - break - game.seconds += 1 - print("Timer:", game.seconds) - except asyncio.CancelledError: - pass - - def ensure_ticker(): - # call with the function, not tick_loop() - if ticker_task is None or ticker_task.done(): - t = ft.context.page.run_task(tick_loop) # <-- no parentheses - set_ticker_task(t) - - def on_tap_down(e: ft.TapEvent): - # e.local_position.x / e.local_position.y are relative to the GestureDetector - # content (the Stack) - if game.over: - return - - if not game.first_click_done: - game.first_click_done = True - game.running = True # <-- start ticking - ensure_ticker() - print("Timer started") - - for s in game.squares: - if ( - s.left <= e.local_position.x <= s.left + SQUARE_SIZE - and s.top <= e.local_position.y <= s.top + SQUARE_SIZE - ): - if s.flagged: - print("square is flagged, cannot reveal") - return - game.square_revealed(s) - break - - def on_right_pan_start(e): - if game.over: - return - if not game.first_click_done: - game.first_click_done = True - game.running = True # <-- start ticking - print("Timer started") - for s in game.squares: - if ( - s.left <= e.local_position.x <= s.left + SQUARE_SIZE - and s.top <= e.local_position.y <= s.top + SQUARE_SIZE - ): - game.square_flagged(s) - break - - board = ft.GestureDetector( - drag_interval=5, - mouse_cursor=ft.MouseCursor.MOVE, - on_tap_down=on_tap_down, - on_right_pan_start=on_right_pan_start, - content=ft.Stack( - controls=[SquareView(c) for c in game.squares], - # width=300, - # height=300, - ), - ) - - top_menu = ft.Row( - controls=[ - ft.Container( - height=50, - width=90, - alignment=ft.Alignment.CENTER, - content=ft.Text( - f"{game.mines_left:03d}", - size=25, - weight=ft.FontWeight.BOLD, - color=ft.Colors.RED, - ), - foreground_decoration=ft.BoxDecoration(border=BEVEL_SUNKEN), - ), - ft.Container( - content=ft.Text( - "🙂" if not game.over else "😎" if game.won else "😵", size=35 - ), - # 😎 😵😢 - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.GREY_400, - on_tap_down=lambda e: set_new_game_tapped(True), - on_click=lambda e: ( - set_game(Game()), - set_new_game_tapped(False), - ticker_task.cancel() - if ticker_task and not ticker_task.done() - else None, - ), - width=50, - height=50, - foreground_decoration=ft.BoxDecoration( - border=BEVEL_RAISED if not new_game_tapped else None - ), - ), - ft.Container( - height=50, - width=90, - alignment=ft.Alignment.CENTER, - content=ft.Text( - f"{game.seconds:03d}", - size=25, - weight=ft.FontWeight.BOLD, - color=ft.Colors.RED, - ), - foreground_decoration=ft.BoxDecoration(border=BEVEL_SUNKEN), - ), - ], - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - expand=True, - ) - - return ft.Container( - content=ft.Column( - controls=[ - ft.Container( - content=top_menu, - foreground_decoration=ft.BoxDecoration(border=BEVEL_SUNKEN), - padding=10, - ), - ft.Container( - content=board, - # alignment=ft.Alignment.TOP_CENTER, - # content=ft.Text("sdfsdfsfd"), - foreground_decoration=ft.BoxDecoration(border=BEVEL_SUNKEN), - padding=5, - height=SQUARE_SIZE * game.rows + 10, - width=SQUARE_SIZE * game.cols + 10, - ), - ], - alignment=ft.MainAxisAlignment.START, - horizontal_alignment=ft.CrossAxisAlignment.START, - spacing=10, - ), - bgcolor=ft.Colors.GREY_400, - foreground_decoration=ft.BoxDecoration(border=BEVEL_RAISED), - width=SQUARE_SIZE * (game.cols + 1), - height=SQUARE_SIZE * (game.rows + 1) + 100, - padding=10, - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/minesweeper/main.py b/sdk/python/examples/apps/declarative/minesweeper/main.py new file mode 100644 index 0000000000..b34102162b --- /dev/null +++ b/sdk/python/examples/apps/declarative/minesweeper/main.py @@ -0,0 +1,352 @@ +# Step 1: Basic drag-and-drop of rectangles (cards) within a bounded area. + +import asyncio +import random +from dataclasses import dataclass +from typing import Optional + +import flet as ft + +# ----------- Visual constants ---------- +SQUARE_SIZE = 30 + +LIGHT = ft.Colors.WHITE_70 +DARK = ft.Colors.BLACK_38 + +BEVEL_RAISED = ft.Border( + bottom=ft.BorderSide(4, DARK), + right=ft.BorderSide(4, DARK), + top=ft.BorderSide(4, LIGHT), + left=ft.BorderSide(4, LIGHT), +) + +BEVEL_SUNKEN = ft.Border( + bottom=ft.BorderSide(4, LIGHT), + right=ft.BorderSide(4, LIGHT), + top=ft.BorderSide(4, DARK), + left=ft.BorderSide(4, DARK), +) + + +# ---------- Model ---------- +@ft.observable +@dataclass +class Square: + top: float = 0 + left: float = 0 + mine: bool = False + revealed: bool = False + flagged: bool = False + # tapped: bool = False + exploded: bool = False + adjacent_mines: int = 0 + + +@ft.observable +@dataclass +class Game: + squares: Optional[list[Square]] = None # to be initialized in __post_init__ + rows: int = 9 + cols: int = 9 + mine_count: int = 10 + mines_left: int = 10 + over = False + won = False + # timer + seconds: int = 0 # elapsed time + running: bool = False # ticking or not + first_click_done: bool = False + + def __post_init__(self): + """Initialize the grid of squares.""" + self.squares = [] + for r in range(self.rows): + for c in range(self.cols): + self.squares.append(Square(left=c * SQUARE_SIZE, top=r * SQUARE_SIZE)) + + # place mines + mine_positions = random.sample(range(len(self.squares)), self.mine_count) + for pos in mine_positions: + self.squares[pos].mine = True + + # calculate adjacent mine counts + for r in range(self.rows): + for c in range(self.cols): + idx = r * self.cols + c + if self.squares[idx].mine: + continue + count = 0 + for dr in (-1, 0, 1): + for dc in (-1, 0, 1): + if dr == 0 and dc == 0: + continue + nr, nc = r + dr, c + dc + if 0 <= nr < self.rows and 0 <= nc < self.cols: + nidx = nr * self.cols + nc + if self.squares[nidx].mine: + count += 1 + self.squares[idx].adjacent_mines = count + + def square_revealed(self, square: Square): + square.revealed = True + # square.tapped = True + if square.mine: + square.exploded = True + self.over = True + for sq in self.squares: + if sq.mine: + sq.revealed = True + print("Game Over!") + elif square.adjacent_mines == 0: + # reveal adjacent squares + r = int(square.top / SQUARE_SIZE) + c = int(square.left / SQUARE_SIZE) + for dr in (-1, 0, 1): + for dc in (-1, 0, 1): + if dr == 0 and dc == 0: + continue + nr, nc = r + dr, c + dc + if 0 <= nr < self.rows and 0 <= nc < self.cols: + nidx = nr * self.cols + nc + nsq = self.squares[nidx] + if not nsq.revealed and not nsq.mine and not nsq.flagged: + self.square_revealed(nsq) + # check for win + if all(sq.revealed or sq.mine for sq in self.squares): + self.over = True + self.won = True + for sq in self.squares: + if sq.mine and not sq.flagged: + sq.flagged = True + print("You Win!") + + def square_flagged(self, square: Square): + if not square.revealed: + square.flagged = not square.flagged + self.mines_left += -1 if square.flagged else 1 + + +# ---------- View (pure) ---------- +@ft.component +def SquareView(square: Square) -> ft.Control: + # Pure view: just render from state + return ft.Container( + bgcolor=( + ft.Colors.RED_900 + # if (square.revealed and square.mine and square.tapped) + if square.exploded + else ft.Colors.GREY_300 + # ft.Colors.GREY_300 + if square.revealed + else ft.Colors.GREY_400 + ), + alignment=ft.Alignment.CENTER, + foreground_decoration=ft.BoxDecoration( + border=BEVEL_RAISED if not square.revealed else None + ), + content=ft.Text( + "💥" + if square.exploded + else "💣" + if square.revealed and square.mine + else "🚩" + if square.flagged + else str(square.adjacent_mines) + if square.revealed and square.adjacent_mines > 0 + else "", + size=SQUARE_SIZE * 0.6, + # text_align=ft.TextAlign.CENTER, + # align=ft.Alignment.CENTER, + weight=ft.FontWeight.BOLD, + color=( + ft.Colors.BLUE + if square.adjacent_mines == 1 + else ft.Colors.GREEN + if square.adjacent_mines == 2 + else ft.Colors.RED + if square.adjacent_mines == 3 + else ft.Colors.ORANGE + if square.adjacent_mines == 4 + else ft.Colors.PURPLE + if square.adjacent_mines == 5 + else ft.Colors.BROWN + if square.adjacent_mines == 6 + else ft.Colors.TEAL + if square.adjacent_mines == 7 + else ft.Colors.BLACK + if square.adjacent_mines == 8 + else ft.Colors.BLACK + ), + ), + left=square.left, + top=square.top, + border=ft.Border.all(1, ft.Colors.GREY_500) if square.revealed else None, + width=SQUARE_SIZE, + height=SQUARE_SIZE, + # on_click=lambda _e: square_revealed(square), + ) + + +# ---------- App ---------- +@ft.component +def App(): + game, set_game = ft.use_state(lambda: Game()) + new_game_tapped, set_new_game_tapped = ft.use_state(False) + + ticker_task, set_ticker_task = ft.use_state(None) + + async def tick_loop(): + try: + while True: + await asyncio.sleep(1) + if not game.running or game.over: + break + game.seconds += 1 + print("Timer:", game.seconds) + except asyncio.CancelledError: + pass + + def ensure_ticker(): + # call with the function, not tick_loop() + if ticker_task is None or ticker_task.done(): + t = ft.context.page.run_task(tick_loop) # <-- no parentheses + set_ticker_task(t) + + def on_tap_down(e: ft.TapEvent): + # e.local_position.x / e.local_position.y are relative to the GestureDetector + # content (the Stack) + if game.over: + return + + if not game.first_click_done: + game.first_click_done = True + game.running = True # <-- start ticking + ensure_ticker() + print("Timer started") + + for s in game.squares: + if ( + s.left <= e.local_position.x <= s.left + SQUARE_SIZE + and s.top <= e.local_position.y <= s.top + SQUARE_SIZE + ): + if s.flagged: + print("square is flagged, cannot reveal") + return + game.square_revealed(s) + break + + def on_right_pan_start(e): + if game.over: + return + if not game.first_click_done: + game.first_click_done = True + game.running = True # <-- start ticking + print("Timer started") + for s in game.squares: + if ( + s.left <= e.local_position.x <= s.left + SQUARE_SIZE + and s.top <= e.local_position.y <= s.top + SQUARE_SIZE + ): + game.square_flagged(s) + break + + board = ft.GestureDetector( + drag_interval=5, + mouse_cursor=ft.MouseCursor.MOVE, + on_tap_down=on_tap_down, + on_right_pan_start=on_right_pan_start, + content=ft.Stack( + controls=[SquareView(c) for c in game.squares], + # width=300, + # height=300, + ), + ) + + top_menu = ft.Row( + controls=[ + ft.Container( + height=50, + width=90, + alignment=ft.Alignment.CENTER, + content=ft.Text( + f"{game.mines_left:03d}", + size=25, + weight=ft.FontWeight.BOLD, + color=ft.Colors.RED, + ), + foreground_decoration=ft.BoxDecoration(border=BEVEL_SUNKEN), + ), + ft.Container( + content=ft.Text( + "🙂" if not game.over else "😎" if game.won else "😵", size=35 + ), + # 😎 😵😢 + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.GREY_400, + on_tap_down=lambda e: set_new_game_tapped(True), + on_click=lambda e: ( + set_game(Game()), + set_new_game_tapped(False), + ticker_task.cancel() + if ticker_task and not ticker_task.done() + else None, + ), + width=50, + height=50, + foreground_decoration=ft.BoxDecoration( + border=BEVEL_RAISED if not new_game_tapped else None + ), + ), + ft.Container( + height=50, + width=90, + alignment=ft.Alignment.CENTER, + content=ft.Text( + f"{game.seconds:03d}", + size=25, + weight=ft.FontWeight.BOLD, + color=ft.Colors.RED, + ), + foreground_decoration=ft.BoxDecoration(border=BEVEL_SUNKEN), + ), + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + expand=True, + ) + + return ft.SafeArea( + content=ft.Container( + content=ft.Column( + controls=[ + ft.Container( + content=top_menu, + foreground_decoration=ft.BoxDecoration(border=BEVEL_SUNKEN), + padding=10, + ), + ft.Container( + content=board, + foreground_decoration=ft.BoxDecoration(border=BEVEL_SUNKEN), + padding=5, + height=SQUARE_SIZE * game.rows + 10, + width=SQUARE_SIZE * game.cols + 10, + ), + ], + alignment=ft.MainAxisAlignment.START, + horizontal_alignment=ft.CrossAxisAlignment.START, + spacing=10, + ), + bgcolor=ft.Colors.GREY_400, + foreground_decoration=ft.BoxDecoration(border=BEVEL_RAISED), + width=SQUARE_SIZE * (game.cols + 1), + height=SQUARE_SIZE * (game.rows + 1) + 100, + padding=10, + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/minesweeper/pyproject.toml b/sdk/python/examples/apps/declarative/minesweeper/pyproject.toml new file mode 100644 index 0000000000..2ed8861e09 --- /dev/null +++ b/sdk/python/examples/apps/declarative/minesweeper/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-minesweeper" +version = "1.0.0" +description = "Implements a declarative Minesweeper board with timer, flagging, and win-loss state." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "minesweeper", "game", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Games", "Apps/Declarative"] + +[tool.flet.metadata] +title = "Minesweeper" +controls = ["SafeArea", "Container", "Column", "Row", "GestureDetector", "Stack", "Text"] +layout_pattern = "center-stage" +complexity = "advanced" +features = ["game state", "async", "timer", "gesture input"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/minimal_reactive.py b/sdk/python/examples/apps/declarative/minimal_reactive.py deleted file mode 100644 index bf735bee4e..0000000000 --- a/sdk/python/examples/apps/declarative/minimal_reactive.py +++ /dev/null @@ -1,7 +0,0 @@ -import flet - -flet.run( - lambda page: page.render( - lambda: flet.Text("Hello, world!"), - ), -) diff --git a/sdk/python/examples/apps/declarative/minimal_reactive/main.py b/sdk/python/examples/apps/declarative/minimal_reactive/main.py new file mode 100644 index 0000000000..e5ab5b87c6 --- /dev/null +++ b/sdk/python/examples/apps/declarative/minimal_reactive/main.py @@ -0,0 +1,14 @@ +import flet as ft + + +@ft.component +def App(): + return ft.SafeArea(content=ft.Text("Hello, world!")) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/minimal_reactive/pyproject.toml b/sdk/python/examples/apps/declarative/minimal_reactive/pyproject.toml new file mode 100644 index 0000000000..d0a1ce8fdc --- /dev/null +++ b/sdk/python/examples/apps/declarative/minimal_reactive/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-minimal-reactive" +version = "1.0.0" +description = "Renders a minimal declarative component to show the smallest reactive app structure." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "minimal", "reactive"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Minimal reactive" +controls = ["SafeArea", "Text"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["reactive rendering"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/progress_bar.py b/sdk/python/examples/apps/declarative/progress_bar.py deleted file mode 100644 index 5f2ea833a2..0000000000 --- a/sdk/python/examples/apps/declarative/progress_bar.py +++ /dev/null @@ -1,29 +0,0 @@ -import asyncio -from dataclasses import dataclass - -import flet as ft - - -@dataclass -@ft.observable -class AppState: - counter: float - - async def start_counter(self): - self.counter = 0 - for _ in range(0, 10): - self.counter += 0.1 - await asyncio.sleep(0.5) - - -@ft.component -def App(): - state, _ = ft.use_state(AppState(counter=0)) - - return [ - ft.ProgressBar(state.counter), - ft.Button("Run!", on_click=state.start_counter), - ] - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/progress_bar/main.py b/sdk/python/examples/apps/declarative/progress_bar/main.py new file mode 100644 index 0000000000..7c4f6feda4 --- /dev/null +++ b/sdk/python/examples/apps/declarative/progress_bar/main.py @@ -0,0 +1,38 @@ +import asyncio +from dataclasses import dataclass + +import flet as ft + + +@dataclass +@ft.observable +class AppState: + counter: float + + async def start_counter(self): + self.counter = 0 + for _ in range(0, 10): + self.counter += 0.1 + await asyncio.sleep(0.5) + + +@ft.component +def App(): + state, _ = ft.use_state(AppState(counter=0)) + + return ft.SafeArea( + content=ft.Column( + controls=[ + ft.ProgressBar(state.counter), + ft.Button("Run!", on_click=state.start_counter), + ] + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/progress_bar/pyproject.toml b/sdk/python/examples/apps/declarative/progress_bar/pyproject.toml new file mode 100644 index 0000000000..e5051ed77e --- /dev/null +++ b/sdk/python/examples/apps/declarative/progress_bar/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-progress-bar" +version = "1.0.0" +description = "Runs an async counter that fills a progress bar over time in a declarative app." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "progress bar", "async", "state"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Progress bar" +controls = ["SafeArea", "Column", "ProgressBar", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["async", "progress updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/routing_two_pages.py b/sdk/python/examples/apps/declarative/routing_two_pages.py deleted file mode 100644 index a109a16bb3..0000000000 --- a/sdk/python/examples/apps/declarative/routing_two_pages.py +++ /dev/null @@ -1,135 +0,0 @@ -import asyncio -import logging -from collections.abc import Callable -from dataclasses import dataclass - -import flet as ft - -logging.basicConfig(level=logging.INFO) -logging.getLogger("flet_components").setLevel(logging.INFO) -# logging.getLogger("flet_object_patch").setLevel(logging.DEBUG) - - -@dataclass(frozen=True) -class ThemeContextValue: - mode: ft.ThemeMode - toggle: Callable[[], None] - - -ThemeContext = ft.create_context(ThemeContextValue(ft.ThemeMode.LIGHT, lambda: None)) - - -@ft.observable -@dataclass -class AppModel: - route: str - theme_mode: ft.ThemeMode = ft.ThemeMode.LIGHT - - def route_change(self, e: ft.RouteChangeEvent): - print("Route changed from:", self.route, "to:", e.route) - self.route = e.route - - async def view_popped(self, e: ft.ViewPopEvent): - print("View popped") - views = ft.unwrap_component(ft.context.page.views) - if len(views) > 1: - await ft.context.page.push_route(views[-2].route) - - def toggle_theme(self): - self.theme_mode = ( - ft.ThemeMode.DARK - if self.theme_mode == ft.ThemeMode.LIGHT - else ft.ThemeMode.LIGHT - ) - - -@ft.component -def ThemeToggle(): - theme = ft.use_context(ThemeContext) - return ft.Switch( - label="Dark mode" if theme.mode == ft.ThemeMode.LIGHT else "Light mode", - value=theme.mode == ft.ThemeMode.DARK, - on_change=lambda: theme.toggle(), - ) - - -@ft.component -def AppBar(): - return ft.AppBar( - title=ft.Text("Flet app"), - bgcolor=ft.Colors.SURFACE_BRIGHT, - actions=[ThemeToggle()], - ) - - -@ft.component -def RoutingExample(): - app, _ = ft.use_state(AppModel(route=ft.context.page.route)) - - # subscribe to page events as soon as possible - ft.context.page.on_route_change = app.route_change - ft.context.page.on_view_pop = app.view_popped - - # stable callback (doesn’t change identity each render) - toggle = ft.use_callback(lambda: app.toggle_theme(), dependencies=[app.theme_mode]) - - # memoize the provided value so its identity changes only when mode changes - theme_value = ft.use_memo( - lambda: ThemeContextValue(mode=app.theme_mode, toggle=toggle), - dependencies=[app.theme_mode, toggle], - ) - - ft.on_mounted( - lambda: print("Page size:", ft.context.page.width, ft.context.page.height) - ) - - def update_theme_mode(): - print("Theme mode changed to:", app.theme_mode) - ft.context.page.theme_mode = app.theme_mode - - ft.on_updated(update_theme_mode, [app.theme_mode]) - - return ThemeContext( - theme_value, - lambda: [ - ft.View( - route="/", - appbar=AppBar(), - controls=[ - ft.Button( - "Visit Store", - on_click=lambda _: asyncio.create_task( - ft.context.page.push_route("/store") - ), - ), - ft.Button( - "Do something", - on_click=lambda _: asyncio.create_task( - ft.context.page.push_route("/do-something") - ), - ), - ], - ), - *( - [ - ft.View( - route="/store", - appbar=AppBar(), - controls=[ - ft.Button( - "Go Home", - on_click=lambda _: asyncio.create_task( - ft.context.page.push_route("/") - ), - ), - ], - ) - ] - if app.route == "/store" - else [] - ), - ], - ) - - -ft.run(lambda page: page.render_views(RoutingExample)) diff --git a/sdk/python/examples/apps/declarative/routing_two_pages/main.py b/sdk/python/examples/apps/declarative/routing_two_pages/main.py new file mode 100644 index 0000000000..da2701bca3 --- /dev/null +++ b/sdk/python/examples/apps/declarative/routing_two_pages/main.py @@ -0,0 +1,152 @@ +import asyncio +import logging +from collections.abc import Callable +from dataclasses import dataclass + +import flet as ft + +logging.basicConfig(level=logging.INFO) +logging.getLogger("flet_components").setLevel(logging.INFO) +# logging.getLogger("flet_object_patch").setLevel(logging.DEBUG) + + +@dataclass(frozen=True) +class ThemeContextValue: + mode: ft.ThemeMode + toggle: Callable[[], None] + + +ThemeContext = ft.create_context(ThemeContextValue(ft.ThemeMode.LIGHT, lambda: None)) + + +@ft.observable +@dataclass +class AppModel: + route: str + theme_mode: ft.ThemeMode = ft.ThemeMode.LIGHT + + def route_change(self, e: ft.RouteChangeEvent): + print("Route changed from:", self.route, "to:", e.route) + self.route = e.route + + async def view_popped(self, e: ft.ViewPopEvent): + print("View popped") + views = ft.unwrap_component(ft.context.page.views) + if len(views) > 1: + await ft.context.page.push_route(views[-2].route) + + def toggle_theme(self): + self.theme_mode = ( + ft.ThemeMode.DARK + if self.theme_mode == ft.ThemeMode.LIGHT + else ft.ThemeMode.LIGHT + ) + + +@ft.component +def ThemeToggle(): + theme = ft.use_context(ThemeContext) + return ft.Switch( + label="Dark mode" if theme.mode == ft.ThemeMode.LIGHT else "Light mode", + value=theme.mode == ft.ThemeMode.DARK, + on_change=lambda: theme.toggle(), + ) + + +@ft.component +def AppBar(): + return ft.AppBar( + title=ft.Text("Flet app"), + bgcolor=ft.Colors.SURFACE_BRIGHT, + actions=[ThemeToggle()], + ) + + +@ft.component +def RoutingExample(): + app, _ = ft.use_state(AppModel(route=ft.context.page.route)) + + # subscribe to page events as soon as possible + ft.context.page.on_route_change = app.route_change + ft.context.page.on_view_pop = app.view_popped + + # stable callback (doesn’t change identity each render) + toggle = ft.use_callback(lambda: app.toggle_theme(), dependencies=[app.theme_mode]) + + # memoize the provided value so its identity changes only when mode changes + theme_value = ft.use_memo( + lambda: ThemeContextValue(mode=app.theme_mode, toggle=toggle), + dependencies=[app.theme_mode, toggle], + ) + + ft.on_mounted( + lambda: print("Page size:", ft.context.page.width, ft.context.page.height) + ) + + def update_theme_mode(): + print("Theme mode changed to:", app.theme_mode) + ft.context.page.theme_mode = app.theme_mode + + ft.on_updated(update_theme_mode, [app.theme_mode]) + + return ThemeContext( + theme_value, + lambda: [ + ft.View( + route="/", + appbar=AppBar(), + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + "Visit Store", + on_click=lambda _: asyncio.create_task( + ft.context.page.push_route("/store") + ), + ), + ft.Button( + "Do something", + on_click=lambda _: asyncio.create_task( + ft.context.page.push_route("/do-something") + ), + ), + ] + ), + ), + ], + ), + *( + [ + ft.View( + route="/store", + appbar=AppBar(), + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + "Go Home", + on_click=lambda _: asyncio.create_task( + ft.context.page.push_route("/") + ), + ), + ] + ), + ), + ], + ) + ] + if app.route == "/store" + else [] + ), + ], + ) + + +def main(page: ft.Page): + page.render_views(RoutingExample) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/routing_two_pages/pyproject.toml b/sdk/python/examples/apps/declarative/routing_two_pages/pyproject.toml new file mode 100644 index 0000000000..f8e72bb542 --- /dev/null +++ b/sdk/python/examples/apps/declarative/routing_two_pages/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-routing-two-pages" +version = "1.0.0" +description = "Demonstrates declarative view routing with theme context shared across two pages." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "routing", "views", "context", "theme"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative", "Apps/Navigation"] + +[tool.flet.metadata] +title = "Routing two pages" +controls = ["View", "SafeArea", "Column", "Button", "AppBar", "Switch"] +layout_pattern = "list-detail" +complexity = "intermediate" +features = ["routing", "context", "theme switching"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/set_state_with_list.py b/sdk/python/examples/apps/declarative/set_state_with_list.py deleted file mode 100644 index 9e10fdc132..0000000000 --- a/sdk/python/examples/apps/declarative/set_state_with_list.py +++ /dev/null @@ -1,36 +0,0 @@ -import asyncio - -import flet as ft - - -@ft.component -def App(): - items, set_items = ft.use_state(list(range(60))) - - async def auto_scroll(e): - for i in range(60, 120): - await asyncio.sleep(1) - set_items(lambda cur, i=i: cur + [i]) - print(f"Scrolling to line {i}") - - return ft.Column( - controls=[ - ft.ListView( - spacing=10, - padding=20, - auto_scroll=True, - height=300, - controls=[ - ft.Text( - key=i, - value=f"Line {i + 1}", - ) - for i in items - ], - ), - ft.OutlinedButton("Start auto-scrolling", on_click=auto_scroll), - ] - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/set_state_with_list/main.py b/sdk/python/examples/apps/declarative/set_state_with_list/main.py new file mode 100644 index 0000000000..46fbf4863b --- /dev/null +++ b/sdk/python/examples/apps/declarative/set_state_with_list/main.py @@ -0,0 +1,43 @@ +import asyncio + +import flet as ft + + +@ft.component +def App(): + items, set_items = ft.use_state(list(range(60))) + + async def auto_scroll(e): + for i in range(60, 120): + await asyncio.sleep(1) + set_items(lambda cur, i=i: cur + [i]) + print(f"Scrolling to line {i}") + + return ft.SafeArea( + content=ft.Column( + controls=[ + ft.ListView( + spacing=10, + padding=20, + auto_scroll=True, + height=300, + controls=[ + ft.Text( + key=i, + value=f"Line {i + 1}", + ) + for i in items + ], + ), + ft.OutlinedButton("Start auto-scrolling", on_click=auto_scroll), + ] + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/set_state_with_list/pyproject.toml b/sdk/python/examples/apps/declarative/set_state_with_list/pyproject.toml new file mode 100644 index 0000000000..61251f50c9 --- /dev/null +++ b/sdk/python/examples/apps/declarative/set_state_with_list/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-set-state-with-list" +version = "1.0.0" +description = "Appends rows to a scrolling list over time to demonstrate list state updates." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "list view", "auto scroll", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Set state with list" +controls = ["SafeArea", "Column", "ListView", "Text", "OutlinedButton"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["async", "auto scroll", "list state updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/shape_drawer.py b/sdk/python/examples/apps/declarative/shape_drawer.py deleted file mode 100644 index 8ec8dcdfdb..0000000000 --- a/sdk/python/examples/apps/declarative/shape_drawer.py +++ /dev/null @@ -1,104 +0,0 @@ -from dataclasses import dataclass, field - -import flet as ft -from flet import canvas - -POINT_RADIUS = 4 -PAINT = ft.Paint( - style=ft.PaintingStyle.STROKE, - color=ft.Colors.RED, - stroke_width=2, -) - - -@dataclass -class Point: - x: float = 0 - y: float = 0 - - -@dataclass -@ft.observable -class Polygon: - points: list[Point] = field(default_factory=list) - - -@dataclass -@ft.observable -class State: - polygons: list[Polygon] = field(default_factory=list[Polygon]) - - -@ft.component -def PolygonView(polygon: Polygon) -> canvas.Path: - return canvas.Path( - elements=[ - canvas.Path.MoveTo( - point.x, - point.y, - ) - if i == 0 - else canvas.Path.LineTo( - point.x, - point.y, - ) - for i, point in enumerate(polygon.points) - ] - + [canvas.Path.Close()], - paint=PAINT, - ) - - -@ft.component -def App(): - state, _ = ft.use_state( - State( - polygons=[ - Polygon([Point(50, 50), Point(150, 50), Point(100, 150)]), - ] - ) - ) - # state, _ = ft.use_state(State(polygons=[Polygon()])) - - def handle_tap_down(e: ft.TapEvent): - # add point to the top polygon - if e.local_position is not None: - state.polygons[-1].points.append( - Point(x=e.local_position.x, y=e.local_position.y) - ) - if len(state.polygons[-1].points) == 1: - # add a temporary point to be updated on hover - state.polygons[-1].points.append( - Point(x=e.local_position.x, y=e.local_position.y) - ) - - def handle_hover(e: ft.HoverEvent): - # update position of the last point in the top polygon - if e.local_position is not None and len(state.polygons[-1].points) > 0: - state.polygons[-1].points[-1].x = e.local_position.x - state.polygons[-1].points[-1].y = e.local_position.y - state.notify() - - def handle_secondary_tap_down(e: ft.TapEvent): - # add new polygon - state.polygons.append(Polygon()) - - return ft.GestureDetector( - on_tap_down=handle_tap_down, - on_secondary_tap_down=handle_secondary_tap_down, - on_hover=handle_hover, - content=ft.Container( - content=canvas.Canvas( - width=float("inf"), - height=float("inf"), - shapes=[PolygonView(polygon) for polygon in state.polygons], - ), - width=500, - height=500, - bgcolor=ft.Colors.GREY_300, - alignment=ft.Alignment.TOP_LEFT, - ), - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/shape_drawer/main.py b/sdk/python/examples/apps/declarative/shape_drawer/main.py new file mode 100644 index 0000000000..47db6728bf --- /dev/null +++ b/sdk/python/examples/apps/declarative/shape_drawer/main.py @@ -0,0 +1,111 @@ +from dataclasses import dataclass, field + +import flet as ft +from flet import canvas + +POINT_RADIUS = 4 +PAINT = ft.Paint( + style=ft.PaintingStyle.STROKE, + color=ft.Colors.RED, + stroke_width=2, +) + + +@dataclass +class Point: + x: float = 0 + y: float = 0 + + +@dataclass +@ft.observable +class Polygon: + points: list[Point] = field(default_factory=list) + + +@dataclass +@ft.observable +class State: + polygons: list[Polygon] = field(default_factory=list[Polygon]) + + +@ft.component +def PolygonView(polygon: Polygon) -> canvas.Path: + return canvas.Path( + elements=[ + canvas.Path.MoveTo( + point.x, + point.y, + ) + if i == 0 + else canvas.Path.LineTo( + point.x, + point.y, + ) + for i, point in enumerate(polygon.points) + ] + + [canvas.Path.Close()], + paint=PAINT, + ) + + +@ft.component +def App(): + state, _ = ft.use_state( + State( + polygons=[ + Polygon([Point(50, 50), Point(150, 50), Point(100, 150)]), + ] + ) + ) + # state, _ = ft.use_state(State(polygons=[Polygon()])) + + def handle_tap_down(e: ft.TapEvent): + # add point to the top polygon + if e.local_position is not None: + state.polygons[-1].points.append( + Point(x=e.local_position.x, y=e.local_position.y) + ) + if len(state.polygons[-1].points) == 1: + # add a temporary point to be updated on hover + state.polygons[-1].points.append( + Point(x=e.local_position.x, y=e.local_position.y) + ) + + def handle_hover(e: ft.HoverEvent): + # update position of the last point in the top polygon + if e.local_position is not None and len(state.polygons[-1].points) > 0: + state.polygons[-1].points[-1].x = e.local_position.x + state.polygons[-1].points[-1].y = e.local_position.y + state.notify() + + def handle_secondary_tap_down(e: ft.TapEvent): + # add new polygon + state.polygons.append(Polygon()) + + return ft.SafeArea( + content=ft.GestureDetector( + on_tap_down=handle_tap_down, + on_secondary_tap_down=handle_secondary_tap_down, + on_hover=handle_hover, + content=ft.Container( + content=canvas.Canvas( + width=float("inf"), + height=float("inf"), + shapes=[PolygonView(polygon) for polygon in state.polygons], + ), + width=500, + height=500, + bgcolor=ft.Colors.GREY_300, + alignment=ft.Alignment.TOP_LEFT, + ), + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/shape_drawer/pyproject.toml b/sdk/python/examples/apps/declarative/shape_drawer/pyproject.toml new file mode 100644 index 0000000000..0769d7090e --- /dev/null +++ b/sdk/python/examples/apps/declarative/shape_drawer/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-shape-drawer" +version = "1.0.0" +description = "Draws polygons on a canvas declaratively in response to tap and hover gestures." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "canvas", "drawing", "gestures"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative", "Apps/Canvas"] + +[tool.flet.metadata] +title = "Shape drawer" +controls = ["SafeArea", "GestureDetector", "Container", "Canvas"] +layout_pattern = "center-stage" +complexity = "intermediate" +features = ["canvas drawing", "gesture input"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/sunflower.py b/sdk/python/examples/apps/declarative/sunflower.py deleted file mode 100644 index f90183e0b0..0000000000 --- a/sdk/python/examples/apps/declarative/sunflower.py +++ /dev/null @@ -1,127 +0,0 @@ -import logging -import math -import random -from dataclasses import dataclass, field -from typing import cast - -import flet as ft - -logging.basicConfig(level=logging.INFO) -logging.getLogger("flet_components").setLevel(logging.INFO) - -MAX_SEEDS = 250 - -rnd = random.Random() - - -@dataclass -class Seed: - key: int - x: float - y: float - inner: bool = False - - -@dataclass -class AppModel(ft.Observable): - seeds_count: float = MAX_SEEDS // 2 - seeds: list[Seed] = field(default_factory=list) - - def __post_init__(self): - ft.context.page.title = "Sunflower" - self.compute_seeds() - - def update_seeds_count(self, new_seeds_count: float): - self.seeds_count = new_seeds_count - self.compute_seeds() - - def compute_seeds(self): - count = round(self.seeds_count) - self.seeds.clear() - tau = math.pi * 2 - scale_factor = 1 / 40 - phi = (math.sqrt(5) + 1) / 2 - - # inner orange seeds - for i in range(0, count): - theta = i * tau / phi - r = math.sqrt(i) * scale_factor - self.seeds.append( - Seed( - key=i, x=r * math.cos(theta), y=-1 * r * math.sin(theta), inner=True - ) - ) - - # outer gray seeds - for j in range(count, MAX_SEEDS): - x = math.cos(tau * j / (MAX_SEEDS - 1)) * 0.9 - y = math.sin(tau * j / (MAX_SEEDS - 1)) * 0.9 - self.seeds.append(Seed(key=j, x=x, y=y, inner=False)) - - -@ft.component -def SeedView(seed: Seed, key=None) -> ft.Control: - return ft.Container( - width=5, - height=5, - bgcolor=ft.Colors.ORANGE if seed.inner else ft.Colors.GREY_700, - align=ft.Alignment(seed.x, seed.y), - animate_align=ft.Animation(round(rnd.random() * 500) + 250), - border_radius=2.5, - ) - - -@ft.component -def Sunflower(): - app, _ = ft.use_state(lambda: AppModel()) - MemoSeedView = ft.memo(SeedView) - return ft.View( - appbar=ft.AppBar(title=ft.Text("Sunflower")), - controls=[ - ft.SafeArea( - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - expand=True, - controls=[ - ft.Container( - content=ft.Stack( - controls=[ - MemoSeedView(s, key=s.key) for s in app.seeds - ], - aspect_ratio=1.0, - ), - alignment=ft.Alignment.CENTER, - expand=True, - ), - ft.Row( - [ - ft.Text( - f"Showing {round(app.seeds_count)} " - f"seed{'s' if app.seeds_count != 1 else ''}" - ) - ], - alignment=ft.MainAxisAlignment.CENTER, - ), - ft.Row( - [ - ft.Slider( - min=1, - max=MAX_SEEDS, - value=app.seeds_count, - width=300, - on_change=lambda e: app.update_seeds_count( - cast(float, e.control.value) - ), - ) - ], - alignment=ft.MainAxisAlignment.CENTER, - ), - ], - ), - expand=True, - ) - ], - ) - - -ft.run(lambda page: page.render_views(Sunflower)) diff --git a/sdk/python/examples/apps/declarative/sunflower/main.py b/sdk/python/examples/apps/declarative/sunflower/main.py new file mode 100644 index 0000000000..7d52027e06 --- /dev/null +++ b/sdk/python/examples/apps/declarative/sunflower/main.py @@ -0,0 +1,132 @@ +import logging +import math +import random +from dataclasses import dataclass, field +from typing import cast + +import flet as ft + +logging.basicConfig(level=logging.INFO) +logging.getLogger("flet_components").setLevel(logging.INFO) + +MAX_SEEDS = 250 + +rnd = random.Random() + + +@dataclass +class Seed: + key: int + x: float + y: float + inner: bool = False + + +@dataclass +class AppModel(ft.Observable): + seeds_count: float = MAX_SEEDS // 2 + seeds: list[Seed] = field(default_factory=list) + + def __post_init__(self): + ft.context.page.title = "Sunflower" + self.compute_seeds() + + def update_seeds_count(self, new_seeds_count: float): + self.seeds_count = new_seeds_count + self.compute_seeds() + + def compute_seeds(self): + count = round(self.seeds_count) + self.seeds.clear() + tau = math.pi * 2 + scale_factor = 1 / 40 + phi = (math.sqrt(5) + 1) / 2 + + # inner orange seeds + for i in range(0, count): + theta = i * tau / phi + r = math.sqrt(i) * scale_factor + self.seeds.append( + Seed( + key=i, x=r * math.cos(theta), y=-1 * r * math.sin(theta), inner=True + ) + ) + + # outer gray seeds + for j in range(count, MAX_SEEDS): + x = math.cos(tau * j / (MAX_SEEDS - 1)) * 0.9 + y = math.sin(tau * j / (MAX_SEEDS - 1)) * 0.9 + self.seeds.append(Seed(key=j, x=x, y=y, inner=False)) + + +@ft.component +def SeedView(seed: Seed, key=None) -> ft.Control: + return ft.Container( + width=5, + height=5, + bgcolor=ft.Colors.ORANGE if seed.inner else ft.Colors.GREY_700, + align=ft.Alignment(seed.x, seed.y), + animate_align=ft.Animation(round(rnd.random() * 500) + 250), + border_radius=2.5, + ) + + +@ft.component +def Sunflower(): + app, _ = ft.use_state(lambda: AppModel()) + MemoSeedView = ft.memo(SeedView) + return ft.View( + appbar=ft.AppBar(title=ft.Text("Sunflower")), + controls=[ + ft.SafeArea( + expand=True, + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + expand=True, + controls=[ + ft.Container( + content=ft.Stack( + controls=[ + MemoSeedView(s, key=s.key) for s in app.seeds + ], + aspect_ratio=1.0, + ), + alignment=ft.Alignment.CENTER, + expand=True, + ), + ft.Row( + [ + ft.Text( + f"Showing {round(app.seeds_count)} " + f"seed{'s' if app.seeds_count != 1 else ''}" + ) + ], + alignment=ft.MainAxisAlignment.CENTER, + ), + ft.Row( + [ + ft.Slider( + min=1, + max=MAX_SEEDS, + value=app.seeds_count, + width=300, + on_change=lambda e: app.update_seeds_count( + cast(float, e.control.value) + ), + ) + ], + alignment=ft.MainAxisAlignment.CENTER, + ), + ], + ), + ) + ], + ) + + +def main(page: ft.Page): + page.render_views(Sunflower) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/sunflower/pyproject.toml b/sdk/python/examples/apps/declarative/sunflower/pyproject.toml new file mode 100644 index 0000000000..d15f6c0d77 --- /dev/null +++ b/sdk/python/examples/apps/declarative/sunflower/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-sunflower" +version = "1.0.0" +description = "Animates a sunflower seed pattern with a slider that recomputes declarative layout." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "animation", "slider", "visualization"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative", "Apps/Visualization"] + +[tool.flet.metadata] +title = "Sunflower" +controls = ["View", "SafeArea", "Column", "Stack", "Slider", "Text", "AppBar"] +layout_pattern = "center-stage" +complexity = "intermediate" +features = ["animation", "data visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/tic_tac_toe.py b/sdk/python/examples/apps/declarative/tic_tac_toe.py deleted file mode 100644 index 507ced114c..0000000000 --- a/sdk/python/examples/apps/declarative/tic_tac_toe.py +++ /dev/null @@ -1,105 +0,0 @@ -import flet as ft - - -@ft.component -def Square(value: str, on_click): - return ft.Button( - ft.Icon(ft.Icons.CIRCLE_OUTLINED) - if value == "O" - else ft.Icon(ft.Icons.CLOSE) - if value == "X" - else "", - width=50, - height=50, - style=ft.ButtonStyle(shape=ft.RoundedRectangleBorder(radius=5), padding=0), - on_click=on_click, - ) - - -@ft.component -def Board(x_is_next: bool, squares: list[str], on_play): - def handle_click(i: int): - if squares[i] or calculate_winner(squares): - return - next_squares = squares[:] - next_squares[i] = "X" if x_is_next else "O" - on_play(next_squares) - - winner = calculate_winner(squares) - - return ft.Column( - [ - ft.Text( - f"Winner: {winner}" - if winner - else f"Next player: {'X' if x_is_next else 'O'}" - ), - ft.Row( - [Square(squares[i], lambda e, i=i: handle_click(i)) for i in range(3)] - ), - ft.Row( - [ - Square(squares[i], lambda e, i=i: handle_click(i)) - for i in range(3, 6) - ] - ), - ft.Row( - [ - Square(squares[i], lambda e, i=i: handle_click(i)) - for i in range(6, 9) - ] - ), - ] - ) - - -@ft.component -def Game(): - history, set_history = ft.use_state([[""] * 9]) - current_move, set_current_move = ft.use_state(0) - x_is_next = current_move % 2 == 0 - - def handle_play(next_squares: list[str]): - next_history = history[: current_move + 1] + [next_squares] - set_history(next_history) - set_current_move(len(next_history) - 1) - - def jump_to(move: int): - set_current_move(move) - - moves: list[ft.Control] = [ - ft.TextButton( - ft.Text(f"Go to move #{move}" if move > 0 else "Go to game start"), - on_click=lambda e, m=move: jump_to(m), - ) - for move, _ in enumerate(history) - ] - - return ft.Row( - [ - Board(x_is_next, history[current_move], handle_play), - ft.Column(moves), - ], - vertical_alignment=ft.CrossAxisAlignment.START, - ) - - -def calculate_winner(squares: list[str]): - lines = [ - [0, 1, 2], - [3, 4, 5], - [6, 7, 8], - [0, 3, 6], - [1, 4, 7], - [2, 5, 8], - [0, 4, 8], - [2, 4, 6], - ] - for line in lines: - a, b, c = line - if squares[a] and squares[a] == squares[b] == squares[c]: - return squares[a] - return None - - -ft.run(lambda page: page.render(Game)) diff --git a/sdk/python/examples/apps/declarative/tic_tac_toe/main.py b/sdk/python/examples/apps/declarative/tic_tac_toe/main.py new file mode 100644 index 0000000000..0e5e8a6648 --- /dev/null +++ b/sdk/python/examples/apps/declarative/tic_tac_toe/main.py @@ -0,0 +1,112 @@ +import flet as ft + + +@ft.component +def Square(value: str, on_click): + return ft.Button( + ft.Icon(ft.Icons.CIRCLE_OUTLINED) + if value == "O" + else ft.Icon(ft.Icons.CLOSE) + if value == "X" + else "", + width=50, + height=50, + style=ft.ButtonStyle(shape=ft.RoundedRectangleBorder(radius=5), padding=0), + on_click=on_click, + ) + + +@ft.component +def Board(x_is_next: bool, squares: list[str], on_play): + def handle_click(i: int): + if squares[i] or calculate_winner(squares): + return + next_squares = squares[:] + next_squares[i] = "X" if x_is_next else "O" + on_play(next_squares) + + winner = calculate_winner(squares) + + return ft.Column( + [ + ft.Text( + f"Winner: {winner}" + if winner + else f"Next player: {'X' if x_is_next else 'O'}" + ), + ft.Row( + [Square(squares[i], lambda e, i=i: handle_click(i)) for i in range(3)] + ), + ft.Row( + [ + Square(squares[i], lambda e, i=i: handle_click(i)) + for i in range(3, 6) + ] + ), + ft.Row( + [ + Square(squares[i], lambda e, i=i: handle_click(i)) + for i in range(6, 9) + ] + ), + ] + ) + + +@ft.component +def Game(): + history, set_history = ft.use_state([[""] * 9]) + current_move, set_current_move = ft.use_state(0) + x_is_next = current_move % 2 == 0 + + def handle_play(next_squares: list[str]): + next_history = history[: current_move + 1] + [next_squares] + set_history(next_history) + set_current_move(len(next_history) - 1) + + def jump_to(move: int): + set_current_move(move) + + moves: list[ft.Control] = [ + ft.TextButton( + ft.Text(f"Go to move #{move}" if move > 0 else "Go to game start"), + on_click=lambda e, m=move: jump_to(m), + ) + for move, _ in enumerate(history) + ] + + return ft.SafeArea( + content=ft.Row( + [ + Board(x_is_next, history[current_move], handle_play), + ft.Column(moves), + ], + vertical_alignment=ft.CrossAxisAlignment.START, + ) + ) + + +def calculate_winner(squares: list[str]): + lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ] + for line in lines: + a, b, c = line + if squares[a] and squares[a] == squares[b] == squares[c]: + return squares[a] + return None + + +def main(page: ft.Page): + page.render(Game) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/tic_tac_toe/pyproject.toml b/sdk/python/examples/apps/declarative/tic_tac_toe/pyproject.toml new file mode 100644 index 0000000000..8febc29c38 --- /dev/null +++ b/sdk/python/examples/apps/declarative/tic_tac_toe/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-tic-tac-toe" +version = "1.0.0" +description = "Plays tic-tac-toe declaratively with move history and winner detection." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "game", "tic tac toe", "history"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Games", "Apps/Declarative"] + +[tool.flet.metadata] +title = "Tic tac toe" +controls = ["SafeArea", "Row", "Column", "Button", "Icon", "Text", "TextButton"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["game state", "move history"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/timer/main.py b/sdk/python/examples/apps/declarative/timer/main.py new file mode 100644 index 0000000000..19853b2adf --- /dev/null +++ b/sdk/python/examples/apps/declarative/timer/main.py @@ -0,0 +1,107 @@ +import asyncio +import time +from dataclasses import dataclass + +import flet as ft + + +def format_hhmmss(seconds: int) -> str: + """Format elapsed seconds as HH:MM:SS.""" + h = seconds // 3600 + m = (seconds % 3600) // 60 + s = seconds % 60 + return f"{h:02}:{m:02}:{s:02}" + + +@ft.observable +@dataclass +class TimerState: + running: bool = False + paused: bool = False + elapsed: int = 0 + + _base_elapsed: int = 0 + _started_at: float = 0.0 + _task: asyncio.Task | None = None + + async def _ticker(self): + while self.running: + if not self.paused: + self.elapsed = self._base_elapsed + int(time.time() - self._started_at) + await asyncio.sleep(1) + self._task = None + + def toggle(self): + if not self.running: + self.running = True + self.paused = False + self._base_elapsed = self.elapsed + self._started_at = time.time() + if self._task is None or self._task.done(): + self._task = asyncio.create_task(self._ticker()) + return + + if not self.paused: + self._base_elapsed += int(time.time() - self._started_at) + self.elapsed = self._base_elapsed + self.paused = True + return + + self.paused = False + self._started_at = time.time() + + def stop(self): + self.running = False + self.paused = False + self.elapsed = 0 + self._base_elapsed = 0 + self._started_at = 0.0 + + +@ft.component +def App(): + state, _ = ft.use_state(TimerState()) + + label = "Pause" if state.running and not state.paused else "Start" + icon = ft.Icons.PAUSE if state.running and not state.paused else ft.Icons.PLAY_ARROW + + return ft.SafeArea( + content=ft.Column( + spacing=20, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Text( + format_hhmmss(state.elapsed), + size=30, + weight=ft.FontWeight.BOLD, + ), + ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.FilledButton( + label, + icon=icon, + on_click=state.toggle, + ), + ft.TextButton( + "Stop", + icon=ft.Icons.STOP, + on_click=state.stop, + disabled=not state.running and state.elapsed == 0, + ), + ], + ), + ], + ) + ) + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/timer/pyproject.toml b/sdk/python/examples/apps/declarative/timer/pyproject.toml new file mode 100644 index 0000000000..cdba54d9ad --- /dev/null +++ b/sdk/python/examples/apps/declarative/timer/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-timer" +version = "1.0.0" +description = "Declarative stopwatch example driven by observable timer state and async ticking." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "timer", "stopwatch", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative", "Apps/Time"] + +[tool.flet.metadata] +title = "Declarative timer" +controls = ["SafeArea", "Column", "Row", "Text", "FilledButton", "TextButton"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["async", "timer controls", "observable state"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/todo.py b/sdk/python/examples/apps/declarative/todo.py deleted file mode 100644 index 9dc8bebb99..0000000000 --- a/sdk/python/examples/apps/declarative/todo.py +++ /dev/null @@ -1,223 +0,0 @@ -import logging -from dataclasses import dataclass, field -from typing import Callable, cast - -import flet as ft - -logging.basicConfig(level=logging.INFO) - -TaskID = ft.IdCounter() - - -@ft.observable -@dataclass -class TaskItem: - name: str - completed: bool = False - id: int = field(default_factory=TaskID) - on_status_changed: Callable | None = None - - def update_task(self, new_name: str): - self.name = new_name - - def toggle_task_status(self): - self.completed = not self.completed - if self.on_status_changed: - self.on_status_changed() - - -@ft.observable -@dataclass -class TodoAppState: - tasks: list[TaskItem] = field(default_factory=list) - statuses: list[str] = field(default_factory=lambda: ["all", "active", "completed"]) - status: str = "all" - - def get_tasks(self) -> list[TaskItem]: - return list( - filter( - lambda task: self.status == "all" - or self.status == "active" - and not task.completed - or self.status == "completed" - and task.completed, - self.tasks, - ) - ) - - @property - def active_tasks_number(self) -> int: - return len([task for task in self.tasks if not task.completed]) - - def status_changed(self, e: ft.Event[ft.Tabs]): - self.status = self.statuses[e.control.selected_index] - - def on_task_status_changed(self): - cast(ft.Observable, self).notify() - - def add_task(self, new_task_event: str): - self.tasks.append( - TaskItem(new_task_event, on_status_changed=self.on_task_status_changed) - ) - - def delete_task(self, task: TaskItem): - self.tasks.remove(task) - - def clear_completed(self): - self.tasks = list(filter(lambda task: not task.completed, self.tasks)) - - -@ft.component -def TodoAppView(): - state, _ = ft.use_state(lambda: TodoAppState()) - new_task_name, set_new_task_name = ft.use_state("") - new_task_field: ft.Ref = ft.Ref() - - async def add_task(): - state.add_task(new_task_name) - set_new_task_name("") - await cast(ft.TextField, new_task_field.current).focus() - - return ft.View( - scroll=ft.ScrollMode.AUTO, - controls=[ - ft.Column( - [ - Header(), - ft.Row( - controls=[ - ft.TextField( - ref=new_task_field, - hint_text="What needs to be done?", - on_submit=add_task, - value=new_task_name, - on_change=lambda e: set_new_task_name(e.control.value), - autofocus=True, - expand=True, - ), - ft.FloatingActionButton( - icon=ft.Icons.ADD, - on_click=add_task, - ), - ], - ), - ft.Column( - spacing=25, - controls=[ - ft.Tabs( - selected_index=state.statuses.index(state.status), - length=len(state.statuses), - on_change=state.status_changed, - content=ft.TabBar( - scrollable=False, - tabs=[ft.Tab(label=tab) for tab in state.statuses], - ), - ), - ft.Column( - [ - TaskItemView(task, state.delete_task, key=task.id) - for task in state.get_tasks() - ] - ), - Footer( - active_tasks_number=state.active_tasks_number, - clear_completed=state.clear_completed, - ), - ], - ), - ] - ) - ], - ) - - -@ft.component -def TaskItemView(task: TaskItem, delete_task, key=None) -> ft.Control: - edit_mode, set_edit_mode = ft.use_state(False) - new_name, set_new_name = ft.use_state("") - - def edit(): - set_edit_mode(True) - set_new_name(task.name) - - def complete_edit(): - task.update_task(new_name) - set_edit_mode(False) - - return ( - ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - vertical_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Checkbox( - value=task.completed, - label=task.name, - on_change=lambda: task.toggle_task_status(), - ), - ft.Row( - spacing=0, - controls=[ - ft.IconButton( - icon=ft.Icons.CREATE_OUTLINED, - tooltip="Edit To-Do", - on_click=edit, - ), - ft.IconButton( - ft.Icons.DELETE_OUTLINE, - tooltip="Delete To-Do", - on_click=lambda: delete_task(task), - ), - ], - ), - ], - ) - if not edit_mode - else ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - vertical_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.TextField( - value=new_name, - on_change=lambda e: set_new_name(e.control.value), - expand=1, - ), - ft.IconButton( - icon=ft.Icons.DONE_OUTLINE_OUTLINED, - icon_color=ft.Colors.GREEN, - tooltip="Update To-Do", - on_click=complete_edit, - ), - ], - ) - ) - - -@ft.component -def Header(): - return ft.Row( - [ - ft.Text( - value="Todos", - theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM, - ) - ], - alignment=ft.MainAxisAlignment.CENTER, - ) - - -@ft.component -def Footer(active_tasks_number: int, clear_completed): - return ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - vertical_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(f"{active_tasks_number} items left"), - ft.OutlinedButton( - content="Clear completed", - on_click=clear_completed, - ), - ], - ) - - -ft.run(lambda page: page.render_views(TodoAppView)) diff --git a/sdk/python/examples/apps/declarative/todo/main.py b/sdk/python/examples/apps/declarative/todo/main.py new file mode 100644 index 0000000000..c24ca7b659 --- /dev/null +++ b/sdk/python/examples/apps/declarative/todo/main.py @@ -0,0 +1,238 @@ +import logging +from dataclasses import dataclass, field +from typing import Callable, cast + +import flet as ft + +logging.basicConfig(level=logging.INFO) + +TaskID = ft.IdCounter() + + +@ft.observable +@dataclass +class TaskItem: + name: str + completed: bool = False + id: int = field(default_factory=TaskID) + on_status_changed: Callable | None = None + + def update_task(self, new_name: str): + self.name = new_name + + def toggle_task_status(self): + self.completed = not self.completed + if self.on_status_changed: + self.on_status_changed() + + +@ft.observable +@dataclass +class TodoAppState: + tasks: list[TaskItem] = field(default_factory=list) + statuses: list[str] = field(default_factory=lambda: ["all", "active", "completed"]) + status: str = "all" + + def get_tasks(self) -> list[TaskItem]: + return list( + filter( + lambda task: self.status == "all" + or self.status == "active" + and not task.completed + or self.status == "completed" + and task.completed, + self.tasks, + ) + ) + + @property + def active_tasks_number(self) -> int: + return len([task for task in self.tasks if not task.completed]) + + def status_changed(self, e: ft.Event[ft.Tabs]): + self.status = self.statuses[e.control.selected_index] + + def on_task_status_changed(self): + cast(ft.Observable, self).notify() + + def add_task(self, new_task_event: str): + self.tasks.append( + TaskItem(new_task_event, on_status_changed=self.on_task_status_changed) + ) + + def delete_task(self, task: TaskItem): + self.tasks.remove(task) + + def clear_completed(self): + self.tasks = list(filter(lambda task: not task.completed, self.tasks)) + + +@ft.component +def TodoAppView(): + state, _ = ft.use_state(lambda: TodoAppState()) + new_task_name, set_new_task_name = ft.use_state("") + new_task_field: ft.Ref = ft.Ref() + + async def add_task(): + state.add_task(new_task_name) + set_new_task_name("") + await cast(ft.TextField, new_task_field.current).focus() + + return ft.View( + scroll=ft.ScrollMode.AUTO, + controls=[ + ft.SafeArea( + content=ft.Column( + [ + Header(), + ft.Row( + controls=[ + ft.TextField( + ref=new_task_field, + hint_text="What needs to be done?", + on_submit=add_task, + value=new_task_name, + on_change=lambda e: set_new_task_name( + e.control.value + ), + autofocus=True, + expand=True, + ), + ft.FloatingActionButton( + icon=ft.Icons.ADD, + on_click=add_task, + ), + ], + ), + ft.Column( + spacing=25, + controls=[ + ft.Tabs( + selected_index=state.statuses.index(state.status), + length=len(state.statuses), + on_change=state.status_changed, + content=ft.TabBar( + scrollable=False, + tabs=[ + ft.Tab(label=tab) for tab in state.statuses + ], + ), + ), + ft.Column( + [ + TaskItemView( + task, + state.delete_task, + key=task.id, + ) + for task in state.get_tasks() + ] + ), + Footer( + active_tasks_number=state.active_tasks_number, + clear_completed=state.clear_completed, + ), + ], + ), + ] + ) + ) + ], + ) + + +@ft.component +def TaskItemView(task: TaskItem, delete_task, key=None) -> ft.Control: + edit_mode, set_edit_mode = ft.use_state(False) + new_name, set_new_name = ft.use_state("") + + def edit(): + set_edit_mode(True) + set_new_name(task.name) + + def complete_edit(): + task.update_task(new_name) + set_edit_mode(False) + + return ( + ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + vertical_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Checkbox( + value=task.completed, + label=task.name, + on_change=lambda: task.toggle_task_status(), + ), + ft.Row( + spacing=0, + controls=[ + ft.IconButton( + icon=ft.Icons.CREATE_OUTLINED, + tooltip="Edit To-Do", + on_click=edit, + ), + ft.IconButton( + ft.Icons.DELETE_OUTLINE, + tooltip="Delete To-Do", + on_click=lambda: delete_task(task), + ), + ], + ), + ], + ) + if not edit_mode + else ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + vertical_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.TextField( + value=new_name, + on_change=lambda e: set_new_name(e.control.value), + expand=1, + ), + ft.IconButton( + icon=ft.Icons.DONE_OUTLINE_OUTLINED, + icon_color=ft.Colors.GREEN, + tooltip="Update To-Do", + on_click=complete_edit, + ), + ], + ) + ) + + +@ft.component +def Header(): + return ft.Row( + [ + ft.Text( + value="Todos", + theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM, + ) + ], + alignment=ft.MainAxisAlignment.CENTER, + ) + + +@ft.component +def Footer(active_tasks_number: int, clear_completed): + return ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + vertical_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text(f"{active_tasks_number} items left"), + ft.OutlinedButton( + content="Clear completed", + on_click=clear_completed, + ), + ], + ) + + +def main(page: ft.Page): + page.render_views(TodoAppView) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/todo/pyproject.toml b/sdk/python/examples/apps/declarative/todo/pyproject.toml new file mode 100644 index 0000000000..eb860555f6 --- /dev/null +++ b/sdk/python/examples/apps/declarative/todo/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-todo" +version = "1.0.0" +description = "Tracks, filters, edits, and clears tasks in a declarative to-do application." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "todo", "tabs", "forms"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative", "Apps/Productivity"] + +[tool.flet.metadata] +title = "To-do" +controls = ["View", "SafeArea", "Column", "Row", "TextField", "Tabs", "Checkbox", "IconButton", "FloatingActionButton"] +layout_pattern = "form" +complexity = "intermediate" +features = ["task filtering", "editing", "list state updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/trolli-declarative/src/assets/Pacifico-Regular.ttf b/sdk/python/examples/apps/declarative/trolli/assets/Pacifico-Regular.ttf similarity index 100% rename from sdk/python/examples/apps/trolli-declarative/src/assets/Pacifico-Regular.ttf rename to sdk/python/examples/apps/declarative/trolli/assets/Pacifico-Regular.ttf diff --git a/sdk/python/examples/apps/declarative/trolli/components/__init__.py b/sdk/python/examples/apps/declarative/trolli/components/__init__.py new file mode 100644 index 0000000000..5ee7e0bd85 --- /dev/null +++ b/sdk/python/examples/apps/declarative/trolli/components/__init__.py @@ -0,0 +1,11 @@ +from .app_bar import TrolliAppBar +from .board import BoardView +from .boards import BoardsView +from .sidebar import Sidebar + +__all__ = [ + "BoardView", + "BoardsView", + "Sidebar", + "TrolliAppBar", +] diff --git a/sdk/python/examples/apps/trolli-declarative/src/components/app_bar.py b/sdk/python/examples/apps/declarative/trolli/components/app_bar.py similarity index 99% rename from sdk/python/examples/apps/trolli-declarative/src/components/app_bar.py rename to sdk/python/examples/apps/declarative/trolli/components/app_bar.py index 7a0f0cb0a7..d1ec9585d3 100644 --- a/sdk/python/examples/apps/trolli-declarative/src/components/app_bar.py +++ b/sdk/python/examples/apps/declarative/trolli/components/app_bar.py @@ -1,8 +1,9 @@ from __future__ import annotations +from models import TrolliState + import flet as ft -from models import TrolliState from .dialogs import show_login_dialog, show_settings_dialog diff --git a/sdk/python/examples/apps/declarative/trolli/components/board.py b/sdk/python/examples/apps/declarative/trolli/components/board.py new file mode 100644 index 0000000000..8024ad5b1c --- /dev/null +++ b/sdk/python/examples/apps/declarative/trolli/components/board.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from models import Board + +import flet as ft + +from .board_list import BoardListView +from .dialogs import show_new_list_dialog + + +@ft.component +def BoardView(board: Board): + return ft.Column( + expand=True, + spacing=10, + controls=[ + ft.Row( + # height=30, + expand_loose=True, + alignment=ft.MainAxisAlignment.END, + controls=[ + ft.FilledButton( + "Add list", + icon=ft.Icons.ADD, + on_click=lambda _: show_new_list_dialog(board), + ), + ], + ), + ft.Row( + alignment=ft.MainAxisAlignment.START, + controls=[ + ft.Text(board.name, theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM), + ], + ), + ft.Row( + expand=True, + scroll=ft.ScrollMode.AUTO, + vertical_alignment=ft.CrossAxisAlignment.START, + controls=[ + *[ + BoardListView(bl, move_list=board.move_list) + for bl in board.lists + ], + ], + ), + ], + ) diff --git a/sdk/python/examples/apps/declarative/trolli/components/board_list.py b/sdk/python/examples/apps/declarative/trolli/components/board_list.py new file mode 100644 index 0000000000..0863c0a983 --- /dev/null +++ b/sdk/python/examples/apps/declarative/trolli/components/board_list.py @@ -0,0 +1,137 @@ +# from __future__ import annotations + +from models import BoardList + +import flet as ft + +from .card import CardView + + +@ft.component +def BoardListView(board_list: BoardList, move_list): + is_list_over, set_is_list_over = ft.use_state(False) + is_end_over, set_is_end_over = ft.use_state(False) + new_card_text, set_new_card_text = ft.use_state("") + card_list: list[ft.Control] = [CardView(card) for card in board_list.cards] + + def on_add_card_click(_: ft.Event[ft.TextButton]): + if stripped := new_card_text.strip(): + board_list.add_card(stripped) + set_new_card_text("") + + def on_add_card_submit(_: ft.Event[ft.TextField]): + if stripped := new_card_text.strip(): + board_list.add_card(stripped) + set_new_card_text("") + + def on_delete(_: ft.Event[ft.PopupMenuItem]): + board_list.board.remove_list(board_list) + + def change_card_text(e: ft.Event[ft.TextField]): + set_new_card_text(e.control.value) + + def on_end_accept(e: ft.DragTargetEvent): + board_list.move_card_into(e.src.data) + set_is_end_over(False) + + def on_list_accept(e: ft.DragTargetEvent): + move_list(e.src.data, board_list) + set_is_list_over(False) + + return ft.Row( + spacing=4, + intrinsic_height=True, + controls=[ + ft.VerticalDivider( + color=ft.Colors.BLACK_54, + width=2, + thickness=2, + radius=2, + leading_indent=15, + trailing_indent=15, + opacity=1.0 if is_list_over else 0.0, + ), + ft.Draggable( + group="lists", + data=board_list, + content=ft.DragTarget( + group="cards", + data=board_list, + on_will_accept=lambda e: set_is_end_over(e.accept), + on_accept=on_end_accept, + on_leave=lambda: set_is_end_over(False), + content=ft.DragTarget( + group="lists", + data=board_list, + on_will_accept=lambda e: set_is_list_over( + e.accept and e.src.data != board_list + ), + on_accept=on_list_accept, + on_leave=lambda: set_is_list_over(False), + content=ft.Container( + border=ft.Border.all( + 2, + ( + ft.Colors.BLACK_38 + if is_list_over + else ft.Colors.BLACK_12 + ), + ), + border_radius=ft.BorderRadius.all(8), + bgcolor=board_list.color, + padding=ft.Padding.all(10), + width=240, + content=ft.Column( + spacing=6, + controls=[ + ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + controls=[ + ft.Text( + board_list.title, + theme_style=ft.TextThemeStyle.TITLE_MEDIUM, + ), + ft.PopupMenuButton( + items=[ + ft.PopupMenuItem( + content=ft.Text("Delete"), + on_click=on_delete, + ) + ] + ), + ], + ), + ft.TextField( + label="New card", + bgcolor=ft.Colors.WHITE, + value=new_card_text, + on_change=change_card_text, + on_submit=on_add_card_submit, + ), + ft.TextButton( + content="Add card", + icon=ft.Icons.ADD, + on_click=on_add_card_click, + ), + ft.Column( + spacing=2, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + *card_list, + ft.Divider( + color=ft.Colors.BLACK_38, + thickness=2, + height=2, + radius=2, + opacity=1.0 if is_end_over else 0.0, + ), + ], + ), + ], + ), + ), + ), + ), + ), + ], + ) diff --git a/sdk/python/examples/apps/trolli-declarative/src/components/boards.py b/sdk/python/examples/apps/declarative/trolli/components/boards.py similarity index 99% rename from sdk/python/examples/apps/trolli-declarative/src/components/boards.py rename to sdk/python/examples/apps/declarative/trolli/components/boards.py index cf1a3c5496..8229269b50 100644 --- a/sdk/python/examples/apps/trolli-declarative/src/components/boards.py +++ b/sdk/python/examples/apps/declarative/trolli/components/boards.py @@ -1,8 +1,9 @@ import asyncio +from models import TrolliState + import flet as ft -from models import TrolliState from .dialogs import show_new_board_dialog diff --git a/sdk/python/examples/apps/trolli-declarative/src/components/card.py b/sdk/python/examples/apps/declarative/trolli/components/card.py similarity index 100% rename from sdk/python/examples/apps/trolli-declarative/src/components/card.py rename to sdk/python/examples/apps/declarative/trolli/components/card.py index f7eee11a14..77811cbdce 100644 --- a/sdk/python/examples/apps/trolli-declarative/src/components/card.py +++ b/sdk/python/examples/apps/declarative/trolli/components/card.py @@ -1,7 +1,7 @@ -import flet as ft - from models import Card +import flet as ft + @ft.component def CardView(card: Card): diff --git a/sdk/python/examples/apps/trolli-declarative/src/components/dialogs.py b/sdk/python/examples/apps/declarative/trolli/components/dialogs.py similarity index 100% rename from sdk/python/examples/apps/trolli-declarative/src/components/dialogs.py rename to sdk/python/examples/apps/declarative/trolli/components/dialogs.py index 60c4f38880..9db842bb2c 100644 --- a/sdk/python/examples/apps/trolli-declarative/src/components/dialogs.py +++ b/sdk/python/examples/apps/declarative/trolli/components/dialogs.py @@ -1,13 +1,13 @@ from __future__ import annotations -import asyncio -from typing import cast +import asyncio from dataclasses import dataclass - -import flet as ft +from typing import cast from models import Board, TrolliState +import flet as ft + @dataclass(frozen=True) class _ColorOption: diff --git a/sdk/python/examples/apps/declarative/trolli/components/sidebar.py b/sdk/python/examples/apps/declarative/trolli/components/sidebar.py new file mode 100644 index 0000000000..e6292d5e1a --- /dev/null +++ b/sdk/python/examples/apps/declarative/trolli/components/sidebar.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +import asyncio + +from models import TrolliState + +import flet as ft + + +@ft.component +def Sidebar(app: TrolliState): + board_id = app.current_board_id + active_board_index = ( + next((i for i, b in enumerate(app.boards) if b.board_id == board_id), None) + if board_id is not None + else None + ) + top_index = ( + 0 + if app.active_screen == "boards" + else 1 + if app.active_screen == "members" + else None + ) + + def top_nav_change(e: ft.Event[ft.NavigationRail]): + if e.control.selected_index == 0: + asyncio.create_task(ft.context.page.push_route("/boards")) + # ft.context.page.go("/boards") + elif e.control.selected_index == 1: + asyncio.create_task(ft.context.page.push_route("/members")) + # ft.context.page.go("/members") + + def bottom_nav_change(e: ft.Event[ft.NavigationRail]): + idx = e.control.selected_index + if idx is None: + return + asyncio.create_task( + ft.context.page.push_route(f"/board/{app.boards[idx].board_id}") + ) + # ft.context.page.go(f"/board/{app.boards[idx].board_id}") + + return ft.Container( + width=200, + bgcolor=ft.Colors.BLUE_GREY, + padding=ft.Padding.all(15), + visible=app.nav_visible, + content=ft.Column( + tight=True, + controls=[ + ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + controls=[ft.Text("Workspace")], + ), + ft.Divider(height=1, thickness=1, color=ft.Colors.BLACK_26), + ft.NavigationRail( + selected_index=top_index, + label_type=ft.NavigationRailLabelType.ALL, + bgcolor=ft.Colors.BLUE_GREY, + extended=True, + height=110, + on_change=top_nav_change, + destinations=[ + ft.NavigationRailDestination( + label="Boards", + icon=ft.Icons.BOOK_OUTLINED, + selected_icon=ft.Icons.BOOK_OUTLINED, + ), + ft.NavigationRailDestination( + label="Members", + icon=ft.Icons.PERSON, + selected_icon=ft.Icons.PERSON, + ), + ], + ), + ft.Divider(height=1, thickness=1, color=ft.Colors.BLACK_26), + ft.NavigationRail( + selected_index=active_board_index, + label_type=ft.NavigationRailLabelType.ALL, + bgcolor=ft.Colors.BLUE_GREY, + extended=True, + expand=True, + on_change=bottom_nav_change, + destinations=[ + ft.NavigationRailDestination( + label=b.name, + icon=ft.Icons.CHEVRON_RIGHT_OUTLINED, + selected_icon=ft.Icons.CHEVRON_RIGHT_ROUNDED, + ) + for b in app.boards + ], + ), + ], + ), + ) diff --git a/sdk/python/examples/apps/declarative/trolli/main.py b/sdk/python/examples/apps/declarative/trolli/main.py new file mode 100644 index 0000000000..870b516b5e --- /dev/null +++ b/sdk/python/examples/apps/declarative/trolli/main.py @@ -0,0 +1,156 @@ +import asyncio +import logging +from typing import Optional + +from components import BoardsView, BoardView, Sidebar, TrolliAppBar +from models import TrolliState + +import flet as ft + +logging.basicConfig(level=logging.INFO) +logging.getLogger("flet_components").setLevel(logging.INFO) + + +def _init_demo_data(app: TrolliState) -> None: + if app.boards: + return + board = app.create_board("My First Board") + board.add_list("To Do", ft.Colors.AMBER_200) + board.add_list("Doing", ft.Colors.LIGHT_BLUE_200) + board.add_list("Done", ft.Colors.LIGHT_GREEN_200) + board.lists[0].add_card("Drag cards between lists") + board.lists[0].add_card("Drag lists to reorder") + board.lists[1].add_card("Add a list from the button") + + +@ft.component +def App(): + app, _ = ft.use_state(lambda: TrolliState(route=ft.context.page.route)) + ft.context.page.padding = 0 + ft.context.page.theme_mode = ft.ThemeMode.LIGHT + ft.context.page.fonts = {"Pacifico": "Pacifico-Regular.ttf"} + ft.context.page.bgcolor = ft.Colors.BLUE_GREY_200 + if ft.context.page.route == "/": + asyncio.create_task(ft.context.page.push_route("/boards")) + # ft.context.page.go("/boards") + + def parse_board_id_from_route(route: str) -> Optional[int]: + troute = ft.TemplateRoute(route) + if not troute.match("/board/:id"): + return None + + raw = getattr(troute, "id", None) + if not isinstance(raw, str): + return None + try: + return int(raw) + except ValueError: + return None + + def route_change(e: ft.RouteChangeEvent): + # Keep route as the single source of truth for navigation-related UI. + app.route = e.route + + if e.route.startswith("/board/"): + board_id = parse_board_id_from_route(e.route) + if board_id is None or app.get_board_by_id(board_id) is None: + asyncio.create_task(ft.context.page.push_route("/boards")) + app.active_screen = "boards" + app.current_board_id = None + app.route = "/boards" + return + + if e.route in ("/", "/boards"): + app.active_screen = "boards" + app.current_board_id = None + elif e.route == "/members": + app.active_screen = "members" + app.current_board_id = None + elif e.route == "/settings": + app.active_screen = "settings" + app.current_board_id = None + elif e.route.startswith("/board/"): + app.active_screen = "board" + app.current_board_id = board_id + else: + asyncio.create_task(ft.context.page.push_route("/boards")) + app.active_screen = "boards" + app.current_board_id = None + app.route = "/boards" + return + + ft.context.page.on_route_change = route_change + + def redirect_unknown_board(): + if not app.route.startswith("/board/"): + return + board_id = parse_board_id_from_route(app.route) + if board_id is None or app.get_board_by_id(board_id) is None: + asyncio.create_task(ft.context.page.push_route("/boards")) + + # ft.on_updated(redirect_unknown_board, [app.route]) + + ft.on_mounted(lambda: _init_demo_data(app)) + + def toggle_sidebar(_: ft.Event[ft.IconButton]): + app.nav_visible = not app.nav_visible + + board_id = parse_board_id_from_route(app.route) + board = app.get_board_by_id(board_id) if board_id is not None else None + + if app.active_screen == "boards": + content: ft.Control = BoardsView(app) + elif app.active_screen == "members": + content = ft.Text("Members view placeholder", align=ft.Alignment.CENTER) + elif app.active_screen == "settings": + content = ft.Text("Settings view placeholder", align=ft.Alignment.CENTER) + else: + board = ( + app.get_board_by_id(app.current_board_id) if app.current_board_id else None + ) + content = BoardView(board) if board else BoardsView(app) + + return ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + spacing=0, + controls=[ + TrolliAppBar(app), + ft.Row( + expand=True, + vertical_alignment=ft.CrossAxisAlignment.START, + controls=[ + Sidebar(app), + ft.Stack( + fit=ft.StackFit.LOOSE, + expand=True, + controls=[ + ft.Container( + padding=ft.Padding.only(left=10, right=10, top=10), + content=content, + ), + ft.IconButton( + icon=ft.Icons.ARROW_CIRCLE_LEFT, + selected=not app.nav_visible, + selected_icon=ft.Icons.ARROW_CIRCLE_RIGHT, + icon_color=ft.Colors.BLUE_GREY_400, + icon_size=20, + padding=0, + on_click=toggle_sidebar, + ), + ], + ), + ], + ), + ], + ), + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main, route_url_strategy="hash") diff --git a/sdk/python/examples/apps/trolli-declarative/src/models.py b/sdk/python/examples/apps/declarative/trolli/models.py similarity index 98% rename from sdk/python/examples/apps/trolli-declarative/src/models.py rename to sdk/python/examples/apps/declarative/trolli/models.py index 4c478413f1..0dbc6e31de 100644 --- a/sdk/python/examples/apps/trolli-declarative/src/models.py +++ b/sdk/python/examples/apps/declarative/trolli/models.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Optional, Literal +from typing import Literal, Optional import flet as ft diff --git a/sdk/python/examples/apps/declarative/trolli/pyproject.toml b/sdk/python/examples/apps/declarative/trolli/pyproject.toml new file mode 100644 index 0000000000..100de87539 --- /dev/null +++ b/sdk/python/examples/apps/declarative/trolli/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-trolli" +version = "1.0.0" +description = "Declarative Trello-style board app with draggable lists, cards, dialogs, and route-based navigation." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "kanban", "drag and drop", "routing", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative", "Apps/Productivity"] + +[tool.flet.metadata] +title = "Trolli" +controls = ["SafeArea", "Column", "Row", "Stack", "AppBar", "NavigationRail", "Draggable", "DragTarget", "TextField", "AlertDialog"] +layout_pattern = "dashboard" +complexity = "advanced" +features = ["drag and drop", "routing", "dialogs", "async", "kanban board"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/declarative/uncontrolled_textfield.py b/sdk/python/examples/apps/declarative/uncontrolled_textfield.py deleted file mode 100644 index 329750e491..0000000000 --- a/sdk/python/examples/apps/declarative/uncontrolled_textfield.py +++ /dev/null @@ -1,41 +0,0 @@ -import random -from typing import cast - -import flet as ft - - -@ft.component -def App(): - state, set_state = ft.use_state("") - - async def submit(e: ft.Event[ft.Button]): - e.page.show_dialog( - ft.AlertDialog( - title="Hello", - content=ft.Text(f"{first_name.value} {last_name.value}!"), - ) - ) - - def reset(): - set_state("reset" + str(random.randint(1, 1000))) - - return [ - first_name := ft.TextField( - label="First name", - ), - last_name := ft.TextField( - label="Last name", - ), - ft.Row( - cast( - list[ft.Control], - [ - ft.FilledButton("Submit", on_click=submit), - ft.FilledTonalButton("Reset", on_click=reset), - ], - ) - ), - ] - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/declarative/uncontrolled_textfield/main.py b/sdk/python/examples/apps/declarative/uncontrolled_textfield/main.py new file mode 100644 index 0000000000..41df167b20 --- /dev/null +++ b/sdk/python/examples/apps/declarative/uncontrolled_textfield/main.py @@ -0,0 +1,50 @@ +import random +from typing import cast + +import flet as ft + + +@ft.component +def App(): + state, set_state = ft.use_state("") + + async def submit(e: ft.Event[ft.Button]): + e.page.show_dialog( + ft.AlertDialog( + title="Hello", + content=ft.Text(f"{first_name.value} {last_name.value}!"), + ) + ) + + def reset(): + set_state("reset" + str(random.randint(1, 1000))) + + return ft.SafeArea( + content=ft.Column( + controls=[ + first_name := ft.TextField( + label="First name", + ), + last_name := ft.TextField( + label="Last name", + ), + ft.Row( + cast( + list[ft.Control], + [ + ft.FilledButton("Submit", on_click=submit), + ft.FilledTonalButton("Reset", on_click=reset), + ], + ) + ), + ] + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/declarative/uncontrolled_textfield/pyproject.toml b/sdk/python/examples/apps/declarative/uncontrolled_textfield/pyproject.toml new file mode 100644 index 0000000000..55f7fd3bbf --- /dev/null +++ b/sdk/python/examples/apps/declarative/uncontrolled_textfield/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-declarative-uncontrolled-textfield" +version = "1.0.0" +description = "Shows how uncontrolled text fields behave in a declarative form with submit and reset actions." +requires-python = ">=3.10" +keywords = ["apps", "declarative", "textfield", "form", "uncontrolled"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Declarative"] + +[tool.flet.metadata] +title = "Uncontrolled textfield" +controls = ["SafeArea", "Column", "Row", "TextField", "FilledButton", "FilledTonalButton", "AlertDialog"] +layout_pattern = "form" +complexity = "basic" +features = ["form submission", "uncontrolled inputs"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/flet_animation/main.py b/sdk/python/examples/apps/flet_animation/main.py index 7a1c80301a..e8a756732a 100644 --- a/sdk/python/examples/apps/flet_animation/main.py +++ b/sdk/python/examples/apps/flet_animation/main.py @@ -134,7 +134,15 @@ def assemble(e): page.horizontal_alignment = ft.CrossAxisAlignment.CENTER page.vertical_alignment = ft.MainAxisAlignment.CENTER page.spacing = 30 - page.add(canvas, go_button, again_button) + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[canvas, go_button, again_button], + ) + ) + ) -ft.run(main) +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/flet_animation/pyproject.toml b/sdk/python/examples/apps/flet_animation/pyproject.toml new file mode 100644 index 0000000000..0fd77a3a3f --- /dev/null +++ b/sdk/python/examples/apps/flet_animation/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-flet-animation" +version = "1.0.0" +description = "Animates scattered blocks into the FLET logo with randomized colors, size, and rotation." +requires-python = ">=3.10" +keywords = ["apps", "animation", "logo", "stack", "implicit animations"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Animation"] + +[tool.flet.metadata] +title = "Flet animation" +controls = ["SafeArea", "Column", "Stack", "Container", "Button"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["implicit animations", "randomized layout"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/flet_animation/requirements.txt b/sdk/python/examples/apps/flet_animation/requirements.txt deleted file mode 100644 index 9f5592458b..0000000000 --- a/sdk/python/examples/apps/flet_animation/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flet>=0.25.1 diff --git a/sdk/python/examples/apps/greeter/basic/main.py b/sdk/python/examples/apps/greeter/basic/main.py new file mode 100644 index 0000000000..645930807f --- /dev/null +++ b/sdk/python/examples/apps/greeter/basic/main.py @@ -0,0 +1,30 @@ +import flet as ft + + +def main(page: ft.Page): + greeting = ft.Text() + txt_name = ft.TextField(label="Your name") + + def btn_click(e): + if not txt_name.value: + txt_name.error_text = "Please enter your name" + greeting.value = "" + else: + txt_name.error_text = None + greeting.value = f"Hello, {txt_name.value}!" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + txt_name, + ft.Button("Say hello!", on_click=btn_click), + greeting, + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/greeter/basic/pyproject.toml b/sdk/python/examples/apps/greeter/basic/pyproject.toml new file mode 100644 index 0000000000..37d0d9c705 --- /dev/null +++ b/sdk/python/examples/apps/greeter/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-greeter-basic" +version = "1.0.0" +description = "Prompts for a name and greets the user while validating an empty text field." +requires-python = ">=3.10" +keywords = ["apps", "greeter", "form", "text field", "validation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Basic"] + +[tool.flet.metadata] +title = "Basic greeter" +controls = ["SafeArea", "Column", "TextField", "Button", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["form validation", "text updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/greeter/greeter.py b/sdk/python/examples/apps/greeter/greeter.py deleted file mode 100644 index 1fec7b5da5..0000000000 --- a/sdk/python/examples/apps/greeter/greeter.py +++ /dev/null @@ -1,19 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def btn_click(e): - if not txt_name.value: - txt_name.error_text = "Please enter your name" - page.update() - else: - name = txt_name.value - page.clean() - page.add(ft.Text(f"Hello, {name}!")) - - txt_name = ft.TextField(label="Your name") - - page.add(txt_name, ft.Button("Say hello!", on_click=btn_click)) - - -ft.run(main) diff --git a/sdk/python/examples/apps/greeter/requirements.txt b/sdk/python/examples/apps/greeter/requirements.txt deleted file mode 100644 index 9f5592458b..0000000000 --- a/sdk/python/examples/apps/greeter/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flet>=0.25.1 diff --git a/sdk/python/examples/apps/hello_world/basic/main.py b/sdk/python/examples/apps/hello_world/basic/main.py new file mode 100644 index 0000000000..4c60595784 --- /dev/null +++ b/sdk/python/examples/apps/hello_world/basic/main.py @@ -0,0 +1,9 @@ +import flet as ft + + +def main(page: ft.Page): + page.add(ft.SafeArea(content=ft.Text("Hello, world!"))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/hello_world/basic/pyproject.toml b/sdk/python/examples/apps/hello_world/basic/pyproject.toml new file mode 100644 index 0000000000..e5d35f22e1 --- /dev/null +++ b/sdk/python/examples/apps/hello_world/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-hello-world-basic" +version = "1.0.0" +description = "Minimal app that renders a single hello-world text message." +requires-python = ">=3.10" +keywords = ["apps", "hello world", "minimal", "text"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Basic"] + +[tool.flet.metadata] +title = "Hello world" +controls = ["SafeArea", "Text"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["minimal app"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/hello_world/hello.py b/sdk/python/examples/apps/hello_world/hello.py deleted file mode 100644 index c1f0bb7215..0000000000 --- a/sdk/python/examples/apps/hello_world/hello.py +++ /dev/null @@ -1,8 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add(ft.Text("Hello, world!")) - - -ft.run(main) diff --git a/sdk/python/examples/apps/hello_world/requirements.txt b/sdk/python/examples/apps/hello_world/requirements.txt deleted file mode 100644 index 9f5592458b..0000000000 --- a/sdk/python/examples/apps/hello_world/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flet>=0.25.1 diff --git a/sdk/python/examples/apps/icons_browser/Dockerfile b/sdk/python/examples/apps/icons_browser/Dockerfile deleted file mode 100644 index 2fe01b9780..0000000000 --- a/sdk/python/examples/apps/icons_browser/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM python:3-alpine - -WORKDIR /app - -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -EXPOSE 8080 - -CMD ["python", "./main.py"] diff --git a/sdk/python/examples/apps/icons_browser/README.md b/sdk/python/examples/apps/icons_browser/README.md deleted file mode 100644 index 636a05e423..0000000000 --- a/sdk/python/examples/apps/icons_browser/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Flet Icons Browser with deployment to Fly.io - -Deploy: - - flyctl deploy - -Check deployment: - - flyctl status - -Re-deploy: - - flyctl deploy --no-cache diff --git a/sdk/python/examples/apps/icons_browser/fly.toml b/sdk/python/examples/apps/icons_browser/fly.toml deleted file mode 100644 index 2809ceb716..0000000000 --- a/sdk/python/examples/apps/icons_browser/fly.toml +++ /dev/null @@ -1,40 +0,0 @@ -app = "flet-icons-browser" - -kill_signal = "SIGINT" -kill_timeout = 5 -processes = [] - -[env] - FLET_SERVER_PORT = "8080" - FLET_FORCE_WEB_SERVER = "true" - -[experimental] - allowed_public_ports = [] - auto_rollback = true - -[[services]] - http_checks = [] - internal_port = 8080 - processes = ["app"] - protocol = "tcp" - script_checks = [] - - [services.concurrency] - hard_limit = 25 - soft_limit = 20 - type = "connections" - - [[services.ports]] - force_https = true - handlers = ["http"] - port = 80 - - [[services.ports]] - handlers = ["tls", "http"] - port = 443 - - [[services.tcp_checks]] - grace_period = "1s" - interval = "15s" - restart_limit = 0 - timeout = "2s" diff --git a/sdk/python/examples/apps/icons_browser/main.py b/sdk/python/examples/apps/icons_browser/main.py index 577cc99100..836f24860f 100644 --- a/sdk/python/examples/apps/icons_browser/main.py +++ b/sdk/python/examples/apps/icons_browser/main.py @@ -147,30 +147,34 @@ async def display_icons(search_term: str): def main(page: ft.Page): page.title = "Flet icons browser" page.add( - ft.Tabs( - selected_index=0, - length=2, + ft.SafeArea( expand=True, - content=ft.Column( + content=ft.Tabs( + selected_index=0, + length=2, expand=True, - controls=[ - ft.TabBar( - tabs=[ - ft.Tab(label="Material"), - ft.Tab(label="Cupertino"), - ] - ), - ft.TabBarView( - expand=True, - controls=[ - IconBrowser(ft.Icons, expand=True), - IconBrowser(ft.CupertinoIcons, expand=True), - ], - ), - ], + content=ft.Column( + expand=True, + controls=[ + ft.TabBar( + tabs=[ + ft.Tab(label="Material"), + ft.Tab(label="Cupertino"), + ] + ), + ft.TabBarView( + expand=True, + controls=[ + IconBrowser(ft.Icons, expand=True), + IconBrowser(ft.CupertinoIcons, expand=True), + ], + ), + ], + ), ), ) ) -ft.run(main) +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/icons_browser/pyproject.toml b/sdk/python/examples/apps/icons_browser/pyproject.toml new file mode 100644 index 0000000000..d1f9c37b6a --- /dev/null +++ b/sdk/python/examples/apps/icons_browser/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-icons-browser" +version = "1.0.0" +description = "Searches Material and Cupertino icon sets and copies selected icon constants to the clipboard." +requires-python = ">=3.10" +keywords = ["apps", "icons", "search", "clipboard", "tabs", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Search"] + +[tool.flet.metadata] +title = "Icons browser" +controls = ["SafeArea", "Tabs", "TabBar", "TabBarView", "TextField", "IconButton", "GridView", "TextButton", "SnackBar"] +layout_pattern = "list-detail" +complexity = "intermediate" +features = ["icon search", "clipboard copy", "async"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/icons_browser/requirements.txt b/sdk/python/examples/apps/icons_browser/requirements.txt deleted file mode 100644 index 3a0e96024e..0000000000 --- a/sdk/python/examples/apps/icons_browser/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flet>=0.80.0 diff --git a/sdk/python/examples/apps/routing_navigation/__init__.py b/sdk/python/examples/apps/routing_navigation/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/apps/routing_navigation/building_views_on_route_change.py b/sdk/python/examples/apps/routing_navigation/building_views_on_route_change.py deleted file mode 100644 index 455cd7f38b..0000000000 --- a/sdk/python/examples/apps/routing_navigation/building_views_on_route_change.py +++ /dev/null @@ -1,73 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Routes Example" - - print("Initial route:", page.route) - - async def open_mail_settings(e): - await page.push_route("/settings/mail") - - async def open_settings(e): - await page.push_route("/settings") - - def route_change(): - print("Route change:", page.route) - page.views.clear() - page.views.append( - ft.View( - route="/", - controls=[ - ft.AppBar(title=ft.Text("Flet app")), - ft.Button("Go to settings", on_click=open_settings), - ], - ) - ) - if page.route == "/settings" or page.route == "/settings/mail": - page.views.append( - ft.View( - route="/settings", - controls=[ - ft.AppBar( - title=ft.Text("Settings"), - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, - ), - ft.Text("Settings!", theme_style=ft.TextThemeStyle.BODY_MEDIUM), - ft.Button( - content="Go to mail settings", - on_click=open_mail_settings, - ), - ], - ) - ) - if page.route == "/settings/mail": - page.views.append( - ft.View( - route="/settings/mail", - controls=[ - ft.AppBar( - title=ft.Text("Mail Settings"), - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, - ), - ft.Text("Mail settings!"), - ], - ) - ) - page.update() - - async def view_pop(e): - if e.view is not None: - print("View pop:", e.view) - page.views.remove(e.view) - top_view = page.views[-1] - await page.push_route(top_view.route) - - page.on_route_change = route_change - page.on_view_pop = view_pop - - route_change() - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/building_views_on_route_change/main.py b/sdk/python/examples/apps/routing_navigation/building_views_on_route_change/main.py new file mode 100644 index 0000000000..1831fb8f40 --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/building_views_on_route_change/main.py @@ -0,0 +1,94 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Routes Example" + + print("Initial route:", page.route) + + async def open_mail_settings(e): + await page.push_route("/settings/mail") + + async def open_settings(e): + await page.push_route("/settings") + + def route_change(): + print("Route change:", page.route) + page.views.clear() + page.views.append( + ft.View( + route="/", + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AppBar(title=ft.Text("Flet app")), + ft.Button("Go to settings", on_click=open_settings), + ] + ) + ) + ], + ) + ) + if page.route == "/settings" or page.route == "/settings/mail": + page.views.append( + ft.View( + route="/settings", + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AppBar( + title=ft.Text("Settings"), + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, + ), + ft.Text( + "Settings!", + theme_style=ft.TextThemeStyle.BODY_MEDIUM, + ), + ft.Button( + content="Go to mail settings", + on_click=open_mail_settings, + ), + ] + ) + ) + ], + ) + ) + if page.route == "/settings/mail": + page.views.append( + ft.View( + route="/settings/mail", + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AppBar( + title=ft.Text("Mail Settings"), + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, + ), + ft.Text("Mail settings!"), + ] + ) + ) + ], + ) + ) + page.update() + + async def view_pop(e): + if e.view is not None: + print("View pop:", e.view) + page.views.remove(e.view) + top_view = page.views[-1] + await page.push_route(top_view.route) + + page.on_route_change = route_change + page.on_view_pop = view_pop + + route_change() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/building_views_on_route_change/pyproject.toml b/sdk/python/examples/apps/routing_navigation/building_views_on_route_change/pyproject.toml new file mode 100644 index 0000000000..812e8b12c6 --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/building_views_on_route_change/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-routing-navigation-building-views-on-route-change" +version = "1.0.0" +description = "Builds a navigation stack from the current route and handles nested settings routes." +requires-python = ">=3.10" +keywords = ["apps", "routing", "navigation", "views", "route change"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Navigation"] + +[tool.flet.metadata] +title = "Building views on route change" +controls = ["View", "SafeArea", "Column", "AppBar", "Button", "Text"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["routing", "nested routes", "back navigation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/routing_navigation/drawer_navigation.py b/sdk/python/examples/apps/routing_navigation/drawer_navigation.py deleted file mode 100644 index cc85713e93..0000000000 --- a/sdk/python/examples/apps/routing_navigation/drawer_navigation.py +++ /dev/null @@ -1,109 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Drawer navigation" - - async def handle_change(e): - if e.control.selected_index == 0: - await page.push_route("/") - elif e.control.selected_index == 1: - await page.push_route("/store") - elif e.control.selected_index == 2: - await page.push_route("/about") - - def create_drawer(selected_index=0): - return ft.NavigationDrawer( - selected_index=selected_index, - on_change=handle_change, - controls=[ - ft.Container(height=12), - ft.NavigationDrawerDestination( - label="Home", - icon=ft.Icons.HOME_OUTLINED, - selected_icon=ft.Icon(ft.Icons.HOME), - ), - ft.Divider(thickness=2), - ft.NavigationDrawerDestination( - label="Store", - icon=ft.Icon(ft.Icons.STORE_OUTLINED), - selected_icon=ft.Icon(ft.Icons.STORE), - ), - ft.NavigationDrawerDestination( - label="About", - icon=ft.Icon(ft.Icons.PHONE_OUTLINED), - selected_icon=ft.Icons.PHONE, - ), - ], - ) - - async def show_drawer(): - await page.show_drawer() - - def route_change(route): - page.views.clear() - page.views.append( - ft.View( - route="/", - controls=[ - ft.AppBar( - title=ft.Text("Home", expand=True), - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, - leading=ft.IconButton(ft.Icons.MENU, on_click=show_drawer), - ), - ft.Text("Welcome to Home Page"), - ], - drawer=create_drawer(selected_index=0) - if page.route == "/" - else None, # add drawer only if home page is shown - ) - ) - - if page.route == "/store": - page.views.append( - ft.View( - route="/store", - controls=[ - ft.AppBar( - title=ft.Text("Store", expand=True), - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, - leading=ft.IconButton(ft.Icons.MENU, on_click=show_drawer), - automatically_imply_leading=False, - ), - ft.Text("Welcome to Store Page"), - ft.Button("Go About", on_click=lambda _: page.go("/about")), - ], - drawer=create_drawer(selected_index=1), - ) - ) - - if page.route == "/about": - page.views.append( - ft.View( - route="/about", - controls=[ - ft.AppBar( - title=ft.Text("About", expand=True), - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, - leading=ft.IconButton(ft.Icons.MENU, on_click=show_drawer), - automatically_imply_leading=False, - ), - ft.Text("Welcome to About Page"), - ft.Button("Go Store", on_click=lambda _: page.go("/store")), - ], - drawer=create_drawer(selected_index=2), - ) - ) - - async def view_pop(view): - page.views.pop() - top_view = page.views[-1] - await page.push_route(top_view.route) - - page.on_route_change = route_change - page.on_view_pop = view_pop - route_change(page.route) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/drawer_navigation/main.py b/sdk/python/examples/apps/routing_navigation/drawer_navigation/main.py new file mode 100644 index 0000000000..a378907d3a --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/drawer_navigation/main.py @@ -0,0 +1,143 @@ +import asyncio + +import flet as ft + + +def main(page: ft.Page): + page.title = "Drawer navigation" + + async def handle_change(e): + if e.control.selected_index == 0: + await page.push_route("/") + elif e.control.selected_index == 1: + await page.push_route("/store") + elif e.control.selected_index == 2: + await page.push_route("/about") + + def create_drawer(selected_index=0): + return ft.NavigationDrawer( + selected_index=selected_index, + on_change=handle_change, + controls=[ + ft.Container(height=12), + ft.NavigationDrawerDestination( + label="Home", + icon=ft.Icons.HOME_OUTLINED, + selected_icon=ft.Icon(ft.Icons.HOME), + ), + ft.Divider(thickness=2), + ft.NavigationDrawerDestination( + label="Store", + icon=ft.Icon(ft.Icons.STORE_OUTLINED), + selected_icon=ft.Icon(ft.Icons.STORE), + ), + ft.NavigationDrawerDestination( + label="About", + icon=ft.Icon(ft.Icons.PHONE_OUTLINED), + selected_icon=ft.Icons.PHONE, + ), + ], + ) + + async def show_drawer(): + await page.show_drawer() + + def route_change(route): + page.views.clear() + page.views.append( + ft.View( + route="/", + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AppBar( + title=ft.Text("Home", expand=True), + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, + leading=ft.IconButton( + ft.Icons.MENU, on_click=show_drawer + ), + ), + ft.Text("Welcome to Home Page"), + ] + ) + ) + ], + drawer=create_drawer(selected_index=0) if page.route == "/" else None, + ) + ) + + if page.route == "/store": + page.views.append( + ft.View( + route="/store", + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AppBar( + title=ft.Text("Store", expand=True), + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, + leading=ft.IconButton( + ft.Icons.MENU, on_click=show_drawer + ), + automatically_imply_leading=False, + ), + ft.Text("Welcome to Store Page"), + ft.Button( + "Go About", + on_click=lambda _: asyncio.create_task( + page.push_route("/about") + ), + ), + ] + ) + ) + ], + drawer=create_drawer(selected_index=1), + ) + ) + + if page.route == "/about": + page.views.append( + ft.View( + route="/about", + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AppBar( + title=ft.Text("About", expand=True), + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, + leading=ft.IconButton( + ft.Icons.MENU, on_click=show_drawer + ), + automatically_imply_leading=False, + ), + ft.Text("Welcome to About Page"), + ft.Button( + "Go Store", + on_click=lambda _: asyncio.create_task( + page.push_route("/store") + ), + ), + ] + ) + ) + ], + drawer=create_drawer(selected_index=2), + ) + ) + + async def view_pop(view): + page.views.pop() + top_view = page.views[-1] + await page.push_route(top_view.route) + + page.on_route_change = route_change + page.on_view_pop = view_pop + route_change(page.route) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/drawer_navigation/pyproject.toml b/sdk/python/examples/apps/routing_navigation/drawer_navigation/pyproject.toml new file mode 100644 index 0000000000..854b6c7fa6 --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/drawer_navigation/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-routing-navigation-drawer-navigation" +version = "1.0.0" +description = "Connects a navigation drawer to route changes across home, store, and about views." +requires-python = ">=3.10" +keywords = ["apps", "routing", "navigation drawer", "views", "destinations"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Navigation"] + +[tool.flet.metadata] +title = "Drawer navigation" +controls = ["View", "SafeArea", "Column", "AppBar", "NavigationDrawer", "NavigationDrawerDestination", "Button", "Text"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["routing", "drawer navigation", "back navigation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/routing_navigation/home_store.py b/sdk/python/examples/apps/routing_navigation/home_store.py deleted file mode 100644 index 32d368d9be..0000000000 --- a/sdk/python/examples/apps/routing_navigation/home_store.py +++ /dev/null @@ -1,56 +0,0 @@ -import asyncio - -import flet as ft - - -def main(page: ft.Page): - page.title = "Routes Example" - - def route_change(): - page.views.clear() - page.views.append( - ft.View( - route="/", - controls=[ - ft.AppBar( - title=ft.Text("Flet app"), bgcolor=ft.Colors.SURFACE_BRIGHT - ), - ft.Button( - "Visit Store", - on_click=lambda: asyncio.create_task(page.push_route("/store")), - ), - ], - ) - ) - if page.route == "/store": - page.views.append( - ft.View( - route="/store", - controls=[ - ft.AppBar( - title=ft.Text("Store"), bgcolor=ft.Colors.SURFACE_BRIGHT - ), - ft.Button( - "Go Home", - on_click=lambda: asyncio.create_task(page.push_route("/")), - ), - ], - ) - ) - page.update() - - async def view_pop(e): - if e.view is not None: - print("View pop:", e.view) - page.views.remove(e.view) - top_view = page.views[-1] - await page.push_route(top_view.route) - - page.on_route_change = route_change - page.on_view_pop = view_pop - - route_change() - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/home_store/main.py b/sdk/python/examples/apps/routing_navigation/home_store/main.py new file mode 100644 index 0000000000..8d673c6a90 --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/home_store/main.py @@ -0,0 +1,74 @@ +import asyncio + +import flet as ft + + +def main(page: ft.Page): + page.title = "Routes Example" + + def route_change(): + page.views.clear() + page.views.append( + ft.View( + route="/", + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AppBar( + title=ft.Text("Flet app"), + bgcolor=ft.Colors.SURFACE_BRIGHT, + ), + ft.Button( + "Visit Store", + on_click=lambda: asyncio.create_task( + page.push_route("/store") + ), + ), + ] + ) + ) + ], + ) + ) + if page.route == "/store": + page.views.append( + ft.View( + route="/store", + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AppBar( + title=ft.Text("Store"), + bgcolor=ft.Colors.SURFACE_BRIGHT, + ), + ft.Button( + "Go Home", + on_click=lambda: asyncio.create_task( + page.push_route("/") + ), + ), + ] + ) + ) + ], + ) + ) + page.update() + + async def view_pop(e): + if e.view is not None: + print("View pop:", e.view) + page.views.remove(e.view) + top_view = page.views[-1] + await page.push_route(top_view.route) + + page.on_route_change = route_change + page.on_view_pop = view_pop + + route_change() + + +if __name__ == "__main__": + ft.run(main, route_url_strategy="hash") diff --git a/sdk/python/examples/apps/routing_navigation/home_store/pyproject.toml b/sdk/python/examples/apps/routing_navigation/home_store/pyproject.toml new file mode 100644 index 0000000000..6af76600d3 --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/home_store/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-routing-navigation-home-store" +version = "1.0.0" +description = "Shows a minimal route-driven home and store flow using view stack navigation." +requires-python = ">=3.10" +keywords = ["apps", "routing", "navigation", "home", "store", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Navigation"] + +[tool.flet.metadata] +title = "Home store" +controls = ["View", "SafeArea", "Column", "AppBar", "Button"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["routing", "view stack", "async"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/routing_navigation/initial_route.py b/sdk/python/examples/apps/routing_navigation/initial_route.py deleted file mode 100644 index d8e4b49ab9..0000000000 --- a/sdk/python/examples/apps/routing_navigation/initial_route.py +++ /dev/null @@ -1,9 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add(ft.Text(f"Initial route: {page.route}")) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/initial_route/main.py b/sdk/python/examples/apps/routing_navigation/initial_route/main.py new file mode 100644 index 0000000000..40dfb1a96a --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/initial_route/main.py @@ -0,0 +1,9 @@ +import flet as ft + + +def main(page: ft.Page): + page.add(ft.SafeArea(content=ft.Text(f"Initial route: {page.route}"))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/initial_route/pyproject.toml b/sdk/python/examples/apps/routing_navigation/initial_route/pyproject.toml new file mode 100644 index 0000000000..2070722e02 --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/initial_route/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-routing-navigation-initial-route" +version = "1.0.0" +description = "Displays the initial route string passed into a Flet app." +requires-python = ">=3.10" +keywords = ["apps", "routing", "route", "text", "minimal"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Navigation"] + +[tool.flet.metadata] +title = "Initial route" +controls = ["SafeArea", "Text"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["routing basics"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/routing_navigation/pop_view_confirm.py b/sdk/python/examples/apps/routing_navigation/pop_view_confirm.py deleted file mode 100644 index dfe8fca43d..0000000000 --- a/sdk/python/examples/apps/routing_navigation/pop_view_confirm.py +++ /dev/null @@ -1,81 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Routes Example" - - def route_change(): - page.views.clear() - page.views.append(MainView("/")) - if page.route == "/store": - page.views.append(PermissionView("/store")) - page.update() - - async def view_pop(e: ft.ViewPopEvent): - if e.view is not None: - print("View pop:", e.view) - page.views.remove(e.view) - top_view = page.views[-1] - await page.push_route(top_view.route) - - page.on_route_change = route_change - page.on_view_pop = view_pop - - route_change() - - -class MainView(ft.View): - def __init__(self, path): - super().__init__( - route=path, - appbar=ft.AppBar(title=ft.Text("Flet app")), - controls=[ - ft.Button("Go to store", on_click=self.open_store), - ], - ) - - async def open_store(self, e): - await self.page.push_route("/store") - - -class PermissionView(ft.View): - def __init__(self, path): - super().__init__( - route=path, - appbar=ft.AppBar(title=ft.Text(f"{path} View")), - can_pop=False, - on_confirm_pop=self.ask_pop_permission, - ) - - async def ask_pop_permission(self, e): - async def on_dlg_yes(e): - self.page.pop_dialog() - await self.confirm_pop(True) - - async def on_dlg_no(e): - self.page.pop_dialog() - await self.confirm_pop(False) - - dlg_modal = ft.AlertDialog( - title=ft.Text("Please confirm"), - content=ft.Text("Go home?"), - actions=[ - ft.TextButton( - "Yes", - on_click=on_dlg_yes, - ), - ft.TextButton( - "No", - on_click=on_dlg_no, - ), - ], - actions_alignment=ft.MainAxisAlignment.END, - on_dismiss=lambda e: print("Modal dialog dismissed!"), - ) - - self.page.show_dialog(dlg_modal) - # await self.confirm_pop(True) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/pop_view_confirm/main.py b/sdk/python/examples/apps/routing_navigation/pop_view_confirm/main.py new file mode 100644 index 0000000000..d4a0b096ff --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/pop_view_confirm/main.py @@ -0,0 +1,92 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Routes Example" + + def route_change(): + page.views.clear() + page.views.append(MainView("/")) + if page.route == "/store": + page.views.append(PermissionView("/store")) + page.update() + + async def view_pop(e: ft.ViewPopEvent): + if e.view is not None: + print("View pop:", e.view) + page.views.remove(e.view) + top_view = page.views[-1] + await page.push_route(top_view.route) + + page.on_route_change = route_change + page.on_view_pop = view_pop + + route_change() + + +class MainView(ft.View): + def __init__(self, path): + super().__init__( + route=path, + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AppBar(title=ft.Text("Flet app")), + ft.Button("Go to store", on_click=self.open_store), + ] + ) + ) + ], + ) + + async def open_store(self, e): + await self.page.push_route("/store") + + +class PermissionView(ft.View): + def __init__(self, path): + super().__init__( + route=path, + controls=[ + ft.SafeArea( + content=ft.Column( + controls=[ft.AppBar(title=ft.Text(f"{path} View"))] + ) + ) + ], + can_pop=False, + on_confirm_pop=self.ask_pop_permission, + ) + + async def ask_pop_permission(self, e): + async def on_dlg_yes(e): + self.page.pop_dialog() + await self.confirm_pop(True) + + async def on_dlg_no(e): + self.page.pop_dialog() + await self.confirm_pop(False) + + dlg_modal = ft.AlertDialog( + title=ft.Text("Please confirm"), + content=ft.Text("Go home?"), + actions=[ + ft.TextButton( + "Yes", + on_click=on_dlg_yes, + ), + ft.TextButton( + "No", + on_click=on_dlg_no, + ), + ], + actions_alignment=ft.MainAxisAlignment.END, + on_dismiss=lambda e: print("Modal dialog dismissed!"), + ) + + self.page.show_dialog(dlg_modal) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/pop_view_confirm/pyproject.toml b/sdk/python/examples/apps/routing_navigation/pop_view_confirm/pyproject.toml new file mode 100644 index 0000000000..191984d07f --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/pop_view_confirm/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-routing-navigation-pop-view-confirm" +version = "1.0.0" +description = "Confirms whether a view is allowed to pop before returning from the store route." +requires-python = ">=3.10" +keywords = ["apps", "routing", "navigation", "confirmation dialog", "view pop"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Navigation"] + +[tool.flet.metadata] +title = "Pop view confirm" +controls = ["View", "SafeArea", "Column", "AppBar", "Button", "AlertDialog", "TextButton", "Text"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["routing", "confirmation dialog", "back navigation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/routing_navigation/route_change_event.py b/sdk/python/examples/apps/routing_navigation/route_change_event.py deleted file mode 100644 index 80fc71f9e3..0000000000 --- a/sdk/python/examples/apps/routing_navigation/route_change_event.py +++ /dev/null @@ -1,15 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add(ft.Text(f"Initial route: {page.route}")) - - def route_change(e): - page.add(ft.Text(f"New route: {e.route}")) - - page.on_route_change = route_change - page.update() - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/route_change_event/main.py b/sdk/python/examples/apps/routing_navigation/route_change_event/main.py new file mode 100644 index 0000000000..c18bfb0cda --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/route_change_event/main.py @@ -0,0 +1,16 @@ +import flet as ft + + +def main(page: ft.Page): + route_log = ft.Column(controls=[ft.Text(f"Initial route: {page.route}")]) + page.add(ft.SafeArea(content=route_log)) + + def route_change(e): + route_log.controls.append(ft.Text(f"New route: {e.route}")) + + page.on_route_change = route_change + page.update() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/routing_navigation/route_change_event/pyproject.toml b/sdk/python/examples/apps/routing_navigation/route_change_event/pyproject.toml new file mode 100644 index 0000000000..6721e3f80b --- /dev/null +++ b/sdk/python/examples/apps/routing_navigation/route_change_event/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-routing-navigation-route-change-event" +version = "1.0.0" +description = "Logs each route change by appending the new route string to the page." +requires-python = ">=3.10" +keywords = ["apps", "routing", "events", "route change", "text"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Navigation"] + +[tool.flet.metadata] +title = "Route change event" +controls = ["SafeArea", "Text"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["routing basics", "route change event"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/timer/basic/main.py b/sdk/python/examples/apps/timer/basic/main.py new file mode 100644 index 0000000000..1db6e6adab --- /dev/null +++ b/sdk/python/examples/apps/timer/basic/main.py @@ -0,0 +1,113 @@ +import asyncio +import time + +import flet as ft + + +def format_hhmmss(seconds: int) -> str: + """Convert elapsed seconds into HH:MM:SS string.""" + h = seconds // 3600 + m = (seconds % 3600) // 60 + s = seconds % 60 + return f"{h:02}:{m:02}:{s:02}" + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + running = False + paused = False + elapsed = 0 + base_elapsed = 0 + started_at = 0.0 + + def sync_ui(): + nonlocal elapsed + timer.value = format_hhmmss(elapsed) + + if running and not paused: + toggle_btn.text = "Pause" + toggle_btn.icon = ft.Icons.PAUSE + else: + toggle_btn.text = "Start" + toggle_btn.icon = ft.Icons.PLAY_ARROW + + stop_btn.disabled = (not running) and (elapsed == 0) + page.update() + + async def ticker(): + nonlocal elapsed + while running: + if not paused: + elapsed = base_elapsed + int(time.time() - started_at) + timer.value = format_hhmmss(elapsed) + timer.update() + await asyncio.sleep(1) + + def handle_toggle(): + nonlocal running, paused, elapsed, base_elapsed, started_at + + if not running: + running = True + paused = False + base_elapsed = elapsed + started_at = time.time() + page.run_task(ticker) + sync_ui() + return + + if not paused: + base_elapsed += int(time.time() - started_at) + elapsed = base_elapsed + paused = True + sync_ui() + return + + paused = False + started_at = time.time() + sync_ui() + + def handle_stop(): + nonlocal running, paused, elapsed, base_elapsed, started_at + running = False + paused = False + elapsed = 0 + base_elapsed = 0 + started_at = 0.0 + sync_ui() + + page.add( + ft.SafeArea( + content=ft.Column( + spacing=20, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + timer := ft.Text("00:00:00", size=30, weight=ft.FontWeight.BOLD), + ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + toggle_btn := ft.FilledButton( + "Start", + icon=ft.Icons.PLAY_ARROW, + on_click=handle_toggle, + ), + stop_btn := ft.TextButton( + "Stop", + icon=ft.Icons.STOP, + disabled=True, + on_click=handle_stop, + ), + ], + ), + ], + ) + ) + ) + + sync_ui() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/timer/basic/pyproject.toml b/sdk/python/examples/apps/timer/basic/pyproject.toml new file mode 100644 index 0000000000..5bbd2875d6 --- /dev/null +++ b/sdk/python/examples/apps/timer/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-timer-basic" +version = "1.0.0" +description = "Stopwatch example with start, pause, resume, and stop controls." +requires-python = ">=3.10" +keywords = ["apps", "timer", "stopwatch", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Time"] + +[tool.flet.metadata] +title = "Basic timer" +controls = ["SafeArea", "Column", "Row", "Text", "FilledButton", "TextButton"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["async", "timer controls"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/timer/declarative.py b/sdk/python/examples/apps/timer/declarative.py deleted file mode 100644 index b160e0dd9d..0000000000 --- a/sdk/python/examples/apps/timer/declarative.py +++ /dev/null @@ -1,135 +0,0 @@ -import asyncio -import time -from dataclasses import dataclass - -import flet as ft - - -def format_hhmmss(seconds: int) -> str: - """Format elapsed seconds as HH:MM:SS.""" - h = seconds // 3600 - m = (seconds % 3600) // 60 - s = seconds % 60 - return f"{h:02}:{m:02}:{s:02}" - - -@ft.observable -@dataclass -class TimerState: - """ - Declarative timer state. - - This class is the single source of truth for the UI. - Any mutation of its public fields automatically triggers a re-render. - """ - - running: bool = False - paused: bool = False - elapsed: int = 0 # seconds shown in UI - - # Internals - _base_elapsed: int = 0 # accumulated time before the current run - _started_at: float = 0.0 # wall-clock start time - _task: asyncio.Task | None = None # background ticker task - - async def _ticker(self): - """ - Background task that updates elapsed time once per second - while the timer is running. - """ - while self.running: - if not self.paused: - self.elapsed = self._base_elapsed + int(time.time() - self._started_at) - await asyncio.sleep(1) - - # Task cleanup when stopped - self._task = None - - def toggle(self): - """ - Toggle button handler: - - stopped → start - - running → pause - - paused → resume - """ - # stopped → start - if not self.running: - self.running = True - self.paused = False - self._base_elapsed = self.elapsed - self._started_at = time.time() - - # Ensure only one ticker task runs - if self._task is None or self._task.done(): - self._task = asyncio.create_task(self._ticker()) - return - - # running → pause - if not self.paused: - self._base_elapsed += int(time.time() - self._started_at) - self.elapsed = self._base_elapsed - self.paused = True - return - - # paused → resume - self.paused = False - self._started_at = time.time() - - def stop(self): - """Stop the timer and reset all state.""" - self.running = False - self.paused = False - self.elapsed = 0 - self._base_elapsed = 0 - self._started_at = 0.0 - - -@ft.component -def App(): - state, _ = ft.use_state(TimerState()) - - # Button appearance derived entirely from state - label = "Pause" if state.running and not state.paused else "Start" - icon = ft.Icons.PAUSE if state.running and not state.paused else ft.Icons.PLAY_ARROW - - return ft.SafeArea( - ft.Column( - spacing=20, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Text( - format_hhmmss(state.elapsed), - size=30, - weight=ft.FontWeight.BOLD, - ), - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.FilledButton( - label, - icon=icon, - on_click=state.toggle, - ), - ft.TextButton( - "Stop", - icon=ft.Icons.STOP, - on_click=state.stop, - disabled=not state.running and state.elapsed == 0, - ), - ], - ), - ], - ) - ) - - -def main(page: ft.Page): - """Application entry point.""" - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.render(App) - - -ft.run(main) diff --git a/sdk/python/examples/apps/timer/imperative.py b/sdk/python/examples/apps/timer/imperative.py deleted file mode 100644 index 46254fbdb1..0000000000 --- a/sdk/python/examples/apps/timer/imperative.py +++ /dev/null @@ -1,133 +0,0 @@ -import asyncio -import time - -import flet as ft - - -def format_hhmmss(seconds: int) -> str: - """Convert elapsed seconds into HH:MM:SS string.""" - h = seconds // 3600 - m = (seconds % 3600) // 60 - s = seconds % 60 - return f"{h:02}:{m:02}:{s:02}" - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - # State variables - running = False - paused = False - elapsed = 0 # seconds shown in the UI - base_elapsed = 0 # accumulated time before the current run - started_at = 0.0 # wall-clock start timestamp - - def sync_ui(): - """Synchronize UI controls with current state variables.""" - timer.value = format_hhmmss(elapsed) - - # Toggle button switches appearance based on state - if running and not paused: - toggle_btn.text = "Pause" - toggle_btn.icon = ft.Icons.PAUSE - else: - toggle_btn.text = "Start" - toggle_btn.icon = ft.Icons.PLAY_ARROW - - # Stop button only enabled when there is something to stop/reset - stop_btn.disabled = (not running) and (elapsed == 0) - - page.update() - - async def ticker(): - """ - Background task that updates elapsed time once per second - while the timer is running. - """ - nonlocal elapsed - while running: - if not paused: - elapsed = base_elapsed + int(time.time() - started_at) - timer.value = format_hhmmss(elapsed) - timer.update() - await asyncio.sleep(1) - - def handle_toggle(): - """ - Toggle button handler: - - stopped → start - - running → pause - - paused → resume - """ - nonlocal running, paused, elapsed, base_elapsed, started_at - - # stopped → start - if not running: - running = True - paused = False - base_elapsed = elapsed - started_at = time.time() - - # Start background ticker task - page.run_task(ticker) - sync_ui() - return - - # running → pause - if not paused: - base_elapsed += int(time.time() - started_at) - elapsed = base_elapsed - paused = True - sync_ui() - return - - # paused → resume - paused = False - started_at = time.time() - sync_ui() - - def handle_stop(): - """Stop the timer and reset it to 00:00:00.""" - nonlocal running, paused, elapsed, base_elapsed, started_at - running = False - paused = False - elapsed = 0 - base_elapsed = 0 - started_at = 0.0 - sync_ui() - - page.add( - ft.SafeArea( - ft.Column( - spacing=20, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - timer := ft.Text("00:00:00", size=30, weight=ft.FontWeight.BOLD), - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - toggle_btn := ft.FilledButton( - "Start", - icon=ft.Icons.PLAY_ARROW, - on_click=handle_toggle, - ), - stop_btn := ft.TextButton( - "Stop", - icon=ft.Icons.STOP, - disabled=True, - on_click=handle_stop, - ), - ], - ), - ], - ) - ) - ) - - # Initial UI sync - sync_ui() - - -ft.run(main) diff --git a/sdk/python/examples/apps/todo/Dockerfile b/sdk/python/examples/apps/todo/Dockerfile deleted file mode 100644 index 28e8fb638c..0000000000 --- a/sdk/python/examples/apps/todo/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM python:3-alpine - -WORKDIR /app - -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -EXPOSE 8080 - -CMD ["python", "./todo.py"] diff --git a/sdk/python/examples/apps/todo/basic/main.py b/sdk/python/examples/apps/todo/basic/main.py new file mode 100644 index 0000000000..39c6ec80ce --- /dev/null +++ b/sdk/python/examples/apps/todo/basic/main.py @@ -0,0 +1,168 @@ +import flet as ft + + +class Task(ft.Column): + def __init__(self, task_name, task_delete): + super().__init__() + self.completed = False + self.task_name = task_name + self.task_delete = task_delete + + def build(self): + self.display_task = ft.Checkbox( + value=False, label=self.task_name, on_change=self.status_changed + ) + self.edit_name = ft.TextField(expand=1) + + self.display_view = ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + vertical_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + self.display_task, + ft.Row( + spacing=0, + controls=[ + ft.IconButton( + icon=ft.Icons.CREATE_OUTLINED, + tooltip="Edit To-Do", + on_click=self.edit_clicked, + ), + ft.IconButton( + ft.Icons.DELETE_OUTLINE, + tooltip="Delete To-Do", + on_click=self.delete_clicked, + ), + ], + ), + ], + ) + + self.edit_view = ft.Row( + visible=False, + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + vertical_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + self.edit_name, + ft.IconButton( + icon=ft.Icons.DONE_OUTLINE_OUTLINED, + icon_color=ft.Colors.GREEN, + tooltip="Update To-Do", + on_click=self.save_clicked, + ), + ], + ) + self.controls = [self.display_view, self.edit_view] + + def edit_clicked(self, e): + self.edit_name.value = self.display_task.label + self.display_view.visible = False + self.edit_view.visible = True + + def save_clicked(self, e): + self.display_task.label = self.edit_name.value + self.display_view.visible = True + self.edit_view.visible = False + + def status_changed(self, e): + self.completed = self.display_task.value + + def delete_clicked(self, e): + self.task_delete(self) + + +class TodoApp(ft.Column): + def build(self): + self.new_task = ft.TextField( + hint_text="What needs to be done?", on_submit=self.add_clicked, expand=True + ) + self.tasks = ft.Column() + + self.filter = ft.TabBar( + scrollable=False, + tabs=[ + ft.Tab(label="all"), + ft.Tab(label="active"), + ft.Tab(label="completed"), + ], + ) + + self.filter_tabs = ft.Tabs( + length=3, + selected_index=0, + on_change=lambda e: self.update(), + content=self.filter, + ) + + self.items_left = ft.Text("0 items left") + + self.width = 600 + self.controls = [ + ft.Row( + [ft.Text(value="Todos", theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM)], + alignment=ft.MainAxisAlignment.CENTER, + ), + ft.Row( + controls=[ + self.new_task, + ft.FloatingActionButton( + icon=ft.Icons.ADD, on_click=self.add_clicked + ), + ], + ), + ft.Column( + spacing=25, + controls=[ + self.filter_tabs, + self.tasks, + ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + vertical_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + self.items_left, + ft.OutlinedButton( + content="Clear completed", on_click=self.clear_clicked + ), + ], + ), + ], + ), + ] + + async def add_clicked(self, e): + if self.new_task.value: + task = Task(self.new_task.value, self.task_delete) + self.tasks.controls.append(task) + self.new_task.value = "" + await self.new_task.focus() + + def task_delete(self, task): + self.tasks.controls.remove(task) + + def clear_clicked(self, e): + for task in self.tasks.controls[:]: + if task.completed: + self.task_delete(task) + + def before_update(self): + status = self.filter.tabs[self.filter_tabs.selected_index].label + count = 0 + for task in self.tasks.controls: + task.visible = ( + status == "all" + or (status == "active" and not task.completed) + or (status == "completed" and task.completed) + ) + if not task.completed: + count += 1 + self.items_left.value = f"{count} active item(s) left" + + +def main(page: ft.Page): + page.title = "ToDo App" + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.scroll = ft.ScrollMode.ADAPTIVE + page.add(ft.SafeArea(content=TodoApp())) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/apps/todo/basic/pyproject.toml b/sdk/python/examples/apps/todo/basic/pyproject.toml new file mode 100644 index 0000000000..812157e33e --- /dev/null +++ b/sdk/python/examples/apps/todo/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "apps-todo-basic" +version = "1.0.0" +description = "Classic to-do app with add, edit, delete, and filter interactions inspired by TodoMVC." +requires-python = ">=3.10" +keywords = ["apps", "todo", "tasks", "tabs", "forms"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Apps/Productivity"] + +[tool.flet.metadata] +title = "Basic to-do" +controls = ["SafeArea", "Column", "Row", "TextField", "Tabs", "TabBar", "Checkbox", "IconButton", "FloatingActionButton", "OutlinedButton", "Text"] +layout_pattern = "form" +complexity = "intermediate" +features = ["task editing", "task filtering", "list state updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/apps/todo/fly.toml b/sdk/python/examples/apps/todo/fly.toml deleted file mode 100644 index 7a3b63726f..0000000000 --- a/sdk/python/examples/apps/todo/fly.toml +++ /dev/null @@ -1,40 +0,0 @@ -app = "flet-todo" - -kill_signal = "SIGINT" -kill_timeout = 5 -processes = [] - -[env] - FLET_SERVER_PORT = "8080" - FLET_FORCE_WEB_SERVER = "true" - -[experimental] - allowed_public_ports = [] - auto_rollback = true - -[[services]] - http_checks = [] - internal_port = 8080 - processes = ["app"] - protocol = "tcp" - script_checks = [] - - [services.concurrency] - hard_limit = 25 - soft_limit = 20 - type = "connections" - - [[services.ports]] - force_https = true - handlers = ["http"] - port = 80 - - [[services.ports]] - handlers = ["tls", "http"] - port = 443 - - [[services.tcp_checks]] - grace_period = "1s" - interval = "15s" - restart_limit = 0 - timeout = "2s" diff --git a/sdk/python/examples/apps/todo/requirements.txt b/sdk/python/examples/apps/todo/requirements.txt deleted file mode 100644 index 9f5592458b..0000000000 --- a/sdk/python/examples/apps/todo/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flet>=0.25.1 diff --git a/sdk/python/examples/apps/todo/todo.py b/sdk/python/examples/apps/todo/todo.py deleted file mode 100644 index 05cf7f7b19..0000000000 --- a/sdk/python/examples/apps/todo/todo.py +++ /dev/null @@ -1,170 +0,0 @@ -import flet as ft - - -class Task(ft.Column): - def __init__(self, task_name, task_delete): - super().__init__() - self.completed = False - self.task_name = task_name - self.task_delete = task_delete - - def build(self): - self.display_task = ft.Checkbox( - value=False, label=self.task_name, on_change=self.status_changed - ) - self.edit_name = ft.TextField(expand=1) - - self.display_view = ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - vertical_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - self.display_task, - ft.Row( - spacing=0, - controls=[ - ft.IconButton( - icon=ft.Icons.CREATE_OUTLINED, - tooltip="Edit To-Do", - on_click=self.edit_clicked, - ), - ft.IconButton( - ft.Icons.DELETE_OUTLINE, - tooltip="Delete To-Do", - on_click=self.delete_clicked, - ), - ], - ), - ], - ) - - self.edit_view = ft.Row( - visible=False, - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - vertical_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - self.edit_name, - ft.IconButton( - icon=ft.Icons.DONE_OUTLINE_OUTLINED, - icon_color=ft.Colors.GREEN, - tooltip="Update To-Do", - on_click=self.save_clicked, - ), - ], - ) - self.controls = [self.display_view, self.edit_view] - - def edit_clicked(self, e): - self.edit_name.value = self.display_task.label - self.display_view.visible = False - self.edit_view.visible = True - - def save_clicked(self, e): - self.display_task.label = self.edit_name.value - self.display_view.visible = True - self.edit_view.visible = False - - def status_changed(self, e): - self.completed = self.display_task.value - - def delete_clicked(self, e): - self.task_delete(self) - - -class TodoApp(ft.Column): - # application's root control is a Column containing all other controls - def build(self): - self.new_task = ft.TextField( - hint_text="What needs to be done?", on_submit=self.add_clicked, expand=True - ) - self.tasks = ft.Column() - - self.filter = ft.TabBar( - scrollable=False, - tabs=[ - ft.Tab(label="all"), - ft.Tab(label="active"), - ft.Tab(label="completed"), - ], - ) - - self.filter_tabs = ft.Tabs( - length=3, - selected_index=0, - on_change=lambda e: self.update(), - content=self.filter, - ) - - self.items_left = ft.Text("0 items left") - - self.width = 600 - self.controls = [ - ft.Row( - [ft.Text(value="Todos", theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM)], - alignment=ft.MainAxisAlignment.CENTER, - ), - ft.Row( - controls=[ - self.new_task, - ft.FloatingActionButton( - icon=ft.Icons.ADD, on_click=self.add_clicked - ), - ], - ), - ft.Column( - spacing=25, - controls=[ - self.filter_tabs, - self.tasks, - ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - vertical_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - self.items_left, - ft.OutlinedButton( - content="Clear completed", on_click=self.clear_clicked - ), - ], - ), - ], - ), - ] - - async def add_clicked(self, e): - if self.new_task.value: - task = Task(self.new_task.value, self.task_delete) - self.tasks.controls.append(task) - self.new_task.value = "" - await self.new_task.focus() - - def task_delete(self, task): - self.tasks.controls.remove(task) - - def clear_clicked(self, e): - for task in self.tasks.controls[:]: - if task.completed: - self.task_delete(task) - - def before_update(self): - status = self.filter.tabs[self.filter_tabs.selected_index].label - count = 0 - for task in self.tasks.controls: - task.visible = ( - status == "all" - or (status == "active" and not task.completed) - or (status == "completed" and task.completed) - ) - if not task.completed: - count += 1 - self.items_left.value = f"{count} active item(s) left" - - -def main(page: ft.Page): - page.title = "ToDo App" - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.scroll = ft.ScrollMode.ADAPTIVE - - # create app control and add it to the page - page.add(TodoApp()) - - -ft.run(main) diff --git a/sdk/python/examples/apps/trolli-declarative/.gitignore b/sdk/python/examples/apps/trolli-declarative/.gitignore deleted file mode 100644 index b6e47617de..0000000000 --- a/sdk/python/examples/apps/trolli-declarative/.gitignore +++ /dev/null @@ -1,129 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ diff --git a/sdk/python/examples/apps/trolli-declarative/pyproject.toml b/sdk/python/examples/apps/trolli-declarative/pyproject.toml deleted file mode 100644 index 515d227d91..0000000000 --- a/sdk/python/examples/apps/trolli-declarative/pyproject.toml +++ /dev/null @@ -1,28 +0,0 @@ -[project] -name = "trolli-declarative-codex" -version = "0.1.0" -description = "" -readme = "README.md" -requires-python = ">=3.13" -dependencies = [ - "flet==0.81.0" -] - - -[tool.flet] -# org name in reverse domain name notation, e.g. "com.mycompany". -# Combined with project.name to build bundle ID for iOS and Android apps -org = "com.mycompany" - -# project display name that is used as an app title on Android and iOS home screens, -# shown in window titles and about app dialogs on desktop. -product = "trolli-declarative-codex" - -# company name to display in about app dialogs -company = "Flet" - -# copyright text to display in about app dialogs -copyright = "Copyright (C) 2024 by Flet" - -[tool.flet.app] -path = "src" diff --git a/sdk/python/examples/apps/trolli-declarative/src/components/__init__.py b/sdk/python/examples/apps/trolli-declarative/src/components/__init__.py deleted file mode 100644 index 2696d2187b..0000000000 --- a/sdk/python/examples/apps/trolli-declarative/src/components/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .app_bar import TrolliAppBar -from .board import BoardView -from .boards import BoardsView -from .sidebar import Sidebar - -__all__ = [ - "BoardView", - "BoardsView", - "Sidebar", - "TrolliAppBar", -] - diff --git a/sdk/python/examples/apps/trolli-declarative/src/components/board.py b/sdk/python/examples/apps/trolli-declarative/src/components/board.py deleted file mode 100644 index d99f8a25e4..0000000000 --- a/sdk/python/examples/apps/trolli-declarative/src/components/board.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import annotations - -import flet as ft - -from models import Board -from .board_list import BoardListView -from .dialogs import show_new_list_dialog - - -@ft.component -def BoardView(board: Board): - return ft.Column( - expand=True, - spacing=10, - controls=[ - ft.Row( - # height=30, - expand_loose=True, - alignment=ft.MainAxisAlignment.END, - controls=[ - ft.FilledButton( - "Add list", - icon=ft.Icons.ADD, - on_click=lambda _: show_new_list_dialog(board), - ), - ], - ), - ft.Row( - alignment=ft.MainAxisAlignment.START, - controls=[ - ft.Text(board.name, theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM), - ], - ), - ft.Row( - expand=True, - scroll=ft.ScrollMode.AUTO, - vertical_alignment=ft.CrossAxisAlignment.START, - controls=[ - *[ - BoardListView(bl, move_list=board.move_list) - for bl in board.lists - ], - ], - ), - ], - ) diff --git a/sdk/python/examples/apps/trolli-declarative/src/components/board_list.py b/sdk/python/examples/apps/trolli-declarative/src/components/board_list.py deleted file mode 100644 index 1c00cb8950..0000000000 --- a/sdk/python/examples/apps/trolli-declarative/src/components/board_list.py +++ /dev/null @@ -1,136 +0,0 @@ -# from __future__ import annotations - -import flet as ft - -from models import BoardList -from .card import CardView - - -@ft.component -def BoardListView(board_list: BoardList, move_list): - is_list_over, set_is_list_over = ft.use_state(False) - is_end_over, set_is_end_over = ft.use_state(False) - new_card_text, set_new_card_text = ft.use_state("") - card_list: list[ft.Control] = [CardView(card) for card in board_list.cards] - - def on_add_card_click(_: ft.Event[ft.TextButton]): - if stripped := new_card_text.strip(): - board_list.add_card(stripped) - set_new_card_text("") - - def on_add_card_submit(_: ft.Event[ft.TextField]): - if stripped := new_card_text.strip(): - board_list.add_card(stripped) - set_new_card_text("") - - def on_delete(_: ft.Event[ft.PopupMenuItem]): - board_list.board.remove_list(board_list) - - def change_card_text(e: ft.Event[ft.TextField]): - set_new_card_text(e.control.value) - - def on_end_accept(e: ft.DragTargetEvent): - board_list.move_card_into(e.src.data) - set_is_end_over(False) - - def on_list_accept(e: ft.DragTargetEvent): - move_list(e.src.data, board_list) - set_is_list_over(False) - - return ft.Row( - spacing=4, - intrinsic_height=True, - controls=[ - ft.VerticalDivider( - color=ft.Colors.BLACK_54, - width=2, - thickness=2, - radius=2, - leading_indent=15, - trailing_indent=15, - opacity=1.0 if is_list_over else 0.0, - ), - ft.Draggable( - group="lists", - data=board_list, - content=ft.DragTarget( - group="cards", - data=board_list, - on_will_accept=lambda e: set_is_end_over(e.accept), - on_accept=on_end_accept, - on_leave=lambda: set_is_end_over(False), - content=ft.DragTarget( - group="lists", - data=board_list, - on_will_accept=lambda e: set_is_list_over( - e.accept and e.src.data != board_list - ), - on_accept=on_list_accept, - on_leave=lambda: set_is_list_over(False), - content=ft.Container( - border=ft.Border.all( - 2, - ( - ft.Colors.BLACK_38 - if is_list_over - else ft.Colors.BLACK_12 - ), - ), - border_radius=ft.BorderRadius.all(8), - bgcolor=board_list.color, - padding=ft.Padding.all(10), - width=240, - content=ft.Column( - spacing=6, - controls=[ - ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - controls=[ - ft.Text( - board_list.title, - theme_style=ft.TextThemeStyle.TITLE_MEDIUM, - ), - ft.PopupMenuButton( - items=[ - ft.PopupMenuItem( - content=ft.Text("Delete"), - on_click=on_delete, - ) - ] - ), - ], - ), - ft.TextField( - label="New card", - bgcolor=ft.Colors.WHITE, - value=new_card_text, - on_change=change_card_text, - on_submit=on_add_card_submit, - ), - ft.TextButton( - content="Add card", - icon=ft.Icons.ADD, - on_click=on_add_card_click, - ), - ft.Column( - spacing=2, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - *card_list, - ft.Divider( - color=ft.Colors.BLACK_38, - thickness=2, - height=2, - radius=2, - opacity=1.0 if is_end_over else 0.0, - ), - ], - ), - ], - ), - ), - ), - ), - ), - ], - ) diff --git a/sdk/python/examples/apps/trolli-declarative/src/components/sidebar.py b/sdk/python/examples/apps/trolli-declarative/src/components/sidebar.py deleted file mode 100644 index 3145b6ff63..0000000000 --- a/sdk/python/examples/apps/trolli-declarative/src/components/sidebar.py +++ /dev/null @@ -1,92 +0,0 @@ -from __future__ import annotations -import asyncio - -import flet as ft - -from models import TrolliState - - -@ft.component -def Sidebar(app: TrolliState): - board_id = app.current_board_id - active_board_index = ( - next((i for i, b in enumerate(app.boards) if b.board_id == board_id), None) - if board_id is not None - else None - ) - top_index = ( - 0 - if app.active_screen == "boards" - else 1 if app.active_screen == "members" else None - ) - - def top_nav_change(e: ft.Event[ft.NavigationRail]): - if e.control.selected_index == 0: - asyncio.create_task(ft.context.page.push_route("/boards")) - # ft.context.page.go("/boards") - elif e.control.selected_index == 1: - asyncio.create_task(ft.context.page.push_route("/members")) - # ft.context.page.go("/members") - - def bottom_nav_change(e: ft.Event[ft.NavigationRail]): - idx = e.control.selected_index - if idx is None: - return - asyncio.create_task( - ft.context.page.push_route(f"/board/{app.boards[idx].board_id}") - ) - # ft.context.page.go(f"/board/{app.boards[idx].board_id}") - - return ft.Container( - width=200, - bgcolor=ft.Colors.BLUE_GREY, - padding=ft.Padding.all(15), - visible=app.nav_visible, - content=ft.Column( - tight=True, - controls=[ - ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - controls=[ft.Text("Workspace")], - ), - ft.Divider(height=1, thickness=1, color=ft.Colors.BLACK_26), - ft.NavigationRail( - selected_index=top_index, - label_type=ft.NavigationRailLabelType.ALL, - bgcolor=ft.Colors.BLUE_GREY, - extended=True, - height=110, - on_change=top_nav_change, - destinations=[ - ft.NavigationRailDestination( - label="Boards", - icon=ft.Icons.BOOK_OUTLINED, - selected_icon=ft.Icons.BOOK_OUTLINED, - ), - ft.NavigationRailDestination( - label="Members", - icon=ft.Icons.PERSON, - selected_icon=ft.Icons.PERSON, - ), - ], - ), - ft.Divider(height=1, thickness=1, color=ft.Colors.BLACK_26), - ft.NavigationRail( - selected_index=active_board_index, - label_type=ft.NavigationRailLabelType.ALL, - bgcolor=ft.Colors.BLUE_GREY, - extended=True, - expand=True, - on_change=bottom_nav_change, - destinations=[ - ft.NavigationRailDestination( - label=b.name, - icon=ft.Icons.CHEVRON_RIGHT_OUTLINED, - selected_icon=ft.Icons.CHEVRON_RIGHT_ROUNDED, - ) - for b in app.boards - ], - ), - ], - ), - ) diff --git a/sdk/python/examples/apps/trolli-declarative/src/main.py b/sdk/python/examples/apps/trolli-declarative/src/main.py deleted file mode 100644 index 31e93aca0b..0000000000 --- a/sdk/python/examples/apps/trolli-declarative/src/main.py +++ /dev/null @@ -1,159 +0,0 @@ -import asyncio -import logging -from typing import Optional - -from components import BoardsView, BoardView, Sidebar, TrolliAppBar -from models import TrolliState - -import flet as ft - -logging.basicConfig(level=logging.INFO) -logging.getLogger("flet_components").setLevel(logging.INFO) - - -def _init_demo_data(app: TrolliState) -> None: - if app.boards: - return - board = app.create_board("My First Board") - board.add_list("To Do", ft.Colors.AMBER_200) - board.add_list("Doing", ft.Colors.LIGHT_BLUE_200) - board.add_list("Done", ft.Colors.LIGHT_GREEN_200) - board.lists[0].add_card("Drag cards between lists") - board.lists[0].add_card("Drag lists to reorder") - board.lists[1].add_card("Add a list from the button") - - -@ft.component -def App(): - app, _ = ft.use_state(lambda: TrolliState(route=ft.context.page.route)) - ft.context.page.padding = 0 - ft.context.page.theme_mode = ft.ThemeMode.LIGHT - ft.context.page.fonts = {"Pacifico": "Pacifico-Regular.ttf"} - ft.context.page.bgcolor = ft.Colors.BLUE_GREY_200 - if ft.context.page.route == "/": - asyncio.create_task(ft.context.page.push_route("/boards")) - # ft.context.page.go("/boards") - - def parse_board_id_from_route(route: str) -> Optional[int]: - troute = ft.TemplateRoute(route) - if not troute.match("/board/:id"): - return None - - raw = getattr(troute, "id", None) - if not isinstance(raw, str): - return None - try: - return int(raw) - except ValueError: - return None - - def route_change(e: ft.RouteChangeEvent): - # Keep route as a single source of truth for navigation-related UI (e.g. sidebar selection). - app.route = e.route - - # deal with bad or incomplete routes first - if e.route.startswith("/board/"): - board_id = parse_board_id_from_route(e.route) - if board_id is None or app.get_board_by_id(board_id) is None: - asyncio.create_task(ft.context.page.push_route("/boards")) - # ft.context.page.go("/boards") - app.active_screen = "boards" - app.current_board_id = None - app.route = "/boards" - return - - match e.route: - # trigger re-render by changing active_screen - case "/" | "/boards": - app.active_screen = "boards" - app.current_board_id = None - case "/members": - app.active_screen = "members" - app.current_board_id = None - case "/settings": - app.active_screen = "settings" - app.current_board_id = None - case name if name.startswith("/board/"): - app.active_screen = "board" - app.current_board_id = board_id - case _: - asyncio.create_task(ft.context.page.push_route("/boards")) - # ft.context.page.go("/boards") - app.active_screen = "boards" - app.current_board_id = None - app.route = "/boards" - return - - ft.context.page.on_route_change = route_change - - def redirect_unknown_board(): - if not app.route.startswith("/board/"): - return - board_id = parse_board_id_from_route(app.route) - if board_id is None or app.get_board_by_id(board_id) is None: - asyncio.create_task(ft.context.page.push_route("/boards")) - - # ft.on_updated(redirect_unknown_board, [app.route]) - - ft.on_mounted(lambda: _init_demo_data(app)) - - def toggle_sidebar(_: ft.Event[ft.IconButton]): - app.nav_visible = not app.nav_visible - - board_id = parse_board_id_from_route(app.route) - board = app.get_board_by_id(board_id) if board_id is not None else None - - content: ft.Control - match app.active_screen: - case "boards": - content = BoardsView(app) - case "members": - content = ft.Text("Members view placeholder", align=ft.Alignment.CENTER) - case "settings": - content = ft.Text("Settings view placeholder", align=ft.Alignment.CENTER) - case "board": - board = ( - app.get_board_by_id(app.current_board_id) - if app.current_board_id - else None - ) - content = BoardView(board) if board else BoardsView(app) - - return ft.Column( - expand=True, - spacing=0, - controls=[ - TrolliAppBar(app), - ft.SafeArea( - expand=True, - content=ft.Row( - vertical_alignment=ft.CrossAxisAlignment.START, - controls=[ - Sidebar(app), - ft.Stack( - fit=ft.StackFit.LOOSE, - expand=True, - controls=[ - ft.Container( - padding=ft.Padding.only(left=10, right=10, top=10), - content=content, - ), - ft.IconButton( - icon=ft.Icons.ARROW_CIRCLE_LEFT, - selected=not app.nav_visible, - selected_icon=ft.Icons.ARROW_CIRCLE_RIGHT, - icon_color=ft.Colors.BLUE_GREY_400, - icon_size=20, - padding=0, - on_click=toggle_sidebar, - ), - ], - ), - ], - ), - ), - ], - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/apps/trolli/.gitignore b/sdk/python/examples/apps/trolli/.gitignore deleted file mode 100644 index b6e47617de..0000000000 --- a/sdk/python/examples/apps/trolli/.gitignore +++ /dev/null @@ -1,129 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ diff --git a/sdk/python/examples/apps/trolli/DockerFile b/sdk/python/examples/apps/trolli/DockerFile deleted file mode 100644 index 80fb9be08b..0000000000 --- a/sdk/python/examples/apps/trolli/DockerFile +++ /dev/null @@ -1,9 +0,0 @@ -FROM python:3-alpine - -COPY . . - -RUN pip install --no-cache-dir -r requirements.txt - -EXPOSE 8080 - -CMD ["python", "./src/main.py"] diff --git a/sdk/python/examples/apps/trolli/README.md b/sdk/python/examples/apps/trolli/README.md deleted file mode 100644 index 57bba596fe..0000000000 --- a/sdk/python/examples/apps/trolli/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# flet-trello-clone -A clone of trello built with Flet. - - -Live demo [here](https://flet-trolli.fly.dev/) diff --git a/sdk/python/examples/apps/trolli/assets/Pacifico-Regular.ttf b/sdk/python/examples/apps/trolli/assets/Pacifico-Regular.ttf deleted file mode 100644 index e7def95d3f..0000000000 Binary files a/sdk/python/examples/apps/trolli/assets/Pacifico-Regular.ttf and /dev/null differ diff --git a/sdk/python/examples/apps/trolli/fly.toml b/sdk/python/examples/apps/trolli/fly.toml deleted file mode 100644 index 3f3e628bf6..0000000000 --- a/sdk/python/examples/apps/trolli/fly.toml +++ /dev/null @@ -1,40 +0,0 @@ -# fly.toml file generated for flet-trolli on 2022-11-28T16:51:31-08:00 - -app = "flet-trolli" -kill_signal = "SIGINT" -kill_timeout = 5 -processes = [] - -[env] - FLET_SERVER_PORT = "8080" - FLET_FORCE_WEB_SERVER = "true" - -[experimental] - allowed_public_ports = [] - auto_rollback = true - -[[services]] - http_checks = [] - internal_port = 8080 - processes = ["app"] - protocol = "tcp" - script_checks = [] - [services.concurrency] - hard_limit = 25 - soft_limit = 20 - type = "connections" - - [[services.ports]] - force_https = true - handlers = ["http"] - port = 80 - - [[services.ports]] - handlers = ["tls", "http"] - port = 443 - - [[services.tcp_checks]] - grace_period = "1s" - interval = "15s" - restart_limit = 0 - timeout = "2s" diff --git a/sdk/python/examples/apps/trolli/pyproject.toml b/sdk/python/examples/apps/trolli/pyproject.toml deleted file mode 100644 index 0f7a490fc6..0000000000 --- a/sdk/python/examples/apps/trolli/pyproject.toml +++ /dev/null @@ -1,28 +0,0 @@ -[project] -name = "new-trolli" -version = "0.1.0" -description = "" -readme = "README.md" -requires-python = ">=3.8" -dependencies = [ - "flet==0.26.0" -] - - -[tool.flet] -# org name in reverse domain name notation, e.g. "com.mycompany". -# Combined with project.name to build bundle ID for iOS and Android apps -org = "com.mycompany" - -# project display name that is used as an app title on Android and iOS home screens, -# shown in window titles and about app dialogs on desktop. -product = "new-trolli" - -# company name to display in about app dialogs -company = "Flet" - -# copyright text to display in about app dialogs -copyright = "Copyright (C) 2024 by Flet" - -[tool.flet.app] -path = "src" diff --git a/sdk/python/examples/apps/trolli/requirements.txt b/sdk/python/examples/apps/trolli/requirements.txt deleted file mode 100644 index 7de514bad0..0000000000 --- a/sdk/python/examples/apps/trolli/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -flet>=0.25.1 -python-dotenv>=0.20.0 diff --git a/sdk/python/examples/apps/trolli/src/app_layout.py b/sdk/python/examples/apps/trolli/src/app_layout.py deleted file mode 100644 index bf600af79c..0000000000 --- a/sdk/python/examples/apps/trolli/src/app_layout.py +++ /dev/null @@ -1,177 +0,0 @@ -import flet as ft -from board import Board -from data_store import DataStore -from sidebar import Sidebar - - -class AppLayout(ft.Row): - def __init__(self, app, page: ft.Page, store: DataStore, *args, **kwargs): - super().__init__(*args, **kwargs) - self.app = app - self.page: ft.Page = page - self.page.on_resized = self.page_resize - self.store: DataStore = store - self.toggle_nav_rail_button = ft.IconButton( - icon=ft.Icons.ARROW_CIRCLE_LEFT, - icon_color=ft.Colors.BLUE_GREY_400, - selected=False, - selected_icon=ft.Icons.ARROW_CIRCLE_RIGHT, - on_click=self.toggle_nav_rail, - ) - self.sidebar = Sidebar(self, self.store) - self.members_view = ft.Text("members view") - self.all_boards_view = ft.Column( - [ - ft.Row( - [ - ft.Container( - ft.Text( - value="Your Boards", - theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM, - ), - expand=True, - padding=ft.Padding.only(top=15), - ), - ft.Container( - ft.TextButton( - "Add new board", - icon=ft.Icons.ADD, - on_click=self.app.add_board, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.DEFAULT: ft.Colors.BLUE_200, - ft.ControlState.HOVERED: ft.Colors.BLUE_400, - }, - shape={ - ft.ControlState.DEFAULT: ft.RoundedRectangleBorder( - radius=3 - ) - }, - ), - ), - padding=ft.Padding.only(right=50, top=15), - ), - ] - ), - ft.Row( - [ - ft.TextField( - hint_text="Search all boards", - autofocus=False, - content_padding=ft.Padding.only(left=10), - width=200, - height=40, - text_size=12, - border_color=ft.Colors.BLACK26, - focused_border_color=ft.Colors.BLUE_ACCENT, - suffix_icon=ft.Icons.SEARCH, - ) - ] - ), - ft.Row([ft.Text("No Boards to Display")]), - ], - expand=True, - ) - self._active_view: ft.Control = self.all_boards_view - - self.controls = [self.sidebar, self.toggle_nav_rail_button, self.active_view] - - @property - def active_view(self): - return self._active_view - - @active_view.setter - def active_view(self, view): - self._active_view = view - self.controls[-1] = self._active_view - self.sidebar.sync_board_destinations() - self.page.update() - - def set_board_view(self, i): - self.active_view = self.store.get_boards()[i] - self.sidebar.bottom_nav_rail.selected_index = i - self.sidebar.top_nav_rail.selected_index = None - self.page_resize() - self.page.update() - - def set_all_boards_view(self): - self.active_view = self.all_boards_view - self.hydrate_all_boards_view() - self.sidebar.top_nav_rail.selected_index = 0 - self.sidebar.bottom_nav_rail.selected_index = None - self.page.update() - - def set_members_view(self): - self.active_view = self.members_view - self.sidebar.top_nav_rail.selected_index = 1 - self.sidebar.bottom_nav_rail.selected_index = None - self.page.update() - - def page_resize(self, e=None): - if type(self.active_view) is Board: - self.active_view.resize( - self.sidebar.visible, self.page.width, self.page.height - ) - self.page.update() - - def hydrate_all_boards_view(self): - self.all_boards_view.controls[-1] = ft.Row( - [ - ft.Container( - content=ft.Row( - [ - ft.Container( - content=ft.Text(value=b.name), - data=b, - expand=True, - on_click=self.board_click, - ), - ft.Container( - content=ft.PopupMenuButton( - items=[ - ft.PopupMenuItem( - content=ft.Text( - value="Delete", - theme_style=ft.TextThemeStyle.LABEL_MEDIUM, - text_align=ft.TextAlign.CENTER, - ), - on_click=self.app.delete_board, - data=b, - ), - ft.PopupMenuItem(), - ft.PopupMenuItem( - content=ft.Text( - value="Archive", - theme_style=ft.TextThemeStyle.LABEL_MEDIUM, - text_align=ft.TextAlign.CENTER, - ), - ), - ] - ), - padding=ft.Padding.only(right=-10), - border_radius=ft.border_radius.all(3), - ), - ], - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - ), - border=ft.border.all(1, ft.Colors.BLACK38), - border_radius=ft.border_radius.all(5), - bgcolor=ft.Colors.WHITE60, - padding=ft.Padding.all(10), - width=250, - data=b, - ) - for b in self.store.get_boards() - ], - wrap=True, - ) - self.sidebar.sync_board_destinations() - - def board_click(self, e): - self.sidebar.bottom_nav_change(self.store.get_boards().index(e.control.data)) - - def toggle_nav_rail(self, e): - self.sidebar.visible = not self.sidebar.visible - self.toggle_nav_rail_button.selected = not self.toggle_nav_rail_button.selected - self.page_resize() - self.page.update() diff --git a/sdk/python/examples/apps/trolli/src/board.py b/sdk/python/examples/apps/trolli/src/board.py deleted file mode 100644 index 212e9b3784..0000000000 --- a/sdk/python/examples/apps/trolli/src/board.py +++ /dev/null @@ -1,152 +0,0 @@ -import itertools - -from board_list import BoardList -from data_store import DataStore - -import flet as ft - - -class Board(ft.Container): - id_counter = itertools.count() - - def __init__(self, app, store: DataStore, name: str, page: ft.Page): - self.page: ft.Page = page - self.board_id = next(Board.id_counter) - self.store: DataStore = store - self.app = app - self.name = name - self.add_list_button = ft.FloatingActionButton( - icon=ft.Icons.ADD, text="add a list", height=30, on_click=self.create_list - ) - - self.board_lists = ft.Row( - controls=[self.add_list_button], - vertical_alignment=ft.CrossAxisAlignment.START, - scroll=ft.ScrollMode.AUTO, - expand=True, - width=(self.app.page.width - 310), - height=(self.app.page.height - 95), - ) - for lst in self.store.get_lists_by_board(self.board_id): - self.add_list(lst) - - super().__init__( - content=self.board_lists, - data=self, - margin=ft.margin.all(0), - padding=ft.Padding.only(top=10, right=0), - height=self.app.page.height, - ) - - def resize(self, nav_rail_extended, width, height): - self.board_lists.width = (width - 310) if nav_rail_extended else (width - 50) - self.height = height - self.update() - - async def create_list(self, e): - option_dict = { - ft.Colors.LIGHT_GREEN: self.color_option_creator(ft.Colors.LIGHT_GREEN), - ft.Colors.RED_200: self.color_option_creator(ft.Colors.RED_200), - ft.Colors.AMBER_500: self.color_option_creator(ft.Colors.AMBER_500), - ft.Colors.PINK_300: self.color_option_creator(ft.Colors.PINK_300), - ft.Colors.ORANGE_300: self.color_option_creator(ft.Colors.ORANGE_300), - ft.Colors.LIGHT_BLUE: self.color_option_creator(ft.Colors.LIGHT_BLUE), - ft.Colors.DEEP_ORANGE_300: self.color_option_creator( - ft.Colors.DEEP_ORANGE_300 - ), - ft.Colors.PURPLE_100: self.color_option_creator(ft.Colors.PURPLE_100), - ft.Colors.RED_700: self.color_option_creator(ft.Colors.RED_700), - ft.Colors.TEAL_500: self.color_option_creator(ft.Colors.TEAL_500), - ft.Colors.YELLOW_400: self.color_option_creator(ft.Colors.YELLOW_400), - ft.Colors.PURPLE_400: self.color_option_creator(ft.Colors.PURPLE_400), - ft.Colors.BROWN_300: self.color_option_creator(ft.Colors.BROWN_300), - ft.Colors.CYAN_500: self.color_option_creator(ft.Colors.CYAN_500), - ft.Colors.BLUE_GREY_500: self.color_option_creator(ft.Colors.BLUE_GREY_500), - } - - def set_color(e): - color_options.data = e.control.data - for k, v in option_dict.items(): - if k == e.control.data: - v.border = ft.border.all(3, ft.Colors.BLACK26) - else: - v.border = None - dialog.content.update() - - color_options = ft.GridView(runs_count=3, max_extent=40, data="", height=150) - - for _, v in option_dict.items(): - v.on_click = set_color - color_options.controls.append(v) - - def close_dlg(e): - if (hasattr(e.control, "text") and e.control.text != "Cancel") or ( - type(e.control) is ft.TextField and e.control.value != "" - ): - new_list = BoardList( - self, - self.store, - dialog_text.value, - self.page, - color=color_options.data, - ) - self.add_list(new_list) - self.page.close(dialog) - - def textfield_change(e): - if dialog_text.value == "": - create_button.disabled = True - else: - create_button.disabled = False - self.page.update() - - dialog_text = ft.TextField( - label="New List Name", on_submit=close_dlg, on_change=textfield_change - ) - create_button = ft.Button( - text="Create", bgcolor=ft.Colors.BLUE_200, on_click=close_dlg, disabled=True - ) - dialog = ft.AlertDialog( - title=ft.Text("Name your new list"), - content=ft.Column( - [ - ft.Container( - content=dialog_text, padding=ft.Padding.symmetric(horizontal=5) - ), - color_options, - ft.Row( - [ - ft.Button(text="Cancel", on_click=close_dlg), - create_button, - ], - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - ), - ], - tight=True, - alignment=ft.MainAxisAlignment.CENTER, - ), - on_dismiss=lambda e: print("Modal dialog dismissed!"), - ) - self.page.open(dialog) - await dialog_text.focus() - - def remove_list(self, list: BoardList, e): - self.board_lists.controls.remove(list) - self.store.remove_list(self.board_id, list.board_list_id) - self.page.update() - - def add_list(self, list): - self.board_lists.controls.insert(-1, list) - self.store.add_list(self.board_id, list) - self.page.update() - - def color_option_creator(self, color: str): - return ft.Container( - bgcolor=color, - border_radius=ft.border_radius.all(50), - height=10, - width=10, - padding=ft.Padding.all(5), - alignment=ft.alignment.center, - data=color, - ) diff --git a/sdk/python/examples/apps/trolli/src/board_list.py b/sdk/python/examples/apps/trolli/src/board_list.py deleted file mode 100644 index 84d01d9640..0000000000 --- a/sdk/python/examples/apps/trolli/src/board_list.py +++ /dev/null @@ -1,276 +0,0 @@ -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from board import Board -import itertools - -import flet as ft -from data_store import DataStore -from item import Item - - -class BoardList(ft.Container): - id_counter = itertools.count() - - def __init__( - self, - board: "Board", - store: DataStore, - title: str, - page: ft.Page, - color: str = "", - ): - self.page: ft.Page = page - self.board_list_id = next(BoardList.id_counter) - self.store: DataStore = store - self.board = board - self.title = title - self.color = color - self.items = ft.Column([], tight=True, spacing=4) - self.items.controls = self.store.get_items(self.board_list_id) - self.new_item_field = ft.TextField( - label="new card name", - height=50, - bgcolor=ft.Colors.WHITE, - on_submit=self.add_item_handler, - ) - - self.end_indicator = ft.Container( - bgcolor=ft.Colors.BLACK26, - border_radius=ft.border_radius.all(30), - height=3, - width=200, - opacity=0.0, - ) - - self.edit_field = ft.Row( - [ - ft.TextField( - value=self.title, - width=150, - height=40, - content_padding=ft.Padding.only(left=10, bottom=10), - ), - ft.TextButton(text="Save", on_click=self.save_title), - ] - ) - - self.header = ft.Row( - controls=[ - ft.Text( - value=self.title, - theme_style=ft.TextThemeStyle.TITLE_MEDIUM, - text_align=ft.TextAlign.LEFT, - overflow=ft.TextOverflow.CLIP, - expand=True, - ), - ft.Container( - ft.PopupMenuButton( - items=[ - ft.PopupMenuItem( - content=ft.Text( - value="Edit", - theme_style=ft.TextThemeStyle.LABEL_MEDIUM, - text_align=ft.TextAlign.CENTER, - color=self.color, - ), - on_click=self.edit_title, - ), - ft.PopupMenuItem(), - ft.PopupMenuItem( - content=ft.Text( - value="Delete", - theme_style=ft.TextThemeStyle.LABEL_MEDIUM, - text_align=ft.TextAlign.CENTER, - color=self.color, - ), - on_click=self.delete_list, - ), - ft.PopupMenuItem(), - ft.PopupMenuItem( - content=ft.Text( - value="Move List", - theme_style=ft.TextThemeStyle.LABEL_MEDIUM, - text_align=ft.TextAlign.CENTER, - color=self.color, - ) - ), - ], - ), - padding=ft.Padding.only(right=-10), - ), - ], - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - ) - - self.inner_list = ft.Container( - content=ft.Column( - [ - self.header, - self.new_item_field, - ft.TextButton( - content=ft.Row( - [ - ft.Icon(ft.Icons.ADD), - ft.Text("add card", color=ft.Colors.BLACK38), - ], - tight=True, - ), - on_click=self.add_item_handler, - ), - self.items, - self.end_indicator, - ], - spacing=4, - tight=True, - data=self.title, - ), - width=250, - border=ft.border.all(2, ft.Colors.BLACK12), - border_radius=ft.border_radius.all(5), - bgcolor=self.color if (self.color != "") else ft.Colors.SURFACE, - padding=ft.Padding.only(bottom=10, right=10, left=10, top=5), - ) - - self.view = ft.DragTarget( - group="items", - content=ft.Draggable( - group="lists", - content=ft.DragTarget( - group="lists", - content=self.inner_list, - data=self, - on_accept=self.list_drag_accept, - on_will_accept=self.list_will_drag_accept, - on_leave=self.list_drag_leave, - ), - ), - data=self, - on_accept=self.item_drag_accept, - on_will_accept=self.item_will_drag_accept, - on_leave=self.item_drag_leave, - ) - super().__init__(content=self.view, data=self) - - def item_drag_accept(self, e): - src = self.page.get_control(e.src_id) - self.add_item(src.data.item_text) - src.data.list.remove_item(src.data) - self.end_indicator.opacity = 0.0 - self.update() - - def item_will_drag_accept(self, e): - if e.data == "true": - self.end_indicator.opacity = 1.0 - self.update() - - def item_drag_leave(self, e): - self.end_indicator.opacity = 0.0 - self.update() - - def list_drag_accept(self, e): - src = self.page.get_control(e.src_id) - l = self.board.content.controls - to_index = l.index(e.control.data) - from_index = l.index(src.content.data) - l[to_index], l[from_index] = l[from_index], l[to_index] - self.inner_list.border = ft.border.all(2, ft.Colors.BLACK12) - self.page.update() - - def list_will_drag_accept(self, e): - if e.data == "true": - self.inner_list.border = ft.border.all(2, ft.Colors.BLACK) - self.update() - - def list_drag_leave(self, e): - self.inner_list.border = ft.border.all(2, ft.Colors.BLACK12) - self.update() - - def delete_list(self, e): - self.board.remove_list(self, e) - - def edit_title(self, e): - self.header.controls[0] = self.edit_field - self.header.controls[1].visible = False - self.update() - - def save_title(self, e): - self.title = self.edit_field.controls[0].value - self.header.controls[0] = ft.Text( - value=self.title, - theme_style=ft.TextThemeStyle.TITLE_MEDIUM, - text_align=ft.TextAlign.LEFT, - overflow=ft.TextOverflow.CLIP, - expand=True, - ) - self.header.controls[1].visible = True - self.update() - - def add_item_handler(self, e): - if self.new_item_field.value == "": - return - self.add_item() - - def add_item( - self, - item: str | None = None, - chosen_control: ft.Draggable | None = None, - swap_control: ft.Draggable | None = None, - ): - controls_list = [x.controls[1] for x in self.items.controls] - to_index = ( - controls_list.index(swap_control) if swap_control in controls_list else None - ) - from_index = ( - controls_list.index(chosen_control) - if chosen_control in controls_list - else None - ) - control_to_add = ft.Column( - [ - ft.Container( - bgcolor=ft.Colors.BLACK26, - border_radius=ft.border_radius.all(30), - height=3, - alignment=ft.alignment.center_right, - width=200, - opacity=0.0, - ) - ] - ) - - # rearrange (i.e. drag drop from same list) - if (from_index is not None) and (to_index is not None): - self.items.controls.insert(to_index, self.items.controls.pop(from_index)) - self.set_indicator_opacity(swap_control, 0.0) - - # insert (drag from other list to middle of this list) - elif to_index is not None: - new_item = Item(self, self.store, item) - control_to_add.controls.append(new_item) - self.items.controls.insert(to_index, control_to_add) - - # add new (drag from other list to end of this list, or use add item button) - else: - new_item = ( - Item(self, self.store, item) - if item - else Item(self, self.store, self.new_item_field.value) - ) - control_to_add.controls.append(new_item) - self.items.controls.append(control_to_add) - self.store.add_item(self.board_list_id, new_item) - self.new_item_field.value = "" - - self.page.update() - - def remove_item(self, item: Item): - controls_list = [x.controls[1] for x in self.items.controls] - del self.items.controls[controls_list.index(item)] - self.store.remove_item(self.board_list_id, item.item_id) - self.view.update() - - def set_indicator_opacity(self, item, opacity): - controls_list = [x.controls[1] for x in self.items.controls] - self.items.controls[controls_list.index(item)].controls[0].opacity = opacity - self.view.update() diff --git a/sdk/python/examples/apps/trolli/src/data_store.py b/sdk/python/examples/apps/trolli/src/data_store.py deleted file mode 100644 index 8f8c26bc5a..0000000000 --- a/sdk/python/examples/apps/trolli/src/data_store.py +++ /dev/null @@ -1,66 +0,0 @@ -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from board import Board - from board_list import BoardList - from item import Item - from user import User - - -class DataStore: - def add_board(self, model) -> None: - raise NotImplementedError - - def get_board(self, id) -> "Board": - raise NotImplementedError - - def get_boards(self) -> list["Board"]: - raise NotImplementedError - - def update_board(self, model, update): - raise NotImplementedError - - def remove_board(self, board) -> None: - raise NotImplementedError - - def add_user(self, model) -> None: - raise NotImplementedError - - def get_users(self) -> list["User"]: - raise NotImplementedError - - def get_user(self, id) -> "User": - raise NotImplementedError - - def remove_user(self, id) -> None: - raise NotImplementedError - - def add_list(self, board, model) -> None: - raise NotImplementedError - - def get_lists(self) -> list["BoardList"]: - raise NotImplementedError - - def get_list(self, id) -> "BoardList": - raise NotImplementedError - - def get_lists_by_board(self, board) -> list["BoardList"]: - raise NotImplementedError - - def remove_list(self, board, id) -> None: - raise NotImplementedError - - def add_item(self, board_list, model) -> None: - raise NotImplementedError - - def get_items(self, board_list) -> list["Item"]: - raise NotImplementedError - - def get_item(self, id) -> "Item": - raise NotImplementedError - - def get_items_by_board(self, board) -> list["Item"]: - raise NotImplementedError - - def remove_item(self, board_list, id) -> None: - raise NotImplementedError diff --git a/sdk/python/examples/apps/trolli/src/item.py b/sdk/python/examples/apps/trolli/src/item.py deleted file mode 100644 index 7fb9992fd6..0000000000 --- a/sdk/python/examples/apps/trolli/src/item.py +++ /dev/null @@ -1,80 +0,0 @@ -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from board_list import BoardList -import itertools - -import flet as ft -from data_store import DataStore - - -class Item(ft.Container): - id_counter = itertools.count() - - def __init__(self, list: "BoardList", store: DataStore, item_text: str): - self.item_id = next(Item.id_counter) - self.store: DataStore = store - self.list = list - self.item_text = item_text - self.card_item = ft.Card( - content=ft.Row( - [ - ft.Container( - content=ft.Checkbox(label=f"{self.item_text}", width=200), - border_radius=ft.border_radius.all(5), - ) - ], - width=200, - wrap=True, - ), - elevation=1, - data=self.list, - ) - self.view = ft.Draggable( - group="items", - content=ft.DragTarget( - group="items", - content=self.card_item, - on_accept=self.drag_accept, - on_leave=self.drag_leave, - on_will_accept=self.drag_will_accept, - ), - data=self, - ) - super().__init__(content=self.view) - - def drag_accept(self, e): - src = self.page.get_control(e.src_id) - - # skip if item is dropped on itself - if src.content.content == e.control.content: - self.card_item.elevation = 1 - self.list.set_indicator_opacity(self, 0.0) - e.control.update() - return - - # item dropped within same list but not on self - if src.data.list == self.list: - self.list.add_item(chosen_control=src.data, swap_control=self) - self.card_item.elevation = 1 - e.control.update() - return - - # item added to different list - self.list.add_item(src.data.item_text, swap_control=self) - # remove from the list to which draggable belongs - src.data.list.remove_item(src.data) - self.list.set_indicator_opacity(self, 0.0) - self.card_item.elevation = 1 - self.page.update() - - def drag_will_accept(self, e): - if e.data == "true": - self.list.set_indicator_opacity(self, 1.0) - self.card_item.elevation = 20 if e.data == "true" else 1 - self.page.update() - - def drag_leave(self, e): - self.list.set_indicator_opacity(self, 0.0) - self.card_item.elevation = 1 - self.page.update() diff --git a/sdk/python/examples/apps/trolli/src/main.py b/sdk/python/examples/apps/trolli/src/main.py deleted file mode 100644 index 5b3bed25db..0000000000 --- a/sdk/python/examples/apps/trolli/src/main.py +++ /dev/null @@ -1,189 +0,0 @@ -from app_layout import AppLayout -from board import Board -from data_store import DataStore -from memory_store import InMemoryStore -from user import User - -import flet as ft - - -class TrelloApp(AppLayout): - def __init__(self, page: ft.Page, store: DataStore): - self.page: ft.Page = page - self.store: DataStore = store - self.user: str | None = None - self.page.on_route_change = self.route_change - self.boards = self.store.get_boards() - self.login_profile_button = ft.PopupMenuItem(text="Log in", on_click=self.login) - self.appbar_items = [ - self.login_profile_button, - ft.PopupMenuItem(), # divider - ft.PopupMenuItem(text="Settings"), - ] - self.appbar = ft.AppBar( - leading=ft.Icon(ft.Icons.GRID_GOLDENRATIO_ROUNDED), - leading_width=100, - title=ft.Text( - "Trolli", - font_family="Pacifico", - size=32, - text_align=ft.TextAlign.START, - ), - center_title=False, - toolbar_height=75, - bgcolor=ft.Colors.LIGHT_BLUE_ACCENT_700, - actions=[ - ft.Container( - content=ft.PopupMenuButton(items=self.appbar_items), - margin=ft.margin.only(left=50, right=25), - ) - ], - ) - self.page.appbar = self.appbar - - self.page.update() - super().__init__( - self, - self.page, - self.store, - tight=True, - expand=True, - vertical_alignment=ft.CrossAxisAlignment.START, - ) - - def initialize(self): - self.page.views.append( - ft.View( - "/", - [self.appbar, self], - padding=ft.Padding.all(0), - bgcolor=ft.Colors.BLUE_GREY_200, - ) - ) - self.page.update() - # create an initial board for demonstration if no boards - if len(self.boards) == 0: - self.create_new_board("My First Board") - self.page.go("/") - - async def login(self, e): - async def close_dlg(e): - if user_name.value == "" or password.value == "": - user_name.error_text = "Please provide username" - password.error_text = "Please provide password" - self.page.update() - return - else: - user = User(user_name.value, password.value) - if user not in self.store.get_users(): - self.store.add_user(user) - self.user = user_name.value - await self.page.shared_preferences.set("current_user", user_name.value) - - self.page.close(dialog) - current_user = await self.page.shared_preferences.get("current_user") - self.appbar_items[0] = ft.PopupMenuItem(content=f"{current_user}'s Profile") - self.page.update() - - user_name = ft.TextField(label="User name") - password = ft.TextField(label="Password", password=True) - dialog = ft.AlertDialog( - title=ft.Text("Please enter your login credentials"), - content=ft.Column( - [ - user_name, - password, - ft.Button(text="Login", on_click=close_dlg), - ], - tight=True, - ), - on_dismiss=lambda e: print("Modal dialog dismissed!"), - ) - self.page.open(dialog) - - def route_change(self, e): - troute = ft.TemplateRoute(self.page.route) - if troute.match("/"): - self.page.go("/boards") - elif troute.match("/board/:id"): - if int(troute.id) > len(self.store.get_boards()): - self.page.go("/") - return - self.set_board_view(int(troute.id)) - elif troute.match("/boards"): - self.set_all_boards_view() - elif troute.match("/members"): - self.set_members_view() - self.page.update() - - async def add_board(self, e): - def close_dlg(e): - if (hasattr(e.control, "text") and e.control.text != "Cancel") or ( - type(e.control) is ft.TextField and e.control.value != "" - ): - self.create_new_board(dialog_text.value) - self.page.close(dialog) - self.page.update() - - def textfield_change(e): - if dialog_text.value == "": - create_button.disabled = True - else: - create_button.disabled = False - self.page.update() - - dialog_text = ft.TextField( - label="New Board Name", on_submit=close_dlg, on_change=textfield_change - ) - create_button = ft.Button( - text="Create", bgcolor=ft.Colors.BLUE_200, on_click=close_dlg, disabled=True - ) - dialog = ft.AlertDialog( - title=ft.Text("Name your new board"), - content=ft.Column( - [ - dialog_text, - ft.Row( - [ - ft.Button(text="Cancel", on_click=close_dlg), - create_button, - ], - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - ), - ], - tight=True, - ), - on_dismiss=lambda e: print("Modal dialog dismissed!"), - ) - self.page.open(dialog) - dialog.open = True - self.page.update() - await dialog_text.focus() - - def create_new_board(self, board_name): - new_board = Board(self, self.store, board_name, self.page) - self.store.add_board(new_board) - self.hydrate_all_boards_view() - - def delete_board(self, e): - self.store.remove_board(e.control.data) - self.set_all_boards_view() - - -def main(page: ft.Page): - page.title = "Flet Trello clone" - page.padding = 0 - page.theme = ft.Theme(font_family="Verdana") - page.theme_mode = ft.ThemeMode.LIGHT - page.theme.page_transitions.windows = "cupertino" - page.fonts = {"Pacifico": "Pacifico-Regular.ttf"} - page.bgcolor = ft.Colors.BLUE_GREY_200 - app = TrelloApp(page, InMemoryStore()) - page.add(app) - page.update() - app.initialize() - - -print("flet version: ", ft.version.version) -print("flet path: ", ft.__file__) -ft.run(target=main, assets_dir="../assets") diff --git a/sdk/python/examples/apps/trolli/src/memory_store.py b/sdk/python/examples/apps/trolli/src/memory_store.py deleted file mode 100644 index 3a62dccc4c..0000000000 --- a/sdk/python/examples/apps/trolli/src/memory_store.py +++ /dev/null @@ -1,68 +0,0 @@ -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from board import Board - from board_list import BoardList - from item import Item - from user import User - -from data_store import DataStore - - -class InMemoryStore(DataStore): - def __init__(self): - self.boards: dict[int, Board] = {} - self.users: dict[str, User] = {} - self.board_lists: dict[int, list[BoardList]] = {} - self.items: dict[int, list[Item]] = {} - - def add_board(self, board: "Board"): - self.boards[board.board_id] = board - - def get_board(self, id: int): - return self.boards[id] - - def update_board(self, board: "Board", update: dict): - for k in update: - setattr(board, k, update[k]) - - def get_boards(self): - return [self.boards[b] for b in self.boards] - - def remove_board(self, board: "Board"): - del self.boards[board.board_id] - self.board_lists[board.board_id] = [] - - def add_list(self, board: int, list: "BoardList"): - if board in self.board_lists: - self.board_lists[board].append(list) - else: - self.board_lists[board] = [list] - - def get_lists_by_board(self, board: int): - return self.board_lists.get(board, []) - - def remove_list(self, board: int, id: int): - self.board_lists[board] = [ - l for l in self.board_lists[board] if not l.board_list_id == id - ] - - def add_user(self, user: "User"): - self.users[user.name] = user - - def get_users(self): - return [self.users[u] for u in self.users] - - def add_item(self, board_list: int, item: "Item"): - if board_list in self.items: - self.items[board_list].append(item) - else: - self.items[board_list] = [item] - - def get_items(self, board_list: int): - return self.items.get(board_list, []) - - def remove_item(self, board_list: int, id: int): - self.items[board_list] = [ - i for i in self.items[board_list] if not i.item_id == id - ] diff --git a/sdk/python/examples/apps/trolli/src/sidebar.py b/sdk/python/examples/apps/trolli/src/sidebar.py deleted file mode 100644 index 32b6b60883..0000000000 --- a/sdk/python/examples/apps/trolli/src/sidebar.py +++ /dev/null @@ -1,141 +0,0 @@ -import flet as ft -from data_store import DataStore - - -class Sidebar(ft.Container): - def __init__(self, app_layout, store: DataStore): - self.store: DataStore = store - self.app_layout = app_layout - self.nav_rail_visible = True - self.top_nav_items = [ - ft.NavigationRailDestination( - label_content=ft.Text("Boards"), - label="Boards", - icon=ft.Icons.BOOK_OUTLINED, - selected_icon=ft.Icons.BOOK_OUTLINED, - ), - ft.NavigationRailDestination( - label_content=ft.Text("Members"), - label="Members", - icon=ft.Icons.PERSON, - selected_icon=ft.Icons.PERSON, - ), - ] - - self.top_nav_rail = ft.NavigationRail( - selected_index=None, - label_type=ft.NavigationRailLabelType.ALL, - on_change=self.top_nav_change, - destinations=self.top_nav_items, - bgcolor=ft.Colors.BLUE_GREY, - extended=True, - height=110, - ) - - self.bottom_nav_rail = ft.NavigationRail( - selected_index=None, - label_type=ft.NavigationRailLabelType.ALL, - on_change=self.bottom_nav_change, - extended=True, - expand=True, - bgcolor=ft.Colors.BLUE_GREY, - ) - self.toggle_nav_rail_button = ft.IconButton(ft.Icons.ARROW_BACK) - - super().__init__( - content=ft.Column( - [ - ft.Row( - [ - ft.Text("Workspace"), - ], - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - ), - # divider - ft.Container( - bgcolor=ft.Colors.BLACK26, - border_radius=ft.border_radius.all(30), - height=1, - alignment=ft.alignment.center_right, - width=220, - ), - self.top_nav_rail, - # divider - ft.Container( - bgcolor=ft.Colors.BLACK26, - border_radius=ft.border_radius.all(30), - height=1, - alignment=ft.alignment.center_right, - width=220, - ), - self.bottom_nav_rail, - ], - tight=True, - ), - padding=ft.Padding.all(15), - margin=ft.margin.all(0), - width=250, - bgcolor=ft.Colors.BLUE_GREY, - visible=self.nav_rail_visible, - ) - - def sync_board_destinations(self): - boards = self.store.get_boards() - self.bottom_nav_rail.destinations = [] - for i in range(len(boards)): - b = boards[i] - self.bottom_nav_rail.destinations.append( - ft.NavigationRailDestination( - label_content=ft.TextField( - value=b.name, - hint_text=b.name, - text_size=12, - read_only=True, - on_focus=self.board_name_focus, - on_blur=self.board_name_blur, - border=ft.InputBorder.NONE, - height=50, - width=150, - text_align=ft.TextAlign.START, - data=i, - ), - label=b.name, - selected_icon=ft.Icons.CHEVRON_RIGHT_ROUNDED, - icon=ft.Icons.CHEVRON_RIGHT_OUTLINED, - ) - ) - - def toggle_nav_rail(self, e): - self.visible = not self.visible - self.page.update() - - def board_name_focus(self, e): - e.control.read_only = False - e.control.border = ft.InputBorder.OUTLINE - self.page.update() - - def board_name_blur(self, e): - self.store.update_board( - self.store.get_boards()[e.control.data], {"name": e.control.value} - ) - self.app_layout.hydrate_all_boards_view() - e.control.read_only = True - e.control.border = ft.InputBorder.NONE - self.page.update() - - def top_nav_change(self, e): - index = e if (type(e) == int) else e.control.selected_index - self.bottom_nav_rail.selected_index = None - self.top_nav_rail.selected_index = index - if index == 0: - self.page.route = "/boards" - elif index == 1: - self.page.route = "/members" - self.page.update() - - def bottom_nav_change(self, e): - index = e if (type(e) == int) else e.control.selected_index - self.top_nav_rail.selected_index = None - self.bottom_nav_rail.selected_index = index - self.page.route = f"/board/{index}" - self.page.update() diff --git a/sdk/python/examples/apps/trolli/src/user.py b/sdk/python/examples/apps/trolli/src/user.py deleted file mode 100644 index 4bd01353bd..0000000000 --- a/sdk/python/examples/apps/trolli/src/user.py +++ /dev/null @@ -1,4 +0,0 @@ -class User: - def __init__(self, name, password): - self.name = name - self.password = password diff --git a/sdk/python/examples/community/mind_queue/README.md b/sdk/python/examples/community/mind_queue/README.md index 1bafcac664..f588709d3d 100644 --- a/sdk/python/examples/community/mind_queue/README.md +++ b/sdk/python/examples/community/mind_queue/README.md @@ -44,7 +44,7 @@ It helps you organize your life into **Systems → Headers → Tasks**, with a f ## **Data Storage** -All data is stored locally in `data.json`: +All data is stored locally in `assets/data.json`: ```json { diff --git a/sdk/python/examples/community/mind_queue/assets/data.json b/sdk/python/examples/community/mind_queue/assets/data.json new file mode 100644 index 0000000000..fe159da64e --- /dev/null +++ b/sdk/python/examples/community/mind_queue/assets/data.json @@ -0,0 +1,64 @@ +{ + "House Chores": { + "Monday": [ + [ + "Morning", + "Cook Meal", + true + ], + [ + "Evening", + "Vacuum", + false + ] + ], + "Tuesday": [ + [ + "Morning", + "Cook Meal", + false + ], + [ + "Evening", + "Mopping", + true + ] + ], + "Wednesday": [ + [ + "Morning", + "Washing Clothes", + false + ], + [ + "Evening", + "Vacuum", + false + ] + ], + "Thursday": [ + [ + "Morning", + "Cook Meal", + true + ], + [ + "Evening", + "Mopping", + true + ] + ], + "Friday": [ + [ + "Morning", + "Washing Clothes", + true + ], + [ + "Evening", + "Deep Cleaning", + false + ] + ] + } +} diff --git a/sdk/python/examples/community/mind_queue/data.json b/sdk/python/examples/community/mind_queue/data.json deleted file mode 100644 index 3fea19f5d5..0000000000 --- a/sdk/python/examples/community/mind_queue/data.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "Daily Routine": { - "Morning": [ - [ - "7:00 AM", - "Wake up & Meditation", - false - ], - [ - "7:15 AM", - "Walk", - true - ], - [ - "7:45 AM", - "Breakfast", - false - ], - [ - "8:00 AM", - "Cooking & Cleaning", - true - ], - [ - "9:00 AM", - "Bath & Ready for office", - false - ] - ], - "Work": [ - [ - "10:00 AM", - "Log in", - true - ], - [ - "2:00 PM", - "Lunch", - false - ], - [ - "7:00 PM", - "Log out", - false - ] - ], - "Night": [ - [ - "8:30 PM", - "Reach Home", - false - ], - [ - "9:00 PM", - "Dinner", - false - ], - [ - "9:30 PM", - "Productivity", - true - ], - [ - "11:00 PM", - "No phone, self-care, meditation", - false - ], - [ - "12:00 AM", - "Sleep", - false - ] - ] - }, - "House Chores": { - "Monday": [ - [ - "Morning", - "Cook Meal", - true - ], - [ - "Evening", - "Vacuum", - false - ] - ], - "Tuesday": [ - [ - "Morning", - "Cook Meal", - false - ], - [ - "Evening", - "Mopping", - true - ] - ], - "Wednesday": [ - [ - "Morning", - "Washing Clothes", - false - ], - [ - "Evening", - "Vacuum", - false - ] - ], - "Thursday": [ - [ - "Morning", - "Cook Meal", - true - ], - [ - "Evening", - "Mopping", - true - ] - ], - "Friday": [ - [ - "Morning", - "Washing Clothes", - true - ], - [ - "Evening", - "Deep Cleaning", - false - ] - ] - } -} diff --git a/sdk/python/examples/community/mind_queue/main.py b/sdk/python/examples/community/mind_queue/main.py index 285ca43de0..53f785787c 100644 --- a/sdk/python/examples/community/mind_queue/main.py +++ b/sdk/python/examples/community/mind_queue/main.py @@ -6,7 +6,7 @@ import flet as ft APP_NAME = "Mind Queue" -DATA_FILE = Path(__file__).resolve().parent / "data.json" +DATA_FILE = Path(__file__).resolve().parent / "assets" / "data.json" def load_data(): @@ -25,7 +25,7 @@ def main(page: ft.Page): page.title = APP_NAME page.theme_mode = ft.ThemeMode.DARK page.bgcolor = "#111111" - page.padding = ft.padding.symmetric(horizontal=24, vertical=24) + page.padding = ft.Padding.symmetric(horizontal=24, vertical=24) page.horizontal_alignment = ft.CrossAxisAlignment.START page.vertical_alignment = ft.MainAxisAlignment.START page.scroll = ft.ScrollMode.HIDDEN @@ -165,7 +165,7 @@ async def do_clone(e): [ft.Text(system_name, size=18, weight=ft.FontWeight.W_600)], ), padding=10, - margin=ft.margin.only(bottom=8), + margin=ft.Margin.only(bottom=8), border_radius=8, bgcolor="#1c1c1c", ), @@ -221,15 +221,17 @@ def on_add(ev): ) page.add( - ft.Column( - [ - title, - ft.Column(systems_list, spacing=4), - ft.Container(height=14), - add_system_btn, - ], - spacing=12, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, + ft.SafeArea( + content=ft.Column( + [ + title, + ft.Column(systems_list, spacing=4), + ft.Container(height=14), + add_system_btn, + ], + spacing=12, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + ) ) ) page.update() @@ -593,7 +595,7 @@ def do_cancel(ev): for idx, (title_str, label, done) in enumerate(tasks): current_index = idx # capture for lambdas - color = ft.Colors.WHITE70 if done else ft.Colors.WHITE + color = ft.Colors.WHITE_70 if done else ft.Colors.WHITE deco = ( ft.TextDecoration.LINE_THROUGH if done else ft.TextDecoration.NONE ) @@ -696,7 +698,9 @@ def do_cancel(ev): ) ) - page.add(ft.Column(content_controls, spacing=6, expand=True)) + page.add( + ft.SafeArea(content=ft.Column(content_controls, spacing=6, expand=True)) + ) page.update() # start at dashboard diff --git a/sdk/python/examples/community/mind_queue/pyproject.toml b/sdk/python/examples/community/mind_queue/pyproject.toml new file mode 100644 index 0000000000..4a8236cf94 --- /dev/null +++ b/sdk/python/examples/community/mind_queue/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "community-mind-queue" +version = "1.0.0" +description = "Community productivity app for organizing systems, headers, and tasks with local JSON storage." +requires-python = ">=3.10" +keywords = ["community", "productivity", "tasks", "json storage", "dialogs"] +authors = [{ name = "Flet community", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Community/Productivity"] + +[tool.flet.metadata] +title = "Mind Queue" +controls = ["SafeArea", "Column", "Row", "Text", "TextField", "Checkbox", "FilledButton", "IconButton", "AlertDialog", "AppBar"] +layout_pattern = "list-detail" +complexity = "intermediate" +features = ["task organization", "local persistence", "dialogs"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/__init__.py b/sdk/python/examples/controls/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/ads/example_1.py b/sdk/python/examples/controls/ads/example_1.py deleted file mode 100644 index 4a5fdd5748..0000000000 --- a/sdk/python/examples/controls/ads/example_1.py +++ /dev/null @@ -1,68 +0,0 @@ -import flet as ft -import flet_ads as fta - -# Test ad unit IDs -ids = { - ft.PagePlatform.ANDROID: { - "banner": "ca-app-pub-3940256099942544/6300978111", - "interstitial": "ca-app-pub-3940256099942544/1033173712", - }, - ft.PagePlatform.IOS: { - "banner": "ca-app-pub-3940256099942544/2934735716", - "interstitial": "ca-app-pub-3940256099942544/4411468910", - }, -} - - -def main(page: ft.Page): - page.appbar = ft.AppBar( - adaptive=True, - title="Mobile Ads Playground", - bgcolor=ft.Colors.LIGHT_BLUE_300, - ) - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.scroll = ft.ScrollMode.AUTO - - def show_new_interstitial_ad(): - async def show_iad(e: ft.Event[fta.InterstitialAd]): - await iad.show() - - iad = fta.InterstitialAd( - unit_id=ids[page.platform]["interstitial"], - on_load=show_iad, - on_error=lambda e: print("InterstitialAd error", e.data), - on_open=lambda e: print("InterstitialAd opened"), - on_close=lambda e: print("InterstitialAd closed"), - on_impression=lambda e: print("InterstitialAd impression"), - on_click=lambda e: print("InterstitialAd clicked"), - ) - - def get_new_banner_ad() -> fta.BannerAd: - return fta.BannerAd( - unit_id=ids[page.platform]["banner"], - width=320, - height=50, - on_click=lambda e: print("BannerAd clicked"), - on_load=lambda e: print("BannerAd loaded"), - on_error=lambda e: print("BannerAd error", e.data), - on_open=lambda e: print("BannerAd opened"), - on_close=lambda e: print("BannerAd closed"), - on_impression=lambda e: print("BannerAd impression"), - on_will_dismiss=lambda e: print("BannerAd will dismiss"), - ) - - page.add( - ft.OutlinedButton( - content="Show InterstitialAd", - on_click=show_new_interstitial_ad, - disabled=page.web or not page.platform.is_mobile(), # mobile only - ), - ft.OutlinedButton( - content="Show BannerAd", - on_click=lambda e: page.add(get_new_banner_ad()), - disabled=page.web or not page.platform.is_mobile(), # mobile only - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/ads/example_1/main.py b/sdk/python/examples/controls/ads/example_1/main.py new file mode 100644 index 0000000000..31f03032e5 --- /dev/null +++ b/sdk/python/examples/controls/ads/example_1/main.py @@ -0,0 +1,77 @@ +import flet as ft +import flet_ads as fta + +# Test ad unit IDs +ids = { + ft.PagePlatform.ANDROID: { + "banner": "ca-app-pub-3940256099942544/6300978111", + "interstitial": "ca-app-pub-3940256099942544/1033173712", + }, + ft.PagePlatform.IOS: { + "banner": "ca-app-pub-3940256099942544/2934735716", + "interstitial": "ca-app-pub-3940256099942544/4411468910", + }, +} + + +def main(page: ft.Page): + page.appbar = ft.AppBar( + adaptive=True, + title="Mobile Ads Playground", + bgcolor=ft.Colors.LIGHT_BLUE_300, + ) + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.scroll = ft.ScrollMode.AUTO + + def show_new_interstitial_ad(): + async def show_iad(e: ft.Event[fta.InterstitialAd]): + await iad.show() + + iad = fta.InterstitialAd( + unit_id=ids[page.platform]["interstitial"], + on_load=show_iad, + on_error=lambda e: print("InterstitialAd error", e.data), + on_open=lambda e: print("InterstitialAd opened"), + on_close=lambda e: print("InterstitialAd closed"), + on_impression=lambda e: print("InterstitialAd impression"), + on_click=lambda e: print("InterstitialAd clicked"), + ) + + def get_new_banner_ad() -> fta.BannerAd: + return fta.BannerAd( + unit_id=ids[page.platform]["banner"], + width=320, + height=50, + on_click=lambda e: print("BannerAd clicked"), + on_load=lambda e: print("BannerAd loaded"), + on_error=lambda e: print("BannerAd error", e.data), + on_open=lambda e: print("BannerAd opened"), + on_close=lambda e: print("BannerAd closed"), + on_impression=lambda e: print("BannerAd impression"), + on_will_dismiss=lambda e: print("BannerAd will dismiss"), + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.OutlinedButton( + content="Show InterstitialAd", + on_click=show_new_interstitial_ad, + disabled=page.web + or not page.platform.is_mobile(), # mobile only + ), + ft.OutlinedButton( + content="Show BannerAd", + on_click=lambda e: page.add(get_new_banner_ad()), + disabled=page.web + or not page.platform.is_mobile(), # mobile only + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/ads/example_1/pyproject.toml b/sdk/python/examples/controls/ads/example_1/pyproject.toml new file mode 100644 index 0000000000..33950f8d18 --- /dev/null +++ b/sdk/python/examples/controls/ads/example_1/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "ads-example-1" +version = "1.0.0" +description = "Mobile ads example with BannerAd and InterstitialAd." +requires-python = ">=3.10" +keywords = ["ads", "banner ad", "interstitial ad", "admob", "mobile"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-ads"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Ads"] + +[tool.flet.metadata] +title = "BannerAd and InterstitialAd" +controls = ["SafeArea", "Column", "OutlinedButton", "AppBar", "BannerAd", "InterstitialAd"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["banner ad loading", "interstitial ad loading", "ad lifecycle callbacks", "platform-specific ad units"] + +[tool.flet] +platforms = ["web", "ios", "android"] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/alert_dialog/__init__.py b/sdk/python/examples/controls/alert_dialog/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/alert_dialog/modal_and_non_modal.py b/sdk/python/examples/controls/alert_dialog/modal_and_non_modal.py deleted file mode 100644 index baf7df861a..0000000000 --- a/sdk/python/examples/controls/alert_dialog/modal_and_non_modal.py +++ /dev/null @@ -1,40 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "AlertDialog examples" - - dialog = ft.AlertDialog( - title=ft.Text("Hello"), - content=ft.Text("You are notified!"), - alignment=ft.Alignment.CENTER, - on_dismiss=lambda e: print("Dialog dismissed!"), - title_padding=ft.Padding.all(25), - ) - - modal_dialog = ft.AlertDialog( - modal=True, - title=ft.Text("Please confirm"), - content=ft.Text("Do you really want to delete all those files?"), - actions=[ - ft.TextButton("Yes", on_click=lambda e: page.pop_dialog()), - ft.TextButton("No", on_click=lambda e: page.pop_dialog()), - ], - actions_alignment=ft.MainAxisAlignment.END, - on_dismiss=lambda e: print("Modal dialog dismissed!"), - ) - - page.add( - ft.Button( - content="Open dialog", - on_click=lambda e: page.show_dialog(dialog), - ), - ft.Button( - content="Open modal dialog", - on_click=lambda e: page.show_dialog(modal_dialog), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/alert_dialog/modal_and_non_modal/main.py b/sdk/python/examples/controls/alert_dialog/modal_and_non_modal/main.py new file mode 100644 index 0000000000..138ade206d --- /dev/null +++ b/sdk/python/examples/controls/alert_dialog/modal_and_non_modal/main.py @@ -0,0 +1,46 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "AlertDialog examples" + + dialog = ft.AlertDialog( + title=ft.Text("Hello"), + content=ft.Text("You are notified!"), + alignment=ft.Alignment.CENTER, + on_dismiss=lambda e: print("Dialog dismissed!"), + title_padding=ft.Padding.all(25), + ) + + modal_dialog = ft.AlertDialog( + modal=True, + title=ft.Text("Please confirm"), + content=ft.Text("Do you really want to delete all those files?"), + actions=[ + ft.TextButton("Yes", on_click=lambda e: page.pop_dialog()), + ft.TextButton("No", on_click=lambda e: page.pop_dialog()), + ], + actions_alignment=ft.MainAxisAlignment.END, + on_dismiss=lambda e: print("Modal dialog dismissed!"), + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + content="Open dialog", + on_click=lambda e: page.show_dialog(dialog), + ), + ft.Button( + content="Open modal dialog", + on_click=lambda e: page.show_dialog(modal_dialog), + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/alert_dialog/modal_and_non_modal/pyproject.toml b/sdk/python/examples/controls/alert_dialog/modal_and_non_modal/pyproject.toml new file mode 100644 index 0000000000..cc9dd91aec --- /dev/null +++ b/sdk/python/examples/controls/alert_dialog/modal_and_non_modal/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "alert-dialog-modal-and-non-modal" +version = "1.0.0" +description = "AlertDialog example showing modal and non-modal dialog behavior." +requires-python = ">=3.10" +keywords = ["alertdialog", "dialog", "modal", "material", "actions"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/AlertDialog"] + +[tool.flet.metadata] +title = "Modal and non-modal dialogs" +controls = ["SafeArea", "Column", "Button", "AlertDialog", "Text", "TextButton"] +layout_pattern = "dialog-flow" +complexity = "basic" +features = ["modal dialog", "non-modal dialog", "dialog actions", "dismiss events"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/animated_switcher/image_switch.py b/sdk/python/examples/controls/animated_switcher/image_switch.py deleted file mode 100644 index 57dd38a392..0000000000 --- a/sdk/python/examples/controls/animated_switcher/image_switch.py +++ /dev/null @@ -1,32 +0,0 @@ -import time - -import flet as ft - - -def main(page: ft.Page): - def animate(e: ft.Event[ft.Button]): - switcher.content = ft.Image( - src=f"https://picsum.photos/200/300?{time.time()}", - width=200, - height=300, - ) - page.update() - - page.add( - switcher := ft.AnimatedSwitcher( - content=ft.Image( - src="https://picsum.photos/200/300", - width=200, - height=300, - ), - transition=ft.AnimatedSwitcherTransition.SCALE, - duration=500, - reverse_duration=100, - switch_in_curve=ft.AnimationCurve.BOUNCE_OUT, - switch_out_curve=ft.AnimationCurve.BOUNCE_IN, - ), - ft.Button("Animate!", on_click=animate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/animated_switcher/image_switch/main.py b/sdk/python/examples/controls/animated_switcher/image_switch/main.py new file mode 100644 index 0000000000..400b957016 --- /dev/null +++ b/sdk/python/examples/controls/animated_switcher/image_switch/main.py @@ -0,0 +1,38 @@ +import time + +import flet as ft + + +def main(page: ft.Page): + def animate(e: ft.Event[ft.Button]): + switcher.content = ft.Image( + src=f"https://picsum.photos/200/300?{time.time()}", + width=200, + height=300, + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + switcher := ft.AnimatedSwitcher( + content=ft.Image( + src="https://picsum.photos/200/300", + width=200, + height=300, + ), + transition=ft.AnimatedSwitcherTransition.SCALE, + duration=500, + reverse_duration=100, + switch_in_curve=ft.AnimationCurve.BOUNCE_OUT, + switch_out_curve=ft.AnimationCurve.BOUNCE_IN, + ), + ft.Button("Animate!", on_click=animate), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/animated_switcher/image_switch/pyproject.toml b/sdk/python/examples/controls/animated_switcher/image_switch/pyproject.toml new file mode 100644 index 0000000000..1c92219c45 --- /dev/null +++ b/sdk/python/examples/controls/animated_switcher/image_switch/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "animated-switcher-image-switch" +version = "1.0.0" +description = "Animated image switching with scale transition." +requires-python = ">=3.10" +keywords = ["animated switcher", "animation", "image", "transition", "scale"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Animations/AnimatedSwitcher"] + +[tool.flet.metadata] +title = "Animate image switch" +controls = ["SafeArea", "Column", "AnimatedSwitcher", "Image", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["dynamic image source", "scale transition", "button-triggered animation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/animated_switcher/image_switch_buffered.py b/sdk/python/examples/controls/animated_switcher/image_switch_buffered.py deleted file mode 100644 index 203b732c5d..0000000000 --- a/sdk/python/examples/controls/animated_switcher/image_switch_buffered.py +++ /dev/null @@ -1,66 +0,0 @@ -import base64 -import time - -import httpx - -import flet as ft - - -class BufferingSwitcher(ft.AnimatedSwitcher): - image_queue = [] - - def __init__(self, image: ft.Image): - super().__init__(image) - self.transition = ft.AnimatedSwitcherTransition.SCALE - self.duration = 500 - self.reverse_duration = 100 - self.switch_in_curve = ft.AnimationCurve.EASE_IN - self.switch_out_curve = ft.AnimationCurve.EASE_OUT - self.image_queue.append(image) - - def animate(self, e): - self.content = ft.Image( - src=self.image_queue.pop(), - width=200, - height=300, - gapless_playback=True, - ) - self.update() - - async def fill_queue(self): - while len(self.image_queue) < 10: - self.image_queue.append( - await self.image_to_base64( - f"https://picsum.photos/200/300?{time.time()}" - ) - ) - - async def image_to_base64(self, url): - response = await httpx.AsyncClient(follow_redirects=True).get(url) - if response.status_code == 200: - base64_str = ( - base64.standard_b64encode(response.content).decode("utf-8").strip() - ) - return base64_str - else: - print(f"Image request failed with {response.status_code}") - return None - - def before_update(self): - self.page.run_task(self.fill_queue) - - -def main(page: ft.Page): - switcher = BufferingSwitcher( - ft.Image( - src=f"https://picsum.photos/200/300?{time.time()}", width=200, height=300 - ) - ) - - page.add( - switcher, - ft.Button("Animate!", on_click=switcher.animate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/animated_switcher/image_switch_buffered/main.py b/sdk/python/examples/controls/animated_switcher/image_switch_buffered/main.py new file mode 100644 index 0000000000..436f5867dd --- /dev/null +++ b/sdk/python/examples/controls/animated_switcher/image_switch_buffered/main.py @@ -0,0 +1,78 @@ +import base64 +import time +from dataclasses import field + +import httpx + +import flet as ft + + +@ft.control +class BufferingSwitcher(ft.AnimatedSwitcher): + content: ft.Image | None = None + image_queue: list[str] = field(default_factory=list) + + def init(self): + self.transition = ft.AnimatedSwitcherTransition.SCALE + self.duration = 500 + self.reverse_duration = 100 + self.switch_in_curve = ft.AnimationCurve.EASE_IN + self.switch_out_curve = ft.AnimationCurve.EASE_OUT + if self.content and self.content.src: + self.image_queue.append(self.content.src) + + def animate(self, e): + self.content = ft.Image( + src=self.image_queue.pop(), + width=200, + height=300, + gapless_playback=True, + ) + self.update() + + async def fill_queue(self): + while len(self.image_queue) < 10: + image_base64 = await self.image_to_base64( + f"https://picsum.photos/200/300?{time.time()}" + ) + if image_base64: + self.image_queue.append(image_base64) + + async def image_to_base64(self, url): + response = await httpx.AsyncClient(follow_redirects=True).get(url) + if response.status_code == 200: + base64_str = ( + base64.standard_b64encode(response.content).decode("utf-8").strip() + ) + return base64_str + else: + print(f"Image request failed with {response.status_code}") + return None + + def before_update(self): + self.page.run_task(self.fill_queue) + + +def main(page: ft.Page): + switcher = BufferingSwitcher( + content=ft.Image( + src=f"https://picsum.photos/200/300?{time.time()}", + width=200, + height=300, + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + switcher, + ft.Button("Animate!", on_click=switcher.animate), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/animated_switcher/image_switch_buffered/pyproject.toml b/sdk/python/examples/controls/animated_switcher/image_switch_buffered/pyproject.toml new file mode 100644 index 0000000000..4096f8f44a --- /dev/null +++ b/sdk/python/examples/controls/animated_switcher/image_switch_buffered/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "animated-switcher-image-switch-buffered" +version = "1.0.0" +description = "Buffered AnimatedSwitcher image transitions with preloaded base64 images." +requires-python = ">=3.10" +keywords = ["animated switcher", "animation", "image", "buffering", "async", "httpx"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "httpx"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Animations/AnimatedSwitcher"] + +[tool.flet.metadata] +title = "Animate image switch buffered" +controls = ["SafeArea", "Column", "AnimatedSwitcher", "Image", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["@ft.control custom switcher", "buffered image queue", "async preloading", "gapless playback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/animated_switcher/scale_effect.py b/sdk/python/examples/controls/animated_switcher/scale_effect.py deleted file mode 100644 index eeef63638f..0000000000 --- a/sdk/python/examples/controls/animated_switcher/scale_effect.py +++ /dev/null @@ -1,51 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - c1 = ft.Container( - content=ft.Text("Hello!", theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM), - alignment=ft.Alignment.CENTER, - width=200, - height=200, - bgcolor=ft.Colors.GREEN, - ) - c2 = ft.Container( - content=ft.Text("Bye!", size=50), - alignment=ft.Alignment.CENTER, - width=200, - height=200, - bgcolor=ft.Colors.YELLOW, - ) - switcher = ft.AnimatedSwitcher( - content=c1, - transition=ft.AnimatedSwitcherTransition.SCALE, - duration=500, - reverse_duration=100, - switch_in_curve=ft.AnimationCurve.BOUNCE_OUT, - switch_out_curve=ft.AnimationCurve.BOUNCE_IN, - ) - - def scale(e): - switcher.content = c2 if switcher.content == c1 else c1 - switcher.transition = ft.AnimatedSwitcherTransition.SCALE - switcher.update() - - def fade(e): - switcher.content = c2 if switcher.content == c1 else c1 - switcher.transition = ft.AnimatedSwitcherTransition.FADE - switcher.update() - - def rotate(e): - switcher.content = c2 if switcher.content == c1 else c1 - switcher.transition = ft.AnimatedSwitcherTransition.ROTATION - switcher.update() - - page.add( - switcher, - ft.Button("Scale", on_click=scale), - ft.Button("Fade", on_click=fade), - ft.Button("Rotate", on_click=rotate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/animated_switcher/scale_effect/main.py b/sdk/python/examples/controls/animated_switcher/scale_effect/main.py new file mode 100644 index 0000000000..b0b4bb72ec --- /dev/null +++ b/sdk/python/examples/controls/animated_switcher/scale_effect/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def main(page: ft.Page): + c1 = ft.Container( + content=ft.Text("Hello!", theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM), + alignment=ft.Alignment.CENTER, + width=200, + height=200, + bgcolor=ft.Colors.GREEN, + ) + c2 = ft.Container( + content=ft.Text("Bye!", size=50), + alignment=ft.Alignment.CENTER, + width=200, + height=200, + bgcolor=ft.Colors.YELLOW, + ) + switcher = ft.AnimatedSwitcher( + content=c1, + transition=ft.AnimatedSwitcherTransition.SCALE, + duration=500, + reverse_duration=100, + switch_in_curve=ft.AnimationCurve.BOUNCE_OUT, + switch_out_curve=ft.AnimationCurve.BOUNCE_IN, + ) + + def scale(e): + switcher.content = c2 if switcher.content == c1 else c1 + switcher.transition = ft.AnimatedSwitcherTransition.SCALE + switcher.update() + + def fade(e): + switcher.content = c2 if switcher.content == c1 else c1 + switcher.transition = ft.AnimatedSwitcherTransition.FADE + switcher.update() + + def rotate(e): + switcher.content = c2 if switcher.content == c1 else c1 + switcher.transition = ft.AnimatedSwitcherTransition.ROTATION + switcher.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + switcher, + ft.Button("Scale", on_click=scale), + ft.Button("Fade", on_click=fade), + ft.Button("Rotate", on_click=rotate), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/animated_switcher/scale_effect/pyproject.toml b/sdk/python/examples/controls/animated_switcher/scale_effect/pyproject.toml new file mode 100644 index 0000000000..e8f25dc950 --- /dev/null +++ b/sdk/python/examples/controls/animated_switcher/scale_effect/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "animated-switcher-scale-effect" +version = "1.0.0" +description = "AnimatedSwitcher transitions between two containers with multiple effects." +requires-python = ">=3.10" +keywords = ["animated switcher", "animation", "scale", "fade", "rotation", "transition"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Animations/AnimatedSwitcher"] + +[tool.flet.metadata] +title = "Scale, fade, and rotate effects" +controls = ["SafeArea", "Column", "Container", "Text", "AnimatedSwitcher", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["multiple transitions", "animated content swap", "button-triggered effects"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/app_bar/actions_and_popup_menu.py b/sdk/python/examples/controls/app_bar/actions_and_popup_menu.py deleted file mode 100644 index ef6b5a405f..0000000000 --- a/sdk/python/examples/controls/app_bar/actions_and_popup_menu.py +++ /dev/null @@ -1,36 +0,0 @@ -import flet as ft - -def main(page: ft.Page): - page.title = "AppBar Example" - - def handle_checked_item_click(e: ft.Event[ft.PopupMenuItem]): - e.control.checked = not e.control.checked - page.update() - - page.appbar = ft.AppBar( - leading=ft.Icon(ft.Icons.PALETTE), - leading_width=40, - title=ft.Text("AppBar Example"), - center_title=False, - bgcolor=ft.Colors.BLUE_GREY_400, - actions=[ - ft.IconButton(ft.Icons.WB_SUNNY_OUTLINED), - ft.IconButton(ft.Icons.FILTER_3), - ft.PopupMenuButton( - items=[ - ft.PopupMenuItem(content="Item 1"), - ft.PopupMenuItem(), # divider - ft.PopupMenuItem( - content="Checked item", - checked=False, - on_click=handle_checked_item_click, - ), - ] - ), - ], - ) - page.add(ft.Text("Body!")) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/app_bar/actions_and_popup_menu/main.py b/sdk/python/examples/controls/app_bar/actions_and_popup_menu/main.py new file mode 100644 index 0000000000..eede1872ae --- /dev/null +++ b/sdk/python/examples/controls/app_bar/actions_and_popup_menu/main.py @@ -0,0 +1,36 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "AppBar Example" + + def handle_checked_item_click(e: ft.Event[ft.PopupMenuItem]): + e.control.checked = not e.control.checked + + page.appbar = ft.AppBar( + leading=ft.Icon(ft.Icons.PALETTE), + leading_width=40, + title=ft.Text("AppBar Example"), + center_title=False, + bgcolor=ft.Colors.BLUE_GREY_400, + actions=[ + ft.IconButton(ft.Icons.WB_SUNNY_OUTLINED), + ft.IconButton(ft.Icons.FILTER_3), + ft.PopupMenuButton( + items=[ + ft.PopupMenuItem(content="Item 1"), + ft.PopupMenuItem(), # divider + ft.PopupMenuItem( + content="Checked item", + checked=False, + on_click=handle_checked_item_click, + ), + ] + ), + ], + ) + page.add(ft.SafeArea(content=ft.Column(controls=[ft.Text("Body!")]))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/app_bar/actions_and_popup_menu/pyproject.toml b/sdk/python/examples/controls/app_bar/actions_and_popup_menu/pyproject.toml new file mode 100644 index 0000000000..78c52f2a25 --- /dev/null +++ b/sdk/python/examples/controls/app_bar/actions_and_popup_menu/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "app-bar-actions-and-popup-menu" +version = "1.0.0" +description = "AppBar with action icons and popup menu interactions." +requires-python = ">=3.10" +keywords = ["appbar", "navigation", "popup menu", "actions", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/AppBar"] + +[tool.flet.metadata] +title = "Actions and popup menu" +controls = ["AppBar", "Icon", "IconButton", "PopupMenuButton", "PopupMenuItem", "SafeArea", "Column", "Text"] +layout_pattern = "toolbar-actions" +complexity = "basic" +features = ["popup menu actions", "checked state toggle", "app bar actions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/app_bar/theme_and_material_mode_toggles.py b/sdk/python/examples/controls/app_bar/theme_and_material_mode_toggles.py deleted file mode 100644 index b2eda194b8..0000000000 --- a/sdk/python/examples/controls/app_bar/theme_and_material_mode_toggles.py +++ /dev/null @@ -1,111 +0,0 @@ -import flet as ft - -LIGHT_SEED_COLOR = ft.Colors.DEEP_ORANGE -DARK_SEED_COLOR = ft.Colors.DEEP_PURPLE - - -def main(page: ft.Page): - page.title = "AppBar Example" - - def handle_checked_item_click(e: ft.Event[ft.PopupMenuItem]): - e.control.checked = not e.control.checked - page.update() - - page.theme_mode = ft.ThemeMode.LIGHT - page.theme = ft.Theme(color_scheme_seed=LIGHT_SEED_COLOR, use_material3=False) - page.dark_theme = ft.Theme(color_scheme_seed=DARK_SEED_COLOR, use_material3=False) - page.update() - - def toggle_theme_mode(e: ft.Event[ft.IconButton]): - page.theme_mode = ( - ft.ThemeMode.DARK - if page.theme_mode == ft.ThemeMode.LIGHT - else ft.ThemeMode.LIGHT - ) - theme_mode_toggle.icon = ( - ft.Icons.WB_SUNNY_OUTLINED - if page.theme_mode == ft.ThemeMode.LIGHT - else ft.Icons.WB_SUNNY - ) - page.update() - - theme_mode_toggle = ft.IconButton( - icon=( - ft.Icons.WB_SUNNY_OUTLINED - if page.theme_mode == ft.ThemeMode.LIGHT - else ft.Icons.WB_SUNNY - ), - on_click=toggle_theme_mode, - ) - - def toggle_material(e: ft.Event[ft.IconButton]): - use_material3 = not page.theme.use_material3 - page.theme = ft.Theme( - color_scheme_seed=LIGHT_SEED_COLOR, use_material3=use_material3 - ) - page.dark_theme = ft.Theme( - color_scheme_seed=DARK_SEED_COLOR, use_material3=use_material3 - ) - material_mode_toggle.icon = ( - ft.Icons.FILTER_3 if page.theme.use_material3 else ft.Icons.FILTER_2 - ) - page.update() - - material_mode_toggle = ft.IconButton( - icon=ft.Icons.FILTER_3 if page.theme.use_material3 else ft.Icons.FILTER_2, - on_click=toggle_material, - ) - - page.padding = 50 - page.appbar = ft.AppBar( - # toolbar_height=100, - bgcolor=ft.Colors.SECONDARY_CONTAINER, - leading=ft.Icon(ft.Icons.PALETTE), - leading_width=40, - title=ft.Text("AppBar Example"), - center_title=False, - actions=[ - theme_mode_toggle, - material_mode_toggle, - ft.PopupMenuButton( - items=[ - ft.PopupMenuItem(content="Item 1"), - ft.PopupMenuItem(icon=ft.Icons.POWER_INPUT, content="Check power"), - ft.PopupMenuItem( - on_click=lambda _: print( - "Button with a custom content clicked!" - ), - content=ft.Row( - controls=[ - ft.Icon(ft.Icons.HOURGLASS_TOP_OUTLINED), - ft.Text("Item with a custom content"), - ] - ), - ), - ft.PopupMenuItem(), # divider - ft.PopupMenuItem( - content="Checked item", - checked=False, - on_click=handle_checked_item_click, - ), - ] - ), - ], - ) - page.add( - ft.Text( - value="Flet is a framework that allows building web, desktop and mobile " - "applications in Python without prior experience in frontend development." - "You can build a UI for your program with Flet controls which are based " - "on Flutter by Google. Flet goes beyond merely wrapping Flutter widgets. " - "It adds its own touch by combining smaller widgets, simplifying " - "complexities, implementing UI best practices, and applying sensible " - "defaults. This ensures that your applications look stylish and polished " - "without requiring additional design efforts on your part.", - text_align=ft.TextAlign.END, - ), - ft.Button("Click me!"), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/app_bar/theme_mode_toggle/main.py b/sdk/python/examples/controls/app_bar/theme_mode_toggle/main.py new file mode 100644 index 0000000000..cf1ed36311 --- /dev/null +++ b/sdk/python/examples/controls/app_bar/theme_mode_toggle/main.py @@ -0,0 +1,98 @@ +import flet as ft + +LIGHT_SEED_COLOR = ft.Colors.DEEP_ORANGE +DARK_SEED_COLOR = ft.Colors.DEEP_PURPLE + + +def main(page: ft.Page): + page.title = "AppBar Example" + + def handle_checked_item_click(e: ft.Event[ft.PopupMenuItem]): + e.control.checked = not e.control.checked + + page.theme_mode = ft.ThemeMode.LIGHT + page.theme = ft.Theme(color_scheme_seed=LIGHT_SEED_COLOR) + page.dark_theme = ft.Theme(color_scheme_seed=DARK_SEED_COLOR) + + def toggle_theme_mode(e: ft.Event[ft.IconButton]): + page.theme_mode = ( + ft.ThemeMode.DARK + if page.theme_mode == ft.ThemeMode.LIGHT + else ft.ThemeMode.LIGHT + ) + theme_mode_toggle.icon = ( + ft.Icons.WB_SUNNY_OUTLINED + if page.theme_mode == ft.ThemeMode.LIGHT + else ft.Icons.WB_SUNNY + ) + + theme_mode_toggle = ft.IconButton( + icon=( + ft.Icons.WB_SUNNY_OUTLINED + if page.theme_mode == ft.ThemeMode.LIGHT + else ft.Icons.WB_SUNNY + ), + on_click=toggle_theme_mode, + ) + + page.padding = 50 + page.appbar = ft.AppBar( + # toolbar_height=100, + bgcolor=ft.Colors.SECONDARY_CONTAINER, + leading=ft.Icon(ft.Icons.PALETTE), + leading_width=40, + title=ft.Text("AppBar Example"), + center_title=False, + actions=[ + theme_mode_toggle, + ft.PopupMenuButton( + items=[ + ft.PopupMenuItem(content="Item 1"), + ft.PopupMenuItem(icon=ft.Icons.POWER_INPUT, content="Check power"), + ft.PopupMenuItem( + on_click=lambda _: print( + "Button with a custom content clicked!" + ), + content=ft.Row( + controls=[ + ft.Icon(ft.Icons.HOURGLASS_TOP_OUTLINED), + ft.Text("Item with a custom content"), + ] + ), + ), + ft.PopupMenuItem(), # divider + ft.PopupMenuItem( + content="Checked item", + checked=False, + on_click=handle_checked_item_click, + ), + ] + ), + ], + ) + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + value="Flet is a framework that allows building web, desktop " + "and mobile applications in Python without prior experience " + "in frontend development.You can build a UI for your program " + "with Flet controls which are based on Flutter by Google. " + "Flet goes beyond merely wrapping Flutter widgets. It adds " + "its own touch by combining smaller widgets, simplifying " + "complexities, implementing UI best practices, and applying " + "sensible defaults. This ensures that your applications look " + "stylish and polished without requiring additional design " + "efforts on your part.", + text_align=ft.TextAlign.END, + ), + ft.Button("Click me!"), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/app_bar/theme_mode_toggle/pyproject.toml b/sdk/python/examples/controls/app_bar/theme_mode_toggle/pyproject.toml new file mode 100644 index 0000000000..03a0df11f3 --- /dev/null +++ b/sdk/python/examples/controls/app_bar/theme_mode_toggle/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "app-bar-theme-mode-toggle" +version = "1.0.0" +description = "AppBar example with theme mode toggle and popup menu actions." +requires-python = ">=3.10" +keywords = ["appbar", "navigation", "theme mode", "popup menu"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/AppBar"] + +[tool.flet.metadata] +title = "Theme mode toggle" +controls = ["AppBar", "Icon", "IconButton", "PopupMenuButton", "PopupMenuItem", "SafeArea", "Column", "Text", "Button", "Theme"] +layout_pattern = "toolbar-actions" +complexity = "basic" +features = ["theme mode toggle", "popup menu interactions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/auto_complete/basic.py b/sdk/python/examples/controls/auto_complete/basic.py deleted file mode 100644 index 750a331e4d..0000000000 --- a/sdk/python/examples/controls/auto_complete/basic.py +++ /dev/null @@ -1,46 +0,0 @@ -import flet as ft - -numbers = [ - ("one 1", "One"), - ("two 2", "Two"), - ("three 3", "Three"), - ("four 4", "Four"), - ("five 5", "Five"), - ("six 6", "Six"), - ("seven 7", "Seven"), - ("eight 8", "Eight"), - ("nine 9", "Nine"), - ("ten 10", "Ten"), -] - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - def handle_change(e: ft.Event[ft.AutoComplete]): - info.value = f"Current input 👀: {e.data!r} \n" - - def handle_select(e: ft.AutoCompleteSelectEvent): - info.value = ( - f"Current input 👀: {e.control.value!r} \n" - f"Your selection: {e.selection.value}" - ) - - page.add( - ft.AutoComplete( - value="One", - width=200, - on_change=handle_change, - on_select=handle_select, - suggestions=[ - ft.AutoCompleteSuggestion(key=key, value=value) - for key, value in numbers - ], - ), - info := ft.Text("Enter a number (in words or digits) to get suggestions."), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/auto_complete/basic/main.py b/sdk/python/examples/controls/auto_complete/basic/main.py new file mode 100644 index 0000000000..7edc3f42bc --- /dev/null +++ b/sdk/python/examples/controls/auto_complete/basic/main.py @@ -0,0 +1,55 @@ +import flet as ft + +numbers = [ + ("one 1", "One"), + ("two 2", "Two"), + ("three 3", "Three"), + ("four 4", "Four"), + ("five 5", "Five"), + ("six 6", "Six"), + ("seven 7", "Seven"), + ("eight 8", "Eight"), + ("nine 9", "Nine"), + ("ten 10", "Ten"), +] + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + def handle_change(e: ft.Event[ft.AutoComplete]): + info.value = f"Current input 👀: {e.data!r} \n" + + def handle_select(e: ft.AutoCompleteSelectEvent): + info.value = ( + f"Current input 👀: {e.control.value!r} \n" + f"Your selection: {e.selection.value}" + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.AutoComplete( + value="One", + width=200, + on_change=handle_change, + on_select=handle_select, + suggestions=[ + ft.AutoCompleteSuggestion(key=key, value=value) + for key, value in numbers + ], + ), + info := ft.Text( + "Enter a number (in words or digits) to get suggestions." + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/auto_complete/basic/pyproject.toml b/sdk/python/examples/controls/auto_complete/basic/pyproject.toml new file mode 100644 index 0000000000..f5e35a1a6c --- /dev/null +++ b/sdk/python/examples/controls/auto_complete/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "auto-complete-basic" +version = "1.0.0" +description = "Basic AutoComplete with change and select event handling." +requires-python = ">=3.10" +keywords = ["autocomplete", "input", "suggestions", "events", "selection"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/AutoComplete"] + +[tool.flet.metadata] +title = "Basic AutoComplete" +controls = ["SafeArea", "Column", "AutoComplete", "AutoCompleteSuggestion", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["input suggestions", "on_change handling", "on_select handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/autofill_group/basic.py b/sdk/python/examples/controls/autofill_group/basic.py deleted file mode 100644 index 0d630481b8..0000000000 --- a/sdk/python/examples/controls/autofill_group/basic.py +++ /dev/null @@ -1,35 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.AutofillGroup( - content=ft.Column( - controls=[ - ft.TextField( - label="Name", - autofill_hints=ft.AutofillHint.NAME, - ), - ft.TextField( - label="Email", - autofill_hints=[ft.AutofillHint.EMAIL], - ), - ft.TextField( - label="Phone Number", - autofill_hints=[ft.AutofillHint.TELEPHONE_NUMBER], - ), - ft.TextField( - label="Street Address", - autofill_hints=ft.AutofillHint.FULL_STREET_ADDRESS, - ), - ft.TextField( - label="Postal Code", - autofill_hints=ft.AutofillHint.POSTAL_CODE, - ), - ] - ) - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/autofill_group/basic/main.py b/sdk/python/examples/controls/autofill_group/basic/main.py new file mode 100644 index 0000000000..b391f4f215 --- /dev/null +++ b/sdk/python/examples/controls/autofill_group/basic/main.py @@ -0,0 +1,38 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.AutofillGroup( + content=ft.Column( + controls=[ + ft.TextField( + label="Name", + autofill_hints=ft.AutofillHint.NAME, + ), + ft.TextField( + label="Email", + autofill_hints=[ft.AutofillHint.EMAIL], + ), + ft.TextField( + label="Phone Number", + autofill_hints=[ft.AutofillHint.TELEPHONE_NUMBER], + ), + ft.TextField( + label="Street Address", + autofill_hints=ft.AutofillHint.FULL_STREET_ADDRESS, + ), + ft.TextField( + label="Postal Code", + autofill_hints=ft.AutofillHint.POSTAL_CODE, + ), + ] + ) + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/autofill_group/basic/pyproject.toml b/sdk/python/examples/controls/autofill_group/basic/pyproject.toml new file mode 100644 index 0000000000..3127e997f6 --- /dev/null +++ b/sdk/python/examples/controls/autofill_group/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "autofill-group-basic" +version = "1.0.0" +description = "Basic AutofillGroup with common autofill hints." +requires-python = ">=3.10" +keywords = ["autofillgroup", "autofill", "input", "text field", "form"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/AutofillGroup"] + +[tool.flet.metadata] +title = "Basic AutofillGroup" +controls = ["SafeArea", "AutofillGroup", "Column", "TextField"] +layout_pattern = "form" +complexity = "basic" +features = ["autofill hints", "grouped form fields"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/badge/__init__.py b/sdk/python/examples/controls/badge/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/badge/basic.py b/sdk/python/examples/controls/badge/basic.py deleted file mode 100644 index 2c2972e81e..0000000000 --- a/sdk/python/examples/controls/badge/basic.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Badge example" - page.navigation_bar = ft.NavigationBar( - destinations=[ - ft.NavigationBarDestination( - icon=ft.Icon( - ft.Icons.EXPLORE, - badge=ft.Badge(small_size=10), - ), - label="Explore", - ), - ft.NavigationBarDestination( - icon=ft.Icons.COMMUTE, - label="Commute", - ), - ft.NavigationBarDestination( - icon=ft.Icon( - ft.Icons.PHONE, - badge="10", - ) - ), - ] - ) - page.add(ft.Text("Body!")) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/badge/basic/main.py b/sdk/python/examples/controls/badge/basic/main.py new file mode 100644 index 0000000000..1bcb907ef2 --- /dev/null +++ b/sdk/python/examples/controls/badge/basic/main.py @@ -0,0 +1,31 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Badge example" + page.navigation_bar = ft.NavigationBar( + destinations=[ + ft.NavigationBarDestination( + icon=ft.Icon( + ft.Icons.EXPLORE, + badge=ft.Badge(small_size=10), + ), + label="Explore", + ), + ft.NavigationBarDestination( + icon=ft.Icons.COMMUTE, + label="Commute", + ), + ft.NavigationBarDestination( + icon=ft.Icon( + ft.Icons.PHONE, + badge="10", + ) + ), + ] + ) + page.add(ft.SafeArea(content=ft.Column(controls=[ft.Text("Body!")]))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/badge/basic/pyproject.toml b/sdk/python/examples/controls/badge/basic/pyproject.toml new file mode 100644 index 0000000000..1172027519 --- /dev/null +++ b/sdk/python/examples/controls/badge/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "badge-basic" +version = "1.0.0" +description = "Basic Badge usage on NavigationBar icons." +requires-python = ">=3.10" +keywords = ["badge", "display", "navigation bar", "icon", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Badge"] + +[tool.flet.metadata] +title = "Basic Badge" +controls = ["NavigationBar", "NavigationBarDestination", "Icon", "Badge", "SafeArea", "Column", "Text"] +layout_pattern = "navigation" +complexity = "basic" +features = ["badge on icon", "text badge", "small badge indicator"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/banner/basic.py b/sdk/python/examples/controls/banner/basic.py deleted file mode 100644 index b9512478d6..0000000000 --- a/sdk/python/examples/controls/banner/basic.py +++ /dev/null @@ -1,45 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_banner_close(e: ft.Event[ft.TextButton]): - page.pop_dialog() - page.add(ft.Text("Action clicked: " + e.control.data)) - - action_button_style = ft.ButtonStyle(color=ft.Colors.BLUE) - banner = ft.Banner( - bgcolor=ft.Colors.AMBER_100, - leading=ft.Icon(ft.Icons.WARNING_AMBER_ROUNDED, color=ft.Colors.AMBER, size=40), - content=ft.Text( - value="Oops, there were some errors while trying to delete the file. " - "What would you like to do?", - color=ft.Colors.BLACK, - ), - actions=[ - ft.TextButton( - content="Retry", - style=action_button_style, - on_click=handle_banner_close, - data="retry", - ), - ft.TextButton( - content="Ignore", - style=action_button_style, - on_click=handle_banner_close, - data="ignore", - ), - ft.TextButton( - content="Cancel", - style=action_button_style, - on_click=handle_banner_close, - data="cancel", - ), - ], - ) - - page.add(ft.Button("Show Banner", on_click=lambda e: page.show_dialog(banner))) - - -ft.run(main) diff --git a/sdk/python/examples/controls/banner/basic/main.py b/sdk/python/examples/controls/banner/basic/main.py new file mode 100644 index 0000000000..38fe8f4578 --- /dev/null +++ b/sdk/python/examples/controls/banner/basic/main.py @@ -0,0 +1,56 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def handle_banner_close(e: ft.Event[ft.TextButton]): + page.pop_dialog() + page.add(ft.Text("Action clicked: " + e.control.data)) + + action_button_style = ft.ButtonStyle(color=ft.Colors.BLUE) + banner = ft.Banner( + bgcolor=ft.Colors.AMBER_100, + leading=ft.Icon(ft.Icons.WARNING_AMBER_ROUNDED, color=ft.Colors.AMBER, size=40), + content=ft.Text( + value="Oops, there were some errors while trying to delete the file. " + "What would you like to do?", + color=ft.Colors.BLACK, + ), + actions=[ + ft.TextButton( + content="Retry", + style=action_button_style, + on_click=handle_banner_close, + data="retry", + ), + ft.TextButton( + content="Ignore", + style=action_button_style, + on_click=handle_banner_close, + data="ignore", + ), + ft.TextButton( + content="Cancel", + style=action_button_style, + on_click=handle_banner_close, + data="cancel", + ), + ], + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + "Show Banner", on_click=lambda e: page.show_dialog(banner) + ) + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/banner/basic/pyproject.toml b/sdk/python/examples/controls/banner/basic/pyproject.toml new file mode 100644 index 0000000000..42eda939e0 --- /dev/null +++ b/sdk/python/examples/controls/banner/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "banner-basic" +version = "1.0.0" +description = "Basic Banner dialog with action buttons." +requires-python = ">=3.10" +keywords = ["banner", "dialog", "actions", "material", "notification"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/Banner"] + +[tool.flet.metadata] +title = "Basic Banner" +controls = ["SafeArea", "Column", "Button", "Banner", "Icon", "Text", "TextButton"] +layout_pattern = "dialog-flow" +complexity = "basic" +features = ["banner dialog", "action buttons", "dialog close and follow-up message"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/bottom_app_bar/__init__.py b/sdk/python/examples/controls/bottom_app_bar/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/bottom_app_bar/border_radius.py b/sdk/python/examples/controls/bottom_app_bar/border_radius.py deleted file mode 100644 index 73ba65b856..0000000000 --- a/sdk/python/examples/controls/bottom_app_bar/border_radius.py +++ /dev/null @@ -1,25 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.bottom_appbar = ft.BottomAppBar( - border_radius=ft.BorderRadius.all(20), - bgcolor=ft.Colors.BLUE, - content=ft.Row( - controls=[ - ft.IconButton(icon=ft.Icons.MENU, icon_color=ft.Colors.WHITE), - ft.Container(expand=True), - ft.IconButton(icon=ft.Icons.SEARCH, icon_color=ft.Colors.WHITE), - ft.IconButton(icon=ft.Icons.FAVORITE, icon_color=ft.Colors.WHITE), - ] - ), - ) - - page.add(ft.Text("Content goes here...")) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/bottom_app_bar/border_radius/main.py b/sdk/python/examples/controls/bottom_app_bar/border_radius/main.py new file mode 100644 index 0000000000..e23794c53e --- /dev/null +++ b/sdk/python/examples/controls/bottom_app_bar/border_radius/main.py @@ -0,0 +1,25 @@ +import flet as ft + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.bottom_appbar = ft.BottomAppBar( + border_radius=ft.BorderRadius.all(20), + bgcolor=ft.Colors.BLUE, + content=ft.Row( + controls=[ + ft.IconButton(icon=ft.Icons.MENU, icon_color=ft.Colors.WHITE), + ft.Container(expand=True), + ft.IconButton(icon=ft.Icons.SEARCH, icon_color=ft.Colors.WHITE), + ft.IconButton(icon=ft.Icons.FAVORITE, icon_color=ft.Colors.WHITE), + ] + ), + ) + + page.add(ft.SafeArea(content=ft.Column(controls=[ft.Text("Content goes here...")]))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/bottom_app_bar/border_radius/pyproject.toml b/sdk/python/examples/controls/bottom_app_bar/border_radius/pyproject.toml new file mode 100644 index 0000000000..ee7bd517a8 --- /dev/null +++ b/sdk/python/examples/controls/bottom_app_bar/border_radius/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "bottom-app-bar-border-radius" +version = "1.0.0" +description = "BottomAppBar with custom border radius." +requires-python = ">=3.10" +keywords = ["bottom app bar", "navigation", "border radius", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/BottomAppBar"] + +[tool.flet.metadata] +title = "BottomAppBar border radius" +controls = ["BottomAppBar", "Row", "IconButton", "Container", "SafeArea", "Column", "Text"] +layout_pattern = "navigation" +complexity = "basic" +features = ["custom border radius", "app bar actions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/bottom_app_bar/notched_fab.py b/sdk/python/examples/controls/bottom_app_bar/notched_fab.py deleted file mode 100644 index ea1b3dd187..0000000000 --- a/sdk/python/examples/controls/bottom_app_bar/notched_fab.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.floating_action_button = ft.FloatingActionButton( - icon=ft.Icons.ADD, - shape=ft.CircleBorder(), - ) - page.floating_action_button_location = ft.FloatingActionButtonLocation.CENTER_DOCKED - - page.bottom_appbar = ft.BottomAppBar( - bgcolor=ft.Colors.BLUE, - shape=ft.CircularRectangleNotchShape(), - content=ft.Row( - controls=[ - ft.IconButton(icon=ft.Icons.MENU, icon_color=ft.Colors.WHITE), - ft.Container(expand=True), - ft.IconButton(icon=ft.Icons.SEARCH, icon_color=ft.Colors.WHITE), - ft.IconButton(icon=ft.Icons.FAVORITE, icon_color=ft.Colors.WHITE), - ] - ), - ) - - page.add(ft.Text("Content goes here...")) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/bottom_app_bar/notched_fab/main.py b/sdk/python/examples/controls/bottom_app_bar/notched_fab/main.py new file mode 100644 index 0000000000..4e3e1dee21 --- /dev/null +++ b/sdk/python/examples/controls/bottom_app_bar/notched_fab/main.py @@ -0,0 +1,31 @@ +import flet as ft + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.floating_action_button = ft.FloatingActionButton( + icon=ft.Icons.ADD, + shape=ft.CircleBorder(), + ) + page.floating_action_button_location = ft.FloatingActionButtonLocation.CENTER_DOCKED + + page.bottom_appbar = ft.BottomAppBar( + bgcolor=ft.Colors.BLUE, + shape=ft.CircularRectangleNotchShape(), + content=ft.Row( + controls=[ + ft.IconButton(icon=ft.Icons.MENU, icon_color=ft.Colors.WHITE), + ft.Container(expand=True), + ft.IconButton(icon=ft.Icons.SEARCH, icon_color=ft.Colors.WHITE), + ft.IconButton(icon=ft.Icons.FAVORITE, icon_color=ft.Colors.WHITE), + ] + ), + ) + + page.add(ft.SafeArea(content=ft.Column(controls=[ft.Text("Content goes here...")]))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/bottom_app_bar/notched_fab/pyproject.toml b/sdk/python/examples/controls/bottom_app_bar/notched_fab/pyproject.toml new file mode 100644 index 0000000000..f36832054d --- /dev/null +++ b/sdk/python/examples/controls/bottom_app_bar/notched_fab/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "bottom-app-bar-notched-fab" +version = "1.0.0" +description = "BottomAppBar with a notched centered FloatingActionButton." +requires-python = ">=3.10" +keywords = ["bottom app bar", "navigation", "floating action button", "notch", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/BottomAppBar"] + +[tool.flet.metadata] +title = "Notched FloatingActionButton" +controls = ["BottomAppBar", "FloatingActionButton", "Row", "IconButton", "Container", "SafeArea", "Column", "Text"] +layout_pattern = "navigation" +complexity = "basic" +features = ["center docked FAB", "notched app bar", "app bar actions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/bottom_sheet/__init__.py b/sdk/python/examples/controls/bottom_sheet/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/bottom_sheet/basic.py b/sdk/python/examples/controls/bottom_sheet/basic.py deleted file mode 100644 index caa3e255e2..0000000000 --- a/sdk/python/examples/controls/bottom_sheet/basic.py +++ /dev/null @@ -1,35 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "BottomSheet Example" - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_sheet_dismissal(e: ft.Event[ft.DialogControl]): - page.add(ft.Text("Bottom sheet dismissed")) - - sheet = ft.BottomSheet( - on_dismiss=handle_sheet_dismissal, - content=ft.Container( - padding=50, - content=ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - tight=True, - controls=[ - ft.Text("Here is a bottom sheet!"), - ft.Button("Dismiss", on_click=lambda _: page.pop_dialog()), - ], - ), - ), - ) - - page.add( - ft.Button( - content="Display bottom sheet", - on_click=lambda e: page.show_dialog(sheet), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/bottom_sheet/basic/main.py b/sdk/python/examples/controls/bottom_sheet/basic/main.py new file mode 100644 index 0000000000..d177d87532 --- /dev/null +++ b/sdk/python/examples/controls/bottom_sheet/basic/main.py @@ -0,0 +1,45 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "BottomSheet Example" + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def handle_sheet_dismissal(e: ft.Event[ft.DialogControl]): + page.add(ft.Text("Bottom sheet dismissed")) + + sheet = ft.BottomSheet( + on_dismiss=handle_sheet_dismissal, + content=ft.Container( + padding=50, + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + tight=True, + controls=[ + ft.Text("Here is a bottom sheet!"), + ft.Button("Dismiss", on_click=lambda _: page.pop_dialog()), + ], + ), + ), + ) + + page.add( + ft.SafeArea( + # avoid_intrusions_left=False, + # avoid_intrusions_top=False, + # avoid_intrusions_right=False, + # avoid_intrusions_bottom=False, + content=ft.Column( + controls=[ + ft.Button( + content="Display bottom sheet", + on_click=lambda e: page.show_dialog(sheet), + ) + ] + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/bottom_sheet/basic/pyproject.toml b/sdk/python/examples/controls/bottom_sheet/basic/pyproject.toml new file mode 100644 index 0000000000..8ca7ef2873 --- /dev/null +++ b/sdk/python/examples/controls/bottom_sheet/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "bottom-sheet-basic" +version = "1.0.0" +description = "Basic BottomSheet dialog with dismiss handling." +requires-python = ">=3.10" +keywords = ["bottom sheet", "dialog", "material", "dismiss", "actions"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/BottomSheet"] + +[tool.flet.metadata] +title = "Basic BottomSheet" +controls = ["SafeArea", "Column", "Button", "BottomSheet", "Container", "Text"] +layout_pattern = "dialog-flow" +complexity = "basic" +features = ["bottom sheet dialog", "dismiss callback", "dialog actions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/bottom_sheet/fullscreen.py b/sdk/python/examples/controls/bottom_sheet/fullscreen.py deleted file mode 100644 index b3696825f9..0000000000 --- a/sdk/python/examples/controls/bottom_sheet/fullscreen.py +++ /dev/null @@ -1,35 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_switch_change(e: ft.Event[ft.Switch]): - sheet.fullscreen = e.control.value - - sheet = ft.BottomSheet( - fullscreen=True, - show_drag_handle=True, - content=ft.Container( - padding=ft.Padding.all(10), - content=ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text("This is bottom sheet's content!"), - ft.Button("Close bottom sheet", on_click=lambda: page.pop_dialog()), - ], - ), - ), - ) - - page.add( - ft.Button( - content="Display bottom sheet", - on_click=lambda e: page.show_dialog(sheet), - ), - ft.Switch(value=True, label="Fullscreen", on_change=handle_switch_change), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/bottom_sheet/fullscreen/main.py b/sdk/python/examples/controls/bottom_sheet/fullscreen/main.py new file mode 100644 index 0000000000..c83d974ad3 --- /dev/null +++ b/sdk/python/examples/controls/bottom_sheet/fullscreen/main.py @@ -0,0 +1,46 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def handle_switch_change(e: ft.Event[ft.Switch]): + sheet.fullscreen = e.control.value + + sheet = ft.BottomSheet( + fullscreen=True, + show_drag_handle=True, + content=ft.Container( + padding=ft.Padding.all(10), + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text("This is bottom sheet's content!"), + ft.Button("Close bottom sheet", on_click=lambda: page.pop_dialog()), + ], + ), + ), + ) + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.START, + controls=[ + ft.Button( + content="Display bottom sheet", + on_click=lambda e: page.show_dialog(sheet), + ), + ft.Switch( + value=True, + label="Fullscreen", + on_change=handle_switch_change, + ), + ], + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/bottom_sheet/fullscreen/pyproject.toml b/sdk/python/examples/controls/bottom_sheet/fullscreen/pyproject.toml new file mode 100644 index 0000000000..ed8a9d2eff --- /dev/null +++ b/sdk/python/examples/controls/bottom_sheet/fullscreen/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "bottom-sheet-fullscreen" +version = "1.0.0" +description = "BottomSheet with fullscreen toggle and drag handle." +requires-python = ">=3.10" +keywords = ["bottom sheet", "dialog", "fullscreen", "switch", "drag handle"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/BottomSheet"] + +[tool.flet.metadata] +title = "BottomSheet fullscreen" +controls = ["SafeArea", "Column", "Button", "Switch", "BottomSheet", "Container", "Text"] +layout_pattern = "dialog-flow" +complexity = "basic" +features = ["fullscreen toggle", "bottom sheet dialog", "drag handle"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/button/__init__.py b/sdk/python/examples/controls/button/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/button/animate_on_hover.py b/sdk/python/examples/controls/button/animate_on_hover.py deleted file mode 100644 index dd6b538fc8..0000000000 --- a/sdk/python/examples/controls/button/animate_on_hover.py +++ /dev/null @@ -1,22 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate(e: ft.Event[ft.Button]): - e.control.rotate = 0.1 if e.data else 0 - page.update() - - page.add( - ft.Button( - content="Hover over me, I'm animated!", - rotate=0, - animate_rotation=100, - on_hover=animate, - on_click=lambda e: page.add(ft.Text("Clicked! Try a long press!")), - on_long_press=lambda e: page.add(ft.Text("I knew you could do it!")), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/button/animate_on_hover/main.py b/sdk/python/examples/controls/button/animate_on_hover/main.py new file mode 100644 index 0000000000..48bee832f6 --- /dev/null +++ b/sdk/python/examples/controls/button/animate_on_hover/main.py @@ -0,0 +1,31 @@ +import flet as ft + + +def main(page: ft.Page): + def animate(e: ft.Event[ft.Button]): + e.control.rotate = 0.1 if e.data else 0 + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + content="Hover over me, I'm animated!", + rotate=0, + animate_rotation=100, + on_hover=animate, + on_click=lambda e: page.add( + ft.Text("Clicked! Try a long press!") + ), + on_long_press=lambda e: page.add( + ft.Text("I knew you could do it!") + ), + ) + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/button/animate_on_hover/pyproject.toml b/sdk/python/examples/controls/button/animate_on_hover/pyproject.toml new file mode 100644 index 0000000000..4fdd92ca6a --- /dev/null +++ b/sdk/python/examples/controls/button/animate_on_hover/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "button-animate-on-hover" +version = "1.0.0" +description = "Animated button rotation on hover with click and long-press actions." +requires-python = ">=3.10" +keywords = ["button", "hover", "animation", "click", "long press"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/Button"] + +[tool.flet.metadata] +title = "Animate on hover" +controls = ["SafeArea", "Column", "Button", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["hover animation", "click handling", "long press handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/button/basic.py b/sdk/python/examples/controls/button/basic.py deleted file mode 100644 index 4a5d689e66..0000000000 --- a/sdk/python/examples/controls/button/basic.py +++ /dev/null @@ -1,12 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Button(content="Enabled button"), - ft.Button(content="Disabled button", disabled=True), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/button/basic/main.py b/sdk/python/examples/controls/button/basic/main.py new file mode 100644 index 0000000000..5ed3eb5012 --- /dev/null +++ b/sdk/python/examples/controls/button/basic/main.py @@ -0,0 +1,18 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button(content="Enabled button"), + ft.Button(content="Disabled button", disabled=True), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/button/basic/pyproject.toml b/sdk/python/examples/controls/button/basic/pyproject.toml new file mode 100644 index 0000000000..5f2ab8d7a5 --- /dev/null +++ b/sdk/python/examples/controls/button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "button-basic" +version = "1.0.0" +description = "Basic enabled and disabled Button examples." +requires-python = ">=3.10" +keywords = ["button", "material", "basic", "disabled"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/Button"] + +[tool.flet.metadata] +title = "Basic button" +controls = ["SafeArea", "Column", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["enabled and disabled states"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/button/button_shapes.py b/sdk/python/examples/controls/button/button_shapes.py deleted file mode 100644 index 76a0d4afa0..0000000000 --- a/sdk/python/examples/controls/button/button_shapes.py +++ /dev/null @@ -1,33 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.padding = 30 - page.spacing = 30 - - page.add( - ft.Button( - content="Stadium", - style=ft.ButtonStyle(shape=ft.StadiumBorder()), - ), - ft.Button( - content="Rounded rectangle", - style=ft.ButtonStyle(shape=ft.RoundedRectangleBorder(radius=10)), - ), - ft.Button( - content="Continuous rectangle", - style=ft.ButtonStyle(shape=ft.ContinuousRectangleBorder(radius=30)), - ), - ft.Button( - content="Beveled rectangle", - style=ft.ButtonStyle(shape=ft.BeveledRectangleBorder(radius=10)), - ), - ft.Button( - content="Circle", - style=ft.ButtonStyle(shape=ft.CircleBorder(), padding=30), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/button/button_shapes/main.py b/sdk/python/examples/controls/button/button_shapes/main.py new file mode 100644 index 0000000000..b8535d9b0b --- /dev/null +++ b/sdk/python/examples/controls/button/button_shapes/main.py @@ -0,0 +1,45 @@ +import flet as ft + + +def main(page: ft.Page): + page.padding = 30 + page.spacing = 30 + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + content="Stadium", + style=ft.ButtonStyle(shape=ft.StadiumBorder()), + ), + ft.Button( + content="Rounded rectangle", + style=ft.ButtonStyle( + shape=ft.RoundedRectangleBorder(radius=10) + ), + ), + ft.Button( + content="Continuous rectangle", + style=ft.ButtonStyle( + shape=ft.ContinuousRectangleBorder(radius=30) + ), + ), + ft.Button( + content="Beveled rectangle", + style=ft.ButtonStyle( + shape=ft.BeveledRectangleBorder(radius=10) + ), + ), + ft.Button( + content="Circle", + style=ft.ButtonStyle(shape=ft.CircleBorder(), padding=30), + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/button/button_shapes/pyproject.toml b/sdk/python/examples/controls/button/button_shapes/pyproject.toml new file mode 100644 index 0000000000..7341199ad7 --- /dev/null +++ b/sdk/python/examples/controls/button/button_shapes/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "button-shapes" +version = "1.0.0" +description = "Button examples demonstrating different shape styles." +requires-python = ">=3.10" +keywords = ["button", "shape", "styling", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/Button"] + +[tool.flet.metadata] +title = "Button shapes" +controls = ["SafeArea", "Column", "Button", "ButtonStyle"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["shape styling", "multiple border types"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/button/custom_content.py b/sdk/python/examples/controls/button/custom_content.py deleted file mode 100644 index cb844ff47b..0000000000 --- a/sdk/python/examples/controls/button/custom_content.py +++ /dev/null @@ -1,34 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Button( - width=150, - content=ft.Row( - alignment=ft.MainAxisAlignment.SPACE_AROUND, - controls=[ - ft.Icon(ft.Icons.FAVORITE, color=ft.Colors.PINK), - ft.Icon(ft.Icons.AUDIOTRACK, color=ft.Colors.GREEN), - ft.Icon(ft.Icons.BEACH_ACCESS, color=ft.Colors.BLUE), - ], - ), - ), - ft.Button( - content=ft.Container( - padding=ft.Padding.all(10), - content=ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - spacing=5, - controls=[ - ft.Text(value="Compound button", size=20), - ft.Text(value="This is secondary text"), - ], - ), - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/button/custom_content/main.py b/sdk/python/examples/controls/button/custom_content/main.py new file mode 100644 index 0000000000..d55ef98eba --- /dev/null +++ b/sdk/python/examples/controls/button/custom_content/main.py @@ -0,0 +1,40 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + width=150, + content=ft.Row( + alignment=ft.MainAxisAlignment.SPACE_AROUND, + controls=[ + ft.Icon(ft.Icons.FAVORITE, color=ft.Colors.PINK), + ft.Icon(ft.Icons.AUDIOTRACK, color=ft.Colors.GREEN), + ft.Icon(ft.Icons.BEACH_ACCESS, color=ft.Colors.BLUE), + ], + ), + ), + ft.Button( + content=ft.Container( + padding=ft.Padding.all(10), + content=ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + spacing=5, + controls=[ + ft.Text(value="Compound button", size=20), + ft.Text(value="This is secondary text"), + ], + ), + ), + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/button/custom_content/pyproject.toml b/sdk/python/examples/controls/button/custom_content/pyproject.toml new file mode 100644 index 0000000000..e35326baaa --- /dev/null +++ b/sdk/python/examples/controls/button/custom_content/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "button-custom-content" +version = "1.0.0" +description = "Buttons with custom row and column content." +requires-python = ">=3.10" +keywords = ["button", "custom content", "layout", "icons", "text"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/Button"] + +[tool.flet.metadata] +title = "Custom content" +controls = ["SafeArea", "Column", "Button", "Row", "Container", "Icon", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["custom button content", "compound button layout"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/button/handling_clicks.py b/sdk/python/examples/controls/button/handling_clicks.py deleted file mode 100644 index 3a7b2ffb0e..0000000000 --- a/sdk/python/examples/controls/button/handling_clicks.py +++ /dev/null @@ -1,21 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def button_clicked(e: ft.Event[ft.Button]): - button.data += 1 - message.value = f"Button clicked {button.data} time(s)" - page.update() - - page.add( - button := ft.Button( - content="Button with 'click' event", - data=0, - on_click=button_clicked, - ), - message := ft.Text(), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/button/handling_clicks/main.py b/sdk/python/examples/controls/button/handling_clicks/main.py new file mode 100644 index 0000000000..74c18fd0fb --- /dev/null +++ b/sdk/python/examples/controls/button/handling_clicks/main.py @@ -0,0 +1,26 @@ +import flet as ft + + +def main(page: ft.Page): + def button_clicked(e: ft.Event[ft.Button]): + button.data += 1 + message.value = f"Button clicked {button.data} time(s)" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + button := ft.Button( + content="Button with 'click' event", + data=0, + on_click=button_clicked, + ), + message := ft.Text(), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/button/handling_clicks/pyproject.toml b/sdk/python/examples/controls/button/handling_clicks/pyproject.toml new file mode 100644 index 0000000000..cd5400a91e --- /dev/null +++ b/sdk/python/examples/controls/button/handling_clicks/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "button-handling-clicks" +version = "1.0.0" +description = "Button click handling with a live click counter." +requires-python = ">=3.10" +keywords = ["button", "events", "on_click", "counter"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/Button"] + +[tool.flet.metadata] +title = "Handling clicks" +controls = ["SafeArea", "Column", "Button", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["click event handling", "dynamic label updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/button/icons.py b/sdk/python/examples/controls/button/icons.py deleted file mode 100644 index abdf66e0ac..0000000000 --- a/sdk/python/examples/controls/button/icons.py +++ /dev/null @@ -1,16 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Button(content="Button with icon", icon=ft.Icons.WAVES_ROUNDED), - ft.Button( - content="Button with colorful icon", - icon=ft.Icons.PARK_ROUNDED, - icon_color=ft.Colors.GREEN_400, - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/button/icons/main.py b/sdk/python/examples/controls/button/icons/main.py new file mode 100644 index 0000000000..0476f6b5ac --- /dev/null +++ b/sdk/python/examples/controls/button/icons/main.py @@ -0,0 +1,22 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button(content="Button with icon", icon=ft.Icons.WAVES_ROUNDED), + ft.Button( + content="Button with colorful icon", + icon=ft.Icons.PARK_ROUNDED, + icon_color=ft.Colors.GREEN_400, + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/button/icons/pyproject.toml b/sdk/python/examples/controls/button/icons/pyproject.toml new file mode 100644 index 0000000000..b5d29b90e7 --- /dev/null +++ b/sdk/python/examples/controls/button/icons/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "button-icons" +version = "1.0.0" +description = "Button examples with default and custom icon colors." +requires-python = ">=3.10" +keywords = ["button", "icon", "material", "styling"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/Button"] + +[tool.flet.metadata] +title = "Buttons with icons" +controls = ["SafeArea", "Column", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["button icons", "custom icon color"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/button/styling.py b/sdk/python/examples/controls/button/styling.py deleted file mode 100644 index 1c629d4951..0000000000 --- a/sdk/python/examples/controls/button/styling.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Button( - content="Styled button 1", - style=ft.ButtonStyle( - color={ - ft.ControlState.HOVERED: ft.Colors.WHITE, - ft.ControlState.FOCUSED: ft.Colors.BLUE, - ft.ControlState.DEFAULT: ft.Colors.BLACK, - }, - bgcolor={ - ft.ControlState.FOCUSED: ft.Colors.PINK_200, - ft.ControlState.DEFAULT: ft.Colors.YELLOW, - }, - padding={ft.ControlState.HOVERED: 20}, - overlay_color=ft.Colors.TRANSPARENT, - elevation={ - ft.ControlState.DEFAULT: 0, - ft.ControlState.HOVERED: 5, - ft.ControlState.PRESSED: 10, - }, - animation_duration=500, - side={ - ft.ControlState.DEFAULT: ft.BorderSide(1, color=ft.Colors.BLUE_100), - ft.ControlState.HOVERED: ft.BorderSide(3, color=ft.Colors.BLUE_400), - ft.ControlState.PRESSED: ft.BorderSide(6, color=ft.Colors.BLUE_600), - }, - shape={ - ft.ControlState.HOVERED: ft.RoundedRectangleBorder(radius=20), - ft.ControlState.DEFAULT: ft.RoundedRectangleBorder(radius=2), - }, - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/button/styling/main.py b/sdk/python/examples/controls/button/styling/main.py new file mode 100644 index 0000000000..9af4e73372 --- /dev/null +++ b/sdk/python/examples/controls/button/styling/main.py @@ -0,0 +1,57 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + content="Styled button 1", + style=ft.ButtonStyle( + color={ + ft.ControlState.HOVERED: ft.Colors.WHITE, + ft.ControlState.FOCUSED: ft.Colors.BLUE, + ft.ControlState.DEFAULT: ft.Colors.BLACK, + }, + bgcolor={ + ft.ControlState.FOCUSED: ft.Colors.PINK_200, + ft.ControlState.DEFAULT: ft.Colors.YELLOW, + }, + padding={ft.ControlState.HOVERED: 20}, + overlay_color=ft.Colors.TRANSPARENT, + elevation={ + ft.ControlState.DEFAULT: 0, + ft.ControlState.HOVERED: 5, + ft.ControlState.PRESSED: 10, + }, + animation_duration=500, + side={ + ft.ControlState.DEFAULT: ft.BorderSide( + 1, color=ft.Colors.BLUE_100 + ), + ft.ControlState.HOVERED: ft.BorderSide( + 3, color=ft.Colors.BLUE_400 + ), + ft.ControlState.PRESSED: ft.BorderSide( + 6, color=ft.Colors.BLUE_600 + ), + }, + shape={ + ft.ControlState.HOVERED: ft.RoundedRectangleBorder( + radius=20 + ), + ft.ControlState.DEFAULT: ft.RoundedRectangleBorder( + radius=2 + ), + }, + ), + ) + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/button/styling/pyproject.toml b/sdk/python/examples/controls/button/styling/pyproject.toml new file mode 100644 index 0000000000..da5d934598 --- /dev/null +++ b/sdk/python/examples/controls/button/styling/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "button-styling" +version = "1.0.0" +description = "Button style customization by control states." +requires-python = ">=3.10" +keywords = ["button", "styling", "control state", "animation", "hover"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/Button"] + +[tool.flet.metadata] +title = "Button styling" +controls = ["SafeArea", "Column", "Button", "ButtonStyle", "BorderSide"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["state-based styling", "hover effects", "animated style transitions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/camera/example_1.py b/sdk/python/examples/controls/camera/example_1/main.py similarity index 100% rename from sdk/python/examples/controls/camera/example_1.py rename to sdk/python/examples/controls/camera/example_1/main.py diff --git a/sdk/python/examples/controls/camera/example_1/pyproject.toml b/sdk/python/examples/controls/camera/example_1/pyproject.toml new file mode 100644 index 0000000000..fc60b189ef --- /dev/null +++ b/sdk/python/examples/controls/camera/example_1/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "camera-example-1" +version = "1.0.0" +description = "Comprehensive camera demo with preview, photos, video recording, and streaming." +requires-python = ">=3.10" +keywords = ["camera", "photo", "video", "streaming", "device orientation", "media"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-camera"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Camera"] + +[tool.flet.metadata] +title = "Camera playground" +controls = ["SafeArea", "Column", "Row", "Container", "IconButton", "Dropdown", "Text", "Image", "FilledIconButton", "FilledTonalIconButton", "OutlinedIconButton", "Camera"] +layout_pattern = "control-panel" +complexity = "advanced" +features = ["camera initialization", "photo capture", "video recording", "record pause/resume", "image streaming", "preview pause/resume", "camera state events"] + +[tool.flet] +platforms = ["web", "ios", "android"] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/bezier_curves.py b/sdk/python/examples/controls/canvas/bezier_curves.py deleted file mode 100644 index 020c43fc9d..0000000000 --- a/sdk/python/examples/controls/canvas/bezier_curves.py +++ /dev/null @@ -1,65 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - cp = cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Path( - paint=ft.Paint(stroke_width=2, style=ft.PaintingStyle.STROKE), - elements=[ - cv.Path.MoveTo(x=75, y=25), - cv.Path.QuadraticTo(cp1x=25, cp1y=25, x=25, y=62.5), - cv.Path.QuadraticTo(cp1x=25, cp1y=100, x=50, y=100), - cv.Path.QuadraticTo(cp1x=50, cp1y=120, x=30, y=125), - cv.Path.QuadraticTo(cp1x=60, cp1y=120, x=65, y=100), - cv.Path.QuadraticTo(cp1x=125, cp1y=100, x=125, y=62.5), - cv.Path.QuadraticTo(cp1x=125, cp1y=25, x=75, y=25), - ], - ), - cv.Path( - elements=[ - cv.Path.SubPath( - x=100, - y=100, - elements=[ - cv.Path.MoveTo(x=75, y=40), - cv.Path.CubicTo( - cp1x=75, cp1y=37, cp2x=70, cp2y=25, x=50, y=25 - ), - cv.Path.CubicTo( - cp1x=20, cp1y=25, cp2x=20, cp2y=62.5, x=20, y=62.5 - ), - cv.Path.CubicTo( - cp1x=20, cp1y=80, cp2x=40, cp2y=102, x=75, y=120 - ), - cv.Path.CubicTo( - cp1x=110, cp1y=102, cp2x=130, cp2y=80, x=130, y=62.5 - ), - cv.Path.CubicTo( - cp1x=130, cp1y=62.5, cp2x=130, cp2y=25, x=100, y=25 - ), - cv.Path.CubicTo( - cp1x=85, cp1y=25, cp2x=75, cp2y=37, x=75, y=40 - ), - ], - ) - ], - paint=ft.Paint( - style=ft.PaintingStyle.FILL, - gradient=ft.PaintRadialGradient( - center=ft.Offset(150, 150), - radius=50, - colors=[ft.Colors.PINK_100, ft.Colors.PINK], - ), - ), - ), - ], - ) - - page.add(cp) - - -ft.run(main) diff --git a/sdk/python/examples/controls/canvas/bezier_curves/main.py b/sdk/python/examples/controls/canvas/bezier_curves/main.py new file mode 100644 index 0000000000..84c048382e --- /dev/null +++ b/sdk/python/examples/controls/canvas/bezier_curves/main.py @@ -0,0 +1,66 @@ +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + cp = cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Path( + paint=ft.Paint(stroke_width=2, style=ft.PaintingStyle.STROKE), + elements=[ + cv.Path.MoveTo(x=75, y=25), + cv.Path.QuadraticTo(cp1x=25, cp1y=25, x=25, y=62.5), + cv.Path.QuadraticTo(cp1x=25, cp1y=100, x=50, y=100), + cv.Path.QuadraticTo(cp1x=50, cp1y=120, x=30, y=125), + cv.Path.QuadraticTo(cp1x=60, cp1y=120, x=65, y=100), + cv.Path.QuadraticTo(cp1x=125, cp1y=100, x=125, y=62.5), + cv.Path.QuadraticTo(cp1x=125, cp1y=25, x=75, y=25), + ], + ), + cv.Path( + elements=[ + cv.Path.SubPath( + x=100, + y=100, + elements=[ + cv.Path.MoveTo(x=75, y=40), + cv.Path.CubicTo( + cp1x=75, cp1y=37, cp2x=70, cp2y=25, x=50, y=25 + ), + cv.Path.CubicTo( + cp1x=20, cp1y=25, cp2x=20, cp2y=62.5, x=20, y=62.5 + ), + cv.Path.CubicTo( + cp1x=20, cp1y=80, cp2x=40, cp2y=102, x=75, y=120 + ), + cv.Path.CubicTo( + cp1x=110, cp1y=102, cp2x=130, cp2y=80, x=130, y=62.5 + ), + cv.Path.CubicTo( + cp1x=130, cp1y=62.5, cp2x=130, cp2y=25, x=100, y=25 + ), + cv.Path.CubicTo( + cp1x=85, cp1y=25, cp2x=75, cp2y=37, x=75, y=40 + ), + ], + ) + ], + paint=ft.Paint( + style=ft.PaintingStyle.FILL, + gradient=ft.PaintRadialGradient( + center=ft.Offset(150, 150), + radius=50, + colors=[ft.Colors.PINK_100, ft.Colors.PINK], + ), + ), + ), + ], + ) + + page.add(ft.SafeArea(content=cp)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/bezier_curves/pyproject.toml b/sdk/python/examples/controls/canvas/bezier_curves/pyproject.toml new file mode 100644 index 0000000000..1a60ab3282 --- /dev/null +++ b/sdk/python/examples/controls/canvas/bezier_curves/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-bezier-curves" +version = "1.0.0" +description = "Canvas bezier curve drawing with gradient fills." +requires-python = ">=3.10" +keywords = ["canvas", "bezier", "path", "gradient"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Bezier curves" +controls = ["SafeArea", "Canvas", "Path"] +layout_pattern = "free-canvas" +complexity = "basic" +features = ["quadratic and cubic paths", "radial gradient fill"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/brush.py b/sdk/python/examples/controls/canvas/brush.py deleted file mode 100644 index 0140b98cb5..0000000000 --- a/sdk/python/examples/controls/canvas/brush.py +++ /dev/null @@ -1,91 +0,0 @@ -from dataclasses import dataclass - -import flet as ft -import flet.canvas as cv - -MAX_SHAPES_PER_CAPTURE = 30 - - -@dataclass -class State: - x: float = 0 - y: float = 0 - shapes_count: int = 1 - - -state = State() - - -def main(page: ft.Page): - page.title = "Canvas Example" - - file_picker = ft.FilePicker() - - def handle_pan_start(e: ft.DragStartEvent): - state.x = e.local_position.x - state.y = e.local_position.y - - async def handle_pan_update(e: ft.DragUpdateEvent): - ft.context.disable_auto_update() - canvas.shapes.append( - cv.Line( - x1=state.x, - y1=state.y, - x2=e.local_position.x, - y2=e.local_position.y, - paint=ft.Paint(stroke_width=3), - ) - ) - canvas.update() - state.shapes_count += 1 - - if state.shapes_count == MAX_SHAPES_PER_CAPTURE: - await canvas.capture() - canvas.shapes.clear() - canvas.update() - state.shapes_count = 0 - - state.x = e.local_position.x - state.y = e.local_position.y - - canvas = cv.Canvas( - expand=False, - shapes=[ - cv.Fill(ft.Paint(color=ft.Colors.WHITE)), - ], - content=ft.GestureDetector( - on_pan_start=handle_pan_start, - on_pan_update=handle_pan_update, - drag_interval=10, - ), - ) - - async def save_image(): - await canvas.capture() - capture = await canvas.get_capture() - if capture: - file_path = await file_picker.save_file( - file_name="flet_picture.png", src_bytes=capture - ) - if file_path and page.platform in [ - ft.PagePlatform.MACOS, - ft.PagePlatform.WINDOWS, - ft.PagePlatform.LINUX, - ]: - with open(file_path, "wb") as f: - f.write(capture) - - page.add( - ft.Button("Save image", on_click=save_image), - ft.Container( - content=canvas, - border_radius=5, - border=ft.Border.all(2), - bgcolor=ft.Colors.WHITE, - width=float("inf"), - expand=True, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/canvas/brush/main.py b/sdk/python/examples/controls/canvas/brush/main.py new file mode 100644 index 0000000000..52ef91dff7 --- /dev/null +++ b/sdk/python/examples/controls/canvas/brush/main.py @@ -0,0 +1,99 @@ +from dataclasses import dataclass + +import flet as ft +import flet.canvas as cv + +MAX_SHAPES_PER_CAPTURE = 30 + + +@dataclass +class State: + x: float = 0 + y: float = 0 + shapes_count: int = 1 + + +state = State() + + +def main(page: ft.Page): + page.title = "Canvas Example" + + file_picker = ft.FilePicker() + + def handle_pan_start(e: ft.DragStartEvent): + state.x = e.local_position.x + state.y = e.local_position.y + + async def handle_pan_update(e: ft.DragUpdateEvent): + # ft.context.disable_auto_update() + canvas.shapes.append( + cv.Line( + x1=state.x, + y1=state.y, + x2=e.local_position.x, + y2=e.local_position.y, + paint=ft.Paint(stroke_width=3), + ) + ) + canvas.update() + state.shapes_count += 1 + + if state.shapes_count == MAX_SHAPES_PER_CAPTURE: + await canvas.capture() + canvas.shapes.clear() + canvas.update() + state.shapes_count = 0 + + state.x = e.local_position.x + state.y = e.local_position.y + + canvas = cv.Canvas( + expand=False, + shapes=[ + cv.Fill(ft.Paint(color=ft.Colors.WHITE)), + ], + content=ft.GestureDetector( + on_pan_start=handle_pan_start, + on_pan_update=handle_pan_update, + drag_interval=10, + ), + ) + + async def save_image(): + await canvas.capture() + capture = await canvas.get_capture() + if capture: + file_path = await file_picker.save_file( + file_name="flet_picture.png", src_bytes=capture + ) + if file_path and page.platform in [ + ft.PagePlatform.MACOS, + ft.PagePlatform.WINDOWS, + ft.PagePlatform.LINUX, + ]: + with open(file_path, "wb") as f: + f.write(capture) + + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + controls=[ + ft.Button("Save image", on_click=save_image), + ft.Container( + border_radius=5, + border=ft.Border.all(2), + bgcolor=ft.Colors.WHITE, + width=float("inf"), + expand=True, + content=canvas, + ), + ] + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/brush/pyproject.toml b/sdk/python/examples/controls/canvas/brush/pyproject.toml new file mode 100644 index 0000000000..dd044beb09 --- /dev/null +++ b/sdk/python/examples/controls/canvas/brush/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-brush" +version = "1.0.0" +description = "Free-hand canvas drawing with capture and save support." +requires-python = ">=3.10" +keywords = ["canvas", "brush", "gesture", "capture", "save"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Brush" +controls = ["SafeArea", "Column", "Button", "Container", "Canvas", "GestureDetector", "Line", "Fill"] +layout_pattern = "control-panel" +complexity = "basic" +features = ["pan drawing", "canvas capture", "save to file"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/brush_on_image.py b/sdk/python/examples/controls/canvas/brush_on_image.py deleted file mode 100644 index cec24534d3..0000000000 --- a/sdk/python/examples/controls/canvas/brush_on_image.py +++ /dev/null @@ -1,60 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -class State: - x: float - y: float - - -state = State() - - -def main(page: ft.Page): - page.title = "Flet Brush" - - def handle_pan_start(e: ft.DragStartEvent): - state.x = e.local_position.x - state.y = e.local_position.y - - def handle_pan_update(e: ft.DragUpdateEvent): - canvas.shapes.append( - cv.Line( - x1=state.x, - y1=state.y, - x2=e.local_position.x, - y2=e.local_position.y, - paint=ft.Paint(stroke_width=3), - ) - ) - canvas.update() - state.x = e.local_position.x - state.y = e.local_position.y - - page.add( - ft.Container( - border_radius=5, - expand=True, - content=ft.Stack( - expand=True, - controls=[ - ft.Image( - src="https://picsum.photos/200/300", - fit=ft.BoxFit.FILL, - width=float("inf"), - ), - canvas := cv.Canvas( - expand=False, - content=ft.GestureDetector( - on_pan_start=handle_pan_start, - on_pan_update=handle_pan_update, - drag_interval=10, - ), - ), - ], - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/canvas/brush_on_image/main.py b/sdk/python/examples/controls/canvas/brush_on_image/main.py new file mode 100644 index 0000000000..abbd2eacd7 --- /dev/null +++ b/sdk/python/examples/controls/canvas/brush_on_image/main.py @@ -0,0 +1,64 @@ +import flet as ft +import flet.canvas as cv + + +class State: + x: float + y: float + + +state = State() + + +def main(page: ft.Page): + page.title = "Flet Brush" + + def handle_pan_start(e: ft.DragStartEvent): + state.x = e.local_position.x + state.y = e.local_position.y + + def handle_pan_update(e: ft.DragUpdateEvent): + canvas.shapes.append( + cv.Line( + x1=state.x, + y1=state.y, + x2=e.local_position.x, + y2=e.local_position.y, + paint=ft.Paint(stroke_width=3), + ) + ) + canvas.update() + state.x = e.local_position.x + state.y = e.local_position.y + + page.add( + ft.SafeArea( + expand=True, + content=ft.Container( + border_radius=5, + expand=True, + content=ft.Stack( + expand=True, + controls=[ + ft.Image( + src="https://picsum.photos/200/300", + fit=ft.BoxFit.FILL, + width=float("inf"), + ), + canvas := cv.Canvas( + expand=False, + content=ft.GestureDetector( + on_pan_start=handle_pan_start, + on_pan_update=handle_pan_update, + drag_interval=10, + ), + ), + ], + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/brush_on_image/pyproject.toml b/sdk/python/examples/controls/canvas/brush_on_image/pyproject.toml new file mode 100644 index 0000000000..9873b3fefc --- /dev/null +++ b/sdk/python/examples/controls/canvas/brush_on_image/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-brush-on-image" +version = "1.0.0" +description = "Draw free-hand strokes over an image using Canvas." +requires-python = ">=3.10" +keywords = ["canvas", "brush", "image", "gesture", "overlay"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Brush on image" +controls = ["SafeArea", "Container", "Stack", "Image", "Canvas", "GestureDetector", "Line"] +layout_pattern = "overlay" +complexity = "basic" +features = ["drawing on image", "pan gesture handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/dash_strokes.py b/sdk/python/examples/controls/canvas/dash_strokes.py deleted file mode 100644 index 51eb09e2fd..0000000000 --- a/sdk/python/examples/controls/canvas/dash_strokes.py +++ /dev/null @@ -1,113 +0,0 @@ -import math -from dataclasses import dataclass - -import flet as ft -import flet.canvas as fc - - -@ft.observable -@dataclass -class AppState: - strokes: bool - - def toggle_strokes(self): - self.strokes = not self.strokes - - -@ft.component -def App(): - state, _ = ft.use_state(lambda: AppState(strokes=True)) - return ft.SafeArea( - expand=True, - content=ft.Column( - controls=[ - ft.Button("Toggle strokes", on_click=state.toggle_strokes), - fc.Canvas( - width=300, - height=300, - shapes=[ - fc.Line( - x1=30, - y1=30, - x2=200, - y2=100, - paint=ft.Paint( - color=ft.Colors.BLACK, - stroke_width=3, - stroke_dash_pattern=[4, 4] if state.strokes else None, - style=ft.PaintingStyle.STROKE, - ), - ), - fc.Circle( - x=150, - y=150, - radius=130, - paint=ft.Paint( - color=ft.Colors.BLUE, - stroke_width=4, - stroke_dash_pattern=[4, 4] if state.strokes else None, - style=ft.PaintingStyle.STROKE, - ), - ), - fc.Oval( - x=10, - y=10, - width=240, - height=140, - paint=ft.Paint( - color=ft.Colors.GREEN, - stroke_width=4, - stroke_dash_pattern=[10, 10] if state.strokes else None, - style=ft.PaintingStyle.STROKE, - ), - ), - fc.Arc( - x=20, - y=20, - width=220, - height=220, - start_angle=0, - sweep_angle=math.pi, - paint=ft.Paint( - color=ft.Colors.RED, - stroke_width=4, - stroke_dash_pattern=[7, 7] if state.strokes else None, - style=ft.PaintingStyle.STROKE, - ), - ), - fc.Rect( - x=40, - y=60, - width=60, - height=70, - border_radius=0, - paint=ft.Paint( - color=ft.Colors.RED, - stroke_width=4, - stroke_dash_pattern=[7, 7] if state.strokes else None, - style=ft.PaintingStyle.STROKE, - ), - ), - fc.Arc( - x=50, - y=50, - width=170, - height=140, - start_angle=math.pi * 0.1, - sweep_angle=math.pi * 0.4, - paint=ft.Paint( - color=ft.Colors.YELLOW, - stroke_width=4, - stroke_dash_pattern=[7, 7] if state.strokes else None, - style=ft.PaintingStyle.STROKE, - ), - use_center=True, - ), - ], - ), - ] - ), - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/controls/canvas/dash_strokes/main.py b/sdk/python/examples/controls/canvas/dash_strokes/main.py new file mode 100644 index 0000000000..edcf4bdaf4 --- /dev/null +++ b/sdk/python/examples/controls/canvas/dash_strokes/main.py @@ -0,0 +1,118 @@ +import math +from dataclasses import dataclass + +import flet as ft +import flet.canvas as fc + + +@ft.observable +@dataclass +class AppState: + strokes: bool + + def toggle_strokes(self): + self.strokes = not self.strokes + + +@ft.component +def App(): + state, _ = ft.use_state(lambda: AppState(strokes=True)) + return ft.SafeArea( + expand=True, + content=ft.Column( + controls=[ + ft.Button("Toggle strokes", on_click=state.toggle_strokes), + fc.Canvas( + width=300, + height=300, + shapes=[ + fc.Line( + x1=30, + y1=30, + x2=200, + y2=100, + paint=ft.Paint( + color=ft.Colors.BLACK, + stroke_width=3, + stroke_dash_pattern=[4, 4] if state.strokes else None, + style=ft.PaintingStyle.STROKE, + ), + ), + fc.Circle( + x=150, + y=150, + radius=130, + paint=ft.Paint( + color=ft.Colors.BLUE, + stroke_width=4, + stroke_dash_pattern=[4, 4] if state.strokes else None, + style=ft.PaintingStyle.STROKE, + ), + ), + fc.Oval( + x=10, + y=10, + width=240, + height=140, + paint=ft.Paint( + color=ft.Colors.GREEN, + stroke_width=4, + stroke_dash_pattern=[10, 10] if state.strokes else None, + style=ft.PaintingStyle.STROKE, + ), + ), + fc.Arc( + x=20, + y=20, + width=220, + height=220, + start_angle=0, + sweep_angle=math.pi, + paint=ft.Paint( + color=ft.Colors.RED, + stroke_width=4, + stroke_dash_pattern=[7, 7] if state.strokes else None, + style=ft.PaintingStyle.STROKE, + ), + ), + fc.Rect( + x=40, + y=60, + width=60, + height=70, + border_radius=0, + paint=ft.Paint( + color=ft.Colors.RED, + stroke_width=4, + stroke_dash_pattern=[7, 7] if state.strokes else None, + style=ft.PaintingStyle.STROKE, + ), + ), + fc.Arc( + x=50, + y=50, + width=170, + height=140, + start_angle=math.pi * 0.1, + sweep_angle=math.pi * 0.4, + paint=ft.Paint( + color=ft.Colors.YELLOW, + stroke_width=4, + stroke_dash_pattern=[7, 7] if state.strokes else None, + style=ft.PaintingStyle.STROKE, + ), + use_center=True, + ), + ], + ), + ] + ), + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/dash_strokes/pyproject.toml b/sdk/python/examples/controls/canvas/dash_strokes/pyproject.toml new file mode 100644 index 0000000000..d2296c763d --- /dev/null +++ b/sdk/python/examples/controls/canvas/dash_strokes/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-dash-strokes" +version = "1.0.0" +description = "Toggle dashed stroke patterns across multiple canvas shapes." +requires-python = ">=3.10" +keywords = ["canvas", "stroke", "dash", "reactive", "shapes"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Dash strokes" +controls = ["SafeArea", "Column", "Button", "Canvas", "Line", "Circle", "Oval", "Arc", "Rect"] +layout_pattern = "control-panel" +complexity = "basic" +features = ["reactive state", "dash stroke toggle", "multiple shape styles"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/flet_logo.py b/sdk/python/examples/controls/canvas/flet_logo.py deleted file mode 100644 index 6d96d2202d..0000000000 --- a/sdk/python/examples/controls/canvas/flet_logo.py +++ /dev/null @@ -1,44 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - page.add( - cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Path( - elements=[ - cv.Path.MoveTo(x=25, y=125), - cv.Path.QuadraticTo(cp1x=50, cp1y=25, x=135, y=35, w=0.35), - cv.Path.QuadraticTo(cp1x=75, cp1y=115, x=135, y=215, w=0.6), - cv.Path.QuadraticTo(cp1x=50, cp1y=225, x=25, y=125, w=0.35), - ], - paint=ft.Paint( - stroke_width=2, - style=ft.PaintingStyle.FILL, - color=ft.Colors.PINK_400, - ), - ), - cv.Path( - elements=[ - cv.Path.MoveTo(x=85, y=125), - cv.Path.QuadraticTo(cp1x=120, cp1y=85, x=165, y=75, w=0.5), - cv.Path.QuadraticTo(cp1x=120, cp1y=115, x=165, y=175, w=0.3), - cv.Path.QuadraticTo(cp1x=120, cp1y=165, x=85, y=125, w=0.5), - ], - paint=ft.Paint( - stroke_width=2, - style=ft.PaintingStyle.FILL, - color=ft.Colors.with_opacity(0.5, ft.Colors.BLUE_400), - ), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/canvas/flet_logo/main.py b/sdk/python/examples/controls/canvas/flet_logo/main.py new file mode 100644 index 0000000000..820c46011f --- /dev/null +++ b/sdk/python/examples/controls/canvas/flet_logo/main.py @@ -0,0 +1,49 @@ +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + page.add( + ft.SafeArea( + content=cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Path( + elements=[ + cv.Path.MoveTo(x=25, y=125), + cv.Path.QuadraticTo(cp1x=50, cp1y=25, x=135, y=35, w=0.35), + cv.Path.QuadraticTo(cp1x=75, cp1y=115, x=135, y=215, w=0.6), + cv.Path.QuadraticTo(cp1x=50, cp1y=225, x=25, y=125, w=0.35), + ], + paint=ft.Paint( + stroke_width=2, + style=ft.PaintingStyle.FILL, + color=ft.Colors.PINK_400, + ), + ), + cv.Path( + elements=[ + cv.Path.MoveTo(x=85, y=125), + cv.Path.QuadraticTo(cp1x=120, cp1y=85, x=165, y=75, w=0.5), + cv.Path.QuadraticTo( + cp1x=120, cp1y=115, x=165, y=175, w=0.3 + ), + cv.Path.QuadraticTo(cp1x=120, cp1y=165, x=85, y=125, w=0.5), + ], + paint=ft.Paint( + stroke_width=2, + style=ft.PaintingStyle.FILL, + color=ft.Colors.with_opacity(0.5, ft.Colors.BLUE_400), + ), + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/flet_logo/pyproject.toml b/sdk/python/examples/controls/canvas/flet_logo/pyproject.toml new file mode 100644 index 0000000000..22a0b75e00 --- /dev/null +++ b/sdk/python/examples/controls/canvas/flet_logo/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-flet-logo" +version = "1.0.0" +description = "Canvas drawing of the Flet logo using quadratic paths." +requires-python = ">=3.10" +keywords = ["canvas", "logo", "path", "quadratic"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Flet logo" +controls = ["SafeArea", "Canvas", "Path"] +layout_pattern = "free-canvas" +complexity = "basic" +features = ["path drawing", "filled vector shapes"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/gradients.py b/sdk/python/examples/controls/canvas/gradients.py deleted file mode 100644 index fe2b161bda..0000000000 --- a/sdk/python/examples/controls/canvas/gradients.py +++ /dev/null @@ -1,71 +0,0 @@ -import math - -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - page.add( - cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Rect( - x=10, - y=10, - width=100, - height=100, - border_radius=5, - paint=ft.Paint( - style=ft.PaintingStyle.FILL, - gradient=ft.PaintLinearGradient( - begin=(0, 10), - end=(100, 50), - colors=[ft.Colors.BLUE, ft.Colors.YELLOW], - ), - ), - ), - cv.Circle( - x=60, - y=170, - radius=50, - paint=ft.Paint( - style=ft.PaintingStyle.FILL, - gradient=ft.PaintRadialGradient( - radius=50, - center=(60, 170), - colors=[ft.Colors.YELLOW, ft.Colors.BLUE], - ), - ), - ), - cv.Path( - elements=[ - cv.Path.Arc( - x=10, - y=230, - width=100, - height=100, - start_angle=3 * math.pi / 4, - sweep_angle=3 * math.pi / 2, - ), - ], - paint=ft.Paint( - stroke_width=15, - stroke_join=ft.StrokeJoin.ROUND, - style=ft.PaintingStyle.STROKE, - gradient=ft.PaintSweepGradient( - start_angle=0, - end_angle=math.pi * 2, - rotation=3 * math.pi / 4, - center=(60, 280), - colors=[ft.Colors.YELLOW, ft.Colors.PURPLE], - color_stops=[0.0, 1.0], - ), - ), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/canvas/gradients/main.py b/sdk/python/examples/controls/canvas/gradients/main.py new file mode 100644 index 0000000000..744b7b2f91 --- /dev/null +++ b/sdk/python/examples/controls/canvas/gradients/main.py @@ -0,0 +1,74 @@ +import math + +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Rect( + x=10, + y=10, + width=100, + height=100, + border_radius=5, + paint=ft.Paint( + style=ft.PaintingStyle.FILL, + gradient=ft.PaintLinearGradient( + begin=(0, 10), + end=(100, 50), + colors=[ft.Colors.BLUE, ft.Colors.YELLOW], + ), + ), + ), + cv.Circle( + x=60, + y=170, + radius=50, + paint=ft.Paint( + style=ft.PaintingStyle.FILL, + gradient=ft.PaintRadialGradient( + radius=50, + center=(60, 170), + colors=[ft.Colors.YELLOW, ft.Colors.BLUE], + ), + ), + ), + cv.Path( + elements=[ + cv.Path.Arc( + x=10, + y=230, + width=100, + height=100, + start_angle=3 * math.pi / 4, + sweep_angle=3 * math.pi / 2, + ), + ], + paint=ft.Paint( + stroke_width=15, + stroke_join=ft.StrokeJoin.ROUND, + style=ft.PaintingStyle.STROKE, + gradient=ft.PaintSweepGradient( + start_angle=0, + end_angle=math.pi * 2, + rotation=3 * math.pi / 4, + center=(60, 280), + colors=[ft.Colors.YELLOW, ft.Colors.PURPLE], + color_stops=[0.0, 1.0], + ), + ), + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/gradients/pyproject.toml b/sdk/python/examples/controls/canvas/gradients/pyproject.toml new file mode 100644 index 0000000000..11ee8446f5 --- /dev/null +++ b/sdk/python/examples/controls/canvas/gradients/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-gradients" +version = "1.0.0" +description = "Canvas gradient fills with linear, radial, and sweep gradients." +requires-python = ">=3.10" +keywords = ["canvas", "gradients", "linear", "radial", "sweep"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Gradients" +controls = ["SafeArea", "Canvas", "Rect", "Circle", "Path"] +layout_pattern = "free-canvas" +complexity = "basic" +features = ["linear gradient", "radial gradient", "sweep gradient"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/resize.py b/sdk/python/examples/controls/canvas/resize.py deleted file mode 100644 index 0751942af9..0000000000 --- a/sdk/python/examples/controls/canvas/resize.py +++ /dev/null @@ -1,32 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - def paint_resize(e: cv.CanvasResizeEvent): - print("On resize:", e.width, e.height) - canvas.shapes[0].x2 = e.width - canvas.shapes[0].y2 = e.height - canvas.shapes[1].y1 = e.height - canvas.shapes[1].x2 = e.width - canvas.update() - - page.add( - ft.Container( - width=float("inf"), - expand=True, - content=( - canvas := cv.Canvas( - resize_interval=10, - on_resize=paint_resize, - shapes=[ - cv.Line(x1=0, y1=0, x2=100, y2=100), - cv.Line(x1=0, y1=100, x2=100, y2=0), - ], - ) - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/canvas/resize/main.py b/sdk/python/examples/controls/canvas/resize/main.py new file mode 100644 index 0000000000..ec403efc6f --- /dev/null +++ b/sdk/python/examples/controls/canvas/resize/main.py @@ -0,0 +1,36 @@ +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + def paint_resize(e: cv.CanvasResizeEvent): + print("On resize:", e.width, e.height) + canvas.shapes[0].x2 = e.width + canvas.shapes[0].y2 = e.height + canvas.shapes[1].y1 = e.height + canvas.shapes[1].x2 = e.width + canvas.update() + + page.add( + ft.SafeArea( + expand=True, + content=ft.Container( + width=float("inf"), + expand=True, + content=( + canvas := cv.Canvas( + resize_interval=10, + on_resize=paint_resize, + shapes=[ + cv.Line(x1=0, y1=0, x2=100, y2=100), + cv.Line(x1=0, y1=100, x2=100, y2=0), + ], + ) + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/resize/pyproject.toml b/sdk/python/examples/controls/canvas/resize/pyproject.toml new file mode 100644 index 0000000000..eeb2e8e3c8 --- /dev/null +++ b/sdk/python/examples/controls/canvas/resize/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-resize" +version = "1.0.0" +description = "Canvas resize handling with dynamic shape updates." +requires-python = ">=3.10" +keywords = ["canvas", "resize", "events", "responsive"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Canvas resize" +controls = ["SafeArea", "Container", "Canvas", "Line"] +layout_pattern = "free-canvas" +complexity = "basic" +features = ["on_resize event", "responsive line drawing"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/smiling_face.py b/sdk/python/examples/controls/canvas/smiling_face.py deleted file mode 100644 index 68361350c2..0000000000 --- a/sdk/python/examples/controls/canvas/smiling_face.py +++ /dev/null @@ -1,35 +0,0 @@ -import math - -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - stroke_paint = ft.Paint(stroke_width=2, style=ft.PaintingStyle.STROKE) - fill_paint = ft.Paint(style=ft.PaintingStyle.FILL) - - page.add( - cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Circle(x=100, y=100, radius=50, paint=stroke_paint), - cv.Circle(x=80, y=90, radius=10, paint=stroke_paint), - cv.Circle(x=84, y=87, radius=5, paint=fill_paint), - cv.Circle(x=120, y=90, radius=10, paint=stroke_paint), - cv.Circle(x=124, y=87, radius=5, paint=fill_paint), - cv.Arc( - x=70, - y=95, - width=60, - height=40, - start_angle=0, - sweep_angle=math.pi, - paint=stroke_paint, - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/canvas/smiling_face/main.py b/sdk/python/examples/controls/canvas/smiling_face/main.py new file mode 100644 index 0000000000..e7d6724cbc --- /dev/null +++ b/sdk/python/examples/controls/canvas/smiling_face/main.py @@ -0,0 +1,38 @@ +import math + +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + stroke_paint = ft.Paint(stroke_width=2, style=ft.PaintingStyle.STROKE) + fill_paint = ft.Paint(style=ft.PaintingStyle.FILL) + + page.add( + ft.SafeArea( + content=cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Circle(x=100, y=100, radius=50, paint=stroke_paint), + cv.Circle(x=80, y=90, radius=10, paint=stroke_paint), + cv.Circle(x=84, y=87, radius=5, paint=fill_paint), + cv.Circle(x=120, y=90, radius=10, paint=stroke_paint), + cv.Circle(x=124, y=87, radius=5, paint=fill_paint), + cv.Arc( + x=70, + y=95, + width=60, + height=40, + start_angle=0, + sweep_angle=math.pi, + paint=stroke_paint, + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/smiling_face/pyproject.toml b/sdk/python/examples/controls/canvas/smiling_face/pyproject.toml new file mode 100644 index 0000000000..fa9778fffc --- /dev/null +++ b/sdk/python/examples/controls/canvas/smiling_face/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-smiling-face" +version = "1.0.0" +description = "Canvas drawing example of a smiling face." +requires-python = ">=3.10" +keywords = ["canvas", "drawing", "shapes", "arc", "circle"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Smiling face" +controls = ["SafeArea", "Canvas", "Circle", "Arc"] +layout_pattern = "free-canvas" +complexity = "basic" +features = ["shape drawing", "stroke and fill paints"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/text.py b/sdk/python/examples/controls/canvas/text.py deleted file mode 100644 index 8a8d69e0ab..0000000000 --- a/sdk/python/examples/controls/canvas/text.py +++ /dev/null @@ -1,95 +0,0 @@ -import math - -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - page.add( - cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Text(x=0, y=0, value="Just a text"), - cv.Circle(x=200, y=100, radius=2, paint=ft.Paint(color=ft.Colors.RED)), - cv.Text( - x=200, - y=100, - style=ft.TextStyle(weight=ft.FontWeight.BOLD, size=30), - alignment=ft.Alignment.TOP_CENTER, - rotate=math.pi * 0.15, - value="Rotated", - spans=[ - ft.TextSpan( - text="around top_center", - style=ft.TextStyle( - italic=True, color=ft.Colors.GREEN, size=20 - ), - ) - ], - ), - cv.Circle(x=400, y=100, radius=2, paint=ft.Paint(color=ft.Colors.RED)), - cv.Text( - x=400, - y=100, - value="Rotated around top_left", - style=ft.TextStyle(size=20), - alignment=ft.Alignment.TOP_LEFT, - rotate=math.pi * -0.15, - ), - cv.Circle(x=600, y=200, radius=2, paint=ft.Paint(color=ft.Colors.RED)), - cv.Text( - x=600, - y=200, - value="Rotated around center", - style=ft.TextStyle(size=20), - alignment=ft.Alignment.CENTER, - rotate=math.pi / 2, - ), - cv.Text( - x=300, - y=400, - value="Limited to max_width and right-aligned.\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", - text_align=ft.TextAlign.RIGHT, - max_width=400, - ), - cv.Text( - x=200, - y=200, - value="WOW!", - style=ft.TextStyle( - weight=ft.FontWeight.BOLD, - size=100, - foreground=ft.Paint( - color=ft.Colors.PINK, - stroke_width=6, - style=ft.PaintingStyle.STROKE, - stroke_join=ft.StrokeJoin.ROUND, - stroke_cap=ft.StrokeCap.ROUND, - ), - ), - ), - cv.Text( - x=200, - y=200, - value="WOW!", - style=ft.TextStyle( - weight=ft.FontWeight.BOLD, - size=100, - foreground=ft.Paint( - gradient=ft.PaintLinearGradient( - begin=(200, 200), - end=(300, 300), - colors=[ft.Colors.YELLOW, ft.Colors.RED], - ), - stroke_join=ft.StrokeJoin.ROUND, - stroke_cap=ft.StrokeCap.ROUND, - ), - ), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/canvas/text/main.py b/sdk/python/examples/controls/canvas/text/main.py new file mode 100644 index 0000000000..565910117a --- /dev/null +++ b/sdk/python/examples/controls/canvas/text/main.py @@ -0,0 +1,111 @@ +import math + +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Text(x=0, y=0, value="Just a text"), + cv.Circle( + x=200, y=100, radius=2, paint=ft.Paint(color=ft.Colors.RED) + ), + cv.Text( + x=200, + y=100, + style=ft.TextStyle(weight=ft.FontWeight.BOLD, size=30), + alignment=ft.Alignment.TOP_CENTER, + rotate=math.pi * 0.15, + value="Rotated", + spans=[ + ft.TextSpan( + text="around top_center", + style=ft.TextStyle( + italic=True, color=ft.Colors.GREEN, size=20 + ), + ) + ], + ), + cv.Circle( + x=400, y=100, radius=2, paint=ft.Paint(color=ft.Colors.RED) + ), + cv.Text( + x=400, + y=100, + value="Rotated around top_left", + style=ft.TextStyle(size=20), + alignment=ft.Alignment.TOP_LEFT, + rotate=math.pi * -0.15, + ), + cv.Circle( + x=600, y=200, radius=2, paint=ft.Paint(color=ft.Colors.RED) + ), + cv.Text( + x=600, + y=200, + value="Rotated around center", + style=ft.TextStyle(size=20), + alignment=ft.Alignment.CENTER, + rotate=math.pi / 2, + ), + cv.Text( + x=300, + y=400, + value=( + "Limited to max_width and right-aligned.\n" + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + "sed do eiusmod tempor incididunt ut labore et dolore " + "magna aliqua. Ut enim ad minim veniam, quis nostrud " + "exercitation " + "ullamco laboris nisi ut aliquip ex ea commodo consequat." + ), + text_align=ft.TextAlign.RIGHT, + max_width=400, + ), + cv.Text( + x=200, + y=200, + value="WOW!", + style=ft.TextStyle( + weight=ft.FontWeight.BOLD, + size=100, + foreground=ft.Paint( + color=ft.Colors.PINK, + stroke_width=6, + style=ft.PaintingStyle.STROKE, + stroke_join=ft.StrokeJoin.ROUND, + stroke_cap=ft.StrokeCap.ROUND, + ), + ), + ), + cv.Text( + x=200, + y=200, + value="WOW!", + style=ft.TextStyle( + weight=ft.FontWeight.BOLD, + size=100, + foreground=ft.Paint( + gradient=ft.PaintLinearGradient( + begin=(200, 200), + end=(300, 300), + colors=[ft.Colors.YELLOW, ft.Colors.RED], + ), + stroke_join=ft.StrokeJoin.ROUND, + stroke_cap=ft.StrokeCap.ROUND, + ), + ), + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/text/pyproject.toml b/sdk/python/examples/controls/canvas/text/pyproject.toml new file mode 100644 index 0000000000..a706829050 --- /dev/null +++ b/sdk/python/examples/controls/canvas/text/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-text" +version = "1.0.0" +description = "Canvas text rendering with transforms, spans, and paint effects." +requires-python = ">=3.10" +keywords = ["canvas", "text", "rotation", "gradient", "spans"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Canvas text" +controls = ["SafeArea", "Canvas", "Text", "TextStyle", "TextSpan", "Circle"] +layout_pattern = "free-canvas" +complexity = "basic" +features = ["rotated text", "text spans", "stroke and gradient paint"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/canvas/triangles.py b/sdk/python/examples/controls/canvas/triangles.py deleted file mode 100644 index bb3c0d1092..0000000000 --- a/sdk/python/examples/controls/canvas/triangles.py +++ /dev/null @@ -1,33 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - page.add( - cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Path( - paint=ft.Paint(style=ft.PaintingStyle.FILL), - elements=[ - cv.Path.MoveTo(x=25, y=25), - cv.Path.LineTo(x=105, y=25), - cv.Path.LineTo(x=25, y=105), - ], - ), - cv.Path( - paint=ft.Paint(stroke_width=2, style=ft.PaintingStyle.STROKE), - elements=[ - cv.Path.MoveTo(x=125, y=125), - cv.Path.LineTo(x=125, y=45), - cv.Path.LineTo(x=45, y=125), - cv.Path.Close(), - ], - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/canvas/triangles/main.py b/sdk/python/examples/controls/canvas/triangles/main.py new file mode 100644 index 0000000000..ce074c4c58 --- /dev/null +++ b/sdk/python/examples/controls/canvas/triangles/main.py @@ -0,0 +1,36 @@ +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Path( + paint=ft.Paint(style=ft.PaintingStyle.FILL), + elements=[ + cv.Path.MoveTo(x=25, y=25), + cv.Path.LineTo(x=105, y=25), + cv.Path.LineTo(x=25, y=105), + ], + ), + cv.Path( + paint=ft.Paint(stroke_width=2, style=ft.PaintingStyle.STROKE), + elements=[ + cv.Path.MoveTo(x=125, y=125), + cv.Path.LineTo(x=125, y=45), + cv.Path.LineTo(x=45, y=125), + cv.Path.Close(), + ], + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/canvas/triangles/pyproject.toml b/sdk/python/examples/controls/canvas/triangles/pyproject.toml new file mode 100644 index 0000000000..8167e855dd --- /dev/null +++ b/sdk/python/examples/controls/canvas/triangles/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "canvas-triangles" +version = "1.0.0" +description = "Canvas triangles with fill and stroke path styles." +requires-python = ">=3.10" +keywords = ["canvas", "triangles", "path", "stroke", "fill"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Canvas"] + +[tool.flet.metadata] +title = "Triangles" +controls = ["SafeArea", "Canvas", "Path"] +layout_pattern = "free-canvas" +complexity = "basic" +features = ["path drawing", "filled and outlined shapes"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/card/__init__.py b/sdk/python/examples/controls/card/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/card/music_info.py b/sdk/python/examples/controls/card/music_info.py deleted file mode 100644 index 9a1442a9a0..0000000000 --- a/sdk/python/examples/controls/card/music_info.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Card Example" - page.theme_mode = ft.ThemeMode.LIGHT - - page.add( - ft.Card( - shadow_color=ft.Colors.ON_SURFACE_VARIANT, - content=ft.Container( - width=400, - padding=10, - content=ft.Column( - controls=[ - ft.ListTile( - bgcolor=ft.Colors.GREY_400, - leading=ft.Icon(ft.Icons.ALBUM), - title=ft.Text("The Enchanted Nightingale"), - subtitle=ft.Text( - "Music by Julie Gable. Lyrics by Sidney Stein." - ), - ), - ft.Row( - alignment=ft.MainAxisAlignment.END, - controls=[ - ft.TextButton("Buy tickets"), - ft.TextButton("Listen"), - ], - ), - ] - ), - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/card/music_info/main.py b/sdk/python/examples/controls/card/music_info/main.py new file mode 100644 index 0000000000..3996d62040 --- /dev/null +++ b/sdk/python/examples/controls/card/music_info/main.py @@ -0,0 +1,41 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Card Example" + page.theme_mode = ft.ThemeMode.LIGHT + + page.add( + ft.SafeArea( + content=ft.Card( + shadow_color=ft.Colors.ON_SURFACE_VARIANT, + content=ft.Container( + width=400, + padding=10, + content=ft.Column( + controls=[ + ft.ListTile( + bgcolor=ft.Colors.GREY_400, + leading=ft.Icon(ft.Icons.ALBUM), + title=ft.Text("The Enchanted Nightingale"), + subtitle=ft.Text( + "Music by Julie Gable. Lyrics by Sidney Stein." + ), + ), + ft.Row( + alignment=ft.MainAxisAlignment.END, + controls=[ + ft.TextButton("Buy tickets"), + ft.TextButton("Listen"), + ], + ), + ] + ), + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/card/music_info/pyproject.toml b/sdk/python/examples/controls/card/music_info/pyproject.toml new file mode 100644 index 0000000000..3915c0af7c --- /dev/null +++ b/sdk/python/examples/controls/card/music_info/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "card-music-info" +version = "1.0.0" +description = "Card with music info and action buttons." +requires-python = ">=3.10" +keywords = ["card", "listtile", "actions", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Card"] + +[tool.flet.metadata] +title = "Music info" +controls = ["SafeArea", "Card", "Container", "Column", "ListTile", "Row", "TextButton"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["card layout", "action buttons"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/bar_chart/__init__.py b/sdk/python/examples/controls/charts/bar_chart/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/charts/bar_chart/example_1.py b/sdk/python/examples/controls/charts/bar_chart/example_1.py deleted file mode 100644 index fb1c91fd98..0000000000 --- a/sdk/python/examples/controls/charts/bar_chart/example_1.py +++ /dev/null @@ -1,97 +0,0 @@ -import flet as ft -import flet_charts as fch - - -def main(page: ft.Page): - page.add( - fch.BarChart( - expand=True, - interactive=True, - max_y=110, - border=ft.Border.all(1, ft.Colors.GREY_400), - horizontal_grid_lines=fch.ChartGridLines( - color=ft.Colors.GREY_300, width=1, dash_pattern=[3, 3] - ), - tooltip=fch.BarChartTooltip( - bgcolor=ft.Colors.with_opacity(0.5, ft.Colors.GREY_300), - border_radius=ft.BorderRadius.all(20), - ), - left_axis=fch.ChartAxis( - label_size=40, title=ft.Text("Fruit supply"), title_size=40 - ), - right_axis=fch.ChartAxis(show_labels=False), - bottom_axis=fch.ChartAxis( - label_size=40, - labels=[ - fch.ChartAxisLabel( - value=0, label=ft.Container(ft.Text("Apple"), padding=10) - ), - fch.ChartAxisLabel( - value=1, label=ft.Container(ft.Text("Blueberry"), padding=10) - ), - fch.ChartAxisLabel( - value=2, label=ft.Container(ft.Text("Cherry"), padding=10) - ), - fch.ChartAxisLabel( - value=3, label=ft.Container(ft.Text("Orange"), padding=10) - ), - ], - ), - groups=[ - fch.BarChartGroup( - x=0, - rods=[ - fch.BarChartRod( - from_y=0, - to_y=40, - width=40, - color=ft.Colors.GREEN, - border_radius=0, - ), - ], - ), - fch.BarChartGroup( - x=1, - rods=[ - fch.BarChartRod( - from_y=0, - to_y=100, - width=40, - color=ft.Colors.BLUE, - tooltip=fch.BarChartRodTooltip("Blueberry"), - border_radius=0, - ), - ], - ), - fch.BarChartGroup( - x=2, - rods=[ - fch.BarChartRod( - from_y=0, - to_y=30, - width=40, - color=ft.Colors.RED, - border_radius=0, - ), - ], - ), - fch.BarChartGroup( - x=3, - rods=[ - fch.BarChartRod( - from_y=0, - to_y=60, - width=40, - color=ft.Colors.ORANGE, - tooltip=fch.BarChartRodTooltip("Orange"), - border_radius=0, - ), - ], - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/bar_chart/example_1/main.py b/sdk/python/examples/controls/charts/bar_chart/example_1/main.py new file mode 100644 index 0000000000..36955504a3 --- /dev/null +++ b/sdk/python/examples/controls/charts/bar_chart/example_1/main.py @@ -0,0 +1,100 @@ +import flet as ft +import flet_charts as fch + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=fch.BarChart( + expand=True, + interactive=True, + max_y=110, + border=ft.Border.all(1, ft.Colors.GREY_400), + horizontal_grid_lines=fch.ChartGridLines( + color=ft.Colors.GREY_300, width=1, dash_pattern=[3, 3] + ), + tooltip=fch.BarChartTooltip( + bgcolor=ft.Colors.with_opacity(0.5, ft.Colors.GREY_300), + border_radius=ft.BorderRadius.all(20), + ), + left_axis=fch.ChartAxis( + label_size=40, title=ft.Text("Fruit supply"), title_size=40 + ), + right_axis=fch.ChartAxis(show_labels=False), + bottom_axis=fch.ChartAxis( + label_size=40, + labels=[ + fch.ChartAxisLabel( + value=0, label=ft.Container(ft.Text("Apple"), padding=10) + ), + fch.ChartAxisLabel( + value=1, + label=ft.Container(ft.Text("Blueberry"), padding=10), + ), + fch.ChartAxisLabel( + value=2, label=ft.Container(ft.Text("Cherry"), padding=10) + ), + fch.ChartAxisLabel( + value=3, label=ft.Container(ft.Text("Orange"), padding=10) + ), + ], + ), + groups=[ + fch.BarChartGroup( + x=0, + rods=[ + fch.BarChartRod( + from_y=0, + to_y=40, + width=40, + color=ft.Colors.GREEN, + border_radius=0, + ), + ], + ), + fch.BarChartGroup( + x=1, + rods=[ + fch.BarChartRod( + from_y=0, + to_y=100, + width=40, + color=ft.Colors.BLUE, + tooltip=fch.BarChartRodTooltip("Blueberry"), + border_radius=0, + ), + ], + ), + fch.BarChartGroup( + x=2, + rods=[ + fch.BarChartRod( + from_y=0, + to_y=30, + width=40, + color=ft.Colors.RED, + border_radius=0, + ), + ], + ), + fch.BarChartGroup( + x=3, + rods=[ + fch.BarChartRod( + from_y=0, + to_y=60, + width=40, + color=ft.Colors.ORANGE, + tooltip=fch.BarChartRodTooltip("Orange"), + border_radius=0, + ), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/bar_chart/example_1/pyproject.toml b/sdk/python/examples/controls/charts/bar_chart/example_1/pyproject.toml new file mode 100644 index 0000000000..4514c98701 --- /dev/null +++ b/sdk/python/examples/controls/charts/bar_chart/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-bar-chart-example-1" +version = "1.0.0" +description = "Interactive bar chart showing fruit supply with custom axes, tooltips, and grid lines." +requires-python = ">=3.10" +keywords = ["charts", "bar-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/BarChart"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "BarChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/bar_chart/example_2.py b/sdk/python/examples/controls/charts/bar_chart/example_2.py deleted file mode 100644 index 56f275005f..0000000000 --- a/sdk/python/examples/controls/charts/bar_chart/example_2.py +++ /dev/null @@ -1,74 +0,0 @@ -import flet_charts as fch - -import flet as ft - - -class CustomRod(fch.BarChartRod): - def __init__(self, y: float, hovered: bool = False): - super().__init__() - self.hovered = hovered - self.y = y - self.width = 22 - self.color = ft.Colors.WHITE - self.bg_to_y = 20 - self.bg_color = ft.Colors.GREEN_300 - - def before_update(self): - super().before_update() - self.to_y = self.y + 0.5 if self.hovered else self.y - self.color = ft.Colors.YELLOW if self.hovered else ft.Colors.WHITE - self.border_side = ( - ft.BorderSide(width=1, color=ft.Colors.RED) - if self.hovered - else ft.BorderSide(width=1, color=ft.Colors.BLUE) - ) - - -def main(page: ft.Page): - def on_chart_event(e: fch.BarChartEvent): - if e.type == fch.ChartEventType.POINTER_HOVER: - for group_index, group in enumerate(chart.groups): - for rod_index, rod in enumerate(group.rods): - rod.hovered = ( - e.group_index == group_index and e.rod_index == rod_index - ) - chart.update() - - chart = fch.BarChart( - on_event=on_chart_event, - interactive=True, - groups=[ - fch.BarChartGroup(x=0, rods=[CustomRod(5)]), - fch.BarChartGroup(x=1, rods=[CustomRod(6.5)]), - fch.BarChartGroup(x=2, rods=[CustomRod(15)]), - fch.BarChartGroup(x=3, rods=[CustomRod(7.5)]), - fch.BarChartGroup(x=4, rods=[CustomRod(9)]), - fch.BarChartGroup(x=5, rods=[CustomRod(11.5)]), - fch.BarChartGroup(x=6, rods=[CustomRod(6)]), - ], - bottom_axis=fch.ChartAxis( - labels=[ - fch.ChartAxisLabel(value=0, label=ft.Text("M", color=ft.Colors.BLUE)), - fch.ChartAxisLabel(value=1, label=ft.Text("T", color=ft.Colors.YELLOW)), - fch.ChartAxisLabel(value=2, label=ft.Text("W", color=ft.Colors.BLUE)), - fch.ChartAxisLabel(value=3, label=ft.Text("T", color=ft.Colors.YELLOW)), - fch.ChartAxisLabel(value=4, label=ft.Text("F", color=ft.Colors.BLUE)), - fch.ChartAxisLabel(value=5, label=ft.Text("S", color=ft.Colors.YELLOW)), - fch.ChartAxisLabel(value=6, label=ft.Text("S", color=ft.Colors.BLUE)), - ], - ), - ) - - page.add( - ft.Container( - content=chart, - bgcolor=ft.Colors.GREEN_200, - padding=10, - border_radius=5, - expand=True, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/bar_chart/example_2/main.py b/sdk/python/examples/controls/charts/bar_chart/example_2/main.py new file mode 100644 index 0000000000..c03f535fd5 --- /dev/null +++ b/sdk/python/examples/controls/charts/bar_chart/example_2/main.py @@ -0,0 +1,75 @@ +import flet as ft +import flet_charts as fch + + +class CustomRod(fch.BarChartRod): + def __init__(self, y: float, hovered: bool = False): + super().__init__() + self.hovered = hovered + self.y = y + self.width = 22 + self.color = ft.Colors.WHITE + self.bg_to_y = 20 + self.bg_color = ft.Colors.GREEN_300 + + def before_update(self): + super().before_update() + self.to_y = self.y + 0.5 if self.hovered else self.y + self.color = ft.Colors.YELLOW if self.hovered else ft.Colors.WHITE + self.border_side = ( + ft.BorderSide(width=1, color=ft.Colors.RED) + if self.hovered + else ft.BorderSide(width=1, color=ft.Colors.BLUE) + ) + + +def main(page: ft.Page): + def on_chart_event(e: fch.BarChartEvent): + if e.type == fch.ChartEventType.POINTER_HOVER: + for group_index, group in enumerate(chart.groups): + for rod_index, rod in enumerate(group.rods): + rod.hovered = ( + e.group_index == group_index and e.rod_index == rod_index + ) + chart.update() + + chart = fch.BarChart( + on_event=on_chart_event, + interactive=True, + groups=[ + fch.BarChartGroup(x=0, rods=[CustomRod(5)]), + fch.BarChartGroup(x=1, rods=[CustomRod(6.5)]), + fch.BarChartGroup(x=2, rods=[CustomRod(15)]), + fch.BarChartGroup(x=3, rods=[CustomRod(7.5)]), + fch.BarChartGroup(x=4, rods=[CustomRod(9)]), + fch.BarChartGroup(x=5, rods=[CustomRod(11.5)]), + fch.BarChartGroup(x=6, rods=[CustomRod(6)]), + ], + bottom_axis=fch.ChartAxis( + labels=[ + fch.ChartAxisLabel(value=0, label=ft.Text("M", color=ft.Colors.BLUE)), + fch.ChartAxisLabel(value=1, label=ft.Text("T", color=ft.Colors.YELLOW)), + fch.ChartAxisLabel(value=2, label=ft.Text("W", color=ft.Colors.BLUE)), + fch.ChartAxisLabel(value=3, label=ft.Text("T", color=ft.Colors.YELLOW)), + fch.ChartAxisLabel(value=4, label=ft.Text("F", color=ft.Colors.BLUE)), + fch.ChartAxisLabel(value=5, label=ft.Text("S", color=ft.Colors.YELLOW)), + fch.ChartAxisLabel(value=6, label=ft.Text("S", color=ft.Colors.BLUE)), + ], + ), + ) + + page.add( + ft.SafeArea( + content=ft.Container( + bgcolor=ft.Colors.GREEN_200, + padding=10, + border_radius=5, + expand=True, + content=chart, + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/bar_chart/example_2/pyproject.toml b/sdk/python/examples/controls/charts/bar_chart/example_2/pyproject.toml new file mode 100644 index 0000000000..4a3b548b99 --- /dev/null +++ b/sdk/python/examples/controls/charts/bar_chart/example_2/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-bar-chart-example-2" +version = "1.0.0" +description = "Bar chart with pointer-hover interaction that highlights individual rods dynamically." +requires-python = ">=3.10" +keywords = ["charts", "bar-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/BarChart"] + +[tool.flet.metadata] +title = "Example 2" +controls = ["SafeArea", "BarChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/candlestick_chart/__init__.py b/sdk/python/examples/controls/charts/candlestick_chart/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/charts/candlestick_chart/example_1.py b/sdk/python/examples/controls/charts/candlestick_chart/example_1.py deleted file mode 100644 index 21e631dd81..0000000000 --- a/sdk/python/examples/controls/charts/candlestick_chart/example_1.py +++ /dev/null @@ -1,128 +0,0 @@ -import flet as ft -import flet_charts as fch - -CANDLE_DATA = [ - ("Mon", 24.8, 28.6, 23.9, 27.2), - ("Tue", 27.2, 30.1, 25.8, 28.4), - ("Wed", 28.4, 31.2, 26.5, 29.1), - ("Thu", 29.1, 32.4, 27.9, 31.8), - ("Fri", 31.8, 34.0, 29.7, 30.2), - ("Sat", 30.2, 33.6, 28.3, 32.7), - ("Sun", 32.7, 35.5, 30.1, 34.6), -] - - -def build_spots() -> list[fch.CandlestickChartSpot]: - """Create candlestick spots from the static data.""" - spots: list[fch.CandlestickChartSpot] = [] - for index, (label, open_, high, low, close) in enumerate(CANDLE_DATA): - spots.append( - fch.CandlestickChartSpot( - x=float(index), - open=open_, - high=high, - low=low, - close=close, - selected=index == len(CANDLE_DATA) - 1, - tooltip=fch.CandlestickChartSpotTooltip( - text=( - f"{label}\n" - f"Open: {open_:0.1f}\n" - f"High: {high:0.1f}\n" - f"Low : {low:0.1f}\n" - f"Close: {close:0.1f}" - ), - bottom_margin=12, - ), - ) - ) - return spots - - -def main(page: ft.Page): - page.title = "Candlestick chart" - page.padding = 24 - page.theme_mode = ft.ThemeMode.DARK - - info = ft.Text("Interact with the chart to see event details.") - - spots = build_spots() - min_x = -0.5 - max_x = len(spots) - 0.5 - min_y = min(low for _, _, _, low, _ in CANDLE_DATA) - 1 - max_y = max(high for _, _, _, _, high in CANDLE_DATA) + 1 - - def handle_event(e: fch.CandlestickChartEvent): - if e.spot_index is not None and e.spot_index >= 0: - label, open_, high, low, close = CANDLE_DATA[e.spot_index] - info.value = ( - f"{e.type.value} • {label}: " - f"O {open_:0.1f} H {high:0.1f} L {low:0.1f} C {close:0.1f}" - ) - else: - info.value = f"{e.type.value} • outside candlesticks" - info.update() - - chart = fch.CandlestickChart( - expand=True, - min_x=min_x, - max_x=max_x, - min_y=min_y, - max_y=max_y, - baseline_x=0, - baseline_y=min_y, - bgcolor=ft.Colors.with_opacity(0.2, ft.Colors.BLUE_GREY_900), - horizontal_grid_lines=fch.ChartGridLines(interval=2, dash_pattern=[2, 2]), - vertical_grid_lines=fch.ChartGridLines(interval=1, dash_pattern=[2, 2]), - left_axis=fch.ChartAxis( - label_spacing=2, - label_size=60, - title=ft.Text("Price (k USD)", color=ft.Colors.GREY_300), - show_min=False, - ), - bottom_axis=fch.ChartAxis( - labels=[ - fch.ChartAxisLabel( - value=index, - label=ft.Text(name, color=ft.Colors.GREY_300), - ) - for index, (name, *_rest) in enumerate(CANDLE_DATA) - ], - label_spacing=1, - label_size=40, - show_min=False, - show_max=False, - ), - spots=spots, - tooltip=fch.CandlestickChartTooltip( - bgcolor=ft.Colors.BLUE_GREY_800, - horizontal_alignment=fch.HorizontalAlignment.CENTER, - fit_inside_horizontally=True, - ), - on_event=handle_event, - ) - - page.add( - ft.Container( - expand=True, - border_radius=16, - padding=20, - content=ft.Column( - expand=True, - spacing=20, - controls=[ - ft.Text( - "Weekly OHLC snapshot (demo data)", - size=20, - weight=ft.FontWeight.BOLD, - ), - chart, - info, - ], - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/candlestick_chart/example_1/main.py b/sdk/python/examples/controls/charts/candlestick_chart/example_1/main.py new file mode 100644 index 0000000000..342be7df31 --- /dev/null +++ b/sdk/python/examples/controls/charts/candlestick_chart/example_1/main.py @@ -0,0 +1,130 @@ +import flet as ft +import flet_charts as fch + +CANDLE_DATA = [ + ("Mon", 24.8, 28.6, 23.9, 27.2), + ("Tue", 27.2, 30.1, 25.8, 28.4), + ("Wed", 28.4, 31.2, 26.5, 29.1), + ("Thu", 29.1, 32.4, 27.9, 31.8), + ("Fri", 31.8, 34.0, 29.7, 30.2), + ("Sat", 30.2, 33.6, 28.3, 32.7), + ("Sun", 32.7, 35.5, 30.1, 34.6), +] + + +def build_spots() -> list[fch.CandlestickChartSpot]: + """Create candlestick spots from the static data.""" + spots: list[fch.CandlestickChartSpot] = [] + for index, (label, open_, high, low, close) in enumerate(CANDLE_DATA): + spots.append( + fch.CandlestickChartSpot( + x=float(index), + open=open_, + high=high, + low=low, + close=close, + selected=index == len(CANDLE_DATA) - 1, + tooltip=fch.CandlestickChartSpotTooltip( + text=( + f"{label}\n" + f"Open: {open_:0.1f}\n" + f"High: {high:0.1f}\n" + f"Low : {low:0.1f}\n" + f"Close: {close:0.1f}" + ), + bottom_margin=12, + ), + ) + ) + return spots + + +def main(page: ft.Page): + page.title = "Candlestick chart" + page.padding = 24 + page.theme_mode = ft.ThemeMode.DARK + + info = ft.Text("Interact with the chart to see event details.") + + spots = build_spots() + min_x = -0.5 + max_x = len(spots) - 0.5 + min_y = min(low for _, _, _, low, _ in CANDLE_DATA) - 1 + max_y = max(high for _, _, _, _, high in CANDLE_DATA) + 1 + + def handle_event(e: fch.CandlestickChartEvent): + if e.spot_index is not None and e.spot_index >= 0: + label, open_, high, low, close = CANDLE_DATA[e.spot_index] + info.value = ( + f"{e.type.value} • {label}: " + f"O {open_:0.1f} H {high:0.1f} L {low:0.1f} C {close:0.1f}" + ) + else: + info.value = f"{e.type.value} • outside candlesticks" + info.update() + + chart = fch.CandlestickChart( + expand=True, + min_x=min_x, + max_x=max_x, + min_y=min_y, + max_y=max_y, + baseline_x=0, + baseline_y=min_y, + bgcolor=ft.Colors.with_opacity(0.2, ft.Colors.BLUE_GREY_900), + horizontal_grid_lines=fch.ChartGridLines(interval=2, dash_pattern=[2, 2]), + vertical_grid_lines=fch.ChartGridLines(interval=1, dash_pattern=[2, 2]), + left_axis=fch.ChartAxis( + label_spacing=2, + label_size=60, + title=ft.Text("Price (k USD)", color=ft.Colors.GREY_300), + show_min=False, + ), + bottom_axis=fch.ChartAxis( + labels=[ + fch.ChartAxisLabel( + value=index, + label=ft.Text(name, color=ft.Colors.GREY_300), + ) + for index, (name, *_rest) in enumerate(CANDLE_DATA) + ], + label_spacing=1, + label_size=40, + show_min=False, + show_max=False, + ), + spots=spots, + tooltip=fch.CandlestickChartTooltip( + bgcolor=ft.Colors.BLUE_GREY_800, + horizontal_alignment=fch.HorizontalAlignment.CENTER, + fit_inside_horizontally=True, + ), + on_event=handle_event, + ) + + page.add( + ft.SafeArea( + content=ft.Container( + expand=True, + border_radius=16, + padding=20, + content=ft.Column( + expand=True, + spacing=20, + controls=[ + ft.Text( + "Weekly OHLC snapshot (demo data)", + size=20, + weight=ft.FontWeight.BOLD, + ), + chart, + info, + ], + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/candlestick_chart/example_1/pyproject.toml b/sdk/python/examples/controls/charts/candlestick_chart/example_1/pyproject.toml new file mode 100644 index 0000000000..cc4fe18069 --- /dev/null +++ b/sdk/python/examples/controls/charts/candlestick_chart/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-candlestick-chart-example-1" +version = "1.0.0" +description = "Candlestick chart visualizing weekly OHLC data with labeled axes and event-driven tooltips." +requires-python = ">=3.10" +keywords = ["charts", "candlestick-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/CandlestickChart"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "CandlestickChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/line_chart/__init__.py b/sdk/python/examples/controls/charts/line_chart/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/charts/line_chart/dashboard_declarative.py b/sdk/python/examples/controls/charts/line_chart/dashboard_declarative.py deleted file mode 100644 index 20f7481e19..0000000000 --- a/sdk/python/examples/controls/charts/line_chart/dashboard_declarative.py +++ /dev/null @@ -1,137 +0,0 @@ -import asyncio -import random -from dataclasses import dataclass, field - -import flet as ft -import flet_charts as ft_charts - -MAX_POINTS = 20 - - -@dataclass -class DataPoint: - x: float - y: float - - -@dataclass -@ft.observable -class ChartData: - x: float = 0.0 - min_y: float = 0.0 - max_y: float = 1.0 - points: list[DataPoint] = field(default_factory=list) - - def __post_init__(self): - for _ in range(MAX_POINTS): - self.add_point() - - def add_point(self): - self.points.append( - DataPoint(x=self.x, y=random.uniform(self.min_y, self.max_y)) - ) - self.x += 0.5 - if len(self.points) > MAX_POINTS: - self.points.pop(0) - - -@ft.component -def Gauge( - min_y: float = 0.0, - max_y: float = 1.0, - width: int = 200, - height: int = 150, - line_color=ft.Colors.BLUE, - bgcolor=ft.Colors.BLUE_100, -): - chart_data, _ = ft.use_state(lambda: ChartData(min_y=min_y, max_y=max_y)) - - async def generate_chart_data(): - for _ in range(0, 100): - chart_data.add_point() - await asyncio.sleep(1.0) - - ft.on_mounted(generate_chart_data) - - return ft_charts.LineChart( - data_series=[ - ft_charts.LineChartData( - stroke_width=2, - color=line_color, - curved=True, - points=[ - ft_charts.LineChartDataPoint( - key=point.x, - x=point.x, - y=point.y, - selected_point=ft_charts.ChartCirclePoint(radius=4), - selected_below_line=False, - tooltip=f"{round(point.y, 2)}%", - ) - for point in chart_data.points - ], - below_line_bgcolor=bgcolor, - ) - ], - border=ft.Border.all(1, ft.Colors.GREY_400), - horizontal_grid_lines=ft_charts.ChartGridLines( - color=ft.Colors.GREY_300, width=1, dash_pattern=[3, 3], interval=0.1 - ), - tooltip=ft_charts.LineChartTooltip( - bgcolor=ft.Colors.BLACK_12, - border_radius=4, - padding=ft.Padding(5), - ), - min_y=0, - max_y=1, - width=width, - height=height, - animation=ft.Animation(duration=0), - ) - - -@ft.component -def App(): - return ft.Row( - controls=[ - ft.Column( - [ - ft.Text("CPU Usage"), - Gauge( - min_y=0.3, - max_y=1.0, - line_color=ft.Colors.BLUE, - bgcolor=ft.Colors.BLUE_100, - ), - ], - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - ), - ft.Column( - [ - ft.Text("Memory Usage"), - Gauge( - min_y=0.2, - max_y=0.5, - line_color=ft.Colors.GREEN, - bgcolor=ft.Colors.GREEN_100, - ), - ], - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - ), - ft.Column( - [ - ft.Text("Disk Usage"), - Gauge( - min_y=0.7, - max_y=0.9, - line_color=ft.Colors.ORANGE, - bgcolor=ft.Colors.ORANGE_100, - ), - ], - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - ), - ] - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/controls/charts/line_chart/dashboard_declarative/main.py b/sdk/python/examples/controls/charts/line_chart/dashboard_declarative/main.py new file mode 100644 index 0000000000..88dda9002d --- /dev/null +++ b/sdk/python/examples/controls/charts/line_chart/dashboard_declarative/main.py @@ -0,0 +1,144 @@ +import asyncio +import random +from dataclasses import dataclass, field + +import flet as ft +import flet_charts as ft_charts + +MAX_POINTS = 20 + + +@dataclass +class DataPoint: + x: float + y: float + + +@dataclass +@ft.observable +class ChartData: + x: float = 0.0 + min_y: float = 0.0 + max_y: float = 1.0 + points: list[DataPoint] = field(default_factory=list) + + def __post_init__(self): + for _ in range(MAX_POINTS): + self.add_point() + + def add_point(self): + self.points.append( + DataPoint(x=self.x, y=random.uniform(self.min_y, self.max_y)) + ) + self.x += 0.5 + if len(self.points) > MAX_POINTS: + self.points.pop(0) + + +@ft.component +def Gauge( + min_y: float = 0.0, + max_y: float = 1.0, + width: int = 200, + height: int = 150, + line_color=ft.Colors.BLUE, + bgcolor=ft.Colors.BLUE_100, +): + chart_data, _ = ft.use_state(lambda: ChartData(min_y=min_y, max_y=max_y)) + + async def generate_chart_data(): + for _ in range(0, 100): + chart_data.add_point() + await asyncio.sleep(1.0) + + ft.on_mounted(generate_chart_data) + + return ft_charts.LineChart( + data_series=[ + ft_charts.LineChartData( + stroke_width=2, + color=line_color, + curved=True, + points=[ + ft_charts.LineChartDataPoint( + key=point.x, + x=point.x, + y=point.y, + selected_point=ft_charts.ChartCirclePoint(radius=4), + selected_below_line=False, + tooltip=f"{round(point.y, 2)}%", + ) + for point in chart_data.points + ], + below_line_bgcolor=bgcolor, + ) + ], + border=ft.Border.all(1, ft.Colors.GREY_400), + horizontal_grid_lines=ft_charts.ChartGridLines( + color=ft.Colors.GREY_300, width=1, dash_pattern=[3, 3], interval=0.1 + ), + tooltip=ft_charts.LineChartTooltip( + bgcolor=ft.Colors.BLACK_12, + border_radius=4, + padding=ft.Padding(5), + ), + min_y=0, + max_y=1, + width=width, + height=height, + animation=ft.Animation(duration=0), + ) + + +@ft.component +def App(): + return ft.SafeArea( + content=ft.Row( + controls=[ + ft.Column( + [ + ft.Text("CPU Usage"), + Gauge( + min_y=0.3, + max_y=1.0, + line_color=ft.Colors.BLUE, + bgcolor=ft.Colors.BLUE_100, + ), + ], + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + ), + ft.Column( + [ + ft.Text("Memory Usage"), + Gauge( + min_y=0.2, + max_y=0.5, + line_color=ft.Colors.GREEN, + bgcolor=ft.Colors.GREEN_100, + ), + ], + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + ), + ft.Column( + [ + ft.Text("Disk Usage"), + Gauge( + min_y=0.7, + max_y=0.9, + line_color=ft.Colors.ORANGE, + bgcolor=ft.Colors.ORANGE_100, + ), + ], + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + ), + ] + ), + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/line_chart/dashboard_declarative/pyproject.toml b/sdk/python/examples/controls/charts/line_chart/dashboard_declarative/pyproject.toml new file mode 100644 index 0000000000..d73af835b5 --- /dev/null +++ b/sdk/python/examples/controls/charts/line_chart/dashboard_declarative/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-line-chart-dashboard-declarative" +version = "1.0.0" +description = "Declarative dashboard with live-updating line chart gauges for CPU, memory, and disk usage." +requires-python = ">=3.10" +keywords = ["charts", "line-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/LineChart"] + +[tool.flet.metadata] +title = "Dashboard Declarative" +controls = ["SafeArea", "LineChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/line_chart/example_1.py b/sdk/python/examples/controls/charts/line_chart/example_1.py deleted file mode 100644 index 9a4734c203..0000000000 --- a/sdk/python/examples/controls/charts/line_chart/example_1.py +++ /dev/null @@ -1,207 +0,0 @@ -import flet_charts as fch - -import flet as ft - - -class State: - toggled = True - - -state = State() - - -def main(page: ft.Page): - data_1 = [ - fch.LineChartData( - stroke_width=8, - color=ft.Colors.LIGHT_GREEN, - curved=True, - rounded_stroke_cap=True, - points=[ - fch.LineChartDataPoint(1, 1), - fch.LineChartDataPoint(3, 1.5), - fch.LineChartDataPoint(5, 1.4), - fch.LineChartDataPoint(7, 3.4), - fch.LineChartDataPoint(10, 2), - fch.LineChartDataPoint(12, 2.2), - fch.LineChartDataPoint(13, 1.8), - ], - ), - fch.LineChartData( - color=ft.Colors.PINK, - below_line_bgcolor=ft.Colors.with_opacity(0, ft.Colors.PINK), - stroke_width=8, - curved=True, - rounded_stroke_cap=True, - points=[ - fch.LineChartDataPoint(1, 1), - fch.LineChartDataPoint(3, 2.8), - fch.LineChartDataPoint(7, 1.2), - fch.LineChartDataPoint(10, 2.8), - fch.LineChartDataPoint(12, 2.6), - fch.LineChartDataPoint(13, 3.9), - ], - ), - fch.LineChartData( - color=ft.Colors.CYAN, - stroke_width=8, - curved=True, - rounded_stroke_cap=True, - points=[ - fch.LineChartDataPoint(1, 2.8), - fch.LineChartDataPoint(3, 1.9), - fch.LineChartDataPoint(6, 3), - fch.LineChartDataPoint(10, 1.3), - fch.LineChartDataPoint(13, 2.5), - ], - ), - ] - - data_2 = [ - fch.LineChartData( - stroke_width=4, - color=ft.Colors.with_opacity(0.5, ft.Colors.LIGHT_GREEN), - rounded_stroke_cap=True, - points=[ - fch.LineChartDataPoint(1, 1), - fch.LineChartDataPoint(3, 4), - fch.LineChartDataPoint(5, 1.8), - fch.LineChartDataPoint(7, 5), - fch.LineChartDataPoint(10, 2), - fch.LineChartDataPoint(12, 2.2), - fch.LineChartDataPoint(13, 1.8), - ], - ), - fch.LineChartData( - color=ft.Colors.with_opacity(0.5, ft.Colors.PINK), - below_line_bgcolor=ft.Colors.with_opacity(0.2, ft.Colors.PINK), - stroke_width=4, - curved=True, - rounded_stroke_cap=True, - points=[ - fch.LineChartDataPoint(1, 1), - fch.LineChartDataPoint(3, 2.8), - fch.LineChartDataPoint(7, 1.2), - fch.LineChartDataPoint(10, 2.8), - fch.LineChartDataPoint(12, 2.6), - fch.LineChartDataPoint(13, 3.9), - ], - ), - fch.LineChartData( - color=ft.Colors.with_opacity(0.5, ft.Colors.CYAN), - stroke_width=4, - rounded_stroke_cap=True, - points=[ - fch.LineChartDataPoint(1, 3.8), - fch.LineChartDataPoint(3, 1.9), - fch.LineChartDataPoint(6, 5), - fch.LineChartDataPoint(10, 3.3), - fch.LineChartDataPoint(13, 4.5), - ], - ), - ] - - chart = fch.LineChart( - data_series=data_1, - border=ft.Border( - bottom=ft.BorderSide(4, ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE)) - ), - tooltip=fch.LineChartTooltip( - bgcolor=ft.Colors.with_opacity(0.8, ft.Colors.BLUE_GREY) - ), - min_y=0, - max_y=4, - min_x=0, - max_x=14, - expand=True, - right_axis=fch.ChartAxis(show_labels=False), - left_axis=fch.ChartAxis( - label_size=40, - labels=[ - fch.ChartAxisLabel( - value=1, - label=ft.Text("1m", size=14, weight=ft.FontWeight.BOLD), - ), - fch.ChartAxisLabel( - value=2, - label=ft.Text("2m", size=14, weight=ft.FontWeight.BOLD), - ), - fch.ChartAxisLabel( - value=3, - label=ft.Text("3m", size=14, weight=ft.FontWeight.BOLD), - ), - fch.ChartAxisLabel( - value=4, - label=ft.Text("4m", size=14, weight=ft.FontWeight.BOLD), - ), - fch.ChartAxisLabel( - value=5, - label=ft.Text("5m", size=14, weight=ft.FontWeight.BOLD), - ), - fch.ChartAxisLabel( - value=6, - label=ft.Text("6m", size=14, weight=ft.FontWeight.BOLD), - ), - ], - ), - bottom_axis=fch.ChartAxis( - label_size=32, - labels=[ - fch.ChartAxisLabel( - value=2, - label=ft.Container( - margin=ft.Margin(top=10), - content=ft.Text( - value="SEP", - size=16, - weight=ft.FontWeight.BOLD, - color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), - ), - ), - ), - fch.ChartAxisLabel( - value=7, - label=ft.Container( - margin=ft.Margin(top=10), - content=ft.Text( - value="OCT", - size=16, - weight=ft.FontWeight.BOLD, - color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), - ), - ), - ), - fch.ChartAxisLabel( - value=12, - label=ft.Container( - margin=ft.Margin(top=10), - content=ft.Text( - value="DEC", - size=16, - weight=ft.FontWeight.BOLD, - color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), - ), - ), - ), - ], - ), - ) - - def toggle_data(e: ft.Event[ft.IconButton]): - if state.toggled: - chart.data_series = data_2 - chart.data_series[2].point = True - chart.max_y = 6 - chart.interactive = False - else: - chart.data_series = data_1 - chart.max_y = 4 - chart.interactive = True - state.toggled = not state.toggled - chart.update() - - page.add(ft.IconButton(ft.Icons.REFRESH, on_click=toggle_data), chart) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/line_chart/example_1/main.py b/sdk/python/examples/controls/charts/line_chart/example_1/main.py new file mode 100644 index 0000000000..5721e35204 --- /dev/null +++ b/sdk/python/examples/controls/charts/line_chart/example_1/main.py @@ -0,0 +1,215 @@ +import flet as ft +import flet_charts as fch + + +class State: + toggled = True + + +state = State() + + +def main(page: ft.Page): + data_1 = [ + fch.LineChartData( + stroke_width=8, + color=ft.Colors.LIGHT_GREEN, + curved=True, + rounded_stroke_cap=True, + points=[ + fch.LineChartDataPoint(1, 1), + fch.LineChartDataPoint(3, 1.5), + fch.LineChartDataPoint(5, 1.4), + fch.LineChartDataPoint(7, 3.4), + fch.LineChartDataPoint(10, 2), + fch.LineChartDataPoint(12, 2.2), + fch.LineChartDataPoint(13, 1.8), + ], + ), + fch.LineChartData( + color=ft.Colors.PINK, + below_line_bgcolor=ft.Colors.with_opacity(0, ft.Colors.PINK), + stroke_width=8, + curved=True, + rounded_stroke_cap=True, + points=[ + fch.LineChartDataPoint(1, 1), + fch.LineChartDataPoint(3, 2.8), + fch.LineChartDataPoint(7, 1.2), + fch.LineChartDataPoint(10, 2.8), + fch.LineChartDataPoint(12, 2.6), + fch.LineChartDataPoint(13, 3.9), + ], + ), + fch.LineChartData( + color=ft.Colors.CYAN, + stroke_width=8, + curved=True, + rounded_stroke_cap=True, + points=[ + fch.LineChartDataPoint(1, 2.8), + fch.LineChartDataPoint(3, 1.9), + fch.LineChartDataPoint(6, 3), + fch.LineChartDataPoint(10, 1.3), + fch.LineChartDataPoint(13, 2.5), + ], + ), + ] + + data_2 = [ + fch.LineChartData( + stroke_width=4, + color=ft.Colors.with_opacity(0.5, ft.Colors.LIGHT_GREEN), + rounded_stroke_cap=True, + points=[ + fch.LineChartDataPoint(1, 1), + fch.LineChartDataPoint(3, 4), + fch.LineChartDataPoint(5, 1.8), + fch.LineChartDataPoint(7, 5), + fch.LineChartDataPoint(10, 2), + fch.LineChartDataPoint(12, 2.2), + fch.LineChartDataPoint(13, 1.8), + ], + ), + fch.LineChartData( + color=ft.Colors.with_opacity(0.5, ft.Colors.PINK), + below_line_bgcolor=ft.Colors.with_opacity(0.2, ft.Colors.PINK), + stroke_width=4, + curved=True, + rounded_stroke_cap=True, + points=[ + fch.LineChartDataPoint(1, 1), + fch.LineChartDataPoint(3, 2.8), + fch.LineChartDataPoint(7, 1.2), + fch.LineChartDataPoint(10, 2.8), + fch.LineChartDataPoint(12, 2.6), + fch.LineChartDataPoint(13, 3.9), + ], + ), + fch.LineChartData( + color=ft.Colors.with_opacity(0.5, ft.Colors.CYAN), + stroke_width=4, + rounded_stroke_cap=True, + points=[ + fch.LineChartDataPoint(1, 3.8), + fch.LineChartDataPoint(3, 1.9), + fch.LineChartDataPoint(6, 5), + fch.LineChartDataPoint(10, 3.3), + fch.LineChartDataPoint(13, 4.5), + ], + ), + ] + + chart = fch.LineChart( + data_series=data_1, + border=ft.Border( + bottom=ft.BorderSide(4, ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE)) + ), + tooltip=fch.LineChartTooltip( + bgcolor=ft.Colors.with_opacity(0.8, ft.Colors.BLUE_GREY) + ), + min_y=0, + max_y=4, + min_x=0, + max_x=14, + expand=True, + right_axis=fch.ChartAxis(show_labels=False), + left_axis=fch.ChartAxis( + label_size=40, + labels=[ + fch.ChartAxisLabel( + value=1, + label=ft.Text("1m", size=14, weight=ft.FontWeight.BOLD), + ), + fch.ChartAxisLabel( + value=2, + label=ft.Text("2m", size=14, weight=ft.FontWeight.BOLD), + ), + fch.ChartAxisLabel( + value=3, + label=ft.Text("3m", size=14, weight=ft.FontWeight.BOLD), + ), + fch.ChartAxisLabel( + value=4, + label=ft.Text("4m", size=14, weight=ft.FontWeight.BOLD), + ), + fch.ChartAxisLabel( + value=5, + label=ft.Text("5m", size=14, weight=ft.FontWeight.BOLD), + ), + fch.ChartAxisLabel( + value=6, + label=ft.Text("6m", size=14, weight=ft.FontWeight.BOLD), + ), + ], + ), + bottom_axis=fch.ChartAxis( + label_size=32, + labels=[ + fch.ChartAxisLabel( + value=2, + label=ft.Container( + margin=ft.Margin(top=10), + content=ft.Text( + value="SEP", + size=16, + weight=ft.FontWeight.BOLD, + color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), + ), + ), + ), + fch.ChartAxisLabel( + value=7, + label=ft.Container( + margin=ft.Margin(top=10), + content=ft.Text( + value="OCT", + size=16, + weight=ft.FontWeight.BOLD, + color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), + ), + ), + ), + fch.ChartAxisLabel( + value=12, + label=ft.Container( + margin=ft.Margin(top=10), + content=ft.Text( + value="DEC", + size=16, + weight=ft.FontWeight.BOLD, + color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), + ), + ), + ), + ], + ), + ) + + def toggle_data(e: ft.Event[ft.IconButton]): + if state.toggled: + chart.data_series = data_2 + chart.data_series[2].point = True + chart.max_y = 6 + chart.interactive = False + else: + chart.data_series = data_1 + chart.max_y = 4 + chart.interactive = True + state.toggled = not state.toggled + chart.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.IconButton(ft.Icons.REFRESH, on_click=toggle_data), + chart, + ] + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/line_chart/example_1/pyproject.toml b/sdk/python/examples/controls/charts/line_chart/example_1/pyproject.toml new file mode 100644 index 0000000000..54d4d14a4f --- /dev/null +++ b/sdk/python/examples/controls/charts/line_chart/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-line-chart-example-1" +version = "1.0.0" +description = "Interactive multi-series line chart that toggles between detailed and simplified datasets." +requires-python = ">=3.10" +keywords = ["charts", "line-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/LineChart"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "LineChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/line_chart/example_2.py b/sdk/python/examples/controls/charts/line_chart/example_2.py deleted file mode 100644 index e541023cc2..0000000000 --- a/sdk/python/examples/controls/charts/line_chart/example_2.py +++ /dev/null @@ -1,140 +0,0 @@ -import flet as ft -import flet_charts as fch - - -class State: - toggled = True - - -state = State() - - -def main(page: ft.Page): - data_1 = [ - fch.LineChartData( - stroke_width=5, - color=ft.Colors.CYAN, - curved=True, - rounded_stroke_cap=True, - points=[ - fch.LineChartDataPoint(0, 3), - fch.LineChartDataPoint(2.6, 2), - fch.LineChartDataPoint(4.9, 5), - fch.LineChartDataPoint(6.8, 3.1), - fch.LineChartDataPoint(8, 4), - fch.LineChartDataPoint(9.5, 3), - fch.LineChartDataPoint(11, 4), - ], - ) - ] - - data_2 = [ - fch.LineChartData( - stroke_width=5, - color=ft.Colors.CYAN, - curved=True, - rounded_stroke_cap=True, - points=[ - fch.LineChartDataPoint(0, 3.44), - fch.LineChartDataPoint(2.6, 3.44), - fch.LineChartDataPoint(4.9, 3.44), - fch.LineChartDataPoint(6.8, 3.44), - fch.LineChartDataPoint(8, 3.44), - fch.LineChartDataPoint(9.5, 3.44), - fch.LineChartDataPoint(11, 3.44), - ], - ) - ] - - chart = fch.LineChart( - expand=True, - data_series=data_1, - min_y=0, - max_y=6, - min_x=0, - max_x=11, - border=ft.Border.all(3, ft.Colors.with_opacity(0.2, ft.Colors.ON_SURFACE)), - horizontal_grid_lines=fch.ChartGridLines( - interval=1, color=ft.Colors.with_opacity(0.2, ft.Colors.ON_SURFACE), width=1 - ), - vertical_grid_lines=fch.ChartGridLines( - interval=1, color=ft.Colors.with_opacity(0.2, ft.Colors.ON_SURFACE), width=1 - ), - tooltip=fch.LineChartTooltip( - bgcolor=ft.Colors.with_opacity(0.8, ft.Colors.BLUE_GREY) - ), - left_axis=fch.ChartAxis( - label_size=40, - labels=[ - fch.ChartAxisLabel( - value=1, - label=ft.Text("10K", size=14, weight=ft.FontWeight.BOLD), - ), - fch.ChartAxisLabel( - value=3, - label=ft.Text("30K", size=14, weight=ft.FontWeight.BOLD), - ), - fch.ChartAxisLabel( - value=5, - label=ft.Text("50K", size=14, weight=ft.FontWeight.BOLD), - ), - ], - ), - bottom_axis=fch.ChartAxis( - label_size=32, - labels=[ - fch.ChartAxisLabel( - value=2, - label=ft.Container( - margin=ft.Margin(top=10), - content=ft.Text( - value="MAR", - size=16, - weight=ft.FontWeight.BOLD, - color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), - ), - ), - ), - fch.ChartAxisLabel( - value=5, - label=ft.Container( - margin=ft.Margin(top=10), - content=ft.Text( - value="JUN", - size=16, - weight=ft.FontWeight.BOLD, - color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), - ), - ), - ), - fch.ChartAxisLabel( - value=8, - label=ft.Container( - margin=ft.Margin(top=10), - content=ft.Text( - value="SEP", - size=16, - weight=ft.FontWeight.BOLD, - color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), - ), - ), - ), - ], - ), - ) - - def toggle_data(e: ft.Event[ft.ElevatedButton]): - if state.toggled: - chart.data_series = data_2 - chart.interactive = False - else: - chart.data_series = data_1 - chart.interactive = True - state.toggled = not state.toggled - chart.update() - - page.add(ft.Button("avg", on_click=toggle_data), chart) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/line_chart/example_2/main.py b/sdk/python/examples/controls/charts/line_chart/example_2/main.py new file mode 100644 index 0000000000..e4dffef422 --- /dev/null +++ b/sdk/python/examples/controls/charts/line_chart/example_2/main.py @@ -0,0 +1,149 @@ +import flet as ft +import flet_charts as fch + + +class State: + toggled = True + + +state = State() + + +def main(page: ft.Page): + data_1 = [ + fch.LineChartData( + stroke_width=5, + color=ft.Colors.CYAN, + curved=True, + rounded_stroke_cap=True, + points=[ + fch.LineChartDataPoint(0, 3), + fch.LineChartDataPoint(2.6, 2), + fch.LineChartDataPoint(4.9, 5), + fch.LineChartDataPoint(6.8, 3.1), + fch.LineChartDataPoint(8, 4), + fch.LineChartDataPoint(9.5, 3), + fch.LineChartDataPoint(11, 4), + ], + ) + ] + + data_2 = [ + fch.LineChartData( + stroke_width=5, + color=ft.Colors.CYAN, + curved=True, + rounded_stroke_cap=True, + points=[ + fch.LineChartDataPoint(0, 3.44), + fch.LineChartDataPoint(2.6, 3.44), + fch.LineChartDataPoint(4.9, 3.44), + fch.LineChartDataPoint(6.8, 3.44), + fch.LineChartDataPoint(8, 3.44), + fch.LineChartDataPoint(9.5, 3.44), + fch.LineChartDataPoint(11, 3.44), + ], + ) + ] + + chart = fch.LineChart( + expand=True, + data_series=data_1, + min_y=0, + max_y=6, + min_x=0, + max_x=11, + border=ft.Border.all(3, ft.Colors.with_opacity(0.2, ft.Colors.ON_SURFACE)), + horizontal_grid_lines=fch.ChartGridLines( + interval=1, color=ft.Colors.with_opacity(0.2, ft.Colors.ON_SURFACE), width=1 + ), + vertical_grid_lines=fch.ChartGridLines( + interval=1, color=ft.Colors.with_opacity(0.2, ft.Colors.ON_SURFACE), width=1 + ), + tooltip=fch.LineChartTooltip( + bgcolor=ft.Colors.with_opacity(0.8, ft.Colors.BLUE_GREY) + ), + left_axis=fch.ChartAxis( + label_size=40, + labels=[ + fch.ChartAxisLabel( + value=1, + label=ft.Text("10K", size=14, weight=ft.FontWeight.BOLD), + ), + fch.ChartAxisLabel( + value=3, + label=ft.Text("30K", size=14, weight=ft.FontWeight.BOLD), + ), + fch.ChartAxisLabel( + value=5, + label=ft.Text("50K", size=14, weight=ft.FontWeight.BOLD), + ), + ], + ), + bottom_axis=fch.ChartAxis( + label_size=32, + labels=[ + fch.ChartAxisLabel( + value=2, + label=ft.Container( + margin=ft.Margin(top=10), + content=ft.Text( + value="MAR", + size=16, + weight=ft.FontWeight.BOLD, + color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), + ), + ), + ), + fch.ChartAxisLabel( + value=5, + label=ft.Container( + margin=ft.Margin(top=10), + content=ft.Text( + value="JUN", + size=16, + weight=ft.FontWeight.BOLD, + color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), + ), + ), + ), + fch.ChartAxisLabel( + value=8, + label=ft.Container( + margin=ft.Margin(top=10), + content=ft.Text( + value="SEP", + size=16, + weight=ft.FontWeight.BOLD, + color=ft.Colors.with_opacity(0.5, ft.Colors.ON_SURFACE), + ), + ), + ), + ], + ), + ) + + def toggle_data(e: ft.Event[ft.ElevatedButton]): + if state.toggled: + chart.data_series = data_2 + chart.interactive = False + else: + chart.data_series = data_1 + chart.interactive = True + state.toggled = not state.toggled + chart.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button("avg", on_click=toggle_data), + chart, + ] + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/line_chart/example_2/pyproject.toml b/sdk/python/examples/controls/charts/line_chart/example_2/pyproject.toml new file mode 100644 index 0000000000..4cf6bc1838 --- /dev/null +++ b/sdk/python/examples/controls/charts/line_chart/example_2/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-line-chart-example-2" +version = "1.0.0" +description = "Line chart with custom axis labels and button-triggered switching between trend and average views." +requires-python = ">=3.10" +keywords = ["charts", "line-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/LineChart"] + +[tool.flet.metadata] +title = "Example 2" +controls = ["SafeArea", "LineChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/__init__.py b/sdk/python/examples/controls/charts/matplotlib_chart/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/animate.py b/sdk/python/examples/controls/charts/matplotlib_chart/animate.py deleted file mode 100644 index 87cdebeb8d..0000000000 --- a/sdk/python/examples/controls/charts/matplotlib_chart/animate.py +++ /dev/null @@ -1,57 +0,0 @@ -import logging - -import matplotlib.pyplot as plt -import numpy as np - -import flet as ft -import flet_charts - -logging.basicConfig(level=logging.INFO) - -state = {} - - -def main(page: ft.Page): - import matplotlib.animation as animation - - # Fixing random state for reproducibility - np.random.seed(19680801) - - def random_walk(num_steps, max_step=0.05): - """Return a 3D random walk as (num_steps, 3) array.""" - start_pos = np.random.random(3) - steps = np.random.uniform(-max_step, max_step, size=(num_steps, 3)) - walk = start_pos + np.cumsum(steps, axis=0) - return walk - - def update_lines(num, walks, lines): - for line, walk in zip(lines, walks): - line.set_data_3d(walk[:num, :].T) - return lines - - # Data: 40 random walks as (num_steps, 3) arrays - num_steps = 30 - walks = [random_walk(num_steps) for index in range(40)] - - # Attaching 3D axis to the figure - fig = plt.figure() - ax = fig.add_subplot(projection="3d") - - # Create lines initially without data - lines = [ax.plot([], [], [])[0] for _ in walks] - - # Setting the Axes properties - ax.set(xlim3d=(0, 1), xlabel="X") - ax.set(ylim3d=(0, 1), ylabel="Y") - ax.set(zlim3d=(0, 1), zlabel="Z") - - # Creating the Animation object - state["anim"] = animation.FuncAnimation( - fig, update_lines, num_steps, fargs=(walks, lines), interval=100 - ) - - page.add(flet_charts.MatplotlibChartWithToolbar(figure=fig, expand=True)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/animate/main.py b/sdk/python/examples/controls/charts/matplotlib_chart/animate/main.py new file mode 100644 index 0000000000..24c820419a --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/animate/main.py @@ -0,0 +1,62 @@ +import logging + +import matplotlib.pyplot as plt +import numpy as np + +import flet as ft +import flet_charts + +logging.basicConfig(level=logging.INFO) + +state = {} + + +def main(page: ft.Page): + import matplotlib.animation as animation + + # Fixing random state for reproducibility + np.random.seed(19680801) + + def random_walk(num_steps, max_step=0.05): + """Return a 3D random walk as (num_steps, 3) array.""" + start_pos = np.random.random(3) + steps = np.random.uniform(-max_step, max_step, size=(num_steps, 3)) + walk = start_pos + np.cumsum(steps, axis=0) + return walk + + def update_lines(num, walks, lines): + for line, walk in zip(lines, walks): + line.set_data_3d(walk[:num, :].T) + return lines + + # Data: 40 random walks as (num_steps, 3) arrays + num_steps = 30 + walks = [random_walk(num_steps) for index in range(40)] + + # Attaching 3D axis to the figure + fig = plt.figure() + ax = fig.add_subplot(projection="3d") + + # Create lines initially without data + lines = [ax.plot([], [], [])[0] for _ in walks] + + # Setting the Axes properties + ax.set(xlim3d=(0, 1), xlabel="X") + ax.set(ylim3d=(0, 1), ylabel="Y") + ax.set(zlim3d=(0, 1), zlabel="Z") + + # Creating the Animation object + state["anim"] = animation.FuncAnimation( + fig, update_lines, num_steps, fargs=(walks, lines), interval=100 + ) + + page.add( + ft.SafeArea( + expand=True, + content=flet_charts.MatplotlibChartWithToolbar(figure=fig, expand=True), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/animate/pyproject.toml b/sdk/python/examples/controls/charts/matplotlib_chart/animate/pyproject.toml new file mode 100644 index 0000000000..6f85b24086 --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/animate/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-matplotlib-chart-animate" +version = "1.0.0" +description = "Animated 3D random-walk Matplotlib chart embedded in a Flet application." +requires-python = ">=3.10" +keywords = ["charts", "matplotlib-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts", "matplotlib", "numpy"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/MatplotlibChart"] + +[tool.flet.metadata] +title = "Animate" +controls = ["SafeArea", "MatplotlibChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["animated chart", "save to file"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/bar_chart.py b/sdk/python/examples/controls/charts/matplotlib_chart/bar_chart.py deleted file mode 100644 index b0fb23e225..0000000000 --- a/sdk/python/examples/controls/charts/matplotlib_chart/bar_chart.py +++ /dev/null @@ -1,25 +0,0 @@ -import matplotlib.pyplot as plt - -import flet as ft -import flet_charts as fch - - -def main(page: ft.Page): - fig, ax = plt.subplots() - - fruits = ["apple", "blueberry", "cherry", "orange"] - counts = [40, 100, 30, 55] - bar_labels = ["red", "blue", "_red", "orange"] - bar_colors = ["tab:red", "tab:blue", "tab:red", "tab:orange"] - - ax.bar(fruits, counts, label=bar_labels, color=bar_colors) - - ax.set_ylabel("fruit supply") - ax.set_title("Fruit supply by kind and color") - ax.legend(title="Fruit color") - - page.add(fch.MatplotlibChart(figure=fig, expand=True)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/bar_chart/main.py b/sdk/python/examples/controls/charts/matplotlib_chart/bar_chart/main.py new file mode 100644 index 0000000000..edd9252dc8 --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/bar_chart/main.py @@ -0,0 +1,30 @@ +import matplotlib.pyplot as plt + +import flet as ft +import flet_charts as fch + + +def main(page: ft.Page): + fig, ax = plt.subplots() + + fruits = ["apple", "blueberry", "cherry", "orange"] + counts = [40, 100, 30, 55] + bar_labels = ["red", "blue", "_red", "orange"] + bar_colors = ["tab:red", "tab:blue", "tab:red", "tab:orange"] + + ax.bar(fruits, counts, label=bar_labels, color=bar_colors) + + ax.set_ylabel("fruit supply") + ax.set_title("Fruit supply by kind and color") + ax.legend(title="Fruit color") + + page.add( + ft.SafeArea( + expand=True, + content=fch.MatplotlibChart(figure=fig, expand=True), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/bar_chart/pyproject.toml b/sdk/python/examples/controls/charts/matplotlib_chart/bar_chart/pyproject.toml new file mode 100644 index 0000000000..e926721e7c --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/bar_chart/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-matplotlib-chart-bar-chart" +version = "1.0.0" +description = "Matplotlib bar chart example with categorical data, labels, and legend integration." +requires-python = ">=3.10" +keywords = ["charts", "matplotlib-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts", "matplotlib", "numpy"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/MatplotlibChart"] + +[tool.flet.metadata] +title = "Bar Chart" +controls = ["SafeArea", "MatplotlibChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/handle_events.py b/sdk/python/examples/controls/charts/matplotlib_chart/handle_events.py deleted file mode 100644 index 499d645f7f..0000000000 --- a/sdk/python/examples/controls/charts/matplotlib_chart/handle_events.py +++ /dev/null @@ -1,104 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -import flet as ft -import flet_charts as fch - -state = {} - - -def main(page: ft.Page): - # Fixing random state for reproducibility - np.random.seed(19680801) - - X = np.random.rand(100, 200) - xs = np.mean(X, axis=1) - ys = np.std(X, axis=1) - - fig, (ax, ax2) = plt.subplots(2, 1) - ax.set_title("click on point to plot time series") - (line,) = ax.plot(xs, ys, "o", picker=True, pickradius=5) - - class PointBrowser: - """ - Click on a point to select and highlight it -- the data that - generated the point will be shown in the lower Axes. Use the 'n' - and 'p' keys to browse through the next and previous points - """ - - def __init__(self): - self.lastind = 0 - - self.text = ax.text( - 0.05, 0.95, "selected: none", transform=ax.transAxes, va="top" - ) - (self.selected,) = ax.plot( - [xs[0]], [ys[0]], "o", ms=12, alpha=0.4, color="yellow", visible=False - ) - - def on_press(self, event): - if self.lastind is None: - return - if event.key not in ("n", "p"): - return - inc = 1 if event.key == "n" else -1 - - self.lastind += inc - self.lastind = np.clip(self.lastind, 0, len(xs) - 1) - self.update() - - def on_pick(self, event): - if event.artist != line: - return True - - N = len(event.ind) - if not N: - return True - - # the click locations - x = event.mouseevent.xdata - y = event.mouseevent.ydata - - distances = np.hypot(x - xs[event.ind], y - ys[event.ind]) - indmin = distances.argmin() - dataind = event.ind[indmin] - - self.lastind = dataind - self.update() - - def update(self): - if self.lastind is None: - return - - dataind = self.lastind - - ax2.clear() - ax2.plot(X[dataind]) - - ax2.text( - 0.05, - 0.9, - f"mu={xs[dataind]:1.3f}\nsigma={ys[dataind]:1.3f}", - transform=ax2.transAxes, - va="top", - ) - ax2.set_ylim(-0.5, 1.5) - self.selected.set_visible(True) - self.selected.set_data([xs[dataind]], [ys[dataind]]) - - self.text.set_text(f"selected: {dataind:d}") - fig.canvas.draw() - - browser = PointBrowser() - state["browser"] = browser - - fig.canvas.mpl_connect("pick_event", browser.on_pick) - fig.canvas.mpl_connect("key_press_event", browser.on_press) - - # plt.show() - - page.add(fch.MatplotlibChartWithToolbar(figure=fig, expand=True)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/handle_events/main.py b/sdk/python/examples/controls/charts/matplotlib_chart/handle_events/main.py new file mode 100644 index 0000000000..2c0541ae5b --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/handle_events/main.py @@ -0,0 +1,109 @@ +import matplotlib.pyplot as plt +import numpy as np + +import flet as ft +import flet_charts as fch + +state = {} + + +def main(page: ft.Page): + # Fixing random state for reproducibility + np.random.seed(19680801) + + X = np.random.rand(100, 200) + xs = np.mean(X, axis=1) + ys = np.std(X, axis=1) + + fig, (ax, ax2) = plt.subplots(2, 1) + ax.set_title("click on point to plot time series") + (line,) = ax.plot(xs, ys, "o", picker=True, pickradius=5) + + class PointBrowser: + """ + Click on a point to select and highlight it -- the data that + generated the point will be shown in the lower Axes. Use the 'n' + and 'p' keys to browse through the next and previous points + """ + + def __init__(self): + self.lastind = 0 + + self.text = ax.text( + 0.05, 0.95, "selected: none", transform=ax.transAxes, va="top" + ) + (self.selected,) = ax.plot( + [xs[0]], [ys[0]], "o", ms=12, alpha=0.4, color="yellow", visible=False + ) + + def on_press(self, event): + if self.lastind is None: + return + if event.key not in ("n", "p"): + return + inc = 1 if event.key == "n" else -1 + + self.lastind += inc + self.lastind = np.clip(self.lastind, 0, len(xs) - 1) + self.update() + + def on_pick(self, event): + if event.artist != line: + return True + + N = len(event.ind) + if not N: + return True + + # the click locations + x = event.mouseevent.xdata + y = event.mouseevent.ydata + + distances = np.hypot(x - xs[event.ind], y - ys[event.ind]) + indmin = distances.argmin() + dataind = event.ind[indmin] + + self.lastind = dataind + self.update() + + def update(self): + if self.lastind is None: + return + + dataind = self.lastind + + ax2.clear() + ax2.plot(X[dataind]) + + ax2.text( + 0.05, + 0.9, + f"mu={xs[dataind]:1.3f}\nsigma={ys[dataind]:1.3f}", + transform=ax2.transAxes, + va="top", + ) + ax2.set_ylim(-0.5, 1.5) + self.selected.set_visible(True) + self.selected.set_data([xs[dataind]], [ys[dataind]]) + + self.text.set_text(f"selected: {dataind:d}") + fig.canvas.draw() + + browser = PointBrowser() + state["browser"] = browser + + fig.canvas.mpl_connect("pick_event", browser.on_pick) + fig.canvas.mpl_connect("key_press_event", browser.on_press) + + # plt.show() + + page.add( + ft.SafeArea( + expand=True, + content=fch.MatplotlibChartWithToolbar(figure=fig, expand=True), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/handle_events/pyproject.toml b/sdk/python/examples/controls/charts/matplotlib_chart/handle_events/pyproject.toml new file mode 100644 index 0000000000..9e577cecfa --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/handle_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-matplotlib-chart-handle-events" +version = "1.0.0" +description = "Matplotlib chart demonstrating pick and keyboard events to inspect selected data points." +requires-python = ">=3.10" +keywords = ["charts", "matplotlib-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts", "matplotlib", "numpy"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/MatplotlibChart"] + +[tool.flet.metadata] +title = "Handle Events" +controls = ["SafeArea", "MatplotlibChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["event handling", "save to file"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/three_d.py b/sdk/python/examples/controls/charts/matplotlib_chart/three_d.py deleted file mode 100644 index 214318b08f..0000000000 --- a/sdk/python/examples/controls/charts/matplotlib_chart/three_d.py +++ /dev/null @@ -1,37 +0,0 @@ -import logging - -import matplotlib.pyplot as plt -import numpy as np - -import flet as ft -import flet_charts - -logging.basicConfig(level=logging.INFO) - - -def main(page: ft.Page): - plt.style.use("_mpl-gallery") - - # Make data for a double helix - n = 50 - theta = np.linspace(0, 2 * np.pi, n) - x1 = np.cos(theta) - y1 = np.sin(theta) - z1 = np.linspace(0, 1, n) - x2 = np.cos(theta + np.pi) - y2 = np.sin(theta + np.pi) - z2 = z1 - - # Plot with defined figure size - fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, figsize=(8, 6)) - ax.fill_between(x1, y1, z1, x2, y2, z2, alpha=0.5) - ax.plot(x1, y1, z1, linewidth=2, color="C0") - ax.plot(x2, y2, z2, linewidth=2, color="C0") - - ax.set(xticklabels=[], yticklabels=[], zticklabels=[]) - - page.add(flet_charts.MatplotlibChartWithToolbar(figure=fig)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/three_d/main.py b/sdk/python/examples/controls/charts/matplotlib_chart/three_d/main.py new file mode 100644 index 0000000000..402c6e6e76 --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/three_d/main.py @@ -0,0 +1,37 @@ +import logging + +import matplotlib.pyplot as plt +import numpy as np + +import flet as ft +import flet_charts + +logging.basicConfig(level=logging.INFO) + + +def main(page: ft.Page): + plt.style.use("_mpl-gallery") + + # Make data for a double helix + n = 50 + theta = np.linspace(0, 2 * np.pi, n) + x1 = np.cos(theta) + y1 = np.sin(theta) + z1 = np.linspace(0, 1, n) + x2 = np.cos(theta + np.pi) + y2 = np.sin(theta + np.pi) + z2 = z1 + + # Plot with defined figure size + fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, figsize=(8, 6)) + ax.fill_between(x1, y1, z1, x2, y2, z2, alpha=0.5) + ax.plot(x1, y1, z1, linewidth=2, color="C0") + ax.plot(x2, y2, z2, linewidth=2, color="C0") + + ax.set(xticklabels=[], yticklabels=[], zticklabels=[]) + + page.add(ft.SafeArea(content=flet_charts.MatplotlibChartWithToolbar(figure=fig))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/three_d/pyproject.toml b/sdk/python/examples/controls/charts/matplotlib_chart/three_d/pyproject.toml new file mode 100644 index 0000000000..252c4b2eb2 --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/three_d/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-matplotlib-chart-three-d" +version = "1.0.0" +description = "Matplotlib 3D chart rendering a double-helix-style surface and line composition." +requires-python = ">=3.10" +keywords = ["charts", "matplotlib-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts", "matplotlib", "numpy"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/MatplotlibChart"] + +[tool.flet.metadata] +title = "Three D" +controls = ["SafeArea", "MatplotlibChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["3d chart", "save to file"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/toolbar.py b/sdk/python/examples/controls/charts/matplotlib_chart/toolbar.py deleted file mode 100644 index d58c1ead61..0000000000 --- a/sdk/python/examples/controls/charts/matplotlib_chart/toolbar.py +++ /dev/null @@ -1,37 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -import flet as ft -import flet_charts as fch - - -def main(page: ft.Page): - # Fixing random state for reproducibility - np.random.seed(19680801) - - dt = 0.01 - t = np.arange(0, 30, dt) - nse1 = np.random.randn(len(t)) # white noise 1 - nse2 = np.random.randn(len(t)) # white noise 2 - - # Two signals with a coherent part at 10Hz and a random part - s1 = np.sin(2 * np.pi * 10 * t) + nse1 - s2 = np.sin(2 * np.pi * 10 * t) + nse2 - - fig, axs = plt.subplots(2, 1) - axs[0].plot(t, s1, t, s2) - axs[0].set_xlim(0, 2) - axs[0].set_xlabel("time") - axs[0].set_ylabel("s1 and s2") - axs[0].grid(True) - - cxy, f = axs[1].cohere(s1, s2, 256, 1.0 / dt) - axs[1].set_ylabel("coherence") - - fig.tight_layout() - - page.add(fch.MatplotlibChartWithToolbar(figure=fig, expand=True)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/toolbar/main.py b/sdk/python/examples/controls/charts/matplotlib_chart/toolbar/main.py new file mode 100644 index 0000000000..b27ca768fd --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/toolbar/main.py @@ -0,0 +1,42 @@ +import matplotlib.pyplot as plt +import numpy as np + +import flet as ft +import flet_charts as fch + + +def main(page: ft.Page): + # Fixing random state for reproducibility + np.random.seed(19680801) + + dt = 0.01 + t = np.arange(0, 30, dt) + nse1 = np.random.randn(len(t)) # white noise 1 + nse2 = np.random.randn(len(t)) # white noise 2 + + # Two signals with a coherent part at 10Hz and a random part + s1 = np.sin(2 * np.pi * 10 * t) + nse1 + s2 = np.sin(2 * np.pi * 10 * t) + nse2 + + fig, axs = plt.subplots(2, 1) + axs[0].plot(t, s1, t, s2) + axs[0].set_xlim(0, 2) + axs[0].set_xlabel("time") + axs[0].set_ylabel("s1 and s2") + axs[0].grid(True) + + cxy, f = axs[1].cohere(s1, s2, 256, 1.0 / dt) + axs[1].set_ylabel("coherence") + + fig.tight_layout() + + page.add( + ft.SafeArea( + expand=True, + content=fch.MatplotlibChartWithToolbar(figure=fig, expand=True), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/matplotlib_chart/toolbar/pyproject.toml b/sdk/python/examples/controls/charts/matplotlib_chart/toolbar/pyproject.toml new file mode 100644 index 0000000000..d2ba114e9e --- /dev/null +++ b/sdk/python/examples/controls/charts/matplotlib_chart/toolbar/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-matplotlib-chart-toolbar" +version = "1.0.0" +description = "Matplotlib chart with built-in toolbar for panning, zooming, and data exploration." +requires-python = ">=3.10" +keywords = ["charts", "matplotlib-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts", "matplotlib", "numpy"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/MatplotlibChart"] + +[tool.flet.metadata] +title = "Toolbar" +controls = ["SafeArea", "MatplotlibChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization", "interactive toolbar", "save to file"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/pie_chart/__init__.py b/sdk/python/examples/controls/charts/pie_chart/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/charts/pie_chart/example_1.py b/sdk/python/examples/controls/charts/pie_chart/example_1.py deleted file mode 100644 index 8f84c472b0..0000000000 --- a/sdk/python/examples/controls/charts/pie_chart/example_1.py +++ /dev/null @@ -1,54 +0,0 @@ -import flet_charts as fch - -import flet as ft - - -def main(page: ft.Page): - normal_border = ft.BorderSide(0, ft.Colors.with_opacity(0, ft.Colors.WHITE)) - hovered_border = ft.BorderSide(6, ft.Colors.SECONDARY) - - def on_chart_event(e: fch.PieChartEvent): - for idx, section in enumerate(chart.sections): - section.border_side = ( - hovered_border if idx == e.section_index else normal_border - ) - chart.update() - - chart = fch.PieChart( - sections_space=1, - center_space_radius=0, - on_event=on_chart_event, - expand=True, - sections=[ - fch.PieChartSection( - value=25, - color=ft.Colors.BLUE, - radius=80, - border_side=normal_border, - ), - fch.PieChartSection( - value=25, - color=ft.Colors.YELLOW, - radius=65, - border_side=normal_border, - ), - fch.PieChartSection( - value=25, - color=ft.Colors.PINK, - radius=60, - border_side=normal_border, - ), - fch.PieChartSection( - value=25, - color=ft.Colors.GREEN, - radius=70, - border_side=normal_border, - ), - ], - ) - - page.add(chart) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/pie_chart/example_1/main.py b/sdk/python/examples/controls/charts/pie_chart/example_1/main.py new file mode 100644 index 0000000000..dd909aa49b --- /dev/null +++ b/sdk/python/examples/controls/charts/pie_chart/example_1/main.py @@ -0,0 +1,53 @@ +import flet as ft +import flet_charts as fch + + +def main(page: ft.Page): + normal_border = ft.BorderSide(0, ft.Colors.with_opacity(0, ft.Colors.WHITE)) + hovered_border = ft.BorderSide(6, ft.Colors.SECONDARY) + + def on_chart_event(e: fch.PieChartEvent): + for idx, section in enumerate(chart.sections): + section.border_side = ( + hovered_border if idx == e.section_index else normal_border + ) + chart.update() + + chart = fch.PieChart( + sections_space=1, + center_space_radius=0, + on_event=on_chart_event, + expand=True, + sections=[ + fch.PieChartSection( + value=25, + color=ft.Colors.BLUE, + radius=80, + border_side=normal_border, + ), + fch.PieChartSection( + value=25, + color=ft.Colors.YELLOW, + radius=65, + border_side=normal_border, + ), + fch.PieChartSection( + value=25, + color=ft.Colors.PINK, + radius=60, + border_side=normal_border, + ), + fch.PieChartSection( + value=25, + color=ft.Colors.GREEN, + radius=70, + border_side=normal_border, + ), + ], + ) + + page.add(ft.SafeArea(content=chart)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/pie_chart/example_1/pyproject.toml b/sdk/python/examples/controls/charts/pie_chart/example_1/pyproject.toml new file mode 100644 index 0000000000..f47f2d8d92 --- /dev/null +++ b/sdk/python/examples/controls/charts/pie_chart/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-pie-chart-example-1" +version = "1.0.0" +description = "Pie chart with hover-sensitive section borders for interactive emphasis." +requires-python = ">=3.10" +keywords = ["charts", "pie-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/PieChart"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "PieChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/pie_chart/example_2.py b/sdk/python/examples/controls/charts/pie_chart/example_2.py deleted file mode 100644 index 97701fb3ac..0000000000 --- a/sdk/python/examples/controls/charts/pie_chart/example_2.py +++ /dev/null @@ -1,69 +0,0 @@ -import flet as ft -import flet_charts as fch - -NORMAL_RADIUS = 50 -HOVER_RADIUS = 60 -NORMAL_TITLE_STYLE = ft.TextStyle( - size=16, color=ft.Colors.WHITE, weight=ft.FontWeight.BOLD -) -HOVER_TITLE_STYLE = ft.TextStyle( - size=22, - color=ft.Colors.WHITE, - weight=ft.FontWeight.BOLD, - shadow=ft.BoxShadow(blur_radius=2, color=ft.Colors.BLACK_54), -) - - -def main(page: ft.Page): - def on_chart_event(e: fch.PieChartEvent): - for idx, section in enumerate(chart.sections): - if idx == e.section_index: - section.radius = HOVER_RADIUS - section.title_style = HOVER_TITLE_STYLE - else: - section.radius = NORMAL_RADIUS - section.title_style = NORMAL_TITLE_STYLE - chart.update() - - chart = fch.PieChart( - expand=True, - sections_space=0, - center_space_radius=40, - on_event=on_chart_event, - sections=[ - fch.PieChartSection( - value=40, - title="40%", - title_style=NORMAL_TITLE_STYLE, - color=ft.Colors.BLUE, - radius=NORMAL_RADIUS, - ), - fch.PieChartSection( - value=30, - title="30%", - title_style=NORMAL_TITLE_STYLE, - color=ft.Colors.YELLOW, - radius=NORMAL_RADIUS, - ), - fch.PieChartSection( - value=15, - title="15%", - title_style=NORMAL_TITLE_STYLE, - color=ft.Colors.PURPLE, - radius=NORMAL_RADIUS, - ), - fch.PieChartSection( - value=15, - title="15%", - title_style=NORMAL_TITLE_STYLE, - color=ft.Colors.GREEN, - radius=NORMAL_RADIUS, - ), - ], - ) - - page.add(chart) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/pie_chart/example_2/main.py b/sdk/python/examples/controls/charts/pie_chart/example_2/main.py new file mode 100644 index 0000000000..fc58e9be39 --- /dev/null +++ b/sdk/python/examples/controls/charts/pie_chart/example_2/main.py @@ -0,0 +1,69 @@ +import flet as ft +import flet_charts as fch + +NORMAL_RADIUS = 50 +HOVER_RADIUS = 60 +NORMAL_TITLE_STYLE = ft.TextStyle( + size=16, color=ft.Colors.WHITE, weight=ft.FontWeight.BOLD +) +HOVER_TITLE_STYLE = ft.TextStyle( + size=22, + color=ft.Colors.WHITE, + weight=ft.FontWeight.BOLD, + shadow=ft.BoxShadow(blur_radius=2, color=ft.Colors.BLACK_54), +) + + +def main(page: ft.Page): + def on_chart_event(e: fch.PieChartEvent): + for idx, section in enumerate(chart.sections): + if idx == e.section_index: + section.radius = HOVER_RADIUS + section.title_style = HOVER_TITLE_STYLE + else: + section.radius = NORMAL_RADIUS + section.title_style = NORMAL_TITLE_STYLE + chart.update() + + chart = fch.PieChart( + expand=True, + sections_space=0, + center_space_radius=40, + on_event=on_chart_event, + sections=[ + fch.PieChartSection( + value=40, + title="40%", + title_style=NORMAL_TITLE_STYLE, + color=ft.Colors.BLUE, + radius=NORMAL_RADIUS, + ), + fch.PieChartSection( + value=30, + title="30%", + title_style=NORMAL_TITLE_STYLE, + color=ft.Colors.YELLOW, + radius=NORMAL_RADIUS, + ), + fch.PieChartSection( + value=15, + title="15%", + title_style=NORMAL_TITLE_STYLE, + color=ft.Colors.PURPLE, + radius=NORMAL_RADIUS, + ), + fch.PieChartSection( + value=15, + title="15%", + title_style=NORMAL_TITLE_STYLE, + color=ft.Colors.GREEN, + radius=NORMAL_RADIUS, + ), + ], + ) + + page.add(ft.SafeArea(content=chart)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/pie_chart/example_2/pyproject.toml b/sdk/python/examples/controls/charts/pie_chart/example_2/pyproject.toml new file mode 100644 index 0000000000..caa7187d66 --- /dev/null +++ b/sdk/python/examples/controls/charts/pie_chart/example_2/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-pie-chart-example-2" +version = "1.0.0" +description = "Pie chart with interactive section radius and title styling on hover." +requires-python = ">=3.10" +keywords = ["charts", "pie-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/PieChart"] + +[tool.flet.metadata] +title = "Example 2" +controls = ["SafeArea", "PieChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/pie_chart/example_3.py b/sdk/python/examples/controls/charts/pie_chart/example_3.py deleted file mode 100644 index 38d649e74a..0000000000 --- a/sdk/python/examples/controls/charts/pie_chart/example_3.py +++ /dev/null @@ -1,91 +0,0 @@ -import flet as ft -import flet_charts as fch - -NORMAL_RADIUS = 100 -HOVER_RADIUS = 110 -NORMAL_TITLE_STYLE = ft.TextStyle( - size=12, color=ft.Colors.WHITE, weight=ft.FontWeight.BOLD -) -HOVER_TITLE_STYLE = ft.TextStyle( - size=16, - color=ft.Colors.WHITE, - weight=ft.FontWeight.BOLD, - shadow=ft.BoxShadow(blur_radius=2, color=ft.Colors.BLACK_54), -) -NORMAL_BADGE_SIZE = 40 -HOVER_BADGE_SIZE = 50 - - -class SectionBadge(ft.Container): - def __init__(self, icon: ft.IconData, size: int = NORMAL_BADGE_SIZE): - super().__init__( - content=ft.Icon(icon), - width=size, - height=size, - border=ft.Border.all(1, ft.Colors.BROWN), - border_radius=size / 2, - bgcolor=ft.Colors.WHITE, - ) - - -def main(page: ft.Page): - def on_chart_event(e: fch.PieChartEvent): - for idx, section in enumerate(chart.sections): - if idx == e.section_index: - section.radius = HOVER_RADIUS - section.title_style = HOVER_TITLE_STYLE - else: - section.radius = NORMAL_RADIUS - section.title_style = NORMAL_TITLE_STYLE - chart.update() - - chart = fch.PieChart( - sections_space=0, - center_space_radius=0, - on_event=on_chart_event, - expand=True, - sections=[ - fch.PieChartSection( - value=40, - title="40%", - title_style=NORMAL_TITLE_STYLE, - color=ft.Colors.BLUE, - radius=NORMAL_RADIUS, - badge=SectionBadge(ft.Icons.AC_UNIT), - badge_position=0.98, - ), - fch.PieChartSection( - value=30, - title="30%", - title_style=NORMAL_TITLE_STYLE, - color=ft.Colors.YELLOW, - radius=NORMAL_RADIUS, - badge=SectionBadge(ft.Icons.ACCESS_ALARM), - badge_position=0.98, - ), - fch.PieChartSection( - value=15, - title="15%", - title_style=NORMAL_TITLE_STYLE, - color=ft.Colors.PURPLE, - radius=NORMAL_RADIUS, - badge=SectionBadge(ft.Icons.APPLE), - badge_position=0.98, - ), - fch.PieChartSection( - value=15, - title="15%", - title_style=NORMAL_TITLE_STYLE, - color=ft.Colors.GREEN, - radius=NORMAL_RADIUS, - badge=SectionBadge(ft.Icons.PEDAL_BIKE), - badge_position=0.98, - ), - ], - ) - - page.add(chart) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/pie_chart/example_3/main.py b/sdk/python/examples/controls/charts/pie_chart/example_3/main.py new file mode 100644 index 0000000000..6f4c0b204c --- /dev/null +++ b/sdk/python/examples/controls/charts/pie_chart/example_3/main.py @@ -0,0 +1,91 @@ +import flet as ft +import flet_charts as fch + +NORMAL_RADIUS = 100 +HOVER_RADIUS = 110 +NORMAL_TITLE_STYLE = ft.TextStyle( + size=12, color=ft.Colors.WHITE, weight=ft.FontWeight.BOLD +) +HOVER_TITLE_STYLE = ft.TextStyle( + size=16, + color=ft.Colors.WHITE, + weight=ft.FontWeight.BOLD, + shadow=ft.BoxShadow(blur_radius=2, color=ft.Colors.BLACK_54), +) +NORMAL_BADGE_SIZE = 40 +HOVER_BADGE_SIZE = 50 + + +class SectionBadge(ft.Container): + def __init__(self, icon: ft.IconData, size: int = NORMAL_BADGE_SIZE): + super().__init__( + content=ft.Icon(icon), + width=size, + height=size, + border=ft.Border.all(1, ft.Colors.BROWN), + border_radius=size / 2, + bgcolor=ft.Colors.WHITE, + ) + + +def main(page: ft.Page): + def on_chart_event(e: fch.PieChartEvent): + for idx, section in enumerate(chart.sections): + if idx == e.section_index: + section.radius = HOVER_RADIUS + section.title_style = HOVER_TITLE_STYLE + else: + section.radius = NORMAL_RADIUS + section.title_style = NORMAL_TITLE_STYLE + chart.update() + + chart = fch.PieChart( + sections_space=0, + center_space_radius=0, + on_event=on_chart_event, + expand=True, + sections=[ + fch.PieChartSection( + value=40, + title="40%", + title_style=NORMAL_TITLE_STYLE, + color=ft.Colors.BLUE, + radius=NORMAL_RADIUS, + badge=SectionBadge(ft.Icons.AC_UNIT), + badge_position=0.98, + ), + fch.PieChartSection( + value=30, + title="30%", + title_style=NORMAL_TITLE_STYLE, + color=ft.Colors.YELLOW, + radius=NORMAL_RADIUS, + badge=SectionBadge(ft.Icons.ACCESS_ALARM), + badge_position=0.98, + ), + fch.PieChartSection( + value=15, + title="15%", + title_style=NORMAL_TITLE_STYLE, + color=ft.Colors.PURPLE, + radius=NORMAL_RADIUS, + badge=SectionBadge(ft.Icons.APPLE), + badge_position=0.98, + ), + fch.PieChartSection( + value=15, + title="15%", + title_style=NORMAL_TITLE_STYLE, + color=ft.Colors.GREEN, + radius=NORMAL_RADIUS, + badge=SectionBadge(ft.Icons.PEDAL_BIKE), + badge_position=0.98, + ), + ], + ) + + page.add(ft.SafeArea(content=chart)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/pie_chart/example_3/pyproject.toml b/sdk/python/examples/controls/charts/pie_chart/example_3/pyproject.toml new file mode 100644 index 0000000000..7501160de0 --- /dev/null +++ b/sdk/python/examples/controls/charts/pie_chart/example_3/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-pie-chart-example-3" +version = "1.0.0" +description = "Pie chart with icon badges and animated section highlighting based on pointer events." +requires-python = ">=3.10" +keywords = ["charts", "pie-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/PieChart"] + +[tool.flet.metadata] +title = "Example 3" +controls = ["SafeArea", "PieChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/plotly_chart/__init__.py b/sdk/python/examples/controls/charts/plotly_chart/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_1.py b/sdk/python/examples/controls/charts/plotly_chart/example_1.py deleted file mode 100644 index 1ac3d9ad1e..0000000000 --- a/sdk/python/examples/controls/charts/plotly_chart/example_1.py +++ /dev/null @@ -1,15 +0,0 @@ -import flet_charts as fch -import plotly.express as px - -import flet as ft - - -def main(page: ft.Page): - df = px.data.gapminder().query("continent=='Oceania'") - fig = px.line(df, x="year", y="lifeExp", color="country") - - page.add(fch.PlotlyChart(figure=fig, expand=True)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_1/main.py b/sdk/python/examples/controls/charts/plotly_chart/example_1/main.py new file mode 100644 index 0000000000..2354419df0 --- /dev/null +++ b/sdk/python/examples/controls/charts/plotly_chart/example_1/main.py @@ -0,0 +1,15 @@ +import plotly.express as px + +import flet as ft +import flet_charts as fch + + +def main(page: ft.Page): + df = px.data.gapminder().query("continent=='Oceania'") + fig = px.line(df, x="year", y="lifeExp", color="country") + + page.add(ft.SafeArea(content=fch.PlotlyChart(figure=fig, expand=True))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_1/pyproject.toml b/sdk/python/examples/controls/charts/plotly_chart/example_1/pyproject.toml new file mode 100644 index 0000000000..0539d01dac --- /dev/null +++ b/sdk/python/examples/controls/charts/plotly_chart/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-plotly-chart-example-1" +version = "1.0.0" +description = "Plotly line chart showing life expectancy trends by country in Oceania." +requires-python = ">=3.10" +keywords = ["charts", "plotly-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts", "kaleido", "numpy", "pandas", "plotly[express]"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/PlotlyChart"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "PlotlyChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_2.py b/sdk/python/examples/controls/charts/plotly_chart/example_2.py deleted file mode 100644 index 0060b1b67b..0000000000 --- a/sdk/python/examples/controls/charts/plotly_chart/example_2.py +++ /dev/null @@ -1,23 +0,0 @@ -import flet_charts as fch -import plotly.express as px - -import flet as ft - - -def main(page: ft.Page): - df = px.data.gapminder().query("continent == 'Oceania'") - fig = px.bar( - df, - x="year", - y="pop", - hover_data=["lifeExp", "gdpPercap"], - color="country", - labels={"pop": "population of Canada"}, - height=400, - ) - - page.add(fch.PlotlyChart(figure=fig, expand=True)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_2/main.py b/sdk/python/examples/controls/charts/plotly_chart/example_2/main.py new file mode 100644 index 0000000000..663b3c9099 --- /dev/null +++ b/sdk/python/examples/controls/charts/plotly_chart/example_2/main.py @@ -0,0 +1,23 @@ +import plotly.express as px + +import flet as ft +import flet_charts as fch + + +def main(page: ft.Page): + df = px.data.gapminder().query("continent == 'Oceania'") + fig = px.bar( + df, + x="year", + y="pop", + hover_data=["lifeExp", "gdpPercap"], + color="country", + labels={"pop": "population of Canada"}, + height=400, + ) + + page.add(ft.SafeArea(content=fch.PlotlyChart(figure=fig, expand=True))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_2/pyproject.toml b/sdk/python/examples/controls/charts/plotly_chart/example_2/pyproject.toml new file mode 100644 index 0000000000..7b02618a0a --- /dev/null +++ b/sdk/python/examples/controls/charts/plotly_chart/example_2/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-plotly-chart-example-2" +version = "1.0.0" +description = "Plotly bar chart comparing population with additional hover metrics by country and year." +requires-python = ">=3.10" +keywords = ["charts", "plotly-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts", "kaleido", "numpy", "pandas", "plotly[express]"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/PlotlyChart"] + +[tool.flet.metadata] +title = "Example 2" +controls = ["SafeArea", "PlotlyChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_3.py b/sdk/python/examples/controls/charts/plotly_chart/example_3.py deleted file mode 100644 index 436bf87f24..0000000000 --- a/sdk/python/examples/controls/charts/plotly_chart/example_3.py +++ /dev/null @@ -1,17 +0,0 @@ -import flet_charts as fch -import plotly.graph_objects as go - -import flet as ft - - -def main(page: ft.Page): - labels = ["Oxygen", "Hydrogen", "Carbon_Dioxide", "Nitrogen"] - values = [4500, 2500, 1053, 500] - - fig = go.Figure(data=[go.Pie(labels=labels, values=values)]) - - page.add(fch.PlotlyChart(figure=fig, expand=True)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_3/main.py b/sdk/python/examples/controls/charts/plotly_chart/example_3/main.py new file mode 100644 index 0000000000..0f28b067fd --- /dev/null +++ b/sdk/python/examples/controls/charts/plotly_chart/example_3/main.py @@ -0,0 +1,17 @@ +import plotly.graph_objects as go + +import flet as ft +import flet_charts as fch + + +def main(page: ft.Page): + labels = ["Oxygen", "Hydrogen", "Carbon_Dioxide", "Nitrogen"] + values = [4500, 2500, 1053, 500] + + fig = go.Figure(data=[go.Pie(labels=labels, values=values)]) + + page.add(ft.SafeArea(content=fch.PlotlyChart(figure=fig, expand=True))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_3/pyproject.toml b/sdk/python/examples/controls/charts/plotly_chart/example_3/pyproject.toml new file mode 100644 index 0000000000..4bbd7ce492 --- /dev/null +++ b/sdk/python/examples/controls/charts/plotly_chart/example_3/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-plotly-chart-example-3" +version = "1.0.0" +description = "Plotly pie chart example for categorical distribution visualization." +requires-python = ">=3.10" +keywords = ["charts", "plotly-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts", "kaleido", "numpy", "pandas", "plotly[express]"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/PlotlyChart"] + +[tool.flet.metadata] +title = "Example 3" +controls = ["SafeArea", "PlotlyChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_4.py b/sdk/python/examples/controls/charts/plotly_chart/example_4.py deleted file mode 100644 index 9fdf998721..0000000000 --- a/sdk/python/examples/controls/charts/plotly_chart/example_4.py +++ /dev/null @@ -1,59 +0,0 @@ -import flet_charts as fch -import plotly.graph_objects as go - -import flet as ft - - -def main(page: ft.Page): - x = [ - "day 1", - "day 1", - "day 1", - "day 1", - "day 1", - "day 1", - "day 2", - "day 2", - "day 2", - "day 2", - "day 2", - "day 2", - ] - - fig = go.Figure() - - fig.add_trace( - go.Box( - y=[0.2, 0.2, 0.6, 1.0, 0.5, 0.4, 0.2, 0.7, 0.9, 0.1, 0.5, 0.3], - x=x, - name="kale", - marker_color="#3D9970", - ) - ) - fig.add_trace( - go.Box( - y=[0.6, 0.7, 0.3, 0.6, 0.0, 0.5, 0.7, 0.9, 0.5, 0.8, 0.7, 0.2], - x=x, - name="radishes", - marker_color="#FF4136", - ) - ) - fig.add_trace( - go.Box( - y=[0.1, 0.3, 0.1, 0.9, 0.6, 0.6, 0.9, 1.0, 0.3, 0.6, 0.8, 0.5], - x=x, - name="carrots", - marker_color="#FF851B", - ) - ) - - fig.update_layout( - yaxis_title="normalized moisture", - boxmode="group", # group together boxes of the different traces - ) - - page.add(fch.PlotlyChart(figure=fig, expand=True)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_4/main.py b/sdk/python/examples/controls/charts/plotly_chart/example_4/main.py new file mode 100644 index 0000000000..9f50698e45 --- /dev/null +++ b/sdk/python/examples/controls/charts/plotly_chart/example_4/main.py @@ -0,0 +1,59 @@ +import plotly.graph_objects as go + +import flet as ft +import flet_charts as fch + + +def main(page: ft.Page): + x = [ + "day 1", + "day 1", + "day 1", + "day 1", + "day 1", + "day 1", + "day 2", + "day 2", + "day 2", + "day 2", + "day 2", + "day 2", + ] + + fig = go.Figure() + + fig.add_trace( + go.Box( + y=[0.2, 0.2, 0.6, 1.0, 0.5, 0.4, 0.2, 0.7, 0.9, 0.1, 0.5, 0.3], + x=x, + name="kale", + marker_color="#3D9970", + ) + ) + fig.add_trace( + go.Box( + y=[0.6, 0.7, 0.3, 0.6, 0.0, 0.5, 0.7, 0.9, 0.5, 0.8, 0.7, 0.2], + x=x, + name="radishes", + marker_color="#FF4136", + ) + ) + fig.add_trace( + go.Box( + y=[0.1, 0.3, 0.1, 0.9, 0.6, 0.6, 0.9, 1.0, 0.3, 0.6, 0.8, 0.5], + x=x, + name="carrots", + marker_color="#FF851B", + ) + ) + + fig.update_layout( + yaxis_title="normalized moisture", + boxmode="group", # group together boxes of the different traces + ) + + page.add(ft.SafeArea(content=fch.PlotlyChart(figure=fig, expand=True))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/plotly_chart/example_4/pyproject.toml b/sdk/python/examples/controls/charts/plotly_chart/example_4/pyproject.toml new file mode 100644 index 0000000000..e8b2498577 --- /dev/null +++ b/sdk/python/examples/controls/charts/plotly_chart/example_4/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-plotly-chart-example-4" +version = "1.0.0" +description = "Plotly grouped box plot comparing moisture distributions across categories." +requires-python = ">=3.10" +keywords = ["charts", "plotly-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts", "kaleido", "numpy", "pandas", "plotly[express]"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/PlotlyChart"] + +[tool.flet.metadata] +title = "Example 4" +controls = ["SafeArea", "PlotlyChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/radar_chart/__init__.py b/sdk/python/examples/controls/charts/radar_chart/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/charts/radar_chart/example_1.py b/sdk/python/examples/controls/charts/radar_chart/example_1.py deleted file mode 100644 index 454c15b844..0000000000 --- a/sdk/python/examples/controls/charts/radar_chart/example_1.py +++ /dev/null @@ -1,61 +0,0 @@ -import flet as ft -import flet_charts as fch - - -def main(page: ft.Page): - page.title = "Radar chart" - page.padding = 20 - # page.vertical_alignment = page.horizontal_alignment = "center" - page.theme_mode = ft.ThemeMode.LIGHT - - categories = ["macOS", "Linux", "Windows"] - - page.add( - fch.RadarChart( - expand=True, - titles=[fch.RadarChartTitle(text=label) for label in categories], - center_min_value=True, - tick_count=4, - ticks_text_style=ft.TextStyle(size=20, color=ft.Colors.ON_SURFACE), - title_text_style=ft.TextStyle( - size=24, weight=ft.FontWeight.BOLD, color=ft.Colors.ON_SURFACE - ), - on_event=lambda e: print(e.type), - data_sets=[ - fch.RadarDataSet( - fill_color=ft.Colors.with_opacity(0.2, ft.Colors.DEEP_PURPLE), - border_color=ft.Colors.DEEP_PURPLE, - entry_radius=4, - entries=[ - fch.RadarDataSetEntry(300), - fch.RadarDataSetEntry(50), - fch.RadarDataSetEntry(250), - ], - ), - fch.RadarDataSet( - fill_color=ft.Colors.with_opacity(0.15, ft.Colors.PINK), - border_color=ft.Colors.PINK, - entry_radius=4, - entries=[ - fch.RadarDataSetEntry(250), - fch.RadarDataSetEntry(100), - fch.RadarDataSetEntry(200), - ], - ), - fch.RadarDataSet( - fill_color=ft.Colors.with_opacity(0.12, ft.Colors.CYAN), - border_color=ft.Colors.CYAN, - entry_radius=4, - entries=[ - fch.RadarDataSetEntry(200), - fch.RadarDataSetEntry(150), - fch.RadarDataSetEntry(50), - ], - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/radar_chart/example_1/main.py b/sdk/python/examples/controls/charts/radar_chart/example_1/main.py new file mode 100644 index 0000000000..4cefb9815a --- /dev/null +++ b/sdk/python/examples/controls/charts/radar_chart/example_1/main.py @@ -0,0 +1,63 @@ +import flet as ft +import flet_charts as fch + + +def main(page: ft.Page): + page.title = "Radar chart" + page.padding = 20 + # page.vertical_alignment = page.horizontal_alignment = "center" + page.theme_mode = ft.ThemeMode.LIGHT + + categories = ["macOS", "Linux", "Windows"] + + page.add( + ft.SafeArea( + content=fch.RadarChart( + expand=True, + titles=[fch.RadarChartTitle(text=label) for label in categories], + center_min_value=True, + tick_count=4, + ticks_text_style=ft.TextStyle(size=20, color=ft.Colors.ON_SURFACE), + title_text_style=ft.TextStyle( + size=24, weight=ft.FontWeight.BOLD, color=ft.Colors.ON_SURFACE + ), + on_event=lambda e: print(e.type), + data_sets=[ + fch.RadarDataSet( + fill_color=ft.Colors.with_opacity(0.2, ft.Colors.DEEP_PURPLE), + border_color=ft.Colors.DEEP_PURPLE, + entry_radius=4, + entries=[ + fch.RadarDataSetEntry(300), + fch.RadarDataSetEntry(50), + fch.RadarDataSetEntry(250), + ], + ), + fch.RadarDataSet( + fill_color=ft.Colors.with_opacity(0.15, ft.Colors.PINK), + border_color=ft.Colors.PINK, + entry_radius=4, + entries=[ + fch.RadarDataSetEntry(250), + fch.RadarDataSetEntry(100), + fch.RadarDataSetEntry(200), + ], + ), + fch.RadarDataSet( + fill_color=ft.Colors.with_opacity(0.12, ft.Colors.CYAN), + border_color=ft.Colors.CYAN, + entry_radius=4, + entries=[ + fch.RadarDataSetEntry(200), + fch.RadarDataSetEntry(150), + fch.RadarDataSetEntry(50), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/radar_chart/example_1/pyproject.toml b/sdk/python/examples/controls/charts/radar_chart/example_1/pyproject.toml new file mode 100644 index 0000000000..cafd659afa --- /dev/null +++ b/sdk/python/examples/controls/charts/radar_chart/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-radar-chart-example-1" +version = "1.0.0" +description = "Radar chart comparing platform values across multiple datasets with styled titles and ticks." +requires-python = ">=3.10" +keywords = ["charts", "radar-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/RadarChart"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "RadarChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/charts/scatter_chart/__init__.py b/sdk/python/examples/controls/charts/scatter_chart/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/charts/scatter_chart/example_1.py b/sdk/python/examples/controls/charts/scatter_chart/example_1.py deleted file mode 100644 index e3ed0c76cc..0000000000 --- a/sdk/python/examples/controls/charts/scatter_chart/example_1.py +++ /dev/null @@ -1,161 +0,0 @@ -import random - -import flet as ft -import flet_charts as fch - - -class MySpot(fch.ScatterChartSpot): - def __init__( - self, - x: float, - y: float, - radius: float = 8.0, - color: ft.Colors = None, - show_tooltip: bool = False, - ): - super().__init__( - x=x, - y=y, - radius=radius, - color=color, - show_tooltip=show_tooltip, - selected=y == 43, - ) - - -flutter_logo_spots = [ - MySpot(20, 14.5), - MySpot(20, 14.5), - MySpot(22, 16.5), - MySpot(24, 18.5), - MySpot(22, 12.5), - MySpot(24, 14.5), - MySpot(26, 16.5), - MySpot(24, 10.5), - MySpot(26, 12.5), - MySpot(28, 14.5), - MySpot(26, 8.5), - MySpot(28, 10.5), - MySpot(30, 12.5), - MySpot(28, 6.5), - MySpot(30, 8.5), - MySpot(32, 10.5), - MySpot(30, 4.5), - MySpot(32, 6.5), - MySpot(34, 8.5), - MySpot(34, 4.5), - MySpot(36, 6.5), - MySpot(38, 4.5), - # section 2 - MySpot(20, 14.5), - MySpot(22, 12.5), - MySpot(24, 10.5), - MySpot(22, 16.5), - MySpot(24, 14.5), - MySpot(26, 12.5), - MySpot(24, 18.5), - MySpot(26, 16.5), - MySpot(28, 14.5), - MySpot(26, 20.5), - MySpot(28, 18.5), - MySpot(30, 16.5), - MySpot(28, 22.5), - MySpot(30, 20.5), - MySpot(32, 18.5), - MySpot(30, 24.5), - MySpot(32, 22.5), - MySpot(34, 20.5), - MySpot(34, 24.5), - MySpot(36, 22.5), - MySpot(38, 24.5), - # section 3 - MySpot(10, 25), - MySpot(12, 23), - MySpot(14, 21), - MySpot(12, 27), - MySpot(14, 25), - MySpot(16, 23), - MySpot(14, 29), - MySpot(16, 27), - MySpot(18, 25), - MySpot(16, 31), - MySpot(18, 29), - MySpot(20, 27), - MySpot(18, 33), - MySpot(20, 31), - MySpot(22, 29), - MySpot(20, 35), - MySpot(22, 33), - MySpot(24, 31), - MySpot(22, 37), - MySpot(24, 35), - MySpot(26, 33), - MySpot(24, 39), - MySpot(26, 37), - MySpot(28, 35), - MySpot(26, 41), - MySpot(28, 39), - MySpot(30, 37), - MySpot(28, 43), - MySpot(30, 41), - MySpot(32, 39), - MySpot(30, 45), - MySpot(32, 43), - MySpot(34, 41), - MySpot(34, 45), - MySpot(36, 43), - MySpot(38, 45), -] - - -def get_random_spots(): - """Generates random spots for the scatter chart.""" - return [ - MySpot( - x=random.uniform(4, 50), - y=random.uniform(4, 50), - radius=random.uniform(4, 20), - ) - for _ in range(len(flutter_logo_spots)) - ] - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_event(e: fch.ScatterChartEvent): - if e.type == fch.ChartEventType.TAP_DOWN: - e.control.spots = ( - flutter_logo_spots - if (e.control.spots != flutter_logo_spots) - else get_random_spots() - ) - - page.add( - ft.Text( - "Tap on the chart to toggle between random spots and Flutter logo spots." - ), - fch.ScatterChart( - expand=True, - aspect_ratio=1.0, - min_x=0.0, - max_x=50.0, - min_y=0.0, - max_y=50.0, - left_axis=fch.ChartAxis(show_labels=False), - right_axis=fch.ChartAxis(show_labels=False), - top_axis=fch.ChartAxis(show_labels=False), - bottom_axis=fch.ChartAxis(show_labels=False), - show_tooltips_for_selected_spots_only=False, - on_event=handle_event, - animation=ft.Animation( - duration=ft.Duration(milliseconds=600), - curve=ft.AnimationCurve.FAST_OUT_SLOWIN, - ), - spots=flutter_logo_spots, - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/charts/scatter_chart/example_1/main.py b/sdk/python/examples/controls/charts/scatter_chart/example_1/main.py new file mode 100644 index 0000000000..8b6faa3d45 --- /dev/null +++ b/sdk/python/examples/controls/charts/scatter_chart/example_1/main.py @@ -0,0 +1,168 @@ +import random + +import flet as ft +import flet_charts as fch + + +class MySpot(fch.ScatterChartSpot): + def __init__( + self, + x: float, + y: float, + radius: float = 8.0, + color: ft.Colors = None, + show_tooltip: bool = False, + ): + super().__init__( + x=x, + y=y, + radius=radius, + color=color, + show_tooltip=show_tooltip, + selected=y == 43, + ) + + +flutter_logo_spots = [ + MySpot(20, 14.5), + MySpot(20, 14.5), + MySpot(22, 16.5), + MySpot(24, 18.5), + MySpot(22, 12.5), + MySpot(24, 14.5), + MySpot(26, 16.5), + MySpot(24, 10.5), + MySpot(26, 12.5), + MySpot(28, 14.5), + MySpot(26, 8.5), + MySpot(28, 10.5), + MySpot(30, 12.5), + MySpot(28, 6.5), + MySpot(30, 8.5), + MySpot(32, 10.5), + MySpot(30, 4.5), + MySpot(32, 6.5), + MySpot(34, 8.5), + MySpot(34, 4.5), + MySpot(36, 6.5), + MySpot(38, 4.5), + # section 2 + MySpot(20, 14.5), + MySpot(22, 12.5), + MySpot(24, 10.5), + MySpot(22, 16.5), + MySpot(24, 14.5), + MySpot(26, 12.5), + MySpot(24, 18.5), + MySpot(26, 16.5), + MySpot(28, 14.5), + MySpot(26, 20.5), + MySpot(28, 18.5), + MySpot(30, 16.5), + MySpot(28, 22.5), + MySpot(30, 20.5), + MySpot(32, 18.5), + MySpot(30, 24.5), + MySpot(32, 22.5), + MySpot(34, 20.5), + MySpot(34, 24.5), + MySpot(36, 22.5), + MySpot(38, 24.5), + # section 3 + MySpot(10, 25), + MySpot(12, 23), + MySpot(14, 21), + MySpot(12, 27), + MySpot(14, 25), + MySpot(16, 23), + MySpot(14, 29), + MySpot(16, 27), + MySpot(18, 25), + MySpot(16, 31), + MySpot(18, 29), + MySpot(20, 27), + MySpot(18, 33), + MySpot(20, 31), + MySpot(22, 29), + MySpot(20, 35), + MySpot(22, 33), + MySpot(24, 31), + MySpot(22, 37), + MySpot(24, 35), + MySpot(26, 33), + MySpot(24, 39), + MySpot(26, 37), + MySpot(28, 35), + MySpot(26, 41), + MySpot(28, 39), + MySpot(30, 37), + MySpot(28, 43), + MySpot(30, 41), + MySpot(32, 39), + MySpot(30, 45), + MySpot(32, 43), + MySpot(34, 41), + MySpot(34, 45), + MySpot(36, 43), + MySpot(38, 45), +] + + +def get_random_spots(): + """Generates random spots for the scatter chart.""" + return [ + MySpot( + x=random.uniform(4, 50), + y=random.uniform(4, 50), + radius=random.uniform(4, 20), + ) + for _ in range(len(flutter_logo_spots)) + ] + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def handle_event(e: fch.ScatterChartEvent): + if e.type == fch.ChartEventType.TAP_DOWN: + e.control.spots = ( + flutter_logo_spots + if (e.control.spots != flutter_logo_spots) + else get_random_spots() + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Tap on the chart to toggle between random spots and Flutter " + "logo spots." + ), + fch.ScatterChart( + expand=True, + aspect_ratio=1.0, + min_x=0.0, + max_x=50.0, + min_y=0.0, + max_y=50.0, + left_axis=fch.ChartAxis(show_labels=False), + right_axis=fch.ChartAxis(show_labels=False), + top_axis=fch.ChartAxis(show_labels=False), + bottom_axis=fch.ChartAxis(show_labels=False), + show_tooltips_for_selected_spots_only=False, + on_event=handle_event, + animation=ft.Animation( + duration=ft.Duration(milliseconds=600), + curve=ft.AnimationCurve.FAST_OUT_SLOWIN, + ), + spots=flutter_logo_spots, + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/charts/scatter_chart/example_1/pyproject.toml b/sdk/python/examples/controls/charts/scatter_chart/example_1/pyproject.toml new file mode 100644 index 0000000000..a3478064b4 --- /dev/null +++ b/sdk/python/examples/controls/charts/scatter_chart/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "charts-scatter-chart-example-1" +version = "1.0.0" +description = "Scatter chart that toggles between random spots and a Flutter-logo point pattern on tap." +requires-python = ">=3.10" +keywords = ["charts", "scatter-chart", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-charts"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Charts/ScatterChart"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "ScatterChart"] +layout_pattern = "single-chart" +complexity = "basic" +features = ["chart visualization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/checkbox/__init__.py b/sdk/python/examples/controls/checkbox/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/checkbox/basic.py b/sdk/python/examples/controls/checkbox/basic.py deleted file mode 100644 index e96c6ce105..0000000000 --- a/sdk/python/examples/controls/checkbox/basic.py +++ /dev/null @@ -1,29 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_button_click(e: ft.Event[ft.Button]): - message.value = ( - f"Checkboxes values are: {c1.value}, {c2.value}, {c3.value}, " - f"{c4.value}, {c5.value}." - ) - page.update() - - page.add( - c1 := ft.Checkbox(label="Unchecked by default checkbox", value=False), - c2 := ft.Checkbox( - label="Undefined by default tristate checkbox", tristate=True - ), - c3 := ft.Checkbox(label="Checked by default checkbox", value=True), - c4 := ft.Checkbox(label="Disabled checkbox", disabled=True), - c5 := ft.Checkbox( - label="Checkbox with LEFT label_position", - label_position=ft.LabelPosition.LEFT, - ), - ft.Button(content="Submit", on_click=handle_button_click), - message := ft.Text(), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/checkbox/basic/main.py b/sdk/python/examples/controls/checkbox/basic/main.py new file mode 100644 index 0000000000..39d2161d59 --- /dev/null +++ b/sdk/python/examples/controls/checkbox/basic/main.py @@ -0,0 +1,36 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_button_click(e: ft.Event[ft.Button]): + message.value = ( + f"Checkboxes values are: {c1.value}, {c2.value}, {c3.value}, " + f"{c4.value}, {c5.value}." + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + c1 := ft.Checkbox( + label="Unchecked by default checkbox", value=False + ), + c2 := ft.Checkbox( + label="Undefined by default tristate checkbox", tristate=True + ), + c3 := ft.Checkbox(label="Checked by default checkbox", value=True), + c4 := ft.Checkbox(label="Disabled checkbox", disabled=True), + c5 := ft.Checkbox( + label="Checkbox with LEFT label_position", + label_position=ft.LabelPosition.LEFT, + ), + ft.Button(content="Submit", on_click=handle_button_click), + message := ft.Text(), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/checkbox/basic/pyproject.toml b/sdk/python/examples/controls/checkbox/basic/pyproject.toml new file mode 100644 index 0000000000..5c43b689e5 --- /dev/null +++ b/sdk/python/examples/controls/checkbox/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "checkbox-basic" +version = "1.0.0" +description = "Basic checkbox states with a submit action." +requires-python = ">=3.10" +keywords = ["checkbox", "material", "input", "form", "state", "tristate"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Checkbox"] + +[tool.flet.metadata] +title = "Basic checkbox" +controls = ["SafeArea", "Column", "Checkbox", "Button", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["checkbox state examples", "button click handling", "value summary output"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/checkbox/handling_events.py b/sdk/python/examples/controls/checkbox/handling_events.py deleted file mode 100644 index 22719231bc..0000000000 --- a/sdk/python/examples/controls/checkbox/handling_events.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_checkbox_change(e: ft.Event[ft.Checkbox]): - page.add(ft.Text(f"Checkbox value changed to {e.control.value}")) - page.update() - - page.add( - ft.Checkbox( - label="Checkbox with 'change' event", - on_change=handle_checkbox_change, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/checkbox/handling_events/main.py b/sdk/python/examples/controls/checkbox/handling_events/main.py new file mode 100644 index 0000000000..01588ec09b --- /dev/null +++ b/sdk/python/examples/controls/checkbox/handling_events/main.py @@ -0,0 +1,26 @@ +import flet as ft + + +def main(page: ft.Page): + events = ft.Column() + + def handle_checkbox_change(e: ft.Event[ft.Checkbox]): + events.controls.append(ft.Text(f"Checkbox value changed to {e.control.value}")) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Checkbox( + label="Checkbox with 'change' event", + on_change=handle_checkbox_change, + ), + events, + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/checkbox/handling_events/pyproject.toml b/sdk/python/examples/controls/checkbox/handling_events/pyproject.toml new file mode 100644 index 0000000000..fce792665c --- /dev/null +++ b/sdk/python/examples/controls/checkbox/handling_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "checkbox-handling-events" +version = "1.0.0" +description = "Checkbox change event handling with dynamic text updates." +requires-python = ">=3.10" +keywords = ["checkbox", "material", "input", "events", "on_change", "state"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Checkbox"] + +[tool.flet.metadata] +title = "Checkbox events" +controls = ["SafeArea", "Column", "Checkbox", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["on_change handling", "event-driven UI updates", "state change log"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/checkbox/styled.py b/sdk/python/examples/controls/checkbox/styled.py deleted file mode 100644 index 951570ff67..0000000000 --- a/sdk/python/examples/controls/checkbox/styled.py +++ /dev/null @@ -1,25 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Checkbox(label="Checkbox with default style"), - ft.Checkbox( - label="Checkbox with constant fill color", - fill_color=ft.Colors.RED, - check_color=ft.Colors.YELLOW, - ), - ft.Checkbox( - label="Checkbox with dynamic fill color", - fill_color={ - ft.ControlState.HOVERED: ft.Colors.BLUE, - ft.ControlState.SELECTED: ft.Colors.GREEN, - ft.ControlState.DEFAULT: ft.Colors.RED, - }, - # border_side={ft.ControlState.HOVERED: ft.BorderSide(width=1.0)}, - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/checkbox/styled/main.py b/sdk/python/examples/controls/checkbox/styled/main.py new file mode 100644 index 0000000000..980fbc2544 --- /dev/null +++ b/sdk/python/examples/controls/checkbox/styled/main.py @@ -0,0 +1,30 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Checkbox(label="Checkbox with default style"), + ft.Checkbox( + label="Checkbox with constant fill color", + fill_color=ft.Colors.RED, + check_color=ft.Colors.YELLOW, + ), + ft.Checkbox( + label="Checkbox with dynamic fill color", + fill_color={ + ft.ControlState.HOVERED: ft.Colors.BLUE, + ft.ControlState.SELECTED: ft.Colors.GREEN, + ft.ControlState.DEFAULT: ft.Colors.RED, + }, + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/checkbox/styled/pyproject.toml b/sdk/python/examples/controls/checkbox/styled/pyproject.toml new file mode 100644 index 0000000000..ecd06ccd75 --- /dev/null +++ b/sdk/python/examples/controls/checkbox/styled/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "checkbox-styled" +version = "1.0.0" +description = "Styled checkboxes with fixed and state-based colors." +requires-python = ">=3.10" +keywords = ["checkbox", "material", "input", "styling", "colors", "controlstate"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Checkbox"] + +[tool.flet.metadata] +title = "Styled checkboxes" +controls = ["SafeArea", "Column", "Checkbox"] +layout_pattern = "form" +complexity = "basic" +features = ["static styling", "state-based fill colors", "custom check color"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/chip/__init__.py b/sdk/python/examples/controls/chip/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/chip/assist_chips.py b/sdk/python/examples/controls/chip/assist_chips.py deleted file mode 100644 index 188709c0cb..0000000000 --- a/sdk/python/examples/controls/chip/assist_chips.py +++ /dev/null @@ -1,38 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_chip1_click(e: ft.Event[ft.Chip]): - e.control.label.value = "Saved to favorites" - e.control.leading = ft.Icon(ft.Icons.FAVORITE_OUTLINED) - e.control.disabled = True - page.update() - - async def handle_chip2_click(e: ft.Event[ft.Chip]): - await page.launch_url("https://maps.google.com") - page.update() - - page.add( - ft.Row( - controls=[ - ft.Chip( - label=ft.Text("Save to favourites"), - leading=ft.Icon(ft.Icons.FAVORITE_BORDER_OUTLINED), - bgcolor=ft.Colors.GREEN_200, - disabled_color=ft.Colors.GREEN_100, - autofocus=True, - on_click=handle_chip1_click, - ), - ft.Chip( - label=ft.Text("9 min walk"), - leading=ft.Icon(ft.Icons.MAP_SHARP), - bgcolor=ft.Colors.GREEN_200, - on_click=handle_chip2_click, - ), - ] - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/chip/assist_chips/main.py b/sdk/python/examples/controls/chip/assist_chips/main.py new file mode 100644 index 0000000000..48fc91e6d6 --- /dev/null +++ b/sdk/python/examples/controls/chip/assist_chips/main.py @@ -0,0 +1,38 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_chip1_click(e: ft.Event[ft.Chip]): + e.control.label.value = "Saved to favorites" + e.control.leading = ft.Icon(ft.Icons.FAVORITE_OUTLINED) + e.control.disabled = True + + async def handle_chip2_click(e: ft.Event[ft.Chip]): + await page.launch_url("https://maps.google.com") + + page.add( + ft.SafeArea( + content=ft.Row( + controls=[ + ft.Chip( + label=ft.Text("Save to favourites"), + leading=ft.Icon(ft.Icons.FAVORITE_BORDER_OUTLINED), + bgcolor=ft.Colors.GREEN_200, + disabled_color=ft.Colors.GREEN_100, + autofocus=True, + on_click=handle_chip1_click, + ), + ft.Chip( + label=ft.Text("9 min walk"), + leading=ft.Icon(ft.Icons.MAP_SHARP), + bgcolor=ft.Colors.GREEN_200, + on_click=handle_chip2_click, + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/chip/assist_chips/pyproject.toml b/sdk/python/examples/controls/chip/assist_chips/pyproject.toml new file mode 100644 index 0000000000..3f1b5da46d --- /dev/null +++ b/sdk/python/examples/controls/chip/assist_chips/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "chip-assist-chips" +version = "1.0.0" +description = "Assist chips with click actions and dynamic state updates." +requires-python = ">=3.10" +keywords = ["chip", "assist chip", "material", "input", "actions", "async", "url launch"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Chip"] + +[tool.flet.metadata] +title = "Assist chips" +controls = ["SafeArea", "Row", "Chip", "Icon", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["click handling", "async url launch", "dynamic chip state"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/chip/filter_chips.py b/sdk/python/examples/controls/chip/filter_chips.py deleted file mode 100644 index aaf1d71a3c..0000000000 --- a/sdk/python/examples/controls/chip/filter_chips.py +++ /dev/null @@ -1,33 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_amenity_selection(e: ft.Event[ft.Chip]): - page.update() - - amenities = ["Washer / Dryer", "Ramp access", "Dogs OK", "Cats OK", "Smoke-free"] - - page.add( - ft.Row( - controls=[ - ft.Icon(ft.Icons.HOTEL_CLASS), - ft.Text("Amenities"), - ] - ), - ft.Row( - controls=[ - ft.Chip( - label=ft.Text(amenity), - bgcolor=ft.Colors.GREEN_200, - disabled_color=ft.Colors.GREEN_100, - autofocus=True, - on_select=handle_amenity_selection, - ) - for amenity in amenities - ] - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/chip/filter_chips/main.py b/sdk/python/examples/controls/chip/filter_chips/main.py new file mode 100644 index 0000000000..d2f4b4ff67 --- /dev/null +++ b/sdk/python/examples/controls/chip/filter_chips/main.py @@ -0,0 +1,39 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_amenity_selection(e: ft.Event[ft.Chip]): + print("Amenity selected:", e.control.label.value) + + amenities = ["Washer / Dryer", "Ramp access", "Dogs OK", "Cats OK", "Smoke-free"] + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + controls=[ + ft.Icon(ft.Icons.HOTEL_CLASS), + ft.Text("Amenities"), + ] + ), + ft.Row( + controls=[ + ft.Chip( + label=ft.Text(amenity), + bgcolor=ft.Colors.GREEN_200, + disabled_color=ft.Colors.GREEN_100, + autofocus=True, + on_select=handle_amenity_selection, + ) + for amenity in amenities + ] + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/chip/filter_chips/pyproject.toml b/sdk/python/examples/controls/chip/filter_chips/pyproject.toml new file mode 100644 index 0000000000..d0c2aea954 --- /dev/null +++ b/sdk/python/examples/controls/chip/filter_chips/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "chip-filter-chips" +version = "1.0.0" +description = "Filter chips for amenity selection with on_select events." +requires-python = ">=3.10" +keywords = ["chip", "filter chip", "material", "input", "selection", "filters"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Chip"] + +[tool.flet.metadata] +title = "Filter chips" +controls = ["SafeArea", "Column", "Row", "Chip", "Icon", "Text"] +layout_pattern = "filter-bar" +complexity = "basic" +features = ["selection handling", "chip toggles", "event-driven filtering UI"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/circle_avatar/user_avatars.py b/sdk/python/examples/controls/circle_avatar/user_avatars.py deleted file mode 100644 index 8841c52d23..0000000000 --- a/sdk/python/examples/controls/circle_avatar/user_avatars.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - # a "normal" avatar with background image - ft.CircleAvatar( - foreground_image_src="https://avatars.githubusercontent.com/u/5041459?s=88&v=4", - content=ft.Text("FF"), - ), - # avatar with failing foreground image and fallback text - ft.CircleAvatar( - foreground_image_src="https://avatars.githubusercontent.com/u/_5041459?s=88&v=4", - content=ft.Text("FF"), - ), - # avatar with icon, aka icon with inverse background - ft.CircleAvatar(content=ft.Icon(ft.Icons.ABC)), - # avatar with icon and custom colors - ft.CircleAvatar( - content=ft.Icon(ft.Icons.WARNING_ROUNDED), - color=ft.Colors.YELLOW_200, - bgcolor=ft.Colors.AMBER_700, - ), - # avatar with online status - ft.Stack( - width=40, - height=40, - controls=[ - ft.CircleAvatar( - foreground_image_src="https://avatars.githubusercontent.com/u/5041459?s=88&v=4" - ), - ft.Container( - content=ft.CircleAvatar(bgcolor=ft.Colors.GREEN, radius=5), - alignment=ft.Alignment.BOTTOM_LEFT, - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/circle_avatar/user_avatars/main.py b/sdk/python/examples/controls/circle_avatar/user_avatars/main.py new file mode 100644 index 0000000000..e05a253f24 --- /dev/null +++ b/sdk/python/examples/controls/circle_avatar/user_avatars/main.py @@ -0,0 +1,50 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + # a "normal" avatar with background image + ft.CircleAvatar( + foreground_image_src="https://avatars.githubusercontent.com/u/5041459?s=88&v=4", + content=ft.Text("FF"), + ), + # avatar with failing foreground image and fallback text + ft.CircleAvatar( + foreground_image_src="https://avatars.githubusercontent.com/u/_5041459?s=88&v=4", + content=ft.Text("FF"), + ), + # avatar with icon, aka icon with inverse background + ft.CircleAvatar(content=ft.Icon(ft.Icons.ABC)), + # avatar with icon and custom colors + ft.CircleAvatar( + content=ft.Icon(ft.Icons.WARNING_ROUNDED), + color=ft.Colors.YELLOW_200, + bgcolor=ft.Colors.AMBER_700, + ), + # avatar with online status + ft.Stack( + width=40, + height=40, + controls=[ + ft.CircleAvatar( + foreground_image_src="https://avatars.githubusercontent.com/u/5041459?s=88&v=4" + ), + ft.Container( + alignment=ft.Alignment.BOTTOM_LEFT, + content=ft.CircleAvatar( + bgcolor=ft.Colors.GREEN, radius=5 + ), + ), + ], + ), + ] + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/circle_avatar/user_avatars/pyproject.toml b/sdk/python/examples/controls/circle_avatar/user_avatars/pyproject.toml new file mode 100644 index 0000000000..4a35a23979 --- /dev/null +++ b/sdk/python/examples/controls/circle_avatar/user_avatars/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "circle-avatar-user-avatars" +version = "1.0.0" +description = "CircleAvatar showcase with images, icons, fallback content, and online status badge." +requires-python = ">=3.10" +keywords = ["circleavatar", "avatar", "image", "icon", "status"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/CircleAvatar"] + +[tool.flet.metadata] +title = "User avatars" +controls = ["SafeArea", "CircleAvatar", "Text", "Icon", "Stack", "Container"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["image avatar", "fallback content", "online status indicator"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/code_editor/__init__.py b/sdk/python/examples/controls/code_editor/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/code_editor/example_1.py b/sdk/python/examples/controls/code_editor/example_1.py deleted file mode 100644 index 44d0fc7648..0000000000 --- a/sdk/python/examples/controls/code_editor/example_1.py +++ /dev/null @@ -1,46 +0,0 @@ -import flet_code_editor as fce - -import flet as ft - -CODE = """import flet as ft - -def main(page: ft.Page): - counter = ft.Text("0", size=50, data=0) - - def btn_click(e): - counter.data += 1 - counter.value = str(counter.data) - counter.update() - - page.floating_action_button = ft.FloatingActionButton( - icon=ft.Icons.ADD, on_click=btn_click - ) - page.add( - ft.SafeArea( - ft.Container( - counter, - alignment=ft.Alignment.CENTER, - expand=True, - ), - expand=True, - ), - ) - -ft.run(main) -""" - - -def main(page: ft.Page): - page.add( - fce.CodeEditor( - language=fce.CodeLanguage.PYTHON, - code_theme=fce.CodeTheme.ATOM_ONE_LIGHT, - value=CODE, - expand=True, - on_change=lambda e: print("Changed:", e.data), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/code_editor/example_1/main.py b/sdk/python/examples/controls/code_editor/example_1/main.py new file mode 100644 index 0000000000..48f89f9b1b --- /dev/null +++ b/sdk/python/examples/controls/code_editor/example_1/main.py @@ -0,0 +1,49 @@ +import flet_code_editor as fce + +import flet as ft + +CODE = """import flet as ft + +def main(page: ft.Page): + counter = ft.Text("0", size=50, data=0) + + def btn_click(e): + counter.data += 1 + counter.value = str(counter.data) + counter.update() + + page.floating_action_button = ft.FloatingActionButton( + icon=ft.Icons.ADD, on_click=btn_click + ) + page.add( + ft.SafeArea( + ft.Container( + counter, + alignment=ft.Alignment.CENTER, + expand=True, + ), + expand=True, + ), + ) + +ft.run(main) +""" + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + expand=True, + content=fce.CodeEditor( + language=fce.CodeLanguage.PYTHON, + code_theme=fce.CodeTheme.ATOM_ONE_LIGHT, + value=CODE, + expand=True, + on_change=lambda e: print("Changed:", e.data), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/code_editor/example_1/pyproject.toml b/sdk/python/examples/controls/code_editor/example_1/pyproject.toml new file mode 100644 index 0000000000..7eb53007c9 --- /dev/null +++ b/sdk/python/examples/controls/code_editor/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "code-editor-example-1" +version = "1.0.0" +description = "Basic CodeEditor setup with Python syntax highlighting and change callbacks." +requires-python = ">=3.10" +keywords = ["code editor", "syntax highlighting", "python", "editor"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-code-editor"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utilities/CodeEditor"] + +[tool.flet.metadata] +title = "Basic example" +controls = ["SafeArea", "CodeEditor"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["syntax highlighting", "change events"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/code_editor/example_2.py b/sdk/python/examples/controls/code_editor/example_2.py deleted file mode 100644 index 9d7e8753bc..0000000000 --- a/sdk/python/examples/controls/code_editor/example_2.py +++ /dev/null @@ -1,133 +0,0 @@ -import flet_code_editor as fce - -import flet as ft - -CODE = """import flet as ft - -def main(page: ft.Page): - counter = ft.Text("0", size=50, data=0) - - def btn_click(e): - counter.data += 1 - counter.value = str(counter.data) - counter.update() - - page.floating_action_button = ft.FloatingActionButton( - icon=ft.Icons.ADD, on_click=btn_click - ) - page.add( - ft.SafeArea( - ft.Container( - counter, - alignment=ft.Alignment.CENTER, - expand=True, - ), - expand=True, - ), - ) - -ft.run(main) -""" - -font_families = [ - # Apple platforms - "SF Mono", - "Menlo", - # Android - "Roboto Mono", - # Windows - "Consolas", - # Linux - "Ubuntu Mono", - # Universal fallbacks - "Courier New", -] - - -def main(page: ft.Page): - page.title = "CodeEditor selection" - max_selection_preview = 80 - - theme = fce.CustomCodeTheme( - keyword=ft.TextStyle(color=ft.Colors.INDIGO_600, weight=ft.FontWeight.W_600), - string=ft.TextStyle(color=ft.Colors.RED_700), - comment=ft.TextStyle(color=ft.Colors.GREY_600, italic=True), - ) - - text_style = ft.TextStyle( - font_family="monospace", font_family_fallback=font_families, size=12 - ) - - gutter_style = fce.GutterStyle( - text_style=ft.TextStyle( - font_family="monospace", font_family_fallback=font_families, size=12 - ), - show_line_numbers=True, - show_folding_handles=True, - width=80, - ) - - def handle_selection_change(e: ft.TextSelectionChangeEvent[fce.CodeEditor]): - if e.selected_text: - normalized = " ".join(e.selected_text.split()) - suffix = "..." if len(normalized) > max_selection_preview else "" - preview = normalized[:max_selection_preview] - selection.value = ( - f"Selection ({len(e.selected_text)} chars): '{preview}{suffix}'" - ) - else: - selection.value = "No selection." - selection_details.value = f"start={e.selection.start}, end={e.selection.end}" - caret.value = f"Caret position: {e.selection.end}" - - async def select_all(e: ft.Event[ft.Button]): - await editor.focus() - editor.selection = ft.TextSelection( - base_offset=0, - extent_offset=len(editor.value or ""), - ) - - async def move_caret_to_start(e: ft.Event[ft.Button]): - await editor.focus() - editor.selection = ft.TextSelection(base_offset=0, extent_offset=0) - - page.add( - ft.Column( - expand=True, - spacing=10, - controls=[ - editor := fce.CodeEditor( - language=fce.CodeLanguage.PYTHON, - code_theme=theme, - autocomplete=True, - autocomplete_words=[ - "Container", - "Button", - "Text", - "Row", - "Column", - ], - value=CODE, - text_style=text_style, - gutter_style=gutter_style, - padding=10, - on_selection_change=handle_selection_change, - expand=True, - ), - selection := ft.Text("Select some text from the editor."), - selection_details := ft.Text(), - caret := ft.Text("Caret position: -"), - ft.Row( - spacing=10, - controls=[ - ft.Button("Select all text", on_click=select_all), - ft.Button("Move caret to start", on_click=move_caret_to_start), - ], - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/code_editor/example_2/main.py b/sdk/python/examples/controls/code_editor/example_2/main.py new file mode 100644 index 0000000000..a39af8be1b --- /dev/null +++ b/sdk/python/examples/controls/code_editor/example_2/main.py @@ -0,0 +1,139 @@ +import flet_code_editor as fce + +import flet as ft + +CODE = """import flet as ft + +def main(page: ft.Page): + counter = ft.Text("0", size=50, data=0) + + def btn_click(e): + counter.data += 1 + counter.value = str(counter.data) + counter.update() + + page.floating_action_button = ft.FloatingActionButton( + icon=ft.Icons.ADD, on_click=btn_click + ) + page.add( + ft.SafeArea( + ft.Container( + counter, + alignment=ft.Alignment.CENTER, + expand=True, + ), + expand=True, + ), + ) + +ft.run(main) +""" + +font_families = [ + # Apple platforms + "SF Mono", + "Menlo", + # Android + "Roboto Mono", + # Windows + "Consolas", + # Linux + "Ubuntu Mono", + # Universal fallbacks + "Courier New", +] + + +def main(page: ft.Page): + page.title = "CodeEditor selection" + max_selection_preview = 80 + + theme = fce.CustomCodeTheme( + keyword=ft.TextStyle(color=ft.Colors.INDIGO_600, weight=ft.FontWeight.W_600), + string=ft.TextStyle(color=ft.Colors.RED_700), + comment=ft.TextStyle(color=ft.Colors.GREY_600, italic=True), + ) + + text_style = ft.TextStyle( + font_family="monospace", font_family_fallback=font_families, size=12 + ) + + gutter_style = fce.GutterStyle( + text_style=ft.TextStyle( + font_family="monospace", font_family_fallback=font_families, size=12 + ), + show_line_numbers=True, + show_folding_handles=True, + width=80, + ) + + def handle_selection_change(e: ft.TextSelectionChangeEvent[fce.CodeEditor]): + if e.selected_text: + normalized = " ".join(e.selected_text.split()) + suffix = "..." if len(normalized) > max_selection_preview else "" + preview = normalized[:max_selection_preview] + selection.value = ( + f"Selection ({len(e.selected_text)} chars): '{preview}{suffix}'" + ) + else: + selection.value = "No selection." + selection_details.value = f"start={e.selection.start}, end={e.selection.end}" + caret.value = f"Caret position: {e.selection.end}" + + async def select_all(e: ft.Event[ft.Button]): + await editor.focus() + editor.selection = ft.TextSelection( + base_offset=0, + extent_offset=len(editor.value or ""), + ) + + async def move_caret_to_start(e: ft.Event[ft.Button]): + await editor.focus() + editor.selection = ft.TextSelection(base_offset=0, extent_offset=0) + + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + spacing=10, + controls=[ + editor := fce.CodeEditor( + language=fce.CodeLanguage.PYTHON, + code_theme=theme, + autocomplete=True, + autocomplete_words=[ + "Container", + "Button", + "Text", + "Row", + "Column", + ], + value=CODE, + text_style=text_style, + gutter_style=gutter_style, + padding=10, + on_selection_change=handle_selection_change, + expand=True, + ), + selection := ft.Text("Select some text from the editor."), + selection_details := ft.Text(), + caret := ft.Text("Caret position: -"), + ft.Row( + spacing=10, + controls=[ + ft.Button("Select all text", on_click=select_all), + ft.Button( + "Move caret to start", + on_click=move_caret_to_start, + ), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/code_editor/example_2/pyproject.toml b/sdk/python/examples/controls/code_editor/example_2/pyproject.toml new file mode 100644 index 0000000000..072abaaddf --- /dev/null +++ b/sdk/python/examples/controls/code_editor/example_2/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "code-editor-example-2" +version = "1.0.0" +description = "CodeEditor selection demo with custom theme, gutter styling, and caret/selection controls." +requires-python = ">=3.10" +keywords = ["code editor", "selection", "caret", "theme", "gutter"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-code-editor"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utilities/CodeEditor"] + +[tool.flet.metadata] +title = "Selection handling" +controls = ["SafeArea", "Column", "CodeEditor", "Text", "Row", "Button"] +layout_pattern = "control-panel" +complexity = "basic" +features = ["selection change handling", "programmatic caret movement", "custom code theme"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/code_editor/example_3.py b/sdk/python/examples/controls/code_editor/example_3.py deleted file mode 100644 index 61351923cb..0000000000 --- a/sdk/python/examples/controls/code_editor/example_3.py +++ /dev/null @@ -1,43 +0,0 @@ -import flet_code_editor as fce - -import flet as ft - -CODE = """# 1 -# 2 -# 3 -import json -import textwrap - -print("Folding demo") -""" - - -def main(page: ft.Page): - editor = fce.CodeEditor( - language=fce.CodeLanguage.PYTHON, - value=CODE, - selection=ft.TextSelection(base_offset=41, extent_offset=62), - autofocus=True, - expand=True, - on_selection_change=lambda e: print("Selection:", e), - ) - - async def fold_imports(): - await editor.fold_imports() - - async def fold_comment(): - await editor.fold_comment_at_line_zero() - - page.add( - ft.Row( - [ - ft.Button("Fold imports", on_click=fold_imports), - ft.Button("Fold comment", on_click=fold_comment), - ] - ), - editor, - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/code_editor/example_3/main.py b/sdk/python/examples/controls/code_editor/example_3/main.py new file mode 100644 index 0000000000..f0a61d7399 --- /dev/null +++ b/sdk/python/examples/controls/code_editor/example_3/main.py @@ -0,0 +1,50 @@ +import flet_code_editor as fce + +import flet as ft + +CODE = """# 1 +# 2 +# 3 +import json +import textwrap + +print("Folding demo") +""" + + +def main(page: ft.Page): + editor = fce.CodeEditor( + language=fce.CodeLanguage.PYTHON, + value=CODE, + selection=ft.TextSelection(base_offset=41, extent_offset=62), + autofocus=True, + expand=True, + on_selection_change=lambda e: print("Selection:", e), + ) + + async def fold_imports(): + await editor.fold_imports() + + async def fold_comment(): + await editor.fold_comment_at_line_zero() + + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + controls=[ + ft.Row( + controls=[ + ft.Button("Fold imports", on_click=fold_imports), + ft.Button("Fold comment", on_click=fold_comment), + ] + ), + editor, + ] + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/code_editor/example_3/pyproject.toml b/sdk/python/examples/controls/code_editor/example_3/pyproject.toml new file mode 100644 index 0000000000..e129a5ba4a --- /dev/null +++ b/sdk/python/examples/controls/code_editor/example_3/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "code-editor-example-3" +version = "1.0.0" +description = "CodeEditor folding demo with predefined selection and toolbar actions for folding code blocks." +requires-python = ">=3.10" +keywords = ["code editor", "folding", "selection", "toolbar", "python"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-code-editor"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utilities/CodeEditor"] + +[tool.flet.metadata] +title = "Folding and initial selection" +controls = ["SafeArea", "Column", "Row", "Button", "CodeEditor"] +layout_pattern = "control-panel" +complexity = "basic" +features = ["code folding", "initial text selection", "selection change events"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/color_pickers/__init__.py b/sdk/python/examples/controls/color_pickers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/color_pickers/example_1.py b/sdk/python/examples/controls/color_pickers/example_1.py deleted file mode 100644 index 29dd016f5b..0000000000 --- a/sdk/python/examples/controls/color_pickers/example_1.py +++ /dev/null @@ -1,44 +0,0 @@ -import flet as ft -from flet_color_pickers import ColorLabelType, ColorPicker, PaletteType - - -def main(page: ft.Page): - page.title = "ColorPicker" - page.padding = 20 - - def on_color_change(e: ft.ControlEvent): - print(f"color: {e.data}") - - def on_history_change(e: ft.ControlEvent): - # e.data is a list of hex strings - print(f"history: {e.data}") - - def on_hsv_color_change(e: ft.ControlEvent): - print("hsv: ", e.control.hsv_color) - - picker = ColorPicker( - color="#ff0000", - # hsv_color=HsvColor(alpha=1, hue=0, saturation=1, value=1), - color_history=[ - "#ff0000", - "#00ff00", - "#0000ff", - "#ffff00", - "#00ffff", - "#ff00ff", - ], - on_color_change=on_color_change, - palette_type=PaletteType.RGB_WITH_GREEN, - on_history_change=on_history_change, - on_hsv_color_change=on_hsv_color_change, - label_types=[ - ColorLabelType.HEX, - ColorLabelType.RGB, - ], - picker_area_border_radius=ft.BorderRadius.all(20), - ) - - page.add(picker) - - -ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_1/main.py b/sdk/python/examples/controls/color_pickers/example_1/main.py new file mode 100644 index 0000000000..72627c2702 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_1/main.py @@ -0,0 +1,45 @@ +import flet as ft +from flet_color_pickers import ColorLabelType, ColorPicker, PaletteType + + +def main(page: ft.Page): + page.title = "ColorPicker" + page.padding = 20 + + def on_color_change(e: ft.ControlEvent): + print(f"color: {e.data}") + + def on_history_change(e: ft.ControlEvent): + # e.data is a list of hex strings + print(f"history: {e.data}") + + def on_hsv_color_change(e: ft.ControlEvent): + print("hsv: ", e.control.hsv_color) + + picker = ColorPicker( + color="#ff0000", + # hsv_color=HsvColor(alpha=1, hue=0, saturation=1, value=1), + color_history=[ + "#ff0000", + "#00ff00", + "#0000ff", + "#ffff00", + "#00ffff", + "#ff00ff", + ], + on_color_change=on_color_change, + palette_type=PaletteType.RGB_WITH_GREEN, + on_history_change=on_history_change, + on_hsv_color_change=on_hsv_color_change, + label_types=[ + ColorLabelType.HEX, + ColorLabelType.RGB, + ], + picker_area_border_radius=ft.BorderRadius.all(20), + ) + + page.add(ft.SafeArea(content=picker)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_1/pyproject.toml b/sdk/python/examples/controls/color_pickers/example_1/pyproject.toml new file mode 100644 index 0000000000..9d330ed917 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "color-pickers-example-1" +version = "1.0.0" +description = "ColorPicker example with RGB palette mode, color history, and HSV/history change callbacks." +requires-python = ">=3.10" +keywords = ["color picker", "rgb", "hsv", "history", "palette"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-color-pickers"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/ColorPickers"] + +[tool.flet.metadata] +title = "ColorPicker" +controls = ["SafeArea", "ColorPicker"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["color change callback", "color history", "hsv updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/color_pickers/example_2.py b/sdk/python/examples/controls/color_pickers/example_2.py deleted file mode 100644 index b4477cae08..0000000000 --- a/sdk/python/examples/controls/color_pickers/example_2.py +++ /dev/null @@ -1,22 +0,0 @@ -import flet as ft -from flet_color_pickers import HueRingPicker - - -def main(page: ft.Page): - page.title = "HueRingPicker" - page.padding = 20 - - def on_color_change(e: ft.ControlEvent): - print(f"color: {e.data}") - - picker = HueRingPicker( - color="#00ff00", - hue_ring_stroke_width=20, - picker_area_border_radius=ft.BorderRadius.all(5), - on_color_change=on_color_change, - ) - - page.add(picker) - - -ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_2/main.py b/sdk/python/examples/controls/color_pickers/example_2/main.py new file mode 100644 index 0000000000..088be69b20 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_2/main.py @@ -0,0 +1,23 @@ +import flet as ft +from flet_color_pickers import HueRingPicker + + +def main(page: ft.Page): + page.title = "HueRingPicker" + page.padding = 20 + + def on_color_change(e: ft.ControlEvent): + print(f"color: {e.data}") + + picker = HueRingPicker( + color="#00ff00", + hue_ring_stroke_width=20, + picker_area_border_radius=ft.BorderRadius.all(5), + on_color_change=on_color_change, + ) + + page.add(ft.SafeArea(content=picker)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_2/pyproject.toml b/sdk/python/examples/controls/color_pickers/example_2/pyproject.toml new file mode 100644 index 0000000000..d6a7eafa2f --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_2/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "color-pickers-example-2" +version = "1.0.0" +description = "HueRingPicker example with custom ring stroke width and color change callback." +requires-python = ">=3.10" +keywords = ["color picker", "hue ring", "color selection", "callback"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-color-pickers"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/ColorPickers"] + +[tool.flet.metadata] +title = "HueRingPicker" +controls = ["SafeArea", "HueRingPicker"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["color change callback", "hue ring customization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/color_pickers/example_3.py b/sdk/python/examples/controls/color_pickers/example_3.py deleted file mode 100644 index 595c0f7da8..0000000000 --- a/sdk/python/examples/controls/color_pickers/example_3.py +++ /dev/null @@ -1,22 +0,0 @@ -import flet as ft -from flet_color_pickers import ColorModel, SlidePicker - - -def main(page: ft.Page): - page.title = "SlidePicker" - page.padding = 20 - - def on_color_change(e: ft.ControlEvent): - print(f"color: {e.data}") - - picker = SlidePicker( - color="#0000ff", - color_model=ColorModel.RGB, - indicator_border_radius=ft.BorderRadius.all(5), - on_color_change=on_color_change, - ) - - page.add(picker) - - -ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_3/main.py b/sdk/python/examples/controls/color_pickers/example_3/main.py new file mode 100644 index 0000000000..9f74c16144 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_3/main.py @@ -0,0 +1,23 @@ +import flet as ft +from flet_color_pickers import ColorModel, SlidePicker + + +def main(page: ft.Page): + page.title = "SlidePicker" + page.padding = 20 + + def on_color_change(e: ft.ControlEvent): + print(f"color: {e.data}") + + picker = SlidePicker( + color="#0000ff", + color_model=ColorModel.RGB, + indicator_border_radius=ft.BorderRadius.all(5), + on_color_change=on_color_change, + ) + + page.add(ft.SafeArea(content=picker)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_3/pyproject.toml b/sdk/python/examples/controls/color_pickers/example_3/pyproject.toml new file mode 100644 index 0000000000..0753398272 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_3/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "color-pickers-example-3" +version = "1.0.0" +description = "SlidePicker example using RGB color model with styled indicator and color change callback." +requires-python = ">=3.10" +keywords = ["color picker", "slide picker", "rgb", "callback"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-color-pickers"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/ColorPickers"] + +[tool.flet.metadata] +title = "SlidePicker" +controls = ["SafeArea", "SlidePicker"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["color change callback", "rgb color model", "indicator styling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/color_pickers/example_4.py b/sdk/python/examples/controls/color_pickers/example_4.py deleted file mode 100644 index 63ec12dee8..0000000000 --- a/sdk/python/examples/controls/color_pickers/example_4.py +++ /dev/null @@ -1,24 +0,0 @@ -import flet as ft -from flet_color_pickers import MaterialPicker - - -def main(page: ft.Page): - page.title = "MaterialPicker" - page.padding = 20 - - def on_color_change(e: ft.ControlEvent): - print(f"color: {e.data}") - - def on_primary_change(e: ft.ControlEvent): - print(f"primary: {e.data}") - - picker = MaterialPicker( - color="#ff9800", - on_color_change=on_color_change, - on_primary_change=on_primary_change, - ) - - page.add(picker) - - -ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_4/main.py b/sdk/python/examples/controls/color_pickers/example_4/main.py new file mode 100644 index 0000000000..d52aaf43b4 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_4/main.py @@ -0,0 +1,25 @@ +import flet as ft +from flet_color_pickers import MaterialPicker + + +def main(page: ft.Page): + page.title = "MaterialPicker" + page.padding = 20 + + def on_color_change(e: ft.ControlEvent): + print(f"color: {e.data}") + + def on_primary_change(e: ft.ControlEvent): + print(f"primary: {e.data}") + + picker = MaterialPicker( + color="#ff9800", + on_color_change=on_color_change, + on_primary_change=on_primary_change, + ) + + page.add(ft.SafeArea(content=picker)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_4/pyproject.toml b/sdk/python/examples/controls/color_pickers/example_4/pyproject.toml new file mode 100644 index 0000000000..fc6a856a5f --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_4/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "color-pickers-example-4" +version = "1.0.0" +description = "MaterialPicker example with callbacks for both selected color and primary swatch changes." +requires-python = ">=3.10" +keywords = ["color picker", "material picker", "swatch", "callback"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-color-pickers"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/ColorPickers"] + +[tool.flet.metadata] +title = "MaterialPicker" +controls = ["SafeArea", "MaterialPicker"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["color change callback", "primary swatch callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/color_pickers/example_5.py b/sdk/python/examples/controls/color_pickers/example_5.py deleted file mode 100644 index 05cc6c80b7..0000000000 --- a/sdk/python/examples/controls/color_pickers/example_5.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft -from flet_color_pickers import BlockPicker - - -def main(page: ft.Page): - page.title = "BlockPicker" - page.padding = 20 - - def on_color_change(e: ft.ControlEvent): - print(f"color: {e.data}") - - dialog_picker = BlockPicker( - color="#9c27b0", - available_colors=[ - "#f44336", - "#e91e63", - "#9c27b0", - "#3f51b5", - "#2196f3", - "#009688", - "#4caf50", - "#795548", - ], - on_color_change=on_color_change, - ) - - dialog = ft.AlertDialog( - modal=True, - title=ft.Text("Pick a color"), - content=dialog_picker, - actions=[ - ft.TextButton("Close", on_click=lambda e: page.pop_dialog()), - ], - ) - - page.add( - ft.IconButton(icon=ft.Icons.BRUSH, on_click=lambda e: page.show_dialog(dialog)), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_5/main.py b/sdk/python/examples/controls/color_pickers/example_5/main.py new file mode 100644 index 0000000000..a6d9a5faa1 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_5/main.py @@ -0,0 +1,47 @@ +import flet as ft +from flet_color_pickers import BlockPicker + + +def main(page: ft.Page): + page.title = "BlockPicker" + page.padding = 20 + + def on_color_change(e: ft.ControlEvent): + print(f"color: {e.data}") + + dialog_picker = BlockPicker( + color="#9c27b0", + available_colors=[ + "#f44336", + "#e91e63", + "#9c27b0", + "#3f51b5", + "#2196f3", + "#009688", + "#4caf50", + "#795548", + ], + on_color_change=on_color_change, + ) + + dialog = ft.AlertDialog( + modal=True, + title=ft.Text("Pick a color"), + content=dialog_picker, + actions=[ + ft.TextButton("Close", on_click=lambda e: page.pop_dialog()), + ], + ) + + page.add( + ft.SafeArea( + content=ft.IconButton( + icon=ft.Icons.BRUSH, + on_click=lambda e: page.show_dialog(dialog), + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_5/pyproject.toml b/sdk/python/examples/controls/color_pickers/example_5/pyproject.toml new file mode 100644 index 0000000000..0603df1f09 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_5/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "color-pickers-example-5" +version = "1.0.0" +description = "BlockPicker dialog example with curated color palette and icon-triggered modal launch." +requires-python = ">=3.10" +keywords = ["color picker", "block picker", "dialog", "palette", "modal"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-color-pickers"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/ColorPickers"] + +[tool.flet.metadata] +title = "BlockPicker" +controls = ["SafeArea", "IconButton", "AlertDialog", "TextButton", "BlockPicker"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["dialog picker", "color change callback", "custom color palette"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/color_pickers/example_6.py b/sdk/python/examples/controls/color_pickers/example_6.py deleted file mode 100644 index 3d8f196ed5..0000000000 --- a/sdk/python/examples/controls/color_pickers/example_6.py +++ /dev/null @@ -1,43 +0,0 @@ -import flet as ft -from flet_color_pickers import MultipleChoiceBlockPicker - - -def main(page: ft.Page): - page.title = "MultipleChoiceBlockPicker" - page.padding = 20 - - def on_colors_change(e: ft.ControlEvent): - print(f"colors: {e.data}") - - dialog_picker = MultipleChoiceBlockPicker( - colors=["#03a9f4", "#4caf50"], - available_colors=[ - "#f44336", - "#e91e63", - "#9c27b0", - "#3f51b5", - "#2196f3", - "#03a9f4", - "#009688", - "#4caf50", - "#ff9800", - "#795548", - ], - on_colors_change=on_colors_change, - ) - - dialog = ft.AlertDialog( - modal=True, - title=ft.Text("Pick colors"), - content=dialog_picker, - actions=[ - ft.TextButton("Close", on_click=lambda e: page.pop_dialog()), - ], - ) - - page.add( - ft.IconButton(icon=ft.Icons.BRUSH, on_click=lambda e: page.show_dialog(dialog)), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_6/main.py b/sdk/python/examples/controls/color_pickers/example_6/main.py new file mode 100644 index 0000000000..c3cf95d121 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_6/main.py @@ -0,0 +1,49 @@ +import flet as ft +from flet_color_pickers import MultipleChoiceBlockPicker + + +def main(page: ft.Page): + page.title = "MultipleChoiceBlockPicker" + page.padding = 20 + + def on_colors_change(e: ft.ControlEvent): + print(f"colors: {e.data}") + + dialog_picker = MultipleChoiceBlockPicker( + colors=["#03a9f4", "#4caf50"], + available_colors=[ + "#f44336", + "#e91e63", + "#9c27b0", + "#3f51b5", + "#2196f3", + "#03a9f4", + "#009688", + "#4caf50", + "#ff9800", + "#795548", + ], + on_colors_change=on_colors_change, + ) + + dialog = ft.AlertDialog( + modal=True, + title=ft.Text("Pick colors"), + content=dialog_picker, + actions=[ + ft.TextButton("Close", on_click=lambda e: page.pop_dialog()), + ], + ) + + page.add( + ft.SafeArea( + content=ft.IconButton( + icon=ft.Icons.BRUSH, + on_click=lambda e: page.show_dialog(dialog), + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/color_pickers/example_6/pyproject.toml b/sdk/python/examples/controls/color_pickers/example_6/pyproject.toml new file mode 100644 index 0000000000..c6f5212d51 --- /dev/null +++ b/sdk/python/examples/controls/color_pickers/example_6/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "color-pickers-example-6" +version = "1.0.0" +description = "MultipleChoiceBlockPicker dialog with multi-select colors and icon-triggered modal launch." +requires-python = ">=3.10" +keywords = ["color picker", "multi-select", "block picker", "dialog", "palette"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-color-pickers"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/ColorPickers"] + +[tool.flet.metadata] +title = "MultipleChoiceBlockPicker" +controls = ["SafeArea", "IconButton", "AlertDialog", "TextButton", "MultipleChoiceBlockPicker"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["dialog picker", "multiple color selection", "colors change callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/alignment.py b/sdk/python/examples/controls/column/alignment.py deleted file mode 100644 index d0238ba9be..0000000000 --- a/sdk/python/examples/controls/column/alignment.py +++ /dev/null @@ -1,49 +0,0 @@ -import flet as ft - - -class ColumnFromVerticalAlignment(ft.Column): - def __init__(self, alignment: ft.MainAxisAlignment): - super().__init__() - self.controls = [ - ft.Text(str(alignment), size=10), - ft.Container( - content=ft.Column(self.generate_items(3), alignment=alignment), - bgcolor=ft.Colors.AMBER_100, - height=400, - ), - ] - - @staticmethod - def generate_items(count: int): - """Generates a list of custom Containers with length `count`.""" - return [ - ft.Container( - content=ft.Text(value=str(i)), - alignment=ft.Alignment.CENTER, - width=50, - height=50, - bgcolor=ft.Colors.AMBER_500, - ) - for i in range(1, count + 1) - ] - - -def main(page: ft.Page): - page.add( - ft.Row( - spacing=30, - alignment=ft.MainAxisAlignment.START, - scroll=ft.ScrollMode.AUTO, - controls=[ - ColumnFromVerticalAlignment(ft.MainAxisAlignment.START), - ColumnFromVerticalAlignment(ft.MainAxisAlignment.CENTER), - ColumnFromVerticalAlignment(ft.MainAxisAlignment.END), - ColumnFromVerticalAlignment(ft.MainAxisAlignment.SPACE_BETWEEN), - ColumnFromVerticalAlignment(ft.MainAxisAlignment.SPACE_AROUND), - ColumnFromVerticalAlignment(ft.MainAxisAlignment.SPACE_EVENLY), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/alignment/main.py b/sdk/python/examples/controls/column/alignment/main.py new file mode 100644 index 0000000000..eb9ae5dd90 --- /dev/null +++ b/sdk/python/examples/controls/column/alignment/main.py @@ -0,0 +1,60 @@ +import flet as ft + + +@ft.control +class ColumnFromVerticalAlignment(ft.Column): + alignment: ft.MainAxisAlignment = ft.MainAxisAlignment.START + + def init(self): + self.controls = [ + ft.Text(str(self.alignment), size=10), + ft.Container( + content=ft.Column(self.generate_items(3), alignment=self.alignment), + bgcolor=ft.Colors.AMBER_100, + height=400, + ), + ] + + @staticmethod + def generate_items(count: int): + """Generates a list of custom Containers with length `count`.""" + return [ + ft.Container( + content=ft.Text(value=str(i)), + alignment=ft.Alignment.CENTER, + width=50, + height=50, + bgcolor=ft.Colors.AMBER_500, + ) + for i in range(1, count + 1) + ] + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Row( + spacing=30, + alignment=ft.MainAxisAlignment.START, + scroll=ft.ScrollMode.AUTO, + controls=[ + ColumnFromVerticalAlignment(alignment=ft.MainAxisAlignment.START), + ColumnFromVerticalAlignment(alignment=ft.MainAxisAlignment.CENTER), + ColumnFromVerticalAlignment(alignment=ft.MainAxisAlignment.END), + ColumnFromVerticalAlignment( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN + ), + ColumnFromVerticalAlignment( + alignment=ft.MainAxisAlignment.SPACE_AROUND + ), + ColumnFromVerticalAlignment( + alignment=ft.MainAxisAlignment.SPACE_EVENLY + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/alignment/pyproject.toml b/sdk/python/examples/controls/column/alignment/pyproject.toml new file mode 100644 index 0000000000..59f3b1f6fb --- /dev/null +++ b/sdk/python/examples/controls/column/alignment/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-alignment" +version = "1.0.0" +description = "Column vertical alignment modes demonstrated side by side." +requires-python = ">=3.10" +keywords = ["column", "layout", "alignment", "mainaxisalignment", "scroll"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column vertical alignment" +controls = ["Row", "Column", "Container", "Text"] +layout_pattern = "comparison-grid" +complexity = "basic" +features = ["main axis alignment", "layout comparison", "horizontal scrolling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/custom_scrollbar.py b/sdk/python/examples/controls/column/custom_scrollbar.py deleted file mode 100644 index 133cb072fb..0000000000 --- a/sdk/python/examples/controls/column/custom_scrollbar.py +++ /dev/null @@ -1,58 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme = ft.Theme( - scrollbar_theme=ft.ScrollbarTheme( - track_color={ - ft.ControlState.HOVERED: ft.Colors.AMBER, - ft.ControlState.DEFAULT: ft.Colors.TRANSPARENT, - }, - track_visibility=True, - track_border_color=ft.Colors.BLUE, - thumb_visibility=True, - thumb_color={ - ft.ControlState.HOVERED: ft.Colors.RED, - ft.ControlState.DEFAULT: ft.Colors.GREY_300, - }, - thickness=30, - radius=15, - main_axis_margin=5, - cross_axis_margin=10, - ) - ) - - fake_messages = [ - ft.Container( - ft.Text(f"Message {i}", size=16, weight=ft.FontWeight.W_500), - bgcolor=ft.Colors.with_opacity(0.15, ft.Colors.BLUE_200), - border_radius=8, - padding=10, - ) - for i in range(1, 31) - ] - - page.add( - ft.Row( - [ - ft.Container( - content=ft.Column( - controls=fake_messages, - spacing=10, - scroll=ft.ScrollMode.ALWAYS, - expand=True, - ), - width=320, - height=420, - bgcolor=ft.Colors.with_opacity(0.15, ft.Colors.AMBER_200), - padding=15, - border_radius=12, - ) - ], - alignment=ft.MainAxisAlignment.CENTER, - expand=True, - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/custom_scrollbar/main.py b/sdk/python/examples/controls/column/custom_scrollbar/main.py new file mode 100644 index 0000000000..f0376fdfba --- /dev/null +++ b/sdk/python/examples/controls/column/custom_scrollbar/main.py @@ -0,0 +1,61 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme = ft.Theme( + scrollbar_theme=ft.ScrollbarTheme( + track_color={ + ft.ControlState.HOVERED: ft.Colors.AMBER, + ft.ControlState.DEFAULT: ft.Colors.TRANSPARENT, + }, + track_visibility=True, + track_border_color=ft.Colors.BLUE, + thumb_visibility=True, + thumb_color={ + ft.ControlState.HOVERED: ft.Colors.RED, + ft.ControlState.DEFAULT: ft.Colors.GREY_300, + }, + thickness=30, + radius=15, + main_axis_margin=5, + cross_axis_margin=10, + ) + ) + + fake_messages = [ + ft.Container( + ft.Text(f"Message {i}", size=16, weight=ft.FontWeight.W_500), + bgcolor=ft.Colors.with_opacity(0.15, ft.Colors.BLUE_200), + border_radius=8, + padding=10, + ) + for i in range(1, 31) + ] + + page.add( + ft.SafeArea( + content=ft.Row( + [ + ft.Container( + content=ft.Column( + controls=fake_messages, + spacing=10, + scroll=ft.ScrollMode.ALWAYS, + expand=True, + ), + width=320, + height=420, + bgcolor=ft.Colors.with_opacity(0.15, ft.Colors.AMBER_200), + padding=15, + border_radius=12, + ) + ], + alignment=ft.MainAxisAlignment.CENTER, + expand=True, + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/custom_scrollbar/pyproject.toml b/sdk/python/examples/controls/column/custom_scrollbar/pyproject.toml new file mode 100644 index 0000000000..b459a8ca61 --- /dev/null +++ b/sdk/python/examples/controls/column/custom_scrollbar/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-custom-scrollbar" +version = "1.0.0" +description = "Column with a customized scrollbar theme and styled message list." +requires-python = ">=3.10" +keywords = ["column", "layout", "scrollbar", "theme", "scroll"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column custom scrollbar" +controls = ["Theme", "ScrollbarTheme", "Row", "Column", "Container", "Text"] +layout_pattern = "chat-list" +complexity = "basic" +features = ["custom scrollbar styling", "themed scroll behavior", "scrollable message list"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/horizontal_alignment.py b/sdk/python/examples/controls/column/horizontal_alignment.py deleted file mode 100644 index 97145c483f..0000000000 --- a/sdk/python/examples/controls/column/horizontal_alignment.py +++ /dev/null @@ -1,49 +0,0 @@ -import flet as ft - - -class ColumnFromHorizontalAlignment(ft.Column): - def __init__(self, alignment: ft.CrossAxisAlignment): - super().__init__() - self.controls = [ - ft.Text(str(alignment), size=16), - ft.Container( - bgcolor=ft.Colors.AMBER_100, - width=100, - content=ft.Column( - controls=self.generate_items(3), - alignment=ft.MainAxisAlignment.START, - horizontal_alignment=alignment, - ), - ), - ] - - @staticmethod - def generate_items(count: int): - """Generates a list of custom Containers with length `count`.""" - return [ - ft.Container( - content=ft.Text(value=str(i)), - alignment=ft.Alignment.CENTER, - width=50, - height=50, - bgcolor=ft.Colors.AMBER_500, - ) - for i in range(1, count + 1) - ] - - -def main(page: ft.Page): - page.add( - ft.Row( - spacing=30, - alignment=ft.MainAxisAlignment.START, - controls=[ - ColumnFromHorizontalAlignment(ft.CrossAxisAlignment.START), - ColumnFromHorizontalAlignment(ft.CrossAxisAlignment.CENTER), - ColumnFromHorizontalAlignment(ft.CrossAxisAlignment.END), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/horizontal_alignment/main.py b/sdk/python/examples/controls/column/horizontal_alignment/main.py new file mode 100644 index 0000000000..dcd9b92f36 --- /dev/null +++ b/sdk/python/examples/controls/column/horizontal_alignment/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +@ft.control +class ColumnFromHorizontalAlignment(ft.Column): + alignment: ft.CrossAxisAlignment = ft.CrossAxisAlignment.START + + def init(self): + self.controls = [ + ft.Text(str(self.alignment), size=16), + ft.Container( + bgcolor=ft.Colors.AMBER_100, + width=100, + content=ft.Column( + controls=self.generate_items(3), + alignment=ft.MainAxisAlignment.START, + horizontal_alignment=self.alignment, + ), + ), + ] + + @staticmethod + def generate_items(count: int): + """Generates a list of custom Containers with length `count`.""" + return [ + ft.Container( + content=ft.Text(value=str(i)), + alignment=ft.Alignment.CENTER, + width=50, + height=50, + bgcolor=ft.Colors.AMBER_500, + ) + for i in range(1, count + 1) + ] + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Row( + spacing=30, + alignment=ft.MainAxisAlignment.START, + controls=[ + ColumnFromHorizontalAlignment( + alignment=ft.CrossAxisAlignment.START + ), + ColumnFromHorizontalAlignment( + alignment=ft.CrossAxisAlignment.CENTER + ), + ColumnFromHorizontalAlignment(alignment=ft.CrossAxisAlignment.END), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/horizontal_alignment/pyproject.toml b/sdk/python/examples/controls/column/horizontal_alignment/pyproject.toml new file mode 100644 index 0000000000..3d6cbf3e84 --- /dev/null +++ b/sdk/python/examples/controls/column/horizontal_alignment/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-horizontal-alignment" +version = "1.0.0" +description = "Column cross-axis alignment modes with visual comparison." +requires-python = ">=3.10" +keywords = ["column", "layout", "crossaxisalignment", "alignment"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column horizontal alignment" +controls = ["Row", "Column", "Container", "Text"] +layout_pattern = "comparison-grid" +complexity = "basic" +features = ["cross axis alignment", "layout comparison"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/infinite_scrolling.py b/sdk/python/examples/controls/column/infinite_scrolling.py deleted file mode 100644 index f41263c3f6..0000000000 --- a/sdk/python/examples/controls/column/infinite_scrolling.py +++ /dev/null @@ -1,40 +0,0 @@ -import threading - -import flet as ft - - -class State: - i = 0 - - -s = State() -sem = threading.Semaphore() - - -def main(page: ft.Page): - def on_scroll(e: ft.OnScrollEvent): - if e.pixels >= e.max_scroll_extent - 100 and sem.acquire(blocking=False): - try: - for _i in range(0, 10): - cl.controls.append(ft.Text(f"Text line {s.i}", key=str(s.i))) - s.i += 1 - cl.update() - finally: - sem.release() - - cl = ft.Column( - spacing=10, - height=200, - width=200, - scroll=ft.ScrollMode.ALWAYS, - scroll_interval=0, - on_scroll=on_scroll, - ) - for _i in range(0, 50): - cl.controls.append(ft.Text(f"Text line {s.i}", key=str(s.i))) - s.i += 1 - - page.add(ft.Container(cl, border=ft.Border.all(1))) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/infinite_scrolling/main.py b/sdk/python/examples/controls/column/infinite_scrolling/main.py new file mode 100644 index 0000000000..22f3212983 --- /dev/null +++ b/sdk/python/examples/controls/column/infinite_scrolling/main.py @@ -0,0 +1,41 @@ +import threading + +import flet as ft + + +class State: + i = 0 + + +s = State() +sem = threading.Semaphore() + + +def main(page: ft.Page): + def on_scroll(e: ft.OnScrollEvent): + if e.pixels >= e.max_scroll_extent - 100 and sem.acquire(blocking=False): + try: + for _i in range(0, 10): + cl.controls.append(ft.Text(f"Text line {s.i}", key=str(s.i))) + s.i += 1 + cl.update() + finally: + sem.release() + + cl = ft.Column( + spacing=10, + height=200, + width=200, + scroll=ft.ScrollMode.ALWAYS, + scroll_interval=0, + on_scroll=on_scroll, + ) + for _i in range(0, 50): + cl.controls.append(ft.Text(f"Text line {s.i}", key=str(s.i))) + s.i += 1 + + page.add(ft.SafeArea(content=ft.Container(cl, border=ft.Border.all(1)))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/infinite_scrolling/pyproject.toml b/sdk/python/examples/controls/column/infinite_scrolling/pyproject.toml new file mode 100644 index 0000000000..943e45b564 --- /dev/null +++ b/sdk/python/examples/controls/column/infinite_scrolling/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-infinite-scrolling" +version = "1.0.0" +description = "Infinite scrolling in a column by appending items on scroll." +requires-python = ">=3.10" +keywords = ["column", "layout", "infinite scrolling", "on_scroll", "lazy loading"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column infinite scrolling" +controls = ["Container", "Column", "Text"] +layout_pattern = "infinite-list" +complexity = "basic" +features = ["on_scroll handling", "dynamic item appending", "semaphore guard"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/programmatic_scroll.py b/sdk/python/examples/controls/column/programmatic_scroll.py deleted file mode 100644 index 7b5c15e5a0..0000000000 --- a/sdk/python/examples/controls/column/programmatic_scroll.py +++ /dev/null @@ -1,54 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - column = ft.Column( - spacing=10, - height=200, - width=float("inf"), - scroll=ft.ScrollMode.ALWAYS, - controls=[ - ft.Text(f"Text line {i}", key=ft.ScrollKey(i)) for i in range(0, 100) - ], - ) - - async def scroll_to_offset(e): - await column.scroll_to(offset=500, duration=1000) - - async def scroll_to_start(e): - await column.scroll_to(offset=0, duration=1000) - - async def scroll_to_end(e): - await column.scroll_to( - offset=-1, duration=2000, curve=ft.AnimationCurve.EASE_IN_OUT - ) - - async def scroll_to_key(e): - await column.scroll_to(scroll_key="20", duration=1000) - - async def scroll_to_delta(e): - await column.scroll_to(delta=100, duration=200) - - async def scroll_to_minus_delta(e): - await column.scroll_to(delta=-100, duration=200) - - page.add( - ft.Container(content=column, border=ft.Border.all(1)), - ft.Button("Scroll to offset 500", on_click=scroll_to_offset), - ft.Row( - controls=[ - ft.Button("Scroll -100", on_click=scroll_to_minus_delta), - ft.Button("Scroll +100", on_click=scroll_to_delta), - ] - ), - ft.Button("Scroll to key '20'", on_click=scroll_to_key), - ft.Row( - controls=[ - ft.Button("Scroll to start", on_click=scroll_to_start), - ft.Button("Scroll to end", on_click=scroll_to_end), - ] - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/programmatic_scroll/main.py b/sdk/python/examples/controls/column/programmatic_scroll/main.py new file mode 100644 index 0000000000..1e5f5026fd --- /dev/null +++ b/sdk/python/examples/controls/column/programmatic_scroll/main.py @@ -0,0 +1,61 @@ +import flet as ft + + +def main(page: ft.Page): + column = ft.Column( + spacing=10, + height=200, + width=float("inf"), + scroll=ft.ScrollMode.ALWAYS, + controls=[ + ft.Text(f"Text line {i}", key=ft.ScrollKey(i)) for i in range(0, 100) + ], + ) + + async def scroll_to_offset(e): + await column.scroll_to(offset=500, duration=1000) + + async def scroll_to_start(e): + await column.scroll_to(offset=0, duration=1000) + + async def scroll_to_end(e): + await column.scroll_to( + offset=-1, duration=2000, curve=ft.AnimationCurve.EASE_IN_OUT + ) + + async def scroll_to_key(e): + await column.scroll_to(scroll_key="20", duration=1000) + + async def scroll_to_delta(e): + await column.scroll_to(delta=100, duration=200) + + async def scroll_to_minus_delta(e): + await column.scroll_to(delta=-100, duration=200) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Container(content=column, border=ft.Border.all(1)), + ft.Button("Scroll to offset 500", on_click=scroll_to_offset), + ft.Row( + controls=[ + ft.Button("Scroll -100", on_click=scroll_to_minus_delta), + ft.Button("Scroll +100", on_click=scroll_to_delta), + ] + ), + ft.Button("Scroll to key '20'", on_click=scroll_to_key), + ft.Row( + controls=[ + ft.Button("Scroll to start", on_click=scroll_to_start), + ft.Button("Scroll to end", on_click=scroll_to_end), + ] + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/programmatic_scroll/pyproject.toml b/sdk/python/examples/controls/column/programmatic_scroll/pyproject.toml new file mode 100644 index 0000000000..7a75ab126e --- /dev/null +++ b/sdk/python/examples/controls/column/programmatic_scroll/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-programmatic-scroll" +version = "1.0.0" +description = "Programmatic scrolling of a column using offsets, keys, and deltas." +requires-python = ">=3.10" +keywords = ["column", "layout", "scroll_to", "scroll key", "animation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column programmatic scroll" +controls = ["Container", "Column", "Row", "Button", "Text"] +layout_pattern = "control-panel" +complexity = "basic" +features = ["async scroll_to", "offset and delta scrolling", "scroll key targeting"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/scroll.py b/sdk/python/examples/controls/column/scroll.py deleted file mode 100644 index f1f66404d4..0000000000 --- a/sdk/python/examples/controls/column/scroll.py +++ /dev/null @@ -1,89 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def add_text_box(e: ft.Event[ft.Button]): - text_field = ft.TextField( - label=f"Text Box {len(left_column.controls)}", - label_style=ft.TextStyle(color=ft.Colors.GREEN), - color=ft.Colors.GREEN, - value=str(len(left_column.controls)), - ) - left_column.controls.append(text_field) - page.update() - - def remove_text_box(e: ft.Event[ft.Button]): - if left_column.controls: - left_column.controls.pop() - page.update() - - def scroll_generator(scroll_mode_list: list): - while True: - yield from scroll_mode_list - - def change_scroll(_): - left_column.scroll = next(scroll_mode) - scroll_mode_text.value = str(left_column.scroll) - page.update() - - add_text_box_button = ft.Button("Add TextBox", on_click=add_text_box) - remove_text_box_button = ft.Button( - content="Remove TextBox", - on_click=remove_text_box, - ) - scroll_change_button = ft.Button( - content="Change Scroll Mode", - on_click=change_scroll, - ) - - scroll_mode = scroll_generator( - [ - None, - ft.ScrollMode.AUTO, - ft.ScrollMode.ADAPTIVE, - ft.ScrollMode.ALWAYS, - ft.ScrollMode.HIDDEN, - ] - ) - - left_column = ft.Column( - controls=[ft.Text("THIS IS COL 1", color=ft.Colors.RED_400)], - scroll=next(scroll_mode), - ) - scroll_mode_text = ft.Text(str(left_column.scroll)) - - page.add( - ft.Row( - expand=True, - controls=[ - ft.Container( - content=left_column, - expand=True, - margin=10, - padding=10, - bgcolor=ft.Colors.AMBER_100, - border_radius=10, - alignment=ft.Alignment.TOP_CENTER, - ), - ft.Container( - margin=10, - padding=10, - bgcolor=ft.Colors.CYAN_500, - border_radius=10, - expand=True, - alignment=ft.Alignment.TOP_LEFT, - content=ft.Column( - controls=[ - add_text_box_button, - remove_text_box_button, - scroll_change_button, - scroll_mode_text, - ], - ), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/scroll/main.py b/sdk/python/examples/controls/column/scroll/main.py new file mode 100644 index 0000000000..b39090274c --- /dev/null +++ b/sdk/python/examples/controls/column/scroll/main.py @@ -0,0 +1,90 @@ +import flet as ft + + +def main(page: ft.Page): + def add_text_box(e: ft.Event[ft.Button]): + text_field = ft.TextField( + label=f"Text Box {len(left_column.controls)}", + label_style=ft.TextStyle(color=ft.Colors.GREEN), + color=ft.Colors.GREEN, + value=str(len(left_column.controls)), + ) + left_column.controls.append(text_field) + + def remove_text_box(e: ft.Event[ft.Button]): + if left_column.controls: + left_column.controls.pop() + + def scroll_generator(scroll_mode_list: list): + while True: + yield from scroll_mode_list + + def change_scroll(_): + left_column.scroll = next(scroll_mode) + scroll_mode_text.value = str(left_column.scroll) + + add_text_box_button = ft.Button("Add TextBox", on_click=add_text_box) + remove_text_box_button = ft.Button( + content="Remove TextBox", + on_click=remove_text_box, + ) + scroll_change_button = ft.Button( + content="Change Scroll Mode", + on_click=change_scroll, + ) + + scroll_mode = scroll_generator( + [ + None, + ft.ScrollMode.AUTO, + ft.ScrollMode.ADAPTIVE, + ft.ScrollMode.ALWAYS, + ft.ScrollMode.HIDDEN, + ] + ) + + left_column = ft.Column( + controls=[ft.Text("THIS IS COL 1", color=ft.Colors.RED_400)], + scroll=next(scroll_mode), + ) + scroll_mode_text = ft.Text(str(left_column.scroll)) + + page.add( + ft.SafeArea( + expand=True, + content=ft.Row( + expand=True, + controls=[ + ft.Container( + content=left_column, + expand=True, + margin=10, + padding=10, + bgcolor=ft.Colors.AMBER_100, + border_radius=10, + alignment=ft.Alignment.TOP_CENTER, + ), + ft.Container( + margin=10, + padding=10, + bgcolor=ft.Colors.CYAN_500, + border_radius=10, + expand=True, + alignment=ft.Alignment.TOP_LEFT, + content=ft.Column( + controls=[ + add_text_box_button, + remove_text_box_button, + scroll_change_button, + scroll_mode_text, + ], + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/scroll/pyproject.toml b/sdk/python/examples/controls/column/scroll/pyproject.toml new file mode 100644 index 0000000000..605d832631 --- /dev/null +++ b/sdk/python/examples/controls/column/scroll/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-scroll" +version = "1.0.0" +description = "Interactive column scrolling with dynamic controls and scroll mode switching." +requires-python = ">=3.10" +keywords = ["column", "layout", "scroll mode", "dynamic controls", "text field"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column scrolling" +controls = ["Row", "Column", "Container", "Button", "Text", "TextField"] +layout_pattern = "split-panel" +complexity = "basic" +features = ["dynamic add/remove", "scroll mode cycling", "interactive control panel"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/scroll_events.py b/sdk/python/examples/controls/column/scroll_events.py deleted file mode 100644 index e4d15bbeff..0000000000 --- a/sdk/python/examples/controls/column/scroll_events.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_column_scroll(e: ft.OnScrollEvent): - print(e) - - page.add( - ft.Container( - border=ft.Border.all(1), - content=ft.Column( - spacing=10, - height=200, - width=200, - scroll=ft.ScrollMode.ALWAYS, - on_scroll=handle_column_scroll, - controls=[ - ft.Text(f"Text line {i}", key=ft.ScrollKey(str(i))) - for i in range(0, 50) - ], - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/scroll_events/main.py b/sdk/python/examples/controls/column/scroll_events/main.py new file mode 100644 index 0000000000..741ff6f31e --- /dev/null +++ b/sdk/python/examples/controls/column/scroll_events/main.py @@ -0,0 +1,29 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_column_scroll(e: ft.OnScrollEvent): + print(e) + + page.add( + ft.SafeArea( + content=ft.Container( + border=ft.Border.all(1), + content=ft.Column( + spacing=10, + height=200, + width=200, + scroll=ft.ScrollMode.ALWAYS, + on_scroll=handle_column_scroll, + controls=[ + ft.Text(f"Text line {i}", key=ft.ScrollKey(str(i))) + for i in range(0, 50) + ], + ), + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/scroll_events/pyproject.toml b/sdk/python/examples/controls/column/scroll_events/pyproject.toml new file mode 100644 index 0000000000..d956025ba2 --- /dev/null +++ b/sdk/python/examples/controls/column/scroll_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-scroll-events" +version = "1.0.0" +description = "Column on_scroll event handling example." +requires-python = ">=3.10" +keywords = ["column", "layout", "on_scroll", "events", "scroll"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column scroll events" +controls = ["Container", "Column", "Text"] +layout_pattern = "event-monitor" +complexity = "basic" +features = ["on_scroll callback", "scroll event inspection"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/scroll_to_key.py b/sdk/python/examples/controls/column/scroll_to_key.py deleted file mode 100644 index c4fa4ef9c9..0000000000 --- a/sdk/python/examples/controls/column/scroll_to_key.py +++ /dev/null @@ -1,87 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - async def scroll_a(): - await column1.scroll_to(scroll_key=ft.ScrollKey("A"), duration=1000) - - async def scroll_b(): - await column1.scroll_to(scroll_key=ft.ScrollKey("B"), duration=1000) - - async def scroll_c(): - await column1.scroll_to(scroll_key=ft.ScrollKey("C"), duration=1000) - - async def scroll_d(): - await column1.scroll_to(scroll_key=ft.ScrollKey("D"), duration=1000) - - page.add( - ft.Container( - border=ft.Border.all(1), - content=( - column1 := ft.Column( - spacing=10, - height=200, - width=300, - scroll=ft.ScrollMode.ALWAYS, - controls=[ - ft.Container( - content=ft.Text("Section A", color=ft.Colors.BLACK), - alignment=ft.Alignment.TOP_LEFT, - bgcolor=ft.Colors.YELLOW_200, - height=100, - key=ft.ScrollKey("A"), - ), - ft.Container( - content=ft.Text("Section B", color=ft.Colors.BLACK), - alignment=ft.Alignment.TOP_LEFT, - bgcolor=ft.Colors.GREEN_200, - height=100, - key=ft.ScrollKey("B"), - ), - ft.Container( - content=ft.Text("Section C", color=ft.Colors.BLACK), - alignment=ft.Alignment.TOP_LEFT, - bgcolor=ft.Colors.BLUE_200, - height=100, - key=ft.ScrollKey("C"), - ), - ft.Container( - content=ft.Text("Section D", color=ft.Colors.BLACK), - alignment=ft.Alignment.TOP_LEFT, - bgcolor=ft.Colors.PINK_200, - height=100, - key=ft.ScrollKey("D"), - ), - ], - ) - ), - ), - ft.Column( - controls=[ - ft.Text("Scroll to:"), - ft.Row( - controls=[ - ft.Button( - content="Section A", - on_click=scroll_a, - ), - ft.Button( - content="Section B", - on_click=scroll_b, - ), - ft.Button( - content="Section C", - on_click=scroll_c, - ), - ft.Button( - content="Section D", - on_click=scroll_d, - ), - ] - ), - ] - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/scroll_to_key/main.py b/sdk/python/examples/controls/column/scroll_to_key/main.py new file mode 100644 index 0000000000..5592e09f2f --- /dev/null +++ b/sdk/python/examples/controls/column/scroll_to_key/main.py @@ -0,0 +1,102 @@ +import flet as ft + + +def main(page: ft.Page): + async def scroll_a(): + await column1.scroll_to(scroll_key=ft.ScrollKey("A"), duration=1000) + + async def scroll_b(): + await column1.scroll_to(scroll_key=ft.ScrollKey("B"), duration=1000) + + async def scroll_c(): + await column1.scroll_to(scroll_key=ft.ScrollKey("C"), duration=1000) + + async def scroll_d(): + await column1.scroll_to(scroll_key=ft.ScrollKey("D"), duration=1000) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Container( + border=ft.Border.all(1), + content=( + column1 := ft.Column( + spacing=10, + height=200, + width=300, + scroll=ft.ScrollMode.ALWAYS, + controls=[ + ft.Container( + content=ft.Text( + "Section A", color=ft.Colors.BLACK + ), + alignment=ft.Alignment.TOP_LEFT, + bgcolor=ft.Colors.YELLOW_200, + height=100, + key=ft.ScrollKey("A"), + ), + ft.Container( + content=ft.Text( + "Section B", color=ft.Colors.BLACK + ), + alignment=ft.Alignment.TOP_LEFT, + bgcolor=ft.Colors.GREEN_200, + height=100, + key=ft.ScrollKey("B"), + ), + ft.Container( + content=ft.Text( + "Section C", color=ft.Colors.BLACK + ), + alignment=ft.Alignment.TOP_LEFT, + bgcolor=ft.Colors.BLUE_200, + height=100, + key=ft.ScrollKey("C"), + ), + ft.Container( + content=ft.Text( + "Section D", color=ft.Colors.BLACK + ), + alignment=ft.Alignment.TOP_LEFT, + bgcolor=ft.Colors.PINK_200, + height=100, + key=ft.ScrollKey("D"), + ), + ], + ) + ), + ), + ft.Column( + controls=[ + ft.Text("Scroll to:"), + ft.Row( + controls=[ + ft.Button( + content="Section A", + on_click=scroll_a, + ), + ft.Button( + content="Section B", + on_click=scroll_b, + ), + ft.Button( + content="Section C", + on_click=scroll_c, + ), + ft.Button( + content="Section D", + on_click=scroll_d, + ), + ] + ), + ] + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/scroll_to_key/pyproject.toml b/sdk/python/examples/controls/column/scroll_to_key/pyproject.toml new file mode 100644 index 0000000000..54749c767b --- /dev/null +++ b/sdk/python/examples/controls/column/scroll_to_key/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-scroll-to-key" +version = "1.0.0" +description = "Scroll a column to keyed sections using action buttons." +requires-python = ">=3.10" +keywords = ["column", "layout", "scroll_to", "scroll key", "sections"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column scroll to key" +controls = ["Container", "Column", "Row", "Button", "Text"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["scroll key navigation", "async button actions", "section anchors"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/spacing.py b/sdk/python/examples/controls/column/spacing.py deleted file mode 100644 index 6baac249f7..0000000000 --- a/sdk/python/examples/controls/column/spacing.py +++ /dev/null @@ -1,43 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def generate_items(count: int): - """Generates a list of custom Containers with length `count`.""" - return [ - ft.Container( - content=ft.Text(value=str(i)), - alignment=ft.Alignment.CENTER, - width=50, - height=50, - bgcolor=ft.Colors.AMBER, - border_radius=ft.BorderRadius.all(5), - ) - for i in range(1, count + 1) - ] - - def handle_slider_change(e: ft.Event[ft.Slider]): - """Updates the spacing between items based on slider value.""" - column.spacing = int(e.control.value) - column.update() - - page.add( - ft.Column( - controls=[ - ft.Text("Spacing between items"), - ft.Slider( - min=0, - max=100, - divisions=10, - value=0, - label="{value}", - width=500, - on_change=handle_slider_change, - ), - ] - ), - column := ft.Column(spacing=0, controls=generate_items(5)), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/spacing/main.py b/sdk/python/examples/controls/column/spacing/main.py new file mode 100644 index 0000000000..aa3bc60204 --- /dev/null +++ b/sdk/python/examples/controls/column/spacing/main.py @@ -0,0 +1,50 @@ +import flet as ft + + +def main(page: ft.Page): + def generate_items(count: int): + """Generates a list of custom Containers with length `count`.""" + return [ + ft.Container( + content=ft.Text(value=str(i)), + alignment=ft.Alignment.CENTER, + width=50, + height=50, + bgcolor=ft.Colors.AMBER, + border_radius=ft.BorderRadius.all(5), + ) + for i in range(1, count + 1) + ] + + def handle_slider_change(e: ft.Event[ft.Slider]): + """Updates the spacing between items based on slider value.""" + column.spacing = int(e.control.value) + column.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + controls=[ + ft.Text("Spacing between items"), + ft.Slider( + min=0, + max=100, + divisions=10, + value=0, + label="{value}", + width=500, + on_change=handle_slider_change, + ), + ] + ), + column := ft.Column(spacing=0, controls=generate_items(5)), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/spacing/pyproject.toml b/sdk/python/examples/controls/column/spacing/pyproject.toml new file mode 100644 index 0000000000..273a2a7d02 --- /dev/null +++ b/sdk/python/examples/controls/column/spacing/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-spacing" +version = "1.0.0" +description = "Adjust column spacing interactively with a slider." +requires-python = ">=3.10" +keywords = ["column", "layout", "spacing", "slider", "interactive"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column spacing" +controls = ["Column", "Container", "Text", "Slider"] +layout_pattern = "control-panel" +complexity = "basic" +features = ["slider-driven spacing", "dynamic layout updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/column/wrap.py b/sdk/python/examples/controls/column/wrap.py deleted file mode 100644 index f14daa0e4f..0000000000 --- a/sdk/python/examples/controls/column/wrap.py +++ /dev/null @@ -1,56 +0,0 @@ -import flet as ft - -HEIGHT = 400 - - -def main(page: ft.Page): - def items(count: int): - return [ - ft.Container( - content=ft.Text(value=str(i)), - alignment=ft.Alignment.CENTER, - width=30, - height=30, - bgcolor=ft.Colors.AMBER, - border_radius=ft.BorderRadius.all(5), - ) - for i in range(1, count + 1) - ] - - def handle_slider_change(e: ft.Event[ft.Slider]): - col.height = float(e.control.value) - col.update() - - page.add( - ft.Column( - controls=[ - ft.Text( - "Change the column height to see how child items wrap onto multiple columns:" - ), - ft.Slider( - min=0, - max=HEIGHT, - divisions=20, - value=HEIGHT, - label="{value}", - width=500, - on_change=handle_slider_change, - ), - ] - ), - ft.Container( - bgcolor=ft.Colors.TRANSPARENT, - content=( - col := ft.Column( - wrap=True, - spacing=10, - run_spacing=10, - controls=items(10), - height=HEIGHT, - ) - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/column/wrap/main.py b/sdk/python/examples/controls/column/wrap/main.py new file mode 100644 index 0000000000..b7e581963a --- /dev/null +++ b/sdk/python/examples/controls/column/wrap/main.py @@ -0,0 +1,64 @@ +import flet as ft + +HEIGHT = 400 + + +def main(page: ft.Page): + def items(count: int): + return [ + ft.Container( + content=ft.Text(value=str(i)), + alignment=ft.Alignment.CENTER, + width=30, + height=30, + bgcolor=ft.Colors.AMBER, + border_radius=ft.BorderRadius.all(5), + ) + for i in range(1, count + 1) + ] + + def handle_slider_change(e: ft.Event[ft.Slider]): + col.height = float(e.control.value) + col.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + controls=[ + ft.Text( + "Change the column height to see how child items " + "wrap onto multiple columns:" + ), + ft.Slider( + min=0, + max=HEIGHT, + divisions=20, + value=HEIGHT, + label="{value}", + width=500, + on_change=handle_slider_change, + ), + ] + ), + ft.Container( + bgcolor=ft.Colors.TRANSPARENT, + content=( + col := ft.Column( + wrap=True, + spacing=10, + run_spacing=10, + controls=items(10), + height=HEIGHT, + ) + ), + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/column/wrap/pyproject.toml b/sdk/python/examples/controls/column/wrap/pyproject.toml new file mode 100644 index 0000000000..45c393b6d2 --- /dev/null +++ b/sdk/python/examples/controls/column/wrap/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "column-wrap" +version = "1.0.0" +description = "Column wrapping behavior controlled by dynamic height." +requires-python = ">=3.10" +keywords = ["column", "layout", "wrap", "slider", "responsive"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Column"] + +[tool.flet.metadata] +title = "Column wrapping" +controls = ["Column", "Container", "Text", "Slider"] +layout_pattern = "control-panel" +complexity = "basic" +features = ["wrap behavior", "height-driven layout", "slider interaction"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/__init__.py b/sdk/python/examples/controls/container/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/container/animate_1.py b/sdk/python/examples/controls/container/animate_1.py deleted file mode 100644 index 46a5389955..0000000000 --- a/sdk/python/examples/controls/container/animate_1.py +++ /dev/null @@ -1,24 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate_container(e: ft.Event[ft.Button]): - container.width = 100 if container.width == 150 else 150 - container.height = 50 if container.height == 150 else 150 - container.bgcolor = ( - ft.Colors.BLUE if container.bgcolor == ft.Colors.RED else ft.Colors.RED - ) - container.update() - - page.add( - container := ft.Container( - width=150, - height=150, - bgcolor=ft.Colors.RED, - animate=ft.Animation(duration=1000, curve=ft.AnimationCurve.BOUNCE_OUT), - ), - ft.Button("Animate container", on_click=animate_container), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/container/animate_1/main.py b/sdk/python/examples/controls/container/animate_1/main.py new file mode 100644 index 0000000000..561c987a34 --- /dev/null +++ b/sdk/python/examples/controls/container/animate_1/main.py @@ -0,0 +1,33 @@ +import flet as ft + + +def main(page: ft.Page): + def animate_container(e: ft.Event[ft.Button]): + container.width = 100 if container.width == 150 else 150 + container.height = 50 if container.height == 150 else 150 + container.bgcolor = ( + ft.Colors.BLUE if container.bgcolor == ft.Colors.RED else ft.Colors.RED + ) + container.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + container := ft.Container( + width=150, + height=150, + bgcolor=ft.Colors.RED, + animate=ft.Animation( + duration=1000, curve=ft.AnimationCurve.BOUNCE_OUT + ), + ), + ft.Button("Animate container", on_click=animate_container), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/animate_1/pyproject.toml b/sdk/python/examples/controls/container/animate_1/pyproject.toml new file mode 100644 index 0000000000..699896a5b5 --- /dev/null +++ b/sdk/python/examples/controls/container/animate_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-animate-1" +version = "1.0.0" +description = "Animated container size and color transitions triggered by button clicks." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Animate 1" +controls = ["SafeArea", "Column", "Container", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["size animation", "color animation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/animate_2.py b/sdk/python/examples/controls/container/animate_2.py deleted file mode 100644 index d891b03259..0000000000 --- a/sdk/python/examples/controls/container/animate_2.py +++ /dev/null @@ -1,55 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - gradient1 = ft.LinearGradient( - begin=ft.Alignment.TOP_CENTER, - end=ft.Alignment.BOTTOM_CENTER, - colors=[ft.Colors.GREEN, ft.Colors.BLUE], - stops=[0.5, 1.0], - ) - gradient2 = ft.RadialGradient( - center=ft.Alignment.TOP_LEFT, - radius=1.0, - colors=[ft.Colors.YELLOW, ft.Colors.DEEP_ORANGE_900], - tile_mode=ft.GradientTileMode.CLAMP, - ) - - message = ft.Text("Animate me!") - - def animate_container(e: ft.Event[ft.Button]): - message.value = ( - "Animate me back!" if message.value == "Animate me!" else "Animate me!" - ) - container.width = 150 if container.width == 250 else 250 - container.height = 150 if container.height == 250 else 250 - container.gradient = gradient2 if container.gradient == gradient1 else gradient1 - if container.alignment == ft.Alignment.TOP_LEFT: - container.alignment = ft.Alignment.BOTTOM_RIGHT - else: - container.alignment = ft.Alignment.TOP_LEFT - container.border_radius = 30 if container.border_radius == 10 else 10 - container.border = ( - ft.Border.all(width=2, color=ft.Colors.BLACK) - if container.border == ft.Border.all(width=2, color=ft.Colors.BLUE) - else ft.Border.all(width=2, color=ft.Colors.BLUE) - ) - container.update() - - page.add( - container := ft.Container( - content=message, - width=250, - height=250, - gradient=gradient2, - alignment=ft.Alignment.TOP_LEFT, - animate=ft.Animation(duration=1000, curve=ft.AnimationCurve.BOUNCE_OUT), - border=ft.Border.all(width=2, color=ft.Colors.BLUE), - border_radius=10, - padding=10, - ), - ft.Button("Animate container", on_click=animate_container), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/container/animate_2/main.py b/sdk/python/examples/controls/container/animate_2/main.py new file mode 100644 index 0000000000..558639d188 --- /dev/null +++ b/sdk/python/examples/controls/container/animate_2/main.py @@ -0,0 +1,64 @@ +import flet as ft + + +def main(page: ft.Page): + gradient1 = ft.LinearGradient( + begin=ft.Alignment.TOP_CENTER, + end=ft.Alignment.BOTTOM_CENTER, + colors=[ft.Colors.GREEN, ft.Colors.BLUE], + stops=[0.5, 1.0], + ) + gradient2 = ft.RadialGradient( + center=ft.Alignment.TOP_LEFT, + radius=1.0, + colors=[ft.Colors.YELLOW, ft.Colors.DEEP_ORANGE_900], + tile_mode=ft.GradientTileMode.CLAMP, + ) + + message = ft.Text("Animate me!") + + def animate_container(e: ft.Event[ft.Button]): + message.value = ( + "Animate me back!" if message.value == "Animate me!" else "Animate me!" + ) + container.width = 150 if container.width == 250 else 250 + container.height = 150 if container.height == 250 else 250 + container.gradient = gradient2 if container.gradient == gradient1 else gradient1 + if container.alignment == ft.Alignment.TOP_LEFT: + container.alignment = ft.Alignment.BOTTOM_RIGHT + else: + container.alignment = ft.Alignment.TOP_LEFT + container.border_radius = 30 if container.border_radius == 10 else 10 + container.border = ( + ft.Border.all(width=2, color=ft.Colors.BLACK) + if container.border == ft.Border.all(width=2, color=ft.Colors.BLUE) + else ft.Border.all(width=2, color=ft.Colors.BLUE) + ) + container.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + container := ft.Container( + width=250, + height=250, + gradient=gradient2, + alignment=ft.Alignment.TOP_LEFT, + animate=ft.Animation( + duration=1000, curve=ft.AnimationCurve.BOUNCE_OUT + ), + border=ft.Border.all(width=2, color=ft.Colors.BLUE), + border_radius=10, + padding=10, + content=message, + ), + ft.Button("Animate container", on_click=animate_container), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/animate_2/pyproject.toml b/sdk/python/examples/controls/container/animate_2/pyproject.toml new file mode 100644 index 0000000000..299edf9de9 --- /dev/null +++ b/sdk/python/examples/controls/container/animate_2/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-animate-2" +version = "1.0.0" +description = "Animated container with gradient, border, alignment, and shape transitions." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Animate 2" +controls = ["SafeArea", "Column", "Container", "Button", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["gradient animation", "border animation", "alignment animation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/animate_3.py b/sdk/python/examples/controls/container/animate_3.py deleted file mode 100644 index cbcf1e1145..0000000000 --- a/sdk/python/examples/controls/container/animate_3.py +++ /dev/null @@ -1,24 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate(e: ft.Event[ft.Button]): - container.width = 100 if container.width == 150 else 150 - container.height = 50 if container.height == 150 else 150 - container.bgcolor = ( - ft.Colors.BLUE if container.bgcolor == ft.Colors.RED else ft.Colors.RED - ) - container.update() - - page.add( - container := ft.Container( - width=150, - height=150, - bgcolor=ft.Colors.RED, - animate=ft.Animation(1000, ft.AnimationCurve.BOUNCE_OUT), - ), - ft.Button("Animate container", on_click=animate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/container/animate_3/main.py b/sdk/python/examples/controls/container/animate_3/main.py new file mode 100644 index 0000000000..56ab86c552 --- /dev/null +++ b/sdk/python/examples/controls/container/animate_3/main.py @@ -0,0 +1,48 @@ +import flet as ft + + +def main(page: ft.Page): + def show_menu(e: ft.Event[ft.Button]): + container.offset = ft.Offset(0, 0) + container.update() + + def hide_menu(e: ft.Event[ft.IconButton]): + container.offset = ft.Offset(-2, 0) + container.update() + + page.overlay.append( + container := ft.Container( + left=10, + top=10, + width=200, + height=300, + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, + border_radius=5, + offset=ft.Offset(-2, 0), + animate_offset=ft.Animation(300, ft.AnimationCurve.EASE_IN), + content=ft.Column( + controls=[ + ft.Row( + alignment=ft.MainAxisAlignment.END, + controls=[ + ft.IconButton(icon=ft.Icons.CLOSE, on_click=hide_menu) + ], + ), + ft.ListTile( + title=ft.Text("Menu A"), + on_click=lambda _: print("Menu A clicked"), + ), + ft.ListTile( + title=ft.Text("Menu B"), + on_click=lambda _: print("Menu B clicked"), + ), + ] + ), + ) + ) + + page.add(ft.SafeArea(content=ft.Button("Show menu", on_click=show_menu))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/animate_3/pyproject.toml b/sdk/python/examples/controls/container/animate_3/pyproject.toml new file mode 100644 index 0000000000..a93d8a6de2 --- /dev/null +++ b/sdk/python/examples/controls/container/animate_3/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-animate-3" +version = "1.0.0" +description = "Slide-in side menu built with animated container offset and overlay controls." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Animate 3" +controls = ["SafeArea", "Button", "Container", "Column", "Row", "IconButton", "ListTile"] +layout_pattern = "overlay" +complexity = "basic" +features = ["overlay menu", "offset animation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/animate_4.py b/sdk/python/examples/controls/container/animate_4.py deleted file mode 100644 index 8d95d1ea5d..0000000000 --- a/sdk/python/examples/controls/container/animate_4.py +++ /dev/null @@ -1,47 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def show_menu(e: ft.Event[ft.Button]): - container.offset = ft.Offset(0, 0) - container.update() - - def hide_menu(e: ft.Event[ft.IconButton]): - container.offset = ft.Offset(-2, 0) - container.update() - - page.overlay.append( - container := ft.Container( - left=10, - top=10, - width=200, - height=300, - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, - border_radius=5, - offset=ft.Offset(-2, 0), - animate_offset=ft.Animation(300, ft.AnimationCurve.EASE_IN), - content=ft.Column( - controls=[ - ft.Row( - alignment=ft.MainAxisAlignment.END, - controls=[ - ft.IconButton(icon=ft.Icons.CLOSE, on_click=hide_menu) - ], - ), - ft.ListTile( - title=ft.Text("Menu A"), - on_click=lambda _: print("Menu A clicked"), - ), - ft.ListTile( - title=ft.Text("Menu B"), - on_click=lambda _: print("Menu B clicked"), - ), - ] - ), - ) - ) - - page.add(ft.Button("Show menu", on_click=show_menu)) - - -ft.run(main) diff --git a/sdk/python/examples/controls/container/clickable.py b/sdk/python/examples/controls/container/clickable.py deleted file mode 100644 index 793199cb2e..0000000000 --- a/sdk/python/examples/controls/container/clickable.py +++ /dev/null @@ -1,63 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Container Example" - page.theme_mode = ft.ThemeMode.LIGHT - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.add( - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Container( - content=ft.Text("Non clickable"), - margin=10, - padding=10, - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.AMBER, - width=150, - height=150, - border_radius=10, - ), - ft.Container( - content=ft.Text("Clickable without Ink"), - margin=10, - padding=10, - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.GREEN_200, - width=150, - height=150, - border_radius=10, - on_click=lambda e: print("Clickable without Ink clicked!"), - ), - ft.Container( - content=ft.Text("Clickable with Ink"), - margin=10, - padding=10, - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.CYAN_200, - width=150, - height=150, - border_radius=10, - ink=True, - on_click=lambda e: print("Clickable with Ink clicked!"), - ), - ft.Container( - content=ft.Text("Clickable transparent with Ink"), - margin=10, - padding=10, - alignment=ft.Alignment.CENTER, - width=150, - height=150, - border_radius=10, - ink=True, - on_click=lambda e: print("Clickable transparent with Ink clicked!"), - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/container/clickable/main.py b/sdk/python/examples/controls/container/clickable/main.py new file mode 100644 index 0000000000..9fa4d0824c --- /dev/null +++ b/sdk/python/examples/controls/container/clickable/main.py @@ -0,0 +1,68 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Container Example" + page.theme_mode = ft.ThemeMode.LIGHT + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.add( + ft.SafeArea( + content=ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Container( + margin=10, + padding=10, + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.AMBER, + width=150, + height=150, + border_radius=10, + content=ft.Text("Non clickable"), + ), + ft.Container( + margin=10, + padding=10, + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.GREEN_200, + width=150, + height=150, + border_radius=10, + on_click=lambda e: print("Clickable without Ink clicked!"), + content=ft.Text("Clickable without Ink"), + ), + ft.Container( + margin=10, + padding=10, + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.CYAN_200, + width=150, + height=150, + border_radius=10, + ink=True, + on_click=lambda e: print("Clickable with Ink clicked!"), + content=ft.Text("Clickable with Ink"), + ), + ft.Container( + margin=10, + padding=10, + alignment=ft.Alignment.CENTER, + width=150, + height=150, + border_radius=10, + ink=True, + on_click=lambda e: print( + "Clickable transparent with Ink clicked!" + ), + content=ft.Text("Clickable transparent with Ink"), + ), + ], + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/clickable/pyproject.toml b/sdk/python/examples/controls/container/clickable/pyproject.toml new file mode 100644 index 0000000000..8152121403 --- /dev/null +++ b/sdk/python/examples/controls/container/clickable/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-clickable" +version = "1.0.0" +description = "Container clickability showcase with non-clickable, clickable, and ink-enabled variants." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Clickable" +controls = ["SafeArea", "Row", "Container", "Text"] +layout_pattern = "row-showcase" +complexity = "basic" +features = ["click handling", "ink effect"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/handling_clicks.py b/sdk/python/examples/controls/container/handling_clicks.py deleted file mode 100644 index 8e0cfe6d54..0000000000 --- a/sdk/python/examples/controls/container/handling_clicks.py +++ /dev/null @@ -1,106 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - lp_counter = 0 - cl_counter = 0 - td_counter = 0 - - def on_click(e): - nonlocal cl_counter - cl_counter += 1 - t1.spans[-1] = ft.TextSpan( - text=f" {cl_counter} ", - style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), - ) - page.update() - - def on_long_press(e): - nonlocal lp_counter - lp_counter += 1 - t3.spans[-1] = ft.TextSpan( - text=f" {lp_counter} ", - style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), - ) - page.update() - - def on_tap_down(e): - nonlocal td_counter - td_counter += 1 - t2.spans[-1] = ft.TextSpan( - text=f" {td_counter} ", - style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), - ) - page.update() - - c = ft.Container( - bgcolor=ft.Colors.PINK_900, - alignment=ft.Alignment.CENTER, - padding=ft.Padding.all(10), - height=150, - width=150, - on_click=on_click, - on_long_press=on_long_press, - on_tap_down=on_tap_down, - content=ft.Text( - "Press Me!", - text_align=ft.TextAlign.CENTER, - style=ft.TextStyle( - size=30, - # weight=ft.FontWeight.BOLD, - foreground=ft.Paint( - color=ft.Colors.BLUE_700, - stroke_cap=ft.StrokeCap.BUTT, - stroke_width=2, - stroke_join=ft.StrokeJoin.BEVEL, - style=ft.PaintingStyle.STROKE, - ), - ), - theme_style=ft.TextThemeStyle.DISPLAY_MEDIUM, - ), - ) - t1 = ft.Text( - spans=[ - ft.TextSpan( - text="On Click", style=ft.TextStyle(size=16, weight=ft.FontWeight.BOLD) - ), - ft.TextSpan(text=" counter: ", style=ft.TextStyle(size=16, italic=True)), - ft.TextSpan( - text=f" {cl_counter} ", - style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), - ), - ] - ) - t2 = ft.Text( - spans=[ - ft.TextSpan( - text="Tap Down", style=ft.TextStyle(size=16, weight=ft.FontWeight.BOLD) - ), - ft.TextSpan(text=" counter: ", style=ft.TextStyle(size=16, italic=True)), - ft.TextSpan( - text=f" {td_counter} ", - style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), - ), - ] - ) - t3 = ft.Text( - spans=[ - ft.TextSpan( - text="Long Press", - style=ft.TextStyle(size=16, weight=ft.FontWeight.BOLD), - ), - ft.TextSpan(text=" counter: ", style=ft.TextStyle(size=16, italic=True)), - ft.TextSpan( - text=f" {lp_counter} ", - style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), - ), - ] - ) - - page.add(c, t1, t3, t2) - - -ft.run(main) diff --git a/sdk/python/examples/controls/container/handling_clicks/main.py b/sdk/python/examples/controls/container/handling_clicks/main.py new file mode 100644 index 0000000000..97eff47a73 --- /dev/null +++ b/sdk/python/examples/controls/container/handling_clicks/main.py @@ -0,0 +1,107 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + lp_counter = 0 + cl_counter = 0 + td_counter = 0 + + def on_click(e): + nonlocal cl_counter + cl_counter += 1 + t1.spans[-1] = ft.TextSpan( + text=f" {cl_counter} ", + style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), + ) + t1.update() + + def on_long_press(e): + nonlocal lp_counter + lp_counter += 1 + t3.spans[-1] = ft.TextSpan( + text=f" {lp_counter} ", + style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), + ) + t3.update() + + def on_tap_down(e): + nonlocal td_counter + td_counter += 1 + t2.spans[-1] = ft.TextSpan( + text=f" {td_counter} ", + style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), + ) + t2.update() + + c = ft.Container( + bgcolor=ft.Colors.PINK_900, + alignment=ft.Alignment.CENTER, + padding=ft.Padding.all(10), + height=150, + width=150, + on_click=on_click, + on_long_press=on_long_press, + on_tap_down=on_tap_down, + content=ft.Text( + "Press Me!", + text_align=ft.TextAlign.CENTER, + style=ft.TextStyle( + size=30, + # weight=ft.FontWeight.BOLD, + foreground=ft.Paint( + color=ft.Colors.BLUE_700, + stroke_cap=ft.StrokeCap.BUTT, + stroke_width=2, + stroke_join=ft.StrokeJoin.BEVEL, + style=ft.PaintingStyle.STROKE, + ), + ), + theme_style=ft.TextThemeStyle.DISPLAY_MEDIUM, + ), + ) + t1 = ft.Text( + spans=[ + ft.TextSpan( + text="On Click", style=ft.TextStyle(size=16, weight=ft.FontWeight.BOLD) + ), + ft.TextSpan(text=" counter: ", style=ft.TextStyle(size=16, italic=True)), + ft.TextSpan( + text=f" {cl_counter} ", + style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), + ), + ] + ) + t2 = ft.Text( + spans=[ + ft.TextSpan( + text="Tap Down", style=ft.TextStyle(size=16, weight=ft.FontWeight.BOLD) + ), + ft.TextSpan(text=" counter: ", style=ft.TextStyle(size=16, italic=True)), + ft.TextSpan( + text=f" {td_counter} ", + style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), + ), + ] + ) + t3 = ft.Text( + spans=[ + ft.TextSpan( + text="Long Press", + style=ft.TextStyle(size=16, weight=ft.FontWeight.BOLD), + ), + ft.TextSpan(text=" counter: ", style=ft.TextStyle(size=16, italic=True)), + ft.TextSpan( + text=f" {lp_counter} ", + style=ft.TextStyle(size=16, bgcolor=ft.Colors.TEAL_300), + ), + ] + ) + + page.add(ft.SafeArea(content=ft.Column(controls=[c, t1, t3, t2]))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/handling_clicks/pyproject.toml b/sdk/python/examples/controls/container/handling_clicks/pyproject.toml new file mode 100644 index 0000000000..221a4caddb --- /dev/null +++ b/sdk/python/examples/controls/container/handling_clicks/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-handling-clicks" +version = "1.0.0" +description = "Container gesture handling demo with click, tap-down, and long-press counters." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Handling clicks" +controls = ["SafeArea", "Column", "Container", "Text", "TextSpan"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["on_click", "on_tap_down", "on_long_press"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/handling_hovers.py b/sdk/python/examples/controls/container/handling_hovers.py deleted file mode 100644 index a53015a4ef..0000000000 --- a/sdk/python/examples/controls/container/handling_hovers.py +++ /dev/null @@ -1,20 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_hover(e: ft.Event[ft.Container]): - e.control.bgcolor = ft.Colors.BLUE if e.data else ft.Colors.RED - e.control.update() - - page.add( - ft.Container( - width=200, - height=200, - bgcolor=ft.Colors.RED, - ink=False, - on_hover=handle_hover, - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/container/handling_hovers/main.py b/sdk/python/examples/controls/container/handling_hovers/main.py new file mode 100644 index 0000000000..f054fb970a --- /dev/null +++ b/sdk/python/examples/controls/container/handling_hovers/main.py @@ -0,0 +1,23 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_hover(e: ft.Event[ft.Container]): + e.control.bgcolor = ft.Colors.BLUE if e.data else ft.Colors.RED + e.control.update() + + page.add( + ft.SafeArea( + content=ft.Container( + width=200, + height=200, + bgcolor=ft.Colors.RED, + ink=False, + on_hover=handle_hover, + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/handling_hovers/pyproject.toml b/sdk/python/examples/controls/container/handling_hovers/pyproject.toml new file mode 100644 index 0000000000..2c34612d4d --- /dev/null +++ b/sdk/python/examples/controls/container/handling_hovers/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-handling-hovers" +version = "1.0.0" +description = "Container hover example that changes background color on pointer enter and leave." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Handling hovers" +controls = ["SafeArea", "Container"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["hover handling", "dynamic styling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/nested_themes_1.py b/sdk/python/examples/controls/container/nested_themes_1.py deleted file mode 100644 index 53dc21bb79..0000000000 --- a/sdk/python/examples/controls/container/nested_themes_1.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - # Yellow page theme with SYSTEM (default) mode - page.theme = ft.Theme( - color_scheme_seed=ft.Colors.YELLOW, - ) - - page.add( - # Page theme - ft.Container( - content=ft.Button("Page theme button"), - bgcolor=ft.Colors.SURFACE_TINT, - padding=20, - width=300, - ), - # Inherited theme with primary color overridden - ft.Container( - theme=ft.Theme(color_scheme=ft.ColorScheme(primary=ft.Colors.PINK)), - content=ft.Button("Inherited theme button"), - bgcolor=ft.Colors.SURFACE_TINT, - padding=20, - width=300, - ), - # Unique always DARK theme - ft.Container( - theme=ft.Theme(color_scheme_seed=ft.Colors.INDIGO), - theme_mode=ft.ThemeMode.DARK, - content=ft.Button("Unique theme button"), - bgcolor=ft.Colors.SURFACE_TINT, - padding=20, - width=300, - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/container/nested_themes_1/main.py b/sdk/python/examples/controls/container/nested_themes_1/main.py new file mode 100644 index 0000000000..18669a08c7 --- /dev/null +++ b/sdk/python/examples/controls/container/nested_themes_1/main.py @@ -0,0 +1,47 @@ +import flet as ft + + +def main(page: ft.Page): + # Yellow page theme with SYSTEM (default) mode + page.theme = ft.Theme( + color_scheme_seed=ft.Colors.YELLOW, + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + # Page theme + ft.Container( + bgcolor=ft.Colors.SURFACE_TINT, + padding=20, + width=300, + content=ft.Button("Page theme button"), + ), + # Inherited theme with primary color overridden + ft.Container( + theme=ft.Theme( + color_scheme=ft.ColorScheme(primary=ft.Colors.PINK) + ), + bgcolor=ft.Colors.SURFACE_TINT, + padding=20, + width=300, + content=ft.Button("Inherited theme button"), + ), + # Unique always DARK theme + ft.Container( + theme=ft.Theme(color_scheme_seed=ft.Colors.INDIGO), + theme_mode=ft.ThemeMode.DARK, + bgcolor=ft.Colors.SURFACE_TINT, + padding=20, + width=300, + content=ft.Button("Unique theme button"), + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/nested_themes_1/pyproject.toml b/sdk/python/examples/controls/container/nested_themes_1/pyproject.toml new file mode 100644 index 0000000000..73c0393525 --- /dev/null +++ b/sdk/python/examples/controls/container/nested_themes_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-nested-themes-1" +version = "1.0.0" +description = "Nested theme containers demonstrating inherited and overridden button color schemes." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Nested themes 1" +controls = ["SafeArea", "Column", "Container", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["nested themes", "theme override"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/nested_themes_2.py b/sdk/python/examples/controls/container/nested_themes_2.py deleted file mode 100644 index dc5fcfa828..0000000000 --- a/sdk/python/examples/controls/container/nested_themes_2.py +++ /dev/null @@ -1,73 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - # page.theme = ft.Theme( - # color_scheme_seed=ft.Colors.YELLOW, - # color_scheme=ft.ColorScheme( - # primary=ft.Colors.GREEN, primary_container=ft.Colors.GREEN_200 - # ), - # ) - - page.add( - ft.Row( - controls=[ - ft.Button("Page theme"), - ft.TextButton("Page theme text button"), - ft.Text( - "Text in primary container color", - color=ft.Colors.PRIMARY_CONTAINER, - ), - ] - ), - ft.Container( - height=100, - theme=ft.Theme(color_scheme=ft.ColorScheme(primary=ft.Colors.PINK)), - content=ft.Row( - controls=[ - ft.Button("Inherited theme with primary color overridden"), - ft.TextButton("Button 2"), - ] - ), - ), - ft.Container( - padding=20, - bgcolor=ft.Colors.SURFACE_TINT, - theme_mode=ft.ThemeMode.DARK, - theme=ft.Theme( - color_scheme_seed=ft.Colors.GREEN, - color_scheme=ft.ColorScheme(primary_container=ft.Colors.BLUE), - ), - content=ft.Row( - controls=[ - ft.Button("Always DARK theme"), - ft.TextButton("Text button"), - ft.Text( - "Text in primary container color", - color=ft.Colors.PRIMARY_CONTAINER, - ), - ] - ), - ), - ft.Container( - padding=20, - bgcolor=ft.Colors.SURFACE_TINT, - border=ft.Border.all(3, ft.Colors.OUTLINE), - theme_mode=ft.ThemeMode.LIGHT, - theme=ft.Theme(), - content=ft.Row( - controls=[ - ft.Button("Always LIGHT theme"), - ft.TextButton("Text button"), - ft.Text( - "Text in primary container color", - color=ft.Colors.PRIMARY_CONTAINER, - ), - ] - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/container/nested_themes_2/main.py b/sdk/python/examples/controls/container/nested_themes_2/main.py new file mode 100644 index 0000000000..e32cf9ad85 --- /dev/null +++ b/sdk/python/examples/controls/container/nested_themes_2/main.py @@ -0,0 +1,85 @@ +import flet as ft + + +def main(page: ft.Page): + # page.theme = ft.Theme( + # color_scheme_seed=ft.Colors.YELLOW, + # color_scheme=ft.ColorScheme( + # primary=ft.Colors.GREEN, primary_container=ft.Colors.GREEN_200 + # ), + # ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + controls=[ + ft.Button("Page theme"), + ft.TextButton("Page theme text button"), + ft.Text( + "Text in primary container color", + color=ft.Colors.PRIMARY_CONTAINER, + ), + ] + ), + ft.Container( + height=100, + theme=ft.Theme( + color_scheme=ft.ColorScheme(primary=ft.Colors.PINK) + ), + content=ft.Row( + controls=[ + ft.Button( + "Inherited theme with primary color overridden" + ), + ft.TextButton("Button 2"), + ] + ), + ), + ft.Container( + padding=20, + bgcolor=ft.Colors.SURFACE_TINT, + theme_mode=ft.ThemeMode.DARK, + theme=ft.Theme( + color_scheme_seed=ft.Colors.GREEN, + color_scheme=ft.ColorScheme( + primary_container=ft.Colors.BLUE + ), + ), + content=ft.Row( + controls=[ + ft.Button("Always DARK theme"), + ft.TextButton("Text button"), + ft.Text( + "Text in primary container color", + color=ft.Colors.PRIMARY_CONTAINER, + ), + ] + ), + ), + ft.Container( + padding=20, + bgcolor=ft.Colors.SURFACE_TINT, + border=ft.Border.all(3, ft.Colors.OUTLINE), + theme_mode=ft.ThemeMode.LIGHT, + theme=ft.Theme(), + content=ft.Row( + controls=[ + ft.Button("Always LIGHT theme"), + ft.TextButton("Text button"), + ft.Text( + "Text in primary container color", + color=ft.Colors.PRIMARY_CONTAINER, + ), + ] + ), + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/nested_themes_2/pyproject.toml b/sdk/python/examples/controls/container/nested_themes_2/pyproject.toml new file mode 100644 index 0000000000..9ff856560c --- /dev/null +++ b/sdk/python/examples/controls/container/nested_themes_2/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-nested-themes-2" +version = "1.0.0" +description = "Multiple nested containers comparing page, dark, and light theme behaviors." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Nested themes 2" +controls = ["SafeArea", "Column", "Row", "Container", "Button", "TextButton", "Text"] +layout_pattern = "stacked-sections" +complexity = "basic" +features = ["nested themes", "theme mode override"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/nested_themes_3.py b/sdk/python/examples/controls/container/nested_themes_3.py deleted file mode 100644 index 42f191ce57..0000000000 --- a/sdk/python/examples/controls/container/nested_themes_3.py +++ /dev/null @@ -1,59 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.DARK - - def handle_switch_change(e: ft.Event[ft.Switch]): - if page.theme_mode == ft.ThemeMode.DARK: - page.theme_mode = ft.ThemeMode.LIGHT - switch.thumb_icon = ft.Icons.LIGHT_MODE - else: - switch.thumb_icon = ft.Icons.DARK_MODE - page.theme_mode = ft.ThemeMode.DARK - page.update() - - # Yellow page theme with SYSTEM (default) mode - page.theme = ft.Theme(color_scheme_seed=ft.Colors.YELLOW) - - switch = ft.Switch(thumb_icon=ft.Icons.DARK_MODE, on_change=handle_switch_change) - - page.add( - # Page theme - ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - controls=[ - ft.Container( - content=ft.Button("Page theme button"), - bgcolor=ft.Colors.SURFACE_TINT, - padding=20, - width=300, - ), - ft.Container( - content=switch, - padding=ft.Padding.only(bottom=50), - alignment=ft.Alignment.TOP_RIGHT, - ), - ], - ), - # Inherited theme with primary color overridden - ft.Container( - theme=ft.Theme(color_scheme=ft.ColorScheme(primary=ft.Colors.PINK)), - content=ft.Button("Inherited theme button"), - bgcolor=ft.Colors.SURFACE_TINT, - padding=20, - width=300, - ), - # Unique always DARK theme - ft.Container( - theme=ft.Theme(color_scheme_seed=ft.Colors.INDIGO), - theme_mode=ft.ThemeMode.DARK, - content=ft.Button("Unique theme button"), - bgcolor=ft.Colors.SURFACE_TINT, - padding=20, - width=300, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/container/nested_themes_3/main.py b/sdk/python/examples/controls/container/nested_themes_3/main.py new file mode 100644 index 0000000000..a10153dc68 --- /dev/null +++ b/sdk/python/examples/controls/container/nested_themes_3/main.py @@ -0,0 +1,68 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.DARK + + def handle_switch_change(e: ft.Event[ft.Switch]): + if page.theme_mode == ft.ThemeMode.DARK: + page.theme_mode = ft.ThemeMode.LIGHT + switch.thumb_icon = ft.Icons.LIGHT_MODE + else: + switch.thumb_icon = ft.Icons.DARK_MODE + page.theme_mode = ft.ThemeMode.DARK + page.update() + + # Yellow page theme with SYSTEM (default) mode + page.theme = ft.Theme(color_scheme_seed=ft.Colors.YELLOW) + + switch = ft.Switch(thumb_icon=ft.Icons.DARK_MODE, on_change=handle_switch_change) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + # Page theme + ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + controls=[ + ft.Container( + bgcolor=ft.Colors.SURFACE_TINT, + padding=20, + width=300, + content=ft.Button("Page theme button"), + ), + ft.Container( + padding=ft.Padding.only(bottom=50), + alignment=ft.Alignment.TOP_RIGHT, + content=switch, + ), + ], + ), + # Inherited theme with primary color overridden + ft.Container( + theme=ft.Theme( + color_scheme=ft.ColorScheme(primary=ft.Colors.PINK) + ), + bgcolor=ft.Colors.SURFACE_TINT, + padding=20, + width=300, + content=ft.Button("Inherited theme button"), + ), + # Unique always DARK theme + ft.Container( + theme=ft.Theme(color_scheme_seed=ft.Colors.INDIGO), + theme_mode=ft.ThemeMode.DARK, + bgcolor=ft.Colors.SURFACE_TINT, + padding=20, + width=300, + content=ft.Button("Unique theme button"), + ), + ] + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/nested_themes_3/pyproject.toml b/sdk/python/examples/controls/container/nested_themes_3/pyproject.toml new file mode 100644 index 0000000000..78d8336e09 --- /dev/null +++ b/sdk/python/examples/controls/container/nested_themes_3/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-nested-themes-3" +version = "1.0.0" +description = "Theme toggle example using a switch to change page mode across nested containers." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Nested themes 3" +controls = ["SafeArea", "Column", "Row", "Container", "Button", "Switch"] +layout_pattern = "stacked-sections" +complexity = "basic" +features = ["theme mode toggle", "nested themes"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/container/size_aware.py b/sdk/python/examples/controls/container/size_aware.py deleted file mode 100644 index 5268c3f786..0000000000 --- a/sdk/python/examples/controls/container/size_aware.py +++ /dev/null @@ -1,21 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_size_change(e: ft.LayoutSizeChangeEvent[ft.Container]): - e.control.content.value = f"{int(e.width)} x {int(e.height)}" - - page.add( - ft.Container( - expand=True, - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.BLUE_ACCENT, - content=ft.Text(color=ft.Colors.WHITE, weight=ft.FontWeight.BOLD), - size_change_interval=100, - on_size_change=handle_size_change, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/container/size_aware/main.py b/sdk/python/examples/controls/container/size_aware/main.py new file mode 100644 index 0000000000..3be1915288 --- /dev/null +++ b/sdk/python/examples/controls/container/size_aware/main.py @@ -0,0 +1,24 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_size_change(e: ft.LayoutSizeChangeEvent[ft.Container]): + e.control.content.value = f"{int(e.width)} x {int(e.height)}" + + page.add( + ft.SafeArea( + expand=True, + content=ft.Container( + expand=True, + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.BLUE_ACCENT, + size_change_interval=100, + on_size_change=handle_size_change, + content=ft.Text(color=ft.Colors.WHITE, weight=ft.FontWeight.BOLD), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/container/size_aware/pyproject.toml b/sdk/python/examples/controls/container/size_aware/pyproject.toml new file mode 100644 index 0000000000..3f543b3ea3 --- /dev/null +++ b/sdk/python/examples/controls/container/size_aware/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "container-size-aware" +version = "1.0.0" +description = "Size-aware container displaying live width and height updates on resize events." +requires-python = ">=3.10" +keywords = ["container", "layout", "flet"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Container"] + +[tool.flet.metadata] +title = "Size aware" +controls = ["SafeArea", "Container", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["on_size_change", "responsive text"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/context_menu/__init__.py b/sdk/python/examples/controls/context_menu/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/context_menu/custom_trigger.py b/sdk/python/examples/controls/context_menu/custom_trigger.py deleted file mode 100644 index e433df487b..0000000000 --- a/sdk/python/examples/controls/context_menu/custom_trigger.py +++ /dev/null @@ -1,42 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - async def open_menu(e: ft.TapEvent[ft.GestureDetector]): - await menu.open( - local_position=e.local_position, - global_position=e.global_position, - ) - - page.add( - menu := ft.ContextMenu( - expand=True, - items=[ - ft.PopupMenuItem( - content="Cut", - on_click=lambda e: print(f"{e.control.content}"), - ), - ft.PopupMenuItem( - content="Copy", - on_click=lambda e: print(f"{e.control.content}"), - ), - ft.PopupMenuItem( - content="Paste", - on_click=lambda e: print(f"{e.control.content}"), - ), - ], - content=ft.GestureDetector( - expand=True, - on_double_tap_down=open_menu, - content=ft.Container( - content=ft.Text("Double-click to open the context menu."), - bgcolor=ft.Colors.BLUE, - alignment=ft.Alignment.CENTER, - ), - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/context_menu/custom_trigger/main.py b/sdk/python/examples/controls/context_menu/custom_trigger/main.py new file mode 100644 index 0000000000..92cae28bf2 --- /dev/null +++ b/sdk/python/examples/controls/context_menu/custom_trigger/main.py @@ -0,0 +1,47 @@ +import flet as ft + + +def main(page: ft.Page): + async def open_menu(e: ft.TapEvent[ft.GestureDetector]): + await menu.open( + local_position=e.local_position, + global_position=e.global_position, + ) + + menu = ft.ContextMenu( + expand=True, + items=[ + ft.PopupMenuItem( + content="Cut", + on_click=lambda e: print(f"{e.control.content}"), + ), + ft.PopupMenuItem( + content="Copy", + on_click=lambda e: print(f"{e.control.content}"), + ), + ft.PopupMenuItem( + content="Paste", + on_click=lambda e: print(f"{e.control.content}"), + ), + ], + content=ft.GestureDetector( + expand=True, + on_double_tap_down=open_menu, + content=ft.Container( + bgcolor=ft.Colors.BLUE, + alignment=ft.Alignment.CENTER, + content=ft.Text("Double-click to open the context menu."), + ), + ), + ) + + page.add( + ft.SafeArea( + expand=True, + content=menu, + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/context_menu/custom_trigger/pyproject.toml b/sdk/python/examples/controls/context_menu/custom_trigger/pyproject.toml new file mode 100644 index 0000000000..9ef4f4a086 --- /dev/null +++ b/sdk/python/examples/controls/context_menu/custom_trigger/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "context-menu-custom-trigger" +version = "1.0.0" +description = "Opens ContextMenu from a custom double-tap gesture with precise pointer position." +requires-python = ">=3.10" +keywords = ["context menu", "custom trigger", "gesture", "double tap"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/ContextMenu"] + +[tool.flet.metadata] +title = "Custom trigger" +controls = ["SafeArea", "ContextMenu", "PopupMenuItem", "GestureDetector", "Container", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["custom open trigger", "position-based menu opening"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/context_menu/programmatic_open.py b/sdk/python/examples/controls/context_menu/programmatic_open.py deleted file mode 100644 index 62e63e266c..0000000000 --- a/sdk/python/examples/controls/context_menu/programmatic_open.py +++ /dev/null @@ -1,38 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - def handle_select(e: ft.ContextMenuSelectEvent): - action = e.item.content - page.show_dialog(ft.SnackBar(f"Item '{action}' selected.")) - - async def open_menu(e: ft.Event[ft.Button]): - await menu.open() - - page.add( - menu := ft.ContextMenu( - on_select=handle_select, - content=ft.Button("Click to open menu", on_click=open_menu), - items=[ - ft.PopupMenuItem( - content="Item 1", - on_click=lambda e: print(f"{e.control.content}"), - ), - ft.PopupMenuItem( - content="Item 2", - on_click=lambda e: print(f"{e.control.content}"), - ), - ft.PopupMenuItem( - content="Item 3", - on_click=lambda e: print(f"{e.control.content}"), - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/context_menu/programmatic_open/main.py b/sdk/python/examples/controls/context_menu/programmatic_open/main.py new file mode 100644 index 0000000000..a9c02c06cf --- /dev/null +++ b/sdk/python/examples/controls/context_menu/programmatic_open/main.py @@ -0,0 +1,42 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + def handle_select(e: ft.ContextMenuSelectEvent): + action = e.item.content + page.show_dialog(ft.SnackBar(f"Item '{action}' selected.")) + + async def open_menu(e: ft.Event[ft.Button]): + await menu.open() + + menu = ft.ContextMenu( + on_select=handle_select, + items=[ + ft.PopupMenuItem( + content="Item 1", + on_click=lambda e: print(f"{e.control.content}"), + ), + ft.PopupMenuItem( + content="Item 2", + on_click=lambda e: print(f"{e.control.content}"), + ), + ft.PopupMenuItem( + content="Item 3", + on_click=lambda e: print(f"{e.control.content}"), + ), + ], + content=ft.Button("Click to open menu", on_click=open_menu), + ) + + page.add( + ft.SafeArea( + content=menu, + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/context_menu/programmatic_open/pyproject.toml b/sdk/python/examples/controls/context_menu/programmatic_open/pyproject.toml new file mode 100644 index 0000000000..d879bdad71 --- /dev/null +++ b/sdk/python/examples/controls/context_menu/programmatic_open/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "context-menu-programmatic-open" +version = "1.0.0" +description = "Programmatically opens ContextMenu from a button and handles selected menu actions." +requires-python = ">=3.10" +keywords = ["context menu", "programmatic open", "popup menu", "selection"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/ContextMenu"] + +[tool.flet.metadata] +title = "Programmatic open" +controls = ["SafeArea", "ContextMenu", "PopupMenuItem", "Button", "SnackBar"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["programmatic menu opening", "selection callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/context_menu/triggers.py b/sdk/python/examples/controls/context_menu/triggers.py deleted file mode 100644 index 9ad3e619fb..0000000000 --- a/sdk/python/examples/controls/context_menu/triggers.py +++ /dev/null @@ -1,45 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - # on web, disable default browser context menu - if page.web: - await page.browser_context_menu.disable() - - def handle_item_click(e: ft.Event[ft.PopupMenuItem]): - action = e.control.content - page.show_dialog(ft.SnackBar(content=f"Item '{action}' selected.")) - - page.add( - ft.ContextMenu( - primary_items=[ - ft.PopupMenuItem(content="Primary 1", on_click=handle_item_click), - ft.PopupMenuItem(content="Primary 2", on_click=handle_item_click), - ], - primary_trigger=ft.ContextMenuTrigger.DOWN, - secondary_items=[ - ft.PopupMenuItem(content="Secondary 1", on_click=handle_item_click), - ft.PopupMenuItem(content="Secondary 2", on_click=handle_item_click), - ], - secondary_trigger=ft.ContextMenuTrigger.DOWN, - tertiary_items=[ - ft.PopupMenuItem(content="Tertiary 1", on_click=handle_item_click), - ft.PopupMenuItem(content="Tertiary 2", on_click=handle_item_click), - ], - tertiary_trigger=ft.ContextMenuTrigger.DOWN, - on_select=lambda e: print(f"Selected item: {e.item.content}"), - on_dismiss=lambda e: print("Menu dismissed"), - expand=True, - content=ft.Container( - expand=True, - bgcolor=ft.Colors.BLUE, - alignment=ft.Alignment.CENTER, - border_radius=ft.BorderRadius.all(12), - content=ft.Text("Left/middle/right click to open a context menu."), - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/context_menu/triggers/main.py b/sdk/python/examples/controls/context_menu/triggers/main.py new file mode 100644 index 0000000000..0fac275403 --- /dev/null +++ b/sdk/python/examples/controls/context_menu/triggers/main.py @@ -0,0 +1,48 @@ +import flet as ft + + +async def main(page: ft.Page): + # on web, disable default browser context menu + if page.web: + await page.browser_context_menu.disable() + + def handle_item_click(e: ft.Event[ft.PopupMenuItem]): + action = e.control.content + page.show_dialog(ft.SnackBar(content=f"Item '{action}' selected.")) + + page.add( + ft.SafeArea( + expand=True, + content=ft.ContextMenu( + primary_items=[ + ft.PopupMenuItem(content="Primary 1", on_click=handle_item_click), + ft.PopupMenuItem(content="Primary 2", on_click=handle_item_click), + ], + primary_trigger=ft.ContextMenuTrigger.DOWN, + secondary_items=[ + ft.PopupMenuItem(content="Secondary 1", on_click=handle_item_click), + ft.PopupMenuItem(content="Secondary 2", on_click=handle_item_click), + ], + secondary_trigger=ft.ContextMenuTrigger.DOWN, + tertiary_items=[ + ft.PopupMenuItem(content="Tertiary 1", on_click=handle_item_click), + ft.PopupMenuItem(content="Tertiary 2", on_click=handle_item_click), + ], + tertiary_trigger=ft.ContextMenuTrigger.DOWN, + on_select=lambda e: print(f"Selected item: {e.item.content}"), + on_dismiss=lambda e: print("Menu dismissed"), + expand=True, + content=ft.Container( + expand=True, + bgcolor=ft.Colors.BLUE, + alignment=ft.Alignment.CENTER, + border_radius=ft.BorderRadius.all(12), + content=ft.Text("Left/middle/right click to open a context menu."), + ), + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/context_menu/triggers/pyproject.toml b/sdk/python/examples/controls/context_menu/triggers/pyproject.toml new file mode 100644 index 0000000000..1a54126e82 --- /dev/null +++ b/sdk/python/examples/controls/context_menu/triggers/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "context-menu-triggers" +version = "1.0.0" +description = "ContextMenu trigger demo with separate primary, secondary, and tertiary click actions." +requires-python = ">=3.10" +keywords = ["context menu", "triggers", "popup menu", "mouse buttons"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/ContextMenu"] + +[tool.flet.metadata] +title = "Triggers" +controls = ["SafeArea", "ContextMenu", "PopupMenuItem", "Container", "Text", "SnackBar"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["multi-trigger menus", "item selection callback", "dismiss callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/control/__init__.py b/sdk/python/examples/controls/control/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/control/expand_loose_chat_messages.py b/sdk/python/examples/controls/control/expand_loose_chat_messages.py deleted file mode 100644 index 4b67623693..0000000000 --- a/sdk/python/examples/controls/control/expand_loose_chat_messages.py +++ /dev/null @@ -1,88 +0,0 @@ -from dataclasses import field - -import flet as ft - - -@ft.control -class Message(ft.Container): - author: str = "" - body: str = "" - border: ft.Border = field(default_factory=lambda: ft.Border.all(1, ft.Colors.BLACK)) - border_radius: ft.BorderRadius = field( - default_factory=lambda: ft.BorderRadius.all(10) - ) - bgcolor: ft.Colors = ft.Colors.GREEN_200 - padding: ft.PaddingValue = 10 - expand: bool = True - expand_loose: bool = True - - def init(self): - self.content = ft.Column( - controls=[ - ft.Text(self.author, weight=ft.FontWeight.BOLD), - ft.Text(self.body), - ], - ) - - -def main(page: ft.Page): - chat = ft.ListView( - padding=10, - spacing=10, - controls=[ - ft.Row( - alignment=ft.MainAxisAlignment.START, - controls=[ - Message( - author="John", - body="Hi, how are you?", - ), - ], - ), - ft.Row( - alignment=ft.MainAxisAlignment.END, - controls=[ - Message( - author="Jake", - body="Hi I am good thanks, how about you?", - ), - ], - ), - ft.Row( - alignment=ft.MainAxisAlignment.START, - controls=[ - Message( - author="John", - body=( - "Lorem Ipsum is simply dummy text of the printing and " - "typesetting industry. Lorem Ipsum has been the industry's " - "standard dummy text ever since the 1500s, when an unknown " - "printer took a galley of type and scrambled it to make a " - "type specimen book." - ), - ), - ], - ), - ft.Row( - alignment=ft.MainAxisAlignment.END, - controls=[ - Message( - author="Jake", - body="Thank you!", - ), - ], - ), - ], - ) - - page.add( - ft.Container( - content=chat, - width=300, - height=500, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/control/expand_loose_chat_messages/main.py b/sdk/python/examples/controls/control/expand_loose_chat_messages/main.py new file mode 100644 index 0000000000..2c23e6dca7 --- /dev/null +++ b/sdk/python/examples/controls/control/expand_loose_chat_messages/main.py @@ -0,0 +1,90 @@ +from dataclasses import field + +import flet as ft + + +@ft.control +class Message(ft.Container): + author: str = "" + body: str = "" + border: ft.Border = field(default_factory=lambda: ft.Border.all(1, ft.Colors.BLACK)) + border_radius: ft.BorderRadius = field( + default_factory=lambda: ft.BorderRadius.all(10) + ) + bgcolor: ft.Colors = ft.Colors.GREEN_200 + padding: ft.PaddingValue = 10 + expand: bool = True + expand_loose: bool = True + + def init(self): + self.content = ft.Column( + controls=[ + ft.Text(self.author, weight=ft.FontWeight.BOLD), + ft.Text(self.body), + ], + ) + + +def main(page: ft.Page): + chat = ft.ListView( + padding=10, + spacing=10, + controls=[ + ft.Row( + alignment=ft.MainAxisAlignment.START, + controls=[ + Message( + author="John", + body="Hi, how are you?", + ), + ], + ), + ft.Row( + alignment=ft.MainAxisAlignment.END, + controls=[ + Message( + author="Jake", + body="Hi I am good thanks, how about you?", + ), + ], + ), + ft.Row( + alignment=ft.MainAxisAlignment.START, + controls=[ + Message( + author="John", + body=( + "Lorem Ipsum is simply dummy text of the printing and " + "typesetting industry. Lorem Ipsum has been the industry's " + "standard dummy text ever since the 1500s, when an unknown " + "printer took a galley of type and scrambled it to make a " + "type specimen book." + ), + ), + ], + ), + ft.Row( + alignment=ft.MainAxisAlignment.END, + controls=[ + Message( + author="Jake", + body="Thank you!", + ), + ], + ), + ], + ) + + page.add( + ft.SafeArea( + content=ft.Container( + width=300, + height=500, + content=chat, + ) + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/control/expand_loose_chat_messages/pyproject.toml b/sdk/python/examples/controls/control/expand_loose_chat_messages/pyproject.toml new file mode 100644 index 0000000000..027409c582 --- /dev/null +++ b/sdk/python/examples/controls/control/expand_loose_chat_messages/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "control-expand-loose-chat-messages" +version = "1.0.0" +description = "Chat message layout demo using expand_loose for flexible message bubble sizing in rows." +requires-python = ">=3.10" +keywords = ["control", "expand_loose", "chat", "row", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Control"] + +[tool.flet.metadata] +title = "Expand loose chat messages" +controls = ["SafeArea", "Container", "ListView", "Row", "Text", "Column"] +layout_pattern = "chat" +complexity = "basic" +features = ["expand_loose behavior", "chat message bubbles"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/control/expand_row_equal_split.py b/sdk/python/examples/controls/control/expand_row_equal_split.py deleted file mode 100644 index 02ef2de503..0000000000 --- a/sdk/python/examples/controls/control/expand_row_equal_split.py +++ /dev/null @@ -1,36 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Container( - width=500, - height=180, - padding=10, - border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), - border_radius=10, - content=ft.Row( - spacing=8, - controls=[ - ft.Container( - expand=True, - bgcolor=ft.Colors.ORANGE_300, - border_radius=8, - alignment=ft.Alignment.CENTER, - content=ft.Text("Card 1"), - ), - ft.Container( - expand=True, - bgcolor=ft.Colors.GREEN_200, - border_radius=8, - alignment=ft.Alignment.CENTER, - content=ft.Text("Card 2"), - ), - ], - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/control/expand_row_equal_split/main.py b/sdk/python/examples/controls/control/expand_row_equal_split/main.py new file mode 100644 index 0000000000..624934537d --- /dev/null +++ b/sdk/python/examples/controls/control/expand_row_equal_split/main.py @@ -0,0 +1,38 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Container( + width=500, + height=180, + padding=10, + border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), + border_radius=10, + content=ft.Row( + spacing=8, + controls=[ + ft.Container( + expand=True, + bgcolor=ft.Colors.ORANGE_300, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text("Card 1"), + ), + ft.Container( + expand=True, + bgcolor=ft.Colors.GREEN_200, + border_radius=8, + alignment=ft.Alignment.CENTER, + content=ft.Text("Card 2"), + ), + ], + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/control/expand_row_equal_split/pyproject.toml b/sdk/python/examples/controls/control/expand_row_equal_split/pyproject.toml new file mode 100644 index 0000000000..09355576dd --- /dev/null +++ b/sdk/python/examples/controls/control/expand_row_equal_split/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "control-expand-row-equal-split" +version = "1.0.0" +description = "Row example where two containers equally split available horizontal space with expand=True." +requires-python = ">=3.10" +keywords = ["control", "expand", "row", "equal split", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Control"] + +[tool.flet.metadata] +title = "Expand row equal split" +controls = ["SafeArea", "Container", "Row", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["equal expand split", "row layout"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/control/expand_row_proportional_1_3_1.py b/sdk/python/examples/controls/control/expand_row_proportional_1_3_1.py deleted file mode 100644 index c025b9347e..0000000000 --- a/sdk/python/examples/controls/control/expand_row_proportional_1_3_1.py +++ /dev/null @@ -1,45 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Container( - width=500, - padding=10, - border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), - border_radius=10, - content=ft.Row( - spacing=8, - controls=[ - ft.Container( - expand=1, - height=60, - bgcolor=ft.Colors.CYAN_300, - alignment=ft.Alignment.CENTER, - border_radius=8, - content=ft.Text("1"), - ), - ft.Container( - expand=3, - height=60, - bgcolor=ft.Colors.AMBER_300, - alignment=ft.Alignment.CENTER, - border_radius=8, - content=ft.Text("3"), - ), - ft.Container( - expand=1, - height=60, - bgcolor=ft.Colors.PINK_200, - alignment=ft.Alignment.CENTER, - border_radius=8, - content=ft.Text("1"), - ), - ], - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/control/expand_row_proportional_1_3_1/main.py b/sdk/python/examples/controls/control/expand_row_proportional_1_3_1/main.py new file mode 100644 index 0000000000..65e1f27dd9 --- /dev/null +++ b/sdk/python/examples/controls/control/expand_row_proportional_1_3_1/main.py @@ -0,0 +1,47 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Container( + width=500, + padding=10, + border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), + border_radius=10, + content=ft.Row( + spacing=8, + controls=[ + ft.Container( + expand=1, + height=60, + bgcolor=ft.Colors.CYAN_300, + alignment=ft.Alignment.CENTER, + border_radius=8, + content=ft.Text("1"), + ), + ft.Container( + expand=3, + height=60, + bgcolor=ft.Colors.AMBER_300, + alignment=ft.Alignment.CENTER, + border_radius=8, + content=ft.Text("3"), + ), + ft.Container( + expand=1, + height=60, + bgcolor=ft.Colors.PINK_200, + alignment=ft.Alignment.CENTER, + border_radius=8, + content=ft.Text("1"), + ), + ], + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/control/expand_row_proportional_1_3_1/pyproject.toml b/sdk/python/examples/controls/control/expand_row_proportional_1_3_1/pyproject.toml new file mode 100644 index 0000000000..d49c520dfa --- /dev/null +++ b/sdk/python/examples/controls/control/expand_row_proportional_1_3_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "control-expand-row-proportional-1-3-1" +version = "1.0.0" +description = "Row example using proportional expand values (1:3:1) for responsive width distribution." +requires-python = ">=3.10" +keywords = ["control", "expand", "row", "proportional", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Control"] + +[tool.flet.metadata] +title = "Expand row proportional 1 3 1" +controls = ["SafeArea", "Container", "Row", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["proportional expand", "row space distribution"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/control/expand_textfield_in_row.py b/sdk/python/examples/controls/control/expand_textfield_in_row.py deleted file mode 100644 index 1da25f2cd9..0000000000 --- a/sdk/python/examples/controls/control/expand_textfield_in_row.py +++ /dev/null @@ -1,22 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Container( - width=480, - padding=10, - border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), - border_radius=10, - content=ft.Row( - controls=[ - ft.TextField(hint_text="Enter your name", expand=True), - ft.Button("Join chat"), - ] - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/control/expand_textfield_in_row/main.py b/sdk/python/examples/controls/control/expand_textfield_in_row/main.py new file mode 100644 index 0000000000..e890745122 --- /dev/null +++ b/sdk/python/examples/controls/control/expand_textfield_in_row/main.py @@ -0,0 +1,24 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Container( + width=480, + padding=10, + border=ft.Border.all(2, ft.Colors.BLUE_GREY_200), + border_radius=10, + content=ft.Row( + controls=[ + ft.TextField(hint_text="Enter your name", expand=True), + ft.Button("Join chat"), + ] + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/control/expand_textfield_in_row/pyproject.toml b/sdk/python/examples/controls/control/expand_textfield_in_row/pyproject.toml new file mode 100644 index 0000000000..34e1114ea1 --- /dev/null +++ b/sdk/python/examples/controls/control/expand_textfield_in_row/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "control-expand-textfield-in-row" +version = "1.0.0" +description = "Row layout with an expanding TextField and fixed-size action button." +requires-python = ">=3.10" +keywords = ["control", "expand", "row", "textfield", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Control"] + +[tool.flet.metadata] +title = "Expand textfield in row" +controls = ["SafeArea", "Container", "Row", "TextField", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["expand in row", "mixed flexible and fixed controls"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_action_sheet/basic.py b/sdk/python/examples/controls/cupertino_action_sheet/basic.py deleted file mode 100644 index a17a30c4ba..0000000000 --- a/sdk/python/examples/controls/cupertino_action_sheet/basic.py +++ /dev/null @@ -1,52 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_click(e): - page.add(ft.Text(f"Action clicked: {e.control.content.value}")) - page.pop_dialog() - - action_sheet = ft.CupertinoActionSheet( - title=ft.Row( - controls=[ft.Text("Title"), ft.Icon(ft.Icons.BEDTIME)], - alignment=ft.MainAxisAlignment.CENTER, - ), - message=ft.Row( - controls=[ft.Text("Description"), ft.Icon(ft.Icons.AUTO_AWESOME)], - alignment=ft.MainAxisAlignment.CENTER, - ), - cancel=ft.CupertinoActionSheetAction( - content=ft.Text("Cancel"), - on_click=handle_click, - ), - actions=[ - ft.CupertinoActionSheetAction( - content=ft.Text("Default Action"), - default=True, - on_click=handle_click, - ), - ft.CupertinoActionSheetAction( - content=ft.Text("Normal Action"), - on_click=handle_click, - ), - ft.CupertinoActionSheetAction( - content=ft.Text("Destructive Action"), - destructive=True, - on_click=handle_click, - ), - ], - ) - - bottom_sheet = ft.CupertinoBottomSheet(action_sheet) - - page.add( - ft.CupertinoFilledButton( - content="Open CupertinoBottomSheet", - on_click=lambda e: page.show_dialog(bottom_sheet), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_action_sheet/basic/main.py b/sdk/python/examples/controls/cupertino_action_sheet/basic/main.py new file mode 100644 index 0000000000..e1b6dfd77b --- /dev/null +++ b/sdk/python/examples/controls/cupertino_action_sheet/basic/main.py @@ -0,0 +1,63 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + messages = ft.Column(tight=True) + + def handle_click(e: ft.Event[ft.CupertinoActionSheetAction]): + messages.controls.append(ft.Text(f"Action clicked: {e.control.content.value}")) + page.pop_dialog() + + action_sheet = ft.CupertinoActionSheet( + title=ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ft.Text("Title"), ft.Icon(ft.Icons.BEDTIME)], + ), + message=ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ft.Text("Description"), ft.Icon(ft.Icons.AUTO_AWESOME)], + ), + cancel=ft.CupertinoActionSheetAction( + on_click=handle_click, + content=ft.Text("Cancel"), + ), + actions=[ + ft.CupertinoActionSheetAction( + default=True, + on_click=handle_click, + content=ft.Text("Default Action"), + ), + ft.CupertinoActionSheetAction( + on_click=handle_click, + content=ft.Text("Normal Action"), + ), + ft.CupertinoActionSheetAction( + destructive=True, + on_click=handle_click, + content=ft.Text("Destructive Action"), + ), + ], + ) + + bottom_sheet = ft.CupertinoBottomSheet(action_sheet) + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.CupertinoFilledButton( + on_click=lambda _: page.show_dialog(bottom_sheet), + content="Open CupertinoBottomSheet", + ), + messages, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_action_sheet/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_action_sheet/basic/pyproject.toml new file mode 100644 index 0000000000..d75c53aeda --- /dev/null +++ b/sdk/python/examples/controls/cupertino_action_sheet/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-action-sheet-basic" +version = "1.0.0" +description = "Opens a CupertinoActionSheet in a CupertinoBottomSheet and logs the selected action." +requires-python = ">=3.10" +keywords = ["cupertino", "action sheet", "bottom sheet", "dialog"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/CupertinoActionSheet"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "CupertinoFilledButton", "CupertinoBottomSheet", "CupertinoActionSheet", "CupertinoActionSheetAction", "Text", "Row", "Icon"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["open dialog", "action callbacks", "destructive action"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_activity_indicator/basic.py b/sdk/python/examples/controls/cupertino_activity_indicator/basic.py deleted file mode 100644 index e0aaf62d2c..0000000000 --- a/sdk/python/examples/controls/cupertino_activity_indicator/basic.py +++ /dev/null @@ -1,14 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.CupertinoActivityIndicator( - animating=True, - color=ft.Colors.RED, - radius=50, - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_activity_indicator/basic/main.py b/sdk/python/examples/controls/cupertino_activity_indicator/basic/main.py new file mode 100644 index 0000000000..daaac7718d --- /dev/null +++ b/sdk/python/examples/controls/cupertino_activity_indicator/basic/main.py @@ -0,0 +1,20 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + page.add( + ft.SafeArea( + content=ft.CupertinoActivityIndicator( + animating=True, + color=ft.Colors.RED, + radius=50, + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_activity_indicator/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_activity_indicator/basic/pyproject.toml new file mode 100644 index 0000000000..059023c68e --- /dev/null +++ b/sdk/python/examples/controls/cupertino_activity_indicator/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-activity-indicator-basic" +version = "1.0.0" +description = "Shows a centered CupertinoActivityIndicator with custom color and radius." +requires-python = ">=3.10" +keywords = ["cupertino", "activity indicator", "loading", "spinner"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/CupertinoActivityIndicator"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "CupertinoActivityIndicator"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["indeterminate loading state", "custom radius", "custom color"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_activity_indicator/progress.py b/sdk/python/examples/controls/cupertino_activity_indicator/progress.py deleted file mode 100644 index da74c76db3..0000000000 --- a/sdk/python/examples/controls/cupertino_activity_indicator/progress.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.spacing = 20 - - def handle_progress_change(e: ft.Event[ft.Slider]): - indicator.progress = e.control.value - - page.add( - indicator := ft.CupertinoActivityIndicator(progress=1.0, radius=40), - ft.Slider( - min=0.0, - value=indicator.progress, - max=1.0, - divisions=10, - round=1, - label="Progress = {value}", - width=400, - on_change=handle_progress_change, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_activity_indicator/progress/main.py b/sdk/python/examples/controls/cupertino_activity_indicator/progress/main.py new file mode 100644 index 0000000000..5e961f6871 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_activity_indicator/progress/main.py @@ -0,0 +1,36 @@ +import flet as ft + + +async def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.spacing = 20 + + indicator = ft.CupertinoActivityIndicator(progress=1.0, radius=40) + + def handle_progress_change(e: ft.Event[ft.Slider]): + indicator.progress = e.control.value + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + indicator, + ft.Slider( + min=0.0, + value=indicator.progress, + max=1.0, + divisions=10, + round=1, + label="Progress = {value}", + width=400, + on_change=handle_progress_change, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_activity_indicator/progress/pyproject.toml b/sdk/python/examples/controls/cupertino_activity_indicator/progress/pyproject.toml new file mode 100644 index 0000000000..9b9dd80ced --- /dev/null +++ b/sdk/python/examples/controls/cupertino_activity_indicator/progress/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-activity-indicator-progress" +version = "1.0.0" +description = "Drives CupertinoActivityIndicator progress with a slider for determinate loading feedback." +requires-python = ">=3.10" +keywords = ["cupertino", "activity indicator", "progress", "slider"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/CupertinoActivityIndicator"] + +[tool.flet.metadata] +title = "Progress" +controls = ["SafeArea", "Column", "CupertinoActivityIndicator", "Slider"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["determinate progress", "slider-driven updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_alert_dialog/cupertino_material_and_adaptive.py b/sdk/python/examples/controls/cupertino_alert_dialog/cupertino_material_and_adaptive.py deleted file mode 100644 index 8dc1105614..0000000000 --- a/sdk/python/examples/controls/cupertino_alert_dialog/cupertino_material_and_adaptive.py +++ /dev/null @@ -1,75 +0,0 @@ -from typing import Union - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.scroll = ft.ScrollMode.AUTO - - def handle_action_click( - e: ft.Event[Union[ft.TextButton, ft.CupertinoDialogAction]], - ): - page.add(ft.Text(f"Action clicked: {e.control.content}")) - page.pop_dialog() - - cupertino_actions = [ - ft.CupertinoDialogAction( - content="Yes", - destructive=True, - on_click=handle_action_click, - ), - ft.CupertinoDialogAction( - content="No", - default=False, - on_click=handle_action_click, - ), - ] - - material_actions = [ - ft.TextButton(content="Yes", on_click=handle_action_click), - ft.TextButton(content="No", on_click=handle_action_click), - ] - - page.add( - ft.FilledButton( - content="Open Material Dialog", - on_click=lambda e: page.show_dialog( - ft.AlertDialog( - title=ft.Text("Material Alert Dialog"), - content=ft.Text("Do you want to delete this file?"), - actions=material_actions, - ) - ), - ), - ft.CupertinoFilledButton( - content="Open Cupertino Dialog", - on_click=lambda e: page.show_dialog( - ft.CupertinoAlertDialog( - title=ft.Text("Cupertino Alert Dialog"), - content=ft.Text("Do you want to delete this file?"), - actions=cupertino_actions, - ) - ), - ), - ft.FilledButton( - content="Open Adaptive Dialog", - adaptive=True, - bgcolor=ft.Colors.BLUE_ACCENT, - on_click=lambda e: page.show_dialog( - ft.AlertDialog( - adaptive=True, - title=ft.Text("Adaptive Alert Dialog"), - content=ft.Text("Do you want to delete this file?"), - actions=( - cupertino_actions - if page.platform.is_apple() - else material_actions - ), - ) - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_alert_dialog/cupertino_material_and_adaptive/main.py b/sdk/python/examples/controls/cupertino_alert_dialog/cupertino_material_and_adaptive/main.py new file mode 100644 index 0000000000..a2ebc10673 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_alert_dialog/cupertino_material_and_adaptive/main.py @@ -0,0 +1,86 @@ +from typing import Union + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.scroll = ft.ScrollMode.AUTO + + messages = ft.Column(tight=True) + + def handle_action_click( + e: ft.Event[Union[ft.TextButton, ft.CupertinoDialogAction]], + ): + messages.controls.append(ft.Text(f"Action clicked: {e.control.content}")) + page.pop_dialog() + + cupertino_actions = [ + ft.CupertinoDialogAction( + destructive=True, + on_click=handle_action_click, + content="Yes", + ), + ft.CupertinoDialogAction( + default=False, + on_click=handle_action_click, + content="No", + ), + ] + + material_actions = [ + ft.TextButton(on_click=handle_action_click, content="Yes"), + ft.TextButton(on_click=handle_action_click, content="No"), + ] + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.FilledButton( + on_click=lambda _: page.show_dialog( + ft.AlertDialog( + title=ft.Text("Material Alert Dialog"), + content=ft.Text("Do you want to delete this file?"), + actions=material_actions, + ) + ), + content="Open Material Dialog", + ), + ft.CupertinoFilledButton( + on_click=lambda _: page.show_dialog( + ft.CupertinoAlertDialog( + title=ft.Text("Cupertino Alert Dialog"), + content=ft.Text("Do you want to delete this file?"), + actions=cupertino_actions, + ) + ), + content="Open Cupertino Dialog", + ), + ft.FilledButton( + adaptive=True, + bgcolor=ft.Colors.BLUE_ACCENT, + on_click=lambda _: page.show_dialog( + ft.AlertDialog( + adaptive=True, + title=ft.Text("Adaptive Alert Dialog"), + content=ft.Text("Do you want to delete this file?"), + actions=( + cupertino_actions + if page.platform.is_apple() + else material_actions + ), + ) + ), + content="Open Adaptive Dialog", + ), + messages, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_alert_dialog/cupertino_material_and_adaptive/pyproject.toml b/sdk/python/examples/controls/cupertino_alert_dialog/cupertino_material_and_adaptive/pyproject.toml new file mode 100644 index 0000000000..9493419196 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_alert_dialog/cupertino_material_and_adaptive/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-alert-dialog-cupertino-material-and-adaptive" +version = "1.0.0" +description = "Compares Material, Cupertino, and adaptive alert dialogs with platform-aware action sets." +requires-python = ">=3.10" +keywords = ["cupertino", "material", "adaptive", "alert dialog", "platform"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/CupertinoAlertDialog"] + +[tool.flet.metadata] +title = "Cupertino, material and adaptive" +controls = ["SafeArea", "Column", "FilledButton", "CupertinoFilledButton", "AlertDialog", "CupertinoAlertDialog", "TextButton", "CupertinoDialogAction", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["platform-adaptive dialog", "material and cupertino comparison", "action callbacks"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_alert_dialog/file_deletion_confirmation.py b/sdk/python/examples/controls/cupertino_alert_dialog/file_deletion_confirmation.py deleted file mode 100644 index c98dd53155..0000000000 --- a/sdk/python/examples/controls/cupertino_alert_dialog/file_deletion_confirmation.py +++ /dev/null @@ -1,38 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_dialog_dismissal(e: ft.Event[ft.DialogControl]): - page.add(ft.Text("Dialog dismissed")) - - def handle_action_click(e: ft.Event[ft.CupertinoDialogAction]): - page.add(ft.Text(f"Action clicked: {e.control.content}")) - page.pop_dialog() - - cupertino_alert_dialog = ft.CupertinoAlertDialog( - title=ft.Text("Cupertino Alert Dialog"), - content=ft.Text("Do you want to delete this file?"), - on_dismiss=handle_dialog_dismissal, - actions=[ - ft.CupertinoDialogAction( - content="Yes", - destructive=True, - on_click=handle_action_click, - ), - ft.CupertinoDialogAction( - content="No", default=True, on_click=handle_action_click - ), - ], - ) - - page.add( - ft.CupertinoFilledButton( - content="Open CupertinoAlertDialog", - on_click=lambda e: page.show_dialog(cupertino_alert_dialog), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_alert_dialog/file_deletion_confirmation/main.py b/sdk/python/examples/controls/cupertino_alert_dialog/file_deletion_confirmation/main.py new file mode 100644 index 0000000000..59b81d76d7 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_alert_dialog/file_deletion_confirmation/main.py @@ -0,0 +1,51 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + messages = ft.Column(tight=True) + + def handle_dialog_dismissal(_: ft.Event[ft.DialogControl]): + messages.controls.append(ft.Text("Dialog dismissed")) + + def handle_action_click(e: ft.Event[ft.CupertinoDialogAction]): + messages.controls.append(ft.Text(f"Action clicked: {e.control.content}")) + page.pop_dialog() + + cupertino_alert_dialog = ft.CupertinoAlertDialog( + title=ft.Text("Cupertino Alert Dialog"), + content=ft.Text("Do you want to delete this file?"), + on_dismiss=handle_dialog_dismissal, + actions=[ + ft.CupertinoDialogAction( + destructive=True, + on_click=handle_action_click, + content="Yes", + ), + ft.CupertinoDialogAction( + default=True, + on_click=handle_action_click, + content="No", + ), + ], + ) + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.CupertinoFilledButton( + on_click=lambda _: page.show_dialog(cupertino_alert_dialog), + content="Open CupertinoAlertDialog", + ), + messages, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_alert_dialog/file_deletion_confirmation/pyproject.toml b/sdk/python/examples/controls/cupertino_alert_dialog/file_deletion_confirmation/pyproject.toml new file mode 100644 index 0000000000..ccc954f4b0 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_alert_dialog/file_deletion_confirmation/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-alert-dialog-file-deletion-confirmation" +version = "1.0.0" +description = "Shows a CupertinoAlertDialog for file deletion confirmation with dismiss and action callbacks." +requires-python = ">=3.10" +keywords = ["cupertino", "alert dialog", "confirmation", "actions"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/CupertinoAlertDialog"] + +[tool.flet.metadata] +title = "File deletion confirmation" +controls = ["SafeArea", "Column", "CupertinoFilledButton", "CupertinoAlertDialog", "CupertinoDialogAction", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["open dialog", "dismiss callback", "action callbacks", "destructive action"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_app_bar/basic.py b/sdk/python/examples/controls/cupertino_app_bar/basic.py deleted file mode 100644 index e6f56be91a..0000000000 --- a/sdk/python/examples/controls/cupertino_app_bar/basic.py +++ /dev/null @@ -1,19 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - page.appbar = ft.CupertinoAppBar( - leading=ft.Icon(ft.Icons.PALETTE, color=ft.Colors.ON_SECONDARY), - title=ft.Text("CupertinoAppBar Example"), - trailing=ft.Icon(ft.Icons.WB_SUNNY_OUTLINED, color=ft.Colors.ON_SECONDARY), - automatic_background_visibility=False, - bgcolor=ft.Colors.SECONDARY, - brightness=ft.Brightness.LIGHT, - ) - - page.add(ft.Text("Body!")) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_app_bar/basic/main.py b/sdk/python/examples/controls/cupertino_app_bar/basic/main.py new file mode 100644 index 0000000000..e8b2b54a4e --- /dev/null +++ b/sdk/python/examples/controls/cupertino_app_bar/basic/main.py @@ -0,0 +1,24 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + page.appbar = ft.CupertinoAppBar( + leading=ft.Icon(ft.Icons.PALETTE, color=ft.Colors.ON_SECONDARY), + title=ft.Text("CupertinoAppBar Example"), + trailing=ft.Icon(ft.Icons.WB_SUNNY_OUTLINED, color=ft.Colors.ON_SECONDARY), + automatic_background_visibility=False, + bgcolor=ft.Colors.SECONDARY, + brightness=ft.Brightness.LIGHT, + ) + + page.add( + ft.SafeArea( + content=ft.Text("Body!"), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_app_bar/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_app_bar/basic/pyproject.toml new file mode 100644 index 0000000000..6daad9f02d --- /dev/null +++ b/sdk/python/examples/controls/cupertino_app_bar/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-app-bar-basic" +version = "1.0.0" +description = "Displays a CupertinoAppBar with custom leading and trailing icons and light theme styling." +requires-python = ">=3.10" +keywords = ["cupertino", "app bar", "navigation", "toolbar"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/CupertinoAppBar"] + +[tool.flet.metadata] +title = "Basic" +controls = ["CupertinoAppBar", "Icon", "Text", "SafeArea"] +layout_pattern = "toolbar-actions" +complexity = "basic" +features = ["custom app bar colors", "leading and trailing icons"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_app_bar/theme_mode_toggle.py b/sdk/python/examples/controls/cupertino_app_bar/theme_mode_toggle.py deleted file mode 100644 index 5376c73dbf..0000000000 --- a/sdk/python/examples/controls/cupertino_app_bar/theme_mode_toggle.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - def handle_theme_mode_toggle(e: ft.Event[ft.IconButton]): - page.theme_mode = ( - ft.ThemeMode.DARK - if page.theme_mode == ft.ThemeMode.LIGHT - else ft.ThemeMode.LIGHT - ) - theme_mode_button.icon = ( - ft.Icons.WB_SUNNY_OUTLINED - if page.theme_mode == ft.ThemeMode.LIGHT - else ft.Icons.WB_SUNNY - ) - page.update() - - theme_mode_button = ft.IconButton( - icon=( - ft.Icons.WB_SUNNY_OUTLINED - if page.theme_mode == ft.ThemeMode.LIGHT - else ft.Icons.WB_SUNNY - ), - icon_color=ft.Colors.ON_INVERSE_SURFACE, - on_click=handle_theme_mode_toggle, - ) - - page.appbar = ft.CupertinoAppBar( - automatic_background_visibility=False, - leading=ft.Icon(ft.Icons.PALETTE, color=ft.Colors.ON_INVERSE_SURFACE), - bgcolor=ft.Colors.INVERSE_SURFACE, - trailing=theme_mode_button, - title=ft.Text("CupertinoAppBar Example", color=ft.Colors.ON_INVERSE_SURFACE), - ) - - page.add(ft.Text("Body!")) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_app_bar/theme_mode_toggle/main.py b/sdk/python/examples/controls/cupertino_app_bar/theme_mode_toggle/main.py new file mode 100644 index 0000000000..bfd61b3be1 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_app_bar/theme_mode_toggle/main.py @@ -0,0 +1,45 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + def handle_theme_mode_toggle(_: ft.Event[ft.IconButton]): + page.theme_mode = ( + ft.ThemeMode.DARK + if page.theme_mode == ft.ThemeMode.LIGHT + else ft.ThemeMode.LIGHT + ) + theme_mode_button.icon = ( + ft.Icons.WB_SUNNY_OUTLINED + if page.theme_mode == ft.ThemeMode.LIGHT + else ft.Icons.WB_SUNNY + ) + + theme_mode_button = ft.IconButton( + icon=( + ft.Icons.WB_SUNNY_OUTLINED + if page.theme_mode == ft.ThemeMode.LIGHT + else ft.Icons.WB_SUNNY + ), + icon_color=ft.Colors.ON_INVERSE_SURFACE, + on_click=handle_theme_mode_toggle, + ) + + page.appbar = ft.CupertinoAppBar( + automatic_background_visibility=False, + leading=ft.Icon(ft.Icons.PALETTE, color=ft.Colors.ON_INVERSE_SURFACE), + bgcolor=ft.Colors.INVERSE_SURFACE, + trailing=theme_mode_button, + title=ft.Text("CupertinoAppBar Example", color=ft.Colors.ON_INVERSE_SURFACE), + ) + + page.add( + ft.SafeArea( + content=ft.Text("Body!"), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_app_bar/theme_mode_toggle/pyproject.toml b/sdk/python/examples/controls/cupertino_app_bar/theme_mode_toggle/pyproject.toml new file mode 100644 index 0000000000..6ee28dccbd --- /dev/null +++ b/sdk/python/examples/controls/cupertino_app_bar/theme_mode_toggle/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-app-bar-theme-mode-toggle" +version = "1.0.0" +description = "Toggles page theme mode from a CupertinoAppBar trailing icon button." +requires-python = ">=3.10" +keywords = ["cupertino", "app bar", "theme", "toggle", "icon button"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/CupertinoAppBar"] + +[tool.flet.metadata] +title = "Theme mode toggle" +controls = ["CupertinoAppBar", "Icon", "IconButton", "Text", "SafeArea"] +layout_pattern = "toolbar-actions" +complexity = "basic" +features = ["theme mode toggle", "dynamic icon update"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_button/basic.py b/sdk/python/examples/controls/cupertino_button/basic.py deleted file mode 100644 index 106ce1747e..0000000000 --- a/sdk/python/examples/controls/cupertino_button/basic.py +++ /dev/null @@ -1,44 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.CupertinoButton( - bgcolor=ft.CupertinoColors.LIGHT_BACKGROUND_GRAY, - opacity_on_click=0.3, - on_click=lambda e: print("Normal CupertinoButton clicked!"), - content=ft.Text( - value="Normal CupertinoButton", - color=ft.CupertinoColors.DESTRUCTIVE_RED, - ), - ), - ft.CupertinoButton( - bgcolor=ft.Colors.PRIMARY, - alignment=ft.Alignment.TOP_LEFT, - border_radius=ft.BorderRadius.all(15), - opacity_on_click=0.5, - on_click=lambda e: print("Filled CupertinoButton clicked!"), - content=ft.Text("Filled CupertinoButton", color=ft.Colors.YELLOW), - ), - ft.CupertinoButton( - bgcolor=ft.Colors.PRIMARY, - disabled=True, - alignment=ft.Alignment.TOP_LEFT, - opacity_on_click=0.5, - content=ft.Text("Disabled CupertinoButton"), - ), - ft.Button( - adaptive=True, - bgcolor=ft.CupertinoColors.SYSTEM_TEAL, - content=ft.Row( - tight=True, - controls=[ - ft.Icon(ft.Icons.FAVORITE, color="pink"), - ft.Text("Button+adaptive"), - ], - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_button/basic/main.py b/sdk/python/examples/controls/cupertino_button/basic/main.py new file mode 100644 index 0000000000..dfffecc41e --- /dev/null +++ b/sdk/python/examples/controls/cupertino_button/basic/main.py @@ -0,0 +1,54 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.CupertinoButton( + bgcolor=ft.CupertinoColors.LIGHT_BACKGROUND_GRAY, + opacity_on_click=0.3, + on_click=lambda _: print("Normal CupertinoButton clicked!"), + content=ft.Text( + value="Normal CupertinoButton", + color=ft.CupertinoColors.DESTRUCTIVE_RED, + ), + ), + ft.CupertinoButton( + bgcolor=ft.Colors.PRIMARY, + alignment=ft.Alignment.TOP_LEFT, + border_radius=ft.BorderRadius.all(15), + opacity_on_click=0.5, + on_click=lambda _: print("Filled CupertinoButton clicked!"), + content=ft.Text( + "Filled CupertinoButton", + color=ft.Colors.YELLOW, + ), + ), + ft.CupertinoButton( + bgcolor=ft.Colors.PRIMARY, + disabled=True, + alignment=ft.Alignment.TOP_LEFT, + opacity_on_click=0.5, + content=ft.Text("Disabled CupertinoButton"), + ), + ft.Button( + adaptive=True, + bgcolor=ft.CupertinoColors.SYSTEM_TEAL, + content=ft.Row( + tight=True, + controls=[ + ft.Icon(ft.Icons.FAVORITE, color="pink"), + ft.Text("Button+adaptive"), + ], + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_button/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_button/basic/pyproject.toml new file mode 100644 index 0000000000..597cbb92ae --- /dev/null +++ b/sdk/python/examples/controls/cupertino_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-button-basic" +version = "1.0.0" +description = "Demonstrates styled, disabled, and adaptive CupertinoButton variations in one layout." +requires-python = ">=3.10" +keywords = ["cupertino", "button", "adaptive", "disabled", "styling"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/CupertinoButton"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "CupertinoButton", "Button", "Text", "Row", "Icon"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["custom button styling", "disabled button", "adaptive button"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_checkbox/cupertino_material_and_adaptive.py b/sdk/python/examples/controls/cupertino_checkbox/cupertino_material_and_adaptive.py deleted file mode 100644 index d010a057f2..0000000000 --- a/sdk/python/examples/controls/cupertino_checkbox/cupertino_material_and_adaptive.py +++ /dev/null @@ -1,16 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.CupertinoCheckbox(label="Cupertino Checkbox", value=True), - ft.Checkbox(label="Material Checkbox", value=True), - ft.Container(height=20), - ft.Text( - value="Adaptive Checkbox shows as CupertinoCheckbox on macOS and iOS and as Checkbox on other platforms:" - ), - ft.Checkbox(adaptive=True, label="Adaptive Checkbox", value=True), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_checkbox/cupertino_material_and_adaptive/main.py b/sdk/python/examples/controls/cupertino_checkbox/cupertino_material_and_adaptive/main.py new file mode 100644 index 0000000000..79d36b7ea0 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_checkbox/cupertino_material_and_adaptive/main.py @@ -0,0 +1,30 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.CupertinoCheckbox(label="Cupertino Checkbox", value=True), + ft.Checkbox(label="Material Checkbox", value=True), + ft.Container(height=20), + ft.Text( + value=( + "Adaptive Checkbox shows as CupertinoCheckbox on macOS " + "and iOS and as Checkbox on other platforms:" + ) + ), + ft.Checkbox( + adaptive=True, + label="Adaptive Checkbox", + value=True, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_checkbox/cupertino_material_and_adaptive/pyproject.toml b/sdk/python/examples/controls/cupertino_checkbox/cupertino_material_and_adaptive/pyproject.toml new file mode 100644 index 0000000000..9715f7580f --- /dev/null +++ b/sdk/python/examples/controls/cupertino_checkbox/cupertino_material_and_adaptive/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-checkbox-cupertino-material-and-adaptive" +version = "1.0.0" +description = "Shows Cupertino, Material, and adaptive checkbox behavior side by side." +requires-python = ">=3.10" +keywords = ["cupertino", "checkbox", "adaptive", "material", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoCheckbox"] + +[tool.flet.metadata] +title = "Cupertino, material and adaptive" +controls = ["SafeArea", "Column", "CupertinoCheckbox", "Checkbox", "Text", "Container"] +layout_pattern = "form" +complexity = "basic" +features = ["platform-adaptive checkbox", "cupertino and material comparison"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_checkbox/styled.py b/sdk/python/examples/controls/cupertino_checkbox/styled.py deleted file mode 100644 index c2bbe8a2e5..0000000000 --- a/sdk/python/examples/controls/cupertino_checkbox/styled.py +++ /dev/null @@ -1,49 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - page.add( - ft.Column( - controls=[ - ft.CupertinoCheckbox( - label="Cupertino Checkbox tristate", - value=True, - tristate=True, - check_color=ft.Colors.GREY_900, - fill_color={ - ft.ControlState.HOVERED: ft.Colors.PINK_200, - ft.ControlState.PRESSED: ft.Colors.LIME_ACCENT_200, - ft.ControlState.SELECTED: ft.Colors.DEEP_ORANGE_200, - ft.ControlState.DEFAULT: ft.Colors.TEAL_200, - }, - ), - ft.CupertinoCheckbox( - label="Cupertino Checkbox circle border", - value=True, - shape=ft.CircleBorder(), - # scale=ft.Scale(2, alignment=ft.Alignment(-1, 0)), - ), - ft.CupertinoCheckbox( - label="Cupertino Checkbox border states", - value=True, - # v1 bug - border_side renders grey box - # border_side={ - # ft.ControlState.HOVERED: ft.BorderSide(width=5), - # ft.ControlState.DEFAULT: ft.BorderSide(width=3), - # ft.ControlState.FOCUSED: ft.BorderSide(), - # }, - # scale=ft.Scale(2, alignment=ft.Alignment(-0.9, 0)), - ), - ft.CupertinoCheckbox( - label="Cupertino Checkbox label position", - value=True, - label_position=ft.LabelPosition.LEFT, - ), - ] - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_checkbox/styled/main.py b/sdk/python/examples/controls/cupertino_checkbox/styled/main.py new file mode 100644 index 0000000000..bdf4215312 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_checkbox/styled/main.py @@ -0,0 +1,44 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.CupertinoCheckbox( + label="Cupertino Checkbox tristate", + value=True, + tristate=True, + check_color=ft.Colors.GREY_900, + fill_color={ + ft.ControlState.HOVERED: ft.Colors.PINK_200, + ft.ControlState.PRESSED: ft.Colors.LIME_ACCENT_200, + ft.ControlState.SELECTED: ft.Colors.DEEP_ORANGE_200, + ft.ControlState.DEFAULT: ft.Colors.TEAL_200, + }, + ), + ft.CupertinoCheckbox( + label="Cupertino Checkbox circle border", + value=True, + shape=ft.CircleBorder(), + ), + ft.CupertinoCheckbox( + label="Cupertino Checkbox border states", + value=True, + ), + ft.CupertinoCheckbox( + label="Cupertino Checkbox label position", + value=True, + label_position=ft.LabelPosition.LEFT, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_checkbox/styled/pyproject.toml b/sdk/python/examples/controls/cupertino_checkbox/styled/pyproject.toml new file mode 100644 index 0000000000..f65ba4f923 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_checkbox/styled/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-checkbox-styled" +version = "1.0.0" +description = "Configures CupertinoCheckbox styles including fill states, shape, tristate, and label position." +requires-python = ">=3.10" +keywords = ["cupertino", "checkbox", "styling", "tristate", "shape"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoCheckbox"] + +[tool.flet.metadata] +title = "Styled" +controls = ["SafeArea", "Column", "CupertinoCheckbox", "CircleBorder"] +layout_pattern = "form" +complexity = "basic" +features = ["tristate checkbox", "state-based fill colors", "custom shape", "left-side label"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_context_menu/basic.py b/sdk/python/examples/controls/cupertino_context_menu/basic.py deleted file mode 100644 index aa3a6db369..0000000000 --- a/sdk/python/examples/controls/cupertino_context_menu/basic.py +++ /dev/null @@ -1,35 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - page.add( - ft.CupertinoContextMenu( - enable_haptic_feedback=True, - content=ft.Image("https://picsum.photos/200/200"), - actions=[ - ft.CupertinoContextMenuAction( - content="Action 1", - default=True, - trailing_icon=ft.Icons.CHECK, - on_click=lambda e: print("Action 1"), - ), - ft.CupertinoContextMenuAction( - content="Action 2", - trailing_icon=ft.Icons.MORE, - on_click=lambda e: print("Action 2"), - ), - ft.CupertinoContextMenuAction( - content="Action 3", - destructive=True, - trailing_icon=ft.Icons.CANCEL, - on_click=lambda e: print("Action 3"), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_context_menu/basic/main.py b/sdk/python/examples/controls/cupertino_context_menu/basic/main.py new file mode 100644 index 0000000000..ef9b62851e --- /dev/null +++ b/sdk/python/examples/controls/cupertino_context_menu/basic/main.py @@ -0,0 +1,38 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + page.add( + ft.SafeArea( + content=ft.CupertinoContextMenu( + enable_haptic_feedback=True, + actions=[ + ft.CupertinoContextMenuAction( + default=True, + trailing_icon=ft.Icons.CHECK, + on_click=lambda _: print("Action 1"), + content="Action 1", + ), + ft.CupertinoContextMenuAction( + trailing_icon=ft.Icons.MORE, + on_click=lambda _: print("Action 2"), + content="Action 2", + ), + ft.CupertinoContextMenuAction( + destructive=True, + trailing_icon=ft.Icons.CANCEL, + on_click=lambda _: print("Action 3"), + content="Action 3", + ), + ], + content=ft.Image("https://picsum.photos/200/200"), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_context_menu/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_context_menu/basic/pyproject.toml new file mode 100644 index 0000000000..95a6ae7858 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_context_menu/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-context-menu-basic" +version = "1.0.0" +description = "Opens a CupertinoContextMenu on image long-press with default and destructive actions." +requires-python = ">=3.10" +keywords = ["cupertino", "context menu", "image", "actions", "long press"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoContextMenu"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "CupertinoContextMenu", "CupertinoContextMenuAction", "Image"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["context menu actions", "destructive action", "haptic feedback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_date_picker/basic.py b/sdk/python/examples/controls/cupertino_date_picker/basic.py deleted file mode 100644 index 0a8105abb5..0000000000 --- a/sdk/python/examples/controls/cupertino_date_picker/basic.py +++ /dev/null @@ -1,34 +0,0 @@ -from datetime import datetime - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_date_change(e: ft.Event[ft.CupertinoDatePicker]): - message.value = f"Chosen Date: {e.control.value.strftime('%Y-%m-%d %H:%M %p')}" - page.update() - - cupertino_date_picker = ft.CupertinoDatePicker( - value=datetime.now(), - date_picker_mode=ft.CupertinoDatePickerMode.DATE_AND_TIME, - on_change=handle_date_change, - ) - - page.add( - ft.CupertinoFilledButton( - content="Open CupertinoDatePicker", - on_click=lambda e: page.show_dialog( - ft.CupertinoBottomSheet( - content=cupertino_date_picker, - height=216, - padding=ft.Padding.only(top=6), - ) - ), - ), - message := ft.Text("Chosen Time: "), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_date_picker/basic/main.py b/sdk/python/examples/controls/cupertino_date_picker/basic/main.py new file mode 100644 index 0000000000..d6dad041da --- /dev/null +++ b/sdk/python/examples/controls/cupertino_date_picker/basic/main.py @@ -0,0 +1,43 @@ +from datetime import datetime + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + message = ft.Text("Chosen Time:") + + def handle_date_change(e: ft.Event[ft.CupertinoDatePicker]): + message.value = f"Chosen Date: {e.control.value.strftime('%Y-%m-%d %H:%M %p')}" + + cupertino_date_picker = ft.CupertinoDatePicker( + value=datetime.now(), + date_picker_mode=ft.CupertinoDatePickerMode.DATE_AND_TIME, + on_change=handle_date_change, + ) + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.CupertinoFilledButton( + on_click=lambda _: page.show_dialog( + ft.CupertinoBottomSheet( + height=216, + padding=ft.Padding.only(top=6), + content=cupertino_date_picker, + ) + ), + content="Open CupertinoDatePicker", + ), + message, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_date_picker/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_date_picker/basic/pyproject.toml new file mode 100644 index 0000000000..f262648823 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_date_picker/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-date-picker-basic" +version = "1.0.0" +description = "Shows a CupertinoDatePicker in a bottom sheet and updates text when the value changes." +requires-python = ">=3.10" +keywords = ["cupertino", "date picker", "bottom sheet", "events", "datetime"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoDatePicker"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "CupertinoFilledButton", "CupertinoBottomSheet", "CupertinoDatePicker", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["date and time selection", "on_change callback", "bottom sheet presentation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_date_picker/custom_locale.py b/sdk/python/examples/controls/cupertino_date_picker/custom_locale.py deleted file mode 100644 index e8ebea22d5..0000000000 --- a/sdk/python/examples/controls/cupertino_date_picker/custom_locale.py +++ /dev/null @@ -1,24 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.add( - ft.CupertinoFilledButton( - content="Open CupertinoDatePicker (zh_Hans locale)", - on_click=lambda e: page.show_dialog( - ft.CupertinoBottomSheet( - height=216, - padding=ft.Padding.only(top=6), - content=ft.CupertinoDatePicker( - locale=ft.Locale("zh", "Hans"), - date_picker_mode=ft.CupertinoDatePickerMode.DATE_AND_TIME, - ), - ) - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_date_picker/custom_locale/main.py b/sdk/python/examples/controls/cupertino_date_picker/custom_locale/main.py new file mode 100644 index 0000000000..8defa93279 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_date_picker/custom_locale/main.py @@ -0,0 +1,27 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.add( + ft.SafeArea( + content=ft.CupertinoFilledButton( + on_click=lambda _: page.show_dialog( + ft.CupertinoBottomSheet( + height=216, + padding=ft.Padding.only(top=6), + content=ft.CupertinoDatePicker( + locale=ft.Locale("zh", "Hans"), + date_picker_mode=ft.CupertinoDatePickerMode.DATE_AND_TIME, + ), + ) + ), + content="Open CupertinoDatePicker (zh_Hans locale)", + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_date_picker/custom_locale/pyproject.toml b/sdk/python/examples/controls/cupertino_date_picker/custom_locale/pyproject.toml new file mode 100644 index 0000000000..5d73da8931 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_date_picker/custom_locale/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-date-picker-custom-locale" +version = "1.0.0" +description = "Opens CupertinoDatePicker with zh_Hans locale to demonstrate localized date formatting." +requires-python = ">=3.10" +keywords = ["date picker", "custom locale", "locale", "internationalization", "zh_Hans"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoDatePicker"] + +[tool.flet.metadata] +title = "Custom locale" +controls = ["SafeArea", "CupertinoFilledButton", "CupertinoBottomSheet", "CupertinoDatePicker", "Locale"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["custom locale", "date and time picker", "bottom sheet presentation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_filled_button/basic.py b/sdk/python/examples/controls/cupertino_filled_button/basic.py deleted file mode 100644 index 37191fbd27..0000000000 --- a/sdk/python/examples/controls/cupertino_filled_button/basic.py +++ /dev/null @@ -1,14 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.CupertinoFilledButton( - content=ft.Text("CupertinoFilledButton"), - opacity_on_click=0.3, - on_click=lambda e: print("CupertinoFilledButton clicked!"), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_filled_button/basic/main.py b/sdk/python/examples/controls/cupertino_filled_button/basic/main.py new file mode 100644 index 0000000000..8f1d5d727d --- /dev/null +++ b/sdk/python/examples/controls/cupertino_filled_button/basic/main.py @@ -0,0 +1,17 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.CupertinoFilledButton( + opacity_on_click=0.3, + on_click=lambda _: print("CupertinoFilledButton clicked!"), + content=ft.Text("CupertinoFilledButton"), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_filled_button/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_filled_button/basic/pyproject.toml new file mode 100644 index 0000000000..66d70134c2 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_filled_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-filled-button-basic" +version = "1.0.0" +description = "Renders a CupertinoFilledButton with click opacity and click callback behavior." +requires-python = ">=3.10" +keywords = ["cupertino", "filled button", "button", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/CupertinoFilledButton"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "CupertinoFilledButton", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["custom click opacity", "button click callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_list_tile/notched.py b/sdk/python/examples/controls/cupertino_list_tile/notched.py deleted file mode 100644 index 44c9d85b6b..0000000000 --- a/sdk/python/examples/controls/cupertino_list_tile/notched.py +++ /dev/null @@ -1,30 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_tile_click(e: ft.Event[ft.CupertinoListTile]): - print("Tile clicked") - - page.add( - ft.CupertinoListTile( - additional_info=ft.Text("Wed Jan 24"), - bgcolor_activated=ft.Colors.AMBER_ACCENT, - leading=ft.Icon(ft.CupertinoIcons.GAME_CONTROLLER), - title=ft.Text("CupertinoListTile: notched = False"), - subtitle=ft.Text("Subtitle"), - trailing=ft.Icon(ft.CupertinoIcons.ALARM), - on_click=handle_tile_click, - ), - ft.CupertinoListTile( - notched=True, - additional_info=ft.Text("Thu Jan 25"), - leading=ft.Icon(ft.CupertinoIcons.GAME_CONTROLLER), - title=ft.Text("CupertinoListTile: notched = True"), - subtitle=ft.Text("Subtitle"), - trailing=ft.Icon(ft.CupertinoIcons.ALARM), - on_click=handle_tile_click, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_list_tile/notched/main.py b/sdk/python/examples/controls/cupertino_list_tile/notched/main.py new file mode 100644 index 0000000000..a60ccaefca --- /dev/null +++ b/sdk/python/examples/controls/cupertino_list_tile/notched/main.py @@ -0,0 +1,37 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_tile_click(_: ft.Event[ft.CupertinoListTile]): + print("Tile clicked") + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.CupertinoListTile( + additional_info=ft.Text("Wed Jan 24"), + bgcolor_activated=ft.Colors.AMBER_ACCENT, + leading=ft.Icon(ft.CupertinoIcons.GAME_CONTROLLER), + title=ft.Text("CupertinoListTile: notched = False"), + subtitle=ft.Text("Subtitle"), + trailing=ft.Icon(ft.CupertinoIcons.ALARM), + on_click=handle_tile_click, + ), + ft.CupertinoListTile( + notched=True, + additional_info=ft.Text("Thu Jan 25"), + leading=ft.Icon(ft.CupertinoIcons.GAME_CONTROLLER), + title=ft.Text("CupertinoListTile: notched = True"), + subtitle=ft.Text("Subtitle"), + trailing=ft.Icon(ft.CupertinoIcons.ALARM), + on_click=handle_tile_click, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_list_tile/notched/pyproject.toml b/sdk/python/examples/controls/cupertino_list_tile/notched/pyproject.toml new file mode 100644 index 0000000000..3e07330748 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_list_tile/notched/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-list-tile-notched" +version = "1.0.0" +description = "Compares CupertinoListTile with and without notched layout, including leading, trailing, and click actions." +requires-python = ">=3.10" +keywords = ["cupertino", "list tile", "notched", "layout", "click"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/CupertinoListTile"] + +[tool.flet.metadata] +title = "Notched" +controls = ["SafeArea", "Column", "CupertinoListTile", "Text", "Icon"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["notched tile", "tile click callback", "additional info"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_navigation_bar/basic.py b/sdk/python/examples/controls/cupertino_navigation_bar/basic.py deleted file mode 100644 index 247b797634..0000000000 --- a/sdk/python/examples/controls/cupertino_navigation_bar/basic.py +++ /dev/null @@ -1,34 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "CupertinoNavigationBar Example" - - page.navigation_bar = ft.CupertinoNavigationBar( - bgcolor=ft.Colors.AMBER_100, - inactive_color=ft.Colors.GREY, - active_color=ft.Colors.BLACK, - on_change=lambda e: print("Selected tab:", e.control.selected_index), - destinations=[ - ft.NavigationBarDestination( - icon=ft.Icons.EXPLORE_OUTLINED, - selected_icon=ft.Icons.EXPLORE, - label="Explore", - ), - ft.NavigationBarDestination( - icon=ft.Icons.COMMUTE_OUTLINED, - selected_icon=ft.Icons.COMMUTE, - label="Commute", - ), - ft.NavigationBarDestination( - icon=ft.Icons.BOOKMARK_BORDER, - selected_icon=ft.Icons.BOOKMARK, - label="Favorites", - ), - ], - ) - - page.add(ft.SafeArea(content=ft.Text("Body!"))) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_navigation_bar/basic/main.py b/sdk/python/examples/controls/cupertino_navigation_bar/basic/main.py new file mode 100644 index 0000000000..8c622ac09d --- /dev/null +++ b/sdk/python/examples/controls/cupertino_navigation_bar/basic/main.py @@ -0,0 +1,39 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "CupertinoNavigationBar Example" + + page.navigation_bar = ft.CupertinoNavigationBar( + bgcolor=ft.Colors.AMBER_100, + inactive_color=ft.Colors.GREY, + active_color=ft.Colors.BLACK, + on_change=lambda e: print("Selected tab:", e.control.selected_index), + destinations=[ + ft.NavigationBarDestination( + icon=ft.Icons.EXPLORE_OUTLINED, + selected_icon=ft.Icons.EXPLORE, + label="Explore", + ), + ft.NavigationBarDestination( + icon=ft.Icons.COMMUTE_OUTLINED, + selected_icon=ft.Icons.COMMUTE, + label="Commute", + ), + ft.NavigationBarDestination( + icon=ft.Icons.BOOKMARK_BORDER, + selected_icon=ft.Icons.BOOKMARK, + label="Favorites", + ), + ], + ) + + page.add( + ft.SafeArea( + content=ft.Text("Body!"), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_navigation_bar/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_navigation_bar/basic/pyproject.toml new file mode 100644 index 0000000000..9883ea5523 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_navigation_bar/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-navigation-bar-basic" +version = "1.0.0" +description = "Shows a CupertinoNavigationBar with three destinations and selected tab callback logging." +requires-python = ">=3.10" +keywords = ["cupertino", "navigation bar", "tabs", "destinations"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/CupertinoNavigationBar"] + +[tool.flet.metadata] +title = "Basic" +controls = ["CupertinoNavigationBar", "NavigationBarDestination", "SafeArea", "Text"] +layout_pattern = "tabbed-navigation" +complexity = "basic" +features = ["bottom navigation", "destination selection callback", "active and inactive colors"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_navigation_bar/wired.py b/sdk/python/examples/controls/cupertino_navigation_bar/wired.py deleted file mode 100644 index 771e8b7441..0000000000 --- a/sdk/python/examples/controls/cupertino_navigation_bar/wired.py +++ /dev/null @@ -1,45 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "CupertinoNavigationBar Example" - - def handle_nav_destination_change(e: ft.Event[ft.CupertinoNavigationBar]): - if e.control.selected_index == 0: - body.content.value = "Explore!" - elif e.control.selected_index == 1: - body.content.value = "Find Your Way!" - else: - body.content.value = "Your Favorites!" - page.update() - - page.navigation_bar = ft.CupertinoNavigationBar( - bgcolor=ft.Colors.AMBER_100, - inactive_color=ft.Colors.GREY, - active_color=ft.Colors.BLACK, - on_change=handle_nav_destination_change, - destinations=[ - ft.NavigationBarDestination( - icon=ft.Icons.EXPLORE_OUTLINED, - selected_icon=ft.Icons.EXPLORE, - label="Explore", - ), - ft.NavigationBarDestination( - icon=ft.Icons.COMMUTE_OUTLINED, - selected_icon=ft.Icons.COMMUTE, - label="Commute", - ), - ft.NavigationBarDestination( - icon=ft.Icons.BOOKMARK_BORDER, - selected_icon=ft.Icons.BOOKMARK, - label="Favorites", - ), - ], - ) - - page.add( - body := ft.SafeArea(content=ft.Text("Explore!")), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_navigation_bar/wired/main.py b/sdk/python/examples/controls/cupertino_navigation_bar/wired/main.py new file mode 100644 index 0000000000..bd46019cc1 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_navigation_bar/wired/main.py @@ -0,0 +1,49 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "CupertinoNavigationBar Example" + + body_text = ft.Text("Explore!") + + def handle_nav_destination_change(e: ft.Event[ft.CupertinoNavigationBar]): + if e.control.selected_index == 0: + body_text.value = "Explore!" + elif e.control.selected_index == 1: + body_text.value = "Find Your Way!" + else: + body_text.value = "Your Favorites!" + + page.navigation_bar = ft.CupertinoNavigationBar( + bgcolor=ft.Colors.AMBER_100, + inactive_color=ft.Colors.GREY, + active_color=ft.Colors.BLACK, + on_change=handle_nav_destination_change, + destinations=[ + ft.NavigationBarDestination( + icon=ft.Icons.EXPLORE_OUTLINED, + selected_icon=ft.Icons.EXPLORE, + label="Explore", + ), + ft.NavigationBarDestination( + icon=ft.Icons.COMMUTE_OUTLINED, + selected_icon=ft.Icons.COMMUTE, + label="Commute", + ), + ft.NavigationBarDestination( + icon=ft.Icons.BOOKMARK_BORDER, + selected_icon=ft.Icons.BOOKMARK, + label="Favorites", + ), + ], + ) + + page.add( + ft.SafeArea( + content=body_text, + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_navigation_bar/wired/pyproject.toml b/sdk/python/examples/controls/cupertino_navigation_bar/wired/pyproject.toml new file mode 100644 index 0000000000..ed3db35d10 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_navigation_bar/wired/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-navigation-bar-wired" +version = "1.0.0" +description = "Wires CupertinoNavigationBar selection to update the page body text for each destination." +requires-python = ">=3.10" +keywords = ["cupertino", "navigation bar", "state", "destinations", "events"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/CupertinoNavigationBar"] + +[tool.flet.metadata] +title = "Wired" +controls = ["CupertinoNavigationBar", "NavigationBarDestination", "SafeArea", "Text"] +layout_pattern = "tabbed-navigation" +complexity = "basic" +features = ["selection-driven content", "destination change handler"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_picker/fruit_selection.py b/sdk/python/examples/controls/cupertino_picker/fruit_selection.py deleted file mode 100644 index ad38f6a3d5..0000000000 --- a/sdk/python/examples/controls/cupertino_picker/fruit_selection.py +++ /dev/null @@ -1,52 +0,0 @@ -import flet as ft - -FRUITS = [ - "Apple", - "Mango", - "Banana", - "Orange", - "Pineapple", - "Strawberry", -] - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - selected_fruit_ref = ft.Ref[ft.Text]() - - def handle_selection_change(e: ft.Event[ft.CupertinoPicker]): - selected_fruit_ref.current.value = FRUITS[int(e.data)] - page.update() - - cupertino_picker = ft.CupertinoPicker( - selected_index=3, - magnification=1.22, - squeeze=1.2, - use_magnifier=True, - on_change=handle_selection_change, - controls=[ft.Text(value=f) for f in FRUITS], - ) - - page.add( - ft.Row( - tight=True, - controls=[ - ft.Text("Selected Fruit:", size=23), - ft.TextButton( - content=ft.Text(value=FRUITS[3], ref=selected_fruit_ref, size=23), - style=ft.ButtonStyle(color=ft.Colors.BLUE), - on_click=lambda e: page.show_dialog( - ft.CupertinoBottomSheet( - content=cupertino_picker, - height=216, - padding=ft.Padding.only(top=6), - ) - ), - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_picker/fruit_selection/main.py b/sdk/python/examples/controls/cupertino_picker/fruit_selection/main.py new file mode 100644 index 0000000000..ba028fea93 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_picker/fruit_selection/main.py @@ -0,0 +1,54 @@ +import flet as ft + +FRUITS = [ + "Apple", + "Mango", + "Banana", + "Orange", + "Pineapple", + "Strawberry", +] + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + selected_fruit_text = ft.Text(value=FRUITS[3], size=23) + + def handle_selection_change(e: ft.Event[ft.CupertinoPicker]): + selected_fruit_text.value = FRUITS[int(e.data)] + + cupertino_picker = ft.CupertinoPicker( + selected_index=3, + magnification=1.22, + squeeze=1.2, + use_magnifier=True, + on_change=handle_selection_change, + controls=[ft.Text(value=f) for f in FRUITS], + ) + + page.add( + ft.SafeArea( + content=ft.Row( + tight=True, + controls=[ + ft.Text("Selected Fruit:", size=23), + ft.TextButton( + style=ft.ButtonStyle(color=ft.Colors.BLUE), + on_click=lambda _: page.show_dialog( + ft.CupertinoBottomSheet( + height=216, + padding=ft.Padding.only(top=6), + content=cupertino_picker, + ) + ), + content=selected_fruit_text, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_picker/fruit_selection/pyproject.toml b/sdk/python/examples/controls/cupertino_picker/fruit_selection/pyproject.toml new file mode 100644 index 0000000000..63f20899d9 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_picker/fruit_selection/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-picker-fruit-selection" +version = "1.0.0" +description = "Opens a CupertinoPicker in a bottom sheet to choose a fruit and reflect the selected value." +requires-python = ">=3.10" +keywords = ["cupertino", "picker", "bottom sheet", "selection", "dialog"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoPicker"] + +[tool.flet.metadata] +title = "Fruit selection" +controls = ["SafeArea", "Row", "Text", "TextButton", "CupertinoBottomSheet", "CupertinoPicker"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["picker selection callback", "bottom sheet presentation", "live value update"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_radio/basic.py b/sdk/python/examples/controls/cupertino_radio/basic.py deleted file mode 100644 index c7d7a3f359..0000000000 --- a/sdk/python/examples/controls/cupertino_radio/basic.py +++ /dev/null @@ -1,38 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_button_click(e: ft.Event[ft.ElevatedButton]): - message.value = f"Your favorite color is: {group.value}" - page.update() - - page.add( - ft.Text("Select your favorite color:"), - group := ft.RadioGroup( - content=ft.Column( - controls=[ - ft.CupertinoRadio( - value="red", - label="Red", - active_color=ft.Colors.RED_200, - inactive_color=ft.Colors.RED_600, - ), - ft.CupertinoRadio( - value="green", - label="Green", - fill_color=ft.Colors.GREEN, - ), - ft.CupertinoRadio( - value="blue", - label="Blue", - active_color=ft.Colors.BLUE, - ), - ] - ) - ), - ft.Button(content="Submit", on_click=handle_button_click), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_radio/basic/main.py b/sdk/python/examples/controls/cupertino_radio/basic/main.py new file mode 100644 index 0000000000..9a5dae4b75 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_radio/basic/main.py @@ -0,0 +1,48 @@ +import flet as ft + + +def main(page: ft.Page): + message = ft.Text() + + group = ft.RadioGroup( + content=ft.Column( + controls=[ + ft.CupertinoRadio( + value="red", + label="Red", + active_color=ft.Colors.RED_200, + inactive_color=ft.Colors.RED_600, + ), + ft.CupertinoRadio( + value="green", + label="Green", + fill_color=ft.Colors.GREEN, + ), + ft.CupertinoRadio( + value="blue", + label="Blue", + active_color=ft.Colors.BLUE, + ), + ], + ) + ) + + def handle_button_click(_: ft.Event[ft.Button]): + message.value = f"Your favorite color is: {group.value}" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Select your favorite color:"), + group, + ft.Button(content="Submit", on_click=handle_button_click), + message, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_radio/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_radio/basic/pyproject.toml new file mode 100644 index 0000000000..cda13ceab4 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_radio/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-radio-basic" +version = "1.0.0" +description = "Collects color selection with CupertinoRadio controls in a RadioGroup and submit action." +requires-python = ">=3.10" +keywords = ["cupertino", "radio", "radio group", "selection", "form"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoRadio"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "RadioGroup", "CupertinoRadio", "Text", "Button"] +layout_pattern = "form" +complexity = "basic" +features = ["single-choice selection", "submit callback", "custom active colors"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_radio/cupertino_material_and_adaptive.py b/sdk/python/examples/controls/cupertino_radio/cupertino_material_and_adaptive.py deleted file mode 100644 index d54a2e5c28..0000000000 --- a/sdk/python/examples/controls/cupertino_radio/cupertino_material_and_adaptive.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_button_click(e: ft.Event[ft.Button]): - message.value = f"Your favorite color is: {group.value}" - page.update() - - page.add( - ft.Text("Select your favorite color:"), - group := ft.RadioGroup( - content=ft.Column( - controls=[ - ft.CupertinoRadio( - value="red", - label="Red - Cupertino Radio", - active_color=ft.Colors.RED, - inactive_color=ft.Colors.RED, - ), - ft.Radio( - value="green", - label="Green - Material Radio", - fill_color=ft.Colors.GREEN, - ), - ft.Radio( - value="blue", - label="Blue - Adaptive Radio", - adaptive=True, - active_color=ft.Colors.BLUE, - ), - ] - ) - ), - ft.Button(content="Submit", on_click=handle_button_click), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_radio/cupertino_material_and_adaptive/main.py b/sdk/python/examples/controls/cupertino_radio/cupertino_material_and_adaptive/main.py new file mode 100644 index 0000000000..e7db545a6c --- /dev/null +++ b/sdk/python/examples/controls/cupertino_radio/cupertino_material_and_adaptive/main.py @@ -0,0 +1,49 @@ +import flet as ft + + +def main(page: ft.Page): + message = ft.Text() + + group = ft.RadioGroup( + content=ft.Column( + controls=[ + ft.CupertinoRadio( + value="red", + label="Red - Cupertino Radio", + active_color=ft.Colors.RED, + inactive_color=ft.Colors.RED, + ), + ft.Radio( + value="green", + label="Green - Material Radio", + fill_color=ft.Colors.GREEN, + ), + ft.Radio( + value="blue", + label="Blue - Adaptive Radio", + adaptive=True, + active_color=ft.Colors.BLUE, + ), + ], + ) + ) + + def handle_button_click(_: ft.Event[ft.Button]): + message.value = f"Your favorite color is: {group.value}" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Select your favorite color:"), + group, + ft.Button(content="Submit", on_click=handle_button_click), + message, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_radio/cupertino_material_and_adaptive/pyproject.toml b/sdk/python/examples/controls/cupertino_radio/cupertino_material_and_adaptive/pyproject.toml new file mode 100644 index 0000000000..5649e80abe --- /dev/null +++ b/sdk/python/examples/controls/cupertino_radio/cupertino_material_and_adaptive/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-radio-cupertino-material-and-adaptive" +version = "1.0.0" +description = "Compares Cupertino, Material, and adaptive radio controls inside one RadioGroup workflow." +requires-python = ">=3.10" +keywords = ["cupertino", "radio", "adaptive", "material", "comparison"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoRadio"] + +[tool.flet.metadata] +title = "Cupertino, material and adaptive" +controls = ["SafeArea", "Column", "RadioGroup", "CupertinoRadio", "Radio", "Text", "Button"] +layout_pattern = "form" +complexity = "basic" +features = ["adaptive radio", "cupertino and material comparison", "submit callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_segmented_button/basic.py b/sdk/python/examples/controls/cupertino_segmented_button/basic.py deleted file mode 100644 index 0dcd7d3529..0000000000 --- a/sdk/python/examples/controls/cupertino_segmented_button/basic.py +++ /dev/null @@ -1,28 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - page.add( - ft.CupertinoSegmentedButton( - selected_index=1, - selected_color=ft.Colors.RED_400, - on_change=lambda e: print(f"selected_index: {e.data}"), - padding=ft.Padding.symmetric(vertical=20, horizontal=50), - controls=[ - ft.Text("One"), - ft.Container( - padding=ft.Padding.symmetric(vertical=10, horizontal=30), - content=ft.Text("Two"), - ), - ft.Container( - padding=ft.Padding.symmetric(vertical=5, horizontal=10), - content=ft.Text("Three"), - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_segmented_button/basic/main.py b/sdk/python/examples/controls/cupertino_segmented_button/basic/main.py new file mode 100644 index 0000000000..2c53dbab8d --- /dev/null +++ b/sdk/python/examples/controls/cupertino_segmented_button/basic/main.py @@ -0,0 +1,31 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + page.add( + ft.SafeArea( + content=ft.CupertinoSegmentedButton( + selected_index=1, + selected_color=ft.Colors.RED_400, + on_change=lambda e: print(f"selected_index: {e.data}"), + padding=ft.Padding.symmetric(vertical=20, horizontal=50), + controls=[ + ft.Text("One"), + ft.Container( + padding=ft.Padding.symmetric(vertical=10, horizontal=30), + content=ft.Text("Two"), + ), + ft.Container( + padding=ft.Padding.symmetric(vertical=5, horizontal=10), + content=ft.Text("Three"), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_segmented_button/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_segmented_button/basic/pyproject.toml new file mode 100644 index 0000000000..5a95cc6b29 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_segmented_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-segmented-button-basic" +version = "1.0.0" +description = "Shows a CupertinoSegmentedButton with mixed segment content sizes and selection events." +requires-python = ">=3.10" +keywords = ["cupertino", "segmented button", "selection", "buttons"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/CupertinoSegmentedButton"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "CupertinoSegmentedButton", "Container", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["segment selection callback", "custom segment padding", "selected color"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_segmented_button/segments_padding.py b/sdk/python/examples/controls/cupertino_segmented_button/segments_padding.py deleted file mode 100644 index b7879b6e02..0000000000 --- a/sdk/python/examples/controls/cupertino_segmented_button/segments_padding.py +++ /dev/null @@ -1,63 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - def handle_vertical_change(e: ft.Event[ft.Slider]): - segmented_button.controls[1].padding = ft.Padding.only( - top=e.control.value, bottom=e.control.value - ) - page.update() - - def handle_horizontal_change(e: ft.Event[ft.Slider]): - segmented_button.controls[2].padding = ft.Padding.only( - left=e.control.value, right=e.control.value - ) - page.update() - - page.add( - segmented_button := ft.CupertinoSegmentedButton( - selected_index=1, - selected_color=ft.Colors.RED_400, - unselected_color=ft.Colors.GREY_400, - on_change=lambda e: print(f"selected_index: {e.data}"), - controls=[ - ft.Text("All"), - ft.Container( - padding=ft.Padding.symmetric(vertical=30, horizontal=0), - content=ft.Text("None"), - ), - ft.Container( - padding=ft.Padding.symmetric(vertical=0, horizontal=30), - content=ft.Text("Some"), - ), - ], - ), - ft.Text("Vertical padding button 1: "), - ft.Slider( - label="{value}", - min=0, - max=50, - divisions=50, - value=30, - on_change=handle_vertical_change, - ), - ft.Text("Horizontal padding button 2:"), - ft.Slider( - label="{value}", - min=0, - max=50, - divisions=50, - value=30, - on_change=handle_horizontal_change, - ), - ft.Text( - value="*note that padding changes to one segment can effect padding on other segments*", - theme_style=ft.TextThemeStyle.LABEL_MEDIUM, - color=ft.Colors.ORANGE_ACCENT, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_segmented_button/segments_padding/main.py b/sdk/python/examples/controls/cupertino_segmented_button/segments_padding/main.py new file mode 100644 index 0000000000..89b8572a37 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_segmented_button/segments_padding/main.py @@ -0,0 +1,75 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + segmented_button = ft.CupertinoSegmentedButton( + selected_index=1, + selected_color=ft.Colors.RED_400, + unselected_color=ft.Colors.GREY_400, + on_change=lambda e: print(f"selected_index: {e.data}"), + controls=[ + ft.Text("All"), + ft.Container( + padding=ft.Padding.symmetric(vertical=30, horizontal=0), + content=ft.Text("None"), + ), + ft.Container( + padding=ft.Padding.symmetric(vertical=0, horizontal=30), + content=ft.Text("Some"), + ), + ], + ) + + def handle_vertical_change(e: ft.Event[ft.Slider]): + segmented_button.controls[1].padding = ft.Padding.only( + top=e.control.value, + bottom=e.control.value, + ) + + def handle_horizontal_change(e: ft.Event[ft.Slider]): + segmented_button.controls[2].padding = ft.Padding.only( + left=e.control.value, + right=e.control.value, + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + segmented_button, + ft.Text("Vertical padding button 1:"), + ft.Slider( + label="{value}", + min=0, + max=50, + divisions=50, + value=30, + on_change=handle_vertical_change, + ), + ft.Text("Horizontal padding button 2:"), + ft.Slider( + label="{value}", + min=0, + max=50, + divisions=50, + value=30, + on_change=handle_horizontal_change, + ), + ft.Text( + value=( + "*note that padding changes to one segment can effect " + "padding on other segments*" + ), + theme_style=ft.TextThemeStyle.LABEL_MEDIUM, + color=ft.Colors.ORANGE_ACCENT, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_segmented_button/segments_padding/pyproject.toml b/sdk/python/examples/controls/cupertino_segmented_button/segments_padding/pyproject.toml new file mode 100644 index 0000000000..cc9405d557 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_segmented_button/segments_padding/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-segmented-button-segments-padding" +version = "1.0.0" +description = "Adjusts CupertinoSegmentedButton segment padding interactively using sliders." +requires-python = ">=3.10" +keywords = ["cupertino", "segmented button", "padding", "slider", "interactive"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/CupertinoSegmentedButton"] + +[tool.flet.metadata] +title = "Segments padding" +controls = ["SafeArea", "Column", "CupertinoSegmentedButton", "Container", "Slider", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["interactive padding controls", "segment layout tuning", "selection callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_slider/handling_events.py b/sdk/python/examples/controls/cupertino_slider/handling_events.py deleted file mode 100644 index 01b4c31b20..0000000000 --- a/sdk/python/examples/controls/cupertino_slider/handling_events.py +++ /dev/null @@ -1,37 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.theme_mode = ft.ThemeMode.LIGHT - - def handle_change_start(e: ft.Event[ft.CupertinoSlider]): - slider_status.value = "Sliding" - page.update() - - def handle_change(e: ft.Event[ft.CupertinoSlider]): - slider_value.value = str(e.control.value) - page.update() - - def handle_change_end(e: ft.Event[ft.CupertinoSlider]): - slider_status.value = "Finished sliding" - page.update() - - page.add( - slider_value := ft.Text("0.0"), - ft.CupertinoSlider( - divisions=20, - min=0, - max=100, - active_color=ft.Colors.PURPLE, - thumb_color=ft.Colors.PURPLE, - on_change_start=handle_change_start, - on_change_end=handle_change_end, - on_change=handle_change, - ), - slider_status := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_slider/handling_events/main.py b/sdk/python/examples/controls/cupertino_slider/handling_events/main.py new file mode 100644 index 0000000000..bf0b5bbe24 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_slider/handling_events/main.py @@ -0,0 +1,45 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.theme_mode = ft.ThemeMode.LIGHT + + slider_value = ft.Text("0.0") + slider_status = ft.Text() + + def handle_change_start(_: ft.Event[ft.CupertinoSlider]): + slider_status.value = "Sliding" + + def handle_change(e: ft.Event[ft.CupertinoSlider]): + slider_value.value = str(e.control.value) + + def handle_change_end(_: ft.Event[ft.CupertinoSlider]): + slider_status.value = "Finished sliding" + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + slider_value, + ft.CupertinoSlider( + divisions=20, + min=0, + max=100, + active_color=ft.Colors.PURPLE, + thumb_color=ft.Colors.PURPLE, + on_change_start=handle_change_start, + on_change_end=handle_change_end, + on_change=handle_change, + ), + slider_status, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_slider/handling_events/pyproject.toml b/sdk/python/examples/controls/cupertino_slider/handling_events/pyproject.toml new file mode 100644 index 0000000000..5107f23c90 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_slider/handling_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-slider-handling-events" +version = "1.0.0" +description = "Tracks CupertinoSlider start, change, and end events with live status text updates." +requires-python = ">=3.10" +keywords = ["cupertino", "slider", "events", "input", "status"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoSlider"] + +[tool.flet.metadata] +title = "Handling events" +controls = ["SafeArea", "Column", "CupertinoSlider", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["on_change_start", "on_change", "on_change_end", "live value display"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_sliding_segmented_button/basic.py b/sdk/python/examples/controls/cupertino_sliding_segmented_button/basic.py deleted file mode 100644 index 94100bf757..0000000000 --- a/sdk/python/examples/controls/cupertino_sliding_segmented_button/basic.py +++ /dev/null @@ -1,27 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "CupertinoSlidingSegmentedButton Example" - page.theme_mode = ft.ThemeMode.LIGHT - - def handle_selection_change(e: ft.Event[ft.CupertinoSlidingSegmentedButton]): - page.show_dialog( - ft.SnackBar(ft.Text(f"Segment {e.control.selected_index + 1} was chosen!")) - ) - - page.add( - ft.CupertinoSlidingSegmentedButton( - selected_index=1, - thumb_color=ft.Colors.BLUE_400, - on_change=handle_selection_change, - controls=[ - ft.Text("One"), - ft.Text("Two"), - ft.Text("Three"), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_sliding_segmented_button/basic/main.py b/sdk/python/examples/controls/cupertino_sliding_segmented_button/basic/main.py new file mode 100644 index 0000000000..7202f24ef7 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_sliding_segmented_button/basic/main.py @@ -0,0 +1,30 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "CupertinoSlidingSegmentedButton Example" + page.theme_mode = ft.ThemeMode.LIGHT + + def handle_selection_change(e: ft.Event[ft.CupertinoSlidingSegmentedButton]): + page.show_dialog( + ft.SnackBar(ft.Text(f"Segment {e.control.selected_index + 1} was chosen!")) + ) + + page.add( + ft.SafeArea( + content=ft.CupertinoSlidingSegmentedButton( + selected_index=1, + thumb_color=ft.Colors.BLUE_400, + on_change=handle_selection_change, + controls=[ + ft.Text("One"), + ft.Text("Two"), + ft.Text("Three"), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_sliding_segmented_button/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_sliding_segmented_button/basic/pyproject.toml new file mode 100644 index 0000000000..1602c82675 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_sliding_segmented_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-sliding-segmented-button-basic" +version = "1.0.0" +description = "Displays CupertinoSlidingSegmentedButton and shows feedback when segment selection changes." +requires-python = ">=3.10" +keywords = ["cupertino", "sliding segmented button", "selection", "snackbar"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/CupertinoSlidingSegmentedButton"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "CupertinoSlidingSegmentedButton", "Text", "SnackBar"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["segment change callback", "selection feedback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_switch/cupertino_material_and_adaptive.py b/sdk/python/examples/controls/cupertino_switch/cupertino_material_and_adaptive.py deleted file mode 100644 index b6849d3a9f..0000000000 --- a/sdk/python/examples/controls/cupertino_switch/cupertino_material_and_adaptive.py +++ /dev/null @@ -1,29 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.CupertinoSwitch( - label="Cupertino Switch", - value=True, - ), - ft.Switch( - label="Material Switch", - value=True, - thumb_color={ft.ControlState.SELECTED: ft.Colors.BLUE}, - track_color=ft.Colors.YELLOW, - focus_color=ft.Colors.PURPLE, - ), - ft.Container(height=20), - ft.Text( - value="Adaptive Switch shows as CupertinoSwitch on macOS and iOS and as Switch on other platforms:" - ), - ft.Switch( - adaptive=True, - label="Adaptive Switch", - value=True, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_switch/cupertino_material_and_adaptive/main.py b/sdk/python/examples/controls/cupertino_switch/cupertino_material_and_adaptive/main.py new file mode 100644 index 0000000000..b8e59fc494 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_switch/cupertino_material_and_adaptive/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.CupertinoSwitch(label="Cupertino Switch", value=True), + ft.Switch( + label="Material Switch", + value=True, + thumb_color={ft.ControlState.SELECTED: ft.Colors.BLUE}, + track_color=ft.Colors.YELLOW, + focus_color=ft.Colors.PURPLE, + ), + ft.Container(height=20), + ft.Text( + value=( + "Adaptive Switch shows as CupertinoSwitch on macOS and " + "iOS and as Switch on other platforms:" + ) + ), + ft.Switch(adaptive=True, label="Adaptive Switch", value=True), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_switch/cupertino_material_and_adaptive/pyproject.toml b/sdk/python/examples/controls/cupertino_switch/cupertino_material_and_adaptive/pyproject.toml new file mode 100644 index 0000000000..1b90621e72 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_switch/cupertino_material_and_adaptive/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-switch-cupertino-material-and-adaptive" +version = "1.0.0" +description = "Shows Cupertino, Material, and adaptive switch controls with custom switch styling." +requires-python = ">=3.10" +keywords = ["cupertino", "switch", "adaptive", "material", "toggle"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoSwitch"] + +[tool.flet.metadata] +title = "Cupertino, material and adaptive" +controls = ["SafeArea", "Column", "CupertinoSwitch", "Switch", "Text", "Container"] +layout_pattern = "form" +complexity = "basic" +features = ["adaptive switch", "cupertino and material comparison", "custom thumb and track colors"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_text_field/background_image.py b/sdk/python/examples/controls/cupertino_text_field/background_image.py deleted file mode 100644 index 8123f706d2..0000000000 --- a/sdk/python/examples/controls/cupertino_text_field/background_image.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - page.add( - ctf := ft.CupertinoTextField( - label="Textfield Label", - label_style=ft.TextStyle(italic=True, weight=ft.FontWeight.BOLD), - bgcolor=ft.Colors.BLUE_GREY, - image=ft.DecorationImage( - src="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" - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_text_field/background_image/main.py b/sdk/python/examples/controls/cupertino_text_field/background_image/main.py new file mode 100644 index 0000000000..bd9c022ea0 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_text_field/background_image/main.py @@ -0,0 +1,20 @@ +import flet as ft + + +async def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + page.add( + ft.SafeArea( + content=ft.CupertinoTextField( + label="Textfield Label", + label_style=ft.TextStyle(italic=True, weight=ft.FontWeight.BOLD), + bgcolor=ft.Colors.BLUE_GREY, + image=ft.DecorationImage(src="https://picsum.photos/1000/260"), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_text_field/background_image/pyproject.toml b/sdk/python/examples/controls/cupertino_text_field/background_image/pyproject.toml new file mode 100644 index 0000000000..fcaf3d9f18 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_text_field/background_image/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-text-field-background-image" +version = "1.0.0" +description = "Applies a background decoration image to CupertinoTextField with custom label styling." +requires-python = ">=3.10" +keywords = ["cupertino", "text field", "background image", "styling"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoTextField"] + +[tool.flet.metadata] +title = "Background image" +controls = ["SafeArea", "CupertinoTextField", "TextStyle", "DecorationImage"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["text field background image", "custom label style"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_text_field/cupertino_material_and_adaptive.py b/sdk/python/examples/controls/cupertino_text_field/cupertino_material_and_adaptive.py deleted file mode 100644 index c579381b9c..0000000000 --- a/sdk/python/examples/controls/cupertino_text_field/cupertino_material_and_adaptive.py +++ /dev/null @@ -1,22 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.TextField( - label="Material text field", - label_style=ft.TextStyle(color=ft.Colors.GREY_400), - ), - ft.CupertinoTextField( - placeholder_text="Cupertino text field", - placeholder_style=ft.TextStyle(color=ft.Colors.GREY_400), - ), - ft.TextField( - adaptive=True, - label="Adaptive text field", - label_style=ft.TextStyle(color=ft.Colors.GREY_400), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_text_field/cupertino_material_and_adaptive/main.py b/sdk/python/examples/controls/cupertino_text_field/cupertino_material_and_adaptive/main.py new file mode 100644 index 0000000000..5243a7595d --- /dev/null +++ b/sdk/python/examples/controls/cupertino_text_field/cupertino_material_and_adaptive/main.py @@ -0,0 +1,29 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.TextField( + label="Material text field", + label_style=ft.TextStyle(color=ft.Colors.GREY_400), + ), + ft.CupertinoTextField( + placeholder_text="Cupertino text field", + placeholder_style=ft.TextStyle(color=ft.Colors.GREY_400), + ), + ft.TextField( + adaptive=True, + label="Adaptive text field", + label_style=ft.TextStyle(color=ft.Colors.GREY_400), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_text_field/cupertino_material_and_adaptive/pyproject.toml b/sdk/python/examples/controls/cupertino_text_field/cupertino_material_and_adaptive/pyproject.toml new file mode 100644 index 0000000000..fb14e28587 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_text_field/cupertino_material_and_adaptive/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-text-field-cupertino-material-and-adaptive" +version = "1.0.0" +description = "Compares Material, Cupertino, and adaptive text fields with placeholder and label styling." +requires-python = ">=3.10" +keywords = ["cupertino", "text field", "adaptive", "material", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoTextField"] + +[tool.flet.metadata] +title = "Cupertino, material and adaptive" +controls = ["SafeArea", "Column", "CupertinoTextField", "TextField", "TextStyle"] +layout_pattern = "form" +complexity = "basic" +features = ["adaptive text field", "cupertino and material comparison"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_text_field/selection_change.py b/sdk/python/examples/controls/cupertino_text_field/selection_change.py deleted file mode 100644 index d44e9be939..0000000000 --- a/sdk/python/examples/controls/cupertino_text_field/selection_change.py +++ /dev/null @@ -1,52 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Text selection" - - def handle_selection_change(e: ft.TextSelectionChangeEvent[ft.CupertinoTextField]): - selection.value = ( - f"Selection: '{e.selected_text}'" if e.selected_text else "No selection." - ) - selection_details.value = f"start={e.selection.start}, end={e.selection.end}" - caret.value = f"Caret position: {e.selection.end}" - - async def select_characters(e: ft.Event[ft.Button]): - await field.focus() - field.selection = ft.TextSelection( - base_offset=0, extent_offset=len(field.value) - ) - - async def move_caret(e: ft.Event[ft.Button]): - await field.focus() - field.selection = ft.TextSelection(base_offset=0, extent_offset=0) - - page.add( - ft.Column( - spacing=10, - controls=[ - field := ft.CupertinoTextField( - value="Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - multiline=True, - min_lines=3, - autofocus=True, - on_selection_change=handle_selection_change, - ), - selection := ft.Text("Select some text from the field."), - selection_details := ft.Text(), - caret := ft.Text("Caret position: -"), - ft.Button( - content="Select all text", - on_click=select_characters, - ), - ft.Button( - content="Move caret to start", - on_click=move_caret, - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_text_field/selection_change/main.py b/sdk/python/examples/controls/cupertino_text_field/selection_change/main.py new file mode 100644 index 0000000000..8f9318ba83 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_text_field/selection_change/main.py @@ -0,0 +1,55 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Text selection" + + selection = ft.Text("Select some text from the field.") + selection_details = ft.Text() + caret = ft.Text("Caret position: -") + + def handle_selection_change(e: ft.TextSelectionChangeEvent[ft.CupertinoTextField]): + selection.value = ( + f"Selection: '{e.selected_text}'" if e.selected_text else "No selection." + ) + selection_details.value = f"start={e.selection.start}, end={e.selection.end}" + caret.value = f"Caret position: {e.selection.end}" + + field = ft.CupertinoTextField( + value="Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + multiline=True, + min_lines=3, + autofocus=True, + on_selection_change=handle_selection_change, + ) + + async def select_characters(_: ft.Event[ft.Button]): + await field.focus() + field.selection = ft.TextSelection( + base_offset=0, + extent_offset=len(field.value), + ) + + async def move_caret(_: ft.Event[ft.Button]): + await field.focus() + field.selection = ft.TextSelection(base_offset=0, extent_offset=0) + + page.add( + ft.SafeArea( + content=ft.Column( + spacing=10, + controls=[ + field, + selection, + selection_details, + caret, + ft.Button(content="Select all text", on_click=select_characters), + ft.Button(content="Move caret to start", on_click=move_caret), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_text_field/selection_change/pyproject.toml b/sdk/python/examples/controls/cupertino_text_field/selection_change/pyproject.toml new file mode 100644 index 0000000000..ec7a8dee60 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_text_field/selection_change/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-text-field-selection-change" +version = "1.0.0" +description = "Handles CupertinoTextField text selection changes and provides buttons to select text or move caret." +requires-python = ">=3.10" +keywords = ["cupertino", "text field", "selection", "caret", "events"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoTextField"] + +[tool.flet.metadata] +title = "Selection change" +controls = ["SafeArea", "Column", "CupertinoTextField", "Text", "Button", "TextSelection"] +layout_pattern = "form" +complexity = "basic" +features = ["selection change callback", "programmatic selection", "caret control"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/cupertino_timer_picker/__init__.py b/sdk/python/examples/controls/cupertino_timer_picker/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/cupertino_timer_picker/basic.py b/sdk/python/examples/controls/cupertino_timer_picker/basic.py deleted file mode 100644 index fe522e926b..0000000000 --- a/sdk/python/examples/controls/cupertino_timer_picker/basic.py +++ /dev/null @@ -1,48 +0,0 @@ -import time - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - timer_value_text = ft.Text( - value="00:01:10", - size=23, - color=ft.CupertinoColors.DESTRUCTIVE_RED, - ) - - def handle_timer_picker_change(e: ft.Event[ft.CupertinoTimerPicker]): - timer_value_text.value = time.strftime("%H:%M:%S", time.gmtime(e.data)) - page.update() - - timer_picker = ft.CupertinoTimerPicker( - value=300, - second_interval=10, - minute_interval=1, - mode=ft.CupertinoTimerPickerMode.HOUR_MINUTE_SECONDS, - on_change=handle_timer_picker_change, - ) - - page.add( - ft.Row( - tight=True, - controls=[ - ft.Text("TimerPicker Value:", size=23), - ft.CupertinoButton( - content=timer_value_text, - on_click=lambda e: page.show_dialog( - ft.CupertinoBottomSheet( - content=timer_picker, - height=216, - padding=ft.Padding.only(top=6), - ) - ), - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_timer_picker/basic/main.py b/sdk/python/examples/controls/cupertino_timer_picker/basic/main.py new file mode 100644 index 0000000000..b427f5f376 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_timer_picker/basic/main.py @@ -0,0 +1,49 @@ +import time + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + timer_value_text = ft.Text( + value="00:01:10", + size=23, + color=ft.CupertinoColors.DESTRUCTIVE_RED, + ) + + def handle_timer_picker_change(e: ft.Event[ft.CupertinoTimerPicker]): + timer_value_text.value = time.strftime("%H:%M:%S", time.gmtime(e.data)) + + timer_picker = ft.CupertinoTimerPicker( + value=300, + second_interval=10, + minute_interval=1, + mode=ft.CupertinoTimerPickerMode.HOUR_MINUTE_SECONDS, + on_change=handle_timer_picker_change, + ) + + page.add( + ft.SafeArea( + content=ft.Row( + tight=True, + controls=[ + ft.Text("TimerPicker Value:", size=23), + ft.CupertinoButton( + on_click=lambda _: page.show_dialog( + ft.CupertinoBottomSheet( + height=216, + padding=ft.Padding.only(top=6), + content=timer_picker, + ) + ), + content=timer_value_text, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/cupertino_timer_picker/basic/pyproject.toml b/sdk/python/examples/controls/cupertino_timer_picker/basic/pyproject.toml new file mode 100644 index 0000000000..28334699da --- /dev/null +++ b/sdk/python/examples/controls/cupertino_timer_picker/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "cupertino-timer-picker-basic" +version = "1.0.0" +description = "Opens CupertinoTimerPicker in a bottom sheet and updates displayed timer value on change." +requires-python = ">=3.10" +keywords = ["cupertino", "timer picker", "bottom sheet", "time", "events"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/CupertinoTimerPicker"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Row", "Text", "CupertinoButton", "CupertinoBottomSheet", "CupertinoTimerPicker"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["timer selection", "on_change callback", "bottom sheet presentation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/data_table/__init__.py b/sdk/python/examples/controls/data_table/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/data_table/adaptive_row_heights.py b/sdk/python/examples/controls/data_table/adaptive_row_heights.py deleted file mode 100644 index ef0e3cc400..0000000000 --- a/sdk/python/examples/controls/data_table/adaptive_row_heights.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.DataTable( - width=560, - data_row_min_height=48, - data_row_max_height=float("inf"), # infinity to allow adaptive row heights - columns=[ - ft.DataColumn(label="Description"), - ft.DataColumn(label="Notes"), - ], - rows=[ - ft.DataRow( - cells=[ - ft.DataCell("TWO lines visible without overflow"), - ft.DataCell("Line 1\nLine 2"), - ], - ), - ft.DataRow( - cells=[ - ft.DataCell("FOUR lines visible without overflow"), - ft.DataCell("Line 1\nLine 2\nLine 3\nLine 4"), - ], - ), - ft.DataRow( - cells=[ - ft.DataCell("FIVE lines visible without overflow"), - ft.DataCell("Line 1\nLine 2\nLine 3\nLine 4\nLine 5"), - ], - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/data_table/adaptive_row_heights/main.py b/sdk/python/examples/controls/data_table/adaptive_row_heights/main.py new file mode 100644 index 0000000000..b274ac4738 --- /dev/null +++ b/sdk/python/examples/controls/data_table/adaptive_row_heights/main.py @@ -0,0 +1,41 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.DataTable( + width=560, + data_row_min_height=48, + data_row_max_height=float("inf"), + columns=[ + ft.DataColumn(label="Description"), + ft.DataColumn(label="Notes"), + ], + rows=[ + ft.DataRow( + cells=[ + ft.DataCell("TWO lines visible without overflow"), + ft.DataCell("Line 1\nLine 2"), + ], + ), + ft.DataRow( + cells=[ + ft.DataCell("FOUR lines visible without overflow"), + ft.DataCell("Line 1\nLine 2\nLine 3\nLine 4"), + ], + ), + ft.DataRow( + cells=[ + ft.DataCell("FIVE lines visible without overflow"), + ft.DataCell("Line 1\nLine 2\nLine 3\nLine 4\nLine 5"), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/data_table/adaptive_row_heights/pyproject.toml b/sdk/python/examples/controls/data_table/adaptive_row_heights/pyproject.toml new file mode 100644 index 0000000000..819ec0f036 --- /dev/null +++ b/sdk/python/examples/controls/data_table/adaptive_row_heights/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "data-table-adaptive-row-heights" +version = "1.0.0" +description = "Demonstrates adaptive DataTable row heights for multi-line cell content using infinite max row height." +requires-python = ">=3.10" +keywords = ["data table", "adaptive row height", "multiline", "cells"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/DataTable"] + +[tool.flet.metadata] +title = "Adaptive row heights" +controls = ["SafeArea", "DataTable", "DataColumn", "DataRow", "DataCell"] +layout_pattern = "table-view" +complexity = "basic" +features = ["adaptive row height", "multiline cell content"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/data_table/basic.py b/sdk/python/examples/controls/data_table/basic.py deleted file mode 100644 index 5efa856e95..0000000000 --- a/sdk/python/examples/controls/data_table/basic.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.DataTable( - expand=True, - columns=[ - ft.DataColumn(label=ft.Text("First name")), - ft.DataColumn(label=ft.Text("Last name")), - ft.DataColumn(label=ft.Text("Age"), numeric=True), - ], - rows=[ - ft.DataRow( - cells=[ - ft.DataCell(ft.Text("John")), - ft.DataCell(ft.Text("Smith")), - ft.DataCell(ft.Text("43")), - ], - ), - ft.DataRow( - cells=[ - ft.DataCell(ft.Text("Jack")), - ft.DataCell(ft.Text("Brown")), - ft.DataCell(ft.Text("19")), - ], - ), - ft.DataRow( - cells=[ - ft.DataCell(ft.Text("Alice")), - ft.DataCell(ft.Text("Wong")), - ft.DataCell(ft.Text("25")), - ], - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/data_table/basic/main.py b/sdk/python/examples/controls/data_table/basic/main.py new file mode 100644 index 0000000000..1aa53bbe3b --- /dev/null +++ b/sdk/python/examples/controls/data_table/basic/main.py @@ -0,0 +1,43 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.DataTable( + expand=True, + columns=[ + ft.DataColumn(label=ft.Text("First name")), + ft.DataColumn(label=ft.Text("Last name")), + ft.DataColumn(label=ft.Text("Age"), numeric=True), + ], + rows=[ + ft.DataRow( + cells=[ + ft.DataCell(ft.Text("John")), + ft.DataCell(ft.Text("Smith")), + ft.DataCell(ft.Text("43")), + ], + ), + ft.DataRow( + cells=[ + ft.DataCell(ft.Text("Jack")), + ft.DataCell(ft.Text("Brown")), + ft.DataCell(ft.Text("19")), + ], + ), + ft.DataRow( + cells=[ + ft.DataCell(ft.Text("Alice")), + ft.DataCell(ft.Text("Wong")), + ft.DataCell(ft.Text("25")), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/data_table/basic/pyproject.toml b/sdk/python/examples/controls/data_table/basic/pyproject.toml new file mode 100644 index 0000000000..0943448c32 --- /dev/null +++ b/sdk/python/examples/controls/data_table/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "data-table-basic" +version = "1.0.0" +description = "Shows a basic DataTable with text columns and numeric age values." +requires-python = ">=3.10" +keywords = ["data table", "rows", "columns", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/DataTable"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "DataTable", "DataColumn", "DataRow", "DataCell", "Text"] +layout_pattern = "table-view" +complexity = "basic" +features = ["tabular data", "numeric column"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/data_table/handling_events.py b/sdk/python/examples/controls/data_table/handling_events.py deleted file mode 100644 index 9b51a8504a..0000000000 --- a/sdk/python/examples/controls/data_table/handling_events.py +++ /dev/null @@ -1,82 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_row_selection_change(e: ft.Event[ft.DataRow]): - if e.control.data: - if e.control.data == 1: - row1.selected = not row1.selected - elif e.control.data == 2: - row2.selected = not row2.selected - elif e.control.data == 3: - row3.selected = not row3.selected - page.update() - - def handle_column_sort(e: ft.DataColumnSortEvent): - if e.control.data: - if e.control.data == 1: - print(f"{e.column_index}, {e.ascending}") - # table.sort_column_index = 1 - table.sort_ascending = e.ascending - elif e.control.data == 2: - print(f"{e.column_index}, {e.ascending}") - # table.sort_column_index = 2 - table.sort_ascending = e.ascending - page.update() - - page.add( - table := ft.DataTable( - width=700, - bgcolor=ft.Colors.TEAL_ACCENT_200, - border=ft.Border.all(2, ft.Colors.RED_ACCENT_200), - border_radius=10, - vertical_lines=ft.border.BorderSide(3, ft.Colors.BLUE_600), - horizontal_lines=ft.border.BorderSide(1, ft.Colors.GREEN_600), - sort_column_index=0, - sort_ascending=True, - heading_row_color=ft.Colors.BLACK_12, - heading_row_height=100, - data_row_color={ft.ControlState.HOVERED: "0x30FF0000"}, - show_checkbox_column=True, - divider_thickness=0, - column_spacing=200, - columns=[ - ft.DataColumn( - label=ft.Text("Column 1"), - tooltip="This is the first column", - data=1, - on_sort=handle_column_sort, - ), - ft.DataColumn( - label=ft.Text("Column 2"), - tooltip="This is a second column", - numeric=True, - data=2, - on_sort=handle_column_sort, - ), - ], - rows=[ - row1 := ft.DataRow( - cells=[ft.DataCell(ft.Text("A")), ft.DataCell(ft.Text("1"))], - selected=True, - on_select_change=handle_row_selection_change, - data=1, - ), - row2 := ft.DataRow( - cells=[ft.DataCell(ft.Text("B")), ft.DataCell(ft.Text("2"))], - selected=False, - on_select_change=handle_row_selection_change, - data=2, - ), - row3 := ft.DataRow( - cells=[ft.DataCell(ft.Text("C")), ft.DataCell(ft.Text("3"))], - selected=False, - on_select_change=handle_row_selection_change, - data=3, - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/data_table/handling_events/main.py b/sdk/python/examples/controls/data_table/handling_events/main.py new file mode 100644 index 0000000000..834ae328cb --- /dev/null +++ b/sdk/python/examples/controls/data_table/handling_events/main.py @@ -0,0 +1,81 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_row_selection_change(e: ft.Event[ft.DataRow]) -> None: + if e.control.data == 1: + row1.selected = not row1.selected + elif e.control.data == 2: + row2.selected = not row2.selected + elif e.control.data == 3: + row3.selected = not row3.selected + + table.update() + + def handle_column_sort(e: ft.DataColumnSortEvent) -> None: + if e.control.data in [1, 2]: + print(f"{e.column_index}, {e.ascending}") + table.sort_ascending = e.ascending + table.update() + + table = ft.DataTable( + width=700, + bgcolor=ft.Colors.TEAL_ACCENT_200, + border=ft.Border.all(2, ft.Colors.RED_ACCENT_200), + border_radius=10, + vertical_lines=ft.border.BorderSide(3, ft.Colors.BLUE_600), + horizontal_lines=ft.border.BorderSide(1, ft.Colors.GREEN_600), + sort_column_index=0, + sort_ascending=True, + heading_row_color=ft.Colors.BLACK_12, + heading_row_height=100, + data_row_color={ft.ControlState.HOVERED: "0x30FF0000"}, + show_checkbox_column=True, + divider_thickness=0, + column_spacing=200, + columns=[ + ft.DataColumn( + label=ft.Text("Column 1"), + tooltip="This is the first column", + data=1, + on_sort=handle_column_sort, + ), + ft.DataColumn( + label=ft.Text("Column 2"), + tooltip="This is a second column", + numeric=True, + data=2, + on_sort=handle_column_sort, + ), + ], + rows=[ + row1 := ft.DataRow( + cells=[ft.DataCell(ft.Text("A")), ft.DataCell(ft.Text("1"))], + selected=True, + on_select_change=handle_row_selection_change, + data=1, + ), + row2 := ft.DataRow( + cells=[ft.DataCell(ft.Text("B")), ft.DataCell(ft.Text("2"))], + selected=False, + on_select_change=handle_row_selection_change, + data=2, + ), + row3 := ft.DataRow( + cells=[ft.DataCell(ft.Text("C")), ft.DataCell(ft.Text("3"))], + selected=False, + on_select_change=handle_row_selection_change, + data=3, + ), + ], + ) + + page.add( + ft.SafeArea( + content=table, + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/data_table/handling_events/pyproject.toml b/sdk/python/examples/controls/data_table/handling_events/pyproject.toml new file mode 100644 index 0000000000..7b0ba7433e --- /dev/null +++ b/sdk/python/examples/controls/data_table/handling_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "data-table-handling-events" +version = "1.0.0" +description = "Handles DataTable row selection and column sort events with visual table state updates." +requires-python = ">=3.10" +keywords = ["data table", "events", "row selection", "sorting"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/DataTable"] + +[tool.flet.metadata] +title = "Handling events" +controls = ["SafeArea", "DataTable", "DataColumn", "DataRow", "DataCell", "Text"] +layout_pattern = "table-view" +complexity = "basic" +features = ["row selection callback", "column sort callback", "table state updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/data_table/sortable_and_selectable.py b/sdk/python/examples/controls/data_table/sortable_and_selectable.py deleted file mode 100644 index d8f4d39ffc..0000000000 --- a/sdk/python/examples/controls/data_table/sortable_and_selectable.py +++ /dev/null @@ -1,149 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - # Source data for the table (your domain objects). Each record has a stable `id` - # so we can track selection even when the table is sorted or rebuilt. - inventory_items = [ - {"id": 1, "name": "Alpha", "qty": 4}, - {"id": 2, "name": "Bravo", "qty": 9}, - {"id": 3, "name": "Charlie", "qty": 2}, - {"id": 4, "name": "Delta", "qty": 6}, - {"id": 5, "name": "Echo", "qty": 3}, - {"id": 6, "name": "Foxtrot", "qty": 8}, - {"id": 7, "name": "Golf", "qty": 1}, - {"id": 8, "name": "Hotel", "qty": 7}, - {"id": 9, "name": "India", "qty": 5}, - {"id": 10, "name": "Juliet", "qty": 10}, - ] - - # Working list used for sorting/reordering. We keep it separate so the original - # input remains untouched (useful if you later reload or re-filter data). - displayed_items = list(inventory_items) - - # Store selected item ids (not row indices) so selection survives sorting. - selected_item_ids: set[int] = {1, 3, 5} - - # Map column index -> callable used for sorting that column. - # Note: DataColumnSortEvent provides a `column_index` and an `ascending` flag. - sort_key_for_column = { - 0: lambda item: str(item["name"]).lower(), # "Item" column - 1: lambda item: int(item["qty"]), # "Quantity" column - } - - def build_rows(items: list[dict[str, int | str]]) -> list[ft.DataRow]: - """Convert a list of item dicts into DataRow objects.""" - return [ - ft.DataRow( - selected=item["id"] in selected_item_ids, - on_select_change=handle_row_selection_change, - data=item["id"], # used by event handlers to identify this item - cells=[ - ft.DataCell(ft.Text(item["name"])), - ft.DataCell(ft.Text(str(item["qty"]))), - ], - ) - for item in items - ] - - def refresh_table_rows(): - """ - Rebuild and redraw the table rows. - - Rebuilding rows is the simplest way to keep selection checkboxes and row - visuals consistent after a bulk change (sort, select-all, clear selection). - """ - table.rows = build_rows(displayed_items) - table.update() - - def handle_row_selection_change(e: ft.Event[ft.DataRow]): - """Called when a single row's checkbox is toggled.""" - row = e.control - item_id = row.data - is_selected = e.data # new selected state - - if is_selected: - selected_item_ids.add(item_id) - else: - selected_item_ids.discard(item_id) - - # Reflect the new state immediately on the toggled row. - e.control.selected = is_selected - e.control.update() - - def handle_select_all(e: ft.Event[ft.DataTable]): - """ - Called when the header "select all" checkbox is toggled. - - `e.data` is True when selecting all, False when clearing. - """ - select_all = e.data - - if select_all: - selected_item_ids.update(int(item["id"]) for item in displayed_items) - else: - selected_item_ids.clear() - - refresh_table_rows() - - def handle_column_sort(e: ft.DataColumnSortEvent): - """ - Called when a column header is clicked to sort. - - We sort `displayed_items` in-place and then refresh the rows. Selection is - preserved because it is tracked by item id in `selected_item_ids`. - """ - displayed_items.sort( - key=sort_key_for_column[e.column_index], - reverse=not e.ascending, - ) - - # Let the Table know which column is currently sorted and in what order. - table.sort_column_index = e.column_index - table.sort_ascending = e.ascending - - refresh_table_rows() - - page.add( - table := ft.DataTable( - width=700, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - border_radius=10, - vertical_lines=ft.border.BorderSide(1, ft.Colors.OUTLINE_VARIANT), - horizontal_lines=ft.border.BorderSide(1, ft.Colors.OUTLINE_VARIANT), - sort_column_index=0, - sort_ascending=True, - heading_row_color=ft.Colors.SURFACE_CONTAINER_HIGHEST, - heading_row_height=100, - data_row_color={ - ft.ControlState.HOVERED: ft.Colors.with_opacity( - 0.08, ft.Colors.PRIMARY - ), - ft.ControlState.SELECTED: ft.Colors.with_opacity( - 0.14, ft.Colors.PRIMARY - ), - }, - show_checkbox_column=True, - on_select_all=handle_select_all, - divider_thickness=1, - column_spacing=200, - columns=[ - ft.DataColumn( - label=ft.Text("Item"), - on_sort=handle_column_sort, - ), - ft.DataColumn( - label=ft.Text("Quantity"), - tooltip="Numeric quantity", - numeric=True, - on_sort=handle_column_sort, - ), - ], - rows=build_rows(displayed_items), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/data_table/sortable_and_selectable/main.py b/sdk/python/examples/controls/data_table/sortable_and_selectable/main.py new file mode 100644 index 0000000000..ba277b0ff2 --- /dev/null +++ b/sdk/python/examples/controls/data_table/sortable_and_selectable/main.py @@ -0,0 +1,116 @@ +import flet as ft + + +def main(page: ft.Page): + inventory_items = [ + {"id": 1, "name": "Alpha", "qty": 4}, + {"id": 2, "name": "Bravo", "qty": 9}, + {"id": 3, "name": "Charlie", "qty": 2}, + {"id": 4, "name": "Delta", "qty": 6}, + {"id": 5, "name": "Echo", "qty": 3}, + {"id": 6, "name": "Foxtrot", "qty": 8}, + {"id": 7, "name": "Golf", "qty": 1}, + {"id": 8, "name": "Hotel", "qty": 7}, + {"id": 9, "name": "India", "qty": 5}, + {"id": 10, "name": "Juliet", "qty": 10}, + ] + displayed_items = list(inventory_items) + selected_item_ids: set[int] = {1, 3, 5} + + sort_key_for_column = { + 0: lambda item: str(item["name"]).lower(), + 1: lambda item: int(item["qty"]), + } + + def build_rows(items: list[dict[str, int | str]]) -> list[ft.DataRow]: + return [ + ft.DataRow( + selected=item["id"] in selected_item_ids, + on_select_change=handle_row_selection_change, + data=item["id"], + cells=[ + ft.DataCell(ft.Text(item["name"])), + ft.DataCell(ft.Text(str(item["qty"]))), + ], + ) + for item in items + ] + + def refresh_table_rows() -> None: + table.rows = build_rows(displayed_items) + table.update() + + def handle_row_selection_change(e: ft.Event[ft.DataRow]) -> None: + row = e.control + item_id = row.data + is_selected = e.data + + if is_selected: + selected_item_ids.add(item_id) + else: + selected_item_ids.discard(item_id) + + row.selected = is_selected + row.update() + + def handle_select_all(e: ft.Event[ft.DataTable]) -> None: + if e.data: + selected_item_ids.update(int(item["id"]) for item in displayed_items) + else: + selected_item_ids.clear() + + refresh_table_rows() + + def handle_column_sort(e: ft.DataColumnSortEvent) -> None: + displayed_items.sort( + key=sort_key_for_column[e.column_index], + reverse=not e.ascending, + ) + + table.sort_column_index = e.column_index + table.sort_ascending = e.ascending + refresh_table_rows() + + table = ft.DataTable( + width=700, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + border_radius=10, + vertical_lines=ft.border.BorderSide(1, ft.Colors.OUTLINE_VARIANT), + horizontal_lines=ft.border.BorderSide(1, ft.Colors.OUTLINE_VARIANT), + sort_column_index=0, + sort_ascending=True, + heading_row_color=ft.Colors.SURFACE_CONTAINER_HIGHEST, + heading_row_height=100, + data_row_color={ + ft.ControlState.HOVERED: ft.Colors.with_opacity(0.08, ft.Colors.PRIMARY), + ft.ControlState.SELECTED: ft.Colors.with_opacity(0.14, ft.Colors.PRIMARY), + }, + show_checkbox_column=True, + on_select_all=handle_select_all, + divider_thickness=1, + column_spacing=200, + columns=[ + ft.DataColumn( + label=ft.Text("Item"), + on_sort=handle_column_sort, + ), + ft.DataColumn( + label=ft.Text("Quantity"), + tooltip="Numeric quantity", + numeric=True, + on_sort=handle_column_sort, + ), + ], + rows=build_rows(displayed_items), + ) + + page.add( + ft.SafeArea( + content=table, + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/data_table/sortable_and_selectable/pyproject.toml b/sdk/python/examples/controls/data_table/sortable_and_selectable/pyproject.toml new file mode 100644 index 0000000000..5a6031e82c --- /dev/null +++ b/sdk/python/examples/controls/data_table/sortable_and_selectable/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "data-table-sortable-and-selectable" +version = "1.0.0" +description = "Implements sortable DataTable columns and selectable rows while preserving selection across sorts." +requires-python = ">=3.10" +keywords = ["data table", "sorting", "selection", "checkbox", "events"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/DataTable"] + +[tool.flet.metadata] +title = "Sortable and selectable" +controls = ["SafeArea", "DataTable", "DataColumn", "DataRow", "DataCell", "Text"] +layout_pattern = "table-view" +complexity = "intermediate" +features = ["column sorting", "row selection", "select all", "stable selection across sorts"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/data_table/spacing.py b/sdk/python/examples/controls/data_table/spacing.py deleted file mode 100644 index 851325ada2..0000000000 --- a/sdk/python/examples/controls/data_table/spacing.py +++ /dev/null @@ -1,107 +0,0 @@ -import flet as ft - - -def _cell(label: str, color: str = ft.Colors.SURFACE_CONTAINER_HIGHEST) -> ft.DataCell: - return ft.DataCell( - ft.Container( - content=ft.Text(label, size=12, weight=ft.FontWeight.W_600), - width=90, - height=32, - alignment=ft.Alignment.CENTER, - bgcolor=color, - border=ft.Border.all(1, ft.Colors.BLACK_26), - ) - ) - - -def main(page: ft.Page): - page.scroll = ft.ScrollMode.AUTO - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.theme_mode = ft.ThemeMode.DARK - - def update_spacing(e: ft.Event[ft.Slider] = None): - table.horizontal_margin = horizontal_margin_slider.value - table.column_spacing = column_spacing_slider.value - page.update() - - def set_preset(horizontal_margin: float, column_spacing: float): - horizontal_margin_slider.value = horizontal_margin - column_spacing_slider.value = column_spacing - update_spacing() - - page.appbar = ft.AppBar(title="DataTable spacing") - page.add( - ft.Container( - width=520, - padding=12, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - border_radius=8, - content=ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text("horizontal_margin (outer edges)"), - horizontal_margin_slider := ft.Slider( - min=0, - max=40, - divisions=40, - value=16, - label="{value}", - on_change=update_spacing, - ), - ft.Text("column_spacing (between columns)"), - column_spacing_slider := ft.Slider( - min=0, - max=40, - divisions=40, - value=16, - label="{value}", - on_change=update_spacing, - ), - ft.Row( - wrap=True, - controls=[ - ft.FilledButton( - "Reset", - on_click=lambda _: set_preset(16, 16), - ), - ft.OutlinedButton( - "Compact preset", - on_click=lambda _: set_preset(0, 0), - ), - ft.OutlinedButton( - "Spacious preset", - on_click=lambda _: set_preset(24, 32), - ), - ], - ), - ], - ), - ), - table := ft.DataTable( - border=ft.Border.all(1, ft.Colors.ON_SURFACE_VARIANT), - horizontal_margin=horizontal_margin_slider.value, - column_spacing=column_spacing_slider.value, - horizontal_lines=ft.BorderSide(1, ft.Colors.ON_SURFACE_VARIANT), - vertical_lines=ft.BorderSide(1, ft.Colors.ON_SURFACE_VARIANT), - heading_row_height=40, - data_row_min_height=40, - data_row_max_height=40, - columns=[ - ft.DataColumn(label="Col A"), - ft.DataColumn(label="Col B"), - ft.DataColumn(label="Col C"), - ], - rows=[ - ft.DataRow( - cells=[_cell("A1"), _cell("B1"), _cell("C1")], - ), - ft.DataRow( - cells=[_cell("A2"), _cell("B2"), _cell("C2")], - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/data_table/spacing/main.py b/sdk/python/examples/controls/data_table/spacing/main.py new file mode 100644 index 0000000000..924dc2f5e0 --- /dev/null +++ b/sdk/python/examples/controls/data_table/spacing/main.py @@ -0,0 +1,120 @@ +import flet as ft + + +def _cell(label: str, color: str = ft.Colors.SURFACE_CONTAINER_HIGHEST) -> ft.DataCell: + return ft.DataCell( + ft.Container( + width=90, + height=32, + alignment=ft.Alignment.CENTER, + bgcolor=color, + border=ft.Border.all(1, ft.Colors.BLACK_26), + content=ft.Text(label, size=12, weight=ft.FontWeight.W_600), + ) + ) + + +def main(page: ft.Page): + page.scroll = ft.ScrollMode.AUTO + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.theme_mode = ft.ThemeMode.DARK + + def update_spacing() -> None: + table.horizontal_margin = horizontal_margin_slider.value + table.column_spacing = column_spacing_slider.value + table.update() + + def handle_spacing_change(_: ft.Event[ft.Slider]) -> None: + update_spacing() + + def set_preset(horizontal_margin: float, column_spacing: float) -> None: + horizontal_margin_slider.value = horizontal_margin + column_spacing_slider.value = column_spacing + horizontal_margin_slider.update() + column_spacing_slider.update() + update_spacing() + + horizontal_margin_slider = ft.Slider( + min=0, + max=40, + divisions=40, + value=16, + label="{value}", + on_change=handle_spacing_change, + ) + column_spacing_slider = ft.Slider( + min=0, + max=40, + divisions=40, + value=16, + label="{value}", + on_change=handle_spacing_change, + ) + + table = ft.DataTable( + border=ft.Border.all(1, ft.Colors.ON_SURFACE_VARIANT), + horizontal_margin=horizontal_margin_slider.value, + column_spacing=column_spacing_slider.value, + horizontal_lines=ft.BorderSide(1, ft.Colors.ON_SURFACE_VARIANT), + vertical_lines=ft.BorderSide(1, ft.Colors.ON_SURFACE_VARIANT), + heading_row_height=40, + data_row_min_height=40, + data_row_max_height=40, + columns=[ + ft.DataColumn(label="Col A"), + ft.DataColumn(label="Col B"), + ft.DataColumn(label="Col C"), + ], + rows=[ + ft.DataRow(cells=[_cell("A1"), _cell("B1"), _cell("C1")]), + ft.DataRow(cells=[_cell("A2"), _cell("B2"), _cell("C2")]), + ], + ) + + page.appbar = ft.AppBar(title="DataTable spacing") + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Container( + width=520, + padding=12, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + border_radius=8, + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text("horizontal_margin (outer edges)"), + horizontal_margin_slider, + ft.Text("column_spacing (between columns)"), + column_spacing_slider, + ft.Row( + wrap=True, + controls=[ + ft.FilledButton( + "Reset", + on_click=lambda _: set_preset(16, 16), + ), + ft.OutlinedButton( + "Compact preset", + on_click=lambda _: set_preset(0, 0), + ), + ft.OutlinedButton( + "Spacious preset", + on_click=lambda _: set_preset(24, 32), + ), + ], + ), + ], + ), + ), + table, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/data_table/spacing/pyproject.toml b/sdk/python/examples/controls/data_table/spacing/pyproject.toml new file mode 100644 index 0000000000..cb2d259a87 --- /dev/null +++ b/sdk/python/examples/controls/data_table/spacing/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "data-table-spacing" +version = "1.0.0" +description = "Adjusts DataTable horizontal margin and column spacing interactively with sliders and presets." +requires-python = ">=3.10" +keywords = ["data table", "spacing", "horizontal margin", "slider", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/DataTable"] + +[tool.flet.metadata] +title = "Spacing" +controls = ["SafeArea", "Column", "DataTable", "DataColumn", "DataRow", "DataCell", "Slider", "FilledButton", "OutlinedButton", "Container", "Text", "AppBar"] +layout_pattern = "dashboard" +complexity = "basic" +features = ["interactive spacing controls", "preset spacing buttons", "live table layout updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/datatable2/example_1.py b/sdk/python/examples/controls/datatable2/example_1.py deleted file mode 100644 index b5a3c56c3b..0000000000 --- a/sdk/python/examples/controls/datatable2/example_1.py +++ /dev/null @@ -1,19 +0,0 @@ -import flet_datatable2 as fdt - -import flet as ft - - -def main(page: ft.Page): - page.add( - fdt.DataTable2( - empty=ft.Text("This table is empty."), - columns=[ - fdt.DataColumn2(label=ft.Text("First name")), - fdt.DataColumn2(label=ft.Text("Last name")), - fdt.DataColumn2(label=ft.Text("Age"), numeric=True), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/datatable2/example_1/main.py b/sdk/python/examples/controls/datatable2/example_1/main.py new file mode 100644 index 0000000000..1ff6eca6c2 --- /dev/null +++ b/sdk/python/examples/controls/datatable2/example_1/main.py @@ -0,0 +1,21 @@ +import flet as ft +import flet_datatable2 as fdt + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=fdt.DataTable2( + empty=ft.Text("This table is empty."), + columns=[ + fdt.DataColumn2(label=ft.Text("First name")), + fdt.DataColumn2(label=ft.Text("Last name")), + fdt.DataColumn2(label=ft.Text("Age"), numeric=True), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/datatable2/example_1/pyproject.toml b/sdk/python/examples/controls/datatable2/example_1/pyproject.toml new file mode 100644 index 0000000000..ce80295c8e --- /dev/null +++ b/sdk/python/examples/controls/datatable2/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "datatable2-example-1" +version = "1.0.0" +description = "Shows an empty DataTable2 state with configured columns and placeholder content." +requires-python = ">=3.10" +keywords = ["datatable2", "data table", "empty state", "extension"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-datatable2"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/DataTable2"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "DataTable2", "DataColumn2", "Text"] +layout_pattern = "table-view" +complexity = "basic" +features = ["empty table state", "DataTable2 columns"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/datatable2/example_2.py b/sdk/python/examples/controls/datatable2/example_2.py deleted file mode 100644 index d929e034bf..0000000000 --- a/sdk/python/examples/controls/datatable2/example_2.py +++ /dev/null @@ -1,120 +0,0 @@ -from data import desserts - -import flet as ft -import flet_datatable2 as ftd - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - sorted_desserts = list(desserts) - data_table: ftd.DataTable2 | None = None - - def handle_row_selection_change(e: ft.Event[ftd.DataRow2]): - e.control.selected = not e.control.selected - e.control.update() - - def sort_column(e: ft.DataColumnSortEvent): - if data_table is None: - return - sorters = [ - lambda d: d.name.lower(), - lambda d: d.calories, - lambda d: d.fat, - lambda d: d.carbs, - lambda d: d.protein, - lambda d: d.sodium, - lambda d: d.calcium, - lambda d: d.iron, - ] - sorted_desserts.sort(key=sorters[e.column_index], reverse=not e.ascending) - data_table.rows = get_data_rows(sorted_desserts) - data_table.sort_column_index = e.column_index - data_table.sort_ascending = e.ascending - data_table.update() - - def get_data_columns(): - data_columns = [ - ftd.DataColumn2( - label=ft.Text("Name"), - size=ftd.DataColumnSize.L, - on_sort=sort_column, - heading_row_alignment=ft.MainAxisAlignment.START, - ), - ftd.DataColumn2( - label=ft.Text("Calories"), - on_sort=sort_column, - numeric=True, - heading_row_alignment=ft.MainAxisAlignment.END, - ), - ftd.DataColumn2( - label=ft.Text("Fat"), - on_sort=sort_column, - numeric=True, - ), - ftd.DataColumn2( - label=ft.Text("Carbs"), - on_sort=sort_column, - numeric=True, - ), - ftd.DataColumn2( - label=ft.Text("Protein"), - on_sort=sort_column, - numeric=True, - ), - ftd.DataColumn2( - label=ft.Text("Sodium"), - on_sort=sort_column, - numeric=True, - ), - ftd.DataColumn2( - label=ft.Text("Calcium"), - on_sort=sort_column, - numeric=True, - ), - ftd.DataColumn2( - label=ft.Text("Iron"), - on_sort=sort_column, - numeric=True, - ), - ] - return data_columns - - def get_data_rows(desserts): - data_rows = [] - for dessert in desserts: - data_rows.append( - ftd.DataRow2( - specific_row_height=50, - on_select_change=handle_row_selection_change, - cells=[ - ft.DataCell(content=ft.Text(dessert.name)), - ft.DataCell(content=ft.Text(dessert.calories)), - ft.DataCell(content=ft.Text(dessert.fat)), - ft.DataCell(content=ft.Text(dessert.carbs)), - ft.DataCell(content=ft.Text(dessert.protein)), - ft.DataCell(content=ft.Text(dessert.sodium)), - ft.DataCell(content=ft.Text(dessert.calcium)), - ft.DataCell(content=ft.Text(dessert.iron)), - ], - ) - ) - return data_rows - - data_table = ftd.DataTable2( - show_checkbox_column=True, - expand=True, - column_spacing=0, - heading_row_color=ft.Colors.SECONDARY_CONTAINER, - horizontal_margin=12, - sort_ascending=True, - bottom_margin=10, - min_width=600, - on_select_all=lambda e: print("All selected"), - columns=get_data_columns(), - rows=get_data_rows(sorted_desserts), - ) - page.add(data_table) - - -ft.run(main) diff --git a/sdk/python/examples/controls/datatable2/data.py b/sdk/python/examples/controls/datatable2/example_2/data.py similarity index 100% rename from sdk/python/examples/controls/datatable2/data.py rename to sdk/python/examples/controls/datatable2/example_2/data.py diff --git a/sdk/python/examples/controls/datatable2/example_2/main.py b/sdk/python/examples/controls/datatable2/example_2/main.py new file mode 100644 index 0000000000..46fcd83d2d --- /dev/null +++ b/sdk/python/examples/controls/datatable2/example_2/main.py @@ -0,0 +1,109 @@ +from data import desserts + +import flet as ft +import flet_datatable2 as ftd + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + sorted_desserts = list(desserts) + data_table: ftd.DataTable2 | None = None + + def handle_row_selection_change(e: ft.Event[ftd.DataRow2]) -> None: + e.control.selected = not e.control.selected + e.control.update() + + def sort_column(e: ft.DataColumnSortEvent) -> None: + if data_table is None: + return + + sorters = [ + lambda d: d.name.lower(), + lambda d: d.calories, + lambda d: d.fat, + lambda d: d.carbs, + lambda d: d.protein, + lambda d: d.sodium, + lambda d: d.calcium, + lambda d: d.iron, + ] + sorted_desserts.sort(key=sorters[e.column_index], reverse=not e.ascending) + data_table.rows = get_data_rows(sorted_desserts) + data_table.sort_column_index = e.column_index + data_table.sort_ascending = e.ascending + data_table.update() + + def get_data_columns() -> list[ftd.DataColumn2]: + return [ + ftd.DataColumn2( + label=ft.Text("Name"), + size=ftd.DataColumnSize.L, + on_sort=sort_column, + heading_row_alignment=ft.MainAxisAlignment.START, + ), + ftd.DataColumn2( + label=ft.Text("Calories"), + on_sort=sort_column, + numeric=True, + heading_row_alignment=ft.MainAxisAlignment.END, + ), + ftd.DataColumn2(label=ft.Text("Fat"), on_sort=sort_column, numeric=True), + ftd.DataColumn2(label=ft.Text("Carbs"), on_sort=sort_column, numeric=True), + ftd.DataColumn2( + label=ft.Text("Protein"), + on_sort=sort_column, + numeric=True, + ), + ftd.DataColumn2(label=ft.Text("Sodium"), on_sort=sort_column, numeric=True), + ftd.DataColumn2( + label=ft.Text("Calcium"), + on_sort=sort_column, + numeric=True, + ), + ftd.DataColumn2(label=ft.Text("Iron"), on_sort=sort_column, numeric=True), + ] + + def get_data_rows(items: list) -> list[ftd.DataRow2]: + return [ + ftd.DataRow2( + specific_row_height=50, + on_select_change=handle_row_selection_change, + cells=[ + ft.DataCell(content=ft.Text(dessert.name)), + ft.DataCell(content=ft.Text(dessert.calories)), + ft.DataCell(content=ft.Text(dessert.fat)), + ft.DataCell(content=ft.Text(dessert.carbs)), + ft.DataCell(content=ft.Text(dessert.protein)), + ft.DataCell(content=ft.Text(dessert.sodium)), + ft.DataCell(content=ft.Text(dessert.calcium)), + ft.DataCell(content=ft.Text(dessert.iron)), + ], + ) + for dessert in items + ] + + data_table = ftd.DataTable2( + show_checkbox_column=True, + expand=True, + column_spacing=0, + heading_row_color=ft.Colors.SECONDARY_CONTAINER, + horizontal_margin=12, + sort_ascending=True, + bottom_margin=10, + min_width=600, + on_select_all=lambda _: print("All selected"), + columns=get_data_columns(), + rows=get_data_rows(sorted_desserts), + ) + + page.add( + ft.SafeArea( + content=data_table, + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/datatable2/example_2/pyproject.toml b/sdk/python/examples/controls/datatable2/example_2/pyproject.toml new file mode 100644 index 0000000000..52a448cd1a --- /dev/null +++ b/sdk/python/examples/controls/datatable2/example_2/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "datatable2-example-2" +version = "1.0.0" +description = "Builds an interactive DataTable2 with sortable nutrition columns and selectable rows." +requires-python = ">=3.10" +keywords = ["datatable2", "sorting", "selection", "rows", "extension"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-datatable2"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/DataTable2"] + +[tool.flet.metadata] +title = "Example 2" +controls = ["SafeArea", "DataTable2", "DataColumn2", "DataRow2", "DataCell", "Text"] +layout_pattern = "table-view" +complexity = "basic" +features = ["column sorting", "row selection", "select-all callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/date_picker/basic.py b/sdk/python/examples/controls/date_picker/basic.py deleted file mode 100644 index b888792b83..0000000000 --- a/sdk/python/examples/controls/date_picker/basic.py +++ /dev/null @@ -1,33 +0,0 @@ -import datetime - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_change(e: ft.Event[ft.DatePicker]): - page.add(ft.Text(f"Date changed: {e.control.value.strftime('%m/%d/%Y')}")) - - def handle_dismissal(e: ft.Event[ft.DialogControl]): - page.add(ft.Text("DatePicker dismissed")) - - today = datetime.datetime.now() - - d = ft.DatePicker( - first_date=datetime.datetime(year=today.year - 1, month=1, day=1), - last_date=datetime.datetime(year=today.year + 1, month=today.month, day=20), - on_change=handle_change, - on_dismiss=handle_dismissal, - ) - - page.add( - ft.Button( - content="Pick date", - icon=ft.Icons.CALENDAR_MONTH, - on_click=lambda e: page.show_dialog(d), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/date_picker/basic/main.py b/sdk/python/examples/controls/date_picker/basic/main.py new file mode 100644 index 0000000000..eae14605f1 --- /dev/null +++ b/sdk/python/examples/controls/date_picker/basic/main.py @@ -0,0 +1,46 @@ +import datetime + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + messages = ft.Column(tight=True) + + def handle_change(e: ft.Event[ft.DatePicker]): + messages.controls.append( + ft.Text(f"Date changed: {e.control.value.strftime('%m/%d/%Y')}") + ) + + def handle_dismissal(_: ft.Event[ft.DialogControl]): + messages.controls.append(ft.Text("DatePicker dismissed")) + + today = datetime.datetime.now() + + picker = ft.DatePicker( + first_date=datetime.datetime(year=today.year - 1, month=1, day=1), + last_date=datetime.datetime(year=today.year + 1, month=today.month, day=20), + on_change=handle_change, + on_dismiss=handle_dismissal, + ) + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Button( + icon=ft.Icons.CALENDAR_MONTH, + on_click=lambda _: page.show_dialog(picker), + content="Pick date", + ), + messages, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/date_picker/basic/pyproject.toml b/sdk/python/examples/controls/date_picker/basic/pyproject.toml new file mode 100644 index 0000000000..3a637a72e7 --- /dev/null +++ b/sdk/python/examples/controls/date_picker/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "date-picker-basic" +version = "1.0.0" +description = "Opens DatePicker dialog and logs selected date and dismiss events in the page content." +requires-python = ">=3.10" +keywords = ["date picker", "dialog", "events", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/DatePicker"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "Button", "DatePicker", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["open dialog", "date change callback", "dismiss callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/date_picker/custom_locale.py b/sdk/python/examples/controls/date_picker/custom_locale.py deleted file mode 100644 index 7fb9d1924a..0000000000 --- a/sdk/python/examples/controls/date_picker/custom_locale.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.add( - ft.Button( - content="Pick date (zh_Hans locale)", - icon=ft.Icons.CALENDAR_MONTH, - on_click=lambda e: page.show_dialog( - ft.DatePicker(locale=ft.Locale("zh", "Hans")) - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/date_picker/custom_locale/main.py b/sdk/python/examples/controls/date_picker/custom_locale/main.py new file mode 100644 index 0000000000..3240e1167c --- /dev/null +++ b/sdk/python/examples/controls/date_picker/custom_locale/main.py @@ -0,0 +1,21 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.add( + ft.SafeArea( + content=ft.Button( + icon=ft.Icons.CALENDAR_MONTH, + on_click=lambda _: page.show_dialog( + ft.DatePicker(locale=ft.Locale("zh", "Hans")) + ), + content="Pick date (zh_Hans locale)", + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/date_picker/custom_locale/pyproject.toml b/sdk/python/examples/controls/date_picker/custom_locale/pyproject.toml new file mode 100644 index 0000000000..e1eaefa81c --- /dev/null +++ b/sdk/python/examples/controls/date_picker/custom_locale/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "date-picker-custom-locale" +version = "1.0.0" +description = "Opens DatePicker with zh_Hans locale to demonstrate localized calendar UI." +requires-python = ">=3.10" +keywords = ["date picker", "custom locale", "locale", "internationalization", "zh_Hans"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/DatePicker"] + +[tool.flet.metadata] +title = "Custom locale" +controls = ["SafeArea", "Button", "DatePicker", "Locale"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["localized date picker", "dialog open action"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/date_range_picker/basic.py b/sdk/python/examples/controls/date_range_picker/basic.py deleted file mode 100644 index bd59d6dfc6..0000000000 --- a/sdk/python/examples/controls/date_range_picker/basic.py +++ /dev/null @@ -1,38 +0,0 @@ -import datetime - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_change(e: ft.Event[ft.DateRangePicker]): - page.add( - ft.Text( - f"Start Date changed: {e.control.start_value.strftime('%m/%d/%Y')}" - ), - ft.Text(f"End Date changed: {e.control.end_value.strftime('%m/%d/%Y')}"), - ) - - def handle_dismissal(e: ft.Event[ft.DialogControl]): - page.add(ft.Text("DatePicker dismissed")) - - today = datetime.datetime.now() - - drp = ft.DateRangePicker( - start_value=datetime.datetime(year=today.year, month=today.month, day=1), - end_value=datetime.datetime(year=today.year, month=today.month, day=15), - on_change=handle_change, - on_dismiss=handle_dismissal, - ) - - page.add( - ft.Button( - content=ft.Text("Pick date"), - icon=ft.Icons.PHONE, - on_click=lambda e: page.show_dialog(drp), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/date_range_picker/basic/main.py b/sdk/python/examples/controls/date_range_picker/basic/main.py new file mode 100644 index 0000000000..3597e791ad --- /dev/null +++ b/sdk/python/examples/controls/date_range_picker/basic/main.py @@ -0,0 +1,53 @@ +import datetime + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + messages = ft.Column(tight=True) + + def handle_change(e: ft.Event[ft.DateRangePicker]): + messages.controls.extend( + [ + ft.Text( + f"Start Date changed: {e.control.start_value.strftime('%m/%d/%Y')}" + ), + ft.Text( + f"End Date changed: {e.control.end_value.strftime('%m/%d/%Y')}" + ), + ] + ) + + def handle_dismissal(_: ft.Event[ft.DialogControl]): + messages.controls.append(ft.Text("DateRangePicker dismissed")) + + today = datetime.datetime.now() + + picker = ft.DateRangePicker( + start_value=datetime.datetime(year=today.year, month=today.month, day=1), + end_value=datetime.datetime(year=today.year, month=today.month, day=15), + on_change=handle_change, + on_dismiss=handle_dismissal, + ) + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Button( + icon=ft.Icons.DATE_RANGE, + on_click=lambda _: page.show_dialog(picker), + content="Pick date range", + ), + messages, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/date_range_picker/basic/pyproject.toml b/sdk/python/examples/controls/date_range_picker/basic/pyproject.toml new file mode 100644 index 0000000000..277af9af5f --- /dev/null +++ b/sdk/python/examples/controls/date_range_picker/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "date-range-picker-basic" +version = "1.0.0" +description = "Opens DateRangePicker and displays start and end date values after selection." +requires-python = ">=3.10" +keywords = ["date range picker", "dialog", "events", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/DateRangePicker"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "Button", "DateRangePicker", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["open dialog", "range change callback", "dismiss callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/date_range_picker/custom_locale.py b/sdk/python/examples/controls/date_range_picker/custom_locale.py deleted file mode 100644 index 79853b7178..0000000000 --- a/sdk/python/examples/controls/date_range_picker/custom_locale.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.add( - ft.Button( - content="Pick dates (zh_Hans locale)", - icon=ft.Icons.CALENDAR_MONTH, - on_click=lambda e: page.show_dialog( - ft.DateRangePicker(locale=ft.Locale("zh", "Hans")) - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/date_range_picker/custom_locale/main.py b/sdk/python/examples/controls/date_range_picker/custom_locale/main.py new file mode 100644 index 0000000000..75d432c54b --- /dev/null +++ b/sdk/python/examples/controls/date_range_picker/custom_locale/main.py @@ -0,0 +1,21 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.add( + ft.SafeArea( + content=ft.Button( + icon=ft.Icons.CALENDAR_MONTH, + on_click=lambda _: page.show_dialog( + ft.DateRangePicker(locale=ft.Locale("zh", "Hans")) + ), + content="Pick dates (zh_Hans locale)", + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/date_range_picker/custom_locale/pyproject.toml b/sdk/python/examples/controls/date_range_picker/custom_locale/pyproject.toml new file mode 100644 index 0000000000..96095238e7 --- /dev/null +++ b/sdk/python/examples/controls/date_range_picker/custom_locale/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "date-range-picker-custom-locale" +version = "1.0.0" +description = "Opens DateRangePicker with zh_Hans locale for localized date range selection." +requires-python = ">=3.10" +keywords = ["date range picker", "custom locale", "locale", "internationalization", "zh_Hans"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/DateRangePicker"] + +[tool.flet.metadata] +title = "Custom locale" +controls = ["SafeArea", "Button", "DateRangePicker", "Locale"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["localized date range picker", "dialog open action"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dismissible/dismissible_list_tiles.py b/sdk/python/examples/controls/dismissible/dismissible_list_tiles.py deleted file mode 100644 index 585c402d8c..0000000000 --- a/sdk/python/examples/controls/dismissible/dismissible_list_tiles.py +++ /dev/null @@ -1,59 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - async def handle_dialog_action_click(e: ft.Event[ft.TextButton]): - page.pop_dialog() - await dlg.data.confirm_dismiss(e.control.data) - - dlg = ft.AlertDialog( - modal=True, - title=ft.Text("Please confirm"), - content=ft.Text("Do you really want to delete this item?"), - actions=[ - ft.TextButton("Yes", data=True, on_click=handle_dialog_action_click), - ft.TextButton("No", data=False, on_click=handle_dialog_action_click), - ], - actions_alignment=ft.MainAxisAlignment.CENTER, - ) - - async def handle_confirm_dismiss(e: ft.DismissibleDismissEvent): - if e.direction == ft.DismissDirection.END_TO_START: # right-to-left slide - # save current dismissible to dialog's data, for confirmation in - # handle_dialog_action_click - dlg.data = e.control - page.show_dialog(dlg) - else: # left-to-right slide - await e.control.confirm_dismiss(True) - - def handle_dismiss(e): - e.control.parent.controls.remove(e.control) - page.update() - - def handle_update(e: ft.DismissibleUpdateEvent): - print(e) - - page.add( - ft.ListView( - expand=True, - controls=[ - ft.Dismissible( - content=ft.ListTile(title=ft.Text(f"Item {i}")), - dismiss_direction=ft.DismissDirection.HORIZONTAL, - background=ft.Container(bgcolor=ft.Colors.GREEN), - secondary_background=ft.Container(bgcolor=ft.Colors.RED), - on_dismiss=handle_dismiss, - on_update=handle_update, - on_confirm_dismiss=handle_confirm_dismiss, - dismiss_thresholds={ - ft.DismissDirection.END_TO_START: 0.2, - ft.DismissDirection.START_TO_END: 0.2, - }, - ) - for i in range(10) - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dismissible/dismissible_list_tiles/main.py b/sdk/python/examples/controls/dismissible/dismissible_list_tiles/main.py new file mode 100644 index 0000000000..cbda275343 --- /dev/null +++ b/sdk/python/examples/controls/dismissible/dismissible_list_tiles/main.py @@ -0,0 +1,60 @@ +import flet as ft + + +def main(page: ft.Page): + async def handle_dialog_action_click(e: ft.Event[ft.TextButton]): + page.pop_dialog() + await dialog.data.confirm_dismiss(e.control.data) + + dialog = ft.AlertDialog( + modal=True, + title=ft.Text("Please confirm"), + content=ft.Text("Do you really want to delete this item?"), + actions=[ + ft.TextButton("Yes", data=True, on_click=handle_dialog_action_click), + ft.TextButton("No", data=False, on_click=handle_dialog_action_click), + ], + actions_alignment=ft.MainAxisAlignment.CENTER, + ) + + async def handle_confirm_dismiss(e: ft.DismissibleDismissEvent): + if e.direction == ft.DismissDirection.END_TO_START: + dialog.data = e.control + page.show_dialog(dialog) + else: + await e.control.confirm_dismiss(True) + + def handle_dismiss(e: ft.Event[ft.Dismissible]): + e.control.parent.controls.remove(e.control) + e.control.parent.update() + + def handle_update(e: ft.DismissibleUpdateEvent): + print(e) + + page.add( + ft.SafeArea( + content=ft.ListView( + expand=True, + controls=[ + ft.Dismissible( + dismiss_direction=ft.DismissDirection.HORIZONTAL, + background=ft.Container(bgcolor=ft.Colors.GREEN), + secondary_background=ft.Container(bgcolor=ft.Colors.RED), + on_dismiss=handle_dismiss, + on_update=handle_update, + on_confirm_dismiss=handle_confirm_dismiss, + dismiss_thresholds={ + ft.DismissDirection.END_TO_START: 0.2, + ft.DismissDirection.START_TO_END: 0.2, + }, + content=ft.ListTile(title=ft.Text(f"Item {i}")), + ) + for i in range(10) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dismissible/dismissible_list_tiles/pyproject.toml b/sdk/python/examples/controls/dismissible/dismissible_list_tiles/pyproject.toml new file mode 100644 index 0000000000..1f6138ddd9 --- /dev/null +++ b/sdk/python/examples/controls/dismissible/dismissible_list_tiles/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dismissible-list-tiles" +version = "1.0.0" +description = "Demonstrates dismissible ListTile items with per-direction backgrounds and confirm-dismiss dialog." +requires-python = ">=3.10" +keywords = ["dismissible", "list", "swipe", "confirmation", "dialog"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Dismissible"] + +[tool.flet.metadata] +title = "Dismissible ListTiles" +controls = ["SafeArea", "ListView", "Dismissible", "ListTile", "AlertDialog", "TextButton", "Container", "Text"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["swipe to dismiss", "confirm dismiss callback", "dismiss progress callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dismissible/remove_on_dismiss_declarative.py b/sdk/python/examples/controls/dismissible/remove_on_dismiss_declarative.py deleted file mode 100644 index 812124f30e..0000000000 --- a/sdk/python/examples/controls/dismissible/remove_on_dismiss_declarative.py +++ /dev/null @@ -1,29 +0,0 @@ -import flet as ft - - -@ft.component -def App(): - items, set_items = ft.use_state(list(range(5))) - - return ft.ListView( - controls=[ - ft.Dismissible( - key=i, - content=ft.ListTile(title=ft.Text(f"Item {i}")), - dismiss_direction=ft.DismissDirection.HORIZONTAL, - background=ft.Container(bgcolor=ft.Colors.GREEN), - secondary_background=ft.Container(bgcolor=ft.Colors.RED), - on_dismiss=lambda e, index=i: set_items( - [item for item in items if item != index] - ), - dismiss_thresholds={ - ft.DismissDirection.HORIZONTAL: 0.1, - ft.DismissDirection.START_TO_END: 0.1, - }, - ) - for i in items - ], - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/controls/dismissible/remove_on_dismiss_declarative/main.py b/sdk/python/examples/controls/dismissible/remove_on_dismiss_declarative/main.py new file mode 100644 index 0000000000..809c235a3d --- /dev/null +++ b/sdk/python/examples/controls/dismissible/remove_on_dismiss_declarative/main.py @@ -0,0 +1,36 @@ +import flet as ft + + +@ft.component +def App(): + items, set_items = ft.use_state(list(range(5))) + + return ft.SafeArea( + content=ft.ListView( + controls=[ + ft.Dismissible( + key=i, + dismiss_direction=ft.DismissDirection.HORIZONTAL, + background=ft.Container(bgcolor=ft.Colors.GREEN), + secondary_background=ft.Container(bgcolor=ft.Colors.RED), + on_dismiss=lambda _, index=i: set_items( + [item for item in items if item != index] + ), + dismiss_thresholds={ + ft.DismissDirection.HORIZONTAL: 0.1, + ft.DismissDirection.START_TO_END: 0.1, + }, + content=ft.ListTile(title=ft.Text(f"Item {i}")), + ) + for i in items + ], + ), + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dismissible/remove_on_dismiss_declarative/pyproject.toml b/sdk/python/examples/controls/dismissible/remove_on_dismiss_declarative/pyproject.toml new file mode 100644 index 0000000000..a4f3eebd5b --- /dev/null +++ b/sdk/python/examples/controls/dismissible/remove_on_dismiss_declarative/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dismissible-remove-on-dismiss-declarative" +version = "1.0.0" +description = "Shows declarative Dismissible list updates by removing items from component state on dismiss." +requires-python = ">=3.10" +keywords = ["dismissible", "declarative", "component", "state", "list"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Dismissible"] + +[tool.flet.metadata] +title = "Remove on dismiss declarative" +controls = ["SafeArea", "ListView", "Dismissible", "ListTile"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["declarative state updates", "swipe to dismiss", "stable dismissible keys"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/divider/basic.py b/sdk/python/examples/controls/divider/basic.py deleted file mode 100644 index e910379cad..0000000000 --- a/sdk/python/examples/controls/divider/basic.py +++ /dev/null @@ -1,38 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Column( - spacing=0, - expand=True, - controls=[ - ft.Container( - expand=True, - bgcolor=ft.Colors.AMBER, - alignment=ft.Alignment.CENTER, - ), - ft.Divider(), - ft.Container( - expand=True, - bgcolor=ft.Colors.PINK, - alignment=ft.Alignment.CENTER, - ), - ft.Divider(height=1, color=ft.Colors.WHITE), - ft.Container( - expand=True, - bgcolor=ft.Colors.BLUE_300, - alignment=ft.Alignment.CENTER, - ), - ft.Divider(height=9, thickness=3), - ft.Container( - expand=True, - bgcolor=ft.Colors.DEEP_PURPLE_200, - alignment=ft.Alignment.CENTER, - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/divider/basic/main.py b/sdk/python/examples/controls/divider/basic/main.py new file mode 100644 index 0000000000..8821065032 --- /dev/null +++ b/sdk/python/examples/controls/divider/basic/main.py @@ -0,0 +1,42 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + spacing=0, + expand=True, + controls=[ + ft.Container( + expand=True, + bgcolor=ft.Colors.AMBER, + alignment=ft.Alignment.CENTER, + ), + ft.Divider(), + ft.Container( + expand=True, + bgcolor=ft.Colors.PINK, + alignment=ft.Alignment.CENTER, + ), + ft.Divider(height=1, color=ft.Colors.WHITE), + ft.Container( + expand=True, + bgcolor=ft.Colors.BLUE_300, + alignment=ft.Alignment.CENTER, + ), + ft.Divider(height=9, thickness=3), + ft.Container( + expand=True, + bgcolor=ft.Colors.DEEP_PURPLE_200, + alignment=ft.Alignment.CENTER, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/divider/basic/pyproject.toml b/sdk/python/examples/controls/divider/basic/pyproject.toml new file mode 100644 index 0000000000..91cdcc1a7d --- /dev/null +++ b/sdk/python/examples/controls/divider/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "divider-basic" +version = "1.0.0" +description = "Demonstrates Divider height, thickness, and color between vertically stacked containers." +requires-python = ">=3.10" +keywords = ["divider", "layout", "spacing", "separator"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Divider"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "Container", "Divider"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["custom divider thickness", "custom divider color", "section separation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers.py b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers.py deleted file mode 100644 index 677d2c21de..0000000000 --- a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers.py +++ /dev/null @@ -1,79 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_drag_will_accept(e: ft.DragWillAcceptEvent): - e.control.content.border = ft.Border.all( - 2, ft.Colors.BLACK_45 if e.accept else ft.Colors.RED - ) - e.control.update() - - def handle_drag_accept(e: ft.DragTargetEvent): - src = page.get_control(e.src_id) - e.control.content.bgcolor = src.content.bgcolor - e.control.content.border = None - e.control.update() - - def handle_drag_leave(e: ft.DragTargetLeaveEvent): - e.control.content.border = None - e.control.update() - - page.add( - ft.Row( - controls=[ - ft.Column( - controls=[ - ft.Draggable( - group="color", - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.CYAN, - border_radius=5, - ), - content_feedback=ft.Container( - width=20, - height=20, - bgcolor=ft.Colors.CYAN, - border_radius=3, - ), - ), - ft.Draggable( - group="color", - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.YELLOW, - border_radius=5, - ), - ), - ft.Draggable( - group="color", - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.GREEN, - border_radius=5, - ), - ), - ] - ), - ft.Container(width=100), - ft.DragTarget( - group="color", - on_will_accept=handle_drag_will_accept, - on_accept=handle_drag_accept, - on_leave=handle_drag_leave, - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.BLUE_GREY_100, - border_radius=5, - ), - ), - ] - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers/main.py b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers/main.py new file mode 100644 index 0000000000..f6d37f7ec6 --- /dev/null +++ b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers/main.py @@ -0,0 +1,83 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_drag_will_accept(e: ft.DragWillAcceptEvent): + e.control.content.border = ft.Border.all( + 2, + ft.Colors.BLACK_45 if e.accept else ft.Colors.RED, + ) + e.control.update() + + def handle_drag_accept(e: ft.DragTargetEvent): + src = page.get_control(e.src_id) + e.control.content.bgcolor = src.content.bgcolor + e.control.content.border = None + e.control.update() + + def handle_drag_leave(e: ft.DragTargetLeaveEvent): + e.control.content.border = None + e.control.update() + + page.add( + ft.SafeArea( + content=ft.Row( + controls=[ + ft.Column( + controls=[ + ft.Draggable( + group="color", + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.CYAN, + border_radius=5, + ), + content_feedback=ft.Container( + width=20, + height=20, + bgcolor=ft.Colors.CYAN, + border_radius=3, + ), + ), + ft.Draggable( + group="color", + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.YELLOW, + border_radius=5, + ), + ), + ft.Draggable( + group="color", + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.GREEN, + border_radius=5, + ), + ), + ], + ), + ft.Container(width=100), + ft.DragTarget( + group="color", + on_will_accept=handle_drag_will_accept, + on_accept=handle_drag_accept, + on_leave=handle_drag_leave, + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.BLUE_GREY_100, + border_radius=5, + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers/pyproject.toml b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers/pyproject.toml new file mode 100644 index 0000000000..14cad26d66 --- /dev/null +++ b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "drag-and-drop-containers" +version = "1.0.0" +description = "Implements imperative drag-and-drop between Draggable color chips and a DragTarget box." +requires-python = ">=3.10" +keywords = ["draggable", "drag target", "drag and drop"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Draggable"] + +[tool.flet.metadata] +title = "Drag and drop containers" +controls = ["SafeArea", "Row", "Column", "Draggable", "DragTarget", "Container"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["drag and drop", "drop acceptance feedback", "target color update"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers_declarative.py b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers_declarative.py deleted file mode 100644 index 621c898ebc..0000000000 --- a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers_declarative.py +++ /dev/null @@ -1,89 +0,0 @@ -from dataclasses import dataclass - -import flet as ft - - -@dataclass -@ft.observable -class TargetState: - bgcolor: ft.Colors = ft.Colors.BLUE_GREY_100 - is_drag_over: bool = False - - -@ft.component -def App(): - target, _ = ft.use_state(lambda: TargetState()) - - def on_will_accept(e: ft.DragWillAcceptEvent): - target.is_drag_over = True - - def on_accept(e: ft.DragTargetEvent): - target.bgcolor = e.src.data - target.is_drag_over = False - - def on_leave(e: ft.DragTargetLeaveEvent): - target.is_drag_over = False - - return ft.Row( - controls=[ - ft.Column( - controls=[ - ft.Draggable( - group="color", - data=ft.Colors.CYAN, - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.CYAN, - border_radius=5, - ), - content_feedback=ft.Container( - width=20, - height=20, - bgcolor=ft.Colors.CYAN, - border_radius=3, - ), - ), - ft.Draggable( - group="color", - data=ft.Colors.YELLOW, - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.YELLOW, - border_radius=5, - ), - ), - ft.Draggable( - group="color", - data=ft.Colors.GREEN, - content=ft.Container( - width=50, - height=50, - bgcolor=ft.Colors.GREEN, - border_radius=5, - ), - ), - ] - ), - ft.Container(width=100), - ft.DragTarget( - group="color", - on_will_accept=on_will_accept, - on_accept=on_accept, - on_leave=on_leave, - content=ft.Container( - width=50, - height=50, - bgcolor=target.bgcolor, - border=ft.Border.all(2, ft.Colors.BLACK_45) - if target.is_drag_over - else None, - border_radius=5, - ), - ), - ] - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers_declarative/main.py b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers_declarative/main.py new file mode 100644 index 0000000000..d3be31fe82 --- /dev/null +++ b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers_declarative/main.py @@ -0,0 +1,98 @@ +from dataclasses import dataclass + +import flet as ft + + +@dataclass +@ft.observable +class TargetState: + bgcolor: ft.Colors = ft.Colors.BLUE_GREY_100 + is_drag_over: bool = False + + +@ft.component +def App(): + target, _ = ft.use_state(lambda: TargetState()) + + def on_will_accept(_: ft.DragWillAcceptEvent): + target.is_drag_over = True + + def on_accept(e: ft.DragTargetEvent): + target.bgcolor = e.src.data + target.is_drag_over = False + + def on_leave(_: ft.DragTargetLeaveEvent): + target.is_drag_over = False + + return ft.SafeArea( + content=ft.Row( + controls=[ + ft.Column( + controls=[ + ft.Draggable( + group="color", + data=ft.Colors.CYAN, + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.CYAN, + border_radius=5, + ), + content_feedback=ft.Container( + width=20, + height=20, + bgcolor=ft.Colors.CYAN, + border_radius=3, + ), + ), + ft.Draggable( + group="color", + data=ft.Colors.YELLOW, + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.YELLOW, + border_radius=5, + ), + ), + ft.Draggable( + group="color", + data=ft.Colors.GREEN, + content=ft.Container( + width=50, + height=50, + bgcolor=ft.Colors.GREEN, + border_radius=5, + ), + ), + ], + ), + ft.Container(width=100), + ft.DragTarget( + group="color", + on_will_accept=on_will_accept, + on_accept=on_accept, + on_leave=on_leave, + content=ft.Container( + width=50, + height=50, + bgcolor=target.bgcolor, + border=( + ft.Border.all(2, ft.Colors.BLACK_45) + if target.is_drag_over + else None + ), + border_radius=5, + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers_declarative/pyproject.toml b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers_declarative/pyproject.toml new file mode 100644 index 0000000000..be5ff96a5a --- /dev/null +++ b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_containers_declarative/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "drag-and-drop-containers-declarative" +version = "1.0.0" +description = "Builds declarative drag-and-drop containers with observable target state and hover feedback." +requires-python = ">=3.10" +keywords = ["draggable", "drag target", "drag and drop", "declarative", "state"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Draggable"] + +[tool.flet.metadata] +title = "Drag and drop containers declarative" +controls = ["SafeArea", "Row", "Column", "Draggable", "DragTarget", "Container"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["declarative state", "drag and drop", "hover border feedback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_ordering_declarative.py b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_ordering_declarative.py deleted file mode 100644 index 04a1b4cf30..0000000000 --- a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_ordering_declarative.py +++ /dev/null @@ -1,253 +0,0 @@ -from dataclasses import dataclass, field - -import flet as ft - -ItemID = ft.IdCounter() - - -@ft.observable -@dataclass -class AppState: - groups: list["Group"] = field(default_factory=list) - - def move_group(self, src: "Group", dst: "Group"): - src_index = self.groups.index(src) - dst_index = self.groups.index(dst) - if src_index != dst_index: - print("Move group", src.title, "to position of", dst.title) - self.groups.insert(dst_index, self.groups.pop(src_index)) - - -@ft.observable -@dataclass -class Group: - title: str - color: ft.Colors - items: list["Item"] = field(default_factory=list) - - def add_item(self, text: str): - self.items.append(Item(text=text, group=self)) - - def move_item_into(self, item: "Item"): - print("Move item", item.text, "from", item.group.title, "to", self.title) - item.group.items.remove(item) - item.group = self - self.items.append(item) - - -@ft.observable -@dataclass -class Item: - text: str - group: Group - id: int = field(default_factory=ItemID) - - def move_item_at(self, item: "Item", to_item: "Item"): - if item == to_item: - return - print( - f"Move item {item.text} from {item.group.title} " - f"to {to_item.group.title} at position of {to_item.text}" - ) - item.group.items.remove(item) - item.group = to_item.group - to_index = to_item.group.items.index(to_item) - to_item.group.items.insert(to_index, item) - - -@ft.component -def ItemView(item: Item, **kwargs): - is_item_over, set_is_item_over = ft.use_state(False) - - def on_accept(e: ft.DragTargetEvent): - item.move_item_at(e.src.data, item) - set_is_item_over(False) - - return ft.Column( - spacing=2, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Divider( - color=ft.Colors.BLACK38, - thickness=2, - height=2, - radius=2, - opacity=1.0 if is_item_over else 0.0, - ), - ft.Draggable( - group="items", - data=item, - content=ft.DragTarget( - group="items", - data=item, - on_will_accept=lambda e: set_is_item_over( - e.accept and e.src.data != item - ), - on_accept=on_accept, - on_leave=lambda: set_is_item_over(False), - content=ft.Card( - content=ft.Container( - padding=7, - width=200, - content=ft.Row( - alignment=ft.MainAxisAlignment.START, - controls=[ - ft.Icon(ft.Icons.CIRCLE_OUTLINED), - ft.Text(value=item.text), - ], - ), - ), - ), - ), - ), - ], - ) - - -@ft.component -def GroupView(group: Group, move_group, **kwargs): - is_group_over, set_is_group_over = ft.use_state(False) - is_item_over, set_is_item_over = ft.use_state(False) - new_item_text, set_new_item_text = ft.use_state("") - - def on_item_accept(e: ft.DragTargetEvent): - group.move_item_into(e.src.data) - set_is_item_over(False) - - def on_group_accept(e: ft.DragTargetEvent): - move_group(e.src.data, group) - set_is_group_over(False) - - def on_add_item(self): - if stripped_text := new_item_text.strip(): - group.add_item(stripped_text) - set_new_item_text("") - - return ft.Row( - spacing=4, - intrinsic_height=True, - controls=[ - ft.VerticalDivider( - color=ft.Colors.BLACK_54, - width=2, - thickness=2, - radius=2, - leading_indent=15, - trailing_indent=15, - opacity=1.0 if is_group_over else 0.0, - ), - ft.Draggable( - group="groups", - data=group, - content=ft.DragTarget( - group="items", - data=group, - on_will_accept=lambda e: set_is_item_over(e.accept), - on_accept=on_item_accept, - on_leave=lambda: set_is_item_over(False), - content=ft.DragTarget( - group="groups", - data=group, - on_will_accept=lambda e: set_is_group_over( - e.accept and e.src.data != group - ), - on_accept=on_group_accept, - on_leave=lambda: set_is_group_over(False), - content=ft.Container( - border=ft.Border.all(2, ft.Colors.BLACK12) - if not is_group_over - else ft.Border.all(2, ft.Colors.BLACK38), - border_radius=ft.BorderRadius.all(15), - bgcolor=group.color, - padding=ft.Padding.all(20), - width=220, - content=ft.Column( - spacing=4, - controls=[ - ft.Text( - group.title, - theme_style=ft.TextThemeStyle.TITLE_LARGE, - ), - ft.TextField( - label="New item", - bgcolor=ft.Colors.WHITE, - value=new_item_text, - on_change=lambda e: set_new_item_text( - e.control.value - ), - on_submit=on_add_item, - ), - ft.TextButton( - content="Add", - icon=ft.Icons.ADD, - on_click=on_add_item, - ), - ft.Column( - spacing=2, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - *[ - ItemView(item, key=item.id) - for item in group.items - ], - ft.Divider( - color=ft.Colors.BLACK38, - thickness=2, - height=2, - radius=2, - opacity=1.0 if is_item_over else 0.0, - ), - ], - ), - ], - ), - ), - ), - ), - ), - ], - ) - - -@ft.component -def App(): - group_1 = Group(title="Group 1", color=ft.Colors.DEEP_ORANGE_400) - group_1.add_item("Item 1") - group_1.add_item("Item 2") - - group_2 = Group(title="Group 2", color=ft.Colors.PINK_400) - group_2.add_item("Item 3") - - group_3 = Group(title="Group 3", color=ft.Colors.CYAN_400) - group_3.add_item("Item 4") - - # group_4 = Group(title="Group 4", color=ft.Colors.GREEN_400) - # group_4.add_item("Item 5") - - app, _ = ft.use_state( - lambda: AppState( - groups=[ - group_1, - group_2, - group_3, - # group_4, - ] - ) - ) - - def on_mounted(): - ft.context.page.theme_mode = ft.ThemeMode.LIGHT - - ft.on_mounted(on_mounted) - - return ft.Row( - spacing=4, - vertical_alignment=ft.CrossAxisAlignment.START, - controls=[ - GroupView(group, move_group=app.move_group, key=group.title) - for group in app.groups - ], - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_ordering_declarative/main.py b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_ordering_declarative/main.py new file mode 100644 index 0000000000..4c0051dec8 --- /dev/null +++ b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_ordering_declarative/main.py @@ -0,0 +1,258 @@ +from dataclasses import dataclass, field + +import flet as ft + +ItemID = ft.IdCounter() + + +@ft.observable +@dataclass +class AppState: + groups: list["Group"] = field(default_factory=list) + + def move_group(self, src: "Group", dst: "Group"): + src_index = self.groups.index(src) + dst_index = self.groups.index(dst) + if src_index != dst_index: + print("Move group", src.title, "to position of", dst.title) + self.groups.insert(dst_index, self.groups.pop(src_index)) + + +@ft.observable +@dataclass +class Group: + title: str + color: ft.Colors + items: list["Item"] = field(default_factory=list) + + def add_item(self, text: str): + self.items.append(Item(text=text, group=self)) + + def move_item_into(self, item: "Item"): + print("Move item", item.text, "from", item.group.title, "to", self.title) + item.group.items.remove(item) + item.group = self + self.items.append(item) + + +@ft.observable +@dataclass +class Item: + text: str + group: Group + id: int = field(default_factory=ItemID) + + def move_item_at(self, item: "Item", to_item: "Item"): + if item == to_item: + return + print( + f"Move item {item.text} from {item.group.title} " + f"to {to_item.group.title} at position of {to_item.text}" + ) + item.group.items.remove(item) + item.group = to_item.group + to_index = to_item.group.items.index(to_item) + to_item.group.items.insert(to_index, item) + + +@ft.component +def ItemView(item: Item, **kwargs): + is_item_over, set_is_item_over = ft.use_state(False) + + def on_accept(e: ft.DragTargetEvent): + item.move_item_at(e.src.data, item) + set_is_item_over(False) + + return ft.Column( + spacing=2, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Divider( + color=ft.Colors.BLACK38, + thickness=2, + height=2, + radius=2, + opacity=1.0 if is_item_over else 0.0, + ), + ft.Draggable( + group="items", + data=item, + content=ft.DragTarget( + group="items", + data=item, + on_will_accept=lambda e: set_is_item_over( + e.accept and e.src.data != item + ), + on_accept=on_accept, + on_leave=lambda _: set_is_item_over(False), + content=ft.Card( + content=ft.Container( + padding=7, + width=200, + content=ft.Row( + alignment=ft.MainAxisAlignment.START, + controls=[ + ft.Icon(ft.Icons.CIRCLE_OUTLINED), + ft.Text(value=item.text), + ], + ), + ), + ), + ), + ), + ], + ) + + +@ft.component +def GroupView(group: Group, move_group, **kwargs): + is_group_over, set_is_group_over = ft.use_state(False) + is_item_over, set_is_item_over = ft.use_state(False) + new_item_text, set_new_item_text = ft.use_state("") + + def on_item_accept(e: ft.DragTargetEvent): + group.move_item_into(e.src.data) + set_is_item_over(False) + + def on_group_accept(e: ft.DragTargetEvent): + move_group(e.src.data, group) + set_is_group_over(False) + + def on_add_item(_: ft.Event[ft.TextButton] | None = None): + if stripped_text := new_item_text.strip(): + group.add_item(stripped_text) + set_new_item_text("") + + return ft.Row( + spacing=4, + intrinsic_height=True, + controls=[ + ft.VerticalDivider( + color=ft.Colors.BLACK_54, + width=2, + thickness=2, + radius=2, + leading_indent=15, + trailing_indent=15, + opacity=1.0 if is_group_over else 0.0, + ), + ft.Draggable( + group="groups", + data=group, + content=ft.DragTarget( + group="items", + data=group, + on_will_accept=lambda e: set_is_item_over(e.accept), + on_accept=on_item_accept, + on_leave=lambda _: set_is_item_over(False), + content=ft.DragTarget( + group="groups", + data=group, + on_will_accept=lambda e: set_is_group_over( + e.accept and e.src.data != group + ), + on_accept=on_group_accept, + on_leave=lambda _: set_is_group_over(False), + content=ft.Container( + border=( + ft.Border.all(2, ft.Colors.BLACK12) + if not is_group_over + else ft.Border.all(2, ft.Colors.BLACK38) + ), + border_radius=ft.BorderRadius.all(15), + bgcolor=group.color, + padding=ft.Padding.all(20), + width=220, + content=ft.Column( + spacing=4, + controls=[ + ft.Text( + group.title, + theme_style=ft.TextThemeStyle.TITLE_LARGE, + ), + ft.TextField( + label="New item", + bgcolor=ft.Colors.WHITE, + value=new_item_text, + on_change=lambda e: set_new_item_text( + e.control.value + ), + on_submit=on_add_item, + ), + ft.TextButton( + icon=ft.Icons.ADD, + on_click=on_add_item, + content="Add", + ), + ft.Column( + spacing=2, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + *[ + ItemView(item, key=item.id) + for item in group.items + ], + ft.Divider( + color=ft.Colors.BLACK38, + thickness=2, + height=2, + radius=2, + opacity=1.0 if is_item_over else 0.0, + ), + ], + ), + ], + ), + ), + ), + ), + ), + ], + ) + + +@ft.component +def App(): + group_1 = Group(title="Group 1", color=ft.Colors.DEEP_ORANGE_400) + group_1.add_item("Item 1") + group_1.add_item("Item 2") + + group_2 = Group(title="Group 2", color=ft.Colors.PINK_400) + group_2.add_item("Item 3") + + group_3 = Group(title="Group 3", color=ft.Colors.CYAN_400) + group_3.add_item("Item 4") + + app, _ = ft.use_state( + lambda: AppState( + groups=[ + group_1, + group_2, + group_3, + ] + ) + ) + + def on_mounted(): + ft.context.page.theme_mode = ft.ThemeMode.LIGHT + + ft.on_mounted(on_mounted) + + return ft.SafeArea( + content=ft.Row( + spacing=4, + vertical_alignment=ft.CrossAxisAlignment.START, + controls=[ + GroupView(group, move_group=app.move_group, key=group.title) + for group in app.groups + ], + ), + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_ordering_declarative/pyproject.toml b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_ordering_declarative/pyproject.toml new file mode 100644 index 0000000000..30778e4be1 --- /dev/null +++ b/sdk/python/examples/controls/drag_target_and_draggable/drag_and_drop_ordering_declarative/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "drag-and-drop-ordering-declarative" +version = "1.0.0" +description = "Provides declarative drag-and-drop ordering across groups with nested DragTarget and Draggable controls." +requires-python = ">=3.10" +keywords = ["draggable", "drag target", "ordering", "declarative", "kanban"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Draggable"] + +[tool.flet.metadata] +title = "Drag and drop ordering declarative" +controls = ["SafeArea", "Row", "Column", "Draggable", "DragTarget", "Card", "TextField", "TextButton", "Divider", "VerticalDivider"] +layout_pattern = "dashboard" +complexity = "intermediate" +features = ["group reordering", "item reordering", "nested drag targets", "declarative state updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown/color_selection_with_filtering.py b/sdk/python/examples/controls/dropdown/color_selection_with_filtering.py deleted file mode 100644 index 2237b662f9..0000000000 --- a/sdk/python/examples/controls/dropdown/color_selection_with_filtering.py +++ /dev/null @@ -1,37 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - def get_options(): - colors = [ - ft.Colors.RED, - ft.Colors.BLUE, - ft.Colors.YELLOW, - ft.Colors.PURPLE, - ft.Colors.LIME, - ] - return [ - ft.DropdownOption( - key=color.value, - content=ft.Text(value=color.value, color=color), - ) - for color in colors - ] - - def handle_dropdown_select(e: ft.Event[ft.Dropdown]): - e.control.color = e.control.value - page.update() - - page.add( - ft.Dropdown( - editable=True, - label="Color", - options=get_options(), - on_select=handle_dropdown_select, - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dropdown/color_selection_with_filtering/main.py b/sdk/python/examples/controls/dropdown/color_selection_with_filtering/main.py new file mode 100644 index 0000000000..516baee2c7 --- /dev/null +++ b/sdk/python/examples/controls/dropdown/color_selection_with_filtering/main.py @@ -0,0 +1,39 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + def get_options() -> list[ft.DropdownOption]: + colors = [ + ft.Colors.RED, + ft.Colors.BLUE, + ft.Colors.YELLOW, + ft.Colors.PURPLE, + ft.Colors.LIME, + ] + return [ + ft.DropdownOption( + key=color.value, + content=ft.Text(value=color.value, color=color), + ) + for color in colors + ] + + def handle_dropdown_select(e: ft.Event[ft.Dropdown]): + e.control.color = e.control.value + + page.add( + ft.SafeArea( + content=ft.Dropdown( + editable=True, + label="Color", + options=get_options(), + on_select=handle_dropdown_select, + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown/color_selection_with_filtering/pyproject.toml b/sdk/python/examples/controls/dropdown/color_selection_with_filtering/pyproject.toml new file mode 100644 index 0000000000..0d65fd09f6 --- /dev/null +++ b/sdk/python/examples/controls/dropdown/color_selection_with_filtering/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-color-selection-with-filtering" +version = "1.0.0" +description = "Filters editable dropdown options by color and applies selected color to the control text." +requires-python = ">=3.10" +keywords = ["dropdown", "filter", "editable", "selection", "color"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Dropdown"] + +[tool.flet.metadata] +title = "Color selection with filtering" +controls = ["SafeArea", "Dropdown", "DropdownOption", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["editable dropdown", "option filtering", "selection callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown/declarative.py b/sdk/python/examples/controls/dropdown/declarative.py deleted file mode 100644 index 74c427414b..0000000000 --- a/sdk/python/examples/controls/dropdown/declarative.py +++ /dev/null @@ -1,50 +0,0 @@ -from dataclasses import dataclass -from typing import cast - -import flet as ft - - -@dataclass -@ft.observable -class Form: - color: str = "red" - - def change_color(self, new_color: str): - print("New color:", new_color) - self.color = new_color - - -@ft.component -def App(): - form, _ = ft.use_state(lambda: Form()) - - return ft.SafeArea( - ft.Column( - cast( - list[ft.Control], - [ - ft.Text(f"Selected color: {form.color}"), - ft.Column( - [ - ft.Dropdown( - editable=True, - label="Color", - value=form.color, - on_select=lambda e: form.change_color( - cast(str, e.control.value) - ), - options=[ - ft.DropdownOption(key="red", text="Red"), - ft.DropdownOption(key="green", text="Green"), - ft.DropdownOption(key="blue", text="Blue"), - ], - ), - ] - ), - ], - ) - ), - ) - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/controls/dropdown/declarative/main.py b/sdk/python/examples/controls/dropdown/declarative/main.py new file mode 100644 index 0000000000..30a4ec4e74 --- /dev/null +++ b/sdk/python/examples/controls/dropdown/declarative/main.py @@ -0,0 +1,55 @@ +from dataclasses import dataclass +from typing import cast + +import flet as ft + + +@dataclass +@ft.observable +class Form: + color: str = "red" + + def change_color(self, new_color: str): + print("New color:", new_color) + self.color = new_color + + +@ft.component +def App(): + form, _ = ft.use_state(lambda: Form()) + + return ft.SafeArea( + content=ft.Column( + controls=cast( + list[ft.Control], + [ + ft.Text(f"Selected color: {form.color}"), + ft.Column( + controls=[ + ft.Dropdown( + editable=True, + label="Color", + value=form.color, + on_select=lambda e: form.change_color( + cast(str, e.control.value) + ), + options=[ + ft.DropdownOption(key="red", text="Red"), + ft.DropdownOption(key="green", text="Green"), + ft.DropdownOption(key="blue", text="Blue"), + ], + ), + ], + ), + ], + ), + ), + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown/declarative/pyproject.toml b/sdk/python/examples/controls/dropdown/declarative/pyproject.toml new file mode 100644 index 0000000000..491b746bca --- /dev/null +++ b/sdk/python/examples/controls/dropdown/declarative/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-declarative" +version = "1.0.0" +description = "Uses a declarative component and observable state to keep Dropdown selection synchronized." +requires-python = ">=3.10" +keywords = ["dropdown", "declarative", "component", "state", "observable"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Dropdown"] + +[tool.flet.metadata] +title = "Declarative" +controls = ["SafeArea", "Column", "Dropdown", "DropdownOption", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["declarative state", "selection binding", "component rendering"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown/icon_selection.py b/sdk/python/examples/controls/dropdown/icon_selection.py deleted file mode 100644 index 5b9ff25abf..0000000000 --- a/sdk/python/examples/controls/dropdown/icon_selection.py +++ /dev/null @@ -1,29 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def get_options(): - icons = [ - {"name": "Smile", "icon": ft.Icons.SENTIMENT_SATISFIED_OUTLINED}, - {"name": "Cloud", "icon": ft.Icons.CLOUD_OUTLINED}, - {"name": "Brush", "icon": ft.Icons.BRUSH_OUTLINED}, - {"name": "Heart", "icon": ft.Icons.FAVORITE}, - ] - return [ - ft.DropdownOption(key=icon["name"], leading_icon=icon["icon"]) - for icon in icons - ] - - page.add( - ft.Dropdown( - border=ft.InputBorder.UNDERLINE, - enable_filter=True, - editable=True, - leading_icon=ft.Icons.SEARCH, - label="Icon", - options=get_options(), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dropdown/icon_selection/main.py b/sdk/python/examples/controls/dropdown/icon_selection/main.py new file mode 100644 index 0000000000..ec32ac8b3f --- /dev/null +++ b/sdk/python/examples/controls/dropdown/icon_selection/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +def main(page: ft.Page): + def get_options() -> list[ft.DropdownOption]: + icons = [ + {"name": "Smile", "icon": ft.Icons.SENTIMENT_SATISFIED_OUTLINED}, + {"name": "Cloud", "icon": ft.Icons.CLOUD_OUTLINED}, + {"name": "Brush", "icon": ft.Icons.BRUSH_OUTLINED}, + {"name": "Heart", "icon": ft.Icons.FAVORITE}, + ] + return [ + ft.DropdownOption(key=icon["name"], leading_icon=icon["icon"]) + for icon in icons + ] + + page.add( + ft.SafeArea( + content=ft.Dropdown( + border=ft.InputBorder.UNDERLINE, + enable_filter=True, + editable=True, + leading_icon=ft.Icons.SEARCH, + label="Icon", + options=get_options(), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown/icon_selection/pyproject.toml b/sdk/python/examples/controls/dropdown/icon_selection/pyproject.toml new file mode 100644 index 0000000000..a956da14f8 --- /dev/null +++ b/sdk/python/examples/controls/dropdown/icon_selection/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-icon-selection" +version = "1.0.0" +description = "Displays dropdown options with leading icons and interactive text filtering." +requires-python = ">=3.10" +keywords = ["dropdown", "icons", "filter", "editable", "search"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Dropdown"] + +[tool.flet.metadata] +title = "Icon selection" +controls = ["SafeArea", "Dropdown", "DropdownOption"] +layout_pattern = "form" +complexity = "basic" +features = ["leading icons", "editable dropdown", "option filtering"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown/select_and_change_events.py b/sdk/python/examples/controls/dropdown/select_and_change_events.py deleted file mode 100644 index f3591aced4..0000000000 --- a/sdk/python/examples/controls/dropdown/select_and_change_events.py +++ /dev/null @@ -1,50 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - colors = [ - ft.Colors.RED, - ft.Colors.BLUE, - ft.Colors.YELLOW, - ft.Colors.PURPLE, - ft.Colors.LIME, - ] - - def get_options(): - options = [] - for color in colors: - options.append( - ft.DropdownOption( - key=color.value, - content=ft.Text( - value=color.value, - color=color, - ), - leading_icon=ft.Icon(ft.Icons.PALETTE, color=color), - ) - ) - return options - - def dropdown_select(e): - e.control.color = e.control.value - display_value.value = f"VALUE changed to {e.control.value}" - - def dropdown_text_change(e): - display_text.value = f"TEXT changed to {e.control.text}" - - page.scroll = ft.ScrollMode.AUTO - page.add( - display_value := ft.Text(), - display_text := ft.Text(), - ft.Dropdown( - editable=True, - label="Color", - width=float("inf"), - options=get_options(), - on_select=dropdown_select, - on_text_change=dropdown_text_change, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dropdown/select_and_change_events/main.py b/sdk/python/examples/controls/dropdown/select_and_change_events/main.py new file mode 100644 index 0000000000..1c4b55908d --- /dev/null +++ b/sdk/python/examples/controls/dropdown/select_and_change_events/main.py @@ -0,0 +1,57 @@ +import flet as ft + + +def main(page: ft.Page): + colors = [ + ft.Colors.RED, + ft.Colors.BLUE, + ft.Colors.YELLOW, + ft.Colors.PURPLE, + ft.Colors.LIME, + ] + + def get_options() -> list[ft.DropdownOption]: + options: list[ft.DropdownOption] = [] + for color in colors: + options.append( + ft.DropdownOption( + key=color.value, + content=ft.Text(value=color.value, color=color), + leading_icon=ft.Icon(ft.Icons.PALETTE, color=color), + ) + ) + return options + + display_value = ft.Text() + display_text = ft.Text() + + def dropdown_select(e: ft.Event[ft.Dropdown]): + e.control.color = e.control.value + display_value.value = f"VALUE changed to {e.control.value}" + + def dropdown_text_change(e: ft.Event[ft.Dropdown]): + display_text.value = f"TEXT changed to {e.control.text}" + + page.scroll = ft.ScrollMode.AUTO + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + display_value, + display_text, + ft.Dropdown( + editable=True, + label="Color", + width=float("inf"), + options=get_options(), + on_select=dropdown_select, + on_text_change=dropdown_text_change, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown/select_and_change_events/pyproject.toml b/sdk/python/examples/controls/dropdown/select_and_change_events/pyproject.toml new file mode 100644 index 0000000000..ecbb6b06ec --- /dev/null +++ b/sdk/python/examples/controls/dropdown/select_and_change_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-select-and-change-events" +version = "1.0.0" +description = "Handles dropdown select and text-change events while showing selected value and typed text." +requires-python = ">=3.10" +keywords = ["dropdown", "events", "on_select", "on_text_change", "editable"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Dropdown"] + +[tool.flet.metadata] +title = "Select and change events" +controls = ["SafeArea", "Column", "Dropdown", "DropdownOption", "Icon", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["select callback", "text change callback", "live value labels"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown/styled.py b/sdk/python/examples/controls/dropdown/styled.py deleted file mode 100644 index 14f354fa52..0000000000 --- a/sdk/python/examples/controls/dropdown/styled.py +++ /dev/null @@ -1,126 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - # 1 - ft.Dropdown( - text_size=20, - content_padding=10, - color=ft.Colors.PURPLE_200, - bgcolor=ft.Colors.BLUE_200, - filled=True, - border_radius=30, - border_color=ft.Colors.GREEN_800, - focused_border_color=ft.Colors.GREEN_ACCENT_400, - focused_border_width=5, - options=[ - ft.DropdownOption("a", "Item A"), - ft.DropdownOption("b", "Item B"), - ft.DropdownOption("c", "Item C"), - ], - ), - # 2 - ft.Dropdown( - border_radius=30, - filled=True, - fill_color=ft.Colors.RED_400, - border_color=ft.Colors.TRANSPARENT, - bgcolor=ft.Colors.RED_200, - color=ft.Colors.CYAN_400, - focused_border_color=ft.Colors.PINK_300, - focused_border_width=20, - options=[ - ft.DropdownOption("a", "Item A"), - ft.DropdownOption("b", "Item B"), - ft.DropdownOption("c", "Item C"), - ], - ), - # 3 - ft.Dropdown( - border_color=ft.Colors.PINK_ACCENT, - focused_border_color=ft.Colors.GREEN_ACCENT_400, - focused_border_width=25, - border_radius=30, - width=150, - border_width=5, - options=[ - ft.DropdownOption("a", "Item A"), - ft.DropdownOption("b", "Item B"), - ft.DropdownOption("c", "Item C"), - ], - ), - # 4 - ft.Container( - padding=ft.Padding.only(bottom=20), - content=ft.Dropdown( - text_size=30, - color=ft.Colors.ORANGE_ACCENT, - border_radius=20, - filled=True, - border_width=0, - autofocus=True, - focused_border_color=ft.Colors.GREEN_100, - focused_border_width=10, - width=200, - height=50, - options=[ - ft.dropdown.Option("a", "Item A"), - ft.dropdown.Option("b", "Item B"), - ft.dropdown.Option("c", "Item C"), - ], - ), - ), - # 5 - ft.Dropdown( - text_size=30, - border_radius=20, - filled=True, - border_width=0, - focused_border_color=ft.Colors.GREEN_100, - focused_border_width=10, - content_padding=20, - width=200, - options=[ - ft.DropdownOption( - key="a", - text="Item A", - style=ft.ButtonStyle( - shape=ft.BeveledRectangleBorder(radius=15), - color={ - ft.ControlState.HOVERED: ft.Colors.WHITE, - ft.ControlState.FOCUSED: ft.Colors.BLUE, - ft.ControlState.DEFAULT: ft.Colors.BLACK, - }, - ), - ), - ft.DropdownOption( - key="b", - text="Item B", - style=ft.ButtonStyle( - shape=ft.BeveledRectangleBorder(radius=15), - color={ - ft.ControlState.HOVERED: ft.Colors.WHITE, - ft.ControlState.FOCUSED: ft.Colors.BLUE, - ft.ControlState.DEFAULT: ft.Colors.BLACK, - }, - ), - ), - ft.DropdownOption( - key="c", - text="Item C", - style=ft.ButtonStyle( - shape=ft.BeveledRectangleBorder(radius=15), - color={ - ft.ControlState.HOVERED: ft.Colors.WHITE, - ft.ControlState.FOCUSED: ft.Colors.BLUE, - ft.ControlState.DEFAULT: ft.Colors.BLACK, - }, - ), - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dropdown/styled/main.py b/sdk/python/examples/controls/dropdown/styled/main.py new file mode 100644 index 0000000000..4427a84169 --- /dev/null +++ b/sdk/python/examples/controls/dropdown/styled/main.py @@ -0,0 +1,128 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Dropdown( + text_size=20, + content_padding=10, + color=ft.Colors.PURPLE_200, + bgcolor=ft.Colors.BLUE_200, + filled=True, + border_radius=30, + border_color=ft.Colors.GREEN_800, + focused_border_color=ft.Colors.GREEN_ACCENT_400, + focused_border_width=5, + options=[ + ft.DropdownOption("a", "Item A"), + ft.DropdownOption("b", "Item B"), + ft.DropdownOption("c", "Item C"), + ], + ), + ft.Dropdown( + border_radius=30, + filled=True, + fill_color=ft.Colors.RED_400, + border_color=ft.Colors.TRANSPARENT, + bgcolor=ft.Colors.RED_200, + color=ft.Colors.CYAN_400, + focused_border_color=ft.Colors.PINK_300, + focused_border_width=20, + options=[ + ft.DropdownOption("a", "Item A"), + ft.DropdownOption("b", "Item B"), + ft.DropdownOption("c", "Item C"), + ], + ), + ft.Dropdown( + border_color=ft.Colors.PINK_ACCENT, + focused_border_color=ft.Colors.GREEN_ACCENT_400, + focused_border_width=25, + border_radius=30, + width=150, + border_width=5, + options=[ + ft.DropdownOption("a", "Item A"), + ft.DropdownOption("b", "Item B"), + ft.DropdownOption("c", "Item C"), + ], + ), + ft.Container( + padding=ft.Padding.only(bottom=20), + content=ft.Dropdown( + text_size=30, + color=ft.Colors.ORANGE_ACCENT, + border_radius=20, + filled=True, + border_width=0, + autofocus=True, + focused_border_color=ft.Colors.GREEN_100, + focused_border_width=10, + width=200, + height=50, + options=[ + ft.dropdown.Option("a", "Item A"), + ft.dropdown.Option("b", "Item B"), + ft.dropdown.Option("c", "Item C"), + ], + ), + ), + ft.Dropdown( + text_size=30, + border_radius=20, + filled=True, + border_width=0, + focused_border_color=ft.Colors.GREEN_100, + focused_border_width=10, + content_padding=20, + width=200, + options=[ + ft.DropdownOption( + key="a", + text="Item A", + style=ft.ButtonStyle( + shape=ft.BeveledRectangleBorder(radius=15), + color={ + ft.ControlState.HOVERED: ft.Colors.WHITE, + ft.ControlState.FOCUSED: ft.Colors.BLUE, + ft.ControlState.DEFAULT: ft.Colors.BLACK, + }, + ), + ), + ft.DropdownOption( + key="b", + text="Item B", + style=ft.ButtonStyle( + shape=ft.BeveledRectangleBorder(radius=15), + color={ + ft.ControlState.HOVERED: ft.Colors.WHITE, + ft.ControlState.FOCUSED: ft.Colors.BLUE, + ft.ControlState.DEFAULT: ft.Colors.BLACK, + }, + ), + ), + ft.DropdownOption( + key="c", + text="Item C", + style=ft.ButtonStyle( + shape=ft.BeveledRectangleBorder(radius=15), + color={ + ft.ControlState.HOVERED: ft.Colors.WHITE, + ft.ControlState.FOCUSED: ft.Colors.BLUE, + ft.ControlState.DEFAULT: ft.Colors.BLACK, + }, + ), + ), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown/styled/pyproject.toml b/sdk/python/examples/controls/dropdown/styled/pyproject.toml new file mode 100644 index 0000000000..dce56d324e --- /dev/null +++ b/sdk/python/examples/controls/dropdown/styled/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-styled" +version = "1.0.0" +description = "Showcases multiple Dropdown visual styles including borders, fills, and per-option button styles." +requires-python = ">=3.10" +keywords = ["dropdown", "styling", "theming", "options", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Dropdown"] + +[tool.flet.metadata] +title = "Styled" +controls = ["SafeArea", "Column", "Container", "Dropdown", "DropdownOption", "ButtonStyle"] +layout_pattern = "form" +complexity = "basic" +features = ["custom borders", "filled styles", "option style customization"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown_m2/add_and_delete_options.py b/sdk/python/examples/controls/dropdown_m2/add_and_delete_options.py deleted file mode 100644 index 44781cd8ab..0000000000 --- a/sdk/python/examples/controls/dropdown_m2/add_and_delete_options.py +++ /dev/null @@ -1,40 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def find_option(option_name): - for option in dropdown.options: - if option_name == option.key: - return option - return None - - def handle_addition(e: ft.Event[ft.Button]): - dropdown.options.append(ft.dropdownm2.Option(input_field.value)) - dropdown.value = input_field.value - input_field.value = "" - page.update() - - def handle_deletion(e: ft.Event[ft.OutlinedButton]): - option = find_option(dropdown.value) - if option is not None: - dropdown.options.remove(option) - # d.value = None - page.update() - - page.add( - dropdown := ft.DropdownM2(options=[], color=ft.Colors.BLUE_400), - ft.Row( - controls=[ - input_field := ft.TextField(hint_text="Enter item name"), - ft.Button(content="Add", on_click=handle_addition), - ft.OutlinedButton( - content="Delete selected", - on_click=handle_deletion, - style=ft.ButtonStyle(bgcolor=ft.Colors.RED), - ), - ] - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/add_and_delete_options/main.py b/sdk/python/examples/controls/dropdown_m2/add_and_delete_options/main.py new file mode 100644 index 0000000000..552f45e500 --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/add_and_delete_options/main.py @@ -0,0 +1,50 @@ +import flet as ft + + +def main(page: ft.Page): + dropdown = ft.DropdownM2(options=[], color=ft.Colors.BLUE_400) + input_field = ft.TextField(hint_text="Enter item name") + + def find_option(option_name: str): + for option in dropdown.options: + if option_name == option.key: + return option + return None + + def handle_addition(_: ft.Event[ft.Button]): + dropdown.options.append(ft.dropdownm2.Option(input_field.value)) + dropdown.value = input_field.value + input_field.value = "" + dropdown.update() + input_field.update() + + def handle_deletion(_: ft.Event[ft.OutlinedButton]): + option = find_option(dropdown.value) + if option is not None: + dropdown.options.remove(option) + dropdown.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + dropdown, + ft.Row( + controls=[ + input_field, + ft.Button(content="Add", on_click=handle_addition), + ft.OutlinedButton( + content="Delete selected", + on_click=handle_deletion, + style=ft.ButtonStyle(bgcolor=ft.Colors.RED), + ), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/add_and_delete_options/pyproject.toml b/sdk/python/examples/controls/dropdown_m2/add_and_delete_options/pyproject.toml new file mode 100644 index 0000000000..25f4c60fda --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/add_and_delete_options/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-m2-add-and-delete-options" +version = "1.0.0" +description = "Adds and removes DropdownM2 options dynamically based on text input and current selection." +requires-python = ">=3.10" +keywords = ["dropdownm2", "dynamic options", "add option", "delete option", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/DropdownM2"] + +[tool.flet.metadata] +title = "Add and delete options" +controls = ["SafeArea", "Column", "Row", "DropdownM2", "TextField", "Button", "OutlinedButton"] +layout_pattern = "form" +complexity = "basic" +features = ["dynamic option insertion", "delete selected option"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown_m2/basic.py b/sdk/python/examples/controls/dropdown_m2/basic.py deleted file mode 100644 index 02b94a10fc..0000000000 --- a/sdk/python/examples/controls/dropdown_m2/basic.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - # page.theme_mode = ft.ThemeMode.DARK - - def handle_button_click(e): - message.value = f"Dropdown value is: {dd.value}" - page.update() - - page.add( - dd := ft.DropdownM2( - width=100, - value="Green", - options=[ - ft.dropdownm2.Option("Red"), - ft.dropdownm2.Option("Green"), - ft.dropdownm2.Option("Blue"), - ], - ), - ft.Button(content="Submit", on_click=handle_button_click), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/basic/main.py b/sdk/python/examples/controls/dropdown_m2/basic/main.py new file mode 100644 index 0000000000..4c305510e3 --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/basic/main.py @@ -0,0 +1,34 @@ +import flet as ft + + +def main(page: ft.Page): + message = ft.Text() + + dd = ft.DropdownM2( + width=100, + value="Green", + options=[ + ft.dropdownm2.Option("Red"), + ft.dropdownm2.Option("Green"), + ft.dropdownm2.Option("Blue"), + ], + ) + + def handle_button_click(_: ft.Event[ft.Button]): + message.value = f"Dropdown value is: {dd.value}" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + dd, + ft.Button(content="Submit", on_click=handle_button_click), + message, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/basic/pyproject.toml b/sdk/python/examples/controls/dropdown_m2/basic/pyproject.toml new file mode 100644 index 0000000000..616704dcd2 --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-m2-basic" +version = "1.0.0" +description = "Submits selected DropdownM2 value and displays it in a message label." +requires-python = ">=3.10" +keywords = ["dropdownm2", "selection", "submit", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/DropdownM2"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "DropdownM2", "Button", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["value selection", "submit callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown_m2/dropdown_random_icon.py b/sdk/python/examples/controls/dropdown_m2/dropdown_random_icon.py deleted file mode 100644 index 02c491b982..0000000000 --- a/sdk/python/examples/controls/dropdown_m2/dropdown_random_icon.py +++ /dev/null @@ -1,32 +0,0 @@ -import random - -import flet as ft - - -def main(page: ft.Page): - def handle_dropdown_change(e: ft.Event[ft.DropdownM2]): - message.value = f"{e.control.value} chosen" - page.update() - - def handle_new_random_item(e: ft.Event[ft.Button]): - icon = ft.Icon(ft.Icons.random()) - dd.options.append( - ft.dropdownm2.Option(text=f"{str(icon.icon)[6:]}", content=icon) - ) - page.update() - - def handle_items_shuffle(e: ft.Event[ft.Button]): - random.shuffle(dd.options) - page.update() - - page.add( - dd := ft.DropdownM2( - options=[], options_fill_horizontally=True, on_change=handle_dropdown_change - ), - ft.Button("Add random Option", on_click=handle_new_random_item), - ft.Button("Shuffle Options", on_click=handle_items_shuffle), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/dropdown_random_icon/main.py b/sdk/python/examples/controls/dropdown_m2/dropdown_random_icon/main.py new file mode 100644 index 0000000000..672eae65a9 --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/dropdown_random_icon/main.py @@ -0,0 +1,44 @@ +import random + +import flet as ft + + +def main(page: ft.Page): + message = ft.Text() + dd = ft.DropdownM2(options=[], options_fill_horizontally=True) + + def handle_dropdown_change(e: ft.Event[ft.DropdownM2]): + message.value = f"{e.control.value} chosen" + + def handle_new_random_item(_: ft.Event[ft.Button]): + icon = ft.Icon(ft.Icons.random()) + dd.options.append( + ft.dropdownm2.Option( + text=f"{str(icon.icon)[6:]}", + content=icon, + ) + ) + dd.update() + + def handle_items_shuffle(_: ft.Event[ft.Button]): + random.shuffle(dd.options) + dd.update() + + dd.on_change = handle_dropdown_change + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + dd, + ft.Button("Add random Option", on_click=handle_new_random_item), + ft.Button("Shuffle Options", on_click=handle_items_shuffle), + message, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/dropdown_random_icon/pyproject.toml b/sdk/python/examples/controls/dropdown_m2/dropdown_random_icon/pyproject.toml new file mode 100644 index 0000000000..deb83af937 --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/dropdown_random_icon/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-m2-dropdown-random-icon" +version = "1.0.0" +description = "Adds random icon options to DropdownM2 and supports shuffling option order interactively." +requires-python = ">=3.10" +keywords = ["dropdownm2", "icons", "dynamic options", "shuffle", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/DropdownM2"] + +[tool.flet.metadata] +title = "Dropdown random icon" +controls = ["SafeArea", "Column", "DropdownM2", "Button", "Icon", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["random option generation", "option shuffling", "selection callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown_m2/handling_events.py b/sdk/python/examples/controls/dropdown_m2/handling_events.py deleted file mode 100644 index ce8558af81..0000000000 --- a/sdk/python/examples/controls/dropdown_m2/handling_events.py +++ /dev/null @@ -1,24 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def dropdown_changed(e: ft.Event[ft.DropdownM2]): - message.value = f"Dropdown changed to {e.control.value}" - page.update() - - page.add( - ft.DropdownM2( - width=200, - color=ft.Colors.BLUE_GREY_700, - on_change=dropdown_changed, - options=[ - ft.dropdownm2.Option("Red"), - ft.dropdownm2.Option("Green"), - ft.dropdownm2.Option("Blue"), - ], - ), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/handling_events/main.py b/sdk/python/examples/controls/dropdown_m2/handling_events/main.py new file mode 100644 index 0000000000..411705aa8a --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/handling_events/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +def main(page: ft.Page): + message = ft.Text() + + def dropdown_changed(e: ft.Event[ft.DropdownM2]): + message.value = f"Dropdown changed to {e.control.value}" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.DropdownM2( + width=200, + color=ft.Colors.BLUE_GREY_700, + on_change=dropdown_changed, + options=[ + ft.dropdownm2.Option("Red"), + ft.dropdownm2.Option("Green"), + ft.dropdownm2.Option("Blue"), + ], + ), + message, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/handling_events/pyproject.toml b/sdk/python/examples/controls/dropdown_m2/handling_events/pyproject.toml new file mode 100644 index 0000000000..7cb513559f --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/handling_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-m2-handling-events" +version = "1.0.0" +description = "Updates helper text when DropdownM2 selection changes via on_change event." +requires-python = ">=3.10" +keywords = ["dropdownm2", "events", "on_change", "selection", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/DropdownM2"] + +[tool.flet.metadata] +title = "Handling events" +controls = ["SafeArea", "Column", "DropdownM2", "Text"] +layout_pattern = "form" +complexity = "basic" +features = ["on_change callback", "live message update"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/dropdown_m2/label_and_hint.py b/sdk/python/examples/controls/dropdown_m2/label_and_hint.py deleted file mode 100644 index 6c75247f00..0000000000 --- a/sdk/python/examples/controls/dropdown_m2/label_and_hint.py +++ /dev/null @@ -1,20 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.DropdownM2( - label="Color", - hint_text="Choose your favourite color?", - autofocus=True, - color=ft.Colors.BLACK, - options=[ - ft.dropdownm2.Option("Red"), - ft.dropdownm2.Option("Green"), - ft.dropdownm2.Option("Blue"), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/label_and_hint/main.py b/sdk/python/examples/controls/dropdown_m2/label_and_hint/main.py new file mode 100644 index 0000000000..16f5a73680 --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/label_and_hint/main.py @@ -0,0 +1,23 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.DropdownM2( + label="Color", + hint_text="Choose your favourite color?", + autofocus=True, + color=ft.Colors.BLACK, + options=[ + ft.dropdownm2.Option("Red"), + ft.dropdownm2.Option("Green"), + ft.dropdownm2.Option("Blue"), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/dropdown_m2/label_and_hint/pyproject.toml b/sdk/python/examples/controls/dropdown_m2/label_and_hint/pyproject.toml new file mode 100644 index 0000000000..41dca107a4 --- /dev/null +++ b/sdk/python/examples/controls/dropdown_m2/label_and_hint/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "dropdown-m2-label-and-hint" +version = "1.0.0" +description = "Shows DropdownM2 with label, hint text, and autofocus configuration." +requires-python = ">=3.10" +keywords = ["dropdownm2", "label", "hint", "autofocus", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/DropdownM2"] + +[tool.flet.metadata] +title = "Dropdown with label and hint" +controls = ["SafeArea", "DropdownM2"] +layout_pattern = "form" +complexity = "basic" +features = ["label text", "hint text", "autofocus"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/expansion_panel_list/basic.py b/sdk/python/examples/controls/expansion_panel_list/basic.py deleted file mode 100644 index 60e9da6b4d..0000000000 --- a/sdk/python/examples/controls/expansion_panel_list/basic.py +++ /dev/null @@ -1,59 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - def handle_change(e: ft.Event[ft.ExpansionPanelList]): - print(f"change on panel with index {e.data}") - - def handle_delete(e: ft.Event[ft.IconButton]): - icon_button = e.control - tile = icon_button.parent - panel = tile.parent - - panel_list.controls.remove(panel) - page.update() - - panel_list = ft.ExpansionPanelList( - expand_icon_color=ft.Colors.AMBER, - elevation=8, - divider_color=ft.Colors.AMBER, - on_change=handle_change, - controls=[ - ft.ExpansionPanel( - # has no header and content - placeholders will be used - bgcolor=ft.Colors.BLUE_400, - expanded=True, - ), - ], - ) - - colors = [ - ft.Colors.GREEN_500, - ft.Colors.BLUE_800, - ft.Colors.RED_800, - ] - - for i in range(len(colors)): - bgcolor = colors[i % len(colors)] - panel_list.controls.append( - ft.ExpansionPanel( - bgcolor=bgcolor, - header=ft.ListTile(title=ft.Text(f"Panel {i}"), bgcolor=bgcolor), - content=ft.ListTile( - bgcolor=bgcolor, - title=ft.Text(f"This is in Panel {i}"), - subtitle=ft.Text(f"Press the icon to delete panel {i}"), - trailing=ft.IconButton( - icon=ft.Icons.DELETE, - on_click=handle_delete, - ), - ), - ) - ) - - page.add(panel_list) - - -ft.run(main) diff --git a/sdk/python/examples/controls/expansion_panel_list/basic/main.py b/sdk/python/examples/controls/expansion_panel_list/basic/main.py new file mode 100644 index 0000000000..70c6420a6d --- /dev/null +++ b/sdk/python/examples/controls/expansion_panel_list/basic/main.py @@ -0,0 +1,62 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + def handle_change(e: ft.Event[ft.ExpansionPanelList]): + print(f"change on panel with index {e.data}") + + def handle_delete(e: ft.Event[ft.IconButton]): + icon_button = e.control + tile = icon_button.parent + panel = tile.parent + + panel_list.controls.remove(panel) + panel_list.update() + + panel_list = ft.ExpansionPanelList( + expand_icon_color=ft.Colors.AMBER, + elevation=8, + divider_color=ft.Colors.AMBER, + on_change=handle_change, + controls=[ + ft.ExpansionPanel( + bgcolor=ft.Colors.BLUE_400, + expanded=True, + ), + ], + ) + + colors = [ + ft.Colors.GREEN_500, + ft.Colors.BLUE_800, + ft.Colors.RED_800, + ] + + for i, bgcolor in enumerate(colors): + panel_list.controls.append( + ft.ExpansionPanel( + bgcolor=bgcolor, + header=ft.ListTile(title=ft.Text(f"Panel {i}"), bgcolor=bgcolor), + content=ft.ListTile( + bgcolor=bgcolor, + title=ft.Text(f"This is in Panel {i}"), + subtitle=ft.Text(f"Press the icon to delete panel {i}"), + trailing=ft.IconButton( + icon=ft.Icons.DELETE, + on_click=handle_delete, + ), + ), + ) + ) + + page.add( + ft.SafeArea( + content=panel_list, + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/expansion_panel_list/basic/pyproject.toml b/sdk/python/examples/controls/expansion_panel_list/basic/pyproject.toml new file mode 100644 index 0000000000..6210493fde --- /dev/null +++ b/sdk/python/examples/controls/expansion_panel_list/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "expansion-panel-list-basic" +version = "1.0.0" +description = "Builds an ExpansionPanelList with dynamic panel deletion and panel-change callbacks." +requires-python = ">=3.10" +keywords = ["expansion panel list", "panels", "delete", "events"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ExpansionPanelList"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "ExpansionPanelList", "ExpansionPanel", "ListTile", "IconButton", "Text"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["panel expand/collapse", "dynamic panel removal", "panel change callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/expansion_tile/__init__.py b/sdk/python/examples/controls/expansion_tile/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/expansion_tile/basic.py b/sdk/python/examples/controls/expansion_tile/basic.py deleted file mode 100644 index 9e47ab1f14..0000000000 --- a/sdk/python/examples/controls/expansion_tile/basic.py +++ /dev/null @@ -1,72 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - page.spacing = 0 - page.padding = 0 - - def handle_tile_change(e: ft.Event[ft.ExpansionTile]): - page.show_dialog( - ft.SnackBar( - duration=1000, - content=ft.Text( - value=( - f"ExpansionTile was " - f"{'expanded' if e.data == 'true' else 'collapsed'}" - ) - ), - ) - ) - if e.control.trailing: - e.control.trailing.icon = ( - ft.Icons.ARROW_DROP_DOWN - if e.control.trailing.icon == ft.Icons.ARROW_DROP_DOWN_CIRCLE - else ft.Icons.ARROW_DROP_DOWN_CIRCLE - ) - page.update() - - page.add( - ft.ExpansionTile( - expanded=True, - title=ft.Text("ExpansionTile 1"), - subtitle=ft.Text("Trailing expansion arrow icon"), - affinity=ft.TileAffinity.PLATFORM, - maintain_state=True, - collapsed_text_color=ft.Colors.RED, - text_color=ft.Colors.RED, - controls=[ - ft.ListTile(title=ft.Text("This is sub-tile number 1.1")), - ft.ListTile(title=ft.Text("This is sub-tile number 1.2")), - ], - ), - ft.ExpansionTile( - expanded=True, - title=ft.Text("ExpansionTile 2"), - subtitle=ft.Text("Custom expansion arrow icon"), - trailing=ft.Icon(ft.Icons.ARROW_DROP_DOWN), - collapsed_text_color=ft.Colors.GREEN, - text_color=ft.Colors.GREEN, - on_change=handle_tile_change, - controls=[ - ft.ListTile(title=ft.Text("This is sub-tile number 2.1")), - ft.ListTile(title=ft.Text("This is sub-tile number 2.2")), - ], - ), - ft.ExpansionTile( - expanded=True, - title=ft.Text("ExpansionTile 3"), - subtitle=ft.Text("Leading expansion arrow icon"), - affinity=ft.TileAffinity.LEADING, - collapsed_text_color=ft.Colors.BLUE_800, - text_color=ft.Colors.BLUE_200, - controls=[ - ft.ListTile(title=ft.Text("This is sub-tile number 3.1")), - ft.ListTile(title=ft.Text("This is sub-tile number 3.2")), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/basic/main.py b/sdk/python/examples/controls/expansion_tile/basic/main.py new file mode 100644 index 0000000000..35c995f70a --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/basic/main.py @@ -0,0 +1,79 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + page.spacing = 0 + page.padding = 0 + + def handle_tile_change(e: ft.Event[ft.ExpansionTile]): + page.show_dialog( + ft.SnackBar( + duration=1000, + content=ft.Text( + value=( + f"ExpansionTile was " + f"{'expanded' if e.data == 'true' else 'collapsed'}" + ) + ), + ) + ) + if e.control.trailing: + e.control.trailing.icon = ( + ft.Icons.ARROW_DROP_DOWN + if e.control.trailing.icon == ft.Icons.ARROW_DROP_DOWN_CIRCLE + else ft.Icons.ARROW_DROP_DOWN_CIRCLE + ) + e.control.trailing.update() + + page.add( + ft.SafeArea( + content=ft.Column( + spacing=0, + controls=[ + ft.ExpansionTile( + expanded=True, + title=ft.Text("ExpansionTile 1"), + subtitle=ft.Text("Trailing expansion arrow icon"), + affinity=ft.TileAffinity.PLATFORM, + maintain_state=True, + collapsed_text_color=ft.Colors.RED, + text_color=ft.Colors.RED, + controls=[ + ft.ListTile(title=ft.Text("This is sub-tile number 1.1")), + ft.ListTile(title=ft.Text("This is sub-tile number 1.2")), + ], + ), + ft.ExpansionTile( + expanded=True, + title=ft.Text("ExpansionTile 2"), + subtitle=ft.Text("Custom expansion arrow icon"), + trailing=ft.Icon(ft.Icons.ARROW_DROP_DOWN), + collapsed_text_color=ft.Colors.GREEN, + text_color=ft.Colors.GREEN, + on_change=handle_tile_change, + controls=[ + ft.ListTile(title=ft.Text("This is sub-tile number 2.1")), + ft.ListTile(title=ft.Text("This is sub-tile number 2.2")), + ], + ), + ft.ExpansionTile( + expanded=True, + title=ft.Text("ExpansionTile 3"), + subtitle=ft.Text("Leading expansion arrow icon"), + affinity=ft.TileAffinity.LEADING, + collapsed_text_color=ft.Colors.BLUE_800, + text_color=ft.Colors.BLUE_200, + controls=[ + ft.ListTile(title=ft.Text("This is sub-tile number 3.1")), + ft.ListTile(title=ft.Text("This is sub-tile number 3.2")), + ], + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/basic/pyproject.toml b/sdk/python/examples/controls/expansion_tile/basic/pyproject.toml new file mode 100644 index 0000000000..215a1a74fe --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "expansion-tile-basic" +version = "1.0.0" +description = "Shows ExpansionTile variants with trailing and leading arrows, snack feedback, and nested list tiles." +requires-python = ">=3.10" +keywords = ["expansion tile", "layout", "events", "list tiles"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ExpansionTile"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "ExpansionTile", "ListTile", "Text", "Icon", "SnackBar"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["tile expand/collapse", "change callback", "custom arrow affinity"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/expansion_tile/borders.py b/sdk/python/examples/controls/expansion_tile/borders.py deleted file mode 100644 index 643b1a5584..0000000000 --- a/sdk/python/examples/controls/expansion_tile/borders.py +++ /dev/null @@ -1,45 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - page.add( - ft.ExpansionTile( - title=ft.Text( - value="Expansion Tile with changing borders", - text_align=ft.TextAlign.CENTER, - ), - subtitle=ft.Text( - value="Tile border changes when expanded", - text_align=ft.TextAlign.CENTER, - ), - bgcolor=ft.Colors.BLUE_GREY_200, - controls_padding=ft.Padding.symmetric(horizontal=10), - collapsed_bgcolor=ft.Colors.BLUE_GREY_200, - affinity=ft.TileAffinity.PLATFORM, - maintain_state=True, - shape=ft.RoundedRectangleBorder(radius=20), - collapsed_shape=ft.StadiumBorder(side=ft.BorderSide(width=2)), - collapsed_text_color=ft.Colors.GREY_800, - text_color=ft.Colors.GREY_800, - controls=[ - ft.ListTile( - title=ft.Text("A sub-tile"), - bgcolor=ft.Colors.BLUE_GREY_200, - shape=ft.RoundedRectangleBorder(radius=20), - # shape=ft.StadiumBorder(), - ), - ft.ListTile( - title=ft.Text("Another sub-tile"), - bgcolor=ft.Colors.BLUE_GREY_200, - shape=ft.RoundedRectangleBorder(radius=20), - # shape=ft.StadiumBorder(), - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/borders/main.py b/sdk/python/examples/controls/expansion_tile/borders/main.py new file mode 100644 index 0000000000..6182183949 --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/borders/main.py @@ -0,0 +1,45 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + page.add( + ft.SafeArea( + content=ft.ExpansionTile( + title=ft.Text( + value="Expansion Tile with changing borders", + text_align=ft.TextAlign.CENTER, + ), + subtitle=ft.Text( + value="Tile border changes when expanded", + text_align=ft.TextAlign.CENTER, + ), + bgcolor=ft.Colors.BLUE_GREY_200, + controls_padding=ft.Padding.symmetric(horizontal=10), + collapsed_bgcolor=ft.Colors.BLUE_GREY_200, + affinity=ft.TileAffinity.PLATFORM, + maintain_state=True, + shape=ft.RoundedRectangleBorder(radius=20), + collapsed_shape=ft.StadiumBorder(side=ft.BorderSide(width=2)), + collapsed_text_color=ft.Colors.GREY_800, + text_color=ft.Colors.GREY_800, + controls=[ + ft.ListTile( + title=ft.Text("A sub-tile"), + bgcolor=ft.Colors.BLUE_GREY_200, + shape=ft.RoundedRectangleBorder(radius=20), + ), + ft.ListTile( + title=ft.Text("Another sub-tile"), + bgcolor=ft.Colors.BLUE_GREY_200, + shape=ft.RoundedRectangleBorder(radius=20), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/borders/pyproject.toml b/sdk/python/examples/controls/expansion_tile/borders/pyproject.toml new file mode 100644 index 0000000000..a8a5d91721 --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/borders/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "expansion-tile-borders" +version = "1.0.0" +description = "Demonstrates ExpansionTile border and shape changes between collapsed and expanded states." +requires-python = ">=3.10" +keywords = ["expansion tile", "borders", "shape", "collapsed state", "expanded state"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ExpansionTile"] + +[tool.flet.metadata] +title = "Borders" +controls = ["SafeArea", "ExpansionTile", "ListTile", "RoundedRectangleBorder", "StadiumBorder", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["collapsed vs expanded shapes", "custom borders", "tile background styling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/expansion_tile/custom_animations.py b/sdk/python/examples/controls/expansion_tile/custom_animations.py deleted file mode 100644 index 14e535c4dc..0000000000 --- a/sdk/python/examples/controls/expansion_tile/custom_animations.py +++ /dev/null @@ -1,45 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.spacing = 20 - - def switch_animation(e: ft.Event[ft.CupertinoSlidingSegmentedButton]): - if e.control.selected_index == 0: - tile.animation_style = None - elif e.control.selected_index == 1: - tile.animation_style = ft.AnimationStyle( - curve=ft.AnimationCurve.BOUNCE_OUT, - duration=ft.Duration(seconds=5), - ) - else: - tile.animation_style = ft.AnimationStyle.no_animation() - - page.add( - ft.CupertinoSlidingSegmentedButton( - selected_index=0, - thumb_color=ft.Colors.BLUE_400, - on_change=switch_animation, - controls=[ - ft.Text("Default animation"), - ft.Text("Custom animation"), - ft.Text("No animation"), - ], - ), - tile := ft.ExpansionTile( - expanded=True, - title=ft.Text( - "Expand/Collapse me while being attentive to the animations!" - ), - controls=[ - ft.ListTile(title=ft.Text("Sub-item 1")), - ft.ListTile(title=ft.Text("Sub-item 2")), - ft.ListTile(title=ft.Text("Sub-item 3")), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/custom_animations/main.py b/sdk/python/examples/controls/expansion_tile/custom_animations/main.py new file mode 100644 index 0000000000..3fd04b24bc --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/custom_animations/main.py @@ -0,0 +1,53 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.spacing = 20 + + tile = ft.ExpansionTile( + expanded=True, + title=ft.Text("Expand/Collapse me while being attentive to the animations!"), + controls=[ + ft.ListTile(title=ft.Text("Sub-item 1")), + ft.ListTile(title=ft.Text("Sub-item 2")), + ft.ListTile(title=ft.Text("Sub-item 3")), + ], + ) + + def switch_animation(e: ft.Event[ft.CupertinoSlidingSegmentedButton]): + if e.control.selected_index == 0: + tile.animation_style = None + elif e.control.selected_index == 1: + tile.animation_style = ft.AnimationStyle( + curve=ft.AnimationCurve.BOUNCE_OUT, + duration=ft.Duration(seconds=5), + ) + else: + tile.animation_style = ft.AnimationStyle.no_animation() + tile.update() + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.CupertinoSlidingSegmentedButton( + selected_index=0, + thumb_color=ft.Colors.BLUE_400, + on_change=switch_animation, + controls=[ + ft.Text("Default animation"), + ft.Text("Custom animation"), + ft.Text("No animation"), + ], + ), + tile, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/custom_animations/pyproject.toml b/sdk/python/examples/controls/expansion_tile/custom_animations/pyproject.toml new file mode 100644 index 0000000000..8b17b4a2e2 --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/custom_animations/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "expansion-tile-custom-animations" +version = "1.0.0" +description = "Switches ExpansionTile animation behavior between default, custom curve, and no animation." +requires-python = ">=3.10" +keywords = ["expansion tile", "animation", "cupertino sliding segmented button", "curve"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ExpansionTile"] + +[tool.flet.metadata] +title = "Custom animations" +controls = ["SafeArea", "Column", "ExpansionTile", "ListTile", "CupertinoSlidingSegmentedButton", "AnimationStyle", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["animation style switching", "custom animation curve", "disable animations"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/expansion_tile/programmatic_expansion.py b/sdk/python/examples/controls/expansion_tile/programmatic_expansion.py deleted file mode 100644 index 508146dce3..0000000000 --- a/sdk/python/examples/controls/expansion_tile/programmatic_expansion.py +++ /dev/null @@ -1,35 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.spacing = 20 - - def expand_tile(e: ft.Event[ft.FilledButton]): - tile.expanded = True - - def collapse_tile(e: ft.Event[ft.OutlinedButton]): - tile.expanded = False - - page.add( - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.FilledButton("Expand Tile", on_click=expand_tile), - ft.OutlinedButton("Collapse Tile", on_click=collapse_tile), - ], - ), - tile := ft.ExpansionTile( - title=ft.Text("I am the title of this tile.", weight=ft.FontWeight.BOLD), - subtitle=ft.Text("This is the subtitle."), - affinity=ft.TileAffinity.LEADING, - controls=[ft.Text("👻", size=80)], - expanded=True, - on_change=lambda e: print( - f"Tile was {'expanded' if e.data else 'collapsed'}" - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/programmatic_expansion/main.py b/sdk/python/examples/controls/expansion_tile/programmatic_expansion/main.py new file mode 100644 index 0000000000..6ff48ec833 --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/programmatic_expansion/main.py @@ -0,0 +1,43 @@ +import flet as ft + + +def main(page: ft.Page): + page.spacing = 20 + + tile = ft.ExpansionTile( + title=ft.Text("I am the title of this tile.", weight=ft.FontWeight.BOLD), + subtitle=ft.Text("This is the subtitle."), + affinity=ft.TileAffinity.LEADING, + controls=[ft.Text("👻", size=80)], + expanded=True, + on_change=lambda e: print(f"Tile was {'expanded' if e.data else 'collapsed'}"), + ) + + def expand_tile(_: ft.Event[ft.FilledButton]): + tile.expanded = True + tile.update() + + def collapse_tile(_: ft.Event[ft.OutlinedButton]): + tile.expanded = False + tile.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.FilledButton("Expand Tile", on_click=expand_tile), + ft.OutlinedButton("Collapse Tile", on_click=collapse_tile), + ], + ), + tile, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/programmatic_expansion/pyproject.toml b/sdk/python/examples/controls/expansion_tile/programmatic_expansion/pyproject.toml new file mode 100644 index 0000000000..e6a00cdf0d --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/programmatic_expansion/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "expansion-tile-programmatic-expansion" +version = "1.0.0" +description = "Controls ExpansionTile expanded state programmatically with dedicated expand and collapse buttons." +requires-python = ">=3.10" +keywords = ["expansion tile", "programmatic", "expand", "collapse", "buttons"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ExpansionTile"] + +[tool.flet.metadata] +title = "Programmatic expansion" +controls = ["SafeArea", "Column", "Row", "ExpansionTile", "FilledButton", "OutlinedButton", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["programmatic expansion", "programmatic collapse"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/expansion_tile/theme_mode_toggle.py b/sdk/python/examples/controls/expansion_tile/theme_mode_toggle.py deleted file mode 100644 index 778bdf031f..0000000000 --- a/sdk/python/examples/controls/expansion_tile/theme_mode_toggle.py +++ /dev/null @@ -1,94 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.spacing = 0 - page.padding = 0 - - def handle_switch_change(e: ft.Event[ft.Switch]): - if page.theme_mode == ft.ThemeMode.DARK: - page.theme_mode = ft.ThemeMode.LIGHT - switch.thumb_icon = ft.Icons.LIGHT_MODE - else: - switch.thumb_icon = ft.Icons.DARK_MODE - page.theme_mode = ft.ThemeMode.DARK - page.update() - - def handle_expansion_tile_change(e: ft.Event[ft.ExpansionTile]): - page.show_dialog( - ft.SnackBar( - duration=1000, - content=ft.Text( - value=( - f"ExpansionTile was " - f"{'expanded' if e.data == 'true' else 'collapsed'}" - ) - ), - ) - ) - if e.control.trailing: - e.control.trailing.icon = ( - ft.Icons.ARROW_DROP_DOWN - if e.control.trailing.icon == ft.Icons.ARROW_DROP_DOWN_CIRCLE - else ft.Icons.ARROW_DROP_DOWN_CIRCLE - ) - page.update() - - switch = ft.Switch(thumb_icon=ft.Icons.DARK_MODE, on_change=handle_switch_change) - - page.add( - ft.ExpansionTile( - title=ft.Text("ExpansionTile 1"), - subtitle=ft.Text("Trailing expansion arrow icon"), - bgcolor=ft.Colors.BLUE_GREY_200, - collapsed_bgcolor=ft.Colors.BLUE_GREY_200, - affinity=ft.TileAffinity.PLATFORM, - maintain_state=True, - collapsed_text_color=ft.Colors.RED, - text_color=ft.Colors.RED, - controls=[ - ft.ListTile( - title=ft.Text("This is sub-tile number 1"), - bgcolor=ft.Colors.BLUE_GREY_200, - ) - ], - ), - ft.ExpansionTile( - title=ft.Text("ExpansionTile 2"), - subtitle=ft.Text("Custom expansion arrow icon"), - trailing=ft.Icon(ft.Icons.ARROW_DROP_DOWN), - collapsed_text_color=ft.Colors.GREEN, - text_color=ft.Colors.GREEN, - on_change=handle_expansion_tile_change, - controls=[ft.ListTile(title=ft.Text("This is sub-tile number 2"))], - ), - ft.ExpansionTile( - title=ft.Text("ExpansionTile 3"), - subtitle=ft.Text("Leading expansion arrow icon"), - affinity=ft.TileAffinity.LEADING, - expanded=True, - collapsed_text_color=ft.Colors.BLUE_800, - text_color=ft.Colors.BLUE_200, - controls=[ - ft.ListTile(title=ft.Text("This is sub-tile number 3")), - ft.ListTile(title=ft.Text("This is sub-tile number 4")), - ft.ListTile(title=ft.Text("This is sub-tile number 5")), - ], - ), - ft.Row( - expand=True, - alignment=ft.MainAxisAlignment.END, - controls=[ - ft.Container( - content=switch, - padding=ft.Padding.only(bottom=50), - alignment=ft.Alignment.BOTTOM_RIGHT, - expand=True, - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/theme_mode_toggle/main.py b/sdk/python/examples/controls/expansion_tile/theme_mode_toggle/main.py new file mode 100644 index 0000000000..1dc1710ce8 --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/theme_mode_toggle/main.py @@ -0,0 +1,103 @@ +import flet as ft + + +def main(page: ft.Page): + page.spacing = 0 + page.padding = 0 + + def handle_switch_change(_: ft.Event[ft.Switch]): + if page.theme_mode == ft.ThemeMode.DARK: + page.theme_mode = ft.ThemeMode.LIGHT + switch.thumb_icon = ft.Icons.LIGHT_MODE + else: + switch.thumb_icon = ft.Icons.DARK_MODE + page.theme_mode = ft.ThemeMode.DARK + switch.update() + + def handle_expansion_tile_change(e: ft.Event[ft.ExpansionTile]): + page.show_dialog( + ft.SnackBar( + duration=1000, + content=ft.Text( + value=( + f"ExpansionTile was " + f"{'expanded' if e.data == 'true' else 'collapsed'}" + ) + ), + ) + ) + if e.control.trailing: + e.control.trailing.icon = ( + ft.Icons.ARROW_DROP_DOWN + if e.control.trailing.icon == ft.Icons.ARROW_DROP_DOWN_CIRCLE + else ft.Icons.ARROW_DROP_DOWN_CIRCLE + ) + e.control.trailing.update() + + switch = ft.Switch(thumb_icon=ft.Icons.DARK_MODE, on_change=handle_switch_change) + + page.add( + ft.SafeArea( + content=ft.Column( + spacing=0, + controls=[ + ft.ExpansionTile( + title=ft.Text("ExpansionTile 1"), + subtitle=ft.Text("Trailing expansion arrow icon"), + bgcolor=ft.Colors.BLUE_GREY_200, + collapsed_bgcolor=ft.Colors.BLUE_GREY_200, + affinity=ft.TileAffinity.PLATFORM, + maintain_state=True, + collapsed_text_color=ft.Colors.RED, + text_color=ft.Colors.RED, + controls=[ + ft.ListTile( + title=ft.Text("This is sub-tile number 1"), + bgcolor=ft.Colors.BLUE_GREY_200, + ) + ], + ), + ft.ExpansionTile( + title=ft.Text("ExpansionTile 2"), + subtitle=ft.Text("Custom expansion arrow icon"), + trailing=ft.Icon(ft.Icons.ARROW_DROP_DOWN), + collapsed_text_color=ft.Colors.GREEN, + text_color=ft.Colors.GREEN, + on_change=handle_expansion_tile_change, + controls=[ + ft.ListTile(title=ft.Text("This is sub-tile number 2")) + ], + ), + ft.ExpansionTile( + title=ft.Text("ExpansionTile 3"), + subtitle=ft.Text("Leading expansion arrow icon"), + affinity=ft.TileAffinity.LEADING, + expanded=True, + collapsed_text_color=ft.Colors.BLUE_800, + text_color=ft.Colors.BLUE_200, + controls=[ + ft.ListTile(title=ft.Text("This is sub-tile number 3")), + ft.ListTile(title=ft.Text("This is sub-tile number 4")), + ft.ListTile(title=ft.Text("This is sub-tile number 5")), + ], + ), + ft.Row( + expand=True, + alignment=ft.MainAxisAlignment.END, + controls=[ + ft.Container( + padding=ft.Padding.only(bottom=50), + alignment=ft.Alignment.BOTTOM_RIGHT, + expand=True, + content=switch, + ), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/expansion_tile/theme_mode_toggle/pyproject.toml b/sdk/python/examples/controls/expansion_tile/theme_mode_toggle/pyproject.toml new file mode 100644 index 0000000000..5f5ccd2e34 --- /dev/null +++ b/sdk/python/examples/controls/expansion_tile/theme_mode_toggle/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "expansion-tile-theme-mode-toggle" +version = "1.0.0" +description = "Combines ExpansionTile layouts with a switch to toggle page theme mode and icon state." +requires-python = ">=3.10" +keywords = ["expansion tile", "theme mode", "toggle", "switch", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ExpansionTile"] + +[tool.flet.metadata] +title = "Theme mode toggle" +controls = ["SafeArea", "Column", "Row", "ExpansionTile", "ListTile", "Switch", "SnackBar", "Text", "Icon"] +layout_pattern = "dashboard" +complexity = "basic" +features = ["theme toggle", "expansion change feedback", "custom tile colors"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/filled_button/basic.py b/sdk/python/examples/controls/filled_button/basic.py deleted file mode 100644 index 0f3db59374..0000000000 --- a/sdk/python/examples/controls/filled_button/basic.py +++ /dev/null @@ -1,14 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "FilledButton Example" - - page.add( - ft.FilledButton(content="Filled button"), - ft.FilledButton(content="Disabled button", disabled=True), - ft.FilledButton(content="Button with icon", icon=ft.Icons.ADD_OUTLINED), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/filled_button/basic/main.py b/sdk/python/examples/controls/filled_button/basic/main.py new file mode 100644 index 0000000000..611921f80e --- /dev/null +++ b/sdk/python/examples/controls/filled_button/basic/main.py @@ -0,0 +1,24 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "FilledButton Example" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.FilledButton(content="Filled button"), + ft.FilledButton(content="Disabled button", disabled=True), + ft.FilledButton( + content="Button with icon", + icon=ft.Icons.ADD_OUTLINED, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/filled_button/basic/pyproject.toml b/sdk/python/examples/controls/filled_button/basic/pyproject.toml new file mode 100644 index 0000000000..d516fe6611 --- /dev/null +++ b/sdk/python/examples/controls/filled_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "filled-button-basic" +version = "1.0.0" +description = "Shows enabled, disabled, and icon variants of FilledButton in a simple vertical layout." +requires-python = ">=3.10" +keywords = ["filled button", "button states", "icon button", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/FilledButton"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "FilledButton"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["enabled and disabled states", "button with icon"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/filled_tonal_button/basic.py b/sdk/python/examples/controls/filled_tonal_button/basic.py deleted file mode 100644 index 28335a7927..0000000000 --- a/sdk/python/examples/controls/filled_tonal_button/basic.py +++ /dev/null @@ -1,14 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "FilledTonalButton Example" - - page.add( - ft.FilledTonalButton(content="Filled tonal button"), - ft.FilledTonalButton(content="Disabled button", disabled=True), - ft.FilledTonalButton(content="Button with icon", icon=ft.Icons.ADD_OUTLINED), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/filled_tonal_button/basic/main.py b/sdk/python/examples/controls/filled_tonal_button/basic/main.py new file mode 100644 index 0000000000..75b3855d51 --- /dev/null +++ b/sdk/python/examples/controls/filled_tonal_button/basic/main.py @@ -0,0 +1,24 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "FilledTonalButton Example" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.FilledTonalButton(content="Filled tonal button"), + ft.FilledTonalButton(content="Disabled button", disabled=True), + ft.FilledTonalButton( + content="Button with icon", + icon=ft.Icons.ADD_OUTLINED, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/filled_tonal_button/basic/pyproject.toml b/sdk/python/examples/controls/filled_tonal_button/basic/pyproject.toml new file mode 100644 index 0000000000..de96406766 --- /dev/null +++ b/sdk/python/examples/controls/filled_tonal_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "filled-tonal-button-basic" +version = "1.0.0" +description = "Demonstrates FilledTonalButton variants including disabled and icon button states." +requires-python = ">=3.10" +keywords = ["filled tonal button", "button states", "icon button", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/FilledTonalButton"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "FilledTonalButton"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["enabled and disabled states", "button with icon"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/floating_action_button/handling_clicks.py b/sdk/python/examples/controls/floating_action_button/handling_clicks.py deleted file mode 100644 index d60358f08f..0000000000 --- a/sdk/python/examples/controls/floating_action_button/handling_clicks.py +++ /dev/null @@ -1,53 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Floating Action Button" - page.theme_mode = ft.ThemeMode.LIGHT - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.padding = 0 - page.scroll = ft.ScrollMode.HIDDEN - - # keeps track of the number of tiles already added - count = 1 - - def handle_fab_click(e: ft.Event[ft.FloatingActionButton]): - nonlocal count # to modify the count variable found in the outer scope - page.add( - ft.ListTile( - title=ft.Text(f"Tile {count}"), - bgcolor=ft.Colors.TEAL_300, - leading=ft.Icon( - ft.Icons.CIRCLE_OUTLINED, color=ft.Colors.DEEP_ORANGE_300 - ), - on_click=lambda x: print(x.control.title.value + " was clicked!"), - ) - ) - page.show_dialog(ft.SnackBar(ft.Text("Tile was added successfully!"))) - count += 1 - - page.floating_action_button = ft.FloatingActionButton( - icon=ft.Icons.ADD, - on_click=handle_fab_click, - bgcolor=ft.Colors.LIME_300, - ) - - page.add( - ft.Container( - bgcolor=ft.Colors.BLUE, - padding=ft.Padding.all(20), - content=ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Text( - value="Floating Action Button Example", - style=ft.TextStyle(size=20, weight=ft.FontWeight.W_500), - ) - ], - ), - ), - ft.Text("Press the FAB to add a tile!"), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/floating_action_button/handling_clicks/main.py b/sdk/python/examples/controls/floating_action_button/handling_clicks/main.py new file mode 100644 index 0000000000..d879b2f0ef --- /dev/null +++ b/sdk/python/examples/controls/floating_action_button/handling_clicks/main.py @@ -0,0 +1,63 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Floating Action Button" + page.theme_mode = ft.ThemeMode.LIGHT + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.padding = 0 + page.scroll = ft.ScrollMode.HIDDEN + + count = 1 + + def handle_fab_click(e: ft.Event[ft.FloatingActionButton]): + nonlocal count + page.add( + ft.ListTile( + title=ft.Text(f"Tile {count}"), + bgcolor=ft.Colors.TEAL_300, + leading=ft.Icon( + ft.Icons.CIRCLE_OUTLINED, + color=ft.Colors.DEEP_ORANGE_300, + ), + on_click=lambda x: print(x.control.title.value + " was clicked!"), + ) + ) + page.show_dialog(ft.SnackBar(ft.Text("Tile was added successfully!"))) + count += 1 + + page.floating_action_button = ft.FloatingActionButton( + icon=ft.Icons.ADD, + on_click=handle_fab_click, + bgcolor=ft.Colors.LIME_300, + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Container( + bgcolor=ft.Colors.BLUE, + padding=ft.Padding.all(20), + content=ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Text( + value="Floating Action Button Example", + style=ft.TextStyle( + size=20, + weight=ft.FontWeight.W_500, + ), + ) + ], + ), + ), + ft.Text("Press the FAB to add a tile!"), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/floating_action_button/handling_clicks/pyproject.toml b/sdk/python/examples/controls/floating_action_button/handling_clicks/pyproject.toml new file mode 100644 index 0000000000..1a136b3c09 --- /dev/null +++ b/sdk/python/examples/controls/floating_action_button/handling_clicks/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "floating-action-button-handling-clicks" +version = "1.0.0" +description = "Adds list tiles with a FloatingActionButton click and shows a SnackBar confirmation." +requires-python = ">=3.10" +keywords = ["floating action button", "list tile", "click handling", "snackbar"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/FloatingActionButton"] + +[tool.flet.metadata] +title = "Handling clicks" +controls = ["SafeArea", "Column", "Container", "Row", "Text", "FloatingActionButton", "ListTile", "SnackBar", "Icon"] +layout_pattern = "feed" +complexity = "basic" +features = ["floating action button click", "dynamic list updates", "snackbar feedback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/gesture_detector/draggable_containers.py b/sdk/python/examples/controls/gesture_detector/draggable_containers.py deleted file mode 100644 index 7b11d7d52d..0000000000 --- a/sdk/python/examples/controls/gesture_detector/draggable_containers.py +++ /dev/null @@ -1,46 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_pan_update1(e: ft.DragUpdateEvent[ft.GestureDetector]): - container = e.control.parent - container.top = max(0.0, container.top + e.local_delta.y) - container.left = max(0.0, container.left + e.local_delta.x) - container.update() - - def handle_pan_update2(e: ft.DragUpdateEvent[ft.GestureDetector]): - e.control.top = max(0.0, e.control.top + e.local_delta.y) - e.control.left = max(0.0, e.control.left + e.local_delta.x) - e.control.update() - - page.add( - ft.Stack( - width=1000, - height=500, - controls=[ - ft.Container( - bgcolor=ft.Colors.AMBER, - width=50, - height=50, - left=0, - top=0, - content=ft.GestureDetector( - mouse_cursor=ft.MouseCursor.MOVE, - drag_interval=50, - on_pan_update=handle_pan_update1, - ), - ), - ft.GestureDetector( - mouse_cursor=ft.MouseCursor.MOVE, - drag_interval=10, - on_vertical_drag_update=handle_pan_update2, - left=100, - top=100, - content=ft.Container(bgcolor=ft.Colors.BLUE, width=50, height=50), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/gesture_detector/draggable_containers/main.py b/sdk/python/examples/controls/gesture_detector/draggable_containers/main.py new file mode 100644 index 0000000000..3ceae2d653 --- /dev/null +++ b/sdk/python/examples/controls/gesture_detector/draggable_containers/main.py @@ -0,0 +1,53 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_pan_update1(e: ft.DragUpdateEvent[ft.GestureDetector]): + container = e.control.parent + container.top = max(0.0, container.top + e.local_delta.y) + container.left = max(0.0, container.left + e.local_delta.x) + container.update() + + def handle_pan_update2(e: ft.DragUpdateEvent[ft.GestureDetector]): + e.control.top = max(0.0, e.control.top + e.local_delta.y) + e.control.left = max(0.0, e.control.left + e.local_delta.x) + e.control.update() + + page.add( + ft.SafeArea( + content=ft.Stack( + width=1000, + height=500, + controls=[ + ft.Container( + bgcolor=ft.Colors.AMBER, + width=50, + height=50, + left=0, + top=0, + content=ft.GestureDetector( + mouse_cursor=ft.MouseCursor.MOVE, + drag_interval=10, + on_pan_update=handle_pan_update1, + ), + ), + ft.GestureDetector( + mouse_cursor=ft.MouseCursor.MOVE, + drag_interval=10, + on_vertical_drag_update=handle_pan_update2, + left=100, + top=100, + content=ft.Container( + bgcolor=ft.Colors.BLUE, + width=50, + height=50, + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/gesture_detector/draggable_containers/pyproject.toml b/sdk/python/examples/controls/gesture_detector/draggable_containers/pyproject.toml new file mode 100644 index 0000000000..b412691782 --- /dev/null +++ b/sdk/python/examples/controls/gesture_detector/draggable_containers/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "gesture-detector-draggable-containers" +version = "1.0.0" +description = "Drags nested and standalone GestureDetector controls inside a Stack using pan updates." +requires-python = ">=3.10" +keywords = ["gesture detector", "drag", "pan update", "stack", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/GestureDetector"] + +[tool.flet.metadata] +title = "Draggable containers" +controls = ["SafeArea", "Stack", "Container", "GestureDetector"] +layout_pattern = "canvas" +complexity = "basic" +features = ["free dragging", "nested gesture detector", "position updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/gesture_detector/handling_events.py b/sdk/python/examples/controls/gesture_detector/handling_events.py deleted file mode 100644 index e35643cca7..0000000000 --- a/sdk/python/examples/controls/gesture_detector/handling_events.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.GestureDetector( - content=ft.Container(bgcolor=ft.Colors.GREEN, width=200, height=200), - hover_interval=50, - on_tap=lambda e: print(e), - on_tap_down=lambda e: print(e), - on_tap_up=lambda e: print(e), - on_secondary_tap=lambda e: print(e), - on_secondary_tap_down=lambda e: print(e), - on_secondary_tap_up=lambda e: print(e), - on_long_press_start=lambda e: print(e), - on_long_press_end=lambda e: print(e), - on_secondary_long_press_start=lambda e: print(e), - on_secondary_long_press_end=lambda e: print(e), - on_double_tap=lambda e: print(e), - on_double_tap_down=lambda e: print(e), - on_pan_start=lambda e: print(e), - on_pan_update=lambda e: print(e), - on_pan_end=lambda e: print(e), - on_hover=lambda e: print(e), - on_enter=lambda e: print(e), - on_exit=lambda e: print(e), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/gesture_detector/handling_events/main.py b/sdk/python/examples/controls/gesture_detector/handling_events/main.py new file mode 100644 index 0000000000..22ce866537 --- /dev/null +++ b/sdk/python/examples/controls/gesture_detector/handling_events/main.py @@ -0,0 +1,38 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.GestureDetector( + hover_interval=50, + on_tap=lambda e: print(e), + on_tap_down=lambda e: print(e), + on_tap_up=lambda e: print(e), + on_secondary_tap=lambda e: print(e), + on_secondary_tap_down=lambda e: print(e), + on_secondary_tap_up=lambda e: print(e), + on_long_press_start=lambda e: print(e), + on_long_press_end=lambda e: print(e), + on_secondary_long_press_start=lambda e: print(e), + on_secondary_long_press_end=lambda e: print(e), + on_double_tap=lambda e: print(e), + on_double_tap_down=lambda e: print(e), + on_pan_start=lambda e: print(e), + on_pan_update=lambda e: print(e), + on_pan_end=lambda e: print(e), + on_hover=lambda e: print(e), + on_enter=lambda e: print(e), + on_exit=lambda e: print(e), + content=ft.Container( + bgcolor=ft.Colors.GREEN, + width=200, + height=200, + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/gesture_detector/handling_events/pyproject.toml b/sdk/python/examples/controls/gesture_detector/handling_events/pyproject.toml new file mode 100644 index 0000000000..c753db7140 --- /dev/null +++ b/sdk/python/examples/controls/gesture_detector/handling_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "gesture-detector-handling-events" +version = "1.0.0" +description = "Logs tap, press, pan, hover, and enter/exit callbacks from a single GestureDetector." +requires-python = ">=3.10" +keywords = ["gesture detector", "events", "tap", "pan", "hover"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/GestureDetector"] + +[tool.flet.metadata] +title = "Handling events" +controls = ["SafeArea", "GestureDetector", "Container"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["multi-gesture callbacks", "hover and pointer callbacks"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/gesture_detector/mouse_cursors.py b/sdk/python/examples/controls/gesture_detector/mouse_cursors.py deleted file mode 100644 index 3fc1c567af..0000000000 --- a/sdk/python/examples/controls/gesture_detector/mouse_cursors.py +++ /dev/null @@ -1,38 +0,0 @@ -import random - -import flet as ft - - -def main(page: ft.Page): - def on_pan_update(event: ft.DragUpdateEvent[ft.GestureDetector]): - container.top = max(0.0, container.top + event.delta_y) - container.left = max(0.0, container.left + event.delta_x) - container.update() - - gesture_detector = ft.GestureDetector( - mouse_cursor=ft.MouseCursor.BASIC, - drag_interval=50, - on_pan_update=on_pan_update, - ) - container = ft.Container( - content=gesture_detector, - bgcolor=ft.Colors.AMBER, - width=150, - height=150, - left=0, - top=0, - ) - - def handle_button_click(e: ft.Event[ft.Button]): - gesture_detector.mouse_cursor = random.choice(list(ft.MouseCursor)) - text.value = f"Mouse Cursor: {gesture_detector.mouse_cursor}" - page.update() - - page.add( - ft.Stack(controls=[container], width=1000, height=500), - ft.Button("Change mouse Cursor", on_click=handle_button_click), - text := ft.Text(f"Mouse Cursor: {gesture_detector.mouse_cursor}"), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/gesture_detector/mouse_cursors/main.py b/sdk/python/examples/controls/gesture_detector/mouse_cursors/main.py new file mode 100644 index 0000000000..f0321d16c2 --- /dev/null +++ b/sdk/python/examples/controls/gesture_detector/mouse_cursors/main.py @@ -0,0 +1,50 @@ +import random + +import flet as ft + + +def main(page: ft.Page): + def on_pan_update(event: ft.DragUpdateEvent[ft.GestureDetector]): + container.top = max(0.0, container.top + event.local_delta.y) + container.left = max(0.0, container.left + event.local_delta.x) + container.update() + + gesture_detector = ft.GestureDetector( + mouse_cursor=ft.MouseCursor.BASIC, + drag_interval=50, + on_pan_update=on_pan_update, + ) + container = ft.Container( + bgcolor=ft.Colors.AMBER, + width=150, + height=150, + left=0, + top=0, + content=gesture_detector, + ) + + def handle_button_click(e: ft.Event[ft.Button]): + gesture_detector.mouse_cursor = random.choice(list(ft.MouseCursor)) + text.value = f"Mouse Cursor: {gesture_detector.mouse_cursor}" + gesture_detector.update() + text.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Stack( + width=1000, + height=500, + controls=[container], + ), + ft.Button("Change mouse Cursor", on_click=handle_button_click), + text := ft.Text(f"Mouse Cursor: {gesture_detector.mouse_cursor}"), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/gesture_detector/mouse_cursors/pyproject.toml b/sdk/python/examples/controls/gesture_detector/mouse_cursors/pyproject.toml new file mode 100644 index 0000000000..14df3ff2e1 --- /dev/null +++ b/sdk/python/examples/controls/gesture_detector/mouse_cursors/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "gesture-detector-mouse-cursors" +version = "1.0.0" +description = "Randomizes GestureDetector mouse cursors and drags the target container in a Stack." +requires-python = ">=3.10" +keywords = ["gesture detector", "mouse cursor", "drag", "events"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/GestureDetector"] + +[tool.flet.metadata] +title = "Mouse cursors" +controls = ["SafeArea", "Column", "Stack", "Container", "GestureDetector", "Button", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["cursor switching", "drag interaction", "live status text"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/gesture_detector/window_drag_area.py b/sdk/python/examples/controls/gesture_detector/window_drag_area.py deleted file mode 100644 index b93b627f15..0000000000 --- a/sdk/python/examples/controls/gesture_detector/window_drag_area.py +++ /dev/null @@ -1,27 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def on_pan_update(e: ft.DragUpdateEvent[ft.GestureDetector]): - page.window.left += e.global_delta.x - page.window.top += e.global_delta.y - page.update() - - page.add( - ft.Stack( - width=1000, - height=500, - controls=[ - ft.GestureDetector( - mouse_cursor=ft.MouseCursor.MOVE, - on_pan_update=on_pan_update, - left=200, - top=200, - content=ft.Container(bgcolor=ft.Colors.PINK, width=50, height=50), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/gesture_detector/window_drag_area/main.py b/sdk/python/examples/controls/gesture_detector/window_drag_area/main.py new file mode 100644 index 0000000000..19221a2d2c --- /dev/null +++ b/sdk/python/examples/controls/gesture_detector/window_drag_area/main.py @@ -0,0 +1,33 @@ +import flet as ft + + +def main(page: ft.Page): + def on_pan_update(e: ft.DragUpdateEvent[ft.GestureDetector]): + page.window.left += e.global_delta.x + page.window.top += e.global_delta.y + + page.add( + ft.SafeArea( + content=ft.Stack( + width=1000, + height=500, + controls=[ + ft.GestureDetector( + mouse_cursor=ft.MouseCursor.MOVE, + on_pan_update=on_pan_update, + left=200, + top=200, + content=ft.Container( + bgcolor=ft.Colors.PINK, + width=50, + height=50, + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/gesture_detector/window_drag_area/pyproject.toml b/sdk/python/examples/controls/gesture_detector/window_drag_area/pyproject.toml new file mode 100644 index 0000000000..d59165524f --- /dev/null +++ b/sdk/python/examples/controls/gesture_detector/window_drag_area/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "gesture-detector-window-drag-area" +version = "1.0.0" +description = "Moves the app window by dragging a GestureDetector handle control." +requires-python = ">=3.10" +keywords = ["gesture detector", "window", "drag", "desktop"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/GestureDetector"] + +[tool.flet.metadata] +title = "Window drag area" +controls = ["SafeArea", "Stack", "GestureDetector", "Container"] +layout_pattern = "canvas" +complexity = "basic" +features = ["window dragging", "global drag delta"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/grid_view/photo_gallery.py b/sdk/python/examples/controls/grid_view/photo_gallery.py deleted file mode 100644 index bbeb1dae1f..0000000000 --- a/sdk/python/examples/controls/grid_view/photo_gallery.py +++ /dev/null @@ -1,30 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "GridView Example" - page.theme_mode = ft.ThemeMode.DARK - page.padding = 50 - - page.add( - ft.GridView( - expand=1, - runs_count=5, - max_extent=150, - child_aspect_ratio=1.0, - spacing=5, - run_spacing=5, - controls=[ - ft.Image( - src=f"https://picsum.photos/150/150?{i}", - fit=ft.BoxFit.NONE, - repeat=ft.ImageRepeat.NO_REPEAT, - border_radius=ft.BorderRadius.all(10), - ) - for i in range(0, 60) - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/grid_view/photo_gallery/main.py b/sdk/python/examples/controls/grid_view/photo_gallery/main.py new file mode 100644 index 0000000000..ea96f88d95 --- /dev/null +++ b/sdk/python/examples/controls/grid_view/photo_gallery/main.py @@ -0,0 +1,34 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "GridView Example" + page.theme_mode = ft.ThemeMode.DARK + page.padding = 50 + + page.add( + ft.SafeArea( + expand=True, + content=ft.GridView( + expand=True, + runs_count=5, + max_extent=150, + child_aspect_ratio=1.0, + spacing=5, + run_spacing=5, + controls=[ + ft.Image( + src=f"https://picsum.photos/150/150?{i}", + fit=ft.BoxFit.NONE, + repeat=ft.ImageRepeat.NO_REPEAT, + border_radius=ft.BorderRadius.all(10), + ) + for i in range(0, 60) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/grid_view/photo_gallery/pyproject.toml b/sdk/python/examples/controls/grid_view/photo_gallery/pyproject.toml new file mode 100644 index 0000000000..4cb1c16f4c --- /dev/null +++ b/sdk/python/examples/controls/grid_view/photo_gallery/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "grid-view-photo-gallery" +version = "1.0.0" +description = "Renders a responsive GridView photo gallery with remote images and spacing configuration." +requires-python = ">=3.10" +keywords = ["grid view", "photo gallery", "images", "responsive layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/GridView"] + +[tool.flet.metadata] +title = "Photo gallery" +controls = ["SafeArea", "GridView", "Image"] +layout_pattern = "gallery" +complexity = "basic" +features = ["remote image grid", "responsive runs and extent", "rounded thumbnails"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/hero/__init__.py b/sdk/python/examples/controls/hero/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/hero/basic.py b/sdk/python/examples/controls/hero/basic.py deleted file mode 100644 index 69ecad6088..0000000000 --- a/sdk/python/examples/controls/hero/basic.py +++ /dev/null @@ -1,125 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - hero_tag = "demo-hero-card" - - def build_card(size: int, label: str) -> ft.Container: - icon_size = max(24, int(size * 0.28)) - return ft.Container( - width=size, - height=size, - border_radius=ft.BorderRadius.all(20), - gradient=ft.LinearGradient( - begin=ft.Alignment.TOP_LEFT, - end=ft.Alignment.BOTTOM_RIGHT, - colors=[ft.Colors.BLUE_700, ft.Colors.CYAN_400], - ), - alignment=ft.Alignment.CENTER, - content=ft.Stack( - fit=ft.StackFit.EXPAND, - controls=[ - ft.Container( - alignment=ft.Alignment.CENTER, - content=ft.Icon( - ft.Icons.ROCKET_LAUNCH, - size=icon_size, - color=ft.Colors.WHITE, - ), - ), - ft.Container( - alignment=ft.Alignment.BOTTOM_CENTER, - padding=ft.Padding.only(left=8, right=8, bottom=10), - content=ft.Text( - label, - color=ft.Colors.WHITE, - weight=ft.FontWeight.BOLD, - size=12, - no_wrap=True, - max_lines=1, - overflow=ft.TextOverflow.ELLIPSIS, - text_align=ft.TextAlign.CENTER, - ), - ), - ], - ), - ) - - async def go_to_details(_): - await page.push_route("/details") - - async def go_home(_): - await page.push_route("/") - - def build_home_view() -> ft.View: - return ft.View( - route="/", - appbar=ft.AppBar(title=ft.Text("Hero animation")), - controls=[ - ft.Container( - expand=True, - alignment=ft.Alignment.CENTER, - content=ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - spacing=20, - controls=[ - ft.Text("Tap the card to navigate"), - ft.GestureDetector( - mouse_cursor=ft.MouseCursor.CLICK, - on_tap=go_to_details, - content=ft.Hero( - tag=hero_tag, - content=build_card(130, "Open details"), - ), - ), - ], - ), - ) - ], - ) - - def build_details_view() -> ft.View: - return ft.View( - route="/details", - appbar=ft.AppBar(title=ft.Text("Details")), - controls=[ - ft.Container( - expand=True, - alignment=ft.Alignment.CENTER, - content=ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - spacing=20, - controls=[ - ft.Hero( - tag=hero_tag, - transition_on_user_gestures=True, - content=build_card(280, "Details"), - ), - ft.Button("Back", on_click=go_home), - ], - ), - ) - ], - ) - - def route_change(e: ft.RouteChangeEvent = None): - page.views.clear() - page.views.append(build_home_view()) - if page.route == "/details": - page.views.append(build_details_view()) - page.update() - - async def view_pop(e: ft.ViewPopEvent): - if e.view is not None: - page.views.remove(e.view) - await page.push_route(page.views[-1].route) - - page.on_route_change = route_change - page.on_view_pop = view_pop - route_change() - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/hero/basic/main.py b/sdk/python/examples/controls/hero/basic/main.py new file mode 100644 index 0000000000..cb3ba79d46 --- /dev/null +++ b/sdk/python/examples/controls/hero/basic/main.py @@ -0,0 +1,131 @@ +import flet as ft + + +def main(page: ft.Page): + hero_tag = "demo-hero-card" + + def build_card(size: int, label: str) -> ft.Container: + icon_size = max(24, int(size * 0.28)) + return ft.Container( + width=size, + height=size, + border_radius=ft.BorderRadius.all(20), + gradient=ft.LinearGradient( + begin=ft.Alignment.TOP_LEFT, + end=ft.Alignment.BOTTOM_RIGHT, + colors=[ft.Colors.BLUE_700, ft.Colors.CYAN_400], + ), + alignment=ft.Alignment.CENTER, + content=ft.Stack( + fit=ft.StackFit.EXPAND, + controls=[ + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Icon( + ft.Icons.ROCKET_LAUNCH, + size=icon_size, + color=ft.Colors.WHITE, + ), + ), + ft.Container( + alignment=ft.Alignment.BOTTOM_CENTER, + padding=ft.Padding.only(left=8, right=8, bottom=10), + content=ft.Text( + label, + color=ft.Colors.WHITE, + weight=ft.FontWeight.BOLD, + size=12, + no_wrap=True, + max_lines=1, + overflow=ft.TextOverflow.ELLIPSIS, + text_align=ft.TextAlign.CENTER, + ), + ), + ], + ), + ) + + async def go_to_details(_): + await page.push_route("/details") + + async def go_home(_): + await page.push_route("/") + + def build_home_view() -> ft.View: + return ft.View( + route="/", + appbar=ft.AppBar(title=ft.Text("Hero animation")), + controls=[ + ft.SafeArea( + expand=True, + content=ft.Container( + expand=True, + alignment=ft.Alignment.CENTER, + content=ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + spacing=20, + controls=[ + ft.Text("Tap the card to navigate"), + ft.GestureDetector( + mouse_cursor=ft.MouseCursor.CLICK, + on_tap=go_to_details, + content=ft.Hero( + tag=hero_tag, + content=build_card(130, "Open details"), + ), + ), + ], + ), + ), + ) + ], + ) + + def build_details_view() -> ft.View: + return ft.View( + route="/details", + appbar=ft.AppBar(title=ft.Text("Details")), + controls=[ + ft.SafeArea( + expand=True, + content=ft.Container( + expand=True, + alignment=ft.Alignment.CENTER, + content=ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + spacing=20, + controls=[ + ft.Hero( + tag=hero_tag, + transition_on_user_gestures=True, + content=build_card(280, "Details"), + ), + ft.Button("Back", on_click=go_home), + ], + ), + ), + ) + ], + ) + + def route_change(e: ft.RouteChangeEvent = None): + page.views.clear() + page.views.append(build_home_view()) + if page.route == "/details": + page.views.append(build_details_view()) + page.update() + + async def view_pop(e: ft.ViewPopEvent): + if e.view is not None: + page.views.remove(e.view) + await page.push_route(page.views[-1].route) + + page.on_route_change = route_change + page.on_view_pop = view_pop + route_change() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/hero/basic/pyproject.toml b/sdk/python/examples/controls/hero/basic/pyproject.toml new file mode 100644 index 0000000000..736bedf137 --- /dev/null +++ b/sdk/python/examples/controls/hero/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "hero-basic" +version = "1.0.0" +description = "Demonstrates Hero animation between home and details views with a shared tagged card." +requires-python = ">=3.10" +keywords = ["hero", "animation", "route transition", "navigation", "gesture"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/Hero"] + +[tool.flet.metadata] +title = "Basic" +controls = ["View", "SafeArea", "Container", "Column", "GestureDetector", "Hero", "AppBar", "Button", "Text", "Icon"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["hero route transition", "shared hero tag", "view navigation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/hero/gallery.py b/sdk/python/examples/controls/hero/gallery.py deleted file mode 100644 index 0139e4c3ca..0000000000 --- a/sdk/python/examples/controls/hero/gallery.py +++ /dev/null @@ -1,217 +0,0 @@ -import flet as ft - -PRODUCTS = [ - { - "id": 1, - "name": "Comet", - "subtitle": "Fast launch profile", - "description": "Balanced setup for quick iteration and lightweight payloads.", - "icon": ft.Icons.ROCKET_LAUNCH, - "start_color": ft.Colors.BLUE_700, - "end_color": ft.Colors.CYAN_400, - }, - { - "id": 2, - "name": "Nebula", - "subtitle": "Visual pipeline", - "description": "Optimized for rich UI transitions and animated data surfaces.", - "icon": ft.Icons.AUTO_AWESOME, - "start_color": ft.Colors.DEEP_PURPLE_700, - "end_color": ft.Colors.PINK_400, - }, - { - "id": 3, - "name": "Orbit", - "subtitle": "Reliable baseline", - "description": "Stable profile focused on consistency and performance.", - "icon": ft.Icons.PUBLIC, - "start_color": ft.Colors.GREEN_700, - "end_color": ft.Colors.LIGHT_GREEN_400, - }, - { - "id": 4, - "name": "Aurora", - "subtitle": "Analytics focus", - "description": "Configured for telemetry-heavy apps and live metrics.", - "icon": ft.Icons.ANALYTICS, - "start_color": ft.Colors.ORANGE_700, - "end_color": ft.Colors.AMBER_400, - }, - { - "id": 5, - "name": "Pulse", - "subtitle": "Real-time profile", - "description": "Tuned for frequent updates, messaging, and event streams.", - "icon": ft.Icons.BOLT, - "start_color": ft.Colors.RED_700, - "end_color": ft.Colors.DEEP_ORANGE_400, - }, -] - - -def hero_tag(product_id: int) -> str: - return f"product-hero-{product_id}" - - -def main(page: ft.Page): - def product_by_id(product_id: int): - for product in PRODUCTS: - if product["id"] == product_id: - return product - return None - - def route_product_id(route: str): - segments = [segment for segment in route.split("/") if segment] - if len(segments) != 2 or segments[0] != "product": - return None - try: - return int(segments[1]) - except ValueError: - return None - - async def open_product(product_id: int): - await page.push_route(f"/product/{product_id}") - - async def back_to_gallery(e: ft.Event[ft.Button]): - await page.push_route("/") - - def build_hero_tile(product: dict, size: int) -> ft.Hero: - icon_size = max(24, int(size * 0.4)) - return ft.Hero( - tag=hero_tag(product["id"]), - transition_on_user_gestures=True, - content=ft.Container( - width=size, - height=size, - border_radius=ft.BorderRadius.all(20), - gradient=ft.LinearGradient( - begin=ft.Alignment.TOP_LEFT, - end=ft.Alignment.BOTTOM_RIGHT, - colors=[product["start_color"], product["end_color"]], - ), - alignment=ft.Alignment.CENTER, - content=ft.Icon(product["icon"], color=ft.Colors.WHITE, size=icon_size), - ), - ) - - def build_home_view() -> ft.View: - rows: list[ft.Control] = [] - for product in PRODUCTS: - rows.append( - ft.GestureDetector( - mouse_cursor=ft.MouseCursor.CLICK, - on_tap=lambda _, pid=product["id"]: page.run_task( - open_product, pid - ), - content=ft.Card( - content=ft.Container( - padding=12, - content=ft.Row( - vertical_alignment=ft.CrossAxisAlignment.CENTER, - spacing=12, - controls=[ - build_hero_tile(product, 78), - ft.Column( - spacing=4, - controls=[ - ft.Text( - product["name"], - size=18, - weight=ft.FontWeight.W_600, - ), - ft.Text( - product["subtitle"], - color=ft.Colors.ON_SURFACE_VARIANT, - ), - ft.Text( - "Tap to view details", - size=12, - color=ft.Colors.PRIMARY, - ), - ], - ), - ], - ), - ) - ), - ) - ) - - return ft.View( - route="/", - appbar=ft.AppBar(title=ft.Text("Hero gallery")), - controls=[ - ft.Column( - expand=True, - spacing=10, - scroll=ft.ScrollMode.AUTO, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text( - "Each card has a unique Hero tag. " - "Open any item to see a matched transition.", - color=ft.Colors.ON_SURFACE_VARIANT, - ), - *rows, - ], - ) - ], - ) - - def build_details_view(product: dict) -> ft.View: - return ft.View( - route=f"/product/{product['id']}", - appbar=ft.AppBar(title=ft.Text(product["name"])), - controls=[ - ft.Column( - expand=True, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - spacing=16, - scroll=ft.ScrollMode.AUTO, - controls=[ - ft.Container(height=8), - build_hero_tile(product, 220), - ft.Text( - product["subtitle"], - size=20, - weight=ft.FontWeight.W_600, - text_align=ft.TextAlign.CENTER, - ), - ft.Container( - width=520, - content=ft.Text( - product["description"], - text_align=ft.TextAlign.CENTER, - color=ft.Colors.ON_SURFACE_VARIANT, - ), - ), - ft.Button("Back to gallery", on_click=back_to_gallery), - ], - ) - ], - ) - - def route_change(e: ft.RouteChangeEvent = None): - page.views.clear() - page.views.append(build_home_view()) - - pid = route_product_id(page.route) - if pid is not None: - product = product_by_id(pid) - if product is not None: - page.views.append(build_details_view(product)) - - page.update() - - async def view_pop(e: ft.ViewPopEvent): - if e.view is not None: - page.views.remove(e.view) - await page.push_route(page.views[-1].route) - - page.on_route_change = route_change - page.on_view_pop = view_pop - route_change() - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/hero/gallery/main.py b/sdk/python/examples/controls/hero/gallery/main.py new file mode 100644 index 0000000000..5acb56eca3 --- /dev/null +++ b/sdk/python/examples/controls/hero/gallery/main.py @@ -0,0 +1,223 @@ +import flet as ft + +PRODUCTS = [ + { + "id": 1, + "name": "Comet", + "subtitle": "Fast launch profile", + "description": "Balanced setup for quick iteration and lightweight payloads.", + "icon": ft.Icons.ROCKET_LAUNCH, + "start_color": ft.Colors.BLUE_700, + "end_color": ft.Colors.CYAN_400, + }, + { + "id": 2, + "name": "Nebula", + "subtitle": "Visual pipeline", + "description": "Optimized for rich UI transitions and animated data surfaces.", + "icon": ft.Icons.AUTO_AWESOME, + "start_color": ft.Colors.DEEP_PURPLE_700, + "end_color": ft.Colors.PINK_400, + }, + { + "id": 3, + "name": "Orbit", + "subtitle": "Reliable baseline", + "description": "Stable profile focused on consistency and performance.", + "icon": ft.Icons.PUBLIC, + "start_color": ft.Colors.GREEN_700, + "end_color": ft.Colors.LIGHT_GREEN_400, + }, + { + "id": 4, + "name": "Aurora", + "subtitle": "Analytics focus", + "description": "Configured for telemetry-heavy apps and live metrics.", + "icon": ft.Icons.ANALYTICS, + "start_color": ft.Colors.ORANGE_700, + "end_color": ft.Colors.AMBER_400, + }, + { + "id": 5, + "name": "Pulse", + "subtitle": "Real-time profile", + "description": "Tuned for frequent updates, messaging, and event streams.", + "icon": ft.Icons.BOLT, + "start_color": ft.Colors.RED_700, + "end_color": ft.Colors.DEEP_ORANGE_400, + }, +] + + +def hero_tag(product_id: int) -> str: + return f"product-hero-{product_id}" + + +def main(page: ft.Page): + def product_by_id(product_id: int): + for product in PRODUCTS: + if product["id"] == product_id: + return product + return None + + def route_product_id(route: str): + segments = [segment for segment in route.split("/") if segment] + if len(segments) != 2 or segments[0] != "product": + return None + try: + return int(segments[1]) + except ValueError: + return None + + async def open_product(product_id: int): + await page.push_route(f"/product/{product_id}") + + async def back_to_gallery(e: ft.Event[ft.Button]): + await page.push_route("/") + + def build_hero_tile(product: dict, size: int) -> ft.Hero: + icon_size = max(24, int(size * 0.4)) + return ft.Hero( + tag=hero_tag(product["id"]), + transition_on_user_gestures=True, + content=ft.Container( + width=size, + height=size, + border_radius=ft.BorderRadius.all(20), + gradient=ft.LinearGradient( + begin=ft.Alignment.TOP_LEFT, + end=ft.Alignment.BOTTOM_RIGHT, + colors=[product["start_color"], product["end_color"]], + ), + alignment=ft.Alignment.CENTER, + content=ft.Icon(product["icon"], color=ft.Colors.WHITE, size=icon_size), + ), + ) + + def build_home_view() -> ft.View: + rows: list[ft.Control] = [] + for product in PRODUCTS: + rows.append( + ft.GestureDetector( + mouse_cursor=ft.MouseCursor.CLICK, + on_tap=lambda _, pid=product["id"]: page.run_task( + open_product, pid + ), + content=ft.Card( + content=ft.Container( + padding=12, + content=ft.Row( + vertical_alignment=ft.CrossAxisAlignment.CENTER, + spacing=12, + controls=[ + build_hero_tile(product, 78), + ft.Column( + spacing=4, + controls=[ + ft.Text( + product["name"], + size=18, + weight=ft.FontWeight.W_600, + ), + ft.Text( + product["subtitle"], + color=ft.Colors.ON_SURFACE_VARIANT, + ), + ft.Text( + "Tap to view details", + size=12, + color=ft.Colors.PRIMARY, + ), + ], + ), + ], + ), + ) + ), + ) + ) + + return ft.View( + route="/", + appbar=ft.AppBar(title=ft.Text("Hero gallery")), + controls=[ + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + spacing=10, + scroll=ft.ScrollMode.AUTO, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text( + "Each card has a unique Hero tag. " + "Open any item to see a matched transition.", + color=ft.Colors.ON_SURFACE_VARIANT, + ), + *rows, + ], + ), + ) + ], + ) + + def build_details_view(product: dict) -> ft.View: + return ft.View( + route=f"/product/{product['id']}", + appbar=ft.AppBar(title=ft.Text(product["name"])), + controls=[ + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + spacing=16, + scroll=ft.ScrollMode.AUTO, + controls=[ + ft.Container(height=8), + build_hero_tile(product, 220), + ft.Text( + product["subtitle"], + size=20, + weight=ft.FontWeight.W_600, + text_align=ft.TextAlign.CENTER, + ), + ft.Container( + width=520, + content=ft.Text( + product["description"], + text_align=ft.TextAlign.CENTER, + color=ft.Colors.ON_SURFACE_VARIANT, + ), + ), + ft.Button("Back to gallery", on_click=back_to_gallery), + ], + ), + ) + ], + ) + + def route_change(e: ft.RouteChangeEvent = None): + page.views.clear() + page.views.append(build_home_view()) + + pid = route_product_id(page.route) + if pid is not None: + product = product_by_id(pid) + if product is not None: + page.views.append(build_details_view(product)) + + page.update() + + async def view_pop(e: ft.ViewPopEvent): + if e.view is not None: + page.views.remove(e.view) + await page.push_route(page.views[-1].route) + + page.on_route_change = route_change + page.on_view_pop = view_pop + route_change() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/hero/gallery/pyproject.toml b/sdk/python/examples/controls/hero/gallery/pyproject.toml new file mode 100644 index 0000000000..d5fa75ca01 --- /dev/null +++ b/sdk/python/examples/controls/hero/gallery/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "hero-gallery" +version = "1.0.0" +description = "Shows a gallery of items with unique Hero tags and animated transitions to detail views." +requires-python = ">=3.10" +keywords = ["hero", "gallery", "animation", "navigation", "cards"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/Hero"] + +[tool.flet.metadata] +title = "Gallery" +controls = ["View", "SafeArea", "Column", "Row", "Container", "Card", "GestureDetector", "Hero", "AppBar", "Button", "Text", "Icon"] +layout_pattern = "gallery" +complexity = "basic" +features = ["multiple hero tags", "list-to-details transition", "scrollable gallery"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/icon/basic.py b/sdk/python/examples/controls/icon/basic.py deleted file mode 100644 index 2ae5b4eb1b..0000000000 --- a/sdk/python/examples/controls/icon/basic.py +++ /dev/null @@ -1,41 +0,0 @@ -from typing import cast - -import flet as ft - - -def main(page: ft.Page): - page.add( - # material - ft.Row( - controls=[ - ft.Icon(ft.Icons.FAVORITE, color=ft.Colors.PINK), - ft.Icon(ft.Icons.AUDIOTRACK, color=ft.Colors.GREEN_400, size=30), - ft.Icon(ft.Icons.BEACH_ACCESS, color=ft.Colors.BLUE, size=50), - ft.Icon(ft.Icons.SETTINGS, color="#c1c1c1"), - ] - ), - # cupertino - ft.Row( - controls=[ - ft.Icon(ft.CupertinoIcons.PROFILE_CIRCLED, color=ft.Colors.PINK), - ft.Icon( - icon=cast(ft.CupertinoIcons, ft.CupertinoIcons.random()), - color=ft.Colors.GREEN_400, - size=30, - ), - ft.Icon( - icon=cast(ft.CupertinoIcons, ft.CupertinoIcons.random()), - color=ft.Colors.BLUE, - size=50, - ), - ft.Icon( - icon=cast(ft.CupertinoIcons, ft.CupertinoIcons.random()), - color="#c1c1c1", - ), - ] - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/icon/basic/main.py b/sdk/python/examples/controls/icon/basic/main.py new file mode 100644 index 0000000000..e6269a2e42 --- /dev/null +++ b/sdk/python/examples/controls/icon/basic/main.py @@ -0,0 +1,67 @@ +from typing import cast + +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + # material + ft.Row( + controls=[ + ft.Icon(ft.Icons.FAVORITE, color=ft.Colors.PINK), + ft.Icon( + ft.Icons.AUDIOTRACK, + color=ft.Colors.GREEN_400, + size=30, + ), + ft.Icon( + ft.Icons.BEACH_ACCESS, + color=ft.Colors.BLUE, + size=50, + ), + ft.Icon(ft.Icons.SETTINGS, color="#c1c1c1"), + ] + ), + # cupertino + ft.Row( + controls=[ + ft.Icon( + ft.CupertinoIcons.PROFILE_CIRCLED, + color=ft.Colors.PINK, + ), + ft.Icon( + icon=cast( + ft.CupertinoIcons, + ft.CupertinoIcons.random(), + ), + color=ft.Colors.GREEN_400, + size=30, + ), + ft.Icon( + icon=cast( + ft.CupertinoIcons, + ft.CupertinoIcons.random(), + ), + color=ft.Colors.BLUE, + size=50, + ), + ft.Icon( + icon=cast( + ft.CupertinoIcons, + ft.CupertinoIcons.random(), + ), + color="#c1c1c1", + ), + ] + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/icon/basic/pyproject.toml b/sdk/python/examples/controls/icon/basic/pyproject.toml new file mode 100644 index 0000000000..4c927ec0ca --- /dev/null +++ b/sdk/python/examples/controls/icon/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "icon-basic" +version = "1.0.0" +description = "Displays Material and Cupertino icons with different sizes and colors." +requires-python = ">=3.10" +keywords = ["icon", "material icons", "cupertino icons", "styling"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Icon"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "Row", "Icon"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["material icons", "cupertino icons", "icon color and size"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/icon_button/handling_clicks.py b/sdk/python/examples/controls/icon_button/handling_clicks.py deleted file mode 100644 index b306db4706..0000000000 --- a/sdk/python/examples/controls/icon_button/handling_clicks.py +++ /dev/null @@ -1,22 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "IconButton Example" - - def button_clicked(e: ft.Event[ft.IconButton]): - button.data += 1 - message.value = f"Button clicked {button.data} time(s)" - page.update() - - page.add( - button := ft.IconButton( - icon=ft.Icons.PLAY_CIRCLE_FILL_OUTLINED, - data=0, - on_click=button_clicked, - ), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/icon_button/handling_clicks/main.py b/sdk/python/examples/controls/icon_button/handling_clicks/main.py new file mode 100644 index 0000000000..a93ad915de --- /dev/null +++ b/sdk/python/examples/controls/icon_button/handling_clicks/main.py @@ -0,0 +1,29 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "IconButton Example" + + def button_clicked(e: ft.Event[ft.IconButton]): + button.data += 1 + message.value = f"Button clicked {button.data} time(s)" + message.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + button := ft.IconButton( + icon=ft.Icons.PLAY_CIRCLE_FILL_OUTLINED, + data=0, + on_click=button_clicked, + ), + message := ft.Text(), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/icon_button/handling_clicks/pyproject.toml b/sdk/python/examples/controls/icon_button/handling_clicks/pyproject.toml new file mode 100644 index 0000000000..8fd6e7bd26 --- /dev/null +++ b/sdk/python/examples/controls/icon_button/handling_clicks/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "icon-button-handling-clicks" +version = "1.0.0" +description = "Counts IconButton clicks and updates a text status message." +requires-python = ">=3.10" +keywords = ["icon button", "click handling", "events", "counter"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/IconButton"] + +[tool.flet.metadata] +title = "Handling clicks" +controls = ["SafeArea", "Column", "IconButton", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["click callback", "live click counter"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/icon_button/selected_icon.py b/sdk/python/examples/controls/icon_button/selected_icon.py deleted file mode 100644 index 8a0a7b2c5c..0000000000 --- a/sdk/python/examples/controls/icon_button/selected_icon.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "IconButton Example" - page.padding = 10 - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - def handle_click(e: ft.Event[ft.IconButton]): - e.control.selected = not e.control.selected - e.control.update() - - page.add( - ft.IconButton( - icon=ft.Icons.BATTERY_1_BAR, - selected_icon=ft.Icons.BATTERY_FULL, - scale=5, - on_click=handle_click, - selected=False, - style=ft.ButtonStyle( - color={ - ft.ControlState.SELECTED: ft.Colors.GREEN, - ft.ControlState.DEFAULT: ft.Colors.RED, - } - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/icon_button/selected_icon/main.py b/sdk/python/examples/controls/icon_button/selected_icon/main.py new file mode 100644 index 0000000000..51da3b112c --- /dev/null +++ b/sdk/python/examples/controls/icon_button/selected_icon/main.py @@ -0,0 +1,34 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "IconButton Example" + page.padding = 10 + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + def handle_click(e: ft.Event[ft.IconButton]): + e.control.selected = not e.control.selected + e.control.update() + + page.add( + ft.SafeArea( + content=ft.IconButton( + icon=ft.Icons.BATTERY_1_BAR, + selected_icon=ft.Icons.BATTERY_FULL, + scale=5, + on_click=handle_click, + selected=False, + style=ft.ButtonStyle( + color={ + ft.ControlState.SELECTED: ft.Colors.GREEN, + ft.ControlState.DEFAULT: ft.Colors.RED, + } + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/icon_button/selected_icon/pyproject.toml b/sdk/python/examples/controls/icon_button/selected_icon/pyproject.toml new file mode 100644 index 0000000000..6544e68bce --- /dev/null +++ b/sdk/python/examples/controls/icon_button/selected_icon/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "icon-button-selected-icon" +version = "1.0.0" +description = "Toggles IconButton selected state and switches between default and selected icons." +requires-python = ">=3.10" +keywords = ["icon button", "selected icon", "toggle", "button style"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/IconButton"] + +[tool.flet.metadata] +title = "Selected icon" +controls = ["SafeArea", "IconButton", "ButtonStyle"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["selected state toggle", "selected icon", "state-based colors"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/icon_button/variants.py b/sdk/python/examples/controls/icon_button/variants.py deleted file mode 100644 index 754208aa16..0000000000 --- a/sdk/python/examples/controls/icon_button/variants.py +++ /dev/null @@ -1,104 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "IconButton variants" - page.padding = 10 - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - def toggle_icon_button(e): - e.control.selected = not e.control.selected - - page.add( - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - spacing=50, - controls=[ - # Normal buttons column (enabled only) - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - spacing=20, - controls=[ - ft.Text( - "Normal", - theme_style=ft.TextThemeStyle.BODY_MEDIUM, - ), - ft.IconButton( - icon=ft.Icons.FILTER_DRAMA, - ), - ft.FilledIconButton( - icon=ft.Icons.FILTER_DRAMA, - ), - ft.FilledTonalIconButton( - icon=ft.Icons.FILTER_DRAMA, - ), - ft.OutlinedIconButton( - icon=ft.Icons.FILTER_DRAMA, - ), - ], - ), - # Disabled buttons column - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - spacing=20, - controls=[ - ft.Text( - "Disabled", - theme_style=ft.TextThemeStyle.BODY_MEDIUM, - ), - ft.IconButton( - icon=ft.Icons.FILTER_DRAMA, - disabled=True, - ), - ft.FilledIconButton( - icon=ft.Icons.FILTER_DRAMA, - disabled=True, - ), - ft.FilledTonalIconButton( - icon=ft.Icons.FILTER_DRAMA, - disabled=True, - ), - ft.OutlinedIconButton( - icon=ft.Icons.FILTER_DRAMA, - disabled=True, - ), - ], - ), - # Toggle buttons column - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - spacing=20, - controls=[ - ft.Text( - "Toggle", - theme_style=ft.TextThemeStyle.BODY_MEDIUM, - ), - ft.IconButton( - icon=ft.Icons.FILTER_DRAMA, - selected=False, - on_click=toggle_icon_button, - ), - ft.FilledIconButton( - icon=ft.Icons.FILTER_DRAMA, - selected=False, - on_click=toggle_icon_button, - ), - ft.FilledTonalIconButton( - icon=ft.Icons.FILTER_DRAMA, - selected=False, - on_click=toggle_icon_button, - ), - ft.OutlinedIconButton( - icon=ft.Icons.FILTER_DRAMA, - selected=False, - on_click=toggle_icon_button, - ), - ], - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/icon_button/variants/main.py b/sdk/python/examples/controls/icon_button/variants/main.py new file mode 100644 index 0000000000..07738941ab --- /dev/null +++ b/sdk/python/examples/controls/icon_button/variants/main.py @@ -0,0 +1,107 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "IconButton variants" + page.padding = 10 + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + def toggle_icon_button(e): + e.control.selected = not e.control.selected + + page.add( + ft.SafeArea( + content=ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + spacing=50, + controls=[ + # Normal buttons column (enabled only) + ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + spacing=20, + controls=[ + ft.Text( + "Normal", + theme_style=ft.TextThemeStyle.BODY_MEDIUM, + ), + ft.IconButton( + icon=ft.Icons.FILTER_DRAMA, + ), + ft.FilledIconButton( + icon=ft.Icons.FILTER_DRAMA, + ), + ft.FilledTonalIconButton( + icon=ft.Icons.FILTER_DRAMA, + ), + ft.OutlinedIconButton( + icon=ft.Icons.FILTER_DRAMA, + ), + ], + ), + # Disabled buttons column + ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + spacing=20, + controls=[ + ft.Text( + "Disabled", + theme_style=ft.TextThemeStyle.BODY_MEDIUM, + ), + ft.IconButton( + icon=ft.Icons.FILTER_DRAMA, + disabled=True, + ), + ft.FilledIconButton( + icon=ft.Icons.FILTER_DRAMA, + disabled=True, + ), + ft.FilledTonalIconButton( + icon=ft.Icons.FILTER_DRAMA, + disabled=True, + ), + ft.OutlinedIconButton( + icon=ft.Icons.FILTER_DRAMA, + disabled=True, + ), + ], + ), + # Toggle buttons column + ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + spacing=20, + controls=[ + ft.Text( + "Toggle", + theme_style=ft.TextThemeStyle.BODY_MEDIUM, + ), + ft.IconButton( + icon=ft.Icons.FILTER_DRAMA, + selected=False, + on_click=toggle_icon_button, + ), + ft.FilledIconButton( + icon=ft.Icons.FILTER_DRAMA, + selected=False, + on_click=toggle_icon_button, + ), + ft.FilledTonalIconButton( + icon=ft.Icons.FILTER_DRAMA, + selected=False, + on_click=toggle_icon_button, + ), + ft.OutlinedIconButton( + icon=ft.Icons.FILTER_DRAMA, + selected=False, + on_click=toggle_icon_button, + ), + ], + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/icon_button/variants/pyproject.toml b/sdk/python/examples/controls/icon_button/variants/pyproject.toml new file mode 100644 index 0000000000..163fd07c2c --- /dev/null +++ b/sdk/python/examples/controls/icon_button/variants/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "icon-button-variants" +version = "1.0.0" +description = "Compares normal, disabled, and toggle variants across IconButton style families." +requires-python = ">=3.10" +keywords = ["icon button", "variants", "filled", "outlined", "toggle"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/IconButton"] + +[tool.flet.metadata] +title = "Variants" +controls = ["SafeArea", "Row", "Column", "Text", "IconButton", "FilledIconButton", "FilledTonalIconButton", "OutlinedIconButton"] +layout_pattern = "comparison" +complexity = "basic" +features = ["variant comparison", "disabled states", "toggle buttons"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/image/dynamic_svg.py b/sdk/python/examples/controls/image/dynamic_svg.py deleted file mode 100644 index 7ebccb4ba2..0000000000 --- a/sdk/python/examples/controls/image/dynamic_svg.py +++ /dev/null @@ -1,25 +0,0 @@ -import asyncio - -import flet as ft - - -async def main(page: ft.Page): - svg_image = """ - - - - - -""" - - img = ft.Image(src=svg_image.format(0, 0)) - page.add(img) - - for c in range(0, 10): - for i in range(0, 10): - img.src = svg_image.format(i * 10, i * 10) - img.update() - await asyncio.sleep(0.1) - - -ft.run(main) diff --git a/sdk/python/examples/controls/image/dynamic_svg/main.py b/sdk/python/examples/controls/image/dynamic_svg/main.py new file mode 100644 index 0000000000..f7fbf83804 --- /dev/null +++ b/sdk/python/examples/controls/image/dynamic_svg/main.py @@ -0,0 +1,26 @@ +import asyncio + +import flet as ft + + +async def main(page: ft.Page): + svg_image = """ + + + + + +""" + + img = ft.Image(src=svg_image.format(0, 0)) + page.add(ft.SafeArea(content=img)) + + for _ in range(0, 10): + for i in range(0, 10): + img.src = svg_image.format(i * 10, i * 10) + img.update() + await asyncio.sleep(0.1) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/image/dynamic_svg/pyproject.toml b/sdk/python/examples/controls/image/dynamic_svg/pyproject.toml new file mode 100644 index 0000000000..9b980ea1ea --- /dev/null +++ b/sdk/python/examples/controls/image/dynamic_svg/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "image-dynamic-svg" +version = "1.0.0" +description = "Animates an inline SVG image by updating ellipse dimensions over time." +requires-python = ">=3.10" +keywords = ["image", "svg", "dynamic svg", "animation", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Image"] + +[tool.flet.metadata] +title = "Displaying a dynamic SVG image" +controls = ["SafeArea", "Image"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["inline svg generation", "async animation loop", "dynamic image updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/image/fade_in.py b/sdk/python/examples/controls/image/fade_in.py deleted file mode 100644 index d92a620668..0000000000 --- a/sdk/python/examples/controls/image/fade_in.py +++ /dev/null @@ -1,35 +0,0 @@ -import time - -import flet as ft - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def show_random(e: ft.Event[ft.Button]): - image.src = f"https://picsum.photos/320/200?random={time.time()}" # random - - page.add( - image := ft.Image( - src="https://picsum.photos/320/200?random=1", - width=360, - height=220, - fit=ft.BoxFit.COVER, - # solid blue image as bas64 - placeholder_src="data:image/png;base64,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", # noqa: E501 - placeholder_fade_out_animation=ft.Animation( - duration=ft.Duration(milliseconds=900), - curve=ft.AnimationCurve.EASE_OUT, - ), - fade_in_animation=ft.Animation( - duration=ft.Duration(milliseconds=700), - curve=ft.AnimationCurve.EASE_IN_OUT, - ), - ), - ft.Button("Show random image", on_click=show_random), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/image/fade_in/main.py b/sdk/python/examples/controls/image/fade_in/main.py new file mode 100644 index 0000000000..1fc7a187de --- /dev/null +++ b/sdk/python/examples/controls/image/fade_in/main.py @@ -0,0 +1,43 @@ +import time + +import flet as ft + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def show_random(e: ft.Event[ft.Button]): + image.src = f"https://picsum.photos/320/200?random={time.time()}" # random + image.update() + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + image := ft.Image( + src="https://picsum.photos/320/200?random=1", + width=360, + height=220, + fit=ft.BoxFit.COVER, + # solid blue image as bas64 + placeholder_src="data:image/png;base64,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", # noqa: E501 + placeholder_fade_out_animation=ft.Animation( + duration=ft.Duration(milliseconds=900), + curve=ft.AnimationCurve.EASE_OUT, + ), + fade_in_animation=ft.Animation( + duration=ft.Duration(milliseconds=700), + curve=ft.AnimationCurve.EASE_IN_OUT, + ), + ), + ft.Button("Show random image", on_click=show_random), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/image/fade_in/pyproject.toml b/sdk/python/examples/controls/image/fade_in/pyproject.toml new file mode 100644 index 0000000000..6ce30c1956 --- /dev/null +++ b/sdk/python/examples/controls/image/fade_in/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "image-fade-in" +version = "1.0.0" +description = "Loads random network images with placeholder and fade-in animations." +requires-python = ">=3.10" +keywords = ["image", "fade in", "placeholder", "animation", "network"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Image"] + +[tool.flet.metadata] +title = "Fade-in images with a placeholder" +controls = ["SafeArea", "Column", "Image", "Button", "Animation", "Duration"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["placeholder image", "fade-in animation", "dynamic image source"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/image/gallery.py b/sdk/python/examples/controls/image/gallery.py deleted file mode 100644 index fb6d6bc2ce..0000000000 --- a/sdk/python/examples/controls/image/gallery.py +++ /dev/null @@ -1,33 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Image Example" - page.theme_mode = ft.ThemeMode.LIGHT - page.padding = 50 - - page.add( - ft.Image( - src="app_icon_512.png", - width=100, - height=100, - fit=ft.BoxFit.CONTAIN, - ), - gallery := ft.Row(expand=1, wrap=False, scroll=ft.ScrollMode.ALWAYS), - ) - - for i in range(0, 30): - gallery.controls.append( - ft.Image( - src=f"https://picsum.photos/200/200?{i}", - width=200, - height=200, - fit=ft.BoxFit.NONE, - repeat=ft.ImageRepeat.NO_REPEAT, - border_radius=ft.BorderRadius.all(10), - ) - ) - page.update() - - -ft.run(main) diff --git a/sdk/python/examples/controls/image/gallery/assets/app_icon_512.png b/sdk/python/examples/controls/image/gallery/assets/app_icon_512.png new file mode 100644 index 0000000000..f89a749ada Binary files /dev/null and b/sdk/python/examples/controls/image/gallery/assets/app_icon_512.png differ diff --git a/sdk/python/examples/controls/image/gallery/main.py b/sdk/python/examples/controls/image/gallery/main.py new file mode 100644 index 0000000000..4aacd340d1 --- /dev/null +++ b/sdk/python/examples/controls/image/gallery/main.py @@ -0,0 +1,43 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Image Example" + page.theme_mode = ft.ThemeMode.LIGHT + page.padding = 50 + + gallery = ft.Row(expand=True, wrap=False, scroll=ft.ScrollMode.ALWAYS) + + for i in range(0, 30): + gallery.controls.append( + ft.Image( + src=f"https://picsum.photos/200/200?{i}", + width=200, + height=200, + fit=ft.BoxFit.NONE, + repeat=ft.ImageRepeat.NO_REPEAT, + border_radius=ft.BorderRadius.all(10), + ) + ) + + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.Image( + src="assets/app_icon_512.png", + width=100, + height=100, + fit=ft.BoxFit.CONTAIN, + ), + gallery, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/image/gallery/pyproject.toml b/sdk/python/examples/controls/image/gallery/pyproject.toml new file mode 100644 index 0000000000..b82b3765f2 --- /dev/null +++ b/sdk/python/examples/controls/image/gallery/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "image-gallery" +version = "1.0.0" +description = "Shows a local app icon and a horizontally scrollable gallery of remote images." +requires-python = ">=3.10" +keywords = ["image", "gallery", "scroll", "assets", "network images"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Image"] + +[tool.flet.metadata] +title = "Image gallery" +controls = ["SafeArea", "Column", "Row", "Image"] +layout_pattern = "gallery" +complexity = "basic" +features = ["local asset image", "horizontal scrolling", "network image thumbnails"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/image/gapless_playback.py b/sdk/python/examples/controls/image/gapless_playback.py deleted file mode 100644 index 0a3c4bc5fc..0000000000 --- a/sdk/python/examples/controls/image/gapless_playback.py +++ /dev/null @@ -1,74 +0,0 @@ -import random - -import flet as ft - - -def get_new_image_src() -> str: - random_id = random.randint(1, 85) - return f"https://picsum.photos/id/{random_id}/1280/720" - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - initial_src = get_new_image_src() - image_with_gapless = ft.Image( - src=initial_src, - width=340, - height=220, - fit=ft.BoxFit.COVER, - gapless_playback=True, - ) - image_without_gapless = ft.Image( - src=initial_src, - width=340, - height=220, - fit=ft.BoxFit.COVER, - gapless_playback=False, - ) - - def image_panel(title: str, image: ft.Image): - return ft.Column( - spacing=8, - width=340, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(title, weight=ft.FontWeight.BOLD), - ft.Container( - content=image, - bgcolor=ft.Colors.BLUE_GREY_900, - border_radius=ft.BorderRadius.all(12), - clip_behavior=ft.ClipBehavior.ANTI_ALIAS, - ), - ], - ) - - def next_photo(e: ft.Event[ft.Button]): - new_src = get_new_image_src() - image_with_gapless.src = new_src - image_without_gapless.src = new_src - page.update() - - page.appbar = ft.AppBar(title="Gapless Playback Showcase") - page.add( - ft.Text( - "Click 'Load next photo' to switch both images to a new URL.\n" - "The left/top image keeps showing the previous frame while loading the " - "next one. This is referred to as gapless playback.", - text_align=ft.TextAlign.CENTER, - ), - ft.Row( - wrap=True, - spacing=20, - run_spacing=20, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - image_panel("gapless_playback=True", image_with_gapless), - image_panel("gapless_playback=False", image_without_gapless), - ], - ), - ft.FilledButton("Load next photo", on_click=next_photo), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/image/gapless_playback/main.py b/sdk/python/examples/controls/image/gapless_playback/main.py new file mode 100644 index 0000000000..a6edc789f2 --- /dev/null +++ b/sdk/python/examples/controls/image/gapless_playback/main.py @@ -0,0 +1,86 @@ +import random + +import flet as ft + + +def get_new_image_src() -> str: + random_id = random.randint(1, 85) + return f"https://picsum.photos/id/{random_id}/1280/720" + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + initial_src = get_new_image_src() + image_with_gapless = ft.Image( + src=initial_src, + width=340, + height=220, + fit=ft.BoxFit.COVER, + gapless_playback=True, + ) + image_without_gapless = ft.Image( + src=initial_src, + width=340, + height=220, + fit=ft.BoxFit.COVER, + gapless_playback=False, + ) + + def image_panel(title: str, image: ft.Image): + return ft.Column( + spacing=8, + width=340, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text(title, weight=ft.FontWeight.BOLD), + ft.Container( + content=image, + bgcolor=ft.Colors.BLUE_GREY_900, + border_radius=ft.BorderRadius.all(12), + clip_behavior=ft.ClipBehavior.ANTI_ALIAS, + ), + ], + ) + + def next_photo(e: ft.Event[ft.Button]): + new_src = get_new_image_src() + image_with_gapless.src = new_src + image_without_gapless.src = new_src + image_with_gapless.update() + image_without_gapless.update() + + page.appbar = ft.AppBar(title="Gapless Playback Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Click 'Load next photo' to switch both images to a new URL.\n" + "The left/top image keeps showing the previous frame while " + "loading " + "the next one. This is referred to as gapless playback.", + text_align=ft.TextAlign.CENTER, + ), + ft.Row( + wrap=True, + spacing=20, + run_spacing=20, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + image_panel("gapless_playback=True", image_with_gapless), + image_panel( + "gapless_playback=False", + image_without_gapless, + ), + ], + ), + ft.FilledButton("Load next photo", on_click=next_photo), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/image/gapless_playback/pyproject.toml b/sdk/python/examples/controls/image/gapless_playback/pyproject.toml new file mode 100644 index 0000000000..c2b1ef33b8 --- /dev/null +++ b/sdk/python/examples/controls/image/gapless_playback/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "image-gapless-playback" +version = "1.0.0" +description = "Compares Image gapless playback enabled vs disabled while switching sources." +requires-python = ">=3.10" +keywords = ["image", "gapless playback", "comparison", "network images"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Image"] + +[tool.flet.metadata] +title = "Gapless playback when changing image sources" +controls = ["SafeArea", "Column", "Row", "Container", "Text", "Image", "FilledButton", "AppBar"] +layout_pattern = "comparison" +complexity = "basic" +features = ["gapless playback comparison", "dynamic image source switching"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/image/lucide_icons.py b/sdk/python/examples/controls/image/lucide_icons.py deleted file mode 100644 index 0301648aa6..0000000000 --- a/sdk/python/examples/controls/image/lucide_icons.py +++ /dev/null @@ -1,22 +0,0 @@ -import flet as ft - -""" -- Browse Lucide icons: https://lucide.dev/ -- Copy SVG and use it as value for `Image.src` -""" - - -def main(page: ft.Page): - page.add( - ft.Image( - src='', - color=ft.Colors.PINK, - ), - ft.Image( - src='', - color=ft.Colors.GREEN, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/image/lucide_icons/main.py b/sdk/python/examples/controls/image/lucide_icons/main.py new file mode 100644 index 0000000000..b21abe75e2 --- /dev/null +++ b/sdk/python/examples/controls/image/lucide_icons/main.py @@ -0,0 +1,29 @@ +import flet as ft + +""" +- Browse Lucide icons: https://lucide.dev/ +- Copy SVG and use it as value for `Image.src` +""" + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Row( + controls=[ + ft.Image( + src='', # noqa: E501 + color=ft.Colors.PINK, + ), + ft.Image( + src='', # noqa: E501 + color=ft.Colors.GREEN, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/image/lucide_icons/pyproject.toml b/sdk/python/examples/controls/image/lucide_icons/pyproject.toml new file mode 100644 index 0000000000..70fce26f51 --- /dev/null +++ b/sdk/python/examples/controls/image/lucide_icons/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "image-lucide-icons" +version = "1.0.0" +description = "Shows Lucide SVG icons rendered through Image with custom colors." +requires-python = ">=3.10" +keywords = ["image", "lucide", "svg icons", "inline svg"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Image"] + +[tool.flet.metadata] +title = "Displaying a Lucide icon" +controls = ["SafeArea", "Row", "Image"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["inline svg icons", "icon color styling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/image/src_base64_and_bytes.py b/sdk/python/examples/controls/image/src_base64_and_bytes.py deleted file mode 100644 index 8d3895505b..0000000000 --- a/sdk/python/examples/controls/image/src_base64_and_bytes.py +++ /dev/null @@ -1,26 +0,0 @@ -import base64 - -import flet as ft - - -def main(page: ft.Page): - # image as base64 string - base64_src = "iVBORw0KGgoAAAANSUhEUgAAABkAAAAgCAYAAADnnNMGAAAACXBIWXMAAAORAAADkQFnq8zdAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA6dJREFUSImllltoHFUYx3/fzOzm0lt23ZrQ1AQbtBehNpvQohgkBYVo410RwQctNE3Sh0IfiiBoIAjqi6TYrKnFy4O3oiiRavDJFi3mXomIBmOxNZe63ay52GR3Zj4f2sTEzmx3m//TYf7/c35zvgPnO6KqrESXqpq3muocAikv6m+/zytj3ejik1VN21G31YA9CgJ6xC+bMyQZPVCuarciPAMYC99V6Vw5pLbFSibHmlVoRVj9P3cmPBM8tSJI/M6mzabpfoAQ9fIF7WK4bd5vvuFnLGgy2vi0abg94A0AcJGvMq3hDxGRyar9r4F+iLAm0yIiRk8m37tctS1WsrIhhrI30+Srmg+J87OXUf3lWGS1q89dC6ltsSanxk4Aj2QBABii96300g87P/rtlrWr8l+vyDMfdlXSyyEikqxsiOUAQJCBhfHdXRfCq1LSsSlcWG+KBAGStvvrMkgiuv8lUc2mREukPwLUfHG+uTQv8Eown7VL3XlbBxYhf1c17hbVF3MDwA9bts280TnaU1YYqPby07aeFlUlHt27wSQ4CLo+F8AvoTCvHmyKF+ZbEb/M77P2LgvAwmrTHAHflN3KZxVbMC2jMFNOpgPnrMSOhvvFkMezXdwV4ePbtvHtxnJAMQ0j4JtVnO+eLb5oiSlt5HDbv7t1O90lpYCCCKbhfzW5kAIwUAazR0BlfII8Ow0I6uoVmI9MyAMwbMs8CExmDbk4zgu931MyO4OI4KrYflkRjOoTI+uM9d1vjotwKPu9QMk/sxzuO8POiVFcdZ1M2YBVsMEAKOqLvaPIe7mACuw0z/80SMH58SMplxlfiDhVi7dw2pltRhjKBQTQdrSja2KKTfE551NHuaZ0QVPvWYQUn31/Vm2nDvgjF4grVJx6suSvrvrSJ/6cSW2Oz9mf264uNrB806xZ1k/CZ49dUKgDEtlCROX2hfHpx8pGuuo3PpqYulw8fjndOp1yhgtNKRevJ1FyR2Ola+jXAjdnwTkZ6o896GdWdxDw7IxFg+0DpmXchTKSBWQnIuJn9u4j7dt+13UfHXEkXQOcuQ4kMhVtqsgUyPiQiPQfHw1NB2sRjmXKuTg1NwwBYLhtPtQX26eqTwGXPDOqvmcC4Hnwfrrad94GrVsOYTqUTkQY+iTlNe/6O1miSP/x0VB/+wMIDwHn/vtV1iQC4Xv95uUEWVCoL9Y5Z+gdovoyMHUFJHv88jmVy0vTuw7cZNv2YaA61Bfb7ZX5F8SaUv2xwZevAAAAAElFTkSuQmCC" - # image as bytes - bytes_src = base64.b64decode(base64_src) - - page.add( - ft.Image( - src=base64_src, - width=100, - height=100, - ), - ft.Image( - src=bytes_src, - width=100, - height=100, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/image/src_base64_and_bytes/main.py b/sdk/python/examples/controls/image/src_base64_and_bytes/main.py new file mode 100644 index 0000000000..be4ab3b798 --- /dev/null +++ b/sdk/python/examples/controls/image/src_base64_and_bytes/main.py @@ -0,0 +1,33 @@ +import base64 + +import flet as ft + + +def main(page: ft.Page): + # image as base64 string + base64_src = "iVBORw0KGgoAAAANSUhEUgAAABkAAAAgCAYAAADnnNMGAAAACXBIWXMAAAORAAADkQFnq8zdAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA6dJREFUSImllltoHFUYx3/fzOzm0lt23ZrQ1AQbtBehNpvQohgkBYVo410RwQctNE3Sh0IfiiBoIAjqi6TYrKnFy4O3oiiRavDJFi3mXomIBmOxNZe63ay52GR3Zj4f2sTEzmx3m//TYf7/c35zvgPnO6KqrESXqpq3muocAikv6m+/zytj3ejik1VN21G31YA9CgJ6xC+bMyQZPVCuarciPAMYC99V6Vw5pLbFSibHmlVoRVj9P3cmPBM8tSJI/M6mzabpfoAQ9fIF7WK4bd5vvuFnLGgy2vi0abg94A0AcJGvMq3hDxGRyar9r4F+iLAm0yIiRk8m37tctS1WsrIhhrI30+Srmg+J87OXUf3lWGS1q89dC6ltsSanxk4Aj2QBABii96300g87P/rtlrWr8l+vyDMfdlXSyyEikqxsiOUAQJCBhfHdXRfCq1LSsSlcWG+KBAGStvvrMkgiuv8lUc2mREukPwLUfHG+uTQv8Eown7VL3XlbBxYhf1c17hbVF3MDwA9bts280TnaU1YYqPby07aeFlUlHt27wSQ4CLo+F8AvoTCvHmyKF+ZbEb/M77P2LgvAwmrTHAHflN3KZxVbMC2jMFNOpgPnrMSOhvvFkMezXdwV4ePbtvHtxnJAMQ0j4JtVnO+eLb5oiSlt5HDbv7t1O90lpYCCCKbhfzW5kAIwUAazR0BlfII8Ow0I6uoVmI9MyAMwbMs8CExmDbk4zgu931MyO4OI4KrYflkRjOoTI+uM9d1vjotwKPu9QMk/sxzuO8POiVFcdZ1M2YBVsMEAKOqLvaPIe7mACuw0z/80SMH58SMplxlfiDhVi7dw2pltRhjKBQTQdrSja2KKTfE551NHuaZ0QVPvWYQUn31/Vm2nDvgjF4grVJx6suSvrvrSJ/6cSW2Oz9mf264uNrB806xZ1k/CZ49dUKgDEtlCROX2hfHpx8pGuuo3PpqYulw8fjndOp1yhgtNKRevJ1FyR2Ola+jXAjdnwTkZ6o896GdWdxDw7IxFg+0DpmXchTKSBWQnIuJn9u4j7dt+13UfHXEkXQOcuQ4kMhVtqsgUyPiQiPQfHw1NB2sRjmXKuTg1NwwBYLhtPtQX26eqTwGXPDOqvmcC4Hnwfrrad94GrVsOYTqUTkQY+iTlNe/6O1miSP/x0VB/+wMIDwHn/vtV1iQC4Xv95uUEWVCoL9Y5Z+gdovoyMHUFJHv88jmVy0vTuw7cZNv2YaA61Bfb7ZX5F8SaUv2xwZevAAAAAElFTkSuQmCC" # noqa: E501 + # image as bytes + bytes_src = base64.b64decode(base64_src) + + page.add( + ft.SafeArea( + content=ft.Row( + controls=[ + ft.Image( + src=base64_src, + width=100, + height=100, + ), + ft.Image( + src=bytes_src, + width=100, + height=100, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/image/src_base64_and_bytes/pyproject.toml b/sdk/python/examples/controls/image/src_base64_and_bytes/pyproject.toml new file mode 100644 index 0000000000..accbac2bbd --- /dev/null +++ b/sdk/python/examples/controls/image/src_base64_and_bytes/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "image-src-base64-and-bytes" +version = "1.0.0" +description = "Renders images from base64 string and decoded byte data sources." +requires-python = ">=3.10" +keywords = ["image", "base64", "bytes", "binary data"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Image"] + +[tool.flet.metadata] +title = "Displaying images from base64 strings and byte data" +controls = ["SafeArea", "Row", "Image"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["base64 image source", "bytes image source"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/image/static_svg.py b/sdk/python/examples/controls/image/static_svg.py deleted file mode 100644 index fd2e3b5ae1..0000000000 --- a/sdk/python/examples/controls/image/static_svg.py +++ /dev/null @@ -1,58 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - svg_image = """ - - - - - - -""" - - page.add( - ft.Image( - src="https://raw.githubusercontent.com/dnfield/flutter_svg/master/packages/flutter_svg/example/assets/wikimedia/Firefox_Logo_2017.svg", - width=200, - height=200, - ), - ft.Image(src=svg_image, width=100, height=100, color=ft.Colors.RED), - ft.Image(src=svg_image, width=100, height=100, color=ft.Colors.BLUE), - ft.Container( - bgcolor=ft.Colors.BLACK_87, - border_radius=5, - content=ft.Image( - src=svg_image, - width=100, - height=100, - color=ft.Colors.WHITE, - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/image/static_svg/main.py b/sdk/python/examples/controls/image/static_svg/main.py new file mode 100644 index 0000000000..c8cca29797 --- /dev/null +++ b/sdk/python/examples/controls/image/static_svg/main.py @@ -0,0 +1,75 @@ +import flet as ft + + +def main(page: ft.Page): + svg_image = """ + + + + + + +""" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Image( + src="https://raw.githubusercontent.com/dnfield/flutter_svg/master/packages/flutter_svg/example/assets/wikimedia/Firefox_Logo_2017.svg", + width=200, + height=200, + ), + ft.Image( + src=svg_image, + width=100, + height=100, + color=ft.Colors.RED, + ), + ft.Image( + src=svg_image, + width=100, + height=100, + color=ft.Colors.BLUE, + ), + ft.Container( + bgcolor=ft.Colors.BLACK_87, + border_radius=5, + content=ft.Image( + src=svg_image, + width=100, + height=100, + color=ft.Colors.WHITE, + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/image/static_svg/pyproject.toml b/sdk/python/examples/controls/image/static_svg/pyproject.toml new file mode 100644 index 0000000000..ca8cd772f6 --- /dev/null +++ b/sdk/python/examples/controls/image/static_svg/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "image-static-svg" +version = "1.0.0" +description = "Displays static SVG images from network and inline SVG strings with color overrides." +requires-python = ">=3.10" +keywords = ["image", "svg", "static svg", "inline svg", "color"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Image"] + +[tool.flet.metadata] +title = "Displaying a static SVG image" +controls = ["SafeArea", "Column", "Image", "Container"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["network svg", "inline svg", "svg color tinting"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/interactive_viewer/handling_events.py b/sdk/python/examples/controls/interactive_viewer/handling_events.py deleted file mode 100644 index 0aa1c3b8fe..0000000000 --- a/sdk/python/examples/controls/interactive_viewer/handling_events.py +++ /dev/null @@ -1,20 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.InteractiveViewer( - min_scale=0.1, - max_scale=15, - boundary_margin=ft.Margin.all(20), - on_interaction_start=lambda e: print(e), - on_interaction_end=lambda e: print(e), - on_interaction_update=lambda e: print(e), - content=ft.Image( - src="https://picsum.photos/500/500", - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/interactive_viewer/handling_events/main.py b/sdk/python/examples/controls/interactive_viewer/handling_events/main.py new file mode 100644 index 0000000000..7acd572406 --- /dev/null +++ b/sdk/python/examples/controls/interactive_viewer/handling_events/main.py @@ -0,0 +1,21 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.InteractiveViewer( + min_scale=0.1, + max_scale=15, + boundary_margin=ft.Margin.all(20), + on_interaction_start=lambda e: print(e), + on_interaction_end=lambda e: print(e), + on_interaction_update=lambda e: print(e), + content=ft.Image(src="https://picsum.photos/500/500"), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/interactive_viewer/handling_events/pyproject.toml b/sdk/python/examples/controls/interactive_viewer/handling_events/pyproject.toml new file mode 100644 index 0000000000..6ea1bbbeb7 --- /dev/null +++ b/sdk/python/examples/controls/interactive_viewer/handling_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "interactive-viewer-handling-events" +version = "1.0.0" +description = "Logs start, update, and end callbacks while panning and zooming an InteractiveViewer image." +requires-python = ">=3.10" +keywords = ["interactive viewer", "events", "zoom", "pan", "image"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/InteractiveViewer"] + +[tool.flet.metadata] +title = "Handling events" +controls = ["SafeArea", "InteractiveViewer", "Image"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["interaction event callbacks", "pinch zoom", "panning"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/interactive_viewer/transformations.py b/sdk/python/examples/controls/interactive_viewer/transformations.py deleted file mode 100644 index 4ffb2d71fc..0000000000 --- a/sdk/python/examples/controls/interactive_viewer/transformations.py +++ /dev/null @@ -1,51 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - async def handle_zoom_in(e: ft.Event[ft.Button]): - await i.zoom(1.2) - - async def handle_zoom_out(e: ft.Event[ft.Button]): - await i.zoom(0.8) - - async def handle_pan(e: ft.Event[ft.Button]): - await i.pan(dx=50, dy=50) - - async def handle_reset(e: ft.Event[ft.Button]): - await i.reset() - - async def handle_reset_slow(e: ft.Event[ft.Button]): - await i.reset(animation_duration=ft.Duration(seconds=2)) - - async def handle_save_state(e: ft.Event[ft.Button]): - await i.save_state() - - async def handle_restore_state(e: ft.Event[ft.Button]): - await i.restore_state() - - page.add( - i := ft.InteractiveViewer( - min_scale=0.1, - max_scale=5, - boundary_margin=ft.Margin.all(20), - content=ft.Image(src="https://picsum.photos/500/500"), - ), - ft.Row( - wrap=True, - controls=[ - ft.Button("Zoom In", on_click=handle_zoom_in), - ft.Button("Zoom Out", on_click=handle_zoom_out), - ft.Button("Pan", on_click=handle_pan), - ft.Button("Save State", on_click=handle_save_state), - ft.Button("Restore State", on_click=handle_restore_state), - ft.Button("Reset (instant)", on_click=handle_reset), - ft.Button("Reset (slow)", on_click=handle_reset_slow), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/interactive_viewer/transformations/main.py b/sdk/python/examples/controls/interactive_viewer/transformations/main.py new file mode 100644 index 0000000000..70366436d0 --- /dev/null +++ b/sdk/python/examples/controls/interactive_viewer/transformations/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + async def handle_zoom_in(e: ft.Event[ft.Button]): + await i.zoom(1.2) + + async def handle_zoom_out(e: ft.Event[ft.Button]): + await i.zoom(0.8) + + async def handle_pan(e: ft.Event[ft.Button]): + await i.pan(dx=50, dy=50) + + async def handle_reset(e: ft.Event[ft.Button]): + await i.reset() + + async def handle_reset_slow(e: ft.Event[ft.Button]): + await i.reset(animation_duration=ft.Duration(seconds=2)) + + async def handle_save_state(e: ft.Event[ft.Button]): + await i.save_state() + + async def handle_restore_state(e: ft.Event[ft.Button]): + await i.restore_state() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + i := ft.InteractiveViewer( + min_scale=0.1, + max_scale=5, + boundary_margin=ft.Margin.all(20), + content=ft.Image(src="https://picsum.photos/500/500"), + ), + ft.Row( + wrap=True, + controls=[ + ft.Button("Zoom In", on_click=handle_zoom_in), + ft.Button("Zoom Out", on_click=handle_zoom_out), + ft.Button("Pan", on_click=handle_pan), + ft.Button("Save State", on_click=handle_save_state), + ft.Button("Restore State", on_click=handle_restore_state), + ft.Button("Reset (instant)", on_click=handle_reset), + ft.Button("Reset (slow)", on_click=handle_reset_slow), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/interactive_viewer/transformations/pyproject.toml b/sdk/python/examples/controls/interactive_viewer/transformations/pyproject.toml new file mode 100644 index 0000000000..6c02a8375f --- /dev/null +++ b/sdk/python/examples/controls/interactive_viewer/transformations/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "interactive-viewer-transformations" +version = "1.0.0" +description = "Applies InteractiveViewer programmatic zoom, pan, save/restore state, and reset actions." +requires-python = ">=3.10" +keywords = ["interactive viewer", "transformations", "zoom", "pan", "state"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/InteractiveViewer"] + +[tool.flet.metadata] +title = "Programmatic transformations" +controls = ["SafeArea", "Column", "Row", "InteractiveViewer", "Image", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["programmatic zoom", "programmatic pan", "save and restore state", "animated reset"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/keyboard_listener/detect_keys.py b/sdk/python/examples/controls/keyboard_listener/detect_keys.py deleted file mode 100644 index 1a2655b151..0000000000 --- a/sdk/python/examples/controls/keyboard_listener/detect_keys.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - pressed_keys = set() - - def key_down(e: ft.KeyDownEvent): - pressed_keys.add(e.key) - keys.controls = [ft.Text(k, size=20) for k in pressed_keys] - - def key_up(e: ft.KeyUpEvent): - pressed_keys.remove(e.key) - keys.controls = [ft.Text(k, size=20) for k in pressed_keys] - - page.add( - ft.Text("Press any keys..."), - ft.KeyboardListener( - content=(keys := ft.Row()), - autofocus=True, - on_key_down=key_down, - on_key_up=key_up, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/keyboard_listener/detect_keys/main.py b/sdk/python/examples/controls/keyboard_listener/detect_keys/main.py new file mode 100644 index 0000000000..c251f16bbc --- /dev/null +++ b/sdk/python/examples/controls/keyboard_listener/detect_keys/main.py @@ -0,0 +1,35 @@ +import flet as ft + + +async def main(page: ft.Page): + pressed_keys = set() + + def key_down(e: ft.KeyDownEvent): + pressed_keys.add(e.key) + keys.controls = [ft.Text(k, size=20) for k in pressed_keys] + keys.update() + + def key_up(e: ft.KeyUpEvent): + pressed_keys.remove(e.key) + keys.controls = [ft.Text(k, size=20) for k in pressed_keys] + keys.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Press any keys..."), + ft.KeyboardListener( + autofocus=True, + on_key_down=key_down, + on_key_up=key_up, + content=(keys := ft.Row()), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/keyboard_listener/detect_keys/pyproject.toml b/sdk/python/examples/controls/keyboard_listener/detect_keys/pyproject.toml new file mode 100644 index 0000000000..ef8e6d6f7d --- /dev/null +++ b/sdk/python/examples/controls/keyboard_listener/detect_keys/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "keyboard-listener-detect-keys" +version = "1.0.0" +description = "Tracks pressed keys with KeyboardListener and shows active keys in real time." +requires-python = ">=3.10" +keywords = ["keyboard listener", "keyboard", "key down", "key up", "events"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/KeyboardListener"] + +[tool.flet.metadata] +title = "Press any keys" +controls = ["SafeArea", "Column", "KeyboardListener", "Row", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["key down and key up handling", "active key list", "keyboard focus"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/animate_align.py b/sdk/python/examples/controls/layout_control/animate_align.py deleted file mode 100644 index 36cb8d9fa5..0000000000 --- a/sdk/python/examples/controls/layout_control/animate_align.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate_click(): - txt.align = ft.Alignment.TOP_LEFT - - page.add( - ft.Stack( - width=200, - height=200, - controls=[ - ( - txt := ft.Text( - "Hello, Flet!", - align=ft.Alignment.BOTTOM_RIGHT, - animate_align=1000, - ) - ) - ], - ), - ft.Button("Animate align", on_click=animate_click), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_align/main.py b/sdk/python/examples/controls/layout_control/animate_align/main.py new file mode 100644 index 0000000000..6a79ab3c94 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_align/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +def main(page: ft.Page): + def animate_click(e: ft.Event[ft.Button]): + txt.align = ft.Alignment.TOP_LEFT + txt.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Stack( + width=200, + height=200, + controls=[ + txt := ft.Text( + "Hello, Flet!", + align=ft.Alignment.BOTTOM_RIGHT, + animate_align=1000, + ) + ], + ), + ft.Button("Animate align", on_click=animate_click), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_align/pyproject.toml b/sdk/python/examples/controls/layout_control/animate_align/pyproject.toml new file mode 100644 index 0000000000..5a719c7158 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_align/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-animate-align" +version = "1.0.0" +description = "Animates Text alignment within a Stack using animate_align." +requires-python = ">=3.10" +keywords = ["layout control", "animate align", "text", "stack"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Animate align" +controls = ["SafeArea", "Column", "Stack", "Text", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["alignment animation", "button-triggered transition"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/animate_margin.py b/sdk/python/examples/controls/layout_control/animate_margin.py deleted file mode 100644 index 28d1e34f71..0000000000 --- a/sdk/python/examples/controls/layout_control/animate_margin.py +++ /dev/null @@ -1,28 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate_click(): - c.margin = 0 - txt.margin = ft.Margin.only(left=50, top=70) - - page.add( - c := ft.Container( - width=200, - height=200, - bgcolor=ft.Colors.AMBER, - margin=40, - animate=True, - content=( - txt := ft.Text( - "Hello, Flet!", - margin=20, - animate_margin=1000, - ) - ), - ), - ft.Button("Animate margin", on_click=animate_click), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_margin/main.py b/sdk/python/examples/controls/layout_control/animate_margin/main.py new file mode 100644 index 0000000000..db328a0c9b --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_margin/main.py @@ -0,0 +1,37 @@ +import flet as ft + + +def main(page: ft.Page): + def animate_click(e: ft.Event[ft.Button]): + c.margin = 0 + txt.margin = ft.Margin.only(left=50, top=70) + c.update() + txt.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + c := ft.Container( + width=200, + height=200, + bgcolor=ft.Colors.AMBER, + margin=40, + animate=True, + content=( + txt := ft.Text( + "Hello, Flet!", + margin=20, + animate_margin=1000, + ) + ), + ), + ft.Button("Animate margin", on_click=animate_click), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_margin/pyproject.toml b/sdk/python/examples/controls/layout_control/animate_margin/pyproject.toml new file mode 100644 index 0000000000..18b08944a6 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_margin/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-animate-margin" +version = "1.0.0" +description = "Animates container and text margins to show layout spacing transitions." +requires-python = ">=3.10" +keywords = ["layout control", "animate margin", "container", "text"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Animate margin" +controls = ["SafeArea", "Column", "Container", "Text", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["margin animation", "container animation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/animate_offset.py b/sdk/python/examples/controls/layout_control/animate_offset.py deleted file mode 100644 index 0d9e6b5bfe..0000000000 --- a/sdk/python/examples/controls/layout_control/animate_offset.py +++ /dev/null @@ -1,25 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate(e: ft.Event[ft.Button]): - container.offset = ft.Offset(0, 0) - container.update() - - page.add( - container := ft.Container( - width=150, - height=150, - bgcolor=ft.Colors.BLUE, - border_radius=ft.BorderRadius.all(10), - offset=ft.Offset(x=-1.1, y=0), - animate_offset=ft.Animation( - duration=600, - curve=ft.AnimationCurve.BOUNCE_OUT, - ), - ), - ft.Button("Reveal!", on_click=animate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_offset/main.py b/sdk/python/examples/controls/layout_control/animate_offset/main.py new file mode 100644 index 0000000000..e66a89701b --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_offset/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +def main(page: ft.Page): + def animate(e: ft.Event[ft.Button]): + container.offset = ft.Offset(0, 0) + container.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + container := ft.Container( + width=150, + height=150, + bgcolor=ft.Colors.BLUE, + border_radius=ft.BorderRadius.all(10), + offset=ft.Offset(x=-1.1, y=0), + animate_offset=ft.Animation( + duration=600, + curve=ft.AnimationCurve.BOUNCE_OUT, + ), + ), + ft.Button("Reveal!", on_click=animate), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_offset/pyproject.toml b/sdk/python/examples/controls/layout_control/animate_offset/pyproject.toml new file mode 100644 index 0000000000..6937d6d1aa --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_offset/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-animate-offset" +version = "1.0.0" +description = "Reveals a container by animating its offset from off-screen to visible." +requires-python = ">=3.10" +keywords = ["layout control", "animate offset", "container", "animation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Animate offset" +controls = ["SafeArea", "Column", "Container", "Button", "Animation"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["offset animation", "slide-in reveal"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/animate_opacity.py b/sdk/python/examples/controls/layout_control/animate_opacity.py deleted file mode 100644 index 9f1f6492f9..0000000000 --- a/sdk/python/examples/controls/layout_control/animate_opacity.py +++ /dev/null @@ -1,24 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate_opacity(e: ft.Event[ft.Button]): - container.opacity = 0 if container.opacity == 1 else 1 - container.update() - - page.add( - container := ft.Container( - width=150, - height=150, - bgcolor=ft.Colors.BLUE, - border_radius=10, - animate_opacity=300, - ), - ft.Button( - content="Animate opacity", - on_click=animate_opacity, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_opacity/main.py b/sdk/python/examples/controls/layout_control/animate_opacity/main.py new file mode 100644 index 0000000000..b5e28999f8 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_opacity/main.py @@ -0,0 +1,31 @@ +import flet as ft + + +def main(page: ft.Page): + def animate_opacity(e: ft.Event[ft.Button]): + container.opacity = 0 if container.opacity == 1 else 1 + container.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + container := ft.Container( + width=150, + height=150, + bgcolor=ft.Colors.BLUE, + border_radius=10, + animate_opacity=300, + ), + ft.Button( + content="Animate opacity", + on_click=animate_opacity, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_opacity/pyproject.toml b/sdk/python/examples/controls/layout_control/animate_opacity/pyproject.toml new file mode 100644 index 0000000000..e573462b70 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_opacity/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-animate-opacity" +version = "1.0.0" +description = "Toggles a container between visible and transparent states with animate_opacity." +requires-python = ">=3.10" +keywords = ["layout control", "animate opacity", "container", "fade"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Animate opacity" +controls = ["SafeArea", "Column", "Container", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["opacity animation", "fade toggle"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/animate_position.py b/sdk/python/examples/controls/layout_control/animate_position.py deleted file mode 100644 index 3cdfee2cca..0000000000 --- a/sdk/python/examples/controls/layout_control/animate_position.py +++ /dev/null @@ -1,43 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate_container(e: ft.Event[ft.Button]): - c1.top = 20 - c1.left = 200 - c2.top = 100 - c2.left = 40 - c3.top = 180 - c3.left = 100 - page.update() - - page.add( - ft.Stack( - height=400, - controls=[ - c1 := ft.Container( - width=50, height=50, bgcolor="red", animate_position=1000 - ), - c2 := ft.Container( - width=50, - height=50, - bgcolor="green", - top=60, - left=0, - animate_position=500, - ), - c3 := ft.Container( - width=50, - height=50, - bgcolor="blue", - top=120, - left=0, - animate_position=1000, - ), - ], - ), - ft.Button("Animate!", on_click=animate_container), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_position/main.py b/sdk/python/examples/controls/layout_control/animate_position/main.py new file mode 100644 index 0000000000..56474d8504 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_position/main.py @@ -0,0 +1,53 @@ +import flet as ft + + +def main(page: ft.Page): + def animate_container(e: ft.Event[ft.Button]): + c1.top = 20 + c1.left = 200 + c2.top = 100 + c2.left = 40 + c3.top = 180 + c3.left = 100 + stack.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + stack := ft.Stack( + height=400, + controls=[ + c1 := ft.Container( + width=50, + height=50, + bgcolor="red", + animate_position=1000, + ), + c2 := ft.Container( + width=50, + height=50, + bgcolor="green", + top=60, + left=0, + animate_position=500, + ), + c3 := ft.Container( + width=50, + height=50, + bgcolor="blue", + top=120, + left=0, + animate_position=1000, + ), + ], + ), + ft.Button("Animate!", on_click=animate_container), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_position/pyproject.toml b/sdk/python/examples/controls/layout_control/animate_position/pyproject.toml new file mode 100644 index 0000000000..b393a63ec4 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_position/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-animate-position" +version = "1.0.0" +description = "Animates top/left positions of multiple containers inside a Stack." +requires-python = ">=3.10" +keywords = ["layout control", "animate position", "stack", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Animate position" +controls = ["SafeArea", "Column", "Stack", "Container", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["position animation", "multi-control movement"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/animate_rotation.py b/sdk/python/examples/controls/layout_control/animate_rotation.py deleted file mode 100644 index ef7921cb04..0000000000 --- a/sdk/python/examples/controls/layout_control/animate_rotation.py +++ /dev/null @@ -1,30 +0,0 @@ -from math import pi - -import flet as ft - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.spacing = 30 - - def animate(e: ft.Event[ft.Button]): - container.rotate.angle += pi / 2 - page.update() - - page.add( - container := ft.Container( - width=100, - height=70, - bgcolor=ft.Colors.BLUE, - border_radius=5, - rotate=ft.Rotate(angle=0, alignment=ft.Alignment.CENTER), - animate_rotation=ft.Animation( - duration=300, curve=ft.AnimationCurve.BOUNCE_OUT - ), - ), - ft.Button("Animate!", on_click=animate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_rotation/main.py b/sdk/python/examples/controls/layout_control/animate_rotation/main.py new file mode 100644 index 0000000000..ac3adda8ce --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_rotation/main.py @@ -0,0 +1,39 @@ +from math import pi + +import flet as ft + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.spacing = 30 + + def animate(e: ft.Event[ft.Button]): + container.rotate.angle += pi / 2 + container.update() + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + container := ft.Container( + width=100, + height=70, + bgcolor=ft.Colors.BLUE, + border_radius=5, + rotate=ft.Rotate(angle=0, alignment=ft.Alignment.CENTER), + animate_rotation=ft.Animation( + duration=300, + curve=ft.AnimationCurve.BOUNCE_OUT, + ), + ), + ft.Button("Animate!", on_click=animate), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_rotation/pyproject.toml b/sdk/python/examples/controls/layout_control/animate_rotation/pyproject.toml new file mode 100644 index 0000000000..62256eecaa --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_rotation/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-animate-rotation" +version = "1.0.0" +description = "Rotates a container in quarter turns with animate_rotation and bounce curve." +requires-python = ">=3.10" +keywords = ["layout control", "animate rotation", "container", "animation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Animate rotation" +controls = ["SafeArea", "Column", "Container", "Button", "Rotate", "Animation"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["rotation animation", "incremental angle updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/animate_scale.py b/sdk/python/examples/controls/layout_control/animate_scale.py deleted file mode 100644 index 828c69ee8b..0000000000 --- a/sdk/python/examples/controls/layout_control/animate_scale.py +++ /dev/null @@ -1,29 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.spacing = 30 - - def animate(e: ft.Event[ft.Button]): - container.scale = 2 if container.scale == 1 else 1 - page.update() - - page.add( - container := ft.Container( - width=100, - height=100, - bgcolor=ft.Colors.BLUE, - border_radius=5, - scale=1, - animate_scale=ft.Animation( - duration=600, - curve=ft.AnimationCurve.BOUNCE_OUT, - ), - ), - ft.Button("Animate!", on_click=animate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_scale/main.py b/sdk/python/examples/controls/layout_control/animate_scale/main.py new file mode 100644 index 0000000000..abd4cfa7cd --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_scale/main.py @@ -0,0 +1,37 @@ +import flet as ft + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.spacing = 30 + + def animate(e: ft.Event[ft.Button]): + container.scale = 2 if container.scale == 1 else 1 + container.update() + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + container := ft.Container( + width=100, + height=100, + bgcolor=ft.Colors.BLUE, + border_radius=5, + scale=1, + animate_scale=ft.Animation( + duration=600, + curve=ft.AnimationCurve.BOUNCE_OUT, + ), + ), + ft.Button("Animate!", on_click=animate), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/animate_scale/pyproject.toml b/sdk/python/examples/controls/layout_control/animate_scale/pyproject.toml new file mode 100644 index 0000000000..a95f5e19aa --- /dev/null +++ b/sdk/python/examples/controls/layout_control/animate_scale/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-animate-scale" +version = "1.0.0" +description = "Animates container scale between normal and enlarged sizes." +requires-python = ">=3.10" +keywords = ["layout control", "animate scale", "container", "animation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Animate scale" +controls = ["SafeArea", "Column", "Container", "Button", "Animation"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["scale animation", "toggle scaling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/bursting_flet.py b/sdk/python/examples/controls/layout_control/bursting_flet.py deleted file mode 100644 index 19cb334506..0000000000 --- a/sdk/python/examples/controls/layout_control/bursting_flet.py +++ /dev/null @@ -1,32 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def animate(e: ft.Event[ft.Button]): - image.scale = 30 - image.opacity = 0 - image.update() - - page.add( - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - image := ft.Image( - src="icon-192.png", - width=100, - height=100, - scale=1.0, - animate_scale=ft.Animation(300, ft.AnimationCurve.EASE_IN_QUINT), - opacity=1.0, - animate_opacity=ft.Animation(300, ft.AnimationCurve.EASE_IN_QUINT), - ), - ft.Button("Boom!", on_click=animate), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/bursting_flet/assets/icon-192.png b/sdk/python/examples/controls/layout_control/bursting_flet/assets/icon-192.png new file mode 100644 index 0000000000..65da3b9571 Binary files /dev/null and b/sdk/python/examples/controls/layout_control/bursting_flet/assets/icon-192.png differ diff --git a/sdk/python/examples/controls/layout_control/bursting_flet/main.py b/sdk/python/examples/controls/layout_control/bursting_flet/main.py new file mode 100644 index 0000000000..9106c60f9e --- /dev/null +++ b/sdk/python/examples/controls/layout_control/bursting_flet/main.py @@ -0,0 +1,41 @@ +import flet as ft + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def animate(e: ft.Event[ft.Button]): + image.scale = 30 + image.opacity = 0 + image.update() + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + image := ft.Image( + src="icon-192.png", + width=100, + height=100, + scale=1.0, + animate_scale=ft.Animation( + 300, + ft.AnimationCurve.EASE_IN_QUINT, + ), + opacity=1.0, + animate_opacity=ft.Animation( + 300, + ft.AnimationCurve.EASE_IN_QUINT, + ), + ), + ft.Button("Boom!", on_click=animate), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/bursting_flet/pyproject.toml b/sdk/python/examples/controls/layout_control/bursting_flet/pyproject.toml new file mode 100644 index 0000000000..49921d2d3c --- /dev/null +++ b/sdk/python/examples/controls/layout_control/bursting_flet/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-bursting-flet" +version = "1.0.0" +description = "Creates a burst effect by rapidly scaling and fading the Flet icon image." +requires-python = ">=3.10" +keywords = ["layout control", "image", "scale", "opacity", "animation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Bursting flet" +controls = ["SafeArea", "Column", "Image", "Button", "Animation"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["combined scale and opacity animations", "asset image animation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/flip.py b/sdk/python/examples/controls/layout_control/flip.py deleted file mode 100644 index 815706a062..0000000000 --- a/sdk/python/examples/controls/layout_control/flip.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.spacing = 20 - - def toggle_x(e: ft.Event[ft.Button]): - card.flip.flip_x = not card.flip.flip_x - page.update() - - def toggle_y(e: ft.Event[ft.Button]): - card.flip.flip_y = not card.flip.flip_y - page.update() - - page.add( - card := ft.Container( - width=220, - height=120, - bgcolor=ft.Colors.BLUE_300, - border_radius=16, - alignment=ft.Alignment.CENTER, - content=ft.Text("Flip me", size=24, weight=ft.FontWeight.BOLD), - flip=ft.Flip( - flip_x=False, - flip_y=False, - filter_quality=ft.FilterQuality.MEDIUM, - ), - ), - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Button("Toggle X", on_click=toggle_x), - ft.Button("Toggle Y", on_click=toggle_y), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/flip/main.py b/sdk/python/examples/controls/layout_control/flip/main.py new file mode 100644 index 0000000000..3abd87179e --- /dev/null +++ b/sdk/python/examples/controls/layout_control/flip/main.py @@ -0,0 +1,49 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.spacing = 20 + + def toggle_x(e: ft.Event[ft.Button]): + card.flip.flip_x = not card.flip.flip_x + card.update() + + def toggle_y(e: ft.Event[ft.Button]): + card.flip.flip_y = not card.flip.flip_y + card.update() + + page.add( + ft.SafeArea( + content=ft.Column( + spacing=20, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + card := ft.Container( + width=220, + height=120, + bgcolor=ft.Colors.BLUE_300, + border_radius=16, + alignment=ft.Alignment.CENTER, + content=ft.Text("Flip me", size=24, weight=ft.FontWeight.BOLD), + flip=ft.Flip( + flip_x=False, + flip_y=False, + filter_quality=ft.FilterQuality.MEDIUM, + ), + ), + ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Button("Toggle X", on_click=toggle_x), + ft.Button("Toggle Y", on_click=toggle_y), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/flip/pyproject.toml b/sdk/python/examples/controls/layout_control/flip/pyproject.toml new file mode 100644 index 0000000000..683de547da --- /dev/null +++ b/sdk/python/examples/controls/layout_control/flip/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-flip" +version = "1.0.0" +description = "Toggles flip_x and flip_y on a card using LayoutControl flip transform." +requires-python = ">=3.10" +keywords = ["layout control", "flip", "transform", "card"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Flip" +controls = ["SafeArea", "Column", "Container", "Text", "Flip", "Row", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["flip_x toggle", "flip_y toggle"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/image_slideshow.py b/sdk/python/examples/controls/layout_control/image_slideshow.py deleted file mode 100644 index 5f14ae33f3..0000000000 --- a/sdk/python/examples/controls/layout_control/image_slideshow.py +++ /dev/null @@ -1,37 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate(e: ft.Event[ft.Button]): - image1.left = 400 if image1.left == 0 else 0 - image2.left = 0 if image2.left == -400 else -400 - page.update() - - page.add( - ft.Stack( - width=200, - height=300, - controls=[ - image1 := ft.Image( - src="https://picsum.photos/200/300?1", - left=0, - animate_position=ft.Animation( - duration=300, - curve=ft.AnimationCurve.BOUNCE_OUT, - ), - ), - image2 := ft.Image( - src="https://picsum.photos/200/300?2", - left=-400, - animate_position=ft.Animation( - duration=300, - curve=ft.AnimationCurve.BOUNCE_OUT, - ), - ), - ], - ), - ft.Button("Slide!", on_click=animate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/image_slideshow/main.py b/sdk/python/examples/controls/layout_control/image_slideshow/main.py new file mode 100644 index 0000000000..c6f1d01d35 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/image_slideshow/main.py @@ -0,0 +1,44 @@ +import flet as ft + + +def main(page: ft.Page): + def animate(e: ft.Event[ft.Button]): + image1.left = 400 if image1.left == 0 else 0 + image2.left = 0 if image2.left == -400 else -400 + stack.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + stack := ft.Stack( + width=200, + height=300, + controls=[ + image1 := ft.Image( + src="https://picsum.photos/200/300?1", + left=0, + animate_position=ft.Animation( + duration=300, + curve=ft.AnimationCurve.BOUNCE_OUT, + ), + ), + image2 := ft.Image( + src="https://picsum.photos/200/300?2", + left=-400, + animate_position=ft.Animation( + duration=300, + curve=ft.AnimationCurve.BOUNCE_OUT, + ), + ), + ], + ), + ft.Button("Slide!", on_click=animate), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/image_slideshow/pyproject.toml b/sdk/python/examples/controls/layout_control/image_slideshow/pyproject.toml new file mode 100644 index 0000000000..459e6a669f --- /dev/null +++ b/sdk/python/examples/controls/layout_control/image_slideshow/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-image-slideshow" +version = "1.0.0" +description = "Slides two images horizontally using animated left position changes." +requires-python = ">=3.10" +keywords = ["layout control", "image slideshow", "animate position", "stack"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Image slideshow" +controls = ["SafeArea", "Column", "Stack", "Image", "Button", "Animation"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["sliding images", "position animation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/matrix4_transform.py b/sdk/python/examples/controls/layout_control/matrix4_transform.py deleted file mode 100644 index acaa6bf075..0000000000 --- a/sdk/python/examples/controls/layout_control/matrix4_transform.py +++ /dev/null @@ -1,81 +0,0 @@ -from math import pi - -import flet as ft - - -def card(title: str, color: str, matrix: ft.Matrix4) -> ft.Container: - return ft.Container( - width=220, - height=130, - border_radius=18, - bgcolor=color, - padding=12, - content=ft.Text(title, size=18, weight=ft.FontWeight.BOLD), - transform=ft.Transform( - matrix=matrix, - alignment=ft.Alignment.CENTER, - filter_quality=ft.FilterQuality.MEDIUM, - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.scroll = ft.ScrollMode.AUTO - page.spacing = 20 - - perspective_tilt = ( - ft.Matrix4.identity() - .set_entry(3, 2, 0.0018) - .rotate_x(-0.35) - .rotate_y(0.45) - .translate(0, -10, 0) - ) - - skew_and_rotate = ft.Matrix4.skew_y(0.28).rotate_z(-pi / 14) - - mirrored_spin = ft.Matrix4.diagonal3_values(-1, 1, 1).rotate_z(pi / 10) - - mix = ft.Matrix4.translation_values(24, -8, 0).multiply( - ft.Matrix4.rotation_z(pi / 16).scale(0.9, 0.9) - ) - - page.add( - ft.Text("Matrix4 transform recording + replay", size=24), - ft.ResponsiveRow( - controls=[ - ft.Container( - col={"sm": 6, "md": 3}, - content=card( - "Perspective tilt", - ft.Colors.CYAN_300, - perspective_tilt, - ), - ), - ft.Container( - col={"sm": 6, "md": 3}, - content=card( - "Skew + rotate", - ft.Colors.AMBER_300, - skew_and_rotate, - ), - ), - ft.Container( - col={"sm": 6, "md": 3}, - content=card( - "Mirror + spin", - ft.Colors.PINK_200, - mirrored_spin, - ), - ), - ft.Container( - col={"sm": 6, "md": 3}, - content=card("Multiply chain", ft.Colors.LIGHT_GREEN_300, mix), - ), - ] - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/matrix4_transform/main.py b/sdk/python/examples/controls/layout_control/matrix4_transform/main.py new file mode 100644 index 0000000000..1b72aad9ef --- /dev/null +++ b/sdk/python/examples/controls/layout_control/matrix4_transform/main.py @@ -0,0 +1,93 @@ +from math import pi + +import flet as ft + + +def card(title: str, color: str, matrix: ft.Matrix4) -> ft.Container: + return ft.Container( + width=220, + height=130, + border_radius=18, + bgcolor=color, + padding=12, + content=ft.Text(title, size=18, weight=ft.FontWeight.BOLD), + transform=ft.Transform( + matrix=matrix, + alignment=ft.Alignment.CENTER, + filter_quality=ft.FilterQuality.MEDIUM, + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.scroll = ft.ScrollMode.AUTO + page.spacing = 20 + + perspective_tilt = ( + ft.Matrix4.identity() + .set_entry(3, 2, 0.0018) + .rotate_x(-0.35) + .rotate_y(0.45) + .translate(0, -10, 0) + ) + + skew_and_rotate = ft.Matrix4.skew_y(0.28).rotate_z(-pi / 14) + + mirrored_spin = ft.Matrix4.diagonal3_values(-1, 1, 1).rotate_z(pi / 10) + + mix = ft.Matrix4.translation_values(24, -8, 0).multiply( + ft.Matrix4.rotation_z(pi / 16).scale(0.9, 0.9) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + spacing=20, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text("Matrix4 transform recording + replay", size=24), + ft.ResponsiveRow( + controls=[ + ft.Container( + col={"sm": 6, "md": 3}, + content=card( + "Perspective tilt", + ft.Colors.CYAN_300, + perspective_tilt, + ), + ), + ft.Container( + col={"sm": 6, "md": 3}, + content=card( + "Skew + rotate", + ft.Colors.AMBER_300, + skew_and_rotate, + ), + ), + ft.Container( + col={"sm": 6, "md": 3}, + content=card( + "Mirror + spin", + ft.Colors.PINK_200, + mirrored_spin, + ), + ), + ft.Container( + col={"sm": 6, "md": 3}, + content=card( + "Multiply chain", + ft.Colors.LIGHT_GREEN_300, + mix, + ), + ), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/matrix4_transform/pyproject.toml b/sdk/python/examples/controls/layout_control/matrix4_transform/pyproject.toml new file mode 100644 index 0000000000..2021b0a6e5 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/matrix4_transform/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-matrix4-transform" +version = "1.0.0" +description = "Demonstrates Matrix4 perspective, skew, mirror, and chained transforms on cards." +requires-python = ">=3.10" +keywords = ["layout control", "matrix4", "transform", "perspective", "skew"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Matrix4 Transform" +controls = ["SafeArea", "Column", "ResponsiveRow", "Container", "Text", "Transform", "Matrix4"] +layout_pattern = "comparison" +complexity = "basic" +features = ["perspective transform", "skew and rotation", "matrix multiply chain"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/offset.py b/sdk/python/examples/controls/layout_control/offset.py deleted file mode 100644 index 530d464658..0000000000 --- a/sdk/python/examples/controls/layout_control/offset.py +++ /dev/null @@ -1,65 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.add( - ft.Stack( - width=460, - height=260, - controls=[ - ft.Text( - "Offset translates by control size.", - left=12, - top=8, - size=16, - color=ft.Colors.ON_SURFACE_VARIANT, - ), - ft.Container( - left=30, - top=70, - width=170, - height=90, - border_radius=16, - bgcolor=ft.Colors.BLUE_100, - border=ft.Border.all(2, ft.Colors.BLUE_GREY_400), - alignment=ft.Alignment.CENTER, - content=ft.Text("Original", size=20, color=ft.Colors.BLUE_GREY_700), - ), - ft.Container( - left=30, - top=70, - width=170, - height=90, - border_radius=16, - bgcolor=ft.Colors.AMBER_300, - alignment=ft.Alignment.CENTER, - content=ft.Text("Offset", size=26, weight=ft.FontWeight.BOLD), - offset=ft.Offset( - x=1.05, - y=0.55, - filter_quality=ft.FilterQuality.MEDIUM, - ), - ), - ft.Icon( - ft.Icons.ARROW_RIGHT_ALT_ROUNDED, - left=212, - top=82, - size=44, - color=ft.Colors.BLUE_GREY_600, - ), - ft.Text( - "offset = Offset(1.05, 0.55)", - left=194, - top=222, - size=14, - color=ft.Colors.ON_SURFACE_VARIANT, - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/offset/main.py b/sdk/python/examples/controls/layout_control/offset/main.py new file mode 100644 index 0000000000..cfd44c59a9 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/offset/main.py @@ -0,0 +1,71 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.add( + ft.SafeArea( + content=ft.Stack( + width=460, + height=260, + controls=[ + ft.Text( + "Offset translates by control size.", + left=12, + top=8, + size=16, + color=ft.Colors.ON_SURFACE_VARIANT, + ), + ft.Container( + left=30, + top=70, + width=170, + height=90, + border_radius=16, + bgcolor=ft.Colors.BLUE_100, + border=ft.Border.all(2, ft.Colors.BLUE_GREY_400), + alignment=ft.Alignment.CENTER, + content=ft.Text( + "Original", + size=20, + color=ft.Colors.BLUE_GREY_700, + ), + ), + ft.Container( + left=30, + top=70, + width=170, + height=90, + border_radius=16, + bgcolor=ft.Colors.AMBER_300, + alignment=ft.Alignment.CENTER, + content=ft.Text("Offset", size=26, weight=ft.FontWeight.BOLD), + offset=ft.Offset( + x=1.05, + y=0.55, + filter_quality=ft.FilterQuality.MEDIUM, + ), + ), + ft.Icon( + ft.Icons.ARROW_RIGHT_ALT_ROUNDED, + left=212, + top=82, + size=44, + color=ft.Colors.BLUE_GREY_600, + ), + ft.Text( + "offset = Offset(1.05, 0.55)", + left=194, + top=222, + size=14, + color=ft.Colors.ON_SURFACE_VARIANT, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/offset/pyproject.toml b/sdk/python/examples/controls/layout_control/offset/pyproject.toml new file mode 100644 index 0000000000..e1d069ba1d --- /dev/null +++ b/sdk/python/examples/controls/layout_control/offset/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-offset" +version = "1.0.0" +description = "Visualizes Offset translation relative to control size with labeled overlays." +requires-python = ">=3.10" +keywords = ["layout control", "offset", "transform", "stack"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Offset" +controls = ["SafeArea", "Stack", "Container", "Text", "Icon", "Offset"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["offset translation", "visual offset comparison"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/rocket.py b/sdk/python/examples/controls/layout_control/rocket.py deleted file mode 100644 index 67b2db9920..0000000000 --- a/sdk/python/examples/controls/layout_control/rocket.py +++ /dev/null @@ -1,45 +0,0 @@ -from math import pi - -import flet as ft - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def animate(e: ft.Event[ft.Button]): - container.rotate.angle -= 0.5 * pi - container.content.scale = 2.0 if container.content.scale == 1.0 else 1.0 - container.content.opacity = 0.4 if container.content.scale == 1.0 else 1.0 - page.update() - - page.add( - ft.Column( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - height=300, - controls=[ - container := ft.Container( - width=120, - height=70, - alignment=ft.Alignment.CENTER_RIGHT, - rotate=ft.Rotate(0, alignment=ft.Alignment.CENTER_LEFT), - animate_rotation=ft.Animation(duration=1000), - content=ft.Container( - scale=1.0, - animate_scale=1000, - opacity=1.0, - animate_opacity=True, - content=ft.Icon( - ft.Icons.ROCKET, - size=40, - color=ft.Colors.BLACK, - ), - ), - ), - ft.Button("Launch!", on_click=animate), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/rocket/main.py b/sdk/python/examples/controls/layout_control/rocket/main.py new file mode 100644 index 0000000000..edc1573306 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/rocket/main.py @@ -0,0 +1,48 @@ +from math import pi + +import flet as ft + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def animate(e: ft.Event[ft.Button]): + container.rotate.angle -= 0.5 * pi + container.content.scale = 2.0 if container.content.scale == 1.0 else 1.0 + container.content.opacity = 0.4 if container.content.scale == 1.0 else 1.0 + container.update() + + page.add( + ft.SafeArea( + content=ft.Column( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + height=300, + controls=[ + container := ft.Container( + width=120, + height=70, + alignment=ft.Alignment.CENTER_RIGHT, + rotate=ft.Rotate(0, alignment=ft.Alignment.CENTER_LEFT), + animate_rotation=ft.Animation(duration=1000), + content=ft.Container( + scale=1.0, + animate_scale=1000, + opacity=1.0, + animate_opacity=True, + content=ft.Icon( + ft.Icons.ROCKET, + size=40, + color=ft.Colors.BLACK, + ), + ), + ), + ft.Button("Launch!", on_click=animate), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/rocket/pyproject.toml b/sdk/python/examples/controls/layout_control/rocket/pyproject.toml new file mode 100644 index 0000000000..af5a85a9b6 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/rocket/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-rocket" +version = "1.0.0" +description = "Launch animation combining rotation, scale, and opacity on a rocket icon." +requires-python = ">=3.10" +keywords = ["layout control", "rocket", "rotation", "scale", "opacity"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Rocket" +controls = ["SafeArea", "Column", "Container", "Icon", "Button", "Rotate", "Animation"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["combined transform animation", "icon launch effect"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/rotate.py b/sdk/python/examples/controls/layout_control/rotate.py deleted file mode 100644 index 23c355ab35..0000000000 --- a/sdk/python/examples/controls/layout_control/rotate.py +++ /dev/null @@ -1,27 +0,0 @@ -from math import pi - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.add( - ft.Container( - width=220, - height=120, - bgcolor=ft.Colors.BLUE_300, - border_radius=16, - alignment=ft.Alignment.CENTER, - content=ft.Text("Rotate", size=28, weight=ft.FontWeight.BOLD), - rotate=ft.Rotate( - angle=pi / 10, - alignment=ft.Alignment.CENTER, - filter_quality=ft.FilterQuality.MEDIUM, - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/rotate/main.py b/sdk/python/examples/controls/layout_control/rotate/main.py new file mode 100644 index 0000000000..5b84f976e9 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/rotate/main.py @@ -0,0 +1,29 @@ +from math import pi + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.add( + ft.SafeArea( + content=ft.Container( + width=220, + height=120, + bgcolor=ft.Colors.BLUE_300, + border_radius=16, + alignment=ft.Alignment.CENTER, + content=ft.Text("Rotate", size=28, weight=ft.FontWeight.BOLD), + rotate=ft.Rotate( + angle=pi / 10, + alignment=ft.Alignment.CENTER, + filter_quality=ft.FilterQuality.MEDIUM, + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/rotate/pyproject.toml b/sdk/python/examples/controls/layout_control/rotate/pyproject.toml new file mode 100644 index 0000000000..b670cf878a --- /dev/null +++ b/sdk/python/examples/controls/layout_control/rotate/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-rotate" +version = "1.0.0" +description = "Applies a fixed rotation transform to a container using Rotate." +requires-python = ">=3.10" +keywords = ["layout control", "rotate", "transform", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Rotate" +controls = ["SafeArea", "Container", "Text", "Rotate"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["static rotation transform"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/scale.py b/sdk/python/examples/controls/layout_control/scale.py deleted file mode 100644 index 0b226daa95..0000000000 --- a/sdk/python/examples/controls/layout_control/scale.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.add( - ft.Container( - width=220, - height=120, - bgcolor=ft.Colors.GREEN_300, - border_radius=16, - alignment=ft.Alignment.CENTER, - content=ft.Text("Scale", size=28, weight=ft.FontWeight.BOLD), - scale=ft.Scale( - scale_x=1.18, - scale_y=0.82, - alignment=ft.Alignment.CENTER, - filter_quality=ft.FilterQuality.MEDIUM, - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/scale/main.py b/sdk/python/examples/controls/layout_control/scale/main.py new file mode 100644 index 0000000000..469eaa2112 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/scale/main.py @@ -0,0 +1,28 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.add( + ft.SafeArea( + content=ft.Container( + width=220, + height=120, + bgcolor=ft.Colors.GREEN_300, + border_radius=16, + alignment=ft.Alignment.CENTER, + content=ft.Text("Scale", size=28, weight=ft.FontWeight.BOLD), + scale=ft.Scale( + scale_x=1.18, + scale_y=0.82, + alignment=ft.Alignment.CENTER, + filter_quality=ft.FilterQuality.MEDIUM, + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/scale/pyproject.toml b/sdk/python/examples/controls/layout_control/scale/pyproject.toml new file mode 100644 index 0000000000..94014411a5 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/scale/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-scale" +version = "1.0.0" +description = "Applies non-uniform scaling to a container using Scale transform." +requires-python = ">=3.10" +keywords = ["layout control", "scale", "transform", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Scale" +controls = ["SafeArea", "Container", "Text", "Scale"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["static scale transform", "non-uniform scaling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/layout_control/switcher.py b/sdk/python/examples/controls/layout_control/switcher.py deleted file mode 100644 index 4795c29be2..0000000000 --- a/sdk/python/examples/controls/layout_control/switcher.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def animate(e: ft.Event[ft.Button]): - shader.rotate = 1 - shader.scale = 3 - page.update() - - page.add( - ft.Stack( - width=500, - height=300, - controls=[ - shader := ft.ShaderMask( - blend_mode=ft.BlendMode.COLOR_BURN, - border_radius=5, - animate_rotation=300, - animate_scale=ft.Animation(600, ft.AnimationCurve.BOUNCE_OUT), - shader=ft.RadialGradient( - center=ft.Alignment.TOP_LEFT, - radius=1.0, - colors=[ft.Colors.YELLOW, ft.Colors.DEEP_ORANGE_900], - tile_mode=ft.GradientTileMode.CLAMP, - ), - content=ft.Image( - src="https://picsum.photos/140/100?1", - width=140, - height=100, - fit=ft.BoxFit.FILL, - ), - ) - ], - ), - ft.Button("Animate!", on_click=animate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/switcher/main.py b/sdk/python/examples/controls/layout_control/switcher/main.py new file mode 100644 index 0000000000..3e5530824c --- /dev/null +++ b/sdk/python/examples/controls/layout_control/switcher/main.py @@ -0,0 +1,52 @@ +import flet as ft + + +def main(page: ft.Page): + def animate(e: ft.Event[ft.Button]): + shader.rotate = 1 + shader.scale = 3 + shader.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Stack( + width=500, + height=300, + controls=[ + shader := ft.ShaderMask( + blend_mode=ft.BlendMode.COLOR_BURN, + border_radius=5, + animate_rotation=300, + animate_scale=ft.Animation( + 600, + ft.AnimationCurve.BOUNCE_OUT, + ), + shader=ft.RadialGradient( + center=ft.Alignment.TOP_LEFT, + radius=1.0, + colors=[ + ft.Colors.YELLOW, + ft.Colors.DEEP_ORANGE_900, + ], + tile_mode=ft.GradientTileMode.CLAMP, + ), + content=ft.Image( + src="https://picsum.photos/140/100?1", + width=140, + height=100, + fit=ft.BoxFit.FILL, + ), + ) + ], + ), + ft.Button("Animate!", on_click=animate), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/layout_control/switcher/pyproject.toml b/sdk/python/examples/controls/layout_control/switcher/pyproject.toml new file mode 100644 index 0000000000..a22e038460 --- /dev/null +++ b/sdk/python/examples/controls/layout_control/switcher/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "layout-control-switcher" +version = "1.0.0" +description = "Animates ShaderMask rotation and scale over an image with gradient shader." +requires-python = ">=3.10" +keywords = ["layout control", "shader mask", "rotation", "scale", "image"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/LayoutControl"] + +[tool.flet.metadata] +title = "Switcher" +controls = ["SafeArea", "Column", "Stack", "ShaderMask", "Image", "Button", "RadialGradient"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["shader mask animation", "rotation and scale animation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/list_tile/basic.py b/sdk/python/examples/controls/list_tile/basic.py deleted file mode 100644 index 723ed41c39..0000000000 --- a/sdk/python/examples/controls/list_tile/basic.py +++ /dev/null @@ -1,76 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "ListTile Example" - - page.add( - ft.Card( - content=ft.Container( - width=500, - padding=ft.Padding.symmetric(vertical=10), - content=ft.Column( - spacing=0, - controls=[ - ft.ListTile(title=ft.Text("One-line list tile")), - ft.ListTile( - title=ft.Text("One-line dense list tile"), - dense=True, - ), - ft.ListTile( - leading=ft.Icon(ft.Icons.SETTINGS), - title=ft.Text("One-line selected list tile"), - selected=True, - ), - ft.ListTile( - leading=ft.Image( - src="/icons/icon-192.png", - fit=ft.BoxFit.CONTAIN, - ), - title=ft.Text("One-line with leading control"), - ), - ft.ListTile( - title=ft.Text("One-line with trailing control"), - trailing=ft.PopupMenuButton( - icon=ft.Icons.MORE_VERT, - items=[ - ft.PopupMenuItem(content="Item 1"), - ft.PopupMenuItem(content="Item 2"), - ], - ), - ), - ft.ListTile( - leading=ft.Icon(ft.Icons.ALBUM), - title=ft.Text( - value="One-line with leading and trailing controls" - ), - trailing=ft.PopupMenuButton( - icon=ft.Icons.MORE_VERT, - items=[ - ft.PopupMenuItem(content="Item 1"), - ft.PopupMenuItem(content="Item 2"), - ], - ), - ), - ft.ListTile( - leading=ft.Icon(ft.Icons.SNOOZE), - title=ft.Text( - value="Two-line with leading and trailing controls" - ), - subtitle=ft.Text("Here is a second title."), - trailing=ft.PopupMenuButton( - icon=ft.Icons.MORE_VERT, - items=[ - ft.PopupMenuItem(content="Item 1"), - ft.PopupMenuItem(content="Item 2"), - ], - ), - ), - ], - ), - ) - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/list_tile/basic/main.py b/sdk/python/examples/controls/list_tile/basic/main.py new file mode 100644 index 0000000000..c86703a7de --- /dev/null +++ b/sdk/python/examples/controls/list_tile/basic/main.py @@ -0,0 +1,83 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "ListTile Example" + + page.add( + ft.SafeArea( + content=ft.Card( + content=ft.Container( + width=500, + padding=ft.Padding.symmetric(vertical=10), + content=ft.Column( + spacing=0, + controls=[ + ft.ListTile(title=ft.Text("One-line list tile")), + ft.ListTile( + title=ft.Text("One-line dense list tile"), + dense=True, + ), + ft.ListTile( + leading=ft.Icon(ft.Icons.SETTINGS), + title=ft.Text("One-line selected list tile"), + selected=True, + ), + ft.ListTile( + leading=ft.Image( + src="/icons/icon-192.png", + fit=ft.BoxFit.CONTAIN, + ), + title=ft.Text("One-line with leading control"), + ), + ft.ListTile( + title=ft.Text("One-line with trailing control"), + trailing=ft.PopupMenuButton( + icon=ft.Icons.MORE_VERT, + items=[ + ft.PopupMenuItem(content="Item 1"), + ft.PopupMenuItem(content="Item 2"), + ], + ), + ), + ft.ListTile( + leading=ft.Icon(ft.Icons.ALBUM), + title=ft.Text( + value=( + "One-line with leading and trailing controls" + ) + ), + trailing=ft.PopupMenuButton( + icon=ft.Icons.MORE_VERT, + items=[ + ft.PopupMenuItem(content="Item 1"), + ft.PopupMenuItem(content="Item 2"), + ], + ), + ), + ft.ListTile( + leading=ft.Icon(ft.Icons.SNOOZE), + title=ft.Text( + value=( + "Two-line with leading and trailing controls" + ) + ), + subtitle=ft.Text("Here is a second title."), + trailing=ft.PopupMenuButton( + icon=ft.Icons.MORE_VERT, + items=[ + ft.PopupMenuItem(content="Item 1"), + ft.PopupMenuItem(content="Item 2"), + ], + ), + ), + ], + ), + ) + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/list_tile/basic/pyproject.toml b/sdk/python/examples/controls/list_tile/basic/pyproject.toml new file mode 100644 index 0000000000..5816f01ba3 --- /dev/null +++ b/sdk/python/examples/controls/list_tile/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "list-tile-basic" +version = "1.0.0" +description = "Showcases one-line and two-line ListTile variants with leading/trailing controls and selection state." +requires-python = ">=3.10" +keywords = ["list tile", "material", "popup menu", "selection", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ListTile"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Card", "Container", "Column", "ListTile", "Icon", "Image", "PopupMenuButton", "PopupMenuItem", "Text"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["tile variants", "leading and trailing controls", "selected and dense states"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/list_view/autoscroll_and_dynamic_items.py b/sdk/python/examples/controls/list_view/autoscroll_and_dynamic_items.py deleted file mode 100644 index dc7dabb5ce..0000000000 --- a/sdk/python/examples/controls/list_view/autoscroll_and_dynamic_items.py +++ /dev/null @@ -1,48 +0,0 @@ -import asyncio - -import flet as ft - - -async def main(page: ft.Page): - def handle_switch_change(e: ft.Event[ft.Switch]): - lv.auto_scroll = not lv.auto_scroll - page.update() - - lv = ft.ListView( - spacing=10, - padding=20, - width=150, - auto_scroll=True, - controls=[ - ft.Text(f"Line {i}", color=ft.Colors.ON_SECONDARY) for i in range(0, 60) - ], - ) - - page.add( - ft.Row( - expand=True, - vertical_alignment=ft.CrossAxisAlignment.START, - controls=[ - ft.Container( - content=lv, - bgcolor=ft.Colors.GREY_500, - ), - ft.Switch( - thumb_icon=ft.Icons.LIST_OUTLINED, - value=True, - label="Auto-scroll", - label_position=ft.LabelPosition.RIGHT, - on_change=handle_switch_change, - ), - ], - ) - ) - - # add a new item to the ListView every 1 second - for i in range(len(lv.controls), 120): - await asyncio.sleep(1) - lv.controls.append(ft.Text(f"Line {i}", color=ft.Colors.ON_SECONDARY)) - page.update() - - -ft.run(main) diff --git a/sdk/python/examples/controls/list_view/autoscroll_and_dynamic_items/main.py b/sdk/python/examples/controls/list_view/autoscroll_and_dynamic_items/main.py new file mode 100644 index 0000000000..e1a40ec6ae --- /dev/null +++ b/sdk/python/examples/controls/list_view/autoscroll_and_dynamic_items/main.py @@ -0,0 +1,52 @@ +import asyncio + +import flet as ft + + +async def main(page: ft.Page): + def handle_switch_change(e: ft.Event[ft.Switch]): + lv.auto_scroll = not lv.auto_scroll + lv.update() + + lv = ft.ListView( + spacing=10, + padding=20, + width=150, + auto_scroll=True, + controls=[ + ft.Text(f"Line {i}", color=ft.Colors.ON_SECONDARY) for i in range(0, 60) + ], + ) + + page.add( + ft.SafeArea( + expand=True, + content=ft.Row( + expand=True, + vertical_alignment=ft.CrossAxisAlignment.START, + controls=[ + ft.Container( + bgcolor=ft.Colors.GREY_500, + content=lv, + ), + ft.Switch( + thumb_icon=ft.Icons.LIST_OUTLINED, + value=True, + label="Auto-scroll", + label_position=ft.LabelPosition.RIGHT, + on_change=handle_switch_change, + ), + ], + ), + ) + ) + + # Add a new item to the ListView every second. + for i in range(len(lv.controls), 120): + await asyncio.sleep(1) + lv.controls.append(ft.Text(f"Line {i}", color=ft.Colors.ON_SECONDARY)) + lv.update() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/list_view/autoscroll_and_dynamic_items/pyproject.toml b/sdk/python/examples/controls/list_view/autoscroll_and_dynamic_items/pyproject.toml new file mode 100644 index 0000000000..9e8059c952 --- /dev/null +++ b/sdk/python/examples/controls/list_view/autoscroll_and_dynamic_items/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "list-view-autoscroll-and-dynamic-items" +version = "1.0.0" +description = "Adds new ListView items over time and toggles auto-scroll behavior with a switch." +requires-python = ">=3.10" +keywords = ["list view", "auto scroll", "dynamic items", "async", "switch"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ListView"] + +[tool.flet.metadata] +title = "Auto-scrolling and dynamical items addition" +controls = ["SafeArea", "Row", "Container", "ListView", "Text", "Switch"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["auto-scroll toggle", "dynamic list item appending", "async updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/lottie/__init__.py b/sdk/python/examples/controls/lottie/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/lottie/example_1.py b/sdk/python/examples/controls/lottie/example_1.py deleted file mode 100644 index 2479cce046..0000000000 --- a/sdk/python/examples/controls/lottie/example_1.py +++ /dev/null @@ -1,27 +0,0 @@ -import flet_lottie as ftl - -import flet as ft - - -def main(page: ft.Page): - page.add( - ftl.Lottie( - src="https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json", - reverse=False, - animate=True, - error_content=ft.Placeholder(ft.Text("Error loading Lottie")), - on_error=lambda e: print(f"Error loading Lottie: {e.data}"), - ), - ftl.Lottie( - src="sample.json", - reverse=False, - animate=True, - enable_merge_paths=True, - enable_layers_opacity=True, - error_content=ft.Placeholder(ft.Text("Error loading Lottie")), - on_error=lambda e: print(f"Error loading Lottie: {e.data}"), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/lottie/example_1/assets/sample.json b/sdk/python/examples/controls/lottie/example_1/assets/sample.json new file mode 100644 index 0000000000..afd268ea80 --- /dev/null +++ b/sdk/python/examples/controls/lottie/example_1/assets/sample.json @@ -0,0 +1 @@ +{"assets":[],"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"B 2","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[32.279,-158.917],[6.297,-132.935],[-32.677,-132.935],[-32.677,-184.598],[6.297,-184.598]],"c":true}},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[16.569,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-93.658],[43.156,-67.676]],"c":true}},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[1,1,1,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":26,"op":37,"st":22,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"B","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[21.149,8.762],[0,21.451],[37.766,0],[0,0],[0,0],[0,0],[0,41.089]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[37.463,0],[0,-29.004]],"v":[[55.241,-119.037],[79.411,-164.355],[12.037,-226.593],[-79.507,-226.593],[-79.507,0],[23.518,0],[90.287,-63.144]],"c":true}},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.01,0.24,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":26,"op":37,"st":22,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"B blue layer 6","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[-1.324,-11.343],[2.439,-7.314],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[3.612,-2.224]],"v":[[-55.603,-180.739],[-55.439,-147.686],[-63.981,-142.398],[-70.654,-190.988],[-63.612,-197.276]],"c":true}],"e":[{"i":[[-2.154,-14.341],[9.939,-11.314],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[9.112,-3.724]],"v":[[-32.103,-179.739],[-36.939,-146.686],[-53.481,-139.398],[-60.154,-190.488],[-47.112,-197.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[-2.154,-14.341],[9.939,-11.314],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[9.112,-3.724]],"v":[[-32.103,-179.739],[-36.939,-146.686],[-53.481,-139.398],[-60.154,-190.488],[-47.112,-197.276]],"c":true}],"e":[{"i":[[-2.295,-14.342],[14.615,-7.06],[0,0],[0,0],[0,0]],"o":[[2.247,14.043],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[-11.249,-176.983],[-23.893,-143.184],[-46.891,-138.152],[-50.397,-189.494],[-30.535,-196.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[-2.295,-14.342],[14.615,-7.06],[0,0],[0,0],[0,0]],"o":[[2.247,14.043],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[-11.249,-176.983],[-23.893,-143.184],[-46.891,-138.152],[-50.397,-189.494],[-30.535,-196.276]],"c":true}],"e":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[4.397,-174.239],[-14.439,-139.686],[-40.981,-136.898],[-44.654,-188.488],[-18.612,-193.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[4.397,-174.239],[-14.439,-139.686],[-40.981,-136.898],[-44.654,-188.488],[-18.612,-193.276]],"c":true}],"e":[{"i":[[-1.534,-14.395],[17.58,-1.876],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.855,-1.149]],"v":[[16.998,-170.132],[-5.273,-137.269],[-37.547,-135.91],[-39.662,-187.025],[-9.278,-190.383]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[-1.534,-14.395],[17.58,-1.876],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.855,-1.149]],"v":[[16.998,-170.132],[-5.273,-137.269],[-37.547,-135.91],[-39.662,-187.025],[-9.278,-190.383]],"c":true}],"e":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[24.639,-165.025],[-0.738,-135.852],[-35.862,-134.172],[-36.92,-187.061],[-2.991,-188.991]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[24.639,-165.025],[-0.738,-135.852],[-35.862,-134.172],[-36.92,-187.061],[-2.991,-188.991]],"c":true}],"e":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[28.279,-161.917],[4.297,-134.435],[-34.177,-133.435],[-34.177,-187.098],[3.297,-187.598]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"n":"0_1_0p167_0p167","t":21.376,"s":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[28.279,-161.917],[4.297,-134.435],[-34.177,-133.435],[-34.177,-187.098],[3.297,-187.598]],"c":true}],"e":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[32.279,-158.917],[6.297,-132.935],[-32.677,-132.935],[-32.677,-184.598],[6.297,-184.598]],"c":true}]},{"t":24}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[5.931,-1.505],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.344,19.176]],"v":[[-44.931,-50.495],[-55.177,-48.495],[-60.677,-102.158],[-53.931,-106.658],[-42.844,-84.676]],"c":true}],"e":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.589,13.962]],"v":[[-27.431,-47.995],[-46.177,-46.495],[-51.177,-100.658],[-34.431,-104.658],[-18.344,-87.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.589,13.962]],"v":[[-27.431,-47.995],[-46.177,-46.495],[-51.177,-100.658],[-34.431,-104.658],[-18.344,-87.176]],"c":true}],"e":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[11.868,-1.421],[2.589,13.962]],"v":[[-10.681,-46.995],[-40.427,-45.245],[-43.677,-98.908],[-17.181,-102.408],[3.406,-81.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[11.868,-1.421],[2.589,13.962]],"v":[[-10.681,-46.995],[-40.427,-45.245],[-43.677,-98.908],[-17.181,-102.408],[3.406,-81.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[2.589,13.962]],"v":[[-0.931,-44.995],[-36.677,-42.995],[-39.177,-97.158],[-3.931,-100.158],[20.156,-76.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[2.589,13.962]],"v":[[-0.931,-44.995],[-36.677,-42.995],[-39.177,-97.158],[-3.931,-100.158],[20.156,-76.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-1.77,-14.283]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.726,14.041]],"v":[[6.902,-43.495],[-34.844,-42.662],[-35.844,-96.158],[5.902,-97.825],[29.989,-73.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-1.77,-14.283]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.726,14.041]],"v":[[6.902,-43.495],[-34.844,-42.662],[-35.844,-96.158],[5.902,-97.825],[29.989,-73.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[12.235,-42.745],[-33.761,-42.329],[-34.761,-95.908],[11.235,-96.242],[37.322,-69.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[12.235,-42.745],[-33.761,-42.329],[-34.761,-95.908],[11.235,-96.242],[37.322,-69.926]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[13.569,-41.995],[-32.677,-41.995],[-33.677,-95.658],[16.569,-94.658],[40.656,-68.676]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"n":"0_1_0p167_0p167","t":21.376,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[13.569,-41.995],[-32.677,-41.995],[-33.677,-95.658],[16.569,-94.658],[40.656,-68.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[16.569,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-93.658],[43.156,-67.676]],"c":true}]},{"t":24}]},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[1,1,1,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":16,"op":26,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"B blue layer 5","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[5.759,27.537],[4.508,20.972],[7.96,-31.787],[0,0],[0,0],[-52.303,4.363],[12.713,79.144]],"o":[[-6.241,-30.463],[-18.411,-85.645],[-8.037,32.093],[0,0],[0,0],[17.982,-1.5],[-4.202,-26.16]],"v":[[-23.759,-157.537],[-41.089,-239.855],[-57.963,-260.093],[-97.507,-214.093],[-76.507,-0.5],[-13.982,-7.5],[-8.213,-83.644]],"c":true}],"e":[{"i":[[8.259,26.537],[3.138,21.22],[33.463,-28.907],[0,0],[0,0],[-52.485,0],[6.213,46.144]],"o":[[-3.241,-28.463],[-9.411,-63.645],[-29.686,25.644],[0,0],[0,0],[43.482,0],[-4.959,-36.833]],"v":[[21.241,-163.037],[14.911,-228.855],[-29.963,-255.093],[-95.007,-217.593],[-76.507,-0.5],[17.018,-3.5],[41.787,-87.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[8.259,26.537],[3.138,21.22],[33.463,-28.907],[0,0],[0,0],[-52.485,0],[6.213,46.144]],"o":[[-3.241,-28.463],[-9.411,-63.645],[-29.686,25.644],[0,0],[0,0],[43.482,0],[-4.959,-36.833]],"v":[[21.241,-163.037],[14.911,-228.855],[-29.963,-255.093],[-95.007,-217.593],[-76.507,-0.5],[17.018,-3.5],[41.787,-87.144]],"c":true}],"e":[{"i":[[15.759,21.037],[2.598,21.286],[37.463,-16.657],[0,0],[0,0],[-26.242,0],[5.463,51.394]],"o":[[3.509,-24.213],[-4.661,-61.895],[-35.537,14.843],[0,0],[0,0],[52.482,4],[-3.676,-32.795]],"v":[[46.741,-144.787],[49.661,-211.605],[-14.963,-245.343],[-91.757,-220.343],[-77.257,-0.75],[21.518,-2],[72.287,-76.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[15.759,21.037],[2.598,21.286],[37.463,-16.657],[0,0],[0,0],[-26.242,0],[5.463,51.394]],"o":[[3.509,-24.213],[-4.661,-61.895],[-35.537,14.843],[0,0],[0,0],[52.482,4],[-3.676,-32.795]],"v":[[46.741,-144.787],[49.661,-211.605],[-14.963,-245.343],[-91.757,-220.343],[-77.257,-0.75],[21.518,-2],[72.287,-76.144]],"c":true}],"e":[{"i":[[23.259,15.537],[2.058,21.352],[34.868,-8.794],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.037,9.593],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[56.241,-133.537],[68.411,-198.355],[-2.963,-239.593],[-88.507,-223.093],[-78.007,-1],[26.018,-0.5],[86.787,-72.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[23.259,15.537],[2.058,21.352],[34.868,-8.794],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.037,9.593],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[56.241,-133.537],[68.411,-198.355],[-2.963,-239.593],[-88.507,-223.093],[-78.007,-1],[26.018,-0.5],[86.787,-72.144]],"c":true}],"e":[{"i":[[24.259,15.287],[1.029,21.401],[36.665,-5.6],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-2.705,-47.408],[-19.018,4.797],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[76.911,-186.105],[5.787,-235.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[24.259,15.287],[1.029,21.401],[36.665,-5.6],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-2.705,-47.408],[-19.018,4.797],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[76.911,-186.105],[5.787,-235.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"n":"0_1_0p167_0p167","t":22.253,"s":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[37.766,0],[0,0],[0,0],[0,0],[0,41.089]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[37.463,0],[0,-29.004]],"v":[[55.241,-119.037],[79.411,-164.355],[12.037,-226.593],[-79.507,-226.593],[-79.507,0],[23.518,0],[90.287,-63.144]],"c":true}]},{"t":24}]},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.01,0.24,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":16,"op":26,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":3,"nm":"Null smoke","parent":34,"ks":{"o":{"k":0},"r":{"k":9.771},"p":{"k":[{"i":{"x":0.601,"y":0.889},"o":{"x":0.239,"y":0.067},"n":"0p601_0p889_0p239_0p067","t":9,"s":[241.443,258.888,0],"e":[222.443,251.888,0],"to":[-0.5,-0.33333334326744,0],"ti":[-1.02731943130493,-0.37357068061829,0]},{"i":{"x":0.686,"y":1},"o":{"x":0.239,"y":0.304},"n":"0p686_1_0p239_0p304","t":11,"s":[222.443,251.888,0],"e":[242,259,0],"to":[5.54179763793945,2.01519918441772,0],"ti":[-3.09325051307678,-1.12481832504272,0]},{"t":20.3449832499027}]},"a":{"k":[0,0,0]},"s":{"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":8.999,"s":[-100,100,100],"e":[-128,128,100]},{"t":21.9237344563007}]}},"ao":0,"ip":9.99999961256981,"op":22.7584036290646,"st":-10.5559570491314,"bm":0,"sr":0.96000009775162},{"ddd":0,"ind":5,"ty":3,"nm":"Null smoke","parent":34,"ks":{"o":{"k":0},"r":{"k":-22.412},"p":{"k":[{"i":{"x":0.592,"y":0.891},"o":{"x":0.257,"y":0.068},"n":"0p592_0p891_0p257_0p068","t":9,"s":[225.656,310.504,0],"e":[206.656,303.504,0],"to":[-0.5,-0.33333334326744,0],"ti":[-1.516930103302,-0.31094110012054,0]},{"i":{"x":0.762,"y":1},"o":{"x":0.257,"y":0.3},"n":"0p762_1_0p257_0p3","t":11,"s":[206.656,303.504,0],"e":[228,308,0],"to":[6.19302797317505,1.26945006847382,0],"ti":[-0.32227402925491,0,0]},{"t":20.3640127778053}]},"a":{"k":[0,0,0]},"s":{"k":[-100,100,100]}},"ao":0,"ip":10.0000003576279,"op":22.5608477592468,"st":-9.35224944353104,"bm":0,"sr":0.95000010728836},{"ddd":0,"ind":6,"ty":3,"nm":"Null smoke","parent":34,"ks":{"o":{"k":0},"r":{"k":26.915},"p":{"k":[{"i":{"x":0.587,"y":0.894},"o":{"x":0.264,"y":0.068},"n":"0p587_0p894_0p264_0p068","t":10,"s":[228.667,205.657,0],"e":[209.667,198.657,0],"to":[-0.5,-0.33333334326744,0],"ti":[-1.19691395759583,-0.40347543358803,0]},{"i":{"x":0.713,"y":1},"o":{"x":0.264,"y":0.336},"n":"0p713_1_0p264_0p336","t":12,"s":[209.667,198.657,0],"e":[223,204,0],"to":[4.62041616439819,1.55752575397491,0],"ti":[-0.48471575975418,0,0]},{"t":19.0271503602465}]},"a":{"k":[0,0,0]},"s":{"k":[-100,100,100]}},"ao":0,"ip":11.0000006233652,"op":22.5608497237166,"st":-9.35224822411935,"bm":0,"sr":0.95000010728836},{"ddd":0,"ind":7,"ty":4,"nm":"B blue layer 4 shadow","parent":34,"ks":{"o":{"k":5},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[6,3],[35.446,17.154],[1.39,-24.214],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[-35.446,-17.154],[-1.585,27.613],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-140,-282],[-94,-266],[-98,-118],[-80,-5],[-119,27],[-97,40],[-59,31],[-74,-216]],"c":true}],"e":[{"i":[[6,3],[0,-5],[-5,-22],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[0,5],[5,22],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-140,-282],[-143,-270],[-133,-137],[-118,-11],[-119,27],[-97,40],[-59,31],[-74,-216]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[6,3],[0,-5],[-5,-22],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[0,5],[5,22],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-140,-282],[-143,-270],[-133,-137],[-118,-11],[-119,27],[-97,40],[-59,31],[-74,-216]],"c":true}],"e":[{"i":[[6,3],[0,-5],[-5,-22],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[0,5],[5,22],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-140,-282],[-178,-280],[-159,-145],[-141,4],[-119,27],[-97,40],[-59,31],[-74,-216]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[6,3],[0,-5],[-5,-22],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[0,5],[5,22],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-140,-282],[-178,-280],[-159,-145],[-141,4],[-119,27],[-97,40],[-59,31],[-74,-216]],"c":true}],"e":[{"i":[[6,3],[0,-5],[-7,-32],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[0,5],[6.414,29.323],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-140,-282],[-158,-284],[-149,-204],[-118,6],[-119,27],[-97,40],[-59,31],[-74,-216]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[6,3],[0,-5],[-7,-32],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[0,5],[6.414,29.323],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-140,-282],[-158,-284],[-149,-204],[-118,6],[-119,27],[-97,40],[-59,31],[-74,-216]],"c":true}],"e":[{"i":[[6,3],[0,-5],[-0.752,-22.548],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[0,5],[1,30],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-106,-286],[-115,-276],[-99,-138],[-79,20],[-119,27],[-97,40],[-59,31],[-74,-216]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[6,3],[0,-5],[-0.752,-22.548],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[0,5],[1,30],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-106,-286],[-115,-276],[-99,-138],[-79,20],[-119,27],[-97,40],[-59,31],[-74,-216]],"c":true}],"e":[{"i":[[6,3],[0,-5],[-0.752,-22.548],[-7,-13],[-11,-18],[-9,-3],[0,0],[0,0]],"o":[[-6,-3],[0,5],[1,30],[7,13],[11,18],[9,3],[0,0],[0,0]],"v":[[-388,-298],[-397,-288],[-381,-150],[-361,8],[-401,15],[-379,28],[-341,19],[-356,-228]],"c":true}]},{"t":15}]},"o":{"k":100},"x":{"k":0},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[3.637,14.035],[-6.562,-18.555],[0,0],[0,0],[0,0],[1.944,58.843],[5.869,46.436],[6.789,29.064]],"o":[[-3.783,-14.599],[12.079,34.152],[0,0],[0,0],[0,0],[-0.327,-9.886],[-2.413,-19.093],[-6.444,-27.588]],"v":[[-100.137,-270.035],[-81.079,-226.152],[-69.301,-172.891],[-65.375,-112.805],[-61.507,1.5],[-65.673,-78.614],[-71.869,-138.936],[-83.556,-205.912]],"c":true}],"e":[{"i":[[1.179,13.587],[-16.316,-11.006],[0,0],[0,0],[0,0],[51.329,28.838],[-0.631,46.436],[1.219,29.821]],"o":[[-1.863,-21.465],[33.579,22.652],[0,0],[0,0],[0,0],[-23.827,-13.386],[0.145,-10.708],[-0.944,-23.088]],"v":[[-180.137,-248.535],[-116.079,-227.152],[-76.301,-169.391],[-71.875,-105.305],[-64.007,0.5],[-127.173,-60.614],[-176.869,-121.436],[-176.556,-181.412]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[1.179,13.587],[-16.316,-11.006],[0,0],[0,0],[0,0],[51.329,28.838],[-0.631,46.436],[1.219,29.821]],"o":[[-1.863,-21.465],[33.579,22.652],[0,0],[0,0],[0,0],[-23.827,-13.386],[0.145,-10.708],[-0.944,-23.088]],"v":[[-180.137,-248.535],[-116.079,-227.152],[-76.301,-169.391],[-71.875,-105.305],[-64.007,0.5],[-127.173,-60.614],[-176.869,-121.436],[-176.556,-181.412]],"c":true}],"e":[{"i":[[0.592,13.423],[-38.037,-14.907],[0,0],[0,0],[0,0],[62.482,13.5],[0.713,41.144],[-1.198,29.377]],"o":[[-0.911,-20.645],[31.01,12.153],[0,0],[0,0],[0,0],[-23.658,-5.112],[-0.183,-10.545],[1.875,-45.981]],"v":[[-210.589,-237.355],[-131.463,-229.593],[-83.507,-180.593],[-80.113,-142.106],[-67.007,-0.5],[-149.982,-38.5],[-213.713,-83.644],[-211.875,-154.519]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[0.592,13.423],[-38.037,-14.907],[0,0],[0,0],[0,0],[62.482,13.5],[0.713,41.144],[-1.198,29.377]],"o":[[-0.911,-20.645],[31.01,12.153],[0,0],[0,0],[0,0],[-23.658,-5.112],[-0.183,-10.545],[1.875,-45.981]],"v":[[-210.589,-237.355],[-131.463,-229.593],[-83.507,-180.593],[-80.113,-142.106],[-67.007,-0.5],[-149.982,-38.5],[-213.713,-83.644],[-211.875,-154.519]],"c":true}],"e":[{"i":[[0.592,13.423],[-38.037,-14.907],[0,0],[0,0],[0,0],[49.59,6.194],[2.062,42.216],[0.844,29.39]],"o":[[-0.911,-20.645],[31.01,12.153],[0,0],[0,0],[0,0],[-24.018,-3],[-0.514,-10.534],[-1.267,-44.142]],"v":[[-219.089,-243.355],[-134.463,-234.593],[-89.007,-187.093],[-84.613,-147.106],[-68.507,-0.5],[-150.482,-30.5],[-213.713,-68.644],[-215.875,-135.019]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[0.592,13.423],[-38.037,-14.907],[0,0],[0,0],[0,0],[49.59,6.194],[2.062,42.216],[0.844,29.39]],"o":[[-0.911,-20.645],[31.01,12.153],[0,0],[0,0],[0,0],[-24.018,-3],[-0.514,-10.534],[-1.267,-44.142]],"v":[[-219.089,-243.355],[-134.463,-234.593],[-89.007,-187.093],[-84.613,-147.106],[-68.507,-0.5],[-150.482,-30.5],[-213.713,-68.644],[-215.875,-135.019]],"c":true}],"e":[{"i":[[1.979,12.724],[-39.037,-17.407],[0,0],[0,0],[0,0],[49.482,7],[3.213,42.144],[3.102,33.18]],"o":[[-3.911,-25.145],[30.419,13.564],[0,0],[0,0],[0,0],[-13.966,-1.976],[-0.805,-10.559],[-4.66,-49.835]],"v":[[-212.089,-255.355],[-134.463,-244.093],[-94.007,-197.593],[-89.078,-155.355],[-71.007,-0.5],[-128.482,-21.5],[-193.213,-61.644],[-199.771,-135.061]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[1.979,12.724],[-39.037,-17.407],[0,0],[0,0],[0,0],[49.482,7],[3.213,42.144],[3.102,33.18]],"o":[[-3.911,-25.145],[30.419,13.564],[0,0],[0,0],[0,0],[-13.966,-1.976],[-0.805,-10.559],[-4.66,-49.835]],"v":[[-212.089,-255.355],[-134.463,-244.093],[-94.007,-197.593],[-89.078,-155.355],[-71.007,-0.5],[-128.482,-21.5],[-193.213,-61.644],[-199.771,-135.061]],"c":true}],"e":[{"i":[[2.758,12.578],[-32.825,-21.239],[0,0],[0,0],[0,0],[34.982,4],[4.713,44.644],[5.969,34.173]],"o":[[-8.911,-40.645],[27.963,18.093],[0,0],[0,0],[0,0],[-14.013,-1.602],[-1.112,-10.531],[-8.965,-51.327]],"v":[[-179.589,-262.855],[-130.463,-262.593],[-96.507,-222.593],[-91.042,-175.962],[-71.007,-5],[-104.982,-18.5],[-144.713,-64.144],[-156.854,-139.358]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[2.758,12.578],[-32.825,-21.239],[0,0],[0,0],[0,0],[34.982,4],[4.713,44.644],[5.969,34.173]],"o":[[-8.911,-40.645],[27.963,18.093],[0,0],[0,0],[0,0],[-14.013,-1.602],[-1.112,-10.531],[-8.965,-51.327]],"v":[[-179.589,-262.855],[-130.463,-262.593],[-96.507,-222.593],[-91.042,-175.962],[-71.007,-5],[-104.982,-18.5],[-144.713,-64.144],[-156.854,-139.358]],"c":true}],"e":[{"i":[[3.235,12.104],[-17.053,-18.911],[0,0],[3.05,20.138],[0,0],[9.544,-4.956],[7.213,61.144],[5.736,34.461]],"o":[[-17.411,-65.145],[14.963,16.593],[0,0],[-3.05,-20.138],[0,0],[-12.518,6.5],[-1.291,-10.944],[-8.055,-48.393]],"v":[[-114.589,-252.855],[-102.463,-279.593],[-83.507,-232.593],[-72.45,-171.862],[-51.007,-10.5],[-65.982,-2.5],[-81.213,-61.644],[-90.187,-141.969]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[3.235,12.104],[-17.053,-18.911],[0,0],[3.05,20.138],[0,0],[9.544,-4.956],[7.213,61.144],[5.736,34.461]],"o":[[-17.411,-65.145],[14.963,16.593],[0,0],[-3.05,-20.138],[0,0],[-12.518,6.5],[-1.291,-10.944],[-8.055,-48.393]],"v":[[-114.589,-252.855],[-102.463,-279.593],[-83.507,-232.593],[-72.45,-171.862],[-51.007,-10.5],[-65.982,-2.5],[-81.213,-61.644],[-90.187,-141.969]],"c":true}],"e":[{"i":[[2.706,12.59],[7.96,-31.787],[0,0],[0,0],[0,0],[-52.303,4.363],[12.713,79.144],[5.528,26.028]],"o":[[-18.411,-85.645],[-8.037,32.093],[0,0],[0,0],[0,0],[17.982,-1.5],[-1.68,-10.456],[-8.303,-39.094]],"v":[[-41.089,-239.855],[-57.963,-260.093],[-97.507,-214.093],[-93.006,-168.319],[-76.507,-0.5],[-13.982,-7.5],[-8.213,-83.644],[-20.289,-143.83]],"c":true}]},{"t":16}]},"nm":"B"},{"ty":"mm","mm":3,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.01,0.24,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":10,"op":15,"st":0,"bm":10,"sr":1},{"ddd":0,"ind":8,"ty":4,"nm":"B blue layer 4","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[3.637,14.035],[-6.562,-18.555],[0,0],[0,0],[0,0],[1.944,58.843],[5.869,46.436],[6.789,29.064]],"o":[[-3.783,-14.599],[12.079,34.152],[0,0],[0,0],[0,0],[-0.327,-9.886],[-2.413,-19.093],[-6.444,-27.588]],"v":[[-100.137,-270.035],[-81.079,-226.152],[-69.301,-172.891],[-65.375,-112.805],[-61.507,1.5],[-65.673,-78.614],[-71.869,-138.936],[-83.556,-205.912]],"c":true}],"e":[{"i":[[1.179,13.587],[-16.316,-11.006],[0,0],[0,0],[0,0],[51.329,28.838],[-0.631,46.436],[1.219,29.821]],"o":[[-1.863,-21.465],[33.579,22.652],[0,0],[0,0],[0,0],[-23.827,-13.386],[0.145,-10.708],[-0.944,-23.088]],"v":[[-180.137,-248.535],[-116.079,-227.152],[-76.301,-169.391],[-71.875,-105.305],[-64.007,0.5],[-127.173,-60.614],[-176.869,-121.436],[-176.556,-181.412]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[1.179,13.587],[-16.316,-11.006],[0,0],[0,0],[0,0],[51.329,28.838],[-0.631,46.436],[1.219,29.821]],"o":[[-1.863,-21.465],[33.579,22.652],[0,0],[0,0],[0,0],[-23.827,-13.386],[0.145,-10.708],[-0.944,-23.088]],"v":[[-180.137,-248.535],[-116.079,-227.152],[-76.301,-169.391],[-71.875,-105.305],[-64.007,0.5],[-127.173,-60.614],[-176.869,-121.436],[-176.556,-181.412]],"c":true}],"e":[{"i":[[0.592,13.423],[-38.037,-14.907],[0,0],[0,0],[0,0],[62.482,13.5],[0.713,41.144],[-1.198,29.377]],"o":[[-0.911,-20.645],[31.01,12.153],[0,0],[0,0],[0,0],[-23.658,-5.112],[-0.183,-10.545],[1.875,-45.981]],"v":[[-210.589,-237.355],[-131.463,-229.593],[-83.507,-180.593],[-80.113,-142.106],[-67.007,-0.5],[-149.982,-38.5],[-213.713,-83.644],[-211.875,-154.519]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[0.592,13.423],[-38.037,-14.907],[0,0],[0,0],[0,0],[62.482,13.5],[0.713,41.144],[-1.198,29.377]],"o":[[-0.911,-20.645],[31.01,12.153],[0,0],[0,0],[0,0],[-23.658,-5.112],[-0.183,-10.545],[1.875,-45.981]],"v":[[-210.589,-237.355],[-131.463,-229.593],[-83.507,-180.593],[-80.113,-142.106],[-67.007,-0.5],[-149.982,-38.5],[-213.713,-83.644],[-211.875,-154.519]],"c":true}],"e":[{"i":[[0.592,13.423],[-38.037,-14.907],[0,0],[0,0],[0,0],[49.59,6.194],[2.062,42.216],[0.844,29.39]],"o":[[-0.911,-20.645],[31.01,12.153],[0,0],[0,0],[0,0],[-24.018,-3],[-0.514,-10.534],[-1.267,-44.142]],"v":[[-219.089,-243.355],[-134.463,-234.593],[-89.007,-187.093],[-84.613,-147.106],[-68.507,-0.5],[-150.482,-30.5],[-213.713,-68.644],[-215.875,-135.019]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[0.592,13.423],[-38.037,-14.907],[0,0],[0,0],[0,0],[49.59,6.194],[2.062,42.216],[0.844,29.39]],"o":[[-0.911,-20.645],[31.01,12.153],[0,0],[0,0],[0,0],[-24.018,-3],[-0.514,-10.534],[-1.267,-44.142]],"v":[[-219.089,-243.355],[-134.463,-234.593],[-89.007,-187.093],[-84.613,-147.106],[-68.507,-0.5],[-150.482,-30.5],[-213.713,-68.644],[-215.875,-135.019]],"c":true}],"e":[{"i":[[1.979,12.724],[-39.037,-17.407],[0,0],[0,0],[0,0],[49.482,7],[3.213,42.144],[3.102,33.18]],"o":[[-3.911,-25.145],[30.419,13.564],[0,0],[0,0],[0,0],[-13.966,-1.976],[-0.805,-10.559],[-4.66,-49.835]],"v":[[-212.089,-255.355],[-134.463,-244.093],[-94.007,-197.593],[-89.078,-155.355],[-71.007,-0.5],[-128.482,-21.5],[-193.213,-61.644],[-199.771,-135.061]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[1.979,12.724],[-39.037,-17.407],[0,0],[0,0],[0,0],[49.482,7],[3.213,42.144],[3.102,33.18]],"o":[[-3.911,-25.145],[30.419,13.564],[0,0],[0,0],[0,0],[-13.966,-1.976],[-0.805,-10.559],[-4.66,-49.835]],"v":[[-212.089,-255.355],[-134.463,-244.093],[-94.007,-197.593],[-89.078,-155.355],[-71.007,-0.5],[-128.482,-21.5],[-193.213,-61.644],[-199.771,-135.061]],"c":true}],"e":[{"i":[[2.758,12.578],[-32.825,-21.239],[0,0],[0,0],[0,0],[34.982,4],[4.713,44.644],[5.969,34.173]],"o":[[-8.911,-40.645],[27.963,18.093],[0,0],[0,0],[0,0],[-14.013,-1.602],[-1.112,-10.531],[-8.965,-51.327]],"v":[[-179.589,-262.855],[-130.463,-262.593],[-96.507,-222.593],[-91.042,-175.962],[-71.007,-5],[-104.982,-18.5],[-144.713,-64.144],[-156.854,-139.358]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[2.758,12.578],[-32.825,-21.239],[0,0],[0,0],[0,0],[34.982,4],[4.713,44.644],[5.969,34.173]],"o":[[-8.911,-40.645],[27.963,18.093],[0,0],[0,0],[0,0],[-14.013,-1.602],[-1.112,-10.531],[-8.965,-51.327]],"v":[[-179.589,-262.855],[-130.463,-262.593],[-96.507,-222.593],[-91.042,-175.962],[-71.007,-5],[-104.982,-18.5],[-144.713,-64.144],[-156.854,-139.358]],"c":true}],"e":[{"i":[[3.235,12.104],[-17.053,-18.911],[0,0],[-3.55,-45.138],[0,0],[4.732,-1.5],[7.213,61.144],[5.736,34.461]],"o":[[-17.411,-65.145],[14.963,16.593],[0,0],[3.168,40.285],[0,0],[-13.445,4.262],[-1.291,-10.944],[-8.055,-48.393]],"v":[[-114.589,-252.855],[-102.463,-279.593],[-83.507,-232.593],[-73.45,-164.862],[-61.507,-3],[-68.482,-0.25],[-81.213,-61.644],[-90.187,-141.969]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[3.235,12.104],[-17.053,-18.911],[0,0],[-3.55,-45.138],[0,0],[4.732,-1.5],[7.213,61.144],[5.736,34.461]],"o":[[-17.411,-65.145],[14.963,16.593],[0,0],[3.168,40.285],[0,0],[-13.445,4.262],[-1.291,-10.944],[-8.055,-48.393]],"v":[[-114.589,-252.855],[-102.463,-279.593],[-83.507,-232.593],[-73.45,-164.862],[-61.507,-3],[-68.482,-0.25],[-81.213,-61.644],[-90.187,-141.969]],"c":true}],"e":[{"i":[[2.706,12.59],[7.96,-31.787],[0,0],[0,0],[0,0],[-52.303,4.363],[12.713,79.144],[5.528,26.028]],"o":[[-18.411,-85.645],[-8.037,32.093],[0,0],[0,0],[0,0],[17.982,-1.5],[-1.68,-10.456],[-8.303,-39.094]],"v":[[-41.089,-239.855],[-57.963,-260.093],[-97.507,-214.093],[-93.006,-168.319],[-76.507,-0.5],[-13.982,-7.5],[-8.213,-83.644],[-20.289,-143.83]],"c":true}]},{"t":16}]},"nm":"B"},{"ty":"mm","mm":3,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.01,0.24,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":9,"op":16,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":9,"ty":4,"nm":"B blue layer 3","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[0,0],[0,0],[20.982,16]],"o":[[0,0],[0,0],[-11.216,-8.553]],"v":[[-78.007,-37.093],[-72.507,0.5],[-84.482,-27.5]],"c":true}},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.01,0.24,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":14,"op":15,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":10,"ty":3,"nm":"Null smoke","parent":34,"ks":{"o":{"k":0},"r":{"k":26.875},"p":{"k":[{"i":{"x":0.663,"y":0.933},"o":{"x":0.334,"y":0.066},"n":"0p663_0p933_0p334_0p066","t":9,"s":[269.705,292.352,0],"e":[250.705,285.352,0],"to":[-0.5,-0.33333334326744,0],"ti":[-0.60055363178253,-0.48845085501671,0]},{"i":{"x":0.685,"y":1},"o":{"x":0.334,"y":0.228},"n":"0p685_1_0p334_0p228","t":11,"s":[250.705,285.352,0],"e":[276,308,0],"to":[9.00963115692139,7.32784128189087,0],"ti":[-0.48727434873581,0,0]},{"t":22.5016243755817}]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"ip":10.0000001490116,"op":24.4098640978336,"st":-11.1670814454556,"bm":0,"sr":0.99000012874603},{"ddd":0,"ind":11,"ty":3,"nm":"Null smoke","parent":34,"ks":{"o":{"k":0},"r":{"k":-21.596},"p":{"k":[{"i":{"x":0.58,"y":0.829},"o":{"x":0.278,"y":0.114},"n":"0p58_0p829_0p278_0p114","t":9,"s":[217.311,226.631,0],"e":[198.311,219.631,0],"to":[0,0,0],"ti":[-3.21461248397827,3.18686246871948,0]},{"i":{"x":0.726,"y":1},"o":{"x":0.278,"y":0.371},"n":"0p726_1_0p278_0p371","t":11,"s":[198.311,219.631,0],"e":[223,195,0],"to":[8.70546340942383,-8.63031387329102,0],"ti":[-1.45636057853699,0,0]},{"t":21.5996034443378}]},"a":{"k":[0,0,0]},"s":{"k":[100,-100,100]}},"ao":0,"ip":10.0000001490116,"op":24.4098640978336,"st":-11.1670814454556,"bm":0,"sr":0.99000012874603},{"ddd":0,"ind":12,"ty":3,"nm":"null smoke","parent":34,"ks":{"o":{"k":0},"r":{"k":16.774},"p":{"k":[{"i":{"x":0.577,"y":0.847},"o":{"x":0.284,"y":0.103},"n":"0p577_0p847_0p284_0p103","t":9,"s":[261.182,221.23,0],"e":[242.182,214.23,0],"to":[0,0,0],"ti":[-3.1457417011261,0.33704376220703,0]},{"i":{"x":0.697,"y":1},"o":{"x":0.284,"y":0.405},"n":"0p697_1_0p284_0p405","t":11,"s":[242.182,214.23,0],"e":[263,212,0],"to":[7.38616800308228,-0.79137516021729,0],"ti":[-3.2727952003479,0.3506566286087,0]},{"t":18.9003057777882}]},"a":{"k":[0,0,0]},"s":{"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":9.9,"s":[100,100,100],"e":[122,122,100]},{"t":20.7004822790623}]}},"ao":0,"ip":10.0000001490116,"op":24.4098640978336,"st":-11.1670814454556,"bm":0,"sr":0.99000012874603},{"ddd":0,"ind":13,"ty":3,"nm":"Null 26","parent":34,"ks":{"o":{"k":0},"r":{"k":-11.144},"p":{"k":[{"i":{"x":0.573,"y":0.757},"o":{"x":0.294,"y":0.167},"n":"0p573_0p757_0p294_0p167","t":9,"s":[267.556,276.269,0],"e":[248.556,269.269,0],"to":[0,0,0],"ti":[-4.61654806137085,2.50612592697144,0]},{"i":{"x":0.699,"y":1},"o":{"x":0.294,"y":0.426},"n":"0p699_1_0p294_0p426","t":11,"s":[248.556,269.269,0],"e":[273,256,0],"to":[9.04685020446777,-4.91114711761475,0],"ti":[-3.86238408088684,2.09672284126282,0]},{"t":17.4897499382496}]},"a":{"k":[0,0,0]},"s":{"k":[100,-100,100]}},"ao":0,"ip":10.0000001490116,"op":21.5510979294777,"st":-11.1670814454556,"bm":0,"sr":0.99000012874603},{"ddd":0,"ind":14,"ty":4,"nm":"B blue layer 2","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":1,"s":[{"i":[[-1.24,5.104],[0.198,1.892],[0,0],[0,0],[0,0],[-0.804,-2.124],[0.343,-5.77],[-1.462,6.091]],"o":[[1.049,-4.319],[-0.297,-2.847],[0,0],[0,0],[0,0],[0.386,1.02],[0.093,-2.77],[0.816,-3.399]],"v":[[-22.001,-21.919],[-20.415,-30.453],[-22.49,-31.96],[-27.759,-19.513],[-32.548,3.666],[-29.736,4.974],[-28.593,7.27],[-25.53,-6.238]],"c":true}],"e":[{"i":[[-1.032,7.042],[1.743,2.453],[0,0],[0,0],[0,0],[-3.84,-2.615],[-0.171,-9.84],[-1.176,8.399]],"o":[[0.873,-5.959],[-2.624,-3.692],[0,0],[0,0],[0,0],[1.845,1.256],[2.053,-13.363],[0.656,-4.688]],"v":[[-19.587,-23.915],[-19.439,-35.572],[-25.627,-39.562],[-30.78,-20.831],[-37.568,3.797],[-29.696,6.026],[-25.346,16.687],[-22.197,-8.804]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":2,"s":[{"i":[[-1.032,7.042],[1.743,2.453],[0,0],[0,0],[0,0],[-3.84,-2.615],[-0.171,-9.84],[-1.176,8.399]],"o":[[0.873,-5.959],[-2.624,-3.692],[0,0],[0,0],[0,0],[1.845,1.256],[2.053,-13.363],[0.656,-4.688]],"v":[[-19.587,-23.915],[-19.439,-35.572],[-25.627,-39.562],[-30.78,-20.831],[-37.568,3.797],[-29.696,6.026],[-25.346,16.687],[-22.197,-8.804]],"c":true}],"e":[{"i":[[-0.284,9.426],[4.036,2.935],[0,0],[0,0],[0,0],[-7.528,-4.291],[-1.775,-13.654],[-0.23,11.234]],"o":[[0.241,-7.977],[-6.074,-4.417],[0,0],[0,0],[0,0],[6.05,3.449],[0.725,-17.904],[0.128,-6.27]],"v":[[-11.741,-32.523],[-17.536,-48.935],[-30.445,-51.84],[-35.936,-26.366],[-41.832,3.676],[-25.8,5.301],[-13.725,26.404],[-12.128,-11.48]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":3,"s":[{"i":[[-0.284,9.426],[4.036,2.935],[0,0],[0,0],[0,0],[-7.528,-4.291],[-1.775,-13.654],[-0.23,11.234]],"o":[[0.241,-7.977],[-6.074,-4.417],[0,0],[0,0],[0,0],[6.05,3.449],[0.725,-17.904],[0.128,-6.27]],"v":[[-11.741,-32.523],[-17.536,-48.935],[-30.445,-51.84],[-35.936,-26.366],[-41.832,3.676],[-25.8,5.301],[-13.725,26.404],[-12.128,-11.48]],"c":true}],"e":[{"i":[[0.512,9.416],[12.036,5.935],[0,0],[0,0],[0,0],[-10.429,-3.711],[-2.775,-15.904],[1.665,20.58]],"o":[[-0.759,-13.977],[-8.794,-4.337],[0,0],[0,0],[0,0],[11.8,4.199],[-1.275,-18.904],[-0.872,-10.77]],"v":[[-0.741,-42.023],[-15.536,-66.935],[-34.945,-68.34],[-40.936,-29.866],[-44.332,2.676],[-17.3,1.801],[4.275,28.904],[1.372,-13.73]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":4,"s":[{"i":[[0.512,9.416],[12.036,5.935],[0,0],[0,0],[0,0],[-10.429,-3.711],[-2.775,-15.904],[1.665,20.58]],"o":[[-0.759,-13.977],[-8.794,-4.337],[0,0],[0,0],[0,0],[11.8,4.199],[-1.275,-18.904],[-0.872,-10.77]],"v":[[-0.741,-42.023],[-15.536,-66.935],[-34.945,-68.34],[-40.936,-29.866],[-44.332,2.676],[-17.3,1.801],[4.275,28.904],[1.372,-13.73]],"c":true}],"e":[{"i":[[2.118,9.189],[13.168,3.137],[0,0],[0,0],[0,0],[-21.716,-6.523],[-3.775,-12.404],[3.122,20.41]],"o":[[-4.259,-18.477],[-14.964,-3.565],[0,0],[0,0],[0,0],[22.3,6.699],[-2.775,-18.904],[-2.872,-18.77]],"v":[[12.259,-65.023],[-14.536,-90.935],[-40.945,-84.34],[-43.436,-51.366],[-48.332,2.176],[-5.8,-5.199],[26.275,28.404],[16.372,-36.73]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[2.118,9.189],[13.168,3.137],[0,0],[0,0],[0,0],[-21.716,-6.523],[-3.775,-12.404],[3.122,20.41]],"o":[[-4.259,-18.477],[-14.964,-3.565],[0,0],[0,0],[0,0],[22.3,6.699],[-2.775,-18.904],[-2.872,-18.77]],"v":[[12.259,-65.023],[-14.536,-90.935],[-40.945,-84.34],[-43.436,-51.366],[-48.332,2.176],[-5.8,-5.199],[26.275,28.404],[16.372,-36.73]],"c":true}],"e":[{"i":[[2.118,9.189],[13.533,0.289],[0,0],[0,0],[0,0],[-54.798,-20.458],[-2.775,-7.904],[4.128,20.23]],"o":[[-4.259,-18.477],[-26.464,-0.565],[0,0],[0,0],[0,0],[23.3,8.699],[-0.275,-14.904],[-3.009,-14.743]],"v":[[31.259,-89.523],[-7.536,-119.435],[-46.945,-102.34],[-48.936,-56.366],[-51.832,2.676],[27.2,-12.199],[54.275,22.904],[40.872,-40.73]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[2.118,9.189],[13.533,0.289],[0,0],[0,0],[0,0],[-54.798,-20.458],[-2.775,-7.904],[4.128,20.23]],"o":[[-4.259,-18.477],[-26.464,-0.565],[0,0],[0,0],[0,0],[23.3,8.699],[-0.275,-14.904],[-3.009,-14.743]],"v":[[31.259,-89.523],[-7.536,-119.435],[-46.945,-102.34],[-48.936,-56.366],[-51.832,2.676],[27.2,-12.199],[54.275,22.904],[40.872,-40.73]],"c":true}],"e":[{"i":[[2.741,9.023],[35.536,-9.065],[0,0],[0,0],[0,0],[-58.271,5.078],[7.345,30.067],[7.32,28.81]],"o":[[-6.003,-19.766],[-38.857,9.912],[0,0],[0,0],[0,0],[72.3,-6.301],[-3.275,-13.404],[-3.372,-13.27]],"v":[[77.259,-132.023],[-1.536,-158.935],[-53.945,-122.34],[-54.436,-66.866],[-54.832,1.176],[23.7,-42.699],[103.275,-31.596],[86.872,-91.23]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[2.741,9.023],[35.536,-9.065],[0,0],[0,0],[0,0],[-58.271,5.078],[7.345,30.067],[7.32,28.81]],"o":[[-6.003,-19.766],[-38.857,9.912],[0,0],[0,0],[0,0],[72.3,-6.301],[-3.275,-13.404],[-3.372,-13.27]],"v":[[77.259,-132.023],[-1.536,-158.935],[-53.945,-122.34],[-54.436,-66.866],[-54.832,1.176],[23.7,-42.699],[103.275,-31.596],[86.872,-91.23]],"c":true}],"e":[{"i":[[2.692,12.158],[14.536,-18.065],[0,0],[0,0],[0,0],[-45.886,36.274],[7.225,30.096],[8.494,27.268]],"o":[[-3.759,-16.977],[-25.14,31.242],[0,0],[0,0],[0,0],[35.8,-28.301],[-2.392,-9.964],[-7.872,-25.27]],"v":[[26.759,-245.023],[-23.036,-213.935],[-60.445,-143.84],[-59.936,-99.866],[-58.832,1.676],[-5.3,-69.199],[64.275,-130.596],[40.372,-201.73]],"c":true}]},{"t":8}]},"nm":"B"},{"ty":"mm","mm":2,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.01,0.24,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":1,"op":9,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":15,"ty":4,"nm":"B orange layer 3 shadow 2","parent":34,"ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16,"s":[5],"e":[40]},{"t":22}]},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-99,-265],[-121,-264],[-133,-219],[-107,10],[-84,33],[-49,13],[-62,-108]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-70,-275],[-92,-274],[-104,-229],[-78,0],[-55,23],[-20,3],[-33,-118]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-70,-275],[-92,-274],[-104,-229],[-78,0],[-55,23],[-20,3],[-33,-118]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-25.168,-278.566],[-92,-274],[-104,-229],[-78,0],[-55,23],[22.285,0.453],[-2.338,-146.717]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-25.168,-278.566],[-92,-274],[-104,-229],[-78,0],[-55,23],[22.285,0.453],[-2.338,-146.717]],"c":true}],"e":[{"i":[[16.753,14.499],[0,0],[0,0],[0,0],[0,0],[-3,10],[21.96,31.036]],"o":[[-16.753,-14.499],[0,0],[0,0],[0,0],[0,0],[3,-10],[2.96,-38.964]],"v":[[19.753,-282.501],[-92,-274],[-104,-229],[-78,0],[-55,23],[64.682,-1.606],[39.04,-141.036]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[16.753,14.499],[0,0],[0,0],[0,0],[0,0],[-3,10],[21.96,31.036]],"o":[[-16.753,-14.499],[0,0],[0,0],[0,0],[0,0],[3,-10],[2.96,-38.964]],"v":[[19.753,-282.501],[-92,-274],[-104,-229],[-78,0],[-55,23],[64.682,-1.606],[39.04,-141.036]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[24,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[12,-31]],"v":[[67,-296],[-92,-274],[-104,-229],[-78,0],[-55,23],[110,9],[57,-143]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[24,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[12,-31]],"v":[[67,-296],[-92,-274],[-104,-229],[-78,0],[-55,23],[110,9],[57,-143]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[60,-286],[-92,-274],[-104,-229],[-78,0],[-55,23],[104,4],[115,-139]],"c":true}]},{"t":19}]},"o":{"k":100},"x":{"k":0},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[1.759,26.037],[0.589,9.355],[8.227,2.939],[0,0],[0,0],[-17.127,-8.576],[-4.287,-8.856]],"o":[[-1.241,-21.463],[-0.382,-6.06],[-9.537,-3.407],[0,0],[0,0],[11.982,6],[-1.787,-26.856]],"v":[[-12.759,-43.537],[-15.089,-87.355],[-25.963,-106.093],[-46.507,-102.093],[-51.007,3.5],[-21.482,0],[-8.713,17.856]],"c":true}],"e":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}],"e":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}],"e":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}],"e":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-73.259,-145.537],[-82.089,-178.355],[-90.963,-174.593],[-83.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-73.259,-145.537],[-82.089,-178.355],[-90.963,-174.593],[-83.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-87.007,-159.593],[-69.007,-0.5],[-59.482,-33.5],[-71.713,-94.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-87.007,-159.593],[-69.007,-0.5],[-59.482,-33.5],[-71.713,-94.644]],"c":true}],"e":[{"i":[[2.759,16.037],[1.089,6.855],[3.15,2.884],[0,0],[0,0],[0.024,23.358],[2.213,13.144]],"o":[[-0.741,-7.963],[-2.92,-18.378],[-1.537,-1.407],[0,0],[0,0],[-0.018,-17.5],[-2.755,-16.362]],"v":[[-74.759,-145.537],[-84.089,-185.855],[-90.463,-219.593],[-95.507,-208.093],[-71.507,0.5],[-51.482,-32],[-65.713,-94.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[2.759,16.037],[1.089,6.855],[3.15,2.884],[0,0],[0,0],[0.024,23.358],[2.213,13.144]],"o":[[-0.741,-7.963],[-2.92,-18.378],[-1.537,-1.407],[0,0],[0,0],[-0.018,-17.5],[-2.755,-16.362]],"v":[[-74.759,-145.537],[-84.089,-185.855],[-90.463,-219.593],[-95.507,-208.093],[-71.507,0.5],[-51.482,-32],[-65.713,-94.144]],"c":true}],"e":[{"i":[[8.509,31.287],[6.813,20.34],[4.963,-11.907],[0,0],[0,0],[-17.018,16],[9.213,50.737]],"o":[[-6.241,-30.463],[-5.911,-17.645],[-6.06,14.54],[0,0],[0,0],[9.992,-9.394],[-3.787,-20.856]],"v":[[-51.259,-158.537],[-71.589,-238.355],[-80.963,-225.093],[-96.007,-202.593],[-72.007,0],[-29.482,-25.5],[-34.213,-88.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[8.509,31.287],[6.813,20.34],[4.963,-11.907],[0,0],[0,0],[-17.018,16],[9.213,50.737]],"o":[[-6.241,-30.463],[-5.911,-17.645],[-6.06,14.54],[0,0],[0,0],[9.992,-9.394],[-3.787,-20.856]],"v":[[-51.259,-158.537],[-71.589,-238.355],[-80.963,-225.093],[-96.007,-202.593],[-72.007,0],[-29.482,-25.5],[-34.213,-88.644]],"c":true}],"e":[{"i":[[8.509,31.287],[4.772,20.913],[18.771,-35.562],[0,0],[0,0],[-35.294,7.846],[9.713,50.644]],"o":[[-6.241,-30.463],[-20.911,-91.645],[-14.037,26.593],[0,0],[0,0],[44.982,-10],[-4.991,-26.022]],"v":[[-6.759,-153.537],[-31.089,-246.855],[-59.463,-246.093],[-97.007,-208.093],[-74.007,0.5],[-18.482,-13.5],[11.287,-77.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[8.509,31.287],[4.772,20.913],[18.771,-35.562],[0,0],[0,0],[-35.294,7.846],[9.713,50.644]],"o":[[-6.241,-30.463],[-20.911,-91.645],[-14.037,26.593],[0,0],[0,0],[44.982,-10],[-4.991,-26.022]],"v":[[-6.759,-153.537],[-31.089,-246.855],[-59.463,-246.093],[-97.007,-208.093],[-74.007,0.5],[-18.482,-13.5],[11.287,-77.644]],"c":true}],"e":[{"i":[[9.759,27.037],[3.56,21.153],[32.297,-24.991],[0,0],[0,0],[-51.855,8.105],[16.213,81.144]],"o":[[-6.241,-30.463],[-13.911,-82.645],[-39.537,30.593],[0,0],[0,0],[47.982,-7.5],[-5.191,-25.982]],"v":[[27.241,-164.537],[14.411,-234.855],[-36.963,-250.093],[-97.507,-214.093],[-76.507,-0.5],[0.018,-9],[47.287,-86.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[9.759,27.037],[3.56,21.153],[32.297,-24.991],[0,0],[0,0],[-51.855,8.105],[16.213,81.144]],"o":[[-6.241,-30.463],[-13.911,-82.645],[-39.537,30.593],[0,0],[0,0],[47.982,-7.5],[-5.191,-25.982]],"v":[[27.241,-164.537],[14.411,-234.855],[-36.963,-250.093],[-97.507,-214.093],[-76.507,-0.5],[0.018,-9],[47.287,-86.144]],"c":true}],"e":[{"i":[[9.759,25.037],[3.589,22.355],[46.963,-21.907],[0,0],[0,0],[-52.444,2.078],[5.958,46.178]],"o":[[-1.241,-27.463],[-10.199,-63.523],[-35.55,16.583],[0,0],[0,0],[50.482,-2],[-5.787,-44.856]],"v":[[49.241,-157.537],[45.911,-224.855],[-24.963,-243.593],[-95.007,-217.593],[-76.507,-0.5],[33.018,-7],[72.287,-80.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[9.759,25.037],[3.589,22.355],[46.963,-21.907],[0,0],[0,0],[-52.444,2.078],[5.958,46.178]],"o":[[-1.241,-27.463],[-10.199,-63.523],[-35.55,16.583],[0,0],[0,0],[50.482,-2],[-5.787,-44.856]],"v":[[49.241,-157.537],[45.911,-224.855],[-24.963,-243.593],[-95.007,-217.593],[-76.507,-0.5],[33.018,-7],[72.287,-80.144]],"c":true}],"e":[{"i":[[15.759,21.037],[2.598,21.286],[45.963,-10.657],[0,0],[0,0],[-28.018,1],[5.463,51.394]],"o":[[3.509,-24.213],[-4.161,-64.895],[-38.537,12.343],[0,0],[0,0],[59.982,2.5],[-3.676,-32.795]],"v":[[59.241,-140.287],[64.661,-201.605],[-11.463,-241.343],[-91.757,-220.343],[-77.257,-0.75],[28.018,-4.5],[84.787,-81.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[15.759,21.037],[2.598,21.286],[45.963,-10.657],[0,0],[0,0],[-28.018,1],[5.463,51.394]],"o":[[3.509,-24.213],[-4.161,-64.895],[-38.537,12.343],[0,0],[0,0],[59.982,2.5],[-3.676,-32.795]],"v":[[59.241,-140.287],[64.661,-201.605],[-11.463,-241.343],[-91.757,-220.343],[-77.257,-0.75],[28.018,-4.5],[84.787,-81.144]],"c":true}],"e":[{"i":[[21.759,21.537],[2.058,21.352],[37.463,-5.907],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.749,6.11],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[62.241,-133.037],[74.911,-192.355],[-0.463,-236.593],[-88.507,-223.093],[-78.007,-1],[23.018,-2.5],[92.287,-70.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[21.759,21.537],[2.058,21.352],[37.463,-5.907],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.749,6.11],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[62.241,-133.037],[74.911,-192.355],[-0.463,-236.593],[-88.507,-223.093],[-78.007,-1],[23.018,-2.5],[92.287,-70.144]],"c":true}],"e":[{"i":[[24.259,15.287],[0.089,22.605],[44.213,-5.407],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[11.759,-13.713],[-2.705,-47.408],[-24.787,2.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[78.411,-180.605],[5.787,-233.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[24.259,15.287],[0.089,22.605],[44.213,-5.407],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[11.759,-13.713],[-2.705,-47.408],[-24.787,2.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[78.411,-180.605],[5.787,-233.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":22.253,"s":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[37.766,0],[0,0],[0,0],[0,0],[0,41.089]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[37.463,0],[0,-29.004]],"v":[[55.241,-119.037],[79.411,-164.355],[12.037,-226.593],[-79.507,-226.593],[-79.507,0],[23.518,0],[90.287,-63.144]],"c":true}]},{"t":24}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[-1.324,-11.343],[1.439,-10.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[3.112,-1.724]],"v":[[-264.603,-181.739],[-262.939,-148.686],[-267.481,-144.898],[-273.154,-192.488],[-271.112,-195.276]],"c":true}],"e":[{"i":[[-1.324,-11.343],[1.439,-10.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[3.112,-1.724]],"v":[[-60.603,-177.739],[-58.939,-144.686],[-63.481,-140.898],[-69.154,-188.488],[-67.112,-191.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[-1.324,-11.343],[1.439,-10.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[3.112,-1.724]],"v":[[-60.603,-177.739],[-58.939,-144.686],[-63.481,-140.898],[-69.154,-188.488],[-67.112,-191.276]],"c":true}],"e":[{"i":[[-1.324,-11.343],[4.939,-8.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[6.612,-3.724]],"v":[[-40.603,-178.239],[-44.439,-146.186],[-54.981,-139.898],[-61.654,-188.488],[-53.612,-193.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[-1.324,-11.343],[4.939,-8.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[6.612,-3.724]],"v":[[-40.603,-178.239],[-44.439,-146.186],[-54.981,-139.898],[-61.654,-188.488],[-53.612,-193.776]],"c":true}],"e":[{"i":[[-0.897,-12.761],[7.439,-4.314],[0,0],[0,0],[0,0]],"o":[[1.049,14.93],[0,0],[0,0],[0,0],[11.112,-4.224]],"v":[[-22.103,-175.239],[-29.439,-144.686],[-49.481,-139.398],[-53.654,-189.988],[-40.112,-193.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[-0.897,-12.761],[7.439,-4.314],[0,0],[0,0],[0,0]],"o":[[1.049,14.93],[0,0],[0,0],[0,0],[11.112,-4.224]],"v":[[-22.103,-175.239],[-29.439,-144.686],[-49.481,-139.398],[-53.654,-189.988],[-40.112,-193.776]],"c":true}],"e":[{"i":[[-2.295,-14.342],[14.615,-7.06],[0,0],[0,0],[0,0]],"o":[[2.247,14.043],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[-5.749,-174.483],[-19.893,-141.684],[-44.391,-137.152],[-47.397,-188.494],[-26.535,-193.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[-2.295,-14.342],[14.615,-7.06],[0,0],[0,0],[0,0]],"o":[[2.247,14.043],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[-5.749,-174.483],[-19.893,-141.684],[-44.391,-137.152],[-47.397,-188.494],[-26.535,-193.276]],"c":true}],"e":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[7.397,-171.739],[-12.939,-138.686],[-40.981,-136.898],[-43.154,-187.488],[-17.112,-191.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[7.397,-171.739],[-12.939,-138.686],[-40.981,-136.898],[-43.154,-187.488],[-17.112,-191.276]],"c":true}],"e":[{"i":[[-1.534,-14.395],[17.58,-1.876],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.855,-1.149]],"v":[[16.498,-167.632],[-5.273,-137.269],[-37.547,-135.91],[-39.662,-187.025],[-9.278,-190.383]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[-1.534,-14.395],[17.58,-1.876],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.855,-1.149]],"v":[[16.498,-167.632],[-5.273,-137.269],[-37.547,-135.91],[-39.662,-187.025],[-9.278,-190.383]],"c":true}],"e":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[24.639,-165.025],[-0.738,-135.852],[-35.862,-134.172],[-36.92,-187.061],[-2.991,-188.991]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[24.639,-165.025],[-0.738,-135.852],[-35.862,-134.172],[-36.92,-187.061],[-2.991,-188.991]],"c":true}],"e":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[28.279,-161.917],[4.297,-134.435],[-34.177,-133.435],[-34.177,-187.098],[3.297,-187.598]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[28.279,-161.917],[4.297,-134.435],[-34.177,-133.435],[-34.177,-187.098],[3.297,-187.598]],"c":true}],"e":[{"i":[[0,-14.502],[16.037,-0.065],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[31.613,-160.417],[4.963,-133.935],[-33.677,-133.268],[-33.677,-186.264],[4.297,-186.598]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":22.253,"s":[{"i":[[0,-14.502],[16.037,-0.065],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[31.613,-160.417],[4.963,-133.935],[-33.677,-133.268],[-33.677,-186.264],[4.297,-186.598]],"c":true}],"e":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[32.279,-158.917],[6.297,-132.935],[-32.677,-132.935],[-32.677,-184.598],[6.297,-184.598]],"c":true}]},{"t":24}]},"nm":"B"},{"ind":2,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[2.931,-7.005],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[2.931,0.658],[2.344,19.176]],"v":[[-262.931,-52.995],[-268.677,-50.995],[-274.177,-102.658],[-270.431,-105.658],[-264.344,-84.176]],"c":true}],"e":[{"i":[[2.931,-7.005],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[2.931,0.658],[2.344,19.176]],"v":[[-46.431,-52.995],[-52.177,-50.995],[-57.677,-102.658],[-53.931,-105.658],[-47.844,-84.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[2.931,-7.005],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[2.931,0.658],[2.344,19.176]],"v":[[-46.431,-52.995],[-52.177,-50.995],[-57.677,-102.658],[-53.931,-105.658],[-47.844,-84.176]],"c":true}],"e":[{"i":[[7.931,-3.505],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.344,19.176]],"v":[[-32.931,-51.495],[-45.677,-48.995],[-51.177,-100.658],[-40.431,-104.658],[-26.844,-83.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[7.931,-3.505],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.344,19.176]],"v":[[-32.931,-51.495],[-45.677,-48.995],[-51.177,-100.658],[-40.431,-104.658],[-26.844,-83.176]],"c":true}],"e":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.589,13.962]],"v":[[-19.931,-48.995],[-40.177,-47.495],[-45.177,-99.158],[-24.431,-103.158],[-6.344,-82.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.589,13.962]],"v":[[-19.931,-48.995],[-40.177,-47.495],[-45.177,-99.158],[-24.431,-103.158],[-6.344,-82.676]],"c":true}],"e":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[15.681,-1.092],[2.589,13.962]],"v":[[-7.681,-46.495],[-37.927,-44.245],[-41.177,-97.908],[-14.681,-101.408],[8.906,-80.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[15.681,-1.092],[2.589,13.962]],"v":[[-7.681,-46.495],[-37.927,-44.245],[-41.177,-97.908],[-14.681,-101.408],[8.906,-80.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[2.589,13.962]],"v":[[-0.931,-44.995],[-36.677,-42.995],[-39.177,-97.158],[-3.931,-100.158],[21.656,-76.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[2.589,13.962]],"v":[[-0.931,-44.995],[-36.677,-42.995],[-39.177,-97.158],[-3.931,-100.158],[21.656,-76.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-1.77,-14.283]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.726,14.041]],"v":[[7.902,-44.495],[-34.344,-44.162],[-35.844,-96.158],[5.902,-97.825],[29.989,-73.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-1.77,-14.283]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.726,14.041]],"v":[[7.902,-44.495],[-34.344,-44.162],[-35.844,-96.158],[5.902,-97.825],[29.989,-73.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[12.235,-42.745],[-33.761,-42.329],[-34.761,-95.908],[11.235,-96.242],[37.322,-69.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[12.235,-42.745],[-33.761,-42.329],[-34.761,-95.908],[11.235,-96.242],[37.322,-69.926]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[13.569,-41.995],[-32.677,-41.995],[-33.677,-95.658],[16.569,-94.658],[40.656,-68.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[13.569,-41.995],[-32.677,-41.995],[-33.677,-95.658],[16.569,-94.658],[40.656,-68.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[16.569,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-94.658],[43.156,-67.676]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":22.253,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[16.569,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-94.658],[43.156,-67.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[16.569,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-93.658],[43.156,-67.676]],"c":true}]},{"t":24}]},"nm":"B"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.86,0.86,0.86,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":15,"op":23,"st":0,"bm":10,"sr":1},{"ddd":0,"ind":16,"ty":4,"nm":"B orange layer 3 shadow","parent":34,"ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16,"s":[5],"e":[40]},{"t":22}]},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-99,-265],[-121,-264],[-133,-219],[-107,10],[-84,33],[-49,13],[-62,-108]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-70,-275],[-92,-274],[-104,-229],[-78,0],[-55,23],[-20,3],[-33,-118]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-70,-275],[-92,-274],[-104,-229],[-78,0],[-55,23],[-20,3],[-33,-118]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-25.168,-278.566],[-92,-274],[-104,-229],[-78,0],[-55,23],[22.285,0.453],[-2.338,-146.717]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[-25.168,-278.566],[-92,-274],[-104,-229],[-78,0],[-55,23],[22.285,0.453],[-2.338,-146.717]],"c":true}],"e":[{"i":[[16.753,14.499],[0,0],[0,0],[0,0],[0,0],[-3,10],[21.96,31.036]],"o":[[-16.753,-14.499],[0,0],[0,0],[0,0],[0,0],[3,-10],[2.96,-38.964]],"v":[[19.753,-282.501],[-92,-274],[-104,-229],[-78,0],[-55,23],[64.682,-1.606],[39.04,-141.036]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[16.753,14.499],[0,0],[0,0],[0,0],[0,0],[-3,10],[21.96,31.036]],"o":[[-16.753,-14.499],[0,0],[0,0],[0,0],[0,0],[3,-10],[2.96,-38.964]],"v":[[19.753,-282.501],[-92,-274],[-104,-229],[-78,0],[-55,23],[64.682,-1.606],[39.04,-141.036]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[24,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[12,-31]],"v":[[67,-296],[-92,-274],[-104,-229],[-78,0],[-55,23],[110,9],[57,-143]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[24,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[12,-31]],"v":[[67,-296],[-92,-274],[-104,-229],[-78,0],[-55,23],[110,9],[57,-143]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-3,10],[4,29]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[3,-10],[-4,-29]],"v":[[60,-286],[-92,-274],[-104,-229],[-78,0],[-55,23],[104,4],[115,-139]],"c":true}]},{"t":19}]},"o":{"k":100},"x":{"k":0},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[1.759,26.037],[0.589,9.355],[8.227,2.939],[0,0],[0,0],[-17.127,-8.576],[-4.287,-8.856]],"o":[[-1.241,-21.463],[-0.382,-6.06],[-9.537,-3.407],[0,0],[0,0],[11.982,6],[-1.787,-26.856]],"v":[[-12.759,-43.537],[-15.089,-87.355],[-25.963,-106.093],[-46.507,-102.093],[-51.007,3.5],[-21.482,0],[-8.713,17.856]],"c":true}],"e":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}],"e":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}],"e":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}],"e":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-73.259,-145.537],[-82.089,-178.355],[-90.963,-174.593],[-83.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-73.259,-145.537],[-82.089,-178.355],[-90.963,-174.593],[-83.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-87.007,-159.593],[-69.007,-0.5],[-59.482,-33.5],[-71.713,-94.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-87.007,-159.593],[-69.007,-0.5],[-59.482,-33.5],[-71.713,-94.644]],"c":true}],"e":[{"i":[[2.759,16.037],[1.089,6.855],[3.15,2.884],[0,0],[0,0],[0.024,23.358],[2.213,13.144]],"o":[[-0.741,-7.963],[-2.92,-18.378],[-1.537,-1.407],[0,0],[0,0],[-0.018,-17.5],[-2.755,-16.362]],"v":[[-74.759,-145.537],[-84.089,-185.855],[-90.463,-219.593],[-95.507,-208.093],[-71.507,0.5],[-51.482,-32],[-65.713,-94.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[2.759,16.037],[1.089,6.855],[3.15,2.884],[0,0],[0,0],[0.024,23.358],[2.213,13.144]],"o":[[-0.741,-7.963],[-2.92,-18.378],[-1.537,-1.407],[0,0],[0,0],[-0.018,-17.5],[-2.755,-16.362]],"v":[[-74.759,-145.537],[-84.089,-185.855],[-90.463,-219.593],[-95.507,-208.093],[-71.507,0.5],[-51.482,-32],[-65.713,-94.144]],"c":true}],"e":[{"i":[[8.509,31.287],[6.813,20.34],[4.963,-11.907],[0,0],[0,0],[-17.018,16],[9.213,50.737]],"o":[[-6.241,-30.463],[-5.911,-17.645],[-6.06,14.54],[0,0],[0,0],[9.992,-9.394],[-3.787,-20.856]],"v":[[-51.259,-158.537],[-71.589,-238.355],[-80.963,-225.093],[-96.007,-202.593],[-72.007,0],[-29.482,-25.5],[-34.213,-88.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[8.509,31.287],[6.813,20.34],[4.963,-11.907],[0,0],[0,0],[-17.018,16],[9.213,50.737]],"o":[[-6.241,-30.463],[-5.911,-17.645],[-6.06,14.54],[0,0],[0,0],[9.992,-9.394],[-3.787,-20.856]],"v":[[-51.259,-158.537],[-71.589,-238.355],[-80.963,-225.093],[-96.007,-202.593],[-72.007,0],[-29.482,-25.5],[-34.213,-88.644]],"c":true}],"e":[{"i":[[8.509,31.287],[4.772,20.913],[18.771,-35.562],[0,0],[0,0],[-35.294,7.846],[9.713,50.644]],"o":[[-6.241,-30.463],[-20.911,-91.645],[-14.037,26.593],[0,0],[0,0],[44.982,-10],[-4.991,-26.022]],"v":[[-6.759,-153.537],[-31.089,-246.855],[-59.463,-246.093],[-97.007,-208.093],[-74.007,0.5],[-18.482,-13.5],[11.287,-77.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[8.509,31.287],[4.772,20.913],[18.771,-35.562],[0,0],[0,0],[-35.294,7.846],[9.713,50.644]],"o":[[-6.241,-30.463],[-20.911,-91.645],[-14.037,26.593],[0,0],[0,0],[44.982,-10],[-4.991,-26.022]],"v":[[-6.759,-153.537],[-31.089,-246.855],[-59.463,-246.093],[-97.007,-208.093],[-74.007,0.5],[-18.482,-13.5],[11.287,-77.644]],"c":true}],"e":[{"i":[[9.759,27.037],[3.56,21.153],[32.297,-24.991],[0,0],[0,0],[-51.855,8.105],[16.213,81.144]],"o":[[-6.241,-30.463],[-13.911,-82.645],[-39.537,30.593],[0,0],[0,0],[47.982,-7.5],[-5.191,-25.982]],"v":[[27.241,-164.537],[14.411,-234.855],[-36.963,-250.093],[-97.507,-214.093],[-76.507,-0.5],[0.018,-9],[47.287,-86.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[9.759,27.037],[3.56,21.153],[32.297,-24.991],[0,0],[0,0],[-51.855,8.105],[16.213,81.144]],"o":[[-6.241,-30.463],[-13.911,-82.645],[-39.537,30.593],[0,0],[0,0],[47.982,-7.5],[-5.191,-25.982]],"v":[[27.241,-164.537],[14.411,-234.855],[-36.963,-250.093],[-97.507,-214.093],[-76.507,-0.5],[0.018,-9],[47.287,-86.144]],"c":true}],"e":[{"i":[[9.759,25.037],[3.589,22.355],[46.963,-21.907],[0,0],[0,0],[-52.444,2.078],[5.958,46.178]],"o":[[-1.241,-27.463],[-10.199,-63.523],[-35.55,16.583],[0,0],[0,0],[50.482,-2],[-5.787,-44.856]],"v":[[49.241,-157.537],[45.911,-224.855],[-24.963,-243.593],[-95.007,-217.593],[-76.507,-0.5],[33.018,-7],[72.287,-80.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[9.759,25.037],[3.589,22.355],[46.963,-21.907],[0,0],[0,0],[-52.444,2.078],[5.958,46.178]],"o":[[-1.241,-27.463],[-10.199,-63.523],[-35.55,16.583],[0,0],[0,0],[50.482,-2],[-5.787,-44.856]],"v":[[49.241,-157.537],[45.911,-224.855],[-24.963,-243.593],[-95.007,-217.593],[-76.507,-0.5],[33.018,-7],[72.287,-80.144]],"c":true}],"e":[{"i":[[15.759,21.037],[2.598,21.286],[45.963,-10.657],[0,0],[0,0],[-28.018,1],[5.463,51.394]],"o":[[3.509,-24.213],[-4.161,-64.895],[-38.537,12.343],[0,0],[0,0],[59.982,2.5],[-3.676,-32.795]],"v":[[59.241,-140.287],[64.661,-201.605],[-11.463,-241.343],[-91.757,-220.343],[-77.257,-0.75],[28.018,-4.5],[84.787,-81.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[15.759,21.037],[2.598,21.286],[45.963,-10.657],[0,0],[0,0],[-28.018,1],[5.463,51.394]],"o":[[3.509,-24.213],[-4.161,-64.895],[-38.537,12.343],[0,0],[0,0],[59.982,2.5],[-3.676,-32.795]],"v":[[59.241,-140.287],[64.661,-201.605],[-11.463,-241.343],[-91.757,-220.343],[-77.257,-0.75],[28.018,-4.5],[84.787,-81.144]],"c":true}],"e":[{"i":[[21.759,21.537],[2.058,21.352],[37.463,-5.907],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.749,6.11],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[62.241,-133.037],[74.911,-192.355],[-0.463,-236.593],[-88.507,-223.093],[-78.007,-1],[23.018,-2.5],[92.287,-70.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[21.759,21.537],[2.058,21.352],[37.463,-5.907],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.749,6.11],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[62.241,-133.037],[74.911,-192.355],[-0.463,-236.593],[-88.507,-223.093],[-78.007,-1],[23.018,-2.5],[92.287,-70.144]],"c":true}],"e":[{"i":[[24.259,15.287],[0.089,22.605],[44.213,-5.407],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[11.759,-13.713],[-2.705,-47.408],[-24.787,2.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[78.411,-180.605],[5.787,-233.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[24.259,15.287],[0.089,22.605],[44.213,-5.407],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[11.759,-13.713],[-2.705,-47.408],[-24.787,2.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[78.411,-180.605],[5.787,-233.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":22.253,"s":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[37.766,0],[0,0],[0,0],[0,0],[0,41.089]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[37.463,0],[0,-29.004]],"v":[[55.241,-119.037],[79.411,-164.355],[12.037,-226.593],[-79.507,-226.593],[-79.507,0],[23.518,0],[90.287,-63.144]],"c":true}]},{"t":24}]},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.86,0.86,0.86,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":14,"op":15,"st":0,"bm":10,"sr":1},{"ddd":0,"ind":17,"ty":4,"nm":"B orange layer 5","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[1.759,26.037],[0.589,9.355],[8.227,2.939],[0,0],[0,0],[-17.127,-8.576],[-4.287,-8.856]],"o":[[-1.241,-21.463],[-0.382,-6.06],[-9.537,-3.407],[0,0],[0,0],[11.982,6],[-1.787,-26.856]],"v":[[-12.759,-43.537],[-15.089,-87.355],[-25.963,-106.093],[-46.507,-102.093],[-51.007,3.5],[-21.482,0],[-8.713,17.856]],"c":true}],"e":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}],"e":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}],"e":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}],"e":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-73.259,-145.537],[-82.089,-178.355],[-90.963,-174.593],[-83.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-73.259,-145.537],[-82.089,-178.355],[-90.963,-174.593],[-83.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-87.007,-159.593],[-69.007,-0.5],[-59.482,-33.5],[-71.713,-94.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-87.007,-159.593],[-69.007,-0.5],[-59.482,-33.5],[-71.713,-94.644]],"c":true}],"e":[{"i":[[2.759,16.037],[1.089,6.855],[3.15,2.884],[0,0],[0,0],[0.024,23.358],[2.213,13.144]],"o":[[-0.741,-7.963],[-2.92,-18.378],[-1.537,-1.407],[0,0],[0,0],[-0.018,-17.5],[-2.755,-16.362]],"v":[[-74.759,-145.537],[-84.089,-185.855],[-90.463,-219.593],[-95.507,-208.093],[-71.507,0.5],[-51.482,-32],[-65.713,-94.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[2.759,16.037],[1.089,6.855],[3.15,2.884],[0,0],[0,0],[0.024,23.358],[2.213,13.144]],"o":[[-0.741,-7.963],[-2.92,-18.378],[-1.537,-1.407],[0,0],[0,0],[-0.018,-17.5],[-2.755,-16.362]],"v":[[-74.759,-145.537],[-84.089,-185.855],[-90.463,-219.593],[-95.507,-208.093],[-71.507,0.5],[-51.482,-32],[-65.713,-94.144]],"c":true}],"e":[{"i":[[8.509,31.287],[6.813,20.34],[4.963,-11.907],[0,0],[0,0],[-17.018,16],[9.213,50.737]],"o":[[-6.241,-30.463],[-5.911,-17.645],[-6.06,14.54],[0,0],[0,0],[9.992,-9.394],[-3.787,-20.856]],"v":[[-51.259,-158.537],[-71.589,-238.355],[-80.963,-225.093],[-96.007,-202.593],[-72.007,0],[-29.482,-25.5],[-34.213,-88.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[8.509,31.287],[6.813,20.34],[4.963,-11.907],[0,0],[0,0],[-17.018,16],[9.213,50.737]],"o":[[-6.241,-30.463],[-5.911,-17.645],[-6.06,14.54],[0,0],[0,0],[9.992,-9.394],[-3.787,-20.856]],"v":[[-51.259,-158.537],[-71.589,-238.355],[-80.963,-225.093],[-96.007,-202.593],[-72.007,0],[-29.482,-25.5],[-34.213,-88.644]],"c":true}],"e":[{"i":[[8.509,31.287],[4.772,20.913],[18.771,-35.562],[0,0],[0,0],[-35.294,7.846],[9.713,50.644]],"o":[[-6.241,-30.463],[-20.911,-91.645],[-14.037,26.593],[0,0],[0,0],[44.982,-10],[-4.991,-26.022]],"v":[[-6.759,-153.537],[-31.089,-246.855],[-59.463,-246.093],[-97.007,-208.093],[-74.007,0.5],[-18.482,-13.5],[11.287,-77.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[8.509,31.287],[4.772,20.913],[18.771,-35.562],[0,0],[0,0],[-35.294,7.846],[9.713,50.644]],"o":[[-6.241,-30.463],[-20.911,-91.645],[-14.037,26.593],[0,0],[0,0],[44.982,-10],[-4.991,-26.022]],"v":[[-6.759,-153.537],[-31.089,-246.855],[-59.463,-246.093],[-97.007,-208.093],[-74.007,0.5],[-18.482,-13.5],[11.287,-77.644]],"c":true}],"e":[{"i":[[9.759,27.037],[3.56,21.153],[32.297,-24.991],[0,0],[0,0],[-51.855,8.105],[16.213,81.144]],"o":[[-6.241,-30.463],[-13.911,-82.645],[-39.537,30.593],[0,0],[0,0],[47.982,-7.5],[-5.191,-25.982]],"v":[[27.241,-164.537],[14.411,-234.855],[-36.963,-250.093],[-97.507,-214.093],[-76.507,-0.5],[0.018,-9],[47.287,-86.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[9.759,27.037],[3.56,21.153],[32.297,-24.991],[0,0],[0,0],[-51.855,8.105],[16.213,81.144]],"o":[[-6.241,-30.463],[-13.911,-82.645],[-39.537,30.593],[0,0],[0,0],[47.982,-7.5],[-5.191,-25.982]],"v":[[27.241,-164.537],[14.411,-234.855],[-36.963,-250.093],[-97.507,-214.093],[-76.507,-0.5],[0.018,-9],[47.287,-86.144]],"c":true}],"e":[{"i":[[9.759,25.037],[3.589,22.355],[46.963,-21.907],[0,0],[0,0],[-52.444,2.078],[5.958,46.178]],"o":[[-1.241,-27.463],[-10.199,-63.523],[-35.55,16.583],[0,0],[0,0],[50.482,-2],[-5.787,-44.856]],"v":[[49.241,-157.537],[45.911,-224.855],[-24.963,-243.593],[-95.007,-217.593],[-76.507,-0.5],[33.018,-7],[72.287,-80.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[9.759,25.037],[3.589,22.355],[46.963,-21.907],[0,0],[0,0],[-52.444,2.078],[5.958,46.178]],"o":[[-1.241,-27.463],[-10.199,-63.523],[-35.55,16.583],[0,0],[0,0],[50.482,-2],[-5.787,-44.856]],"v":[[49.241,-157.537],[45.911,-224.855],[-24.963,-243.593],[-95.007,-217.593],[-76.507,-0.5],[33.018,-7],[72.287,-80.144]],"c":true}],"e":[{"i":[[15.759,21.037],[2.598,21.286],[45.963,-10.657],[0,0],[0,0],[-28.018,1],[5.463,51.394]],"o":[[3.509,-24.213],[-4.161,-64.895],[-38.537,12.343],[0,0],[0,0],[59.982,2.5],[-3.676,-32.795]],"v":[[59.241,-140.287],[64.661,-201.605],[-11.463,-241.343],[-91.757,-220.343],[-77.257,-0.75],[28.018,-4.5],[84.787,-81.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[15.759,21.037],[2.598,21.286],[45.963,-10.657],[0,0],[0,0],[-28.018,1],[5.463,51.394]],"o":[[3.509,-24.213],[-4.161,-64.895],[-38.537,12.343],[0,0],[0,0],[59.982,2.5],[-3.676,-32.795]],"v":[[59.241,-140.287],[64.661,-201.605],[-11.463,-241.343],[-91.757,-220.343],[-77.257,-0.75],[28.018,-4.5],[84.787,-81.144]],"c":true}],"e":[{"i":[[21.759,21.537],[2.058,21.352],[37.463,-5.907],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.749,6.11],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[62.241,-133.037],[74.911,-192.355],[-0.463,-236.593],[-88.507,-223.093],[-78.007,-1],[23.018,-2.5],[92.287,-70.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[21.759,21.537],[2.058,21.352],[37.463,-5.907],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.749,6.11],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[62.241,-133.037],[74.911,-192.355],[-0.463,-236.593],[-88.507,-223.093],[-78.007,-1],[23.018,-2.5],[92.287,-70.144]],"c":true}],"e":[{"i":[[24.259,15.287],[0.089,22.605],[44.213,-5.407],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[11.759,-13.713],[-2.705,-47.408],[-24.787,2.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[78.411,-180.605],[5.787,-233.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[24.259,15.287],[0.089,22.605],[44.213,-5.407],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[11.759,-13.713],[-2.705,-47.408],[-24.787,2.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[78.411,-180.605],[5.787,-233.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":22.253,"s":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[37.766,0],[0,0],[0,0],[0,0],[0,41.089]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[37.463,0],[0,-29.004]],"v":[[55.241,-119.037],[79.411,-164.355],[12.037,-226.593],[-79.507,-226.593],[-79.507,0],[23.518,0],[90.287,-63.144]],"c":true}]},{"t":24}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[-1.324,-11.343],[1.439,-10.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[3.112,-1.724]],"v":[[-264.603,-181.739],[-262.939,-148.686],[-267.481,-144.898],[-273.154,-192.488],[-271.112,-195.276]],"c":true}],"e":[{"i":[[-1.324,-11.343],[1.439,-10.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[3.112,-1.724]],"v":[[-60.603,-177.739],[-58.939,-144.686],[-63.481,-140.898],[-69.154,-188.488],[-67.112,-191.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[-1.324,-11.343],[1.439,-10.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[3.112,-1.724]],"v":[[-60.603,-177.739],[-58.939,-144.686],[-63.481,-140.898],[-69.154,-188.488],[-67.112,-191.276]],"c":true}],"e":[{"i":[[-1.324,-11.343],[4.939,-8.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[6.612,-3.724]],"v":[[-40.603,-178.239],[-44.439,-146.186],[-54.981,-139.898],[-61.654,-188.488],[-53.612,-193.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[-1.324,-11.343],[4.939,-8.814],[0,0],[0,0],[0,0]],"o":[[1.603,13.739],[0,0],[0,0],[0,0],[6.612,-3.724]],"v":[[-40.603,-178.239],[-44.439,-146.186],[-54.981,-139.898],[-61.654,-188.488],[-53.612,-193.776]],"c":true}],"e":[{"i":[[-0.897,-12.761],[7.439,-4.314],[0,0],[0,0],[0,0]],"o":[[1.049,14.93],[0,0],[0,0],[0,0],[11.112,-4.224]],"v":[[-22.103,-175.239],[-29.439,-144.686],[-49.481,-139.398],[-53.654,-189.988],[-40.112,-193.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[-0.897,-12.761],[7.439,-4.314],[0,0],[0,0],[0,0]],"o":[[1.049,14.93],[0,0],[0,0],[0,0],[11.112,-4.224]],"v":[[-22.103,-175.239],[-29.439,-144.686],[-49.481,-139.398],[-53.654,-189.988],[-40.112,-193.776]],"c":true}],"e":[{"i":[[-2.295,-14.342],[14.615,-7.06],[0,0],[0,0],[0,0]],"o":[[2.247,14.043],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[-5.749,-174.483],[-19.893,-141.684],[-44.391,-137.152],[-47.397,-188.494],[-26.535,-193.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[-2.295,-14.342],[14.615,-7.06],[0,0],[0,0],[0,0]],"o":[[2.247,14.043],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[-5.749,-174.483],[-19.893,-141.684],[-44.391,-137.152],[-47.397,-188.494],[-26.535,-193.276]],"c":true}],"e":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[7.397,-171.739],[-12.939,-138.686],[-40.981,-136.898],[-43.154,-187.488],[-17.112,-191.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[7.397,-171.739],[-12.939,-138.686],[-40.981,-136.898],[-43.154,-187.488],[-17.112,-191.276]],"c":true}],"e":[{"i":[[-1.534,-14.395],[17.58,-1.876],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.855,-1.149]],"v":[[16.498,-167.632],[-5.273,-137.269],[-37.547,-135.91],[-39.662,-187.025],[-9.278,-190.383]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[-1.534,-14.395],[17.58,-1.876],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.855,-1.149]],"v":[[16.498,-167.632],[-5.273,-137.269],[-37.547,-135.91],[-39.662,-187.025],[-9.278,-190.383]],"c":true}],"e":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[24.639,-165.025],[-0.738,-135.852],[-35.862,-134.172],[-36.92,-187.061],[-2.991,-188.991]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[24.639,-165.025],[-0.738,-135.852],[-35.862,-134.172],[-36.92,-187.061],[-2.991,-188.991]],"c":true}],"e":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[28.279,-161.917],[4.297,-134.435],[-34.177,-133.435],[-34.177,-187.098],[3.297,-187.598]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[28.279,-161.917],[4.297,-134.435],[-34.177,-133.435],[-34.177,-187.098],[3.297,-187.598]],"c":true}],"e":[{"i":[[0,-14.502],[16.037,-0.065],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[31.613,-160.417],[4.963,-133.935],[-33.677,-133.268],[-33.677,-186.264],[4.297,-186.598]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":22.253,"s":[{"i":[[0,-14.502],[16.037,-0.065],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[31.613,-160.417],[4.963,-133.935],[-33.677,-133.268],[-33.677,-186.264],[4.297,-186.598]],"c":true}],"e":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[32.279,-158.917],[6.297,-132.935],[-32.677,-132.935],[-32.677,-184.598],[6.297,-184.598]],"c":true}]},{"t":24}]},"nm":"B"},{"ind":2,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[2.931,-7.005],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[2.931,0.658],[2.344,19.176]],"v":[[-262.931,-52.995],[-268.677,-50.995],[-274.177,-102.658],[-270.431,-105.658],[-264.344,-84.176]],"c":true}],"e":[{"i":[[2.931,-7.005],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[2.931,0.658],[2.344,19.176]],"v":[[-46.431,-52.995],[-52.177,-50.995],[-57.677,-102.658],[-53.931,-105.658],[-47.844,-84.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[2.931,-7.005],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[2.931,0.658],[2.344,19.176]],"v":[[-46.431,-52.995],[-52.177,-50.995],[-57.677,-102.658],[-53.931,-105.658],[-47.844,-84.176]],"c":true}],"e":[{"i":[[7.931,-3.505],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.344,19.176]],"v":[[-32.931,-51.495],[-45.677,-48.995],[-51.177,-100.658],[-40.431,-104.658],[-26.844,-83.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[7.931,-3.505],[0,0],[0,0],[0,0],[-1.768,-14.461]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.344,19.176]],"v":[[-32.931,-51.495],[-45.677,-48.995],[-51.177,-100.658],[-40.431,-104.658],[-26.844,-83.176]],"c":true}],"e":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.589,13.962]],"v":[[-19.931,-48.995],[-40.177,-47.495],[-45.177,-99.158],[-24.431,-103.158],[-6.344,-82.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[8.931,-2.842],[2.589,13.962]],"v":[[-19.931,-48.995],[-40.177,-47.495],[-45.177,-99.158],[-24.431,-103.158],[-6.344,-82.676]],"c":true}],"e":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[15.681,-1.092],[2.589,13.962]],"v":[[-7.681,-46.495],[-37.927,-44.245],[-41.177,-97.908],[-14.681,-101.408],[8.906,-80.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[15.681,-1.092],[2.589,13.962]],"v":[[-7.681,-46.495],[-37.927,-44.245],[-41.177,-97.908],[-14.681,-101.408],[8.906,-80.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[2.589,13.962]],"v":[[-0.931,-44.995],[-36.677,-42.995],[-39.177,-97.158],[-3.931,-100.158],[21.656,-76.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[2.589,13.962]],"v":[[-0.931,-44.995],[-36.677,-42.995],[-39.177,-97.158],[-3.931,-100.158],[21.656,-76.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-1.77,-14.283]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.726,14.041]],"v":[[7.902,-44.495],[-34.344,-44.162],[-35.844,-96.158],[5.902,-97.825],[29.989,-73.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-1.77,-14.283]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.726,14.041]],"v":[[7.902,-44.495],[-34.344,-44.162],[-35.844,-96.158],[5.902,-97.825],[29.989,-73.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[12.235,-42.745],[-33.761,-42.329],[-34.761,-95.908],[11.235,-96.242],[37.322,-69.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[12.235,-42.745],[-33.761,-42.329],[-34.761,-95.908],[11.235,-96.242],[37.322,-69.926]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[13.569,-41.995],[-32.677,-41.995],[-33.677,-95.658],[16.569,-94.658],[40.656,-68.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[13.569,-41.995],[-32.677,-41.995],[-33.677,-95.658],[16.569,-94.658],[40.656,-68.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[16.569,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-94.658],[43.156,-67.676]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":22.253,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[16.569,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-94.658],[43.156,-67.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[16.569,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-93.658],[43.156,-67.676]],"c":true}]},{"t":24}]},"nm":"B"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.86,0.86,0.86,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":15,"op":23,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":18,"ty":4,"nm":"B orange layer 3","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[1.759,26.037],[0.589,9.355],[8.227,2.939],[0,0],[0,0],[-17.127,-8.576],[-4.287,-8.856]],"o":[[-1.241,-21.463],[-0.382,-6.06],[-9.537,-3.407],[0,0],[0,0],[11.982,6],[-1.787,-26.856]],"v":[[-12.759,-43.537],[-15.089,-87.355],[-25.963,-106.093],[-46.507,-102.093],[-51.007,3.5],[-21.482,0],[-8.713,17.856]],"c":true}],"e":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}],"e":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}],"e":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}],"e":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-73.259,-145.537],[-82.089,-178.355],[-90.963,-174.593],[-83.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-73.259,-145.537],[-82.089,-178.355],[-90.963,-174.593],[-83.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-87.007,-159.593],[-69.007,-0.5],[-59.482,-33.5],[-71.713,-94.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-87.007,-159.593],[-69.007,-0.5],[-59.482,-33.5],[-71.713,-94.644]],"c":true}],"e":[{"i":[[2.759,16.037],[1.089,6.855],[3.15,2.884],[0,0],[0,0],[0.024,23.358],[2.213,13.144]],"o":[[-0.741,-7.963],[-2.92,-18.378],[-1.537,-1.407],[0,0],[0,0],[-0.018,-17.5],[-2.755,-16.362]],"v":[[-74.759,-145.537],[-84.089,-185.855],[-90.463,-219.593],[-95.507,-208.093],[-71.507,0.5],[-51.482,-32],[-65.713,-94.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[2.759,16.037],[1.089,6.855],[3.15,2.884],[0,0],[0,0],[0.024,23.358],[2.213,13.144]],"o":[[-0.741,-7.963],[-2.92,-18.378],[-1.537,-1.407],[0,0],[0,0],[-0.018,-17.5],[-2.755,-16.362]],"v":[[-74.759,-145.537],[-84.089,-185.855],[-90.463,-219.593],[-95.507,-208.093],[-71.507,0.5],[-51.482,-32],[-65.713,-94.144]],"c":true}],"e":[{"i":[[8.509,31.287],[6.813,20.34],[4.963,-11.907],[0,0],[0,0],[-17.018,16],[9.213,50.737]],"o":[[-6.241,-30.463],[-5.911,-17.645],[-6.06,14.54],[0,0],[0,0],[9.992,-9.394],[-3.787,-20.856]],"v":[[-51.259,-158.537],[-71.589,-238.355],[-80.963,-225.093],[-96.007,-202.593],[-72.007,0],[-29.482,-25.5],[-34.213,-88.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[8.509,31.287],[6.813,20.34],[4.963,-11.907],[0,0],[0,0],[-17.018,16],[9.213,50.737]],"o":[[-6.241,-30.463],[-5.911,-17.645],[-6.06,14.54],[0,0],[0,0],[9.992,-9.394],[-3.787,-20.856]],"v":[[-51.259,-158.537],[-71.589,-238.355],[-80.963,-225.093],[-96.007,-202.593],[-72.007,0],[-29.482,-25.5],[-34.213,-88.644]],"c":true}],"e":[{"i":[[8.509,31.287],[4.772,20.913],[18.771,-35.562],[0,0],[0,0],[-35.294,7.846],[9.713,50.644]],"o":[[-6.241,-30.463],[-20.911,-91.645],[-14.037,26.593],[0,0],[0,0],[44.982,-10],[-4.991,-26.022]],"v":[[-6.759,-153.537],[-31.089,-246.855],[-59.463,-246.093],[-97.007,-208.093],[-74.007,0.5],[-18.482,-13.5],[11.287,-77.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[8.509,31.287],[4.772,20.913],[18.771,-35.562],[0,0],[0,0],[-35.294,7.846],[9.713,50.644]],"o":[[-6.241,-30.463],[-20.911,-91.645],[-14.037,26.593],[0,0],[0,0],[44.982,-10],[-4.991,-26.022]],"v":[[-6.759,-153.537],[-31.089,-246.855],[-59.463,-246.093],[-97.007,-208.093],[-74.007,0.5],[-18.482,-13.5],[11.287,-77.644]],"c":true}],"e":[{"i":[[9.759,27.037],[3.56,21.153],[32.297,-24.991],[0,0],[0,0],[-51.855,8.105],[16.213,81.144]],"o":[[-6.241,-30.463],[-13.911,-82.645],[-39.537,30.593],[0,0],[0,0],[47.982,-7.5],[-5.191,-25.982]],"v":[[27.241,-164.537],[14.411,-234.855],[-36.963,-250.093],[-97.507,-214.093],[-76.507,-0.5],[0.018,-9],[47.287,-86.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[9.759,27.037],[3.56,21.153],[32.297,-24.991],[0,0],[0,0],[-51.855,8.105],[16.213,81.144]],"o":[[-6.241,-30.463],[-13.911,-82.645],[-39.537,30.593],[0,0],[0,0],[47.982,-7.5],[-5.191,-25.982]],"v":[[27.241,-164.537],[14.411,-234.855],[-36.963,-250.093],[-97.507,-214.093],[-76.507,-0.5],[0.018,-9],[47.287,-86.144]],"c":true}],"e":[{"i":[[9.759,25.037],[3.589,22.355],[46.963,-21.907],[0,0],[0,0],[-52.444,2.078],[5.958,46.178]],"o":[[-1.241,-27.463],[-10.199,-63.523],[-35.55,16.583],[0,0],[0,0],[50.482,-2],[-5.787,-44.856]],"v":[[49.241,-157.537],[45.911,-224.855],[-24.963,-243.593],[-95.007,-217.593],[-76.507,-0.5],[33.018,-7],[72.287,-80.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[9.759,25.037],[3.589,22.355],[46.963,-21.907],[0,0],[0,0],[-52.444,2.078],[5.958,46.178]],"o":[[-1.241,-27.463],[-10.199,-63.523],[-35.55,16.583],[0,0],[0,0],[50.482,-2],[-5.787,-44.856]],"v":[[49.241,-157.537],[45.911,-224.855],[-24.963,-243.593],[-95.007,-217.593],[-76.507,-0.5],[33.018,-7],[72.287,-80.144]],"c":true}],"e":[{"i":[[15.759,21.037],[2.598,21.286],[45.963,-10.657],[0,0],[0,0],[-28.018,1],[5.463,51.394]],"o":[[3.509,-24.213],[-4.161,-64.895],[-38.537,12.343],[0,0],[0,0],[59.982,2.5],[-3.676,-32.795]],"v":[[59.241,-140.287],[64.661,-201.605],[-11.463,-241.343],[-91.757,-220.343],[-77.257,-0.75],[28.018,-4.5],[84.787,-81.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[15.759,21.037],[2.598,21.286],[45.963,-10.657],[0,0],[0,0],[-28.018,1],[5.463,51.394]],"o":[[3.509,-24.213],[-4.161,-64.895],[-38.537,12.343],[0,0],[0,0],[59.982,2.5],[-3.676,-32.795]],"v":[[59.241,-140.287],[64.661,-201.605],[-11.463,-241.343],[-91.757,-220.343],[-77.257,-0.75],[28.018,-4.5],[84.787,-81.144]],"c":true}],"e":[{"i":[[21.759,21.537],[2.058,21.352],[37.463,-5.907],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.749,6.11],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[62.241,-133.037],[74.911,-192.355],[-0.463,-236.593],[-88.507,-223.093],[-78.007,-1],[23.018,-2.5],[92.287,-70.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[21.759,21.537],[2.058,21.352],[37.463,-5.907],[0,0],[0,0],[0,0],[4.713,56.644]],"o":[[10.259,-19.963],[-5.411,-56.145],[-38.749,6.11],[0,0],[0,0],[45.482,1],[-2.393,-28.758]],"v":[[62.241,-133.037],[74.911,-192.355],[-0.463,-236.593],[-88.507,-223.093],[-78.007,-1],[23.018,-2.5],[92.287,-70.144]],"c":true}],"e":[{"i":[[24.259,15.287],[0.089,22.605],[44.213,-5.407],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[11.759,-13.713],[-2.705,-47.408],[-24.787,2.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[78.411,-180.605],[5.787,-233.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[24.259,15.287],[0.089,22.605],[44.213,-5.407],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[11.759,-13.713],[-2.705,-47.408],[-24.787,2.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[60.241,-127.787],[78.411,-180.605],[5.787,-233.593],[-86.007,-223.593],[-78.007,-1],[24.768,-0.25],[93.287,-66.644]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[21.149,8.762],[0,21.451],[38.463,-2.407],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[59.241,-124.037],[79.411,-176.855],[14.537,-231.593],[-83.507,-224.093],[-78.007,-1],[23.518,0],[94.787,-61.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":22.253,"s":[{"i":[[21.149,8.762],[0.749,21.438],[38.114,-1.203],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.741,-120.537],[79.911,-168.605],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[92.037,-63.144]],"c":true}],"e":[{"i":[[21.149,8.762],[0,21.451],[37.766,0],[0,0],[0,0],[0,0],[0,41.089]],"o":[[15.106,-9.064],[0,-38.672],[0,0],[0,0],[0,0],[37.463,0],[0,-29.004]],"v":[[55.241,-119.037],[79.411,-164.355],[12.037,-226.593],[-79.507,-226.593],[-79.507,0],[23.518,0],[90.287,-63.144]],"c":true}]},{"t":24}]},"nm":"B"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.86,0.86,0.86,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":11,"op":15,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":19,"ty":4,"nm":"B orange layer 2 shadow","parent":34,"ks":{"o":{"k":5},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[27,-20],[0,0],[-5,-11],[-20,3],[0,21],[-1,63]],"o":[[-10.811,8.008],[0,0],[5,11],[20,-3],[0,-21],[1,-63]],"v":[[-55,-172],[-86,-85],[-92,10],[-95,22],[0,21],[-15,-137]],"c":true}],"e":[{"i":[[27,-20],[0,0],[-5,-11],[-20,3],[0,21],[-1,63]],"o":[[-10.811,8.008],[0,0],[5,11],[20,-3],[0,-21],[1,-63]],"v":[[-55,-172],[-86,-85],[-92,10],[-58,40],[42,28],[20,-147]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[27,-20],[0,0],[-5,-11],[-20,3],[0,21],[-1,63]],"o":[[-10.811,8.008],[0,0],[5,11],[20,-3],[0,-21],[1,-63]],"v":[[-55,-172],[-86,-85],[-92,10],[-58,40],[42,28],[20,-147]],"c":true}],"e":[{"i":[[55,-29],[0,0],[-5,-11],[-20,3],[0,21],[28,61]],"o":[[-55,29],[0,0],[5,11],[20,-3],[0,-21],[-26.285,-57.263]],"v":[[-79,-179],[-124,-104],[-119,72],[-21,87],[50,-29],[4,-198]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[55,-29],[0,0],[-5,-11],[-20,3],[0,21],[28,61]],"o":[[-55,29],[0,0],[5,11],[20,-3],[0,-21],[-26.285,-57.263]],"v":[[-79,-179],[-124,-104],[-119,72],[-21,87],[50,-29],[4,-198]],"c":true}],"e":[{"i":[[29.478,-54.745],[0,0],[-5,-11],[-20,3],[0,21],[13,66]],"o":[[-7,13],[0,0],[5,11],[20,-3],[0,-21],[-12.177,-61.82]],"v":[[-115,-165],[-124,-104],[-107,16],[-80,39],[-19,5],[-54,-216]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[29.478,-54.745],[0,0],[-5,-11],[-20,3],[0,21],[13,66]],"o":[[-7,13],[0,0],[5,11],[20,-3],[0,-21],[-12.177,-61.82]],"v":[[-115,-165],[-124,-104],[-107,16],[-80,39],[-19,5],[-54,-216]],"c":true}],"e":[{"i":[[29.478,-54.745],[0,0],[-5,-11],[-20,3],[0,21],[13,66]],"o":[[-7,13],[0,0],[5,11],[20,-3],[0,-21],[-12.177,-61.82]],"v":[[-150,-165],[-159,-104],[-142,16],[-115,39],[-54,5],[-77,-199]],"c":true}]},{"t":10}]},"o":{"k":100},"x":{"k":0},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":4,"s":[{"i":[[-0.993,13.755],[-0.576,6.238],[1.302,0.585],[0,0],[0,0],[0,0],[-2.275,-1.384],[-0.28,-3.435]],"o":[[1.757,-16.495],[0.509,-5.518],[-2.446,-1.099],[0,0],[0,0],[0,0],[1.508,0.917],[1.082,-12.356]],"v":[[-36.507,-32.755],[-33.509,-58.982],[-33.554,-66.651],[-36.609,-64.578],[-39.481,-45.472],[-46.5,1.214],[-41.725,1.384],[-40.332,4.606]],"c":true}],"e":[{"i":[[-0.741,19.537],[-0.161,8.855],[3.194,1.609],[0,0],[0,0],[0,0],[-8.518,-3.5],[-1.037,-4.856]],"o":[[-0.241,-13.963],[0.11,-6.071],[-3.787,-1.907],[0,0],[0,0],[0,0],[4.451,1.829],[0.963,-28.106]],"v":[[-29.259,-43.037],[-28.339,-74.105],[-32.213,-84.593],[-40.757,-84.843],[-44.677,-58.245],[-54.257,6.75],[-37.482,2.75],[-30.463,12.606]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-0.741,19.537],[-0.161,8.855],[3.194,1.609],[0,0],[0,0],[0,0],[-8.518,-3.5],[-1.037,-4.856]],"o":[[-0.241,-13.963],[0.11,-6.071],[-3.787,-1.907],[0,0],[0,0],[0,0],[4.451,1.829],[0.963,-28.106]],"v":[[-29.259,-43.037],[-28.339,-74.105],[-32.213,-84.593],[-40.757,-84.843],[-44.677,-58.245],[-54.257,6.75],[-37.482,2.75],[-30.463,12.606]],"c":true}],"e":[{"i":[[1.759,26.037],[0.589,9.355],[8.227,2.939],[0,0],[0,0],[0,0],[-15.518,-7.5],[-1.787,-7.356]],"o":[[-1.241,-21.463],[-0.382,-6.06],[-9.537,-3.407],[0,0],[0,0],[0,0],[12.065,5.831],[-1.787,-26.856]],"v":[[-12.759,-43.537],[-15.089,-87.355],[-25.963,-106.093],[-47.257,-101.593],[-49.484,-64.148],[-53.507,3.5],[-22.232,0.5],[-8.713,17.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[1.759,26.037],[0.589,9.355],[8.227,2.939],[0,0],[0,0],[0,0],[-15.518,-7.5],[-1.787,-7.356]],"o":[[-1.241,-21.463],[-0.382,-6.06],[-9.537,-3.407],[0,0],[0,0],[0,0],[12.065,5.831],[-1.787,-26.856]],"v":[[-12.759,-43.537],[-15.089,-87.355],[-25.963,-106.093],[-47.257,-101.593],[-49.484,-64.148],[-53.507,3.5],[-22.232,0.5],[-8.713,17.856]],"c":true}],"e":[{"i":[[5.759,37.537],[1.35375,9.22725],[1.2945,5.92749999999999],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-3.1205,-19.9815],[-1.35375,-9.22725],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[12.9565,-90.9865],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[5.759,37.537],[1.35375,9.22725],[1.2945,5.92749999999999],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-3.1205,-19.9815],[-1.35375,-9.22725],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[12.9565,-90.9865],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}],"e":[{"i":[[7.259,32.537],[1.54124999999999,8.22725],[1.5445,4.67749999999998],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-3.62050000000001,-19.2315],[-1.54125000000001,-8.22725],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[76.519,-124.8615],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[7.259,32.537],[1.54124999999999,8.22725],[1.5445,4.67749999999998],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-3.62050000000001,-19.2315],[-1.54125000000001,-8.22725],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[76.519,-124.8615],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}],"e":[{"i":[[10.759,29.037],[4.4935,12.920375],[2.2355,2.65499999999997],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-3.6205,-10.9815],[-4.4935,-12.920375],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[32.28725,-233.940875],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[10.759,29.037],[4.4935,12.920375],[2.2355,2.65499999999997],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-3.6205,-10.9815],[-4.4935,-12.920375],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[32.28725,-233.940875],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}],"e":[{"i":[[4.259,17.037],[3.073,9.17012500000001],[0.66749999999999,3.40600000000001],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-1.3705,-6.23150000000001],[-3.07300000000001,-9.17012499999998],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-60.45125,-196.815125],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[4.259,17.037],[3.073,9.17012500000001],[0.66749999999999,3.40600000000001],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-1.3705,-6.23150000000001],[-3.07300000000001,-9.17012499999998],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-60.45125,-196.815125],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.545875,6.84424999999999],[0.27600000000001,3.45950000000002],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.37050000000001,-3.98150000000001],[-0.545875,-6.84424999999999],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-80.744875,-161.3375],[-82.089,-178.355],[-90.963,-174.593],[-80.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}]},{"t":11}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":4,"s":[{"i":[[-0.993,13.755],[-0.576,6.238],[1.302,0.585],[0,0],[0,0],[-2.275,-1.384],[-0.28,-3.435]],"o":[[1.757,-16.495],[0.509,-5.518],[-2.446,-1.099],[0,0],[0,0],[1.508,0.917],[1.082,-12.356]],"v":[[-36.507,-32.755],[-33.509,-58.982],[-33.554,-66.651],[-36.609,-64.578],[-46.5,1.214],[-41.725,1.384],[-40.332,4.606]],"c":true}],"e":[{"i":[[-0.741,19.537],[-0.161,8.855],[3.194,1.609],[0,0],[0,0],[-8.518,-3.5],[-1.037,-4.856]],"o":[[-0.241,-13.963],[0.11,-6.071],[-3.787,-1.907],[0,0],[0,0],[4.451,1.829],[0.963,-28.106]],"v":[[-29.259,-43.037],[-28.339,-74.105],[-32.213,-84.593],[-40.757,-84.843],[-54.257,6.75],[-37.482,2.75],[-30.463,12.606]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-0.741,19.537],[-0.161,8.855],[3.194,1.609],[0,0],[0,0],[-8.518,-3.5],[-1.037,-4.856]],"o":[[-0.241,-13.963],[0.11,-6.071],[-3.787,-1.907],[0,0],[0,0],[4.451,1.829],[0.963,-28.106]],"v":[[-29.259,-43.037],[-28.339,-74.105],[-32.213,-84.593],[-40.757,-84.843],[-54.257,6.75],[-37.482,2.75],[-30.463,12.606]],"c":true}],"e":[{"i":[[0.832,26.037],[0.279,9.355],[-0.34,-0.907],[0,0],[0,0],[-1.764,2.5],[-2,4.644]],"o":[[-0.587,-21.463],[-0.181,-6.06],[-2.641,3.629],[0,0],[0,0],[1.764,-2.5],[3,-26.606]],"v":[[-48.414,-64.537],[-46.516,-92.355],[-47.66,-93.593],[-51.128,-76.843],[-60.507,13.25],[-56.514,6],[-52.5,-2.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[0.832,26.037],[0.279,9.355],[-0.34,-0.907],[0,0],[0,0],[-1.764,2.5],[-2,4.644]],"o":[[-0.587,-21.463],[-0.181,-6.06],[-2.641,3.629],[0,0],[0,0],[1.764,-2.5],[3,-26.606]],"v":[[-48.414,-64.537],[-46.516,-92.355],[-47.66,-93.593],[-51.128,-76.843],[-60.507,13.25],[-56.514,6],[-52.5,-2.394]],"c":true}],"e":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}],"e":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}],"e":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}],"e":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-80.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}]},{"t":11}]},"nm":"B 2"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.86,0.86,0.86,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":6,"op":11,"st":0,"bm":10,"sr":1},{"ddd":0,"ind":20,"ty":4,"nm":"B orange layer 2","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":4,"s":[{"i":[[-0.993,13.755],[-0.576,6.238],[1.302,0.585],[0,0],[0,0],[0,0],[-2.275,-1.384],[-0.28,-3.435]],"o":[[1.757,-16.495],[0.509,-5.518],[-2.446,-1.099],[0,0],[0,0],[0,0],[1.508,0.917],[1.082,-12.356]],"v":[[-36.507,-32.755],[-33.509,-58.982],[-33.554,-66.651],[-36.609,-64.578],[-39.481,-45.472],[-46.5,1.214],[-41.725,1.384],[-40.332,4.606]],"c":true}],"e":[{"i":[[-0.741,19.537],[-0.161,8.855],[3.194,1.609],[0,0],[0,0],[0,0],[-8.518,-3.5],[-1.037,-4.856]],"o":[[-0.241,-13.963],[0.11,-6.071],[-3.787,-1.907],[0,0],[0,0],[0,0],[4.451,1.829],[0.963,-28.106]],"v":[[-29.259,-43.037],[-28.339,-74.105],[-32.213,-84.593],[-40.757,-84.843],[-44.677,-58.245],[-54.257,6.75],[-37.482,2.75],[-30.463,12.606]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-0.741,19.537],[-0.161,8.855],[3.194,1.609],[0,0],[0,0],[0,0],[-8.518,-3.5],[-1.037,-4.856]],"o":[[-0.241,-13.963],[0.11,-6.071],[-3.787,-1.907],[0,0],[0,0],[0,0],[4.451,1.829],[0.963,-28.106]],"v":[[-29.259,-43.037],[-28.339,-74.105],[-32.213,-84.593],[-40.757,-84.843],[-44.677,-58.245],[-54.257,6.75],[-37.482,2.75],[-30.463,12.606]],"c":true}],"e":[{"i":[[1.759,26.037],[0.589,9.355],[8.227,2.939],[0,0],[0,0],[0,0],[-15.518,-7.5],[-1.787,-7.356]],"o":[[-1.241,-21.463],[-0.382,-6.06],[-9.537,-3.407],[0,0],[0,0],[0,0],[12.065,5.831],[-1.787,-26.856]],"v":[[-12.759,-43.537],[-15.089,-87.355],[-25.963,-106.093],[-47.257,-101.593],[-49.484,-64.148],[-53.507,3.5],[-22.232,0.5],[-8.713,17.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[1.759,26.037],[0.589,9.355],[8.227,2.939],[0,0],[0,0],[0,0],[-15.518,-7.5],[-1.787,-7.356]],"o":[[-1.241,-21.463],[-0.382,-6.06],[-9.537,-3.407],[0,0],[0,0],[0,0],[12.065,5.831],[-1.787,-26.856]],"v":[[-12.759,-43.537],[-15.089,-87.355],[-25.963,-106.093],[-47.257,-101.593],[-49.484,-64.148],[-53.507,3.5],[-22.232,0.5],[-8.713,17.856]],"c":true}],"e":[{"i":[[5.759,37.537],[1.35375,9.22725],[1.2945,5.92749999999999],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-3.1205,-19.9815],[-1.35375,-9.22725],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[12.9565,-90.9865],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[5.759,37.537],[1.35375,9.22725],[1.2945,5.92749999999999],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-3.1205,-19.9815],[-1.35375,-9.22725],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[12.9565,-90.9865],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}],"e":[{"i":[[7.259,32.537],[1.54124999999999,8.22725],[1.5445,4.67749999999998],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-3.62050000000001,-19.2315],[-1.54125000000001,-8.22725],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[76.519,-124.8615],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[7.259,32.537],[1.54124999999999,8.22725],[1.5445,4.67749999999998],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-3.62050000000001,-19.2315],[-1.54125000000001,-8.22725],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[76.519,-124.8615],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}],"e":[{"i":[[10.759,29.037],[4.4935,12.920375],[2.2355,2.65499999999997],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-3.6205,-10.9815],[-4.4935,-12.920375],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[32.28725,-233.940875],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[10.759,29.037],[4.4935,12.920375],[2.2355,2.65499999999997],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-3.6205,-10.9815],[-4.4935,-12.920375],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[32.28725,-233.940875],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}],"e":[{"i":[[4.259,17.037],[3.073,9.17012500000001],[0.66749999999999,3.40600000000001],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-1.3705,-6.23150000000001],[-3.07300000000001,-9.17012499999998],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-60.45125,-196.815125],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[4.259,17.037],[3.073,9.17012500000001],[0.66749999999999,3.40600000000001],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-1.3705,-6.23150000000001],[-3.07300000000001,-9.17012499999998],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-60.45125,-196.815125],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.545875,6.84424999999999],[0.27600000000001,3.45950000000002],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.37050000000001,-3.98150000000001],[-0.545875,-6.84424999999999],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-80.744875,-161.3375],[-82.089,-178.355],[-90.963,-174.593],[-80.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}]},{"t":11}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":4,"s":[{"i":[[-0.993,13.755],[-0.576,6.238],[1.302,0.585],[0,0],[0,0],[-2.275,-1.384],[-0.28,-3.435]],"o":[[1.757,-16.495],[0.509,-5.518],[-2.446,-1.099],[0,0],[0,0],[1.508,0.917],[1.082,-12.356]],"v":[[-36.507,-32.755],[-33.509,-58.982],[-33.554,-66.651],[-36.609,-64.578],[-46.5,1.214],[-41.725,1.384],[-40.332,4.606]],"c":true}],"e":[{"i":[[-0.741,19.537],[-0.161,8.855],[3.194,1.609],[0,0],[0,0],[-8.518,-3.5],[-1.037,-4.856]],"o":[[-0.241,-13.963],[0.11,-6.071],[-3.787,-1.907],[0,0],[0,0],[4.451,1.829],[0.963,-28.106]],"v":[[-29.259,-43.037],[-28.339,-74.105],[-32.213,-84.593],[-40.757,-84.843],[-54.257,6.75],[-37.482,2.75],[-30.463,12.606]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-0.741,19.537],[-0.161,8.855],[3.194,1.609],[0,0],[0,0],[-8.518,-3.5],[-1.037,-4.856]],"o":[[-0.241,-13.963],[0.11,-6.071],[-3.787,-1.907],[0,0],[0,0],[4.451,1.829],[0.963,-28.106]],"v":[[-29.259,-43.037],[-28.339,-74.105],[-32.213,-84.593],[-40.757,-84.843],[-54.257,6.75],[-37.482,2.75],[-30.463,12.606]],"c":true}],"e":[{"i":[[0.832,26.037],[0.279,9.355],[-0.34,-0.907],[0,0],[0,0],[-1.764,2.5],[-2,4.644]],"o":[[-0.587,-21.463],[-0.181,-6.06],[-2.641,3.629],[0,0],[0,0],[1.764,-2.5],[3,-26.606]],"v":[[-48.414,-64.537],[-46.516,-92.355],[-47.66,-93.593],[-51.128,-76.843],[-60.507,13.25],[-56.514,6],[-52.5,-2.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[0.832,26.037],[0.279,9.355],[-0.34,-0.907],[0,0],[0,0],[-1.764,2.5],[-2,4.644]],"o":[[-0.587,-21.463],[-0.181,-6.06],[-2.641,3.629],[0,0],[0,0],[1.764,-2.5],[3,-26.606]],"v":[[-48.414,-64.537],[-46.516,-92.355],[-47.66,-93.593],[-51.128,-76.843],[-60.507,13.25],[-56.514,6],[-52.5,-2.394]],"c":true}],"e":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[5.759,37.537],[2.589,11.855],[15.486,1.354],[0,0],[0,0],[-32.518,-11.5],[-4.287,-8.856]],"o":[[-6.241,-39.963],[-1.296,-5.932],[-27.537,-2.407],[0,0],[0,0],[21.489,7.6],[-3.287,-20.356]],"v":[[19.241,-49.037],[9.411,-111.855],[-17.463,-132.593],[-53.507,-120.593],[-55.507,2.5],[4.018,-9.5],[30.287,16.856]],"c":true}],"e":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[7.259,32.537],[3.089,9.355],[44.113,-3.3],[0,0],[0,0],[-53.018,1],[3.713,19.144]],"o":[[-7.241,-38.463],[-1.904,-5.765],[-48.037,3.593],[0,0],[0,0],[60.505,-1.141],[-3.159,-16.288]],"v":[[83.741,-85.537],[72.411,-142.355],[4.537,-172.593],[-61.007,-138.593],[-59.007,1.5],[28.018,-33.5],[99.787,-17.644]],"c":true}],"e":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[10.759,29.037],[4.471,5.31],[29.463,-40.407],[0,0],[0,0],[-43.018,23.5],[3.713,19.144]],"o":[[-7.241,-21.963],[-3.911,-4.645],[-7.99,10.959],[0,0],[0,0],[44.538,-24.331],[-3.159,-16.288]],"v":[[45.241,-195.037],[21.411,-260.355],[-37.963,-203.593],[-68.007,-155.093],[-61.507,3],[11.018,-60],[71.287,-111.144]],"c":true}],"e":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[4.259,17.037],[1.335,6.812],[2.963,-14.907],[0,0],[0,0],[-7.018,26.5],[3.713,19.144]],"o":[[-2.741,-12.463],[-0.911,-4.645],[-2.644,13.302],[0,0],[0,0],[9.673,-36.528],[-3.159,-16.288]],"v":[[-52.759,-171.537],[-67.089,-217.855],[-68.963,-189.593],[-76.007,-170.093],[-64.007,1.5],[-40.982,-50],[-39.713,-115.644]],"c":true}],"e":[{"i":[[2.759,16.037],[0.552,6.919],[3.15,2.884],[0,0],[0,0],[4.147,22.987],[2.213,13.144]],"o":[[-0.741,-7.963],[-0.411,-5.145],[-1.537,-1.407],[0,0],[0,0],[-3.518,-19.5],[-2.755,-16.362]],"v":[[-79.259,-143.537],[-82.089,-178.355],[-90.963,-174.593],[-80.007,-159.593],[-66.507,1.5],[-58.482,-36],[-71.713,-94.644]],"c":true}]},{"t":11}]},"nm":"B 2"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.86,0.86,0.86,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":4,"op":11,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":21,"ty":4,"nm":"B blue layer 1","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":1,"s":[{"i":[[0,0],[1.135,0.16],[0.519,-0.169],[0.368,-1.304],[0.165,-1.422],[-2.463,0.226],[-0.024,0.731],[0.1,1.443]],"o":[[0,0],[-0.591,-0.084],[-0.572,0.81],[-0.408,1.446],[-0.141,1.212],[2.637,-0.242],[-0.113,-1.414],[-0.065,-0.946]],"v":[[-29.39,-0.524],[-31.301,-0.669],[-32.486,-0.16],[-34.047,2.548],[-34.859,5.55],[-31.571,8.49],[-28.75,5.075],[-29.073,1.857]],"c":true}],"e":[{"i":[[0,0],[4.131,0.584],[1.89,-0.615],[1.34,-4.746],[0.602,-5.175],[-8.964,0.823],[-0.088,2.66],[0.363,5.252]],"o":[[0,0],[-2.152,-0.304],[-2.08,2.95],[-1.486,5.262],[-0.513,4.413],[9.6,-0.881],[-0.41,-5.148],[-0.238,-3.443]],"v":[[-27.079,-7.92],[-34.036,-8.446],[-38.349,-6.594],[-44.031,3.262],[-46.987,14.191],[-35.017,24.893],[-24.75,12.463],[-25.925,0.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":2,"s":[{"i":[[0,0],[4.131,0.584],[1.89,-0.615],[1.34,-4.746],[0.602,-5.175],[-8.964,0.823],[-0.088,2.66],[0.363,5.252]],"o":[[0,0],[-2.152,-0.304],[-2.08,2.95],[-1.486,5.262],[-0.513,4.413],[9.6,-0.881],[-0.41,-5.148],[-0.238,-3.443]],"v":[[-27.079,-7.92],[-34.036,-8.446],[-38.349,-6.594],[-44.031,3.262],[-46.987,14.191],[-35.017,24.893],[-24.75,12.463],[-25.925,0.75]],"c":true}],"e":[{"i":[[0,0],[8.324,1.188],[3.808,-1.25],[2.7,-9.654],[1.213,-10.525],[-18.063,1.674],[-0.178,5.41],[0.731,10.682]],"o":[[0,0],[-4.337,-0.619],[-4.192,6],[-2.994,10.703],[-1.034,8.975],[19.344,-1.793],[-0.826,-10.471],[-0.479,-7.002]],"v":[[-18.099,-21.695],[-32.116,-22.765],[-40.808,-19],[-52.256,1.047],[-58.213,23.275],[-34.094,45.043],[-13.405,19.761],[-15.773,-4.063]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":3,"s":[{"i":[[0,0],[8.324,1.188],[3.808,-1.25],[2.7,-9.654],[1.213,-10.525],[-18.063,1.674],[-0.178,5.41],[0.731,10.682]],"o":[[0,0],[-4.337,-0.619],[-4.192,6],[-2.994,10.703],[-1.034,8.975],[19.344,-1.793],[-0.826,-10.471],[-0.479,-7.002]],"v":[[-18.099,-21.695],[-32.116,-22.765],[-40.808,-19],[-52.256,1.047],[-58.213,23.275],[-34.094,45.043],[-13.405,19.761],[-15.773,-4.063]],"c":true}],"e":[{"i":[[0,0],[12.843,1.833],[4.559,8.938],[3.186,-15.134],[3.035,-13.004],[-27.952,1.396],[-0.275,8.346],[1.128,16.48]],"o":[[0,0],[-6.691,-0.955],[-2.441,12.438],[-4.015,19.071],[-3.168,13.574],[31.05,-1.551],[-1.275,-16.154],[-0.74,-10.803]],"v":[[-2.195,-42.59],[-24.593,-43.083],[-44.559,-47.688],[-56.436,-7.116],[-64.082,27.176],[-28.8,62.301],[4.275,27.154],[0.622,-14.23]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":4,"s":[{"i":[[0,0],[12.843,1.833],[4.559,8.938],[3.186,-15.134],[3.035,-13.004],[-27.952,1.396],[-0.275,8.346],[1.128,16.48]],"o":[[0,0],[-6.691,-0.955],[-2.441,12.438],[-4.015,19.071],[-3.168,13.574],[31.05,-1.551],[-1.275,-16.154],[-0.74,-10.803]],"v":[[-2.195,-42.59],[-24.593,-43.083],[-44.559,-47.688],[-56.436,-7.116],[-64.082,27.176],[-28.8,62.301],[4.275,27.154],[0.622,-14.23]],"c":true}],"e":[{"i":[[0,0],[30.844,0.507],[4.059,4.688],[1.936,-11.384],[-2.168,-13.176],[-27.451,0.398],[1.225,11.846],[3.429,20.36]],"o":[[0,0],[-10.157,-0.167],[-1.941,14.188],[-3.267,19.214],[1.6,9.723],[38.05,-0.551],[-1.275,-15.904],[-3.372,-20.02]],"v":[[12.805,-58.84],[-26.843,-38.833],[-49.309,-49.938],[-55.686,-8.616],[-60.582,43.176],[-20.55,70.051],[26.275,28.904],[17.122,-30.23]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[0,0],[30.844,0.507],[4.059,4.688],[1.936,-11.384],[-2.168,-13.176],[-27.451,0.398],[1.225,11.846],[3.429,20.36]],"o":[[0,0],[-10.157,-0.167],[-1.941,14.188],[-3.267,19.214],[1.6,9.723],[38.05,-0.551],[-1.275,-15.904],[-3.372,-20.02]],"v":[[12.805,-58.84],[-26.843,-38.833],[-49.309,-49.938],[-55.686,-8.616],[-60.582,43.176],[-20.55,70.051],[26.275,28.904],[17.122,-30.23]],"c":true}],"e":[{"i":[[0,0],[-0.089,-0.987],[-0.278,-3.396],[-0.525,-7.148],[-4.945,-5.803],[-9.866,11.148],[0.725,3.596],[6.837,19.482]],"o":[[0,0],[0.121,1.333],[0.323,3.936],[1.428,19.437],[2.832,3.324],[11.55,-13.051],[-1.275,-15.904],[-5.622,-16.02]],"v":[[10.305,-57.09],[10.44,-55.64],[11.04,-48.623],[12.314,-32.116],[21.668,57.426],[44.2,51.551],[54.275,22.904],[39.872,-29.73]],"c":true}]},{"t":6}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":1,"s":[{"i":[[-0.404,3.747],[1.477,0.688],[1.74,-9.708],[-5.091,0.021],[-0.25,4.402]],"o":[[-1.673,-0.138],[-1.523,11.938],[-0.954,5.323],[3.195,-0.013],[0.819,-14.412]],"v":[[-23.513,-54.481],[-25.144,-53.423],[-32.713,-2.808],[-29.826,7.494],[-26.917,-1.136]],"c":true}],"e":[{"i":[[-0.404,3.747],[1.477,0.688],[1.74,-9.708],[-5.091,0.021],[-0.25,4.402]],"o":[[-1.673,-0.138],[-1.523,11.938],[-0.954,5.323],[3.195,-0.013],[0.819,-14.412]],"v":[[-31.013,-45.315],[-32.644,-44.256],[-40.213,6.359],[-37.326,16.661],[-34.417,8.03]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":3,"s":[{"i":[[-0.404,3.747],[1.477,0.688],[1.74,-9.708],[-5.091,0.021],[-0.25,4.402]],"o":[[-1.673,-0.138],[-1.523,11.938],[-0.954,5.323],[3.195,-0.013],[0.819,-14.412]],"v":[[-31.013,-45.315],[-32.644,-44.256],[-40.213,6.359],[-37.326,16.661],[-34.417,8.03]],"c":true}],"e":[{"i":[[-0.404,3.747],[1.477,0.688],[1.74,-9.708],[-5.091,0.021],[-0.25,4.402]],"o":[[-1.673,-0.138],[-1.523,11.938],[-0.954,5.323],[3.196,-0.013],[0.819,-14.412]],"v":[[-37.096,-52.997],[-38.727,-51.938],[-46.296,-1.323],[-43.159,7.979],[-40.5,0.348]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":4,"s":[{"i":[[-0.404,3.747],[1.477,0.688],[1.74,-9.708],[-5.091,0.021],[-0.25,4.402]],"o":[[-1.673,-0.138],[-1.523,11.938],[-0.954,5.323],[3.196,-0.013],[0.819,-14.412]],"v":[[-37.096,-52.997],[-38.727,-51.938],[-46.296,-1.323],[-43.159,7.979],[-40.5,0.348]],"c":true}],"e":[{"i":[[-0.103,4.085],[4.059,4.688],[1.686,-13.134],[-16.7,0.449],[-0.025,15.596]],"o":[[-5.407,-0.167],[-1.941,14.188],[-2.481,19.331],[7.341,-0.197],[-0.025,-17.404]],"v":[[-31.343,-55.333],[-44.309,-60.438],[-51.186,-11.116],[-42.05,21.551],[-30.225,2.654]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-0.103,4.085],[4.059,4.688],[1.686,-13.134],[-16.7,0.449],[-0.025,15.596]],"o":[[-5.407,-0.167],[-1.941,14.188],[-2.481,19.331],[7.341,-0.197],[-0.025,-17.404]],"v":[[-31.343,-55.333],[-44.309,-60.438],[-51.186,-11.116],[-42.05,21.551],[-30.225,2.654]],"c":true}],"e":[{"i":[[-0.089,-0.987],[-0.278,-3.396],[-0.525,-7.148],[-9.866,11.148],[0.725,3.596]],"o":[[0.121,1.333],[0.323,3.936],[1.428,19.437],[11.55,-13.051],[-1.275,-15.904]],"v":[[5.773,-76.735],[6.373,-69.719],[7.647,-53.212],[39.533,30.455],[49.608,1.808]],"c":true}]},{"t":6}]},"nm":"B 2"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.01,0.24,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":1,"op":7,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":22,"ty":4,"nm":"B white layer 2 shadow 2","parent":34,"ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16,"s":[5],"e":[40]},{"t":22}]},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[0,0],[0,0]],"v":[[-38.124,-198.023],[-153.77,-179.155],[-179.51,0.476],[-152.785,32.892],[-44.446,28.81],[-41.27,-85.157]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[0,0],[0,0]],"v":[[-26.124,-205.023],[-141.77,-186.155],[-167.51,-6.524],[-140.785,25.892],[-10.446,30.81],[-18.323,-87.679]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[0,0],[0,0]],"v":[[-26.124,-205.023],[-141.77,-186.155],[-167.51,-6.524],[-140.785,25.892],[-10.446,30.81],[-18.323,-87.679]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[-22.446,5.81],[5.009,56.785]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[11.259,-2.914],[-4.977,-56.418]],"v":[[7.876,-190.023],[-107.77,-171.155],[-133.51,8.476],[-106.785,40.892],[16.554,39.31],[20.584,-78.085]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[-22.446,5.81],[5.009,56.785]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[11.259,-2.914],[-4.977,-56.418]],"v":[[7.876,-190.023],[-107.77,-171.155],[-133.51,8.476],[-106.785,40.892],[16.554,39.31],[20.584,-78.085]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-18.5,31.5],[9.358,52.957]],"o":[[0,0],[0,0],[0,0],[50,-11],[9.28,-15.801],[-9.297,-52.616]],"v":[[37.5,-220],[-101,-170],[-88,11],[-55,37],[65.5,7],[58.347,-118.825]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-18.5,31.5],[9.358,52.957]],"o":[[0,0],[0,0],[0,0],[50,-11],[9.28,-15.801],[-9.297,-52.616]],"v":[[37.5,-220],[-101,-170],[-88,11],[-55,37],[65.5,7],[58.347,-118.825]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-18.5,33],[14.123,54.022]],"o":[[0,0],[0,0],[0,0],[50,-11],[9.28,-16.553],[-14.032,-53.673]],"v":[[-38.5,-242],[-236,-164],[-88,11],[-55,36],[8.5,-10],[-8.199,-138.898]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-18.5,33],[14.123,54.022]],"o":[[0,0],[0,0],[0,0],[50,-11],[9.28,-16.553],[-14.032,-53.673]],"v":[[-38.5,-242],[-236,-164],[-88,11],[-55,36],[8.5,-10],[-8.199,-138.898]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-10.976,14.749],[13.055,55.474]],"o":[[0,0],[0,0],[0,0],[50,-11],[8.026,-10.785],[-12.971,-55.116]],"v":[[-61,-230],[-236,-164],[-109,24],[-60,28],[-17,2],[-33.126,-122.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-10.976,14.749],[13.055,55.474]],"o":[[0,0],[0,0],[0,0],[50,-11],[8.026,-10.785],[-12.971,-55.116]],"v":[[-61,-230],[-236,-164],[-109,24],[-60,28],[-17,2],[-33.126,-122.6]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-9,14.5],[13.551,58.865]],"o":[[0,0],[0,0],[0,0],[50,-11],[4.515,-7.273],[-13.463,-58.485]],"v":[[-61,-237],[-191,-176],[-109,24],[-76,30],[-11.5,5],[-33.006,-122.007]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-9,14.5],[13.551,58.865]],"o":[[0,0],[0,0],[0,0],[50,-11],[4.515,-7.273],[-13.463,-58.485]],"v":[[-61,-237],[-191,-176],[-109,24],[-76,30],[-11.5,5],[-33.006,-122.007]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-14,18],[17.568,58.925]],"o":[[0,0],[0,0],[0,0],[50,-11],[7.023,-9.029],[-17.454,-58.545]],"v":[[-56,-244],[-191,-176],[-109,24],[-76,30],[7,0],[-19.42,-129.321]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-14,18],[17.568,58.925]],"o":[[0,0],[0,0],[0,0],[50,-11],[7.023,-9.029],[-17.454,-58.545]],"v":[[-56,-244],[-191,-176],[-109,24],[-76,30],[7,0],[-19.42,-129.321]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-12,13],[17.065,63.067]],"o":[[0,0],[0,0],[0,0],[50,-11],[6.019,-6.521],[-16.954,-62.66]],"v":[[-39,-257],[-191,-176],[-109,24],[-76,30],[23,1],[-3.665,-133.486]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-12,13],[17.065,63.067]],"o":[[0,0],[0,0],[0,0],[50,-11],[6.019,-6.521],[-16.954,-62.66]],"v":[[-39,-257],[-191,-176],[-109,24],[-76,30],[23,1],[-3.665,-133.486]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-9,17],[17.689,64.569]],"o":[[0,0],[0,0],[0,0],[50,-11],[4.515,-8.528],[-17.575,-64.152]],"v":[[-13,-271],[-191,-176],[-109,24],[-76,30],[53,-5],[23.204,-145]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-9,17],[17.689,64.569]],"o":[[0,0],[0,0],[0,0],[50,-11],[4.515,-8.528],[-17.575,-64.152]],"v":[[-13,-271],[-191,-176],[-109,24],[-76,30],[53,-5],[23.204,-145]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-16,20],[23.562,61.869]],"o":[[0,0],[0,0],[0,0],[50,-11],[7.752,-9.689],[-8.438,-57.131]],"v":[[17,-282],[-191,-176],[-109,24],[-76,30],[71,-9],[50.438,-146.869]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-16,20],[23.562,61.869]],"o":[[0,0],[0,0],[0,0],[50,-11],[7.752,-9.689],[-8.438,-57.131]],"v":[[17,-282],[-191,-176],[-109,24],[-76,30],[71,-9],[50.438,-146.869]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-16,15],[24.604,34.177]],"o":[[0,0],[0,0],[0,0],[50,-11],[16,-15],[3.604,-39.823]],"v":[[46,-289],[-116,-244],[-109,24],[-51,19],[77,-7],[61.396,-150.177]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-16,15],[24.604,34.177]],"o":[[0,0],[0,0],[0,0],[50,-11],[16,-15],[3.604,-39.823]],"v":[[46,-289],[-116,-244],[-109,24],[-51,19],[77,-7],[61.396,-150.177]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-10.999,17.25],[28.431,29.796]],"o":[[0,0],[0,0],[0,0],[50,-11],[10.999,-17.25],[16.431,-29.204]],"v":[[65.252,-286.25],[-116,-244],[-109,24],[-51,19],[88.001,-9.75],[65.569,-132.796]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-10.999,17.25],[28.431,29.796]],"o":[[0,0],[0,0],[0,0],[50,-11],[10.999,-17.25],[16.431,-29.204]],"v":[[65.252,-286.25],[-116,-244],[-109,24],[-51,19],[88.001,-9.75],[65.569,-132.796]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[50,-11],[0,0],[0,0]],"v":[[92,-282],[-116,-244],[-109,24],[-51,19],[110,22],[100.956,-130.738]],"c":true}]},{"t":19.1474609375}]},"o":{"k":100},"x":{"k":0},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-1.282,17.269],[-0.661,17.73],[1.838,0.593],[0,0],[0,0],[-1.643,0],[-0.912,-1.481]],"o":[[1.259,-16.963],[0.075,-2.02],[-1.701,-0.549],[0,0],[0,0],[1.257,0],[0.338,-3.231]],"v":[[-41.009,-32.537],[-38.089,-79.48],[-39.588,-83.593],[-42.507,-82.468],[-50.507,4.375],[-47.107,4],[-44.213,5.856]],"c":true}],"e":[{"i":[[-0.008,17.316],[0.339,16.48],[3.281,1.942],[0,0],[0,0],[-6.268,-1],[-1.162,-9.106]],"o":[[0.009,-21.463],[-0.098,-4.771],[-4.912,-2.907],[0,0],[0,0],[5.666,0.904],[-0.412,-10.356]],"v":[[-34.509,-42.537],[-34.339,-91.23],[-37.588,-101.593],[-47.007,-102.468],[-59.132,7.5],[-45.982,2.75],[-34.588,14.106]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.008,17.316],[0.339,16.48],[3.281,1.942],[0,0],[0,0],[-6.268,-1],[-1.162,-9.106]],"o":[[0.009,-21.463],[-0.098,-4.771],[-4.912,-2.907],[0,0],[0,0],[5.666,0.904],[-0.412,-10.356]],"v":[[-34.509,-42.537],[-34.339,-91.23],[-37.588,-101.593],[-47.007,-102.468],[-59.132,7.5],[-45.982,2.75],[-34.588,14.106]],"c":true}],"e":[{"i":[[1.009,17.287],[2.01,28.256],[7.354,3.812],[0,0],[0,0],[-11.66,-5.191],[-3.162,-10.856]],"o":[[-1.064,-18.224],[-0.411,-5.77],[-10.912,-5.657],[0,0],[0,0],[11.232,5],[-1.162,-18.356]],"v":[[-16.009,-48.037],[-20.089,-101.23],[-28.588,-120.343],[-52.757,-120.968],[-55.882,3.75],[-26.232,2.5],[-10.838,21.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[1.009,17.287],[2.01,28.256],[7.354,3.812],[0,0],[0,0],[-11.66,-5.191],[-3.162,-10.856]],"o":[[-1.064,-18.224],[-0.411,-5.77],[-10.912,-5.657],[0,0],[0,0],[11.232,5],[-1.162,-18.356]],"v":[[-16.009,-48.037],[-20.089,-101.23],[-28.588,-120.343],[-52.757,-120.968],[-55.882,3.75],[-26.232,2.5],[-10.838,21.356]],"c":true}],"e":[{"i":[[3.581,24.09],[2.339,28.23],[18.566,5.173],[0,0],[0,0],[-42.815,-12.244],[-3.162,-9.856]],"o":[[-4.491,-30.213],[-0.972,-11.734],[-34.662,-9.657],[0,0],[0,0],[19.232,5.5],[-2.412,-16.356]],"v":[[28.491,-50.787],[21.161,-112.48],[-7.588,-142.843],[-60.007,-138.968],[-58.382,3.5],[13.518,-2.5],[41.412,22.606]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[3.581,24.09],[2.339,28.23],[18.566,5.173],[0,0],[0,0],[-42.815,-12.244],[-3.162,-9.856]],"o":[[-4.491,-30.213],[-0.972,-11.734],[-34.662,-9.657],[0,0],[0,0],[19.232,5.5],[-2.412,-16.356]],"v":[[28.491,-50.787],[21.161,-112.48],[-7.588,-142.843],[-60.007,-138.968],[-58.382,3.5],[13.518,-2.5],[41.412,22.606]],"c":true}],"e":[{"i":[[6.259,23.537],[9.028,29.832],[35.338,-11.407],[0,0],[0,0],[-43.647,8.83],[7.838,30.644]],"o":[[-6.901,-25.951],[-3.411,-11.27],[-35.699,11.524],[0,0],[0,0],[66.732,-13.5],[-4.529,-17.709]],"v":[[98.991,-111.037],[77.161,-192.23],[3.912,-187.593],[-68.757,-153.968],[-60.882,2],[23.768,-27],[112.412,-55.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[6.259,23.537],[9.028,29.832],[35.338,-11.407],[0,0],[0,0],[-43.647,8.83],[7.838,30.644]],"o":[[-6.901,-25.951],[-3.411,-11.27],[-35.699,11.524],[0,0],[0,0],[66.732,-13.5],[-4.529,-17.709]],"v":[[98.991,-111.037],[77.161,-192.23],[3.912,-187.593],[-68.757,-153.968],[-60.882,2],[23.768,-27],[112.412,-55.894]],"c":true}],"e":[{"i":[[9.241,25.213],[14.339,35.48],[31.838,-35.407],[0,0],[0,0],[-37.642,23.794],[7.838,30.644]],"o":[[-9.241,-25.213],[-15.283,-37.816],[-16.668,18.536],[0,0],[0,0],[45.482,-28.75],[-4.529,-17.709]],"v":[[33.991,-189.537],[9.161,-252.48],[-36.838,-201.593],[-76.007,-167.218],[-63.882,2],[24.768,-48],[62.412,-113.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[9.241,25.213],[14.339,35.48],[31.838,-35.407],[0,0],[0,0],[-37.642,23.794],[7.838,30.644]],"o":[[-9.241,-25.213],[-15.283,-37.816],[-16.668,18.536],[0,0],[0,0],[45.482,-28.75],[-4.529,-17.709]],"v":[[33.991,-189.537],[9.161,-252.48],[-36.838,-201.593],[-76.007,-167.218],[-63.882,2],[24.768,-48],[62.412,-113.394]],"c":true}],"e":[{"i":[[9.241,25.213],[15.339,35.73],[24.588,-31.657],[0,0],[0,0],[-33.385,29.471],[8.088,31.144]],"o":[[-9.241,-25.213],[-19.116,-44.526],[-15.291,19.687],[0,0],[0,0],[38.232,-33.75],[-4.594,-17.692]],"v":[[1.491,-181.287],[-33.839,-268.23],[-51.588,-209.843],[-83.257,-178.968],[-66.382,1.5],[2.768,-41.25],[25.662,-113.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[9.241,25.213],[15.339,35.73],[24.588,-31.657],[0,0],[0,0],[-33.385,29.471],[8.088,31.144]],"o":[[-9.241,-25.213],[-19.116,-44.526],[-15.291,19.687],[0,0],[0,0],[38.232,-33.75],[-4.594,-17.692]],"v":[[1.491,-181.287],[-33.839,-268.23],[-51.588,-209.843],[-83.257,-178.968],[-66.382,1.5],[2.768,-41.25],[25.662,-113.144]],"c":true}],"e":[{"i":[[9.241,25.213],[14.589,37.98],[24.588,-31.657],[0,0],[0,0],[-36.476,25.546],[8.088,31.144]],"o":[[-9.241,-25.213],[-17.376,-45.234],[-15.291,19.687],[0,0],[0,0],[34.982,-24.5],[-4.594,-17.692]],"v":[[-0.759,-180.537],[-36.839,-272.73],[-56.338,-216.843],[-89.257,-186.468],[-69.382,1],[6.518,-38],[24.662,-104.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[9.241,25.213],[14.589,37.98],[24.588,-31.657],[0,0],[0,0],[-36.476,25.546],[8.088,31.144]],"o":[[-9.241,-25.213],[-17.376,-45.234],[-15.291,19.687],[0,0],[0,0],[34.982,-24.5],[-4.594,-17.692]],"v":[[-0.759,-180.537],[-36.839,-272.73],[-56.338,-216.843],[-89.257,-186.468],[-69.382,1],[6.518,-38],[24.662,-104.894]],"c":true}],"e":[{"i":[[8.009,25.037],[13.247,38.034],[35.338,-37.657],[0,0],[0,0],[-39.772,20.031],[8.088,31.144]],"o":[[-11.991,-32.463],[-17.16,-49.27],[-21.388,22.792],[0,0],[0,0],[36.732,-18.5],[-4.594,-17.692]],"v":[[11.241,-169.037],[-25.59,-271.98],[-60.088,-220.343],[-93.257,-193.718],[-71.382,1],[15.768,-34.75],[32.662,-102.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[8.009,25.037],[13.247,38.034],[35.338,-37.657],[0,0],[0,0],[-39.772,20.031],[8.088,31.144]],"o":[[-11.991,-32.463],[-17.16,-49.27],[-21.388,22.792],[0,0],[0,0],[36.732,-18.5],[-4.594,-17.692]],"v":[[11.241,-169.037],[-25.59,-271.98],[-60.088,-220.343],[-93.257,-193.718],[-71.382,1],[15.768,-34.75],[32.662,-102.144]],"c":true}],"e":[{"i":[[8.259,24.037],[10.641,35.776],[35.838,-35.157],[0,0],[0,0],[-42.018,14.75],[10.338,43.894]],"o":[[-9.241,-31.213],[-13.91,-46.77],[-22.312,21.888],[0,0],[0,0],[40.972,-14.383],[-7.971,-33.842]],"v":[[23.991,-176.037],[-5.09,-269.48],[-49.338,-232.593],[-95.757,-200.718],[-72.382,0.25],[27.268,-29],[47.662,-96.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[8.259,24.037],[10.641,35.776],[35.838,-35.157],[0,0],[0,0],[-42.018,14.75],[10.338,43.894]],"o":[[-9.241,-31.213],[-13.91,-46.77],[-22.312,21.888],[0,0],[0,0],[40.972,-14.383],[-7.971,-33.842]],"v":[[23.991,-176.037],[-5.09,-269.48],[-49.338,-232.593],[-95.757,-200.718],[-72.382,0.25],[27.268,-29],[47.662,-96.394]],"c":true}],"e":[{"i":[[7.009,23.287],[7.59,33.73],[50.58,-35.169],[0,0],[0,0],[0,0],[11.714,49.114]],"o":[[-7.241,-28.463],[-10.225,-45.445],[-25.662,17.843],[0,0],[0,0],[43.732,-7.5],[-10.162,-42.606]],"v":[[42.741,-173.287],[20.411,-260.73],[-42.588,-234.593],[-97.257,-206.968],[-73.882,0.25],[35.018,-20.5],[65.662,-88.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[7.009,23.287],[7.59,33.73],[50.58,-35.169],[0,0],[0,0],[0,0],[11.714,49.114]],"o":[[-7.241,-28.463],[-10.225,-45.445],[-25.662,17.843],[0,0],[0,0],[43.732,-7.5],[-10.162,-42.606]],"v":[[42.741,-173.287],[20.411,-260.73],[-42.588,-234.593],[-97.257,-206.968],[-73.882,0.25],[35.018,-20.5],[65.662,-88.144]],"c":true}],"e":[{"i":[[3.259,21.287],[6.044,31.942],[57.588,-23.657],[0,0],[0,0],[0,0],[10.088,66.644]],"o":[[-1.741,-23.963],[-8.661,-45.77],[-33.448,13.74],[0,0],[0,0],[43.732,-7.5],[-8.74,-57.737]],"v":[[54.991,-175.537],[44.161,-246.98],[-37.838,-233.593],[-96.757,-212.718],[-75.382,0],[38.518,-14.75],[79.662,-80.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[3.259,21.287],[6.044,31.942],[57.588,-23.657],[0,0],[0,0],[0,0],[10.088,66.644]],"o":[[-1.741,-23.963],[-8.661,-45.77],[-33.448,13.74],[0,0],[0,0],[43.732,-7.5],[-8.74,-57.737]],"v":[[54.991,-175.537],[44.161,-246.98],[-37.838,-233.593],[-96.757,-212.718],[-75.382,0],[38.518,-14.75],[79.662,-80.894]],"c":true}],"e":[{"i":[[3.259,21.287],[4.985,35.86],[31.838,-13.407],[0,0],[0,0],[0,0],[7.588,68.394]],"o":[[-1.491,-26.213],[-5.911,-42.52],[-31.691,13.345],[0,0],[0,0],[38.732,-3.75],[-6.443,-58.072]],"v":[[65.241,-152.287],[61.661,-221.73],[10.912,-247.343],[-94.007,-216.968],[-77.382,-0.75],[40.268,-9.5],[88.162,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[3.259,21.287],[4.985,35.86],[31.838,-13.407],[0,0],[0,0],[0,0],[7.588,68.394]],"o":[[-1.491,-26.213],[-5.911,-42.52],[-31.691,13.345],[0,0],[0,0],[38.732,-3.75],[-6.443,-58.072]],"v":[[65.241,-152.287],[61.661,-221.73],[10.912,-247.343],[-94.007,-216.968],[-77.382,-0.75],[40.268,-9.5],[88.162,-79.394]],"c":true}],"e":[{"i":[[2.759,8.787],[5.839,35.73],[33.162,-9.093],[0,0],[0,0],[0,0],[2.478,49.663]],"o":[[-2.741,-7.463],[-6.377,-39.022],[-33.162,9.093],[0,0],[0,0],[38.732,-3.75],[-2.912,-58.356]],"v":[[66.241,-138.287],[71.911,-205.73],[16.162,-241.093],[-91.507,-220.468],[-77.382,-0.75],[41.768,-6.5],[92.912,-66.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[2.759,8.787],[5.839,35.73],[33.162,-9.093],[0,0],[0,0],[0,0],[2.478,49.663]],"o":[[-2.741,-7.463],[-6.377,-39.022],[-33.162,9.093],[0,0],[0,0],[38.732,-3.75],[-2.912,-58.356]],"v":[[66.241,-138.287],[71.911,-205.73],[16.162,-241.093],[-91.507,-220.468],[-77.382,-0.75],[41.768,-6.5],[92.912,-66.894]],"c":true}],"e":[{"i":[[27.259,18.287],[2.089,21.98],[37.838,-4.407],[0,0],[0,0],[0,0],[0.838,46.394]],"o":[[11.509,-17.213],[-3.742,-39.362],[0,0],[0,0],[0,0],[36.982,0],[-0.523,-28.962]],"v":[[63.241,-131.037],[77.161,-188.98],[16.162,-235.843],[-89.507,-222.468],[-78.882,-0.5],[31.768,-3.75],[95.162,-60.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[27.259,18.287],[2.089,21.98],[37.838,-4.407],[0,0],[0,0],[0,0],[0.838,46.394]],"o":[[11.509,-17.213],[-3.742,-39.362],[0,0],[0,0],[0,0],[36.982,0],[-0.523,-28.962]],"v":[[63.241,-131.037],[77.161,-188.98],[16.162,-235.843],[-89.507,-222.468],[-78.882,-0.5],[31.768,-3.75],[95.162,-60.894]],"c":true}],"e":[{"i":[[25.509,15.287],[1.964,22.98],[38.338,-2.657],[0,0],[0,0],[0,0],[-0.037,44.769]],"o":[[11.884,-14.088],[-3.365,-39.367],[0,0],[0,0],[0,0],[36.982,0],[0.026,-31.246]],"v":[[61.616,-125.787],[79.286,-179.98],[15.537,-232.218],[-86.382,-224.093],[-79.007,-0.375],[30.268,-2.75],[94.037,-62.519]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[25.509,15.287],[1.964,22.98],[38.338,-2.657],[0,0],[0,0],[0,0],[-0.037,44.769]],"o":[[11.884,-14.088],[-3.365,-39.367],[0,0],[0,0],[0,0],[36.982,0],[0.026,-31.246]],"v":[[61.616,-125.787],[79.286,-179.98],[15.537,-232.218],[-86.382,-224.093],[-79.007,-0.375],[30.268,-2.75],[94.037,-62.519]],"c":true}],"e":[{"i":[[23.759,12.287],[0.374,21.444],[38.838,-0.907],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[12.259,-10.963],[-0.705,-39.533],[0,0],[0,0],[0,0],[36.982,0],[0.053,-28.967]],"v":[[59.491,-123.537],[79.911,-170.48],[17.412,-229.593],[-83.257,-225.718],[-79.132,-0.25],[28.768,-1.75],[93.412,-61.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[23.759,12.287],[0.374,21.444],[38.838,-0.907],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[12.259,-10.963],[-0.705,-39.533],[0,0],[0,0],[0,0],[36.982,0],[0.053,-28.967]],"v":[[59.491,-123.537],[79.911,-170.48],[17.412,-229.593],[-83.257,-225.718],[-79.132,-0.25],[28.768,-1.75],[93.412,-61.644]],"c":true}],"e":[{"i":[[23.009,8.287],[0.374,21.444],[37.94,-0.602],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[39.468,0.25],[0.053,-28.967]],"v":[[55.991,-119.287],[79.661,-166.48],[11.162,-226.593],[-79.507,-226.218],[-79.132,-0.25],[22.518,-0.25],[91.162,-62.644]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[-2.196,-15.458],[4.87,-2.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[2.979,-1.252]],"v":[[-276.179,-132.292],[-277.37,-102.994],[-282.552,-101.351],[-288.355,-146.931],[-283.229,-149.748]],"c":true}],"e":[{"i":[[-2.196,-15.458],[4.87,-2.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[2.979,-1.252]],"v":[[-45.179,-170.292],[-46.37,-140.994],[-51.552,-139.351],[-57.355,-184.931],[-52.229,-187.748]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[-2.196,-15.458],[4.87,-2.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[2.979,-1.252]],"v":[[-45.179,-170.292],[-46.37,-140.994],[-51.552,-139.351],[-57.355,-184.931],[-52.229,-187.748]],"c":true}],"e":[{"i":[[-2.196,-15.458],[10.37,-3.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[5.729,-1.752]],"v":[[-29.679,-174.292],[-36.62,-141.244],[-48.302,-138.851],[-53.605,-186.431],[-42.979,-189.748]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[-2.196,-15.458],[10.37,-3.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[5.729,-1.752]],"v":[[-29.679,-174.292],[-36.62,-141.244],[-48.302,-138.851],[-53.605,-186.431],[-42.979,-189.748]],"c":true}],"e":[{"i":[[-2.196,-15.458],[11.62,-1.881],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[12.104,-2.127]],"v":[[-14.179,-171.542],[-26.62,-140.744],[-44.802,-137.851],[-48.605,-186.931],[-32.479,-190.998]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[-2.196,-15.458],[11.62,-1.881],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[12.104,-2.127]],"v":[[-14.179,-171.542],[-26.62,-140.744],[-44.802,-137.851],[-48.605,-186.931],[-32.479,-190.998]],"c":true}],"e":[{"i":[[-0.946,-15.083],[11.62,-1.881],[0,0],[0,0],[0,0]],"o":[[0.889,14.172],[0,0],[0,0],[0,0],[11.854,-1.752]],"v":[[-0.554,-168.667],[-16.12,-139.619],[-41.927,-136.351],[-45.105,-186.931],[-21.854,-190.998]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[-0.946,-15.083],[11.62,-1.881],[0,0],[0,0],[0,0]],"o":[[0.889,14.172],[0,0],[0,0],[0,0],[11.854,-1.752]],"v":[[-0.554,-168.667],[-16.12,-139.619],[-41.927,-136.351],[-45.105,-186.931],[-21.854,-190.998]],"c":true}],"e":[{"i":[[0,-14.502],[17.62,-0.131],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[16.104,-1.752]],"v":[[9.196,-166.167],[-14.62,-138.119],[-39.927,-136.351],[-41.855,-185.931],[-15.354,-189.498]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[0,-14.502],[17.62,-0.131],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[16.104,-1.752]],"v":[[9.196,-166.167],[-14.62,-138.119],[-39.927,-136.351],[-41.855,-185.931],[-15.354,-189.498]],"c":true}],"e":[{"i":[[0,-14.502],[18.37,-0.631],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[15.303,-0.876]],"v":[[18.196,-164.417],[-8.37,-136.494],[-37.552,-135.101],[-38.855,-185.806],[-8.479,-188.373]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[0,-14.502],[18.37,-0.631],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[15.303,-0.876]],"v":[[18.196,-164.417],[-8.37,-136.494],[-37.552,-135.101],[-38.855,-185.806],[-8.479,-188.373]],"c":true}],"e":[{"i":[[0,-14.502],[19.12,-1.131],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[24.196,-162.667],[-2.12,-134.869],[-35.177,-133.851],[-35.855,-185.681],[-1.604,-187.248]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[0,-14.502],[19.12,-1.131],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[24.196,-162.667],[-2.12,-134.869],[-35.177,-133.851],[-35.855,-185.681],[-1.604,-187.248]],"c":true}],"e":[{"i":[[0,-14.502],[18.37,0.935],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[30.446,-160.417],[1.88,-133.369],[-33.177,-133.101],[-33.105,-184.681],[4.646,-184.998]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ind":2,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[6.056,-1.63],[0,0],[0,0],[0,0],[-1.281,-9.949]],"o":[[0,0],[0,0],[0,0],[4.931,-1.967],[1.649,12.81]],"v":[[-287.306,-16.62],[-293.677,-14.62],[-299.802,-64.783],[-294.431,-67.033],[-284.719,-47.051]],"c":true}],"e":[{"i":[[6.056,-1.63],[0,0],[0,0],[0,0],[-1.281,-9.949]],"o":[[0,0],[0,0],[0,0],[4.931,-1.967],[1.649,12.81]],"v":[[-33.306,-52.62],[-39.677,-50.62],[-45.802,-100.783],[-40.431,-103.033],[-30.719,-83.051]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[6.056,-1.63],[0,0],[0,0],[0,0],[-1.281,-9.949]],"o":[[0,0],[0,0],[0,0],[4.931,-1.967],[1.649,12.81]],"v":[[-33.306,-52.62],[-39.677,-50.62],[-45.802,-100.783],[-40.431,-103.033],[-30.719,-83.051]],"c":true}],"e":[{"i":[[11.556,-3.13],[0,0],[0,0],[0,0],[-0.953,-10.127]],"o":[[0,0],[0,0],[0,0],[8.681,-1.967],[0.969,10.301]],"v":[[-21.806,-51.87],[-37.427,-49.12],[-43.552,-99.783],[-28.431,-103.533],[-13.719,-83.551]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[11.556,-3.13],[0,0],[0,0],[0,0],[-0.953,-10.127]],"o":[[0,0],[0,0],[0,0],[8.681,-1.967],[0.969,10.301]],"v":[[-21.806,-51.87],[-37.427,-49.12],[-43.552,-99.783],[-28.431,-103.533],[-13.719,-83.551]],"c":true}],"e":[{"i":[[13.181,-3.38],[0,0],[0,0],[0,0],[-0.953,-10.127]],"o":[[0,0],[0,0],[0,0],[14.306,-2.342],[0.969,10.301]],"v":[[-10.556,-49.87],[-36.177,-47.37],[-40.302,-98.783],[-18.181,-102.533],[2.031,-79.801]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[13.181,-3.38],[0,0],[0,0],[0,0],[-0.953,-10.127]],"o":[[0,0],[0,0],[0,0],[14.306,-2.342],[0.969,10.301]],"v":[[-10.556,-49.87],[-36.177,-47.37],[-40.302,-98.783],[-18.181,-102.533],[2.031,-79.801]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[-5.181,-47.745],[-34.927,-46.745],[-38.427,-97.908],[-7.931,-101.158],[14.156,-75.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[-5.181,-47.745],[-34.927,-46.745],[-38.427,-97.908],[-7.931,-101.158],[14.156,-75.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[2.819,-46.495],[-33.927,-44.495],[-36.427,-96.908],[0.069,-98.908],[23.656,-72.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[2.819,-46.495],[-33.927,-44.495],[-36.427,-96.908],[0.069,-98.908],[23.656,-72.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[16.556,-0.967],[0,14.2]],"v":[[6.819,-45.12],[-33.427,-44.245],[-34.802,-95.783],[5.694,-97.283],[31.781,-71.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[16.556,-0.967],[0,14.2]],"v":[[6.819,-45.12],[-33.427,-44.245],[-34.802,-95.783],[5.694,-97.283],[31.781,-71.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[10.819,-43.745],[-32.927,-43.995],[-33.177,-94.658],[11.319,-95.658],[36.406,-69.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[10.819,-43.745],[-32.927,-43.995],[-33.177,-94.658],[11.319,-95.658],[36.406,-69.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[14.819,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-94.158],[41.656,-68.176]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.32,0.5,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":15,"op":23,"st":0,"bm":10,"sr":1},{"ddd":0,"ind":23,"ty":4,"nm":"B white layer 2 shadow","parent":34,"ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16,"s":[5],"e":[40]},{"t":22}]},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[0,0],[0,0]],"v":[[-38.124,-198.023],[-153.77,-179.155],[-179.51,0.476],[-152.785,32.892],[-44.446,28.81],[-41.27,-85.157]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[0,0],[0,0]],"v":[[-26.124,-205.023],[-141.77,-186.155],[-167.51,-6.524],[-140.785,25.892],[-10.446,30.81],[-18.323,-87.679]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[0,0],[0,0]],"v":[[-26.124,-205.023],[-141.77,-186.155],[-167.51,-6.524],[-140.785,25.892],[-10.446,30.81],[-18.323,-87.679]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[-22.446,5.81],[5.009,56.785]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[11.259,-2.914],[-4.977,-56.418]],"v":[[7.876,-190.023],[-107.77,-171.155],[-133.51,8.476],[-106.785,40.892],[16.554,39.31],[20.584,-78.085]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[0,0],[0,0],[0,0],[-4,0.01],[-22.446,5.81],[5.009,56.785]],"o":[[0,0],[0,0],[0,0],[51.196,-0.129],[11.259,-2.914],[-4.977,-56.418]],"v":[[7.876,-190.023],[-107.77,-171.155],[-133.51,8.476],[-106.785,40.892],[16.554,39.31],[20.584,-78.085]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-18.5,31.5],[9.358,52.957]],"o":[[0,0],[0,0],[0,0],[50,-11],[9.28,-15.801],[-9.297,-52.616]],"v":[[37.5,-220],[-101,-170],[-88,11],[-55,37],[65.5,7],[58.347,-118.825]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-18.5,31.5],[9.358,52.957]],"o":[[0,0],[0,0],[0,0],[50,-11],[9.28,-15.801],[-9.297,-52.616]],"v":[[37.5,-220],[-101,-170],[-88,11],[-55,37],[65.5,7],[58.347,-118.825]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-18.5,33],[14.123,54.022]],"o":[[0,0],[0,0],[0,0],[50,-11],[9.28,-16.553],[-14.032,-53.673]],"v":[[-38.5,-242],[-236,-164],[-88,11],[-55,36],[8.5,-10],[-8.199,-138.898]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-18.5,33],[14.123,54.022]],"o":[[0,0],[0,0],[0,0],[50,-11],[9.28,-16.553],[-14.032,-53.673]],"v":[[-38.5,-242],[-236,-164],[-88,11],[-55,36],[8.5,-10],[-8.199,-138.898]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-10.976,14.749],[13.055,55.474]],"o":[[0,0],[0,0],[0,0],[50,-11],[8.026,-10.785],[-12.971,-55.116]],"v":[[-61,-230],[-236,-164],[-109,24],[-60,28],[-17,2],[-33.126,-122.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-10.976,14.749],[13.055,55.474]],"o":[[0,0],[0,0],[0,0],[50,-11],[8.026,-10.785],[-12.971,-55.116]],"v":[[-61,-230],[-236,-164],[-109,24],[-60,28],[-17,2],[-33.126,-122.6]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-9,14.5],[13.551,58.865]],"o":[[0,0],[0,0],[0,0],[50,-11],[4.515,-7.273],[-13.463,-58.485]],"v":[[-61,-237],[-191,-176],[-109,24],[-76,30],[-11.5,5],[-33.006,-122.007]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-9,14.5],[13.551,58.865]],"o":[[0,0],[0,0],[0,0],[50,-11],[4.515,-7.273],[-13.463,-58.485]],"v":[[-61,-237],[-191,-176],[-109,24],[-76,30],[-11.5,5],[-33.006,-122.007]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-14,18],[17.568,58.925]],"o":[[0,0],[0,0],[0,0],[50,-11],[7.023,-9.029],[-17.454,-58.545]],"v":[[-56,-244],[-191,-176],[-109,24],[-76,30],[7,0],[-19.42,-129.321]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-14,18],[17.568,58.925]],"o":[[0,0],[0,0],[0,0],[50,-11],[7.023,-9.029],[-17.454,-58.545]],"v":[[-56,-244],[-191,-176],[-109,24],[-76,30],[7,0],[-19.42,-129.321]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-12,13],[17.065,63.067]],"o":[[0,0],[0,0],[0,0],[50,-11],[6.019,-6.521],[-16.954,-62.66]],"v":[[-39,-257],[-191,-176],[-109,24],[-76,30],[23,1],[-3.665,-133.486]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-12,13],[17.065,63.067]],"o":[[0,0],[0,0],[0,0],[50,-11],[6.019,-6.521],[-16.954,-62.66]],"v":[[-39,-257],[-191,-176],[-109,24],[-76,30],[23,1],[-3.665,-133.486]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-9,17],[17.689,64.569]],"o":[[0,0],[0,0],[0,0],[50,-11],[4.515,-8.528],[-17.575,-64.152]],"v":[[-13,-271],[-191,-176],[-109,24],[-76,30],[53,-5],[23.204,-145]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-9,17],[17.689,64.569]],"o":[[0,0],[0,0],[0,0],[50,-11],[4.515,-8.528],[-17.575,-64.152]],"v":[[-13,-271],[-191,-176],[-109,24],[-76,30],[53,-5],[23.204,-145]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-16,20],[23.562,61.869]],"o":[[0,0],[0,0],[0,0],[50,-11],[7.752,-9.689],[-8.438,-57.131]],"v":[[17,-282],[-191,-176],[-109,24],[-76,30],[71,-9],[50.438,-146.869]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-16,20],[23.562,61.869]],"o":[[0,0],[0,0],[0,0],[50,-11],[7.752,-9.689],[-8.438,-57.131]],"v":[[17,-282],[-191,-176],[-109,24],[-76,30],[71,-9],[50.438,-146.869]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-16,15],[24.604,34.177]],"o":[[0,0],[0,0],[0,0],[50,-11],[16,-15],[3.604,-39.823]],"v":[[46,-289],[-116,-244],[-109,24],[-51,19],[77,-7],[61.396,-150.177]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-16,15],[24.604,34.177]],"o":[[0,0],[0,0],[0,0],[50,-11],[16,-15],[3.604,-39.823]],"v":[[46,-289],[-116,-244],[-109,24],[-51,19],[77,-7],[61.396,-150.177]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-10.999,17.25],[28.431,29.796]],"o":[[0,0],[0,0],[0,0],[50,-11],[10.999,-17.25],[16.431,-29.204]],"v":[[65.252,-286.25],[-116,-244],[-109,24],[-51,19],[88.001,-9.75],[65.569,-132.796]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18,"s":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[-10.999,17.25],[28.431,29.796]],"o":[[0,0],[0,0],[0,0],[50,-11],[10.999,-17.25],[16.431,-29.204]],"v":[[65.252,-286.25],[-116,-244],[-109,24],[-51,19],[88.001,-9.75],[65.569,-132.796]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[-3.907,0.859],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[50,-11],[0,0],[0,0]],"v":[[92,-282],[-116,-244],[-109,24],[-51,19],[110,22],[100.956,-130.738]],"c":true}]},{"t":19.1474609375}]},"o":{"k":100},"x":{"k":0},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-1.282,17.269],[-0.661,17.73],[1.838,0.593],[0,0],[0,0],[-1.643,0],[-0.912,-1.481]],"o":[[1.259,-16.963],[0.075,-2.02],[-1.701,-0.549],[0,0],[0,0],[1.257,0],[0.338,-3.231]],"v":[[-41.009,-32.537],[-38.089,-79.48],[-39.588,-83.593],[-42.507,-82.468],[-50.507,4.375],[-47.107,4],[-44.213,5.856]],"c":true}],"e":[{"i":[[-0.008,17.316],[0.339,16.48],[3.281,1.942],[0,0],[0,0],[-6.268,-1],[-1.162,-9.106]],"o":[[0.009,-21.463],[-0.098,-4.771],[-4.912,-2.907],[0,0],[0,0],[5.666,0.904],[-0.412,-10.356]],"v":[[-34.509,-42.537],[-34.339,-91.23],[-37.588,-101.593],[-47.007,-102.468],[-59.132,7.5],[-45.982,2.75],[-34.588,14.106]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.008,17.316],[0.339,16.48],[3.281,1.942],[0,0],[0,0],[-6.268,-1],[-1.162,-9.106]],"o":[[0.009,-21.463],[-0.098,-4.771],[-4.912,-2.907],[0,0],[0,0],[5.666,0.904],[-0.412,-10.356]],"v":[[-34.509,-42.537],[-34.339,-91.23],[-37.588,-101.593],[-47.007,-102.468],[-59.132,7.5],[-45.982,2.75],[-34.588,14.106]],"c":true}],"e":[{"i":[[1.009,17.287],[2.01,28.256],[7.354,3.812],[0,0],[0,0],[-11.66,-5.191],[-3.162,-10.856]],"o":[[-1.064,-18.224],[-0.411,-5.77],[-10.912,-5.657],[0,0],[0,0],[11.232,5],[-1.162,-18.356]],"v":[[-16.009,-48.037],[-20.089,-101.23],[-28.588,-120.343],[-52.757,-120.968],[-55.882,3.75],[-26.232,2.5],[-10.838,21.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[1.009,17.287],[2.01,28.256],[7.354,3.812],[0,0],[0,0],[-11.66,-5.191],[-3.162,-10.856]],"o":[[-1.064,-18.224],[-0.411,-5.77],[-10.912,-5.657],[0,0],[0,0],[11.232,5],[-1.162,-18.356]],"v":[[-16.009,-48.037],[-20.089,-101.23],[-28.588,-120.343],[-52.757,-120.968],[-55.882,3.75],[-26.232,2.5],[-10.838,21.356]],"c":true}],"e":[{"i":[[3.581,24.09],[2.339,28.23],[18.566,5.173],[0,0],[0,0],[-42.815,-12.244],[-3.162,-9.856]],"o":[[-4.491,-30.213],[-0.972,-11.734],[-34.662,-9.657],[0,0],[0,0],[19.232,5.5],[-2.412,-16.356]],"v":[[28.491,-50.787],[21.161,-112.48],[-7.588,-142.843],[-60.007,-138.968],[-58.382,3.5],[13.518,-2.5],[41.412,22.606]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[3.581,24.09],[2.339,28.23],[18.566,5.173],[0,0],[0,0],[-42.815,-12.244],[-3.162,-9.856]],"o":[[-4.491,-30.213],[-0.972,-11.734],[-34.662,-9.657],[0,0],[0,0],[19.232,5.5],[-2.412,-16.356]],"v":[[28.491,-50.787],[21.161,-112.48],[-7.588,-142.843],[-60.007,-138.968],[-58.382,3.5],[13.518,-2.5],[41.412,22.606]],"c":true}],"e":[{"i":[[6.259,23.537],[9.028,29.832],[35.338,-11.407],[0,0],[0,0],[-43.647,8.83],[7.838,30.644]],"o":[[-6.901,-25.951],[-3.411,-11.27],[-35.699,11.524],[0,0],[0,0],[66.732,-13.5],[-4.529,-17.709]],"v":[[98.991,-111.037],[77.161,-192.23],[3.912,-187.593],[-68.757,-153.968],[-60.882,2],[23.768,-27],[112.412,-55.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[6.259,23.537],[9.028,29.832],[35.338,-11.407],[0,0],[0,0],[-43.647,8.83],[7.838,30.644]],"o":[[-6.901,-25.951],[-3.411,-11.27],[-35.699,11.524],[0,0],[0,0],[66.732,-13.5],[-4.529,-17.709]],"v":[[98.991,-111.037],[77.161,-192.23],[3.912,-187.593],[-68.757,-153.968],[-60.882,2],[23.768,-27],[112.412,-55.894]],"c":true}],"e":[{"i":[[9.241,25.213],[14.339,35.48],[31.838,-35.407],[0,0],[0,0],[-37.642,23.794],[7.838,30.644]],"o":[[-9.241,-25.213],[-15.283,-37.816],[-16.668,18.536],[0,0],[0,0],[45.482,-28.75],[-4.529,-17.709]],"v":[[33.991,-189.537],[9.161,-252.48],[-36.838,-201.593],[-76.007,-167.218],[-63.882,2],[24.768,-48],[62.412,-113.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[9.241,25.213],[14.339,35.48],[31.838,-35.407],[0,0],[0,0],[-37.642,23.794],[7.838,30.644]],"o":[[-9.241,-25.213],[-15.283,-37.816],[-16.668,18.536],[0,0],[0,0],[45.482,-28.75],[-4.529,-17.709]],"v":[[33.991,-189.537],[9.161,-252.48],[-36.838,-201.593],[-76.007,-167.218],[-63.882,2],[24.768,-48],[62.412,-113.394]],"c":true}],"e":[{"i":[[9.241,25.213],[15.339,35.73],[24.588,-31.657],[0,0],[0,0],[-33.385,29.471],[8.088,31.144]],"o":[[-9.241,-25.213],[-19.116,-44.526],[-15.291,19.687],[0,0],[0,0],[38.232,-33.75],[-4.594,-17.692]],"v":[[1.491,-181.287],[-33.839,-268.23],[-51.588,-209.843],[-83.257,-178.968],[-66.382,1.5],[2.768,-41.25],[25.662,-113.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[9.241,25.213],[15.339,35.73],[24.588,-31.657],[0,0],[0,0],[-33.385,29.471],[8.088,31.144]],"o":[[-9.241,-25.213],[-19.116,-44.526],[-15.291,19.687],[0,0],[0,0],[38.232,-33.75],[-4.594,-17.692]],"v":[[1.491,-181.287],[-33.839,-268.23],[-51.588,-209.843],[-83.257,-178.968],[-66.382,1.5],[2.768,-41.25],[25.662,-113.144]],"c":true}],"e":[{"i":[[9.241,25.213],[14.589,37.98],[24.588,-31.657],[0,0],[0,0],[-36.476,25.546],[8.088,31.144]],"o":[[-9.241,-25.213],[-17.376,-45.234],[-15.291,19.687],[0,0],[0,0],[34.982,-24.5],[-4.594,-17.692]],"v":[[-0.759,-180.537],[-36.839,-272.73],[-56.338,-216.843],[-89.257,-186.468],[-69.382,1],[6.518,-38],[24.662,-104.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[9.241,25.213],[14.589,37.98],[24.588,-31.657],[0,0],[0,0],[-36.476,25.546],[8.088,31.144]],"o":[[-9.241,-25.213],[-17.376,-45.234],[-15.291,19.687],[0,0],[0,0],[34.982,-24.5],[-4.594,-17.692]],"v":[[-0.759,-180.537],[-36.839,-272.73],[-56.338,-216.843],[-89.257,-186.468],[-69.382,1],[6.518,-38],[24.662,-104.894]],"c":true}],"e":[{"i":[[8.009,25.037],[13.247,38.034],[35.338,-37.657],[0,0],[0,0],[-39.772,20.031],[8.088,31.144]],"o":[[-11.991,-32.463],[-17.16,-49.27],[-21.388,22.792],[0,0],[0,0],[36.732,-18.5],[-4.594,-17.692]],"v":[[11.241,-169.037],[-25.59,-271.98],[-60.088,-220.343],[-93.257,-193.718],[-71.382,1],[15.768,-34.75],[32.662,-102.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[8.009,25.037],[13.247,38.034],[35.338,-37.657],[0,0],[0,0],[-39.772,20.031],[8.088,31.144]],"o":[[-11.991,-32.463],[-17.16,-49.27],[-21.388,22.792],[0,0],[0,0],[36.732,-18.5],[-4.594,-17.692]],"v":[[11.241,-169.037],[-25.59,-271.98],[-60.088,-220.343],[-93.257,-193.718],[-71.382,1],[15.768,-34.75],[32.662,-102.144]],"c":true}],"e":[{"i":[[8.259,24.037],[10.641,35.776],[35.838,-35.157],[0,0],[0,0],[-42.018,14.75],[10.338,43.894]],"o":[[-9.241,-31.213],[-13.91,-46.77],[-22.312,21.888],[0,0],[0,0],[40.972,-14.383],[-7.971,-33.842]],"v":[[23.991,-176.037],[-5.09,-269.48],[-49.338,-232.593],[-95.757,-200.718],[-72.382,0.25],[27.268,-29],[47.662,-96.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[8.259,24.037],[10.641,35.776],[35.838,-35.157],[0,0],[0,0],[-42.018,14.75],[10.338,43.894]],"o":[[-9.241,-31.213],[-13.91,-46.77],[-22.312,21.888],[0,0],[0,0],[40.972,-14.383],[-7.971,-33.842]],"v":[[23.991,-176.037],[-5.09,-269.48],[-49.338,-232.593],[-95.757,-200.718],[-72.382,0.25],[27.268,-29],[47.662,-96.394]],"c":true}],"e":[{"i":[[7.009,23.287],[7.59,33.73],[50.58,-35.169],[0,0],[0,0],[0,0],[11.714,49.114]],"o":[[-7.241,-28.463],[-10.225,-45.445],[-25.662,17.843],[0,0],[0,0],[43.732,-7.5],[-10.162,-42.606]],"v":[[42.741,-173.287],[20.411,-260.73],[-42.588,-234.593],[-97.257,-206.968],[-73.882,0.25],[35.018,-20.5],[65.662,-88.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[7.009,23.287],[7.59,33.73],[50.58,-35.169],[0,0],[0,0],[0,0],[11.714,49.114]],"o":[[-7.241,-28.463],[-10.225,-45.445],[-25.662,17.843],[0,0],[0,0],[43.732,-7.5],[-10.162,-42.606]],"v":[[42.741,-173.287],[20.411,-260.73],[-42.588,-234.593],[-97.257,-206.968],[-73.882,0.25],[35.018,-20.5],[65.662,-88.144]],"c":true}],"e":[{"i":[[3.259,21.287],[6.044,31.942],[57.588,-23.657],[0,0],[0,0],[0,0],[10.088,66.644]],"o":[[-1.741,-23.963],[-8.661,-45.77],[-33.448,13.74],[0,0],[0,0],[43.732,-7.5],[-8.74,-57.737]],"v":[[54.991,-175.537],[44.161,-246.98],[-37.838,-233.593],[-96.757,-212.718],[-75.382,0],[38.518,-14.75],[79.662,-80.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[3.259,21.287],[6.044,31.942],[57.588,-23.657],[0,0],[0,0],[0,0],[10.088,66.644]],"o":[[-1.741,-23.963],[-8.661,-45.77],[-33.448,13.74],[0,0],[0,0],[43.732,-7.5],[-8.74,-57.737]],"v":[[54.991,-175.537],[44.161,-246.98],[-37.838,-233.593],[-96.757,-212.718],[-75.382,0],[38.518,-14.75],[79.662,-80.894]],"c":true}],"e":[{"i":[[3.259,21.287],[4.985,35.86],[31.838,-13.407],[0,0],[0,0],[0,0],[7.588,68.394]],"o":[[-1.491,-26.213],[-5.911,-42.52],[-31.691,13.345],[0,0],[0,0],[38.732,-3.75],[-6.443,-58.072]],"v":[[65.241,-152.287],[61.661,-221.73],[10.912,-247.343],[-94.007,-216.968],[-77.382,-0.75],[40.268,-9.5],[88.162,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[3.259,21.287],[4.985,35.86],[31.838,-13.407],[0,0],[0,0],[0,0],[7.588,68.394]],"o":[[-1.491,-26.213],[-5.911,-42.52],[-31.691,13.345],[0,0],[0,0],[38.732,-3.75],[-6.443,-58.072]],"v":[[65.241,-152.287],[61.661,-221.73],[10.912,-247.343],[-94.007,-216.968],[-77.382,-0.75],[40.268,-9.5],[88.162,-79.394]],"c":true}],"e":[{"i":[[2.759,8.787],[5.839,35.73],[33.162,-9.093],[0,0],[0,0],[0,0],[2.478,49.663]],"o":[[-2.741,-7.463],[-6.377,-39.022],[-33.162,9.093],[0,0],[0,0],[38.732,-3.75],[-2.912,-58.356]],"v":[[66.241,-138.287],[71.911,-205.73],[16.162,-241.093],[-91.507,-220.468],[-77.382,-0.75],[41.768,-6.5],[92.912,-66.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[2.759,8.787],[5.839,35.73],[33.162,-9.093],[0,0],[0,0],[0,0],[2.478,49.663]],"o":[[-2.741,-7.463],[-6.377,-39.022],[-33.162,9.093],[0,0],[0,0],[38.732,-3.75],[-2.912,-58.356]],"v":[[66.241,-138.287],[71.911,-205.73],[16.162,-241.093],[-91.507,-220.468],[-77.382,-0.75],[41.768,-6.5],[92.912,-66.894]],"c":true}],"e":[{"i":[[27.259,18.287],[2.089,21.98],[37.838,-4.407],[0,0],[0,0],[0,0],[0.838,46.394]],"o":[[11.509,-17.213],[-3.742,-39.362],[0,0],[0,0],[0,0],[36.982,0],[-0.523,-28.962]],"v":[[63.241,-131.037],[77.161,-188.98],[16.162,-235.843],[-89.507,-222.468],[-78.882,-0.5],[31.768,-3.75],[95.162,-60.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[27.259,18.287],[2.089,21.98],[37.838,-4.407],[0,0],[0,0],[0,0],[0.838,46.394]],"o":[[11.509,-17.213],[-3.742,-39.362],[0,0],[0,0],[0,0],[36.982,0],[-0.523,-28.962]],"v":[[63.241,-131.037],[77.161,-188.98],[16.162,-235.843],[-89.507,-222.468],[-78.882,-0.5],[31.768,-3.75],[95.162,-60.894]],"c":true}],"e":[{"i":[[25.509,15.287],[1.964,22.98],[38.338,-2.657],[0,0],[0,0],[0,0],[-0.037,44.769]],"o":[[11.884,-14.088],[-3.365,-39.367],[0,0],[0,0],[0,0],[36.982,0],[0.026,-31.246]],"v":[[61.616,-125.787],[79.286,-179.98],[15.537,-232.218],[-86.382,-224.093],[-79.007,-0.375],[30.268,-2.75],[94.037,-62.519]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[25.509,15.287],[1.964,22.98],[38.338,-2.657],[0,0],[0,0],[0,0],[-0.037,44.769]],"o":[[11.884,-14.088],[-3.365,-39.367],[0,0],[0,0],[0,0],[36.982,0],[0.026,-31.246]],"v":[[61.616,-125.787],[79.286,-179.98],[15.537,-232.218],[-86.382,-224.093],[-79.007,-0.375],[30.268,-2.75],[94.037,-62.519]],"c":true}],"e":[{"i":[[23.759,12.287],[0.374,21.444],[38.838,-0.907],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[12.259,-10.963],[-0.705,-39.533],[0,0],[0,0],[0,0],[36.982,0],[0.053,-28.967]],"v":[[59.491,-123.537],[79.911,-170.48],[17.412,-229.593],[-83.257,-225.718],[-79.132,-0.25],[28.768,-1.75],[93.412,-61.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[23.759,12.287],[0.374,21.444],[38.838,-0.907],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[12.259,-10.963],[-0.705,-39.533],[0,0],[0,0],[0,0],[36.982,0],[0.053,-28.967]],"v":[[59.491,-123.537],[79.911,-170.48],[17.412,-229.593],[-83.257,-225.718],[-79.132,-0.25],[28.768,-1.75],[93.412,-61.644]],"c":true}],"e":[{"i":[[23.009,8.287],[0.374,21.444],[37.94,-0.602],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[39.468,0.25],[0.053,-28.967]],"v":[[55.991,-119.287],[79.661,-166.48],[11.162,-226.593],[-79.507,-226.218],[-79.132,-0.25],[22.518,-0.25],[91.162,-62.644]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.32,0.5,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":6,"op":15,"st":0,"bm":10,"sr":1},{"ddd":0,"ind":24,"ty":4,"nm":"B white layer 3","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-1.282,17.269],[-0.661,17.73],[1.838,0.593],[0,0],[0,0],[-1.643,0],[-0.912,-1.481]],"o":[[1.259,-16.963],[0.075,-2.02],[-1.701,-0.549],[0,0],[0,0],[1.257,0],[0.338,-3.231]],"v":[[-41.009,-32.537],[-38.089,-79.48],[-39.588,-83.593],[-42.507,-82.468],[-50.507,4.375],[-47.107,4],[-44.213,5.856]],"c":true}],"e":[{"i":[[-0.008,17.316],[0.339,16.48],[3.281,1.942],[0,0],[0,0],[-6.268,-1],[-1.162,-9.106]],"o":[[0.009,-21.463],[-0.098,-4.771],[-4.912,-2.907],[0,0],[0,0],[5.666,0.904],[-0.412,-10.356]],"v":[[-34.509,-42.537],[-34.339,-91.23],[-37.588,-101.593],[-47.007,-102.468],[-59.132,7.5],[-45.982,2.75],[-34.588,14.106]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.008,17.316],[0.339,16.48],[3.281,1.942],[0,0],[0,0],[-6.268,-1],[-1.162,-9.106]],"o":[[0.009,-21.463],[-0.098,-4.771],[-4.912,-2.907],[0,0],[0,0],[5.666,0.904],[-0.412,-10.356]],"v":[[-34.509,-42.537],[-34.339,-91.23],[-37.588,-101.593],[-47.007,-102.468],[-59.132,7.5],[-45.982,2.75],[-34.588,14.106]],"c":true}],"e":[{"i":[[1.009,17.287],[2.01,28.256],[7.354,3.812],[0,0],[0,0],[-11.66,-5.191],[-3.162,-10.856]],"o":[[-1.064,-18.224],[-0.411,-5.77],[-10.912,-5.657],[0,0],[0,0],[11.232,5],[-1.162,-18.356]],"v":[[-16.009,-48.037],[-20.089,-101.23],[-28.588,-120.343],[-52.757,-120.968],[-55.882,3.75],[-26.232,2.5],[-10.838,21.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[1.009,17.287],[2.01,28.256],[7.354,3.812],[0,0],[0,0],[-11.66,-5.191],[-3.162,-10.856]],"o":[[-1.064,-18.224],[-0.411,-5.77],[-10.912,-5.657],[0,0],[0,0],[11.232,5],[-1.162,-18.356]],"v":[[-16.009,-48.037],[-20.089,-101.23],[-28.588,-120.343],[-52.757,-120.968],[-55.882,3.75],[-26.232,2.5],[-10.838,21.356]],"c":true}],"e":[{"i":[[3.581,24.09],[2.339,28.23],[18.566,5.173],[0,0],[0,0],[-42.815,-12.244],[-3.162,-9.856]],"o":[[-4.491,-30.213],[-0.972,-11.734],[-34.662,-9.657],[0,0],[0,0],[19.232,5.5],[-2.412,-16.356]],"v":[[28.491,-50.787],[21.161,-112.48],[-7.588,-142.843],[-60.007,-138.968],[-58.382,3.5],[13.518,-2.5],[41.412,22.606]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[3.581,24.09],[2.339,28.23],[18.566,5.173],[0,0],[0,0],[-42.815,-12.244],[-3.162,-9.856]],"o":[[-4.491,-30.213],[-0.972,-11.734],[-34.662,-9.657],[0,0],[0,0],[19.232,5.5],[-2.412,-16.356]],"v":[[28.491,-50.787],[21.161,-112.48],[-7.588,-142.843],[-60.007,-138.968],[-58.382,3.5],[13.518,-2.5],[41.412,22.606]],"c":true}],"e":[{"i":[[6.259,23.537],[9.028,29.832],[35.338,-11.407],[0,0],[0,0],[-43.647,8.83],[7.838,30.644]],"o":[[-6.901,-25.951],[-3.411,-11.27],[-35.699,11.524],[0,0],[0,0],[66.732,-13.5],[-4.529,-17.709]],"v":[[98.991,-111.037],[77.161,-192.23],[3.912,-187.593],[-68.757,-153.968],[-60.882,2],[23.768,-27],[112.412,-55.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[6.259,23.537],[9.028,29.832],[35.338,-11.407],[0,0],[0,0],[-43.647,8.83],[7.838,30.644]],"o":[[-6.901,-25.951],[-3.411,-11.27],[-35.699,11.524],[0,0],[0,0],[66.732,-13.5],[-4.529,-17.709]],"v":[[98.991,-111.037],[77.161,-192.23],[3.912,-187.593],[-68.757,-153.968],[-60.882,2],[23.768,-27],[112.412,-55.894]],"c":true}],"e":[{"i":[[9.241,25.213],[14.339,35.48],[31.838,-35.407],[0,0],[0,0],[-37.642,23.794],[7.838,30.644]],"o":[[-9.241,-25.213],[-15.283,-37.816],[-16.668,18.536],[0,0],[0,0],[45.482,-28.75],[-4.529,-17.709]],"v":[[33.991,-189.537],[9.161,-252.48],[-36.838,-201.593],[-76.007,-167.218],[-63.882,2],[24.768,-48],[62.412,-113.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[9.241,25.213],[14.339,35.48],[31.838,-35.407],[0,0],[0,0],[-37.642,23.794],[7.838,30.644]],"o":[[-9.241,-25.213],[-15.283,-37.816],[-16.668,18.536],[0,0],[0,0],[45.482,-28.75],[-4.529,-17.709]],"v":[[33.991,-189.537],[9.161,-252.48],[-36.838,-201.593],[-76.007,-167.218],[-63.882,2],[24.768,-48],[62.412,-113.394]],"c":true}],"e":[{"i":[[9.241,25.213],[15.339,35.73],[24.588,-31.657],[0,0],[0,0],[-33.385,29.471],[8.088,31.144]],"o":[[-9.241,-25.213],[-19.116,-44.526],[-15.291,19.687],[0,0],[0,0],[38.232,-33.75],[-4.594,-17.692]],"v":[[1.491,-181.287],[-33.839,-268.23],[-51.588,-209.843],[-83.257,-178.968],[-66.382,1.5],[2.768,-41.25],[25.662,-113.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[9.241,25.213],[15.339,35.73],[24.588,-31.657],[0,0],[0,0],[-33.385,29.471],[8.088,31.144]],"o":[[-9.241,-25.213],[-19.116,-44.526],[-15.291,19.687],[0,0],[0,0],[38.232,-33.75],[-4.594,-17.692]],"v":[[1.491,-181.287],[-33.839,-268.23],[-51.588,-209.843],[-83.257,-178.968],[-66.382,1.5],[2.768,-41.25],[25.662,-113.144]],"c":true}],"e":[{"i":[[9.241,25.213],[14.589,37.98],[24.588,-31.657],[0,0],[0,0],[-36.476,25.546],[8.088,31.144]],"o":[[-9.241,-25.213],[-17.376,-45.234],[-15.291,19.687],[0,0],[0,0],[34.982,-24.5],[-4.594,-17.692]],"v":[[-0.759,-180.537],[-36.839,-272.73],[-56.338,-216.843],[-89.257,-186.468],[-69.382,1],[6.518,-38],[24.662,-104.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[9.241,25.213],[14.589,37.98],[24.588,-31.657],[0,0],[0,0],[-36.476,25.546],[8.088,31.144]],"o":[[-9.241,-25.213],[-17.376,-45.234],[-15.291,19.687],[0,0],[0,0],[34.982,-24.5],[-4.594,-17.692]],"v":[[-0.759,-180.537],[-36.839,-272.73],[-56.338,-216.843],[-89.257,-186.468],[-69.382,1],[6.518,-38],[24.662,-104.894]],"c":true}],"e":[{"i":[[8.009,25.037],[13.247,38.034],[35.338,-37.657],[0,0],[0,0],[-39.772,20.031],[8.088,31.144]],"o":[[-11.991,-32.463],[-17.16,-49.27],[-21.388,22.792],[0,0],[0,0],[36.732,-18.5],[-4.594,-17.692]],"v":[[11.241,-169.037],[-25.59,-271.98],[-60.088,-220.343],[-93.257,-193.718],[-71.382,1],[15.768,-34.75],[32.662,-102.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[8.009,25.037],[13.247,38.034],[35.338,-37.657],[0,0],[0,0],[-39.772,20.031],[8.088,31.144]],"o":[[-11.991,-32.463],[-17.16,-49.27],[-21.388,22.792],[0,0],[0,0],[36.732,-18.5],[-4.594,-17.692]],"v":[[11.241,-169.037],[-25.59,-271.98],[-60.088,-220.343],[-93.257,-193.718],[-71.382,1],[15.768,-34.75],[32.662,-102.144]],"c":true}],"e":[{"i":[[8.259,24.037],[10.641,35.776],[35.838,-35.157],[0,0],[0,0],[-42.018,14.75],[10.338,43.894]],"o":[[-9.241,-31.213],[-13.91,-46.77],[-22.312,21.888],[0,0],[0,0],[40.972,-14.383],[-7.971,-33.842]],"v":[[23.991,-176.037],[-5.09,-269.48],[-49.338,-232.593],[-95.757,-200.718],[-72.382,0.25],[27.268,-29],[47.662,-96.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[8.259,24.037],[10.641,35.776],[35.838,-35.157],[0,0],[0,0],[-42.018,14.75],[10.338,43.894]],"o":[[-9.241,-31.213],[-13.91,-46.77],[-22.312,21.888],[0,0],[0,0],[40.972,-14.383],[-7.971,-33.842]],"v":[[23.991,-176.037],[-5.09,-269.48],[-49.338,-232.593],[-95.757,-200.718],[-72.382,0.25],[27.268,-29],[47.662,-96.394]],"c":true}],"e":[{"i":[[7.009,23.287],[7.59,33.73],[50.58,-35.169],[0,0],[0,0],[0,0],[11.714,49.114]],"o":[[-7.241,-28.463],[-10.225,-45.445],[-25.662,17.843],[0,0],[0,0],[43.732,-7.5],[-10.162,-42.606]],"v":[[42.741,-173.287],[20.411,-260.73],[-42.588,-234.593],[-97.257,-206.968],[-73.882,0.25],[35.018,-20.5],[65.662,-88.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[7.009,23.287],[7.59,33.73],[50.58,-35.169],[0,0],[0,0],[0,0],[11.714,49.114]],"o":[[-7.241,-28.463],[-10.225,-45.445],[-25.662,17.843],[0,0],[0,0],[43.732,-7.5],[-10.162,-42.606]],"v":[[42.741,-173.287],[20.411,-260.73],[-42.588,-234.593],[-97.257,-206.968],[-73.882,0.25],[35.018,-20.5],[65.662,-88.144]],"c":true}],"e":[{"i":[[3.259,21.287],[6.044,31.942],[57.588,-23.657],[0,0],[0,0],[0,0],[10.088,66.644]],"o":[[-1.741,-23.963],[-8.661,-45.77],[-33.448,13.74],[0,0],[0,0],[43.732,-7.5],[-8.74,-57.737]],"v":[[54.991,-175.537],[44.161,-246.98],[-37.838,-233.593],[-96.757,-212.718],[-75.382,0],[38.518,-14.75],[79.662,-80.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[3.259,21.287],[6.044,31.942],[57.588,-23.657],[0,0],[0,0],[0,0],[10.088,66.644]],"o":[[-1.741,-23.963],[-8.661,-45.77],[-33.448,13.74],[0,0],[0,0],[43.732,-7.5],[-8.74,-57.737]],"v":[[54.991,-175.537],[44.161,-246.98],[-37.838,-233.593],[-96.757,-212.718],[-75.382,0],[38.518,-14.75],[79.662,-80.894]],"c":true}],"e":[{"i":[[3.259,21.287],[4.985,35.86],[31.838,-13.407],[0,0],[0,0],[0,0],[7.588,68.394]],"o":[[-1.491,-26.213],[-5.911,-42.52],[-31.691,13.345],[0,0],[0,0],[38.732,-3.75],[-6.443,-58.072]],"v":[[65.241,-152.287],[61.661,-221.73],[10.912,-247.343],[-94.007,-216.968],[-77.382,-0.75],[40.268,-9.5],[88.162,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[3.259,21.287],[4.985,35.86],[31.838,-13.407],[0,0],[0,0],[0,0],[7.588,68.394]],"o":[[-1.491,-26.213],[-5.911,-42.52],[-31.691,13.345],[0,0],[0,0],[38.732,-3.75],[-6.443,-58.072]],"v":[[65.241,-152.287],[61.661,-221.73],[10.912,-247.343],[-94.007,-216.968],[-77.382,-0.75],[40.268,-9.5],[88.162,-79.394]],"c":true}],"e":[{"i":[[2.759,8.787],[5.839,35.73],[33.162,-9.093],[0,0],[0,0],[0,0],[2.478,49.663]],"o":[[-2.741,-7.463],[-6.377,-39.022],[-33.162,9.093],[0,0],[0,0],[38.732,-3.75],[-2.912,-58.356]],"v":[[66.241,-138.287],[71.911,-205.73],[16.162,-241.093],[-91.507,-220.468],[-77.382,-0.75],[41.768,-6.5],[92.912,-66.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[2.759,8.787],[5.839,35.73],[33.162,-9.093],[0,0],[0,0],[0,0],[2.478,49.663]],"o":[[-2.741,-7.463],[-6.377,-39.022],[-33.162,9.093],[0,0],[0,0],[38.732,-3.75],[-2.912,-58.356]],"v":[[66.241,-138.287],[71.911,-205.73],[16.162,-241.093],[-91.507,-220.468],[-77.382,-0.75],[41.768,-6.5],[92.912,-66.894]],"c":true}],"e":[{"i":[[27.259,18.287],[2.089,21.98],[37.838,-4.407],[0,0],[0,0],[0,0],[0.838,46.394]],"o":[[11.509,-17.213],[-3.742,-39.362],[0,0],[0,0],[0,0],[36.982,0],[-0.523,-28.962]],"v":[[63.241,-131.037],[77.161,-188.98],[16.162,-235.843],[-89.507,-222.468],[-78.882,-0.5],[31.768,-3.75],[95.162,-60.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[27.259,18.287],[2.089,21.98],[37.838,-4.407],[0,0],[0,0],[0,0],[0.838,46.394]],"o":[[11.509,-17.213],[-3.742,-39.362],[0,0],[0,0],[0,0],[36.982,0],[-0.523,-28.962]],"v":[[63.241,-131.037],[77.161,-188.98],[16.162,-235.843],[-89.507,-222.468],[-78.882,-0.5],[31.768,-3.75],[95.162,-60.894]],"c":true}],"e":[{"i":[[25.509,15.287],[1.964,22.98],[38.338,-2.657],[0,0],[0,0],[0,0],[-0.037,44.769]],"o":[[11.884,-14.088],[-3.365,-39.367],[0,0],[0,0],[0,0],[36.982,0],[0.026,-31.246]],"v":[[61.616,-125.787],[79.286,-179.98],[15.537,-232.218],[-86.382,-224.093],[-79.007,-0.375],[30.268,-2.75],[94.037,-62.519]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[25.509,15.287],[1.964,22.98],[38.338,-2.657],[0,0],[0,0],[0,0],[-0.037,44.769]],"o":[[11.884,-14.088],[-3.365,-39.367],[0,0],[0,0],[0,0],[36.982,0],[0.026,-31.246]],"v":[[61.616,-125.787],[79.286,-179.98],[15.537,-232.218],[-86.382,-224.093],[-79.007,-0.375],[30.268,-2.75],[94.037,-62.519]],"c":true}],"e":[{"i":[[23.759,12.287],[0.374,21.444],[38.838,-0.907],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[12.259,-10.963],[-0.705,-39.533],[0,0],[0,0],[0,0],[36.982,0],[0.053,-28.967]],"v":[[59.491,-123.537],[79.911,-170.48],[17.412,-229.593],[-83.257,-225.718],[-79.132,-0.25],[28.768,-1.75],[93.412,-61.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[23.759,12.287],[0.374,21.444],[38.838,-0.907],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[12.259,-10.963],[-0.705,-39.533],[0,0],[0,0],[0,0],[36.982,0],[0.053,-28.967]],"v":[[59.491,-123.537],[79.911,-170.48],[17.412,-229.593],[-83.257,-225.718],[-79.132,-0.25],[28.768,-1.75],[93.412,-61.644]],"c":true}],"e":[{"i":[[23.009,8.287],[0.374,21.444],[37.94,-0.602],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[39.468,0.25],[0.053,-28.967]],"v":[[55.991,-119.287],[79.661,-166.48],[11.162,-226.593],[-79.507,-226.218],[-79.132,-0.25],[22.518,-0.25],[91.162,-62.644]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[-2.196,-15.458],[4.87,-2.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[2.979,-1.252]],"v":[[-276.179,-132.292],[-277.37,-102.994],[-282.552,-101.351],[-288.355,-146.931],[-283.229,-149.748]],"c":true}],"e":[{"i":[[-2.196,-15.458],[4.87,-2.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[2.979,-1.252]],"v":[[-45.179,-170.292],[-46.37,-140.994],[-51.552,-139.351],[-57.355,-184.931],[-52.229,-187.748]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[-2.196,-15.458],[4.87,-2.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[2.979,-1.252]],"v":[[-45.179,-170.292],[-46.37,-140.994],[-51.552,-139.351],[-57.355,-184.931],[-52.229,-187.748]],"c":true}],"e":[{"i":[[-2.196,-15.458],[10.37,-3.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[5.729,-1.752]],"v":[[-29.679,-174.292],[-36.62,-141.244],[-48.302,-138.851],[-53.605,-186.431],[-42.979,-189.748]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[-2.196,-15.458],[10.37,-3.756],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[5.729,-1.752]],"v":[[-29.679,-174.292],[-36.62,-141.244],[-48.302,-138.851],[-53.605,-186.431],[-42.979,-189.748]],"c":true}],"e":[{"i":[[-2.196,-15.458],[11.62,-1.881],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[12.104,-2.127]],"v":[[-14.179,-171.542],[-26.62,-140.744],[-44.802,-137.851],[-48.605,-186.931],[-32.479,-190.998]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[-2.196,-15.458],[11.62,-1.881],[0,0],[0,0],[0,0]],"o":[[1.997,14.059],[0,0],[0,0],[0,0],[12.104,-2.127]],"v":[[-14.179,-171.542],[-26.62,-140.744],[-44.802,-137.851],[-48.605,-186.931],[-32.479,-190.998]],"c":true}],"e":[{"i":[[-0.946,-15.083],[11.62,-1.881],[0,0],[0,0],[0,0]],"o":[[0.889,14.172],[0,0],[0,0],[0,0],[11.854,-1.752]],"v":[[-0.554,-168.667],[-16.12,-139.619],[-41.927,-136.351],[-45.105,-186.931],[-21.854,-190.998]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[-0.946,-15.083],[11.62,-1.881],[0,0],[0,0],[0,0]],"o":[[0.889,14.172],[0,0],[0,0],[0,0],[11.854,-1.752]],"v":[[-0.554,-168.667],[-16.12,-139.619],[-41.927,-136.351],[-45.105,-186.931],[-21.854,-190.998]],"c":true}],"e":[{"i":[[0,-14.502],[17.62,-0.131],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[16.104,-1.752]],"v":[[9.196,-166.167],[-14.62,-138.119],[-39.927,-136.351],[-41.855,-185.931],[-15.354,-189.498]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[0,-14.502],[17.62,-0.131],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[16.104,-1.752]],"v":[[9.196,-166.167],[-14.62,-138.119],[-39.927,-136.351],[-41.855,-185.931],[-15.354,-189.498]],"c":true}],"e":[{"i":[[0,-14.502],[18.37,-0.631],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[15.303,-0.876]],"v":[[18.196,-164.417],[-8.37,-136.494],[-37.552,-135.101],[-38.855,-185.806],[-8.479,-188.373]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[0,-14.502],[18.37,-0.631],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[15.303,-0.876]],"v":[[18.196,-164.417],[-8.37,-136.494],[-37.552,-135.101],[-38.855,-185.806],[-8.479,-188.373]],"c":true}],"e":[{"i":[[0,-14.502],[19.12,-1.131],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[24.196,-162.667],[-2.12,-134.869],[-35.177,-133.851],[-35.855,-185.681],[-1.604,-187.248]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[0,-14.502],[19.12,-1.131],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[24.196,-162.667],[-2.12,-134.869],[-35.177,-133.851],[-35.855,-185.681],[-1.604,-187.248]],"c":true}],"e":[{"i":[[0,-14.502],[18.37,0.935],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[30.446,-160.417],[1.88,-133.369],[-33.177,-133.101],[-33.105,-184.681],[4.646,-184.998]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ind":2,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[6.056,-1.63],[0,0],[0,0],[0,0],[-1.281,-9.949]],"o":[[0,0],[0,0],[0,0],[4.931,-1.967],[1.649,12.81]],"v":[[-287.306,-16.62],[-293.677,-14.62],[-299.802,-64.783],[-294.431,-67.033],[-284.719,-47.051]],"c":true}],"e":[{"i":[[6.056,-1.63],[0,0],[0,0],[0,0],[-1.281,-9.949]],"o":[[0,0],[0,0],[0,0],[4.931,-1.967],[1.649,12.81]],"v":[[-33.306,-52.62],[-39.677,-50.62],[-45.802,-100.783],[-40.431,-103.033],[-30.719,-83.051]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[6.056,-1.63],[0,0],[0,0],[0,0],[-1.281,-9.949]],"o":[[0,0],[0,0],[0,0],[4.931,-1.967],[1.649,12.81]],"v":[[-33.306,-52.62],[-39.677,-50.62],[-45.802,-100.783],[-40.431,-103.033],[-30.719,-83.051]],"c":true}],"e":[{"i":[[11.556,-3.13],[0,0],[0,0],[0,0],[-0.953,-10.127]],"o":[[0,0],[0,0],[0,0],[8.681,-1.967],[0.969,10.301]],"v":[[-21.806,-51.87],[-37.427,-49.12],[-43.552,-99.783],[-28.431,-103.533],[-13.719,-83.551]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[11.556,-3.13],[0,0],[0,0],[0,0],[-0.953,-10.127]],"o":[[0,0],[0,0],[0,0],[8.681,-1.967],[0.969,10.301]],"v":[[-21.806,-51.87],[-37.427,-49.12],[-43.552,-99.783],[-28.431,-103.533],[-13.719,-83.551]],"c":true}],"e":[{"i":[[13.181,-3.38],[0,0],[0,0],[0,0],[-0.953,-10.127]],"o":[[0,0],[0,0],[0,0],[14.306,-2.342],[0.969,10.301]],"v":[[-10.556,-49.87],[-36.177,-47.37],[-40.302,-98.783],[-18.181,-102.533],[2.031,-79.801]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[13.181,-3.38],[0,0],[0,0],[0,0],[-0.953,-10.127]],"o":[[0,0],[0,0],[0,0],[14.306,-2.342],[0.969,10.301]],"v":[[-10.556,-49.87],[-36.177,-47.37],[-40.302,-98.783],[-18.181,-102.533],[2.031,-79.801]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[-5.181,-47.745],[-34.927,-46.745],[-38.427,-97.908],[-7.931,-101.158],[14.156,-75.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[-5.181,-47.745],[-34.927,-46.745],[-38.427,-97.908],[-7.931,-101.158],[14.156,-75.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[2.819,-46.495],[-33.927,-44.495],[-36.427,-96.908],[0.069,-98.908],[23.656,-72.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[2.819,-46.495],[-33.927,-44.495],[-36.427,-96.908],[0.069,-98.908],[23.656,-72.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[16.556,-0.967],[0,14.2]],"v":[[6.819,-45.12],[-33.427,-44.245],[-34.802,-95.783],[5.694,-97.283],[31.781,-71.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[16.556,-0.967],[0,14.2]],"v":[[6.819,-45.12],[-33.427,-44.245],[-34.802,-95.783],[5.694,-97.283],[31.781,-71.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[10.819,-43.745],[-32.927,-43.995],[-33.177,-94.658],[11.319,-95.658],[36.406,-69.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[10.819,-43.745],[-32.927,-43.995],[-33.177,-94.658],[11.319,-95.658],[36.406,-69.676]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[14.819,-41.995],[-32.677,-41.995],[-32.677,-93.658],[16.569,-94.158],[41.656,-68.176]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.32,0.5,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":15,"op":23,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":25,"ty":4,"nm":"B white layer 2","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-1.282,17.269],[-0.661,17.73],[1.838,0.593],[0,0],[0,0],[-1.643,0],[-0.912,-1.481]],"o":[[1.259,-16.963],[0.075,-2.02],[-1.701,-0.549],[0,0],[0,0],[1.257,0],[0.338,-3.231]],"v":[[-41.009,-32.537],[-38.089,-79.48],[-39.588,-83.593],[-42.507,-82.468],[-50.507,4.375],[-47.107,4],[-44.213,5.856]],"c":true}],"e":[{"i":[[-0.008,17.316],[0.339,16.48],[3.281,1.942],[0,0],[0,0],[-6.268,-1],[-1.162,-9.106]],"o":[[0.009,-21.463],[-0.098,-4.771],[-4.912,-2.907],[0,0],[0,0],[5.666,0.904],[-0.412,-10.356]],"v":[[-34.509,-42.537],[-34.339,-91.23],[-37.588,-101.593],[-47.007,-102.468],[-59.132,7.5],[-45.982,2.75],[-34.588,14.106]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.008,17.316],[0.339,16.48],[3.281,1.942],[0,0],[0,0],[-6.268,-1],[-1.162,-9.106]],"o":[[0.009,-21.463],[-0.098,-4.771],[-4.912,-2.907],[0,0],[0,0],[5.666,0.904],[-0.412,-10.356]],"v":[[-34.509,-42.537],[-34.339,-91.23],[-37.588,-101.593],[-47.007,-102.468],[-59.132,7.5],[-45.982,2.75],[-34.588,14.106]],"c":true}],"e":[{"i":[[1.009,17.287],[2.01,28.256],[7.354,3.812],[0,0],[0,0],[-11.66,-5.191],[-3.162,-10.856]],"o":[[-1.064,-18.224],[-0.411,-5.77],[-10.912,-5.657],[0,0],[0,0],[11.232,5],[-1.162,-18.356]],"v":[[-16.009,-48.037],[-20.089,-101.23],[-28.588,-120.343],[-52.757,-120.968],[-55.882,3.75],[-26.232,2.5],[-10.838,21.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[1.009,17.287],[2.01,28.256],[7.354,3.812],[0,0],[0,0],[-11.66,-5.191],[-3.162,-10.856]],"o":[[-1.064,-18.224],[-0.411,-5.77],[-10.912,-5.657],[0,0],[0,0],[11.232,5],[-1.162,-18.356]],"v":[[-16.009,-48.037],[-20.089,-101.23],[-28.588,-120.343],[-52.757,-120.968],[-55.882,3.75],[-26.232,2.5],[-10.838,21.356]],"c":true}],"e":[{"i":[[3.581,24.09],[2.339,28.23],[18.566,5.173],[0,0],[0,0],[-42.815,-12.244],[-3.162,-9.856]],"o":[[-4.491,-30.213],[-0.972,-11.734],[-34.662,-9.657],[0,0],[0,0],[19.232,5.5],[-2.412,-16.356]],"v":[[28.491,-50.787],[21.161,-112.48],[-7.588,-142.843],[-60.007,-138.968],[-58.382,3.5],[13.518,-2.5],[41.412,22.606]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[3.581,24.09],[2.339,28.23],[18.566,5.173],[0,0],[0,0],[-42.815,-12.244],[-3.162,-9.856]],"o":[[-4.491,-30.213],[-0.972,-11.734],[-34.662,-9.657],[0,0],[0,0],[19.232,5.5],[-2.412,-16.356]],"v":[[28.491,-50.787],[21.161,-112.48],[-7.588,-142.843],[-60.007,-138.968],[-58.382,3.5],[13.518,-2.5],[41.412,22.606]],"c":true}],"e":[{"i":[[6.259,23.537],[9.028,29.832],[35.338,-11.407],[0,0],[0,0],[-43.647,8.83],[7.838,30.644]],"o":[[-6.901,-25.951],[-3.411,-11.27],[-35.699,11.524],[0,0],[0,0],[66.732,-13.5],[-4.529,-17.709]],"v":[[98.991,-111.037],[77.161,-192.23],[3.912,-187.593],[-68.757,-153.968],[-60.882,2],[23.768,-27],[112.412,-55.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[6.259,23.537],[9.028,29.832],[35.338,-11.407],[0,0],[0,0],[-43.647,8.83],[7.838,30.644]],"o":[[-6.901,-25.951],[-3.411,-11.27],[-35.699,11.524],[0,0],[0,0],[66.732,-13.5],[-4.529,-17.709]],"v":[[98.991,-111.037],[77.161,-192.23],[3.912,-187.593],[-68.757,-153.968],[-60.882,2],[23.768,-27],[112.412,-55.894]],"c":true}],"e":[{"i":[[9.241,25.213],[14.339,35.48],[31.838,-35.407],[0,0],[0,0],[-37.642,23.794],[7.838,30.644]],"o":[[-9.241,-25.213],[-15.283,-37.816],[-16.668,18.536],[0,0],[0,0],[45.482,-28.75],[-4.529,-17.709]],"v":[[33.991,-189.537],[9.161,-252.48],[-36.838,-201.593],[-76.007,-167.218],[-63.882,2],[24.768,-48],[62.412,-113.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[9.241,25.213],[14.339,35.48],[31.838,-35.407],[0,0],[0,0],[-37.642,23.794],[7.838,30.644]],"o":[[-9.241,-25.213],[-15.283,-37.816],[-16.668,18.536],[0,0],[0,0],[45.482,-28.75],[-4.529,-17.709]],"v":[[33.991,-189.537],[9.161,-252.48],[-36.838,-201.593],[-76.007,-167.218],[-63.882,2],[24.768,-48],[62.412,-113.394]],"c":true}],"e":[{"i":[[9.241,25.213],[15.339,35.73],[24.588,-31.657],[0,0],[0,0],[-33.385,29.471],[8.088,31.144]],"o":[[-9.241,-25.213],[-19.116,-44.526],[-15.291,19.687],[0,0],[0,0],[38.232,-33.75],[-4.594,-17.692]],"v":[[1.491,-181.287],[-33.839,-268.23],[-51.588,-209.843],[-83.257,-178.968],[-66.382,1.5],[2.768,-41.25],[25.662,-113.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[9.241,25.213],[15.339,35.73],[24.588,-31.657],[0,0],[0,0],[-33.385,29.471],[8.088,31.144]],"o":[[-9.241,-25.213],[-19.116,-44.526],[-15.291,19.687],[0,0],[0,0],[38.232,-33.75],[-4.594,-17.692]],"v":[[1.491,-181.287],[-33.839,-268.23],[-51.588,-209.843],[-83.257,-178.968],[-66.382,1.5],[2.768,-41.25],[25.662,-113.144]],"c":true}],"e":[{"i":[[9.241,25.213],[14.589,37.98],[24.588,-31.657],[0,0],[0,0],[-36.476,25.546],[8.088,31.144]],"o":[[-9.241,-25.213],[-17.376,-45.234],[-15.291,19.687],[0,0],[0,0],[34.982,-24.5],[-4.594,-17.692]],"v":[[-0.759,-180.537],[-36.839,-272.73],[-56.338,-216.843],[-89.257,-186.468],[-69.382,1],[6.518,-38],[24.662,-104.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[9.241,25.213],[14.589,37.98],[24.588,-31.657],[0,0],[0,0],[-36.476,25.546],[8.088,31.144]],"o":[[-9.241,-25.213],[-17.376,-45.234],[-15.291,19.687],[0,0],[0,0],[34.982,-24.5],[-4.594,-17.692]],"v":[[-0.759,-180.537],[-36.839,-272.73],[-56.338,-216.843],[-89.257,-186.468],[-69.382,1],[6.518,-38],[24.662,-104.894]],"c":true}],"e":[{"i":[[8.009,25.037],[13.247,38.034],[35.338,-37.657],[0,0],[0,0],[-39.772,20.031],[8.088,31.144]],"o":[[-11.991,-32.463],[-17.16,-49.27],[-21.388,22.792],[0,0],[0,0],[36.732,-18.5],[-4.594,-17.692]],"v":[[11.241,-169.037],[-25.59,-271.98],[-60.088,-220.343],[-93.257,-193.718],[-71.382,1],[15.768,-34.75],[32.662,-102.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[8.009,25.037],[13.247,38.034],[35.338,-37.657],[0,0],[0,0],[-39.772,20.031],[8.088,31.144]],"o":[[-11.991,-32.463],[-17.16,-49.27],[-21.388,22.792],[0,0],[0,0],[36.732,-18.5],[-4.594,-17.692]],"v":[[11.241,-169.037],[-25.59,-271.98],[-60.088,-220.343],[-93.257,-193.718],[-71.382,1],[15.768,-34.75],[32.662,-102.144]],"c":true}],"e":[{"i":[[8.259,24.037],[10.641,35.776],[35.838,-35.157],[0,0],[0,0],[-42.018,14.75],[10.338,43.894]],"o":[[-9.241,-31.213],[-13.91,-46.77],[-22.312,21.888],[0,0],[0,0],[40.972,-14.383],[-7.971,-33.842]],"v":[[23.991,-176.037],[-5.09,-269.48],[-49.338,-232.593],[-95.757,-200.718],[-72.382,0.25],[27.268,-29],[47.662,-96.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[8.259,24.037],[10.641,35.776],[35.838,-35.157],[0,0],[0,0],[-42.018,14.75],[10.338,43.894]],"o":[[-9.241,-31.213],[-13.91,-46.77],[-22.312,21.888],[0,0],[0,0],[40.972,-14.383],[-7.971,-33.842]],"v":[[23.991,-176.037],[-5.09,-269.48],[-49.338,-232.593],[-95.757,-200.718],[-72.382,0.25],[27.268,-29],[47.662,-96.394]],"c":true}],"e":[{"i":[[7.009,23.287],[7.59,33.73],[50.58,-35.169],[0,0],[0,0],[0,0],[11.714,49.114]],"o":[[-7.241,-28.463],[-10.225,-45.445],[-25.662,17.843],[0,0],[0,0],[43.732,-7.5],[-10.162,-42.606]],"v":[[42.741,-173.287],[20.411,-260.73],[-42.588,-234.593],[-97.257,-206.968],[-73.882,0.25],[35.018,-20.5],[65.662,-88.144]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[7.009,23.287],[7.59,33.73],[50.58,-35.169],[0,0],[0,0],[0,0],[11.714,49.114]],"o":[[-7.241,-28.463],[-10.225,-45.445],[-25.662,17.843],[0,0],[0,0],[43.732,-7.5],[-10.162,-42.606]],"v":[[42.741,-173.287],[20.411,-260.73],[-42.588,-234.593],[-97.257,-206.968],[-73.882,0.25],[35.018,-20.5],[65.662,-88.144]],"c":true}],"e":[{"i":[[3.259,21.287],[6.044,31.942],[57.588,-23.657],[0,0],[0,0],[0,0],[10.088,66.644]],"o":[[-1.741,-23.963],[-8.661,-45.77],[-33.448,13.74],[0,0],[0,0],[43.732,-7.5],[-8.74,-57.737]],"v":[[54.991,-175.537],[44.161,-246.98],[-37.838,-233.593],[-96.757,-212.718],[-75.382,0],[38.518,-14.75],[79.662,-80.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[3.259,21.287],[6.044,31.942],[57.588,-23.657],[0,0],[0,0],[0,0],[10.088,66.644]],"o":[[-1.741,-23.963],[-8.661,-45.77],[-33.448,13.74],[0,0],[0,0],[43.732,-7.5],[-8.74,-57.737]],"v":[[54.991,-175.537],[44.161,-246.98],[-37.838,-233.593],[-96.757,-212.718],[-75.382,0],[38.518,-14.75],[79.662,-80.894]],"c":true}],"e":[{"i":[[3.259,21.287],[4.985,35.86],[31.838,-13.407],[0,0],[0,0],[0,0],[7.588,68.394]],"o":[[-1.491,-26.213],[-5.911,-42.52],[-31.691,13.345],[0,0],[0,0],[38.732,-3.75],[-6.443,-58.072]],"v":[[65.241,-152.287],[61.661,-221.73],[10.912,-247.343],[-94.007,-216.968],[-77.382,-0.75],[40.268,-9.5],[88.162,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[3.259,21.287],[4.985,35.86],[31.838,-13.407],[0,0],[0,0],[0,0],[7.588,68.394]],"o":[[-1.491,-26.213],[-5.911,-42.52],[-31.691,13.345],[0,0],[0,0],[38.732,-3.75],[-6.443,-58.072]],"v":[[65.241,-152.287],[61.661,-221.73],[10.912,-247.343],[-94.007,-216.968],[-77.382,-0.75],[40.268,-9.5],[88.162,-79.394]],"c":true}],"e":[{"i":[[2.759,8.787],[5.839,35.73],[33.162,-9.093],[0,0],[0,0],[0,0],[2.478,49.663]],"o":[[-2.741,-7.463],[-6.377,-39.022],[-33.162,9.093],[0,0],[0,0],[38.732,-3.75],[-2.912,-58.356]],"v":[[66.241,-138.287],[71.911,-205.73],[16.162,-241.093],[-91.507,-220.468],[-77.382,-0.75],[41.768,-6.5],[92.912,-66.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[2.759,8.787],[5.839,35.73],[33.162,-9.093],[0,0],[0,0],[0,0],[2.478,49.663]],"o":[[-2.741,-7.463],[-6.377,-39.022],[-33.162,9.093],[0,0],[0,0],[38.732,-3.75],[-2.912,-58.356]],"v":[[66.241,-138.287],[71.911,-205.73],[16.162,-241.093],[-91.507,-220.468],[-77.382,-0.75],[41.768,-6.5],[92.912,-66.894]],"c":true}],"e":[{"i":[[27.259,18.287],[2.089,21.98],[37.838,-4.407],[0,0],[0,0],[0,0],[0.838,46.394]],"o":[[11.509,-17.213],[-3.742,-39.362],[0,0],[0,0],[0,0],[36.982,0],[-0.523,-28.962]],"v":[[63.241,-131.037],[77.161,-188.98],[16.162,-235.843],[-89.507,-222.468],[-78.882,-0.5],[31.768,-3.75],[95.162,-60.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[27.259,18.287],[2.089,21.98],[37.838,-4.407],[0,0],[0,0],[0,0],[0.838,46.394]],"o":[[11.509,-17.213],[-3.742,-39.362],[0,0],[0,0],[0,0],[36.982,0],[-0.523,-28.962]],"v":[[63.241,-131.037],[77.161,-188.98],[16.162,-235.843],[-89.507,-222.468],[-78.882,-0.5],[31.768,-3.75],[95.162,-60.894]],"c":true}],"e":[{"i":[[25.509,15.287],[1.964,22.98],[38.338,-2.657],[0,0],[0,0],[0,0],[-0.037,44.769]],"o":[[11.884,-14.088],[-3.365,-39.367],[0,0],[0,0],[0,0],[36.982,0],[0.026,-31.246]],"v":[[61.616,-125.787],[79.286,-179.98],[15.537,-232.218],[-86.382,-224.093],[-79.007,-0.375],[30.268,-2.75],[94.037,-62.519]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[25.509,15.287],[1.964,22.98],[38.338,-2.657],[0,0],[0,0],[0,0],[-0.037,44.769]],"o":[[11.884,-14.088],[-3.365,-39.367],[0,0],[0,0],[0,0],[36.982,0],[0.026,-31.246]],"v":[[61.616,-125.787],[79.286,-179.98],[15.537,-232.218],[-86.382,-224.093],[-79.007,-0.375],[30.268,-2.75],[94.037,-62.519]],"c":true}],"e":[{"i":[[23.759,12.287],[0.374,21.444],[38.838,-0.907],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[12.259,-10.963],[-0.705,-39.533],[0,0],[0,0],[0,0],[36.982,0],[0.053,-28.967]],"v":[[59.491,-123.537],[79.911,-170.48],[17.412,-229.593],[-83.257,-225.718],[-79.132,-0.25],[28.768,-1.75],[93.412,-61.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[23.759,12.287],[0.374,21.444],[38.838,-0.907],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[12.259,-10.963],[-0.705,-39.533],[0,0],[0,0],[0,0],[36.982,0],[0.053,-28.967]],"v":[[59.491,-123.537],[79.911,-170.48],[17.412,-229.593],[-83.257,-225.718],[-79.132,-0.25],[28.768,-1.75],[93.412,-61.644]],"c":true}],"e":[{"i":[[23.009,8.287],[0.374,21.444],[37.94,-0.602],[0,0],[0,0],[0,0],[-0.076,41.089]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[39.468,0.25],[0.053,-28.967]],"v":[[55.991,-119.287],[79.661,-166.48],[11.162,-226.593],[-79.507,-226.218],[-79.132,-0.25],[22.518,-0.25],[91.162,-62.644]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.32,0.5,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":5,"op":15,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":26,"ty":4,"nm":"B light blue layer 3 shadow 2","parent":34,"ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16,"s":[5],"e":[40]},{"t":22}]},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0,0],[0,0],[-3.12,0.516],[4.596,42.859],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[-5.931,-55.302],[0,0]],"v":[[-58.13,-221.16],[-111.991,-226.753],[-103.289,41.823],[-46.069,-81.698],[-54.696,-253.118]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3.12,0.516],[9.482,45.79],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[-11.278,-54.464],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[-0.569,37.802],[-54.696,-253.118]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[0,0],[0,0],[-3.12,0.516],[9.482,45.79],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[-11.278,-54.464],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[-0.569,37.802],[-54.696,-253.118]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3.12,0.516],[-3.803,42.936],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[3.803,-42.936],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[42.431,32.302],[11.804,-249.118]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[0,0],[0,0],[-3.12,0.516],[-3.803,42.936],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[3.803,-42.936],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[42.431,32.302],[11.804,-249.118]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3.12,0.516],[-3.803,42.936],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[3.803,-42.936],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[70.931,-10.698],[8.304,-239.118]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[0,0],[0,0],[-3.12,0.516],[-3.803,42.936],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[3.803,-42.936],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[70.931,-10.698],[8.304,-239.118]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[3,43],[0,0]],"o":[[0,0],[0,0],[3,-1],[-3,-43],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[59,30],[-33,-263]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[0,0],[0,0],[-3,1],[3,43],[0,0]],"o":[[0,0],[0,0],[3,-1],[-3,-43],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[59,30],[-33,-263]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[3,43],[0,0]],"o":[[0,0],[0,0],[3,-1],[-3,-43],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[41,8],[-35,-264]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[0,0],[0,0],[-3,1],[3,43],[0,0]],"o":[[0,0],[0,0],[3,-1],[-3,-43],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[41,8],[-35,-264]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[-1,39],[0,0]],"o":[[0,0],[0,0],[3,-1],[1.444,-56.302],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[70,-17],[3,-268]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[0,0],[0,0],[-3,1],[-1,39],[0,0]],"o":[[0,0],[0,0],[3,-1],[1.444,-56.302],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[70,-17],[3,-268]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[96,-9],[28,-280]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[96,-9],[28,-280]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[96,-19],[50,-287]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[96,-19],[50,-287]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[116,-7],[70,-285]],"c":true}]},{"t":18}]},"o":{"k":100},"x":{"k":0},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.201,21.411],[-0.286,15.23],[2.474,0.203],[0,0],[0,0],[-1.893,-0.375],[-0.662,-1.856]],"o":[[0.134,-14.338],[0.036,-1.896],[-1.912,-0.157],[0,0],[0,0],[0.644,0.128],[1.338,-20.856]],"v":[[-46.509,-50.162],[-44.214,-98.23],[-45.963,-101.593],[-49.132,-100.593],[-55.007,4.75],[-50.857,4.25],[-48.588,6.856]],"c":true}],"e":[{"i":[[0.279,24.912],[0.464,22.605],[2.088,1.343],[0,0],[0,0],[-4.143,-2],[-0.787,-3.981]],"o":[[-0.241,-21.463],[-0.098,-4.771],[-4.242,-2.728],[0,0],[0,0],[3.51,1.694],[-0.037,-19.356]],"v":[[-40.884,-52.662],[-41.839,-108.48],[-44.463,-118.593],[-53.507,-120.718],[-54.757,3.5],[-45.357,4.875],[-39.963,12.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0.279,24.912],[0.464,22.605],[2.088,1.343],[0,0],[0,0],[-4.143,-2],[-0.787,-3.981]],"o":[[-0.241,-21.463],[-0.098,-4.771],[-4.242,-2.728],[0,0],[0,0],[3.51,1.694],[-0.037,-19.356]],"v":[[-40.884,-52.662],[-41.839,-108.48],[-44.463,-118.593],[-53.507,-120.718],[-54.757,3.5],[-45.357,4.875],[-39.963,12.856]],"c":true}],"e":[{"i":[[2.509,24.787],[1.952,19.983],[11.463,8.593],[0,0],[0,0],[-8.268,-3.75],[-3.537,-7.856]],"o":[[-1.87,-18.476],[-0.161,-1.645],[-7.865,-5.896],[0,0],[0,0],[10.005,4.538],[-1.787,-20.356]],"v":[[-22.759,-58.037],[-27.839,-117.855],[-38.713,-134.843],[-60.257,-138.093],[-58.257,2.5],[-32.482,4.25],[-15.963,19.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[2.509,24.787],[1.952,19.983],[11.463,8.593],[0,0],[0,0],[-8.268,-3.75],[-3.537,-7.856]],"o":[[-1.87,-18.476],[-0.161,-1.645],[-7.865,-5.896],[0,0],[0,0],[10.005,4.538],[-1.787,-20.356]],"v":[[-22.759,-58.037],[-27.839,-117.855],[-38.713,-134.843],[-60.257,-138.093],[-58.257,2.5],[-32.482,4.25],[-15.963,19.356]],"c":true}],"e":[{"i":[[2.509,23.537],[1.952,19.983],[37.213,10.843],[0,0],[0,0],[-37.012,-3.318],[3.87,36.773]],"o":[[-1.969,-18.466],[-0.161,-1.645],[-39.595,-11.537],[0,0],[0,0],[52.982,4.75],[-2.037,-19.356]],"v":[[46.741,-67.787],[41.411,-115.355],[-2.713,-150.593],[-68.757,-153.593],[-61.507,2.25],[4.268,0.25],[56.037,7.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[2.509,23.537],[1.952,19.983],[37.213,10.843],[0,0],[0,0],[-37.012,-3.318],[3.87,36.773]],"o":[[-1.969,-18.466],[-0.161,-1.645],[-39.595,-11.537],[0,0],[0,0],[52.982,4.75],[-2.037,-19.356]],"v":[[46.741,-67.787],[41.411,-115.355],[-2.713,-150.593],[-68.757,-153.593],[-61.507,2.25],[4.268,0.25],[56.037,7.356]],"c":true}],"e":[{"i":[[5.869,20.677],[5.339,19.355],[49.963,-14.657],[0,0],[0,0],[-48.018,8.5],[14.463,52.894]],"o":[[-5.241,-18.463],[-6.854,-24.846],[-39.574,11.609],[0,0],[0,0],[56.175,-9.944],[-11.083,-40.532]],"v":[[79.741,-141.537],[64.661,-196.855],[-12.963,-182.843],[-76.257,-168.093],[-64.507,2],[54.518,-20],[98.287,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[5.869,20.677],[5.339,19.355],[49.963,-14.657],[0,0],[0,0],[-48.018,8.5],[14.463,52.894]],"o":[[-5.241,-18.463],[-6.854,-24.846],[-39.574,11.609],[0,0],[0,0],[56.175,-9.944],[-11.083,-40.532]],"v":[[79.741,-141.537],[64.661,-196.855],[-12.963,-182.843],[-76.257,-168.093],[-64.507,2],[54.518,-20],[98.287,-79.394]],"c":true}],"e":[{"i":[[7.415,20.175],[10.589,29.855],[50.213,-22.407],[0,0],[0,0],[-48.907,15.377],[14.389,46.029]],"o":[[-8.991,-24.463],[-8.616,-24.291],[-37.662,16.806],[0,0],[0,0],[43.732,-13.75],[-12.537,-40.106]],"v":[[75.491,-152.537],[45.911,-232.355],[-25.463,-197.593],[-83.007,-177.843],[-66.757,2],[45.518,-26.75],[98.787,-83.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[7.415,20.175],[10.589,29.855],[50.213,-22.407],[0,0],[0,0],[-48.907,15.377],[14.389,46.029]],"o":[[-8.991,-24.463],[-8.616,-24.291],[-37.662,16.806],[0,0],[0,0],[43.732,-13.75],[-12.537,-40.106]],"v":[[75.491,-152.537],[45.911,-232.355],[-25.463,-197.593],[-83.007,-177.843],[-66.757,2],[45.518,-26.75],[98.787,-83.894]],"c":true}],"e":[{"i":[[6.732,20.413],[9.339,28.855],[50.213,-22.407],[0,0],[0,0],[-49.459,13.494],[14.043,47.445]],"o":[[-7.491,-22.713],[-7.937,-24.521],[-37.662,16.806],[0,0],[0,0],[43.982,-12],[-12.537,-42.356]],"v":[[73.491,-153.537],[43.411,-239.855],[-29.463,-206.343],[-89.007,-186.343],[-68.507,1.5],[45.518,-26.75],[96.287,-84.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[6.732,20.413],[9.339,28.855],[50.213,-22.407],[0,0],[0,0],[-49.459,13.494],[14.043,47.445]],"o":[[-7.491,-22.713],[-7.937,-24.521],[-37.662,16.806],[0,0],[0,0],[43.982,-12],[-12.537,-42.356]],"v":[[73.491,-153.537],[43.411,-239.855],[-29.463,-206.343],[-89.007,-186.343],[-68.507,1.5],[45.518,-26.75],[96.287,-84.644]],"c":true}],"e":[{"i":[[5.741,20.713],[9.339,28.855],[60.463,-27.657],[0,0],[0,0],[-49.459,13.494],[12.963,47.644]],"o":[[-5.741,-20.713],[-7.937,-24.521],[-37.505,17.155],[0,0],[0,0],[43.982,-12],[-11.586,-42.584]],"v":[[73.741,-153.787],[45.411,-241.855],[-29.713,-213.843],[-93.257,-193.343],[-71.257,0.5],[49.768,-26.5],[95.287,-81.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[5.741,20.713],[9.339,28.855],[60.463,-27.657],[0,0],[0,0],[-49.459,13.494],[12.963,47.644]],"o":[[-5.741,-20.713],[-7.937,-24.521],[-37.505,17.155],[0,0],[0,0],[43.982,-12],[-11.586,-42.584]],"v":[[73.741,-153.787],[45.411,-241.855],[-29.713,-213.843],[-93.257,-193.343],[-71.257,0.5],[49.768,-26.5],[95.287,-81.894]],"c":true}],"e":[{"i":[[5.741,20.713],[7.839,29.855],[63.713,-28.907],[0,0],[0,0],[-50.018,11.25],[12.963,53.394]],"o":[[-5.741,-20.713],[-6.546,-24.929],[-37.557,17.04],[0,0],[0,0],[46.058,-10.359],[-10.749,-44.276]],"v":[[75.741,-154.037],[52.411,-240.355],[-22.963,-222.593],[-96.007,-200.343],[-72.507,1.25],[48.768,-23.5],[95.037,-81.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[5.741,20.713],[7.839,29.855],[63.713,-28.907],[0,0],[0,0],[-50.018,11.25],[12.963,53.394]],"o":[[-5.741,-20.713],[-6.546,-24.929],[-37.557,17.04],[0,0],[0,0],[46.058,-10.359],[-10.749,-44.276]],"v":[[75.741,-154.037],[52.411,-240.355],[-22.963,-222.593],[-96.007,-200.343],[-72.507,1.25],[48.768,-23.5],[95.037,-81.894]],"c":true}],"e":[{"i":[[4.259,17.037],[7.839,29.855],[64.463,-23.407],[0,0],[0,0],[-49.518,7.25],[10.523,50.543]],"o":[[-5.991,-25.463],[-6.546,-24.929],[-38.765,14.076],[0,0],[0,0],[48.964,-7.169],[-9.287,-44.606]],"v":[[76.491,-154.787],[58.161,-239.105],[-23.213,-225.593],[-97.507,-206.343],[-74.007,1.25],[52.268,-20],[94.787,-82.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[4.259,17.037],[7.839,29.855],[64.463,-23.407],[0,0],[0,0],[-49.518,7.25],[10.523,50.543]],"o":[[-5.991,-25.463],[-6.546,-24.929],[-38.765,14.076],[0,0],[0,0],[48.964,-7.169],[-9.287,-44.606]],"v":[[76.491,-154.787],[58.161,-239.105],[-23.213,-225.593],[-97.507,-206.343],[-74.007,1.25],[52.268,-20],[94.787,-82.394]],"c":true}],"e":[{"i":[[4.009,18.287],[9.089,30.105],[63.963,-18.157],[0,0],[0,0],[-52.207,5.395],[4.963,38.144]],"o":[[-1.741,-23.713],[-7.236,-23.967],[-39.674,11.262],[0,0],[0,0],[67.732,-7],[-5.852,-44.98]],"v":[[74.491,-158.287],[64.411,-233.605],[-18.713,-229.093],[-96.757,-211.843],[-75.507,0.25],[45.268,-14.75],[94.287,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[4.009,18.287],[9.089,30.105],[63.963,-18.157],[0,0],[0,0],[-52.207,5.395],[4.963,38.144]],"o":[[-1.741,-23.713],[-7.236,-23.967],[-39.674,11.262],[0,0],[0,0],[67.732,-7],[-5.852,-44.98]],"v":[[74.491,-158.287],[64.411,-233.605],[-18.713,-229.093],[-96.757,-211.843],[-75.507,0.25],[45.268,-14.75],[94.287,-79.394]],"c":true}],"e":[{"i":[[5.509,15.037],[12.589,49.605],[49.213,-8.907],[0,0],[0,0],[-52.29,4.52],[5.084,46.282]],"o":[[-2.241,-23.713],[-7.654,-30.16],[-40.583,7.345],[0,0],[0,0],[60.732,-5.25],[-5.037,-45.856]],"v":[[73.241,-141.287],[69.161,-222.855],[-16.963,-229.093],[-94.757,-216.843],[-76.507,-0.5],[42.768,-11.75],[94.037,-80.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[5.509,15.037],[12.589,49.605],[49.213,-8.907],[0,0],[0,0],[-52.29,4.52],[5.084,46.282]],"o":[[-2.241,-23.713],[-7.654,-30.16],[-40.583,7.345],[0,0],[0,0],[60.732,-5.25],[-5.037,-45.856]],"v":[[73.241,-141.287],[69.161,-222.855],[-16.963,-229.093],[-94.757,-216.843],[-76.507,-0.5],[42.768,-11.75],[94.037,-80.894]],"c":true}],"e":[{"i":[[15.759,20.037],[2.089,16.855],[46.963,-6.157],[0,0],[0,0],[-32.059,1.901],[3.213,53.144]],"o":[[4.509,-21.713],[-5.161,-61.645],[-32.938,4.318],[0,0],[0,0],[63.232,-3.75],[-3.787,-41.856]],"v":[[68.491,-134.287],[76.911,-187.605],[-8.463,-229.843],[-91.507,-219.843],[-77.257,-0.75],[22.518,-7.25],[94.037,-72.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[15.759,20.037],[2.089,16.855],[46.963,-6.157],[0,0],[0,0],[-32.059,1.901],[3.213,53.144]],"o":[[4.509,-21.713],[-5.161,-61.645],[-32.938,4.318],[0,0],[0,0],[63.232,-3.75],[-3.787,-41.856]],"v":[[68.491,-134.287],[76.911,-187.605],[-8.463,-229.843],[-91.507,-219.843],[-77.257,-0.75],[22.518,-7.25],[94.037,-72.894]],"c":true}],"e":[{"i":[[20.259,14.037],[1.766,21.378],[30.713,-3.907],[0,0],[0,0],[-39.232,1.5],[3.134,56.753]],"o":[[11.509,-19.963],[-4.411,-53.395],[-38.914,4.95],[0,0],[0,0],[39.232,-1.5],[-1.787,-32.356]],"v":[[63.741,-128.537],[77.411,-184.105],[3.537,-230.593],[-88.507,-223.093],[-78.007,-1],[25.018,-5.5],[93.537,-70.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[20.259,14.037],[1.766,21.378],[30.713,-3.907],[0,0],[0,0],[-39.232,1.5],[3.134,56.753]],"o":[[11.509,-19.963],[-4.411,-53.395],[-38.914,4.95],[0,0],[0,0],[39.232,-1.5],[-1.787,-32.356]],"v":[[63.741,-128.537],[77.411,-184.105],[3.537,-230.593],[-88.507,-223.093],[-78.007,-1],[25.018,-5.5],[93.537,-70.894]],"c":true}],"e":[{"i":[[24.259,15.287],[1.029,21.401],[37.213,-2.157],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-3.911,-45.645],[-21.287,1.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[61.241,-124.787],[78.661,-178.355],[4.787,-229.343],[-86.007,-223.593],[-78.007,-1],[18.768,-3.5],[92.287,-66.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[24.259,15.287],[1.029,21.401],[37.213,-2.157],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-3.911,-45.645],[-21.287,1.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[61.241,-124.787],[78.661,-178.355],[4.787,-229.343],[-86.007,-223.593],[-78.007,-1],[18.768,-3.5],[92.287,-66.644]],"c":true}],"e":[{"i":[[22.009,10.287],[0,21.451],[39.713,-1.157],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[14.509,-10.713],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[58.491,-122.287],[79.161,-171.855],[11.037,-228.593],[-83.507,-224.093],[-78.007,-1],[21.268,-2.5],[92.037,-63.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[22.009,10.287],[0,21.451],[39.713,-1.157],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[14.509,-10.713],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[58.491,-122.287],[79.161,-171.855],[11.037,-228.593],[-83.507,-224.093],[-78.007,-1],[21.268,-2.5],[92.037,-63.394]],"c":true}],"e":[{"i":[[21.149,8.762],[0.374,21.444],[35.338,-1.657],[0,0],[0,0],[0,0],[-0.227,41.088]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[43.478,0.75],[0.16,-28.893]],"v":[[56.741,-121.162],[79.411,-167.48],[13.912,-227.343],[-81.507,-224.968],[-78.382,-0.75],[19.518,-1.25],[91.287,-62.019]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[21.149,8.762],[0.374,21.444],[35.338,-1.657],[0,0],[0,0],[0,0],[-0.227,41.088]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[43.478,0.75],[0.16,-28.893]],"v":[[56.741,-121.162],[79.411,-167.48],[13.912,-227.343],[-81.507,-224.968],[-78.382,-0.75],[19.518,-1.25],[91.287,-62.019]],"c":true}],"e":[{"i":[[21.149,8.762],[0.749,21.438],[32.213,-0.907],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.241,-119.787],[79.411,-168.105],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[90.787,-62.894]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[-2.154,-14.341],[5.939,-1.064],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[3.862,-1.474]],"v":[[-268.103,-143.239],[-271.189,-115.936],[-276.731,-115.148],[-282.154,-159.988],[-277.112,-161.276]],"c":true}],"e":[{"i":[[-2.154,-14.341],[5.939,-1.064],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[3.862,-1.474]],"v":[[-36.103,-162.739],[-39.189,-135.436],[-44.731,-134.648],[-50.154,-179.488],[-45.112,-180.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[-2.154,-14.341],[5.939,-1.064],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[3.862,-1.474]],"v":[[-36.103,-162.739],[-39.189,-135.436],[-44.731,-134.648],[-50.154,-179.488],[-45.112,-180.776]],"c":true}],"e":[{"i":[[-2.154,-14.341],[7.939,-1.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[7.862,-1.724]],"v":[[-22.103,-167.239],[-29.189,-138.186],[-43.481,-135.648],[-49.154,-181.988],[-37.862,-184.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[-2.154,-14.341],[7.939,-1.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[7.862,-1.724]],"v":[[-22.103,-167.239],[-29.189,-138.186],[-43.481,-135.648],[-49.154,-181.988],[-37.862,-184.776]],"c":true}],"e":[{"i":[[-2.154,-14.341],[7.939,-1.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[10.862,0.026]],"v":[[-9.353,-166.739],[-20.189,-138.686],[-42.231,-135.648],[-46.654,-183.988],[-26.862,-187.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[-2.154,-14.341],[7.939,-1.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[10.862,0.026]],"v":[[-9.353,-166.739],[-20.189,-138.686],[-42.231,-135.648],[-46.654,-183.988],[-26.862,-187.276]],"c":true}],"e":[{"i":[[-1.634,-14.432],[15.643,-0.316],[0,0],[0,0],[0,0]],"o":[[1.499,13.233],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[1.501,-165.483],[-19.143,-137.184],[-39.891,-135.152],[-43.647,-185.494],[-18.285,-187.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[-1.634,-14.432],[15.643,-0.316],[0,0],[0,0],[0,0]],"o":[[1.499,13.233],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[1.501,-165.483],[-19.143,-137.184],[-39.891,-135.152],[-43.647,-185.494],[-18.285,-187.276]],"c":true}],"e":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[10.147,-167.739],[-8.939,-136.936],[-38.481,-134.898],[-40.154,-185.488],[-12.862,-186.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[10.147,-167.739],[-8.939,-136.936],[-38.481,-134.898],[-40.154,-185.488],[-12.862,-186.776]],"c":true}],"e":[{"i":[[-1.534,-14.395],[14.273,-2.231],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.778,1.383]],"v":[[17.998,-164.132],[-2.523,-136.269],[-36.047,-134.41],[-37.912,-185.525],[-5.028,-187.133]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[-1.534,-14.395],[14.273,-2.231],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.778,1.383]],"v":[[17.998,-164.132],[-2.523,-136.269],[-36.047,-134.41],[-37.912,-185.525],[-5.028,-187.133]],"c":true}],"e":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[23.139,-163.775],[0.762,-135.102],[-34.862,-134.172],[-35.92,-185.061],[-3.241,-186.241]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[23.139,-163.775],[0.762,-135.102],[-34.862,-134.172],[-35.92,-185.061],[-3.241,-186.241]],"c":true}],"e":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[27.029,-160.917],[4.297,-134.435],[-33.677,-133.685],[-33.677,-184.598],[2.797,-185.348]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[27.029,-160.917],[4.297,-134.435],[-33.677,-133.685],[-33.677,-184.598],[2.797,-185.348]],"c":true}],"e":[{"i":[[0,-14.502],[16.037,-0.065],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[30.363,-160.417],[4.963,-133.935],[-32.927,-133.268],[-32.927,-184.014],[3.297,-184.848]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ind":2,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[7.181,0.245],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[5.681,-0.592],[2.589,13.962]],"v":[[-272.931,-26.995],[-280.427,-25.995],[-286.427,-75.158],[-279.181,-76.658],[-269.094,-56.926]],"c":true}],"e":[{"i":[[7.181,0.245],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[5.681,-0.592],[2.589,13.962]],"v":[[-25.931,-49.995],[-33.427,-48.995],[-39.427,-98.158],[-32.181,-99.658],[-22.094,-79.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[7.181,0.245],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[5.681,-0.592],[2.589,13.962]],"v":[[-25.931,-49.995],[-33.427,-48.995],[-39.427,-98.158],[-32.181,-99.658],[-22.094,-79.926]],"c":true}],"e":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[7.681,-0.842],[2.589,13.962]],"v":[[-17.681,-50.245],[-33.677,-48.495],[-39.427,-98.158],[-22.431,-100.908],[-7.094,-82.426]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[7.681,-0.842],[2.589,13.962]],"v":[[-17.681,-50.245],[-33.677,-48.495],[-39.427,-98.158],[-22.431,-100.908],[-7.094,-82.426]],"c":true}],"e":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[11.931,0.158],[2.589,13.962]],"v":[[-9.181,-49.745],[-33.677,-47.745],[-37.927,-97.908],[-12.681,-100.908],[5.906,-78.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[11.931,0.158],[2.589,13.962]],"v":[[-9.181,-49.745],[-33.677,-47.745],[-37.927,-97.908],[-12.681,-100.908],[5.906,-78.676]],"c":true}],"e":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-1.406,-16.074]],"o":[[0,0],[0,0],[0,0],[11.868,-1.421],[1.237,14.146]],"v":[[-2.681,-47.745],[-34.177,-45.745],[-36.927,-96.908],[-7.181,-99.908],[16.656,-76.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-1.406,-16.074]],"o":[[0,0],[0,0],[0,0],[11.868,-1.421],[1.237,14.146]],"v":[[-2.681,-47.745],[-34.177,-45.745],[-36.927,-96.908],[-7.181,-99.908],[16.656,-76.926]],"c":true}],"e":[{"i":[[12.181,0.495],[0,0],[0,0],[0,0],[-0.906,-12.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.041,14.162]],"v":[[3.069,-47.245],[-32.927,-45.495],[-35.677,-96.408],[1.819,-98.408],[24.906,-73.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[12.181,0.495],[0,0],[0,0],[0,0],[-0.906,-12.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.041,14.162]],"v":[[3.069,-47.245],[-32.927,-45.495],[-35.677,-96.408],[1.819,-98.408],[24.906,-73.926]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.264,-12.571]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.261,12.426]],"v":[[8.652,-45.745],[-33.344,-44.412],[-34.844,-95.658],[6.902,-97.325],[31.239,-71.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.264,-12.571]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.261,12.426]],"v":[[8.652,-45.745],[-33.344,-44.412],[-34.844,-95.658],[6.902,-97.325],[31.239,-71.926]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[10.735,-44.495],[-33.261,-43.579],[-33.761,-94.908],[11.235,-95.742],[35.822,-70.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[10.735,-44.495],[-33.261,-43.579],[-33.761,-94.908],[11.235,-95.742],[35.822,-70.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[12.681,0.408],[0,14.2]],"v":[[14.319,-43.995],[-32.677,-43.245],[-33.177,-93.658],[16.569,-94.658],[39.156,-68.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[12.681,0.408],[0,14.2]],"v":[[14.319,-43.995],[-32.677,-43.245],[-33.177,-93.658],[16.569,-94.658],[39.156,-68.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[14.819,-42.745],[-32.677,-41.995],[-32.677,-93.658],[16.069,-94.158],[41.406,-67.926]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.15,0.8,0.55,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":15,"op":23,"st":0,"bm":10,"sr":1},{"ddd":0,"ind":27,"ty":4,"nm":"B light blue layer 3 shadow","parent":34,"ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16,"s":[5],"e":[40]},{"t":22}]},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0,0],[0,0],[-3.12,0.516],[4.596,42.859],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[-5.931,-55.302],[0,0]],"v":[[-58.13,-221.16],[-111.991,-226.753],[-103.289,41.823],[-46.069,-81.698],[-54.696,-253.118]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3.12,0.516],[9.482,45.79],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[-11.278,-54.464],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[-0.569,37.802],[-54.696,-253.118]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[0,0],[0,0],[-3.12,0.516],[9.482,45.79],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[-11.278,-54.464],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[-0.569,37.802],[-54.696,-253.118]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3.12,0.516],[-3.803,42.936],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[3.803,-42.936],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[42.431,32.302],[11.804,-249.118]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[0,0],[0,0],[-3.12,0.516],[-3.803,42.936],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[3.803,-42.936],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[42.431,32.302],[11.804,-249.118]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3.12,0.516],[-3.803,42.936],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[3.803,-42.936],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[70.931,-10.698],[8.304,-239.118]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[0,0],[0,0],[-3.12,0.516],[-3.803,42.936],[0,0]],"o":[[0,0],[0,0],[3.12,-0.516],[3.803,-42.936],[0,0]],"v":[[-9.13,-263.16],[-128.991,-241.753],[-103.289,41.823],[70.931,-10.698],[8.304,-239.118]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[3,43],[0,0]],"o":[[0,0],[0,0],[3,-1],[-3,-43],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[59,30],[-33,-263]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[0,0],[0,0],[-3,1],[3,43],[0,0]],"o":[[0,0],[0,0],[3,-1],[-3,-43],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[59,30],[-33,-263]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[3,43],[0,0]],"o":[[0,0],[0,0],[3,-1],[-3,-43],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[41,8],[-35,-264]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[0,0],[0,0],[-3,1],[3,43],[0,0]],"o":[[0,0],[0,0],[3,-1],[-3,-43],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[41,8],[-35,-264]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[-1,39],[0,0]],"o":[[0,0],[0,0],[3,-1],[1.444,-56.302],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[70,-17],[3,-268]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[0,0],[0,0],[-3,1],[-1,39],[0,0]],"o":[[0,0],[0,0],[3,-1],[1.444,-56.302],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[70,-17],[3,-268]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[96,-9],[28,-280]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[96,-9],[28,-280]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[96,-19],[50,-287]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[96,-19],[50,-287]],"c":true}],"e":[{"i":[[0,0],[0,0],[-3,1],[9.112,40.348],[0,0]],"o":[[0,0],[0,0],[3,-1],[-12.406,-54.937],[0,0]],"v":[[-47,-270],[-162,-230],[-92,46],[116,-7],[70,-285]],"c":true}]},{"t":18}]},"o":{"k":100},"x":{"k":0},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.201,21.411],[-0.286,15.23],[2.474,0.203],[0,0],[0,0],[-1.893,-0.375],[-0.662,-1.856]],"o":[[0.134,-14.338],[0.036,-1.896],[-1.912,-0.157],[0,0],[0,0],[0.644,0.128],[1.338,-20.856]],"v":[[-46.509,-50.162],[-44.214,-98.23],[-45.963,-101.593],[-49.132,-100.593],[-55.007,4.75],[-50.857,4.25],[-48.588,6.856]],"c":true}],"e":[{"i":[[0.279,24.912],[0.464,22.605],[2.088,1.343],[0,0],[0,0],[-4.143,-2],[-0.787,-3.981]],"o":[[-0.241,-21.463],[-0.098,-4.771],[-4.242,-2.728],[0,0],[0,0],[3.51,1.694],[-0.037,-19.356]],"v":[[-40.884,-52.662],[-41.839,-108.48],[-44.463,-118.593],[-53.507,-120.718],[-54.757,3.5],[-45.357,4.875],[-39.963,12.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0.279,24.912],[0.464,22.605],[2.088,1.343],[0,0],[0,0],[-4.143,-2],[-0.787,-3.981]],"o":[[-0.241,-21.463],[-0.098,-4.771],[-4.242,-2.728],[0,0],[0,0],[3.51,1.694],[-0.037,-19.356]],"v":[[-40.884,-52.662],[-41.839,-108.48],[-44.463,-118.593],[-53.507,-120.718],[-54.757,3.5],[-45.357,4.875],[-39.963,12.856]],"c":true}],"e":[{"i":[[2.509,24.787],[1.952,19.983],[11.463,8.593],[0,0],[0,0],[-8.268,-3.75],[-3.537,-7.856]],"o":[[-1.87,-18.476],[-0.161,-1.645],[-7.865,-5.896],[0,0],[0,0],[10.005,4.538],[-1.787,-20.356]],"v":[[-22.759,-58.037],[-27.839,-117.855],[-38.713,-134.843],[-60.257,-138.093],[-58.257,2.5],[-32.482,4.25],[-15.963,19.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[2.509,24.787],[1.952,19.983],[11.463,8.593],[0,0],[0,0],[-8.268,-3.75],[-3.537,-7.856]],"o":[[-1.87,-18.476],[-0.161,-1.645],[-7.865,-5.896],[0,0],[0,0],[10.005,4.538],[-1.787,-20.356]],"v":[[-22.759,-58.037],[-27.839,-117.855],[-38.713,-134.843],[-60.257,-138.093],[-58.257,2.5],[-32.482,4.25],[-15.963,19.356]],"c":true}],"e":[{"i":[[2.509,23.537],[1.952,19.983],[37.213,10.843],[0,0],[0,0],[-37.012,-3.318],[3.87,36.773]],"o":[[-1.969,-18.466],[-0.161,-1.645],[-39.595,-11.537],[0,0],[0,0],[52.982,4.75],[-2.037,-19.356]],"v":[[46.741,-67.787],[41.411,-115.355],[-2.713,-150.593],[-68.757,-153.593],[-61.507,2.25],[4.268,0.25],[56.037,7.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[2.509,23.537],[1.952,19.983],[37.213,10.843],[0,0],[0,0],[-37.012,-3.318],[3.87,36.773]],"o":[[-1.969,-18.466],[-0.161,-1.645],[-39.595,-11.537],[0,0],[0,0],[52.982,4.75],[-2.037,-19.356]],"v":[[46.741,-67.787],[41.411,-115.355],[-2.713,-150.593],[-68.757,-153.593],[-61.507,2.25],[4.268,0.25],[56.037,7.356]],"c":true}],"e":[{"i":[[5.869,20.677],[5.339,19.355],[49.963,-14.657],[0,0],[0,0],[-48.018,8.5],[14.463,52.894]],"o":[[-5.241,-18.463],[-6.854,-24.846],[-39.574,11.609],[0,0],[0,0],[56.175,-9.944],[-11.083,-40.532]],"v":[[79.741,-141.537],[64.661,-196.855],[-12.963,-182.843],[-76.257,-168.093],[-64.507,2],[54.518,-20],[98.287,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[5.869,20.677],[5.339,19.355],[49.963,-14.657],[0,0],[0,0],[-48.018,8.5],[14.463,52.894]],"o":[[-5.241,-18.463],[-6.854,-24.846],[-39.574,11.609],[0,0],[0,0],[56.175,-9.944],[-11.083,-40.532]],"v":[[79.741,-141.537],[64.661,-196.855],[-12.963,-182.843],[-76.257,-168.093],[-64.507,2],[54.518,-20],[98.287,-79.394]],"c":true}],"e":[{"i":[[7.415,20.175],[10.589,29.855],[50.213,-22.407],[0,0],[0,0],[-48.907,15.377],[14.389,46.029]],"o":[[-8.991,-24.463],[-8.616,-24.291],[-37.662,16.806],[0,0],[0,0],[43.732,-13.75],[-12.537,-40.106]],"v":[[75.491,-152.537],[45.911,-232.355],[-25.463,-197.593],[-83.007,-177.843],[-66.757,2],[45.518,-26.75],[98.787,-83.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[7.415,20.175],[10.589,29.855],[50.213,-22.407],[0,0],[0,0],[-48.907,15.377],[14.389,46.029]],"o":[[-8.991,-24.463],[-8.616,-24.291],[-37.662,16.806],[0,0],[0,0],[43.732,-13.75],[-12.537,-40.106]],"v":[[75.491,-152.537],[45.911,-232.355],[-25.463,-197.593],[-83.007,-177.843],[-66.757,2],[45.518,-26.75],[98.787,-83.894]],"c":true}],"e":[{"i":[[6.732,20.413],[9.339,28.855],[50.213,-22.407],[0,0],[0,0],[-49.459,13.494],[14.043,47.445]],"o":[[-7.491,-22.713],[-7.937,-24.521],[-37.662,16.806],[0,0],[0,0],[43.982,-12],[-12.537,-42.356]],"v":[[73.491,-153.537],[43.411,-239.855],[-29.463,-206.343],[-89.007,-186.343],[-68.507,1.5],[45.518,-26.75],[96.287,-84.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[6.732,20.413],[9.339,28.855],[50.213,-22.407],[0,0],[0,0],[-49.459,13.494],[14.043,47.445]],"o":[[-7.491,-22.713],[-7.937,-24.521],[-37.662,16.806],[0,0],[0,0],[43.982,-12],[-12.537,-42.356]],"v":[[73.491,-153.537],[43.411,-239.855],[-29.463,-206.343],[-89.007,-186.343],[-68.507,1.5],[45.518,-26.75],[96.287,-84.644]],"c":true}],"e":[{"i":[[5.741,20.713],[9.339,28.855],[60.463,-27.657],[0,0],[0,0],[-49.459,13.494],[12.963,47.644]],"o":[[-5.741,-20.713],[-7.937,-24.521],[-37.505,17.155],[0,0],[0,0],[43.982,-12],[-11.586,-42.584]],"v":[[73.741,-153.787],[45.411,-241.855],[-29.713,-213.843],[-93.257,-193.343],[-71.257,0.5],[49.768,-26.5],[95.287,-81.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[5.741,20.713],[9.339,28.855],[60.463,-27.657],[0,0],[0,0],[-49.459,13.494],[12.963,47.644]],"o":[[-5.741,-20.713],[-7.937,-24.521],[-37.505,17.155],[0,0],[0,0],[43.982,-12],[-11.586,-42.584]],"v":[[73.741,-153.787],[45.411,-241.855],[-29.713,-213.843],[-93.257,-193.343],[-71.257,0.5],[49.768,-26.5],[95.287,-81.894]],"c":true}],"e":[{"i":[[5.741,20.713],[7.839,29.855],[63.713,-28.907],[0,0],[0,0],[-50.018,11.25],[12.963,53.394]],"o":[[-5.741,-20.713],[-6.546,-24.929],[-37.557,17.04],[0,0],[0,0],[46.058,-10.359],[-10.749,-44.276]],"v":[[75.741,-154.037],[52.411,-240.355],[-22.963,-222.593],[-96.007,-200.343],[-72.507,1.25],[48.768,-23.5],[95.037,-81.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[5.741,20.713],[7.839,29.855],[63.713,-28.907],[0,0],[0,0],[-50.018,11.25],[12.963,53.394]],"o":[[-5.741,-20.713],[-6.546,-24.929],[-37.557,17.04],[0,0],[0,0],[46.058,-10.359],[-10.749,-44.276]],"v":[[75.741,-154.037],[52.411,-240.355],[-22.963,-222.593],[-96.007,-200.343],[-72.507,1.25],[48.768,-23.5],[95.037,-81.894]],"c":true}],"e":[{"i":[[4.259,17.037],[7.839,29.855],[64.463,-23.407],[0,0],[0,0],[-49.518,7.25],[10.523,50.543]],"o":[[-5.991,-25.463],[-6.546,-24.929],[-38.765,14.076],[0,0],[0,0],[48.964,-7.169],[-9.287,-44.606]],"v":[[76.491,-154.787],[58.161,-239.105],[-23.213,-225.593],[-97.507,-206.343],[-74.007,1.25],[52.268,-20],[94.787,-82.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[4.259,17.037],[7.839,29.855],[64.463,-23.407],[0,0],[0,0],[-49.518,7.25],[10.523,50.543]],"o":[[-5.991,-25.463],[-6.546,-24.929],[-38.765,14.076],[0,0],[0,0],[48.964,-7.169],[-9.287,-44.606]],"v":[[76.491,-154.787],[58.161,-239.105],[-23.213,-225.593],[-97.507,-206.343],[-74.007,1.25],[52.268,-20],[94.787,-82.394]],"c":true}],"e":[{"i":[[4.009,18.287],[9.089,30.105],[63.963,-18.157],[0,0],[0,0],[-52.207,5.395],[4.963,38.144]],"o":[[-1.741,-23.713],[-7.236,-23.967],[-39.674,11.262],[0,0],[0,0],[67.732,-7],[-5.852,-44.98]],"v":[[74.491,-158.287],[64.411,-233.605],[-18.713,-229.093],[-96.757,-211.843],[-75.507,0.25],[45.268,-14.75],[94.287,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[4.009,18.287],[9.089,30.105],[63.963,-18.157],[0,0],[0,0],[-52.207,5.395],[4.963,38.144]],"o":[[-1.741,-23.713],[-7.236,-23.967],[-39.674,11.262],[0,0],[0,0],[67.732,-7],[-5.852,-44.98]],"v":[[74.491,-158.287],[64.411,-233.605],[-18.713,-229.093],[-96.757,-211.843],[-75.507,0.25],[45.268,-14.75],[94.287,-79.394]],"c":true}],"e":[{"i":[[5.509,15.037],[12.589,49.605],[49.213,-8.907],[0,0],[0,0],[-52.29,4.52],[5.084,46.282]],"o":[[-2.241,-23.713],[-7.654,-30.16],[-40.583,7.345],[0,0],[0,0],[60.732,-5.25],[-5.037,-45.856]],"v":[[73.241,-141.287],[69.161,-222.855],[-16.963,-229.093],[-94.757,-216.843],[-76.507,-0.5],[42.768,-11.75],[94.037,-80.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[5.509,15.037],[12.589,49.605],[49.213,-8.907],[0,0],[0,0],[-52.29,4.52],[5.084,46.282]],"o":[[-2.241,-23.713],[-7.654,-30.16],[-40.583,7.345],[0,0],[0,0],[60.732,-5.25],[-5.037,-45.856]],"v":[[73.241,-141.287],[69.161,-222.855],[-16.963,-229.093],[-94.757,-216.843],[-76.507,-0.5],[42.768,-11.75],[94.037,-80.894]],"c":true}],"e":[{"i":[[15.759,20.037],[2.089,16.855],[46.963,-6.157],[0,0],[0,0],[-32.059,1.901],[3.213,53.144]],"o":[[4.509,-21.713],[-5.161,-61.645],[-32.938,4.318],[0,0],[0,0],[63.232,-3.75],[-3.787,-41.856]],"v":[[68.491,-134.287],[76.911,-187.605],[-8.463,-229.843],[-91.507,-219.843],[-77.257,-0.75],[22.518,-7.25],[94.037,-72.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[15.759,20.037],[2.089,16.855],[46.963,-6.157],[0,0],[0,0],[-32.059,1.901],[3.213,53.144]],"o":[[4.509,-21.713],[-5.161,-61.645],[-32.938,4.318],[0,0],[0,0],[63.232,-3.75],[-3.787,-41.856]],"v":[[68.491,-134.287],[76.911,-187.605],[-8.463,-229.843],[-91.507,-219.843],[-77.257,-0.75],[22.518,-7.25],[94.037,-72.894]],"c":true}],"e":[{"i":[[20.259,14.037],[1.766,21.378],[30.713,-3.907],[0,0],[0,0],[-39.232,1.5],[3.134,56.753]],"o":[[11.509,-19.963],[-4.411,-53.395],[-38.914,4.95],[0,0],[0,0],[39.232,-1.5],[-1.787,-32.356]],"v":[[63.741,-128.537],[77.411,-184.105],[3.537,-230.593],[-88.507,-223.093],[-78.007,-1],[25.018,-5.5],[93.537,-70.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[20.259,14.037],[1.766,21.378],[30.713,-3.907],[0,0],[0,0],[-39.232,1.5],[3.134,56.753]],"o":[[11.509,-19.963],[-4.411,-53.395],[-38.914,4.95],[0,0],[0,0],[39.232,-1.5],[-1.787,-32.356]],"v":[[63.741,-128.537],[77.411,-184.105],[3.537,-230.593],[-88.507,-223.093],[-78.007,-1],[25.018,-5.5],[93.537,-70.894]],"c":true}],"e":[{"i":[[24.259,15.287],[1.029,21.401],[37.213,-2.157],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-3.911,-45.645],[-21.287,1.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[61.241,-124.787],[78.661,-178.355],[4.787,-229.343],[-86.007,-223.593],[-78.007,-1],[18.768,-3.5],[92.287,-66.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[24.259,15.287],[1.029,21.401],[37.213,-2.157],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-3.911,-45.645],[-21.287,1.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[61.241,-124.787],[78.661,-178.355],[4.787,-229.343],[-86.007,-223.593],[-78.007,-1],[18.768,-3.5],[92.287,-66.644]],"c":true}],"e":[{"i":[[22.009,10.287],[0,21.451],[39.713,-1.157],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[14.509,-10.713],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[58.491,-122.287],[79.161,-171.855],[11.037,-228.593],[-83.507,-224.093],[-78.007,-1],[21.268,-2.5],[92.037,-63.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[22.009,10.287],[0,21.451],[39.713,-1.157],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[14.509,-10.713],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[58.491,-122.287],[79.161,-171.855],[11.037,-228.593],[-83.507,-224.093],[-78.007,-1],[21.268,-2.5],[92.037,-63.394]],"c":true}],"e":[{"i":[[21.149,8.762],[0.374,21.444],[35.338,-1.657],[0,0],[0,0],[0,0],[-0.227,41.088]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[43.478,0.75],[0.16,-28.893]],"v":[[56.741,-121.162],[79.411,-167.48],[13.912,-227.343],[-81.507,-224.968],[-78.382,-0.75],[19.518,-1.25],[91.287,-62.019]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[21.149,8.762],[0.374,21.444],[35.338,-1.657],[0,0],[0,0],[0,0],[-0.227,41.088]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[43.478,0.75],[0.16,-28.893]],"v":[[56.741,-121.162],[79.411,-167.48],[13.912,-227.343],[-81.507,-224.968],[-78.382,-0.75],[19.518,-1.25],[91.287,-62.019]],"c":true}],"e":[{"i":[[21.149,8.762],[0.749,21.438],[32.213,-0.907],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.241,-119.787],[79.411,-168.105],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[90.787,-62.894]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.15,0.8,0.55,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":7,"op":15,"st":0,"bm":10,"sr":1},{"ddd":0,"ind":28,"ty":4,"nm":"B light blue layer 4","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.201,21.411],[-0.286,15.23],[2.474,0.203],[0,0],[0,0],[-1.893,-0.375],[-0.662,-1.856]],"o":[[0.134,-14.338],[0.036,-1.896],[-1.912,-0.157],[0,0],[0,0],[0.644,0.128],[1.338,-20.856]],"v":[[-46.509,-50.162],[-44.214,-98.23],[-45.963,-101.593],[-49.132,-100.593],[-55.007,4.75],[-50.857,4.25],[-48.588,6.856]],"c":true}],"e":[{"i":[[0.279,24.912],[0.464,22.605],[2.088,1.343],[0,0],[0,0],[-4.143,-2],[-0.787,-3.981]],"o":[[-0.241,-21.463],[-0.098,-4.771],[-4.242,-2.728],[0,0],[0,0],[3.51,1.694],[-0.037,-19.356]],"v":[[-40.884,-52.662],[-41.839,-108.48],[-44.463,-118.593],[-53.507,-120.718],[-54.757,3.5],[-45.357,4.875],[-39.963,12.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0.279,24.912],[0.464,22.605],[2.088,1.343],[0,0],[0,0],[-4.143,-2],[-0.787,-3.981]],"o":[[-0.241,-21.463],[-0.098,-4.771],[-4.242,-2.728],[0,0],[0,0],[3.51,1.694],[-0.037,-19.356]],"v":[[-40.884,-52.662],[-41.839,-108.48],[-44.463,-118.593],[-53.507,-120.718],[-54.757,3.5],[-45.357,4.875],[-39.963,12.856]],"c":true}],"e":[{"i":[[2.509,24.787],[1.952,19.983],[11.463,8.593],[0,0],[0,0],[-8.268,-3.75],[-3.537,-7.856]],"o":[[-1.87,-18.476],[-0.161,-1.645],[-7.865,-5.896],[0,0],[0,0],[10.005,4.538],[-1.787,-20.356]],"v":[[-22.759,-58.037],[-27.839,-117.855],[-38.713,-134.843],[-60.257,-138.093],[-58.257,2.5],[-32.482,4.25],[-15.963,19.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[2.509,24.787],[1.952,19.983],[11.463,8.593],[0,0],[0,0],[-8.268,-3.75],[-3.537,-7.856]],"o":[[-1.87,-18.476],[-0.161,-1.645],[-7.865,-5.896],[0,0],[0,0],[10.005,4.538],[-1.787,-20.356]],"v":[[-22.759,-58.037],[-27.839,-117.855],[-38.713,-134.843],[-60.257,-138.093],[-58.257,2.5],[-32.482,4.25],[-15.963,19.356]],"c":true}],"e":[{"i":[[2.509,23.537],[1.952,19.983],[37.213,10.843],[0,0],[0,0],[-37.012,-3.318],[3.87,36.773]],"o":[[-1.969,-18.466],[-0.161,-1.645],[-39.595,-11.537],[0,0],[0,0],[52.982,4.75],[-2.037,-19.356]],"v":[[46.741,-67.787],[41.411,-115.355],[-2.713,-150.593],[-68.757,-153.593],[-61.507,2.25],[4.268,0.25],[56.037,7.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[2.509,23.537],[1.952,19.983],[37.213,10.843],[0,0],[0,0],[-37.012,-3.318],[3.87,36.773]],"o":[[-1.969,-18.466],[-0.161,-1.645],[-39.595,-11.537],[0,0],[0,0],[52.982,4.75],[-2.037,-19.356]],"v":[[46.741,-67.787],[41.411,-115.355],[-2.713,-150.593],[-68.757,-153.593],[-61.507,2.25],[4.268,0.25],[56.037,7.356]],"c":true}],"e":[{"i":[[5.869,20.677],[5.339,19.355],[49.963,-14.657],[0,0],[0,0],[-48.018,8.5],[14.463,52.894]],"o":[[-5.241,-18.463],[-6.854,-24.846],[-39.574,11.609],[0,0],[0,0],[56.175,-9.944],[-11.083,-40.532]],"v":[[79.741,-141.537],[64.661,-196.855],[-12.963,-182.843],[-76.257,-168.093],[-64.507,2],[54.518,-20],[98.287,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[5.869,20.677],[5.339,19.355],[49.963,-14.657],[0,0],[0,0],[-48.018,8.5],[14.463,52.894]],"o":[[-5.241,-18.463],[-6.854,-24.846],[-39.574,11.609],[0,0],[0,0],[56.175,-9.944],[-11.083,-40.532]],"v":[[79.741,-141.537],[64.661,-196.855],[-12.963,-182.843],[-76.257,-168.093],[-64.507,2],[54.518,-20],[98.287,-79.394]],"c":true}],"e":[{"i":[[7.415,20.175],[10.589,29.855],[50.213,-22.407],[0,0],[0,0],[-48.907,15.377],[14.389,46.029]],"o":[[-8.991,-24.463],[-8.616,-24.291],[-37.662,16.806],[0,0],[0,0],[43.732,-13.75],[-12.537,-40.106]],"v":[[75.491,-152.537],[45.911,-232.355],[-25.463,-197.593],[-83.007,-177.843],[-66.757,2],[45.518,-26.75],[98.787,-83.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[7.415,20.175],[10.589,29.855],[50.213,-22.407],[0,0],[0,0],[-48.907,15.377],[14.389,46.029]],"o":[[-8.991,-24.463],[-8.616,-24.291],[-37.662,16.806],[0,0],[0,0],[43.732,-13.75],[-12.537,-40.106]],"v":[[75.491,-152.537],[45.911,-232.355],[-25.463,-197.593],[-83.007,-177.843],[-66.757,2],[45.518,-26.75],[98.787,-83.894]],"c":true}],"e":[{"i":[[6.732,20.413],[9.339,28.855],[50.213,-22.407],[0,0],[0,0],[-49.459,13.494],[14.043,47.445]],"o":[[-7.491,-22.713],[-7.937,-24.521],[-37.662,16.806],[0,0],[0,0],[43.982,-12],[-12.537,-42.356]],"v":[[73.491,-153.537],[43.411,-239.855],[-29.463,-206.343],[-89.007,-186.343],[-68.507,1.5],[45.518,-26.75],[96.287,-84.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[6.732,20.413],[9.339,28.855],[50.213,-22.407],[0,0],[0,0],[-49.459,13.494],[14.043,47.445]],"o":[[-7.491,-22.713],[-7.937,-24.521],[-37.662,16.806],[0,0],[0,0],[43.982,-12],[-12.537,-42.356]],"v":[[73.491,-153.537],[43.411,-239.855],[-29.463,-206.343],[-89.007,-186.343],[-68.507,1.5],[45.518,-26.75],[96.287,-84.644]],"c":true}],"e":[{"i":[[5.741,20.713],[9.339,28.855],[60.463,-27.657],[0,0],[0,0],[-49.459,13.494],[12.963,47.644]],"o":[[-5.741,-20.713],[-7.937,-24.521],[-37.505,17.155],[0,0],[0,0],[43.982,-12],[-11.586,-42.584]],"v":[[73.741,-153.787],[45.411,-241.855],[-29.713,-213.843],[-93.257,-193.343],[-71.257,0.5],[49.768,-26.5],[95.287,-81.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[5.741,20.713],[9.339,28.855],[60.463,-27.657],[0,0],[0,0],[-49.459,13.494],[12.963,47.644]],"o":[[-5.741,-20.713],[-7.937,-24.521],[-37.505,17.155],[0,0],[0,0],[43.982,-12],[-11.586,-42.584]],"v":[[73.741,-153.787],[45.411,-241.855],[-29.713,-213.843],[-93.257,-193.343],[-71.257,0.5],[49.768,-26.5],[95.287,-81.894]],"c":true}],"e":[{"i":[[5.741,20.713],[7.839,29.855],[63.713,-28.907],[0,0],[0,0],[-50.018,11.25],[12.963,53.394]],"o":[[-5.741,-20.713],[-6.546,-24.929],[-37.557,17.04],[0,0],[0,0],[46.058,-10.359],[-10.749,-44.276]],"v":[[75.741,-154.037],[52.411,-240.355],[-22.963,-222.593],[-96.007,-200.343],[-72.507,1.25],[48.768,-23.5],[95.037,-81.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[5.741,20.713],[7.839,29.855],[63.713,-28.907],[0,0],[0,0],[-50.018,11.25],[12.963,53.394]],"o":[[-5.741,-20.713],[-6.546,-24.929],[-37.557,17.04],[0,0],[0,0],[46.058,-10.359],[-10.749,-44.276]],"v":[[75.741,-154.037],[52.411,-240.355],[-22.963,-222.593],[-96.007,-200.343],[-72.507,1.25],[48.768,-23.5],[95.037,-81.894]],"c":true}],"e":[{"i":[[4.259,17.037],[7.839,29.855],[64.463,-23.407],[0,0],[0,0],[-49.518,7.25],[10.523,50.543]],"o":[[-5.991,-25.463],[-6.546,-24.929],[-38.765,14.076],[0,0],[0,0],[48.964,-7.169],[-9.287,-44.606]],"v":[[76.491,-154.787],[58.161,-239.105],[-23.213,-225.593],[-97.507,-206.343],[-74.007,1.25],[52.268,-20],[94.787,-82.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[4.259,17.037],[7.839,29.855],[64.463,-23.407],[0,0],[0,0],[-49.518,7.25],[10.523,50.543]],"o":[[-5.991,-25.463],[-6.546,-24.929],[-38.765,14.076],[0,0],[0,0],[48.964,-7.169],[-9.287,-44.606]],"v":[[76.491,-154.787],[58.161,-239.105],[-23.213,-225.593],[-97.507,-206.343],[-74.007,1.25],[52.268,-20],[94.787,-82.394]],"c":true}],"e":[{"i":[[4.009,18.287],[9.089,30.105],[63.963,-18.157],[0,0],[0,0],[-52.207,5.395],[4.963,38.144]],"o":[[-1.741,-23.713],[-7.236,-23.967],[-39.674,11.262],[0,0],[0,0],[67.732,-7],[-5.852,-44.98]],"v":[[74.491,-158.287],[64.411,-233.605],[-18.713,-229.093],[-96.757,-211.843],[-75.507,0.25],[45.268,-14.75],[94.287,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[4.009,18.287],[9.089,30.105],[63.963,-18.157],[0,0],[0,0],[-52.207,5.395],[4.963,38.144]],"o":[[-1.741,-23.713],[-7.236,-23.967],[-39.674,11.262],[0,0],[0,0],[67.732,-7],[-5.852,-44.98]],"v":[[74.491,-158.287],[64.411,-233.605],[-18.713,-229.093],[-96.757,-211.843],[-75.507,0.25],[45.268,-14.75],[94.287,-79.394]],"c":true}],"e":[{"i":[[5.509,15.037],[12.589,49.605],[49.213,-8.907],[0,0],[0,0],[-52.29,4.52],[5.084,46.282]],"o":[[-2.241,-23.713],[-7.654,-30.16],[-40.583,7.345],[0,0],[0,0],[60.732,-5.25],[-5.037,-45.856]],"v":[[73.241,-141.287],[69.161,-222.855],[-16.963,-229.093],[-94.757,-216.843],[-76.507,-0.5],[42.768,-11.75],[94.037,-80.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[5.509,15.037],[12.589,49.605],[49.213,-8.907],[0,0],[0,0],[-52.29,4.52],[5.084,46.282]],"o":[[-2.241,-23.713],[-7.654,-30.16],[-40.583,7.345],[0,0],[0,0],[60.732,-5.25],[-5.037,-45.856]],"v":[[73.241,-141.287],[69.161,-222.855],[-16.963,-229.093],[-94.757,-216.843],[-76.507,-0.5],[42.768,-11.75],[94.037,-80.894]],"c":true}],"e":[{"i":[[15.759,20.037],[2.089,16.855],[46.963,-6.157],[0,0],[0,0],[-32.059,1.901],[3.213,53.144]],"o":[[4.509,-21.713],[-5.161,-61.645],[-32.938,4.318],[0,0],[0,0],[63.232,-3.75],[-3.787,-41.856]],"v":[[68.491,-134.287],[76.911,-187.605],[-8.463,-229.843],[-91.507,-219.843],[-77.257,-0.75],[22.518,-7.25],[94.037,-72.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[15.759,20.037],[2.089,16.855],[46.963,-6.157],[0,0],[0,0],[-32.059,1.901],[3.213,53.144]],"o":[[4.509,-21.713],[-5.161,-61.645],[-32.938,4.318],[0,0],[0,0],[63.232,-3.75],[-3.787,-41.856]],"v":[[68.491,-134.287],[76.911,-187.605],[-8.463,-229.843],[-91.507,-219.843],[-77.257,-0.75],[22.518,-7.25],[94.037,-72.894]],"c":true}],"e":[{"i":[[20.259,14.037],[1.766,21.378],[30.713,-3.907],[0,0],[0,0],[-39.232,1.5],[3.134,56.753]],"o":[[11.509,-19.963],[-4.411,-53.395],[-38.914,4.95],[0,0],[0,0],[39.232,-1.5],[-1.787,-32.356]],"v":[[63.741,-128.537],[77.411,-184.105],[3.537,-230.593],[-88.507,-223.093],[-78.007,-1],[25.018,-5.5],[93.537,-70.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[20.259,14.037],[1.766,21.378],[30.713,-3.907],[0,0],[0,0],[-39.232,1.5],[3.134,56.753]],"o":[[11.509,-19.963],[-4.411,-53.395],[-38.914,4.95],[0,0],[0,0],[39.232,-1.5],[-1.787,-32.356]],"v":[[63.741,-128.537],[77.411,-184.105],[3.537,-230.593],[-88.507,-223.093],[-78.007,-1],[25.018,-5.5],[93.537,-70.894]],"c":true}],"e":[{"i":[[24.259,15.287],[1.029,21.401],[37.213,-2.157],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-3.911,-45.645],[-21.287,1.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[61.241,-124.787],[78.661,-178.355],[4.787,-229.343],[-86.007,-223.593],[-78.007,-1],[18.768,-3.5],[92.287,-66.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[24.259,15.287],[1.029,21.401],[37.213,-2.157],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-3.911,-45.645],[-21.287,1.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[61.241,-124.787],[78.661,-178.355],[4.787,-229.343],[-86.007,-223.593],[-78.007,-1],[18.768,-3.5],[92.287,-66.644]],"c":true}],"e":[{"i":[[22.009,10.287],[0,21.451],[39.713,-1.157],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[14.509,-10.713],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[58.491,-122.287],[79.161,-171.855],[11.037,-228.593],[-83.507,-224.093],[-78.007,-1],[21.268,-2.5],[92.037,-63.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[22.009,10.287],[0,21.451],[39.713,-1.157],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[14.509,-10.713],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[58.491,-122.287],[79.161,-171.855],[11.037,-228.593],[-83.507,-224.093],[-78.007,-1],[21.268,-2.5],[92.037,-63.394]],"c":true}],"e":[{"i":[[21.149,8.762],[0.374,21.444],[35.338,-1.657],[0,0],[0,0],[0,0],[-0.227,41.088]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[43.478,0.75],[0.16,-28.893]],"v":[[56.741,-121.162],[79.411,-167.48],[13.912,-227.343],[-81.507,-224.968],[-78.382,-0.75],[19.518,-1.25],[91.287,-62.019]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[21.149,8.762],[0.374,21.444],[35.338,-1.657],[0,0],[0,0],[0,0],[-0.227,41.088]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[43.478,0.75],[0.16,-28.893]],"v":[[56.741,-121.162],[79.411,-167.48],[13.912,-227.343],[-81.507,-224.968],[-78.382,-0.75],[19.518,-1.25],[91.287,-62.019]],"c":true}],"e":[{"i":[[21.149,8.762],[0.749,21.438],[32.213,-0.907],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.241,-119.787],[79.411,-168.105],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[90.787,-62.894]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[-2.154,-14.341],[5.939,-1.064],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[3.862,-1.474]],"v":[[-268.103,-143.239],[-271.189,-115.936],[-276.731,-115.148],[-282.154,-159.988],[-277.112,-161.276]],"c":true}],"e":[{"i":[[-2.154,-14.341],[5.939,-1.064],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[3.862,-1.474]],"v":[[-36.103,-162.739],[-39.189,-135.436],[-44.731,-134.648],[-50.154,-179.488],[-45.112,-180.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[-2.154,-14.341],[5.939,-1.064],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[3.862,-1.474]],"v":[[-36.103,-162.739],[-39.189,-135.436],[-44.731,-134.648],[-50.154,-179.488],[-45.112,-180.776]],"c":true}],"e":[{"i":[[-2.154,-14.341],[7.939,-1.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[7.862,-1.724]],"v":[[-22.103,-167.239],[-29.189,-138.186],[-43.481,-135.648],[-49.154,-181.988],[-37.862,-184.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[-2.154,-14.341],[7.939,-1.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[7.862,-1.724]],"v":[[-22.103,-167.239],[-29.189,-138.186],[-43.481,-135.648],[-49.154,-181.988],[-37.862,-184.776]],"c":true}],"e":[{"i":[[-2.154,-14.341],[7.939,-1.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[10.862,0.026]],"v":[[-9.353,-166.739],[-20.189,-138.686],[-42.231,-135.648],[-46.654,-183.988],[-26.862,-187.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[-2.154,-14.341],[7.939,-1.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[10.862,0.026]],"v":[[-9.353,-166.739],[-20.189,-138.686],[-42.231,-135.648],[-46.654,-183.988],[-26.862,-187.276]],"c":true}],"e":[{"i":[[-1.634,-14.432],[15.643,-0.316],[0,0],[0,0],[0,0]],"o":[[1.499,13.233],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[1.501,-165.483],[-19.143,-137.184],[-39.891,-135.152],[-43.647,-185.494],[-18.285,-187.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[-1.634,-14.432],[15.643,-0.316],[0,0],[0,0],[0,0]],"o":[[1.499,13.233],[0,0],[0,0],[0,0],[12.13,-2.72]],"v":[[1.501,-165.483],[-19.143,-137.184],[-39.891,-135.152],[-43.647,-185.494],[-18.285,-187.276]],"c":true}],"e":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[10.147,-167.739],[-8.939,-136.936],[-38.481,-134.898],[-40.154,-185.488],[-12.862,-186.776]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[-2.154,-14.341],[17.439,-2.814],[0,0],[0,0],[0,0]],"o":[[2.109,14.042],[0,0],[0,0],[0,0],[13.612,-1.724]],"v":[[10.147,-167.739],[-8.939,-136.936],[-38.481,-134.898],[-40.154,-185.488],[-12.862,-186.776]],"c":true}],"e":[{"i":[[-1.534,-14.395],[14.273,-2.231],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.778,1.383]],"v":[[17.998,-164.132],[-2.523,-136.269],[-36.047,-134.41],[-37.912,-185.525],[-5.028,-187.133]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[-1.534,-14.395],[14.273,-2.231],[0,0],[0,0],[0,0]],"o":[[1.502,14.095],[0,0],[0,0],[0,0],[14.778,1.383]],"v":[[17.998,-164.132],[-2.523,-136.269],[-36.047,-134.41],[-37.912,-185.525],[-5.028,-187.133]],"c":true}],"e":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[23.139,-163.775],[0.762,-135.102],[-34.862,-134.172],[-35.92,-185.061],[-3.241,-186.241]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[-0.767,-14.448],[16.041,-0.938],[0,0],[0,0],[0,0]],"o":[[0.751,14.147],[0,0],[0,0],[0,0],[14.678,-0.575]],"v":[[23.139,-163.775],[0.762,-135.102],[-34.862,-134.172],[-35.92,-185.061],[-3.241,-186.241]],"c":true}],"e":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[27.029,-160.917],[4.297,-134.435],[-33.677,-133.685],[-33.677,-184.598],[2.797,-185.348]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[0,-14.502],[14.502,0],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[27.029,-160.917],[4.297,-134.435],[-33.677,-133.685],[-33.677,-184.598],[2.797,-185.348]],"c":true}],"e":[{"i":[[0,-14.502],[16.037,-0.065],[0,0],[0,0],[0,0]],"o":[[0,14.2],[0,0],[0,0],[0,0],[14.502,0]],"v":[[30.363,-160.417],[4.963,-133.935],[-32.927,-133.268],[-32.927,-184.014],[3.297,-184.848]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ind":2,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[7.181,0.245],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[5.681,-0.592],[2.589,13.962]],"v":[[-272.931,-26.995],[-280.427,-25.995],[-286.427,-75.158],[-279.181,-76.658],[-269.094,-56.926]],"c":true}],"e":[{"i":[[7.181,0.245],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[5.681,-0.592],[2.589,13.962]],"v":[[-25.931,-49.995],[-33.427,-48.995],[-39.427,-98.158],[-32.181,-99.658],[-22.094,-79.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[7.181,0.245],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[5.681,-0.592],[2.589,13.962]],"v":[[-25.931,-49.995],[-33.427,-48.995],[-39.427,-98.158],[-32.181,-99.658],[-22.094,-79.926]],"c":true}],"e":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[7.681,-0.842],[2.589,13.962]],"v":[[-17.681,-50.245],[-33.677,-48.495],[-39.427,-98.158],[-22.431,-100.908],[-7.094,-82.426]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[7.681,-0.842],[2.589,13.962]],"v":[[-17.681,-50.245],[-33.677,-48.495],[-39.427,-98.158],[-22.431,-100.908],[-7.094,-82.426]],"c":true}],"e":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[11.931,0.158],[2.589,13.962]],"v":[[-9.181,-49.745],[-33.677,-47.745],[-37.927,-97.908],[-12.681,-100.908],[5.906,-78.676]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[13.431,-1.505],[0,0],[0,0],[0,0],[-2.656,-14.324]],"o":[[0,0],[0,0],[0,0],[11.931,0.158],[2.589,13.962]],"v":[[-9.181,-49.745],[-33.677,-47.745],[-37.927,-97.908],[-12.681,-100.908],[5.906,-78.676]],"c":true}],"e":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-1.406,-16.074]],"o":[[0,0],[0,0],[0,0],[11.868,-1.421],[1.237,14.146]],"v":[[-2.681,-47.745],[-34.177,-45.745],[-36.927,-96.908],[-7.181,-99.908],[16.656,-76.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[14.118,-0.752],[0,0],[0,0],[0,0],[-1.406,-16.074]],"o":[[0,0],[0,0],[0,0],[11.868,-1.421],[1.237,14.146]],"v":[[-2.681,-47.745],[-34.177,-45.745],[-36.927,-96.908],[-7.181,-99.908],[16.656,-76.926]],"c":true}],"e":[{"i":[[12.181,0.495],[0,0],[0,0],[0,0],[-0.906,-12.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.041,14.162]],"v":[[3.069,-47.245],[-32.927,-45.495],[-35.677,-96.408],[1.819,-98.408],[24.906,-73.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[12.181,0.495],[0,0],[0,0],[0,0],[-0.906,-12.324]],"o":[[0,0],[0,0],[0,0],[14.804,0],[1.041,14.162]],"v":[[3.069,-47.245],[-32.927,-45.495],[-35.677,-96.408],[1.819,-98.408],[24.906,-73.926]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.264,-12.571]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.261,12.426]],"v":[[8.652,-45.745],[-33.344,-44.412],[-34.844,-95.658],[6.902,-97.325],[31.239,-71.926]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.264,-12.571]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.261,12.426]],"v":[[8.652,-45.745],[-33.344,-44.412],[-34.844,-95.658],[6.902,-97.325],[31.239,-71.926]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[10.735,-44.495],[-33.261,-43.579],[-33.761,-94.908],[11.235,-95.742],[35.822,-70.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[-0.885,-14.241]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0.863,14.121]],"v":[[10.735,-44.495],[-33.261,-43.579],[-33.761,-94.908],[11.235,-95.742],[35.822,-70.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[12.681,0.408],[0,14.2]],"v":[[14.319,-43.995],[-32.677,-43.245],[-33.177,-93.658],[16.569,-94.658],[39.156,-68.176]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[12.681,0.408],[0,14.2]],"v":[[14.319,-43.995],[-32.677,-43.245],[-33.177,-93.658],[16.569,-94.658],[39.156,-68.176]],"c":true}],"e":[{"i":[[14.804,0],[0,0],[0,0],[0,0],[0,-14.2]],"o":[[0,0],[0,0],[0,0],[14.804,0],[0,14.2]],"v":[[14.819,-42.745],[-32.677,-41.995],[-32.677,-93.658],[16.069,-94.158],[41.406,-67.926]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.15,0.8,0.55,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":15,"op":23,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":29,"ty":4,"nm":"B light blue layer 3","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.201,21.411],[-0.286,15.23],[2.474,0.203],[0,0],[0,0],[-1.893,-0.375],[-0.662,-1.856]],"o":[[0.134,-14.338],[0.036,-1.896],[-1.912,-0.157],[0,0],[0,0],[0.644,0.128],[1.338,-20.856]],"v":[[-46.509,-50.162],[-44.214,-98.23],[-45.963,-101.593],[-49.132,-100.593],[-55.007,4.75],[-50.857,4.25],[-48.588,6.856]],"c":true}],"e":[{"i":[[0.279,24.912],[0.464,22.605],[2.088,1.343],[0,0],[0,0],[-4.143,-2],[-0.787,-3.981]],"o":[[-0.241,-21.463],[-0.098,-4.771],[-4.242,-2.728],[0,0],[0,0],[3.51,1.694],[-0.037,-19.356]],"v":[[-40.884,-52.662],[-41.839,-108.48],[-44.463,-118.593],[-53.507,-120.718],[-54.757,3.5],[-45.357,4.875],[-39.963,12.856]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0.279,24.912],[0.464,22.605],[2.088,1.343],[0,0],[0,0],[-4.143,-2],[-0.787,-3.981]],"o":[[-0.241,-21.463],[-0.098,-4.771],[-4.242,-2.728],[0,0],[0,0],[3.51,1.694],[-0.037,-19.356]],"v":[[-40.884,-52.662],[-41.839,-108.48],[-44.463,-118.593],[-53.507,-120.718],[-54.757,3.5],[-45.357,4.875],[-39.963,12.856]],"c":true}],"e":[{"i":[[2.509,24.787],[1.952,19.983],[11.463,8.593],[0,0],[0,0],[-8.268,-3.75],[-3.537,-7.856]],"o":[[-1.87,-18.476],[-0.161,-1.645],[-7.865,-5.896],[0,0],[0,0],[10.005,4.538],[-1.787,-20.356]],"v":[[-22.759,-58.037],[-27.839,-117.855],[-38.713,-134.843],[-60.257,-138.093],[-58.257,2.5],[-32.482,4.25],[-15.963,19.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":8,"s":[{"i":[[2.509,24.787],[1.952,19.983],[11.463,8.593],[0,0],[0,0],[-8.268,-3.75],[-3.537,-7.856]],"o":[[-1.87,-18.476],[-0.161,-1.645],[-7.865,-5.896],[0,0],[0,0],[10.005,4.538],[-1.787,-20.356]],"v":[[-22.759,-58.037],[-27.839,-117.855],[-38.713,-134.843],[-60.257,-138.093],[-58.257,2.5],[-32.482,4.25],[-15.963,19.356]],"c":true}],"e":[{"i":[[2.509,23.537],[1.952,19.983],[37.213,10.843],[0,0],[0,0],[-37.012,-3.318],[3.87,36.773]],"o":[[-1.969,-18.466],[-0.161,-1.645],[-39.595,-11.537],[0,0],[0,0],[52.982,4.75],[-2.037,-19.356]],"v":[[46.741,-67.787],[41.411,-115.355],[-2.713,-150.593],[-68.757,-153.593],[-61.507,2.25],[4.268,0.25],[56.037,7.356]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":9,"s":[{"i":[[2.509,23.537],[1.952,19.983],[37.213,10.843],[0,0],[0,0],[-37.012,-3.318],[3.87,36.773]],"o":[[-1.969,-18.466],[-0.161,-1.645],[-39.595,-11.537],[0,0],[0,0],[52.982,4.75],[-2.037,-19.356]],"v":[[46.741,-67.787],[41.411,-115.355],[-2.713,-150.593],[-68.757,-153.593],[-61.507,2.25],[4.268,0.25],[56.037,7.356]],"c":true}],"e":[{"i":[[5.869,20.677],[5.339,19.355],[49.963,-14.657],[0,0],[0,0],[-48.018,8.5],[14.463,52.894]],"o":[[-5.241,-18.463],[-6.854,-24.846],[-39.574,11.609],[0,0],[0,0],[56.175,-9.944],[-11.083,-40.532]],"v":[[79.741,-141.537],[64.661,-196.855],[-12.963,-182.843],[-76.257,-168.093],[-64.507,2],[54.518,-20],[98.287,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10,"s":[{"i":[[5.869,20.677],[5.339,19.355],[49.963,-14.657],[0,0],[0,0],[-48.018,8.5],[14.463,52.894]],"o":[[-5.241,-18.463],[-6.854,-24.846],[-39.574,11.609],[0,0],[0,0],[56.175,-9.944],[-11.083,-40.532]],"v":[[79.741,-141.537],[64.661,-196.855],[-12.963,-182.843],[-76.257,-168.093],[-64.507,2],[54.518,-20],[98.287,-79.394]],"c":true}],"e":[{"i":[[7.415,20.175],[10.589,29.855],[50.213,-22.407],[0,0],[0,0],[-48.907,15.377],[14.389,46.029]],"o":[[-8.991,-24.463],[-8.616,-24.291],[-37.662,16.806],[0,0],[0,0],[43.732,-13.75],[-12.537,-40.106]],"v":[[75.491,-152.537],[45.911,-232.355],[-25.463,-197.593],[-83.007,-177.843],[-66.757,2],[45.518,-26.75],[98.787,-83.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":11,"s":[{"i":[[7.415,20.175],[10.589,29.855],[50.213,-22.407],[0,0],[0,0],[-48.907,15.377],[14.389,46.029]],"o":[[-8.991,-24.463],[-8.616,-24.291],[-37.662,16.806],[0,0],[0,0],[43.732,-13.75],[-12.537,-40.106]],"v":[[75.491,-152.537],[45.911,-232.355],[-25.463,-197.593],[-83.007,-177.843],[-66.757,2],[45.518,-26.75],[98.787,-83.894]],"c":true}],"e":[{"i":[[6.732,20.413],[9.339,28.855],[50.213,-22.407],[0,0],[0,0],[-49.459,13.494],[14.043,47.445]],"o":[[-7.491,-22.713],[-7.937,-24.521],[-37.662,16.806],[0,0],[0,0],[43.982,-12],[-12.537,-42.356]],"v":[[73.491,-153.537],[43.411,-239.855],[-29.463,-206.343],[-89.007,-186.343],[-68.507,1.5],[45.518,-26.75],[96.287,-84.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[6.732,20.413],[9.339,28.855],[50.213,-22.407],[0,0],[0,0],[-49.459,13.494],[14.043,47.445]],"o":[[-7.491,-22.713],[-7.937,-24.521],[-37.662,16.806],[0,0],[0,0],[43.982,-12],[-12.537,-42.356]],"v":[[73.491,-153.537],[43.411,-239.855],[-29.463,-206.343],[-89.007,-186.343],[-68.507,1.5],[45.518,-26.75],[96.287,-84.644]],"c":true}],"e":[{"i":[[5.741,20.713],[9.339,28.855],[60.463,-27.657],[0,0],[0,0],[-49.459,13.494],[12.963,47.644]],"o":[[-5.741,-20.713],[-7.937,-24.521],[-37.505,17.155],[0,0],[0,0],[43.982,-12],[-11.586,-42.584]],"v":[[73.741,-153.787],[45.411,-241.855],[-29.713,-213.843],[-93.257,-193.343],[-71.257,0.5],[49.768,-26.5],[95.287,-81.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[5.741,20.713],[9.339,28.855],[60.463,-27.657],[0,0],[0,0],[-49.459,13.494],[12.963,47.644]],"o":[[-5.741,-20.713],[-7.937,-24.521],[-37.505,17.155],[0,0],[0,0],[43.982,-12],[-11.586,-42.584]],"v":[[73.741,-153.787],[45.411,-241.855],[-29.713,-213.843],[-93.257,-193.343],[-71.257,0.5],[49.768,-26.5],[95.287,-81.894]],"c":true}],"e":[{"i":[[5.741,20.713],[7.839,29.855],[63.713,-28.907],[0,0],[0,0],[-50.018,11.25],[12.963,53.394]],"o":[[-5.741,-20.713],[-6.546,-24.929],[-37.557,17.04],[0,0],[0,0],[46.058,-10.359],[-10.749,-44.276]],"v":[[75.741,-154.037],[52.411,-240.355],[-22.963,-222.593],[-96.007,-200.343],[-72.507,1.25],[48.768,-23.5],[95.037,-81.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[5.741,20.713],[7.839,29.855],[63.713,-28.907],[0,0],[0,0],[-50.018,11.25],[12.963,53.394]],"o":[[-5.741,-20.713],[-6.546,-24.929],[-37.557,17.04],[0,0],[0,0],[46.058,-10.359],[-10.749,-44.276]],"v":[[75.741,-154.037],[52.411,-240.355],[-22.963,-222.593],[-96.007,-200.343],[-72.507,1.25],[48.768,-23.5],[95.037,-81.894]],"c":true}],"e":[{"i":[[4.259,17.037],[7.839,29.855],[64.463,-23.407],[0,0],[0,0],[-49.518,7.25],[10.523,50.543]],"o":[[-5.991,-25.463],[-6.546,-24.929],[-38.765,14.076],[0,0],[0,0],[48.964,-7.169],[-9.287,-44.606]],"v":[[76.491,-154.787],[58.161,-239.105],[-23.213,-225.593],[-97.507,-206.343],[-74.007,1.25],[52.268,-20],[94.787,-82.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[4.259,17.037],[7.839,29.855],[64.463,-23.407],[0,0],[0,0],[-49.518,7.25],[10.523,50.543]],"o":[[-5.991,-25.463],[-6.546,-24.929],[-38.765,14.076],[0,0],[0,0],[48.964,-7.169],[-9.287,-44.606]],"v":[[76.491,-154.787],[58.161,-239.105],[-23.213,-225.593],[-97.507,-206.343],[-74.007,1.25],[52.268,-20],[94.787,-82.394]],"c":true}],"e":[{"i":[[4.009,18.287],[9.089,30.105],[63.963,-18.157],[0,0],[0,0],[-52.207,5.395],[4.963,38.144]],"o":[[-1.741,-23.713],[-7.236,-23.967],[-39.674,11.262],[0,0],[0,0],[67.732,-7],[-5.852,-44.98]],"v":[[74.491,-158.287],[64.411,-233.605],[-18.713,-229.093],[-96.757,-211.843],[-75.507,0.25],[45.268,-14.75],[94.287,-79.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[{"i":[[4.009,18.287],[9.089,30.105],[63.963,-18.157],[0,0],[0,0],[-52.207,5.395],[4.963,38.144]],"o":[[-1.741,-23.713],[-7.236,-23.967],[-39.674,11.262],[0,0],[0,0],[67.732,-7],[-5.852,-44.98]],"v":[[74.491,-158.287],[64.411,-233.605],[-18.713,-229.093],[-96.757,-211.843],[-75.507,0.25],[45.268,-14.75],[94.287,-79.394]],"c":true}],"e":[{"i":[[5.509,15.037],[12.589,49.605],[49.213,-8.907],[0,0],[0,0],[-52.29,4.52],[5.084,46.282]],"o":[[-2.241,-23.713],[-7.654,-30.16],[-40.583,7.345],[0,0],[0,0],[60.732,-5.25],[-5.037,-45.856]],"v":[[73.241,-141.287],[69.161,-222.855],[-16.963,-229.093],[-94.757,-216.843],[-76.507,-0.5],[42.768,-11.75],[94.037,-80.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.963,"s":[{"i":[[5.509,15.037],[12.589,49.605],[49.213,-8.907],[0,0],[0,0],[-52.29,4.52],[5.084,46.282]],"o":[[-2.241,-23.713],[-7.654,-30.16],[-40.583,7.345],[0,0],[0,0],[60.732,-5.25],[-5.037,-45.856]],"v":[[73.241,-141.287],[69.161,-222.855],[-16.963,-229.093],[-94.757,-216.843],[-76.507,-0.5],[42.768,-11.75],[94.037,-80.894]],"c":true}],"e":[{"i":[[15.759,20.037],[2.089,16.855],[46.963,-6.157],[0,0],[0,0],[-32.059,1.901],[3.213,53.144]],"o":[[4.509,-21.713],[-5.161,-61.645],[-32.938,4.318],[0,0],[0,0],[63.232,-3.75],[-3.787,-41.856]],"v":[[68.491,-134.287],[76.911,-187.605],[-8.463,-229.843],[-91.507,-219.843],[-77.257,-0.75],[22.518,-7.25],[94.037,-72.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17.93,"s":[{"i":[[15.759,20.037],[2.089,16.855],[46.963,-6.157],[0,0],[0,0],[-32.059,1.901],[3.213,53.144]],"o":[[4.509,-21.713],[-5.161,-61.645],[-32.938,4.318],[0,0],[0,0],[63.232,-3.75],[-3.787,-41.856]],"v":[[68.491,-134.287],[76.911,-187.605],[-8.463,-229.843],[-91.507,-219.843],[-77.257,-0.75],[22.518,-7.25],[94.037,-72.894]],"c":true}],"e":[{"i":[[20.259,14.037],[1.766,21.378],[30.713,-3.907],[0,0],[0,0],[-39.232,1.5],[3.134,56.753]],"o":[[11.509,-19.963],[-4.411,-53.395],[-38.914,4.95],[0,0],[0,0],[39.232,-1.5],[-1.787,-32.356]],"v":[[63.741,-128.537],[77.411,-184.105],[3.537,-230.593],[-88.507,-223.093],[-78.007,-1],[25.018,-5.5],[93.537,-70.894]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.756,"s":[{"i":[[20.259,14.037],[1.766,21.378],[30.713,-3.907],[0,0],[0,0],[-39.232,1.5],[3.134,56.753]],"o":[[11.509,-19.963],[-4.411,-53.395],[-38.914,4.95],[0,0],[0,0],[39.232,-1.5],[-1.787,-32.356]],"v":[[63.741,-128.537],[77.411,-184.105],[3.537,-230.593],[-88.507,-223.093],[-78.007,-1],[25.018,-5.5],[93.537,-70.894]],"c":true}],"e":[{"i":[[24.259,15.287],[1.029,21.401],[37.213,-2.157],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-3.911,-45.645],[-21.287,1.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[61.241,-124.787],[78.661,-178.355],[4.787,-229.343],[-86.007,-223.593],[-78.007,-1],[18.768,-3.5],[92.287,-66.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19.63,"s":[{"i":[[24.259,15.287],[1.029,21.401],[37.213,-2.157],[0,0],[0,0],[0,0],[2.205,48.866]],"o":[[12.683,-14.513],[-3.911,-45.645],[-21.287,1.093],[0,0],[0,0],[45.482,1],[-1.09,-28.807]],"v":[[61.241,-124.787],[78.661,-178.355],[4.787,-229.343],[-86.007,-223.593],[-78.007,-1],[18.768,-3.5],[92.287,-66.644]],"c":true}],"e":[{"i":[[22.009,10.287],[0,21.451],[39.713,-1.157],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[14.509,-10.713],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[58.491,-122.287],[79.161,-171.855],[11.037,-228.593],[-83.507,-224.093],[-78.007,-1],[21.268,-2.5],[92.037,-63.394]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.505,"s":[{"i":[[22.009,10.287],[0,21.451],[39.713,-1.157],[0,0],[0,0],[0,0],[-0.303,41.088]],"o":[[14.509,-10.713],[0,-38.672],[0,0],[0,0],[0,0],[45.482,1],[0.213,-28.856]],"v":[[58.491,-122.287],[79.161,-171.855],[11.037,-228.593],[-83.507,-224.093],[-78.007,-1],[21.268,-2.5],[92.037,-63.394]],"c":true}],"e":[{"i":[[21.149,8.762],[0.374,21.444],[35.338,-1.657],[0,0],[0,0],[0,0],[-0.227,41.088]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[43.478,0.75],[0.16,-28.893]],"v":[[56.741,-121.162],[79.411,-167.48],[13.912,-227.343],[-81.507,-224.968],[-78.382,-0.75],[19.518,-1.25],[91.287,-62.019]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21.376,"s":[{"i":[[21.149,8.762],[0.374,21.444],[35.338,-1.657],[0,0],[0,0],[0,0],[-0.227,41.088]],"o":[[15.106,-9.064],[-0.705,-39.533],[0,0],[0,0],[0,0],[43.478,0.75],[0.16,-28.893]],"v":[[56.741,-121.162],[79.411,-167.48],[13.912,-227.343],[-81.507,-224.968],[-78.382,-0.75],[19.518,-1.25],[91.287,-62.019]],"c":true}],"e":[{"i":[[21.149,8.762],[0.749,21.438],[32.213,-0.907],[0,0],[0,0],[0,0],[-0.152,41.088]],"o":[[15.106,-9.064],[-1.411,-40.395],[0,0],[0,0],[0,0],[41.473,0.5],[0.106,-28.93]],"v":[[56.241,-119.787],[79.411,-168.105],[13.287,-226.593],[-79.507,-225.843],[-78.757,-0.5],[21.518,-0.5],[90.787,-62.894]],"c":true}]},{"t":22.2529296875}]},"nm":"B"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.15,0.8,0.55,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":6,"op":15,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":30,"ty":4,"nm":"B light blue layer 2","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[-0.899,27.404],[-1.161,21.73],[-2.043,1.082],[0,0],[0,0],[1.78,-1.142],[1.963,-3.606]],"o":[[0.634,-19.338],[0.161,-3.017],[1.713,-0.907],[0,0],[0,0],[-2.143,1.375],[-0.037,-20.106]],"v":[[-64.884,-51.412],[-61.589,-110.48],[-56.963,-116.843],[-53.507,-117.468],[-55.257,2.75],[-61.857,5.625],[-66.463,11.356]],"c":true}},"nm":"B"},{"ty":"mm","mm":1,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.15,0.8,0.55,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":7,"op":8,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":31,"ty":4,"nm":"B light blue layer 1","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[0,0],[2.067,0.035],[0.272,0.321],[0.13,-0.779],[-0.356,-1.134],[-1.495,0.021],[0.012,0.775],[0.067,1.41]],"o":[[0,0],[-0.681,-0.011],[-0.197,0.927],[-0.219,1.314],[0.201,0.642],[2.094,-0.03],[0.082,-1.953],[-0.078,-1.64]],"v":[[-48.938,-1.125],[-52.016,0.387],[-54.014,-0.086],[-54.792,2.812],[-54.769,6.713],[-51.876,8.407],[-48.457,5.952],[-48.648,1.19]],"c":true}],"e":[{"i":[[0,0],[8.009,0.121],[1.054,1.117],[0.503,-2.712],[-1.38,-3.952],[-5.796,0.074],[0.046,2.698],[0.26,4.913]],"o":[[0,0],[-2.637,-0.04],[-0.765,3.23],[-0.848,4.577],[0.781,2.235],[8.117,-0.104],[0.318,-6.802],[-0.303,-5.714]],"v":[[-41.806,-11.476],[-53.736,-6.21],[-62.84,-11.105],[-65.858,1.238],[-65.495,16.577],[-54.51,22.479],[-39.943,14.677],[-40.685,-3.661]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0,0],[8.009,0.121],[1.054,1.117],[0.503,-2.712],[-1.38,-3.952],[-5.796,0.074],[0.046,2.698],[0.26,4.913]],"o":[[0,0],[-2.637,-0.04],[-0.765,3.23],[-0.848,4.577],[0.781,2.235],[8.117,-0.104],[0.318,-6.802],[-0.303,-5.714]],"v":[[-41.806,-11.476],[-53.736,-6.21],[-62.84,-11.105],[-65.858,1.238],[-65.495,16.577],[-54.51,22.479],[-39.943,14.677],[-40.685,-3.661]],"c":true}],"e":[{"i":[[0,0],[18.114,0.262],[4.357,-2.3],[0.602,-17.382],[-1.273,-6.802],[-19.282,0.006],[0.47,7.998],[1.175,10.637]],"o":[[0,0],[-5.965,-0.086],[-1.14,7.324],[-0.349,10.097],[0.939,5.019],[22.347,-0.007],[-0.749,-8.21],[-1.405,-12.724]],"v":[[-32.13,-94.796],[-52.413,-95.468],[-69.857,-95.45],[-70.102,-37.118],[-69.977,24.12],[-51.218,42.744],[-15.72,20.252],[-22.595,-20.776]],"c":true}]},{"t":8}]},"nm":"B"},{"ty":"mm","mm":3,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.15,0.8,0.55,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":6,"op":9,"st":2,"bm":0,"sr":1},{"ddd":0,"ind":32,"ty":4,"nm":"B white layer 1","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[0,0],[2.067,0.035],[0.272,0.321],[0.13,-0.779],[-0.356,-1.134],[-1.495,0.021],[0.012,0.775],[0.067,1.41]],"o":[[0,0],[-0.681,-0.011],[-0.197,0.927],[-0.219,1.314],[0.201,0.642],[2.094,-0.03],[0.082,-1.953],[-0.078,-1.64]],"v":[[-45.188,-1.125],[-48.266,0.387],[-50.264,-0.086],[-51.042,2.812],[-51.019,6.713],[-48.126,8.407],[-44.707,5.952],[-44.898,1.19]],"c":true}],"e":[{"i":[[0,0],[7.348,0.121],[0.967,1.117],[0.461,-2.712],[-1.266,-3.952],[-5.317,0.074],[0.042,2.698],[0.239,4.913]],"o":[[0,0],[-2.42,-0.04],[-0.702,3.23],[-0.778,4.577],[0.716,2.235],[7.447,-0.104],[0.292,-6.802],[-0.278,-5.714]],"v":[[-36.501,-11.351],[-47.446,-6.085],[-55.798,-10.98],[-58.567,1.363],[-58.234,15.952],[-47.697,21.854],[-34.792,13.302],[-35.472,-3.536]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[0,0],[7.348,0.121],[0.967,1.117],[0.461,-2.712],[-1.266,-3.952],[-5.317,0.074],[0.042,2.698],[0.239,4.913]],"o":[[0,0],[-2.42,-0.04],[-0.702,3.23],[-0.778,4.577],[0.716,2.235],[7.447,-0.104],[0.292,-6.802],[-0.278,-5.714]],"v":[[-36.501,-11.351],[-47.446,-6.085],[-55.798,-10.98],[-58.567,1.363],[-58.234,15.952],[-47.697,21.854],[-34.792,13.302],[-35.472,-3.536]],"c":true}],"e":[{"i":[[0,0],[18.114,0.262],[2.384,2.42],[1.102,-14.882],[-1.273,-6.802],[-19.282,0.006],[0.47,7.998],[1.175,10.637]],"o":[[0,0],[-5.965,-0.086],[-1.14,7.324],[-0.746,10.075],[0.939,5.019],[22.347,-0.007],[-0.749,-8.211],[-1.405,-12.724]],"v":[[-20.38,-97.546],[-49.163,-91.968],[-61.857,-100.7],[-65.602,-41.118],[-67.977,23.12],[-40.968,39.244],[-11.22,17.752],[-14.595,-21.526]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[0,0],[18.114,0.262],[2.384,2.42],[1.102,-14.882],[-1.273,-6.802],[-19.282,0.006],[0.47,7.998],[1.175,10.637]],"o":[[0,0],[-5.965,-0.086],[-1.14,7.324],[-0.746,10.075],[0.939,5.019],[22.347,-0.007],[-0.749,-8.211],[-1.405,-12.724]],"v":[[-20.38,-97.546],[-49.163,-91.968],[-61.857,-100.7],[-65.602,-41.118],[-67.977,23.12],[-40.968,39.244],[-11.22,17.752],[-14.595,-21.526]],"c":true}],"e":[{"i":[[0,0],[5.836,-0.538],[0.9,1.062],[-1.035,-10.477],[-0.481,-2.985],[-4.731,0.661],[-0.012,3.839],[1.64,5.168]],"o":[[0,0],[-2.242,0.207],[0.323,6.451],[0.436,4.412],[0.355,2.203],[8.337,-1.165],[-0.012,-6.693],[-1.375,-4.332]],"v":[[33.321,-6.126],[25.664,-9.212],[19,-11.513],[22.285,15.477],[25.087,31.97],[31.697,39.589],[41.03,25.38],[38.496,8.259]],"c":true}]},{"t":8}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-0.007,0.279],[0.272,0.321],[0.079,-0.813],[-1.102,-0.004],[0.001,1.067]],"o":[[-0.362,-0.011],[-0.13,0.97],[-0.13,1.326],[0.541,0.002],[-0.002,-1.19]],"v":[[-254.943,4.83],[-255.945,4.464],[-256.48,7.854],[-255.801,10.105],[-255.008,8.796]],"c":true}],"e":[{"i":[[-0.025,0.973],[0.967,1.117],[0.283,-2.831],[-3.919,-0.012],[0.005,3.715]],"o":[[-1.288,-0.04],[-0.462,3.38],[-0.461,4.62],[1.925,0.006],[-0.006,-4.146]],"v":[[-49.268,-10.266],[-52.833,-11.541],[-54.733,0.268],[-52.319,8.11],[-49.502,3.548]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.025,0.973],[0.967,1.117],[0.283,-2.831],[-3.919,-0.012],[0.005,3.715]],"o":[[-1.288,-0.04],[-0.462,3.38],[-0.461,4.62],[1.925,0.006],[-0.006,-4.146]],"v":[[-49.268,-10.266],[-52.833,-11.541],[-54.733,0.268],[-52.319,8.11],[-49.502,3.548]],"c":true}],"e":[{"i":[[-0.103,2.475],[4.052,2.841],[0.919,-10.247],[-10.422,0.015],[0.022,12.118]],"o":[[-5.362,-2.495],[-1.938,8.597],[-0.804,8.962],[8.067,-0.012],[-0.019,-10.546]],"v":[[-44.638,-94.255],[-60.829,-101],[-66.196,3.288],[-56.078,21.235],[-40.272,8.382]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":7,"s":[{"i":[[-0.103,2.475],[4.052,2.841],[0.919,-10.247],[-10.422,0.015],[0.022,12.118]],"o":[[-5.362,-2.495],[-1.938,8.597],[-0.804,8.962],[8.067,-0.012],[-0.019,-10.546]],"v":[[-44.638,-94.255],[-60.829,-101],[-66.196,3.288],[-56.078,21.235],[-40.272,8.382]],"c":true}],"e":[{"i":[[-0.061,2.109],[3.595,-2.75],[0.696,-6.135],[-9.66,-0.026],[0.013,8.051]],"o":[[-3.175,-0.086],[-1.14,7.324],[-1.137,10.012],[4.745,0.013],[-0.015,-8.985]],"v":[[-56.056,-36.486],[-75.595,-26.5],[-76.296,12.591],[-66.594,33.585],[-52.9,17.2]],"c":true}]},{"t":8}]},"nm":"B 2"},{"ty":"mm","mm":3,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.32,0.5,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":5,"op":9,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":33,"ty":4,"nm":"B orange layer 1","parent":34,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,400,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":4,"s":[{"i":[[0,0],[2.067,0.035],[0.272,0.321],[0.13,-0.779],[-0.356,-1.134],[-1.495,0.021],[0.012,0.775],[0.067,1.41]],"o":[[0,0],[-0.681,-0.011],[-0.197,0.927],[-0.219,1.314],[0.201,0.642],[2.094,-0.03],[0.082,-1.953],[-0.078,-1.64]],"v":[[-40.938,-1],[-44.016,0.512],[-46.014,0.039],[-46.792,2.937],[-46.769,6.838],[-43.876,9.032],[-40.457,6.077],[-40.648,1.315]],"c":true}],"e":[{"i":[[0,0],[7.348,0.121],[0.967,1.117],[0.461,-2.712],[-1.266,-3.952],[-5.317,0.074],[0.042,2.698],[0.239,4.913]],"o":[[0,0],[-2.42,-0.04],[-0.702,3.23],[-0.778,4.577],[0.716,2.235],[7.447,-0.104],[0.292,-6.802],[-0.278,-5.714]],"v":[[-32.501,-11.351],[-43.446,-6.085],[-50.548,-7.73],[-53.317,2.363],[-53.734,15.952],[-42.697,22.604],[-30.292,13.552],[-31.472,-3.286]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[0,0],[7.348,0.121],[0.967,1.117],[0.461,-2.712],[-1.266,-3.952],[-5.317,0.074],[0.042,2.698],[0.239,4.913]],"o":[[0,0],[-2.42,-0.04],[-0.702,3.23],[-0.778,4.577],[0.716,2.235],[7.447,-0.104],[0.292,-6.802],[-0.278,-5.714]],"v":[[-32.501,-11.351],[-43.446,-6.085],[-50.548,-7.73],[-53.317,2.363],[-53.734,15.952],[-42.697,22.604],[-30.292,13.552],[-31.472,-3.286]],"c":true}],"e":[{"i":[[0,0],[18.114,0.262],[2.384,2.42],[1.602,-8.382],[-1.273,-6.802],[-16.121,0.205],[0.47,7.998],[2.014,10.511]],"o":[[0,0],[-5.965,-0.086],[-1.14,7.324],[-1.896,9.923],[0.939,5.019],[22.345,-0.285],[-0.749,-8.211],[-1.98,-10.335]],"v":[[-16.38,-29.046],[-39.663,-18.718],[-54.607,-25.2],[-57.602,-5.368],[-59.727,23.12],[-35.218,37.244],[-9.72,15.252],[-13.845,-14.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[0,0],[18.114,0.262],[2.384,2.42],[1.602,-8.382],[-1.273,-6.802],[-16.121,0.205],[0.47,7.998],[2.014,10.511]],"o":[[0,0],[-5.965,-0.086],[-1.14,7.324],[-1.896,9.923],[0.939,5.019],[22.345,-0.285],[-0.749,-8.211],[-1.98,-10.335]],"v":[[-16.38,-29.046],[-39.663,-18.718],[-54.607,-25.2],[-57.602,-5.368],[-59.727,23.12],[-35.218,37.244],[-9.72,15.252],[-13.845,-14.276]],"c":true}],"e":[{"i":[[0,0],[18.114,0.262],[2.384,2.42],[0.352,-20.882],[-1.273,-6.802],[-12.532,1.506],[-0.03,8.748],[4.345,11.776]],"o":[[0,0],[-5.965,-0.086],[0.857,14.7],[-0.17,10.101],[0.939,5.019],[22.085,-2.655],[-0.03,-15.252],[-3.642,-9.873]],"v":[[7.12,-52.546],[-13.163,-60.718],[-24.857,-45.45],[-24.102,2.382],[-21.977,37.12],[-4.468,50.494],[29.53,19.252],[22.155,-15.776]],"c":true}]},{"t":7}]},"nm":"B"},{"ind":1,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":4,"s":[{"i":[[-0.007,0.279],[0.272,0.321],[0.079,-0.813],[-1.102,-0.004],[0.001,1.067]],"o":[[-0.362,-0.011],[-0.13,0.97],[-0.13,1.326],[0.541,0.002],[-0.002,-1.19]],"v":[[-254.943,4.83],[-255.945,4.464],[-256.48,7.854],[-255.801,10.105],[-255.008,8.796]],"c":true}],"e":[{"i":[[-0.025,0.973],[0.967,1.117],[0.283,-2.831],[-3.919,-0.012],[0.005,3.715]],"o":[[-1.288,-0.04],[-0.462,3.38],[-0.461,4.62],[1.925,0.006],[-0.006,-4.146]],"v":[[-44.518,-9.766],[-48.083,-11.041],[-49.983,0.768],[-47.319,8.11],[-44.752,2.798]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":5,"s":[{"i":[[-0.025,0.973],[0.967,1.117],[0.283,-2.831],[-3.919,-0.012],[0.005,3.715]],"o":[[-1.288,-0.04],[-0.462,3.38],[-0.461,4.62],[1.925,0.006],[-0.006,-4.146]],"v":[[-44.518,-9.766],[-48.083,-11.041],[-49.983,0.768],[-47.319,8.11],[-44.752,2.798]],"c":true}],"e":[{"i":[[-0.103,2.475],[4.052,2.841],[1.184,-7.201],[-11.422,1.515],[0.022,9.45]],"o":[[-5.398,-0.101],[-1.938,8.597],[-1.932,11.751],[7.997,-1.061],[-0.025,-10.546]],"v":[[-35.638,-26.505],[-50.579,-29.75],[-56.696,0.288],[-45.828,20.985],[-35.272,6.382]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":6,"s":[{"i":[[-0.103,2.475],[4.052,2.841],[1.184,-7.201],[-11.422,1.515],[0.022,9.45]],"o":[[-5.398,-0.101],[-1.938,8.597],[-1.932,11.751],[7.997,-1.061],[-0.025,-10.546]],"v":[[-35.638,-26.505],[-50.579,-29.75],[-56.696,0.288],[-45.828,20.985],[-35.272,6.382]],"c":true}],"e":[{"i":[[-0.061,2.109],[3.595,-2.75],[0.696,-6.135],[-9.66,-0.026],[0.013,8.051]],"o":[[-3.175,-0.086],[-1.14,7.324],[-1.137,10.012],[4.745,0.013],[-0.015,-8.985]],"v":[[-15.306,-36.486],[-34.095,-25],[-34.796,14.091],[-25.094,35.085],[-12.4,18.95]],"c":true}]},{"t":7}]},"nm":"B 2"},{"ty":"mm","mm":3,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0.86,0.86,0.86,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"B"}],"ip":4,"op":8,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":34,"ty":1,"nm":"ResizerTemp","parent":35,"ks":{"o":{"k":0},"r":{"k":0},"p":{"k":[74,102,0]},"a":{"k":[250,300,0]},"s":{"k":[30,30,100]}},"ao":0,"sw":500,"sh":600,"sc":"#ffffff","ip":0,"op":37,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":35,"ty":1,"nm":"White Solid 8","ks":{"o":{"k":0},"r":{"k":0},"p":{"k":[40,50,0]},"a":{"k":[70,100,0]},"s":{"k":[57.143,50,100]}},"ao":0,"sw":140,"sh":200,"sc":"#ffffff","ip":0,"op":37,"st":0,"bm":0,"sr":1}],"v":"4.4.26","ddd":0,"ip":0,"op":37,"fr":25,"w":80,"h":100} diff --git a/sdk/python/examples/controls/lottie/example_1/main.py b/sdk/python/examples/controls/lottie/example_1/main.py new file mode 100644 index 0000000000..cba7564e9b --- /dev/null +++ b/sdk/python/examples/controls/lottie/example_1/main.py @@ -0,0 +1,33 @@ +import flet as ft +import flet_lottie as ftl + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ftl.Lottie( + src="https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json", + reverse=False, + animate=True, + error_content=ft.Placeholder(ft.Text("Error loading Lottie")), + on_error=lambda e: print(f"Error loading Lottie: {e.data}"), + ), + ftl.Lottie( + src="sample.json", + reverse=False, + animate=True, + enable_merge_paths=True, + enable_layers_opacity=True, + error_content=ft.Placeholder(ft.Text("Error loading Lottie")), + on_error=lambda e: print(f"Error loading Lottie: {e.data}"), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/lottie/example_1/pyproject.toml b/sdk/python/examples/controls/lottie/example_1/pyproject.toml new file mode 100644 index 0000000000..670db24128 --- /dev/null +++ b/sdk/python/examples/controls/lottie/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "lottie-example-1" +version = "1.0.0" +description = "Plays Lottie animations from a remote URL and local JSON file with error fallback content." +requires-python = ">=3.10" +keywords = ["lottie", "animation", "json", "remote", "assets"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-lottie"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Media/Lottie"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "Column", "Lottie", "Placeholder", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["remote lottie animation", "local lottie asset", "error callback and fallback content"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/map/basic.py b/sdk/python/examples/controls/map/basic.py deleted file mode 100644 index cf01f65186..0000000000 --- a/sdk/python/examples/controls/map/basic.py +++ /dev/null @@ -1,22 +0,0 @@ -import flet as ft -import flet_map as ftm - - -def main(page: ft.Page): - page.add( - ft.SafeArea( - expand=True, - content=ftm.Map( - expand=True, - layers=[ - ftm.TileLayer( - url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png", - on_image_error=lambda e: print(f"TileLayer Error: {e.data}"), - ), - ], - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/map/basic/main.py b/sdk/python/examples/controls/map/basic/main.py new file mode 100644 index 0000000000..45a4d8870c --- /dev/null +++ b/sdk/python/examples/controls/map/basic/main.py @@ -0,0 +1,23 @@ +import flet as ft +import flet_map as ftm + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + expand=True, + content=ftm.Map( + expand=True, + layers=[ + ftm.TileLayer( + url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png", + on_image_error=lambda e: print(f"TileLayer Error: {e.data}"), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/map/basic/pyproject.toml b/sdk/python/examples/controls/map/basic/pyproject.toml new file mode 100644 index 0000000000..9192cfc420 --- /dev/null +++ b/sdk/python/examples/controls/map/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "map-basic" +version = "1.0.0" +description = "Shows a basic flet-map tile layer with error callback handling." +requires-python = ">=3.10" +keywords = ["map", "tile layer", "flet-map", "geospatial"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-map"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/Map"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Map", "TileLayer"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["map tile rendering", "tile load error callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/map/camera_controls.py b/sdk/python/examples/controls/map/camera_controls.py deleted file mode 100644 index a475ecf237..0000000000 --- a/sdk/python/examples/controls/map/camera_controls.py +++ /dev/null @@ -1,106 +0,0 @@ -import flet as ft -import flet_map as ftm - - -def main(page: ft.Page): - page.padding = 16 - - async def update_camera_status(trigger: str): - camera = await my_map.get_camera() - camera_status.value = ( - f"Camera [{trigger}]: " - f"center=({camera.center.latitude:.5f}, {camera.center.longitude:.5f}), " - f"zoom={camera.zoom:.2f}, rotation={camera.rotation:.1f}" - ) - page.update() - - async def zoom_in(e: ft.Event[ft.Button]): - await my_map.zoom_in() - await update_camera_status("zoom_in") - - async def zoom_out(e: ft.Event[ft.Button]): - await my_map.zoom_out() - await update_camera_status("zoom_out") - - async def rotate_plus_15(e: ft.Event[ft.Button]): - await my_map.rotate_from(15) - await update_camera_status("rotate_from(+15)") - - async def reset_rotation(e: ft.Event[ft.Button]): - await my_map.reset_rotation() - await update_camera_status("reset_rotation") - - async def center_berlin(e: ft.Event[ft.Button]): - await my_map.center_on(point=ftm.MapLatitudeLongitude(52.52, 13.405), zoom=12) - await update_camera_status("center_on(berlin)") - - async def move_tokyo(e: ft.Event[ft.Button]): - await my_map.move_to( - destination=ftm.MapLatitudeLongitude(35.6762, 139.6503), zoom=11 - ) - await update_camera_status("move_to(tokyo)") - - async def set_world_zoom(e: ft.Event[ft.Button]): - await my_map.zoom_to(3) - await update_camera_status("zoom_to(3)") - - page.appbar = ft.AppBar(title="Camera controls") - page.add( - ft.Column( - expand=True, - controls=[ - ft.Text( - "Use buttons to control map camera programmatically.", - size=12, - ), - ft.Row( - wrap=True, - spacing=8, - run_spacing=8, - controls=[ - ft.Button("Zoom in", on_click=zoom_in), - ft.Button("Zoom out", on_click=zoom_out), - ft.Button("Rotate +15°", on_click=rotate_plus_15), - ft.Button("Reset rotation", on_click=reset_rotation), - ft.Button("Center Berlin", on_click=center_berlin), - ft.Button("Move to Tokyo", on_click=move_tokyo), - ft.Button("World zoom (3)", on_click=set_world_zoom), - ], - ), - camera_status := ft.Text(selectable=True, font_family="monospace"), - my_map := ftm.Map( - expand=True, - initial_center=ftm.MapLatitudeLongitude(52.52, 13.405), - initial_zoom=5, - layers=[ - ftm.TileLayer( - url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png" - ), - ftm.SimpleAttribution( - text="OpenStreetMap contributors", - on_click=lambda e: e.page.launch_url( - "https://www.openstreetmap.org/copyright" - ), - ), - ftm.MarkerLayer( - markers=[ - ftm.Marker( - coordinates=ftm.MapLatitudeLongitude(52.52, 13.405), - content=ft.Icon(ft.Icons.LOCATION_ON), - ), - ftm.Marker( - coordinates=ftm.MapLatitudeLongitude( - 35.6762, 139.6503 - ), - content=ft.Icon(ft.Icons.LOCATION_ON), - ), - ] - ), - ], - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/map/camera_controls/main.py b/sdk/python/examples/controls/map/camera_controls/main.py new file mode 100644 index 0000000000..f3f74e9d37 --- /dev/null +++ b/sdk/python/examples/controls/map/camera_controls/main.py @@ -0,0 +1,112 @@ +import flet as ft +import flet_map as ftm + + +def main(page: ft.Page): + page.padding = 16 + + async def update_camera_status(trigger: str): + camera = await my_map.get_camera() + camera_status.value = ( + f"Camera [{trigger}]: " + f"center=({camera.center.latitude:.5f}, {camera.center.longitude:.5f}), " + f"zoom={camera.zoom:.2f}, rotation={camera.rotation:.1f}" + ) + page.update() + + async def zoom_in(e: ft.Event[ft.Button]): + await my_map.zoom_in() + await update_camera_status("zoom_in") + + async def zoom_out(e: ft.Event[ft.Button]): + await my_map.zoom_out() + await update_camera_status("zoom_out") + + async def rotate_plus_15(e: ft.Event[ft.Button]): + await my_map.rotate_from(15) + await update_camera_status("rotate_from(+15)") + + async def reset_rotation(e: ft.Event[ft.Button]): + await my_map.reset_rotation() + await update_camera_status("reset_rotation") + + async def center_berlin(e: ft.Event[ft.Button]): + await my_map.center_on(point=ftm.MapLatitudeLongitude(52.52, 13.405), zoom=12) + await update_camera_status("center_on(berlin)") + + async def move_tokyo(e: ft.Event[ft.Button]): + await my_map.move_to( + destination=ftm.MapLatitudeLongitude(35.6762, 139.6503), zoom=11 + ) + await update_camera_status("move_to(tokyo)") + + async def set_world_zoom(e: ft.Event[ft.Button]): + await my_map.zoom_to(3) + await update_camera_status("zoom_to(3)") + + page.appbar = ft.AppBar(title="Camera controls") + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.Text( + "Use buttons to control map camera programmatically.", + size=12, + ), + ft.Row( + wrap=True, + spacing=8, + run_spacing=8, + controls=[ + ft.Button("Zoom in", on_click=zoom_in), + ft.Button("Zoom out", on_click=zoom_out), + ft.Button("Rotate +15°", on_click=rotate_plus_15), + ft.Button("Reset rotation", on_click=reset_rotation), + ft.Button("Center Berlin", on_click=center_berlin), + ft.Button("Move to Tokyo", on_click=move_tokyo), + ft.Button("World zoom (3)", on_click=set_world_zoom), + ], + ), + camera_status := ft.Text(selectable=True, font_family="monospace"), + my_map := ftm.Map( + expand=True, + initial_center=ftm.MapLatitudeLongitude(52.52, 13.405), + initial_zoom=5, + layers=[ + ftm.TileLayer( + url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png" + ), + ftm.SimpleAttribution( + text="OpenStreetMap contributors", + on_click=lambda e: e.page.launch_url( + "https://www.openstreetmap.org/copyright" + ), + ), + ftm.MarkerLayer( + markers=[ + ftm.Marker( + coordinates=ftm.MapLatitudeLongitude( + 52.52, 13.405 + ), + content=ft.Icon(ft.Icons.LOCATION_ON), + ), + ftm.Marker( + coordinates=ftm.MapLatitudeLongitude( + 35.6762, 139.6503 + ), + content=ft.Icon(ft.Icons.LOCATION_ON), + ), + ] + ), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/map/camera_controls/pyproject.toml b/sdk/python/examples/controls/map/camera_controls/pyproject.toml new file mode 100644 index 0000000000..4469103649 --- /dev/null +++ b/sdk/python/examples/controls/map/camera_controls/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "map-camera-controls" +version = "1.0.0" +description = "Controls map camera programmatically with zoom, rotate, center, move, and state text updates." +requires-python = ">=3.10" +keywords = ["map", "camera controls", "zoom", "rotate", "markers"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-map"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/Map"] + +[tool.flet.metadata] +title = "Camera Controls" +controls = ["SafeArea", "Column", "Row", "Map", "TileLayer", "SimpleAttribution", "MarkerLayer", "Marker", "Button", "Text", "Icon", "AppBar"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["programmatic camera actions", "camera state display", "predefined map centers"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/map/idle_camera.py b/sdk/python/examples/controls/map/idle_camera.py deleted file mode 100644 index 9630e0140c..0000000000 --- a/sdk/python/examples/controls/map/idle_camera.py +++ /dev/null @@ -1,76 +0,0 @@ -import flet as ft -import flet_map as ftm - -IDLE_EVENT_TYPES = { - ftm.MapEventType.MOVE_END, - ftm.MapEventType.FLING_ANIMATION_END, - ftm.MapEventType.FLING_ANIMATION_NOT_STARTED, - ftm.MapEventType.DOUBLE_TAP_ZOOM_END, - ftm.MapEventType.ROTATE_END, -} - - -def main(page: ft.Page): - page.padding = 16 - - async def handle_map_event(e: ftm.MapEvent): - last_event.value = ( - "Last event: " - f"type={e.event_type}, source={e.source.value}, zoom={e.camera.zoom:.2f}" - ) - - if e.event_type in IDLE_EVENT_TYPES: # here the camera is settled/idle - camera = await e.control.get_camera() - settled_camera.value = ( - "Settled camera: " - f"center=({camera.center.latitude:.3f}, " - f"{camera.center.longitude:.3f}), " - f"zoom={camera.zoom:.2f}, rotation={camera.rotation:.1f}, " - f"trigger={e.event_type}" - ) - - page.update() - - page.add( - ft.Column( - expand=True, - controls=[ - ft.Text("Camera idle pattern", size=20, weight=ft.FontWeight.BOLD), - ft.Text( - "Drag, fling, rotate, zoom and watch when the camera is settled.", - size=12, - ), - last_event := ft.Text( - "Last event: -", selectable=True, font_family="monospace" - ), - settled_camera := ft.Text( - "Settled camera: -", selectable=True, font_family="monospace" - ), - ftm.Map( - expand=True, - initial_center=ftm.MapLatitudeLongitude( - latitude=52.52, longitude=13.405 - ), - initial_zoom=11, - on_event=handle_map_event, - interaction_configuration=ftm.InteractionConfiguration( - flags=ftm.InteractionFlag.ALL - ), - layers=[ - ftm.TileLayer( - url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png" - ), - ftm.SimpleAttribution( - text="OpenStreetMap contributors", - on_click=lambda e: e.page.launch_url( - "https://www.openstreetmap.org/copyright" - ), - ), - ], - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/map/idle_camera/main.py b/sdk/python/examples/controls/map/idle_camera/main.py new file mode 100644 index 0000000000..7a14addd86 --- /dev/null +++ b/sdk/python/examples/controls/map/idle_camera/main.py @@ -0,0 +1,83 @@ +import flet as ft +import flet_map as ftm + +IDLE_EVENT_TYPES = { + ftm.MapEventType.MOVE_END, + ftm.MapEventType.FLING_ANIMATION_END, + ftm.MapEventType.FLING_ANIMATION_NOT_STARTED, + ftm.MapEventType.DOUBLE_TAP_ZOOM_END, + ftm.MapEventType.ROTATE_END, +} + + +def main(page: ft.Page): + page.padding = 16 + + async def handle_map_event(e: ftm.MapEvent): + last_event.value = ( + "Last event: " + f"type={e.event_type}, source={e.source.value}, zoom={e.camera.zoom:.2f}" + ) + + if e.event_type in IDLE_EVENT_TYPES: # here the camera is settled/idle + camera = await e.control.get_camera() + settled_camera.value = ( + "Settled camera: " + f"center=({camera.center.latitude:.3f}, " + f"{camera.center.longitude:.3f}), " + f"zoom={camera.zoom:.2f}, rotation={camera.rotation:.1f}, " + f"trigger={e.event_type}" + ) + + page.update() + + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.Text("Camera idle pattern", size=20, weight=ft.FontWeight.BOLD), + ft.Text( + ( + "Drag, fling, rotate, zoom and watch " + "when the camera is settled." + ), + size=12, + ), + last_event := ft.Text( + "Last event: -", selectable=True, font_family="monospace" + ), + settled_camera := ft.Text( + "Settled camera: -", selectable=True, font_family="monospace" + ), + ftm.Map( + expand=True, + initial_center=ftm.MapLatitudeLongitude( + latitude=52.52, longitude=13.405 + ), + initial_zoom=11, + on_event=handle_map_event, + interaction_configuration=ftm.InteractionConfiguration( + flags=ftm.InteractionFlag.ALL + ), + layers=[ + ftm.TileLayer( + url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png" + ), + ftm.SimpleAttribution( + text="OpenStreetMap contributors", + on_click=lambda e: e.page.launch_url( + "https://www.openstreetmap.org/copyright" + ), + ), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/map/idle_camera/pyproject.toml b/sdk/python/examples/controls/map/idle_camera/pyproject.toml new file mode 100644 index 0000000000..9d3721d8ba --- /dev/null +++ b/sdk/python/examples/controls/map/idle_camera/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "map-idle-camera" +version = "1.0.0" +description = "Tracks map events and reports settled camera state when idle-related events occur." +requires-python = ">=3.10" +keywords = ["map", "camera idle", "events", "interaction flags"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-map"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/Map"] + +[tool.flet.metadata] +title = "Idle Camera" +controls = ["SafeArea", "Column", "Map", "TileLayer", "SimpleAttribution", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["map event handling", "idle camera detection", "camera status output"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/map/interaction_flags.py b/sdk/python/examples/controls/map/interaction_flags.py deleted file mode 100644 index 5a9cf7cc49..0000000000 --- a/sdk/python/examples/controls/map/interaction_flags.py +++ /dev/null @@ -1,101 +0,0 @@ -import flet as ft -import flet_map as ftm - -FLAG_OPTIONS: list[tuple[str, ftm.InteractionFlag]] = [ - ("Drag", ftm.InteractionFlag.DRAG), - ("Fling animation", ftm.InteractionFlag.FLING_ANIMATION), - ("Pinch move", ftm.InteractionFlag.PINCH_MOVE), - ("Pinch zoom", ftm.InteractionFlag.PINCH_ZOOM), - ("Double tap zoom", ftm.InteractionFlag.DOUBLE_TAP_ZOOM), - ("Double tap drag zoom", ftm.InteractionFlag.DOUBLE_TAP_DRAG_ZOOM), - ("Scroll wheel zoom", ftm.InteractionFlag.SCROLL_WHEEL_ZOOM), - ("Rotate", ftm.InteractionFlag.ROTATE), -] - - -def main(page: ft.Page): - page.padding = 16 - - def get_selected_flags() -> ftm.InteractionFlag: - flags = ftm.InteractionFlag.NONE - for checkbox in checkboxes: - if checkbox.value: - flags |= checkbox.data - return flags - - def update_interaction_flags(e: ft.Event[ft.Checkbox] = None): - flags = get_selected_flags() - my_map.interaction_configuration = ftm.InteractionConfiguration(flags=flags) - page.update() - - def handle_map_event(e: ftm.MapEvent): - event_type = e.event_type.value if e.event_type else "-" - last_event.value = ( - "Last event: " - f"type={event_type}, source={e.source.value}, zoom={e.camera.zoom:.2f}" - ) - page.update() - - checkboxes = [ - ft.Checkbox( - label=label, - value=True, - data=flag, - on_change=update_interaction_flags, - ) - for label, flag in FLAG_OPTIONS - ] - - my_map = ftm.Map( - expand=True, - initial_center=ftm.MapLatitudeLongitude(latitude=52.52, longitude=13.405), - initial_zoom=11, - on_event=handle_map_event, - interaction_configuration=ftm.InteractionConfiguration( - flags=ftm.InteractionFlag.ALL - ), - layers=[ - ftm.TileLayer( - url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png", - on_image_error=lambda e: print(f"TileLayer Error: {e.data}"), - ), - ftm.RichAttribution( - attributions=[ - ftm.TextSourceAttribution( - text="OpenStreetMap contributors", - on_click=lambda e: e.page.launch_url( - "https://www.openstreetmap.org/copyright" - ), - ) - ] - ), - ], - ) - - page.appbar = ft.AppBar(title="Interaction flags") - page.add( - ft.Column( - expand=True, - controls=[ - ft.Text( - "Toggle flags and try dragging, zooming, rotating, and scrolling.", - size=12, - ), - ft.ResponsiveRow( - controls=[ - ft.Container(col={"sm": 6, "md": 4}, content=c) - for c in checkboxes - ] - ), - last_event := ft.Text( - "Last event: -", selectable=True, font_family="monospace" - ), - ft.Container(expand=True, content=my_map), - ], - ) - ) - - update_interaction_flags() - - -ft.run(main) diff --git a/sdk/python/examples/controls/map/interaction_flags/main.py b/sdk/python/examples/controls/map/interaction_flags/main.py new file mode 100644 index 0000000000..77a2b07dd5 --- /dev/null +++ b/sdk/python/examples/controls/map/interaction_flags/main.py @@ -0,0 +1,108 @@ +import flet as ft +import flet_map as ftm + +FLAG_OPTIONS: list[tuple[str, ftm.InteractionFlag]] = [ + ("Drag", ftm.InteractionFlag.DRAG), + ("Fling animation", ftm.InteractionFlag.FLING_ANIMATION), + ("Pinch move", ftm.InteractionFlag.PINCH_MOVE), + ("Pinch zoom", ftm.InteractionFlag.PINCH_ZOOM), + ("Double tap zoom", ftm.InteractionFlag.DOUBLE_TAP_ZOOM), + ("Double tap drag zoom", ftm.InteractionFlag.DOUBLE_TAP_DRAG_ZOOM), + ("Scroll wheel zoom", ftm.InteractionFlag.SCROLL_WHEEL_ZOOM), + ("Rotate", ftm.InteractionFlag.ROTATE), +] + + +def main(page: ft.Page): + page.padding = 16 + + def get_selected_flags() -> ftm.InteractionFlag: + flags = ftm.InteractionFlag.NONE + for checkbox in checkboxes: + if checkbox.value: + flags |= checkbox.data + return flags + + def update_interaction_flags(e: ft.Event[ft.Checkbox] = None): + flags = get_selected_flags() + my_map.interaction_configuration = ftm.InteractionConfiguration(flags=flags) + page.update() + + def handle_map_event(e: ftm.MapEvent): + event_type = e.event_type.value if e.event_type else "-" + last_event.value = ( + "Last event: " + f"type={event_type}, source={e.source.value}, zoom={e.camera.zoom:.2f}" + ) + page.update() + + checkboxes = [ + ft.Checkbox( + label=label, + value=True, + data=flag, + on_change=update_interaction_flags, + ) + for label, flag in FLAG_OPTIONS + ] + + my_map = ftm.Map( + expand=True, + initial_center=ftm.MapLatitudeLongitude(latitude=52.52, longitude=13.405), + initial_zoom=11, + on_event=handle_map_event, + interaction_configuration=ftm.InteractionConfiguration( + flags=ftm.InteractionFlag.ALL + ), + layers=[ + ftm.TileLayer( + url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png", + on_image_error=lambda e: print(f"TileLayer Error: {e.data}"), + ), + ftm.RichAttribution( + attributions=[ + ftm.TextSourceAttribution( + text="OpenStreetMap contributors", + on_click=lambda e: e.page.launch_url( + "https://www.openstreetmap.org/copyright" + ), + ) + ] + ), + ], + ) + + page.appbar = ft.AppBar(title="Interaction flags") + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.Text( + ( + "Toggle flags and try dragging, zooming, rotating, " + "and scrolling." + ), + size=12, + ), + ft.ResponsiveRow( + controls=[ + ft.Container(col={"sm": 6, "md": 4}, content=c) + for c in checkboxes + ] + ), + last_event := ft.Text( + "Last event: -", selectable=True, font_family="monospace" + ), + ft.Container(expand=True, content=my_map), + ], + ), + ) + ) + + update_interaction_flags() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/map/interaction_flags/pyproject.toml b/sdk/python/examples/controls/map/interaction_flags/pyproject.toml new file mode 100644 index 0000000000..da37819e2c --- /dev/null +++ b/sdk/python/examples/controls/map/interaction_flags/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "map-interaction-flags" +version = "1.0.0" +description = "Toggles map interaction flags with checkboxes and displays live map event details." +requires-python = ">=3.10" +keywords = ["map", "interaction flags", "checkbox", "events", "attribution"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-map"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/Map"] + +[tool.flet.metadata] +title = "Interaction Flags" +controls = ["SafeArea", "Column", "ResponsiveRow", "Container", "Checkbox", "Map", "TileLayer", "RichAttribution", "TextSourceAttribution", "Text", "AppBar"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["runtime interaction toggles", "map event logging", "custom attributions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/map/multi_layers.py b/sdk/python/examples/controls/map/multi_layers.py deleted file mode 100644 index 50dc80699e..0000000000 --- a/sdk/python/examples/controls/map/multi_layers.py +++ /dev/null @@ -1,132 +0,0 @@ -import random - -import flet as ft -import flet_map as ftm - - -def main(page: ft.Page): - def handle_tap(e: ftm.MapTapEvent): - if e.name == "tap": - marker_layer.markers.append( - ftm.Marker( - content=ft.Icon( - ft.Icons.LOCATION_ON, color=ft.CupertinoColors.DESTRUCTIVE_RED - ), - coordinates=e.coordinates, - ) - ) - elif e.name == "secondary_tap": - circle_layer.circles.append( - ftm.CircleMarker( - radius=random.randint(5, 10), - coordinates=e.coordinates, - color=ft.Colors.random(), - border_color=ft.Colors.random(), - border_stroke_width=4, - ) - ) - page.update() - - page.appbar = ft.AppBar(title="Multiple Layers") - page.add( - ft.Text("Click anywhere to add a Marker, right-click to add a CircleMarker."), - ftm.Map( - expand=True, - initial_center=ftm.MapLatitudeLongitude(15, 10), - initial_zoom=4.2, - interaction_configuration=ftm.InteractionConfiguration( - flags=ftm.InteractionFlag.ALL - ), - on_tap=handle_tap, - on_secondary_tap=handle_tap, - on_event=print, - layers=[ - ftm.TileLayer( - url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png", - on_image_error=lambda e: print("TileLayer Error"), - ), - ftm.RichAttribution( - attributions=[ - ftm.TextSourceAttribution( - text="OpenStreetMap Contributors", - on_click=lambda e: e.page.launch_url( - "https://www.openstreetmap.org/copyright" - ), - ), - ftm.TextSourceAttribution( - text="Flet", - on_click=lambda e: e.page.launch_url("https://flet.dev"), - ), - ] - ), - ftm.SimpleAttribution( - text="Flet", - alignment=ft.Alignment.TOP_RIGHT, - on_click=lambda e: print("Clicked SimpleAttribution"), - ), - marker_layer := ftm.MarkerLayer( - markers=[ - ftm.Marker( - content=ft.Icon(ft.Icons.LOCATION_ON), - coordinates=ftm.MapLatitudeLongitude(30, 15), - ), - ftm.Marker( - content=ft.Icon(ft.Icons.LOCATION_ON), - coordinates=ftm.MapLatitudeLongitude(10, 10), - ), - ftm.Marker( - content=ft.Icon(ft.Icons.LOCATION_ON), - coordinates=ftm.MapLatitudeLongitude(25, 45), - ), - ], - ), - circle_layer := ftm.CircleLayer( - circles=[ - ftm.CircleMarker( - radius=10, - coordinates=ftm.MapLatitudeLongitude(16, 24), - color=ft.Colors.RED, - border_color=ft.Colors.BLUE, - border_stroke_width=4, - ), - ], - ), - ftm.PolygonLayer( - polygons=[ - ftm.PolygonMarker( - label="Popular Touristic Area", - label_text_style=ft.TextStyle( - color=ft.Colors.BLACK, - size=15, - weight=ft.FontWeight.BOLD, - ), - color=ft.Colors.with_opacity(0.3, ft.Colors.BLUE), - coordinates=[ - ftm.MapLatitudeLongitude(10, 10), - ftm.MapLatitudeLongitude(30, 15), - ftm.MapLatitudeLongitude(25, 45), - ], - ), - ], - ), - ftm.PolylineLayer( - polylines=[ - ftm.PolylineMarker( - border_stroke_width=3, - border_color=ft.Colors.RED, - gradient_colors=[ft.Colors.BLACK, ft.Colors.BLACK], - color=ft.Colors.with_opacity(0.6, ft.Colors.GREEN), - coordinates=[ - ftm.MapLatitudeLongitude(10, 10), - ftm.MapLatitudeLongitude(30, 15), - ftm.MapLatitudeLongitude(25, 45), - ], - ), - ], - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/map/multi_layers/main.py b/sdk/python/examples/controls/map/multi_layers/main.py new file mode 100644 index 0000000000..693cb9716f --- /dev/null +++ b/sdk/python/examples/controls/map/multi_layers/main.py @@ -0,0 +1,153 @@ +import random + +import flet as ft +import flet_map as ftm + + +def main(page: ft.Page): + def handle_tap(e: ftm.MapTapEvent): + if e.name == "tap": + marker_layer.markers.append( + ftm.Marker( + content=ft.Icon( + ft.Icons.LOCATION_ON, color=ft.CupertinoColors.DESTRUCTIVE_RED + ), + coordinates=e.coordinates, + ) + ) + elif e.name == "secondary_tap": + circle_layer.circles.append( + ftm.CircleMarker( + radius=random.randint(5, 10), + coordinates=e.coordinates, + color=ft.Colors.random(), + border_color=ft.Colors.random(), + border_stroke_width=4, + ) + ) + page.update() + + page.appbar = ft.AppBar(title="Multiple Layers") + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.Text( + "Click anywhere to add a Marker, right-click to add a " + "CircleMarker." + ), + ftm.Map( + expand=True, + initial_center=ftm.MapLatitudeLongitude(15, 10), + initial_zoom=4.2, + interaction_configuration=ftm.InteractionConfiguration( + flags=ftm.InteractionFlag.ALL + ), + on_tap=handle_tap, + on_secondary_tap=handle_tap, + on_event=print, + layers=[ + ftm.TileLayer( + url_template="https://tile.memomaps.de/tilegen/{z}/{x}/{y}.png", + on_image_error=lambda e: print("TileLayer Error"), + ), + ftm.RichAttribution( + attributions=[ + ftm.TextSourceAttribution( + text="OpenStreetMap Contributors", + on_click=lambda e: e.page.launch_url( + "https://www.openstreetmap.org/copyright" + ), + ), + ftm.TextSourceAttribution( + text="Flet", + on_click=lambda e: e.page.launch_url( + "https://flet.dev" + ), + ), + ] + ), + ftm.SimpleAttribution( + text="Flet", + alignment=ft.Alignment.TOP_RIGHT, + on_click=lambda e: print("Clicked SimpleAttribution"), + ), + marker_layer := ftm.MarkerLayer( + markers=[ + ftm.Marker( + content=ft.Icon(ft.Icons.LOCATION_ON), + coordinates=ftm.MapLatitudeLongitude(30, 15), + ), + ftm.Marker( + content=ft.Icon(ft.Icons.LOCATION_ON), + coordinates=ftm.MapLatitudeLongitude(10, 10), + ), + ftm.Marker( + content=ft.Icon(ft.Icons.LOCATION_ON), + coordinates=ftm.MapLatitudeLongitude(25, 45), + ), + ], + ), + circle_layer := ftm.CircleLayer( + circles=[ + ftm.CircleMarker( + radius=10, + coordinates=ftm.MapLatitudeLongitude(16, 24), + color=ft.Colors.RED, + border_color=ft.Colors.BLUE, + border_stroke_width=4, + ), + ], + ), + ftm.PolygonLayer( + polygons=[ + ftm.PolygonMarker( + label="Popular Touristic Area", + label_text_style=ft.TextStyle( + color=ft.Colors.BLACK, + size=15, + weight=ft.FontWeight.BOLD, + ), + color=ft.Colors.with_opacity( + 0.3, ft.Colors.BLUE + ), + coordinates=[ + ftm.MapLatitudeLongitude(10, 10), + ftm.MapLatitudeLongitude(30, 15), + ftm.MapLatitudeLongitude(25, 45), + ], + ), + ], + ), + ftm.PolylineLayer( + polylines=[ + ftm.PolylineMarker( + border_stroke_width=3, + border_color=ft.Colors.RED, + gradient_colors=[ + ft.Colors.BLACK, + ft.Colors.BLACK, + ], + color=ft.Colors.with_opacity( + 0.6, ft.Colors.GREEN + ), + coordinates=[ + ftm.MapLatitudeLongitude(10, 10), + ftm.MapLatitudeLongitude(30, 15), + ftm.MapLatitudeLongitude(25, 45), + ], + ), + ], + ), + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/map/multi_layers/pyproject.toml b/sdk/python/examples/controls/map/multi_layers/pyproject.toml new file mode 100644 index 0000000000..64ec87c460 --- /dev/null +++ b/sdk/python/examples/controls/map/multi_layers/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "map-multi-layers" +version = "1.0.0" +description = "Combines tile, marker, circle, polygon, and polyline layers with tap-to-add overlays." +requires-python = ">=3.10" +keywords = ["map", "layers", "markers", "polygons", "polylines"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-map"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Extensions/Map"] + +[tool.flet.metadata] +title = "Multiple Layers" +controls = ["SafeArea", "Column", "Text", "Map", "TileLayer", "RichAttribution", "SimpleAttribution", "TextSourceAttribution", "MarkerLayer", "Marker", "CircleLayer", "CircleMarker", "PolygonLayer", "PolygonMarker", "PolylineLayer", "PolylineMarker", "Icon", "AppBar"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["multi-layer map composition", "tap and secondary-tap overlay creation", "interactive attributions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/markdown/basic.py b/sdk/python/examples/controls/markdown/basic.py deleted file mode 100644 index 7788678401..0000000000 --- a/sdk/python/examples/controls/markdown/basic.py +++ /dev/null @@ -1,95 +0,0 @@ -import flet as ft - -sample = """ -# Markdown Example -Markdown allows you to easily include formatted text, images, and even formatted Dart -code in your app. - -## Titles - -Setext-style - -This is an H1 -============= - -This is an H2 -------------- - -Atx-style - -# This is an H1 - -## This is an H2 - -###### This is an H6 - -Select the valid headers: - -- [x] `# hello` -- [ ] `#hello` - -## Links - -[inline-style](https://www.google.com) - -## Images - -![Image from Flet assets](/icons/icon-192.png) - -![Test image](https://picsum.photos/200/300) - -## Tables - -|Syntax |Result | -|---------------------------------------|-------------------------------------| -|`*italic 1*` |*italic 1* | -|`_italic 2_` | _italic 2_ | -|`**bold 1**` |**bold 1** | -|`__bold 2__` |__bold 2__ | -|`This is a ~~strikethrough~~` |This is a ~~strikethrough~~ | -|`***italic bold 1***` |***italic bold 1*** | -|`___italic bold 2___` |___italic bold 2___ | -|`***~~italic bold strikethrough 1~~***`|***~~italic bold strikethrough 1~~***| -|`~~***italic bold strikethrough 2***~~`|~~***italic bold strikethrough 2***~~| - -## Styling - -Style text as _italic_, __bold__, ~~strikethrough~~, or `inline code`. - -- Use bulleted lists -- To better clarify -- Your points - -## Code blocks - -Formatted Dart code looks really pretty too: - -~~~dart -void main() { - runApp(MaterialApp( - home: Scaffold( - body: ft.Markdown(data: markdownData), - ), - )); -} -~~~ -""" - - -def main(page: ft.Page): - page.scroll = ft.ScrollMode.AUTO - - async def handle_link_tap(e: ft.Event[ft.Markdown]): - await page.launch_url(e.data) - - page.add( - ft.Markdown( - value=sample, - selectable=True, - extension_set=ft.MarkdownExtensionSet.GITHUB_WEB, - on_tap_link=handle_link_tap, - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/markdown/basic/main.py b/sdk/python/examples/controls/markdown/basic/main.py new file mode 100644 index 0000000000..075fc15c35 --- /dev/null +++ b/sdk/python/examples/controls/markdown/basic/main.py @@ -0,0 +1,98 @@ +import flet as ft + +sample = """ +# Markdown Example +Markdown allows you to easily include formatted text, images, and even formatted Dart +code in your app. + +## Titles + +Setext-style + +This is an H1 +============= + +This is an H2 +------------- + +Atx-style + +# This is an H1 + +## This is an H2 + +###### This is an H6 + +Select the valid headers: + +- [x] `# hello` +- [ ] `#hello` + +## Links + +[inline-style](https://www.google.com) + +## Images + +![Image from Flet assets](/icons/icon-192.png) + +![Test image](https://picsum.photos/200/300) + +## Tables + +|Syntax |Result | +|---------------------------------------|-------------------------------------| +|`*italic 1*` |*italic 1* | +|`_italic 2_` | _italic 2_ | +|`**bold 1**` |**bold 1** | +|`__bold 2__` |__bold 2__ | +|`This is a ~~strikethrough~~` |This is a ~~strikethrough~~ | +|`***italic bold 1***` |***italic bold 1*** | +|`___italic bold 2___` |___italic bold 2___ | +|`***~~italic bold strikethrough 1~~***`|***~~italic bold strikethrough 1~~***| +|`~~***italic bold strikethrough 2***~~`|~~***italic bold strikethrough 2***~~| + +## Styling + +Style text as _italic_, __bold__, ~~strikethrough~~, or `inline code`. + +- Use bulleted lists +- To better clarify +- Your points + +## Code blocks + +Formatted Dart code looks really pretty too: + +~~~dart +void main() { + runApp(MaterialApp( + home: Scaffold( + body: ft.Markdown(data: markdownData), + ), + )); +} +~~~ +""" + + +def main(page: ft.Page): + page.scroll = ft.ScrollMode.AUTO + + async def handle_link_tap(e: ft.Event[ft.Markdown]): + await page.launch_url(e.data) + + page.add( + ft.SafeArea( + content=ft.Markdown( + value=sample, + selectable=True, + extension_set=ft.MarkdownExtensionSet.GITHUB_WEB, + on_tap_link=handle_link_tap, + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/markdown/basic/pyproject.toml b/sdk/python/examples/controls/markdown/basic/pyproject.toml new file mode 100644 index 0000000000..784c5c4417 --- /dev/null +++ b/sdk/python/examples/controls/markdown/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "markdown-basic" +version = "1.0.0" +description = "Renders a long Markdown document with links, images, tables, and code blocks." +requires-python = ">=3.10" +keywords = ["markdown", "rich text", "links", "tables", "code blocks"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Markdown"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Markdown"] +layout_pattern = "article" +complexity = "basic" +features = ["github markdown rendering", "link tap callback", "scrollable markdown"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/markdown/code_syntax_highlight.py b/sdk/python/examples/controls/markdown/code_syntax_highlight.py deleted file mode 100644 index 72838ac1d3..0000000000 --- a/sdk/python/examples/controls/markdown/code_syntax_highlight.py +++ /dev/null @@ -1,178 +0,0 @@ -import flet as ft - -sample = """ -# Flet - - - -Flet is a framework for adding server-driven UI (SDUI) experiences to existing Flutter -apps or building standalone web, mobile and desktop apps with Flutter UI. - -Add an interactive `FletApp` widget to your Flutter app whose content is controlled by a remote Python script. -It is an ideal solution for building non-core or frequently changing functionality such as product catalog, feedback form, in-app survey or support chat. Flet enables your team to ship new features faster by reducing the number of App Store validation cycles. Just re-deploy a web app hosting a Python script and your users will get an instant update! - -On the server side Flet provides an easy to learn programming model that enables Python developers without prior Flutter (or even front-end) experience to participate in development of your larger Flutter app or build their own apps with Flutter UI from scratch. - -## Getting started with Flet - -### Install `flet` Python module - -Flet requires Python 3.7 or above. To start with Flet, you need to install flet module first: - -~~~bash -pip install flet -~~~ - -### Create Python program - -Create a new Python program using Flet which will be driving the content of `FletApp` widget. - -Let's do a simple `counter.py` app similar to a Flutter new project template: - -~~~python -import flet -from flet import IconButton, Page, Row, TextField, icons - -def main(page: Page): - page.title = "Flet counter example" - page.vertical_alignment = "center" - - txt_number = TextField(value="0", text_align="right", width=100) - - def minus_click(e): - txt_number.value = int(txt_number.value) - 1 - page.update() - - def plus_click(e): - txt_number.value = int(txt_number.value) + 1 - page.update() - - page.add( - Row( - [ - IconButton(icons.REMOVE, on_click=minus_click), - txt_number, - IconButton(icons.ADD, on_click=plus_click), - ], - alignment="center", - ) - ) - -flet.app(main, port=8550) -~~~ - -Run the app: - -~~~bash -python counter.py -~~~ - -You should see the app running in a native OS window. - -There is a web server (Fletd) running in the background on a fixed port `8550`. Fletd web server is a "bridge" between Python and Flutter. - -`FletApp` widget in your Flutter application will be communicating with Fletd web server via WebSockets to receive UI updates and send user-generated UI events. - -For production use Python app along with Fletd could be [deployed to a public web host](https://docs.flet.dev/publish/web/dynamic-website/) and be accessible via HTTPS with domain name. - -### Add Flet widget to a Flutter app - -Create a new or open existing Flutter project. - -Install Flutter `flet` package: - -~~~bash -flutter pub add flet -~~~ - -For a new project replace `main.dart` with the following: - -~~~dart -import 'package:flet/flet.dart'; -import 'package:flutter/material.dart'; - -void main() async { - await setupDesktop(); - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return const MaterialApp( - title: 'Flet Flutter Demo', - home: FletApp(pageUrl: "http://localhost:8550"), - ); - } -} -~~~ - -In the app above `FletApp` widget is hosted inside `MaterialApp` widget. - -If Flet app must be able to handle page route change events (web browser URL changes, mobile app deep linking) it must be the top most widget as it contains its own `MaterialApp` widget handling route changes: - -~~~dart -import 'package:flet/flet.dart'; -import 'package:flutter/material.dart'; - -void main() async { - await setupDesktop(); - runApp(const FletApp(pageUrl: "http://localhost:8550")); -} -~~~ - -Run the program and see Flet app running inside a Flutter app. - -When adding `FletApp` widget to the existing desktop Flutter app make sure `setupDesktop()` is called before `runApp()` to initialize Flet's built-in window manager. - -## Flet learning resources - -* [Getting started for Python](https://docs.flet.dev/getting-started/installation/) -* [Controls reference](https://docs.flet.dev/controls) -* [Tutorials](https://docs.flet.dev/tutorials) -* [Examples](https://github.com/flet-dev/examples/tree/main/python) - -## Flet community - -* [Discussions](https://github.com/flet-dev/flet/discussions) -* [Discord](https://discord.gg/dzWXP8SHG8) -* [Twitter](https://twitter.com/fletdev) -* [Email](mailto:hello@flet.dev) - -## FAQ - -Coming soon. - -## Adding custom Flutter widgets - -Coming soon. -""" # noqa: E501 - - -def main(page: ft.Page): - page.scroll = ft.ScrollMode.AUTO - - page.fonts = { - "Roboto Mono": "RobotoMono-VariableFont_wght.ttf", - } - - async def navigate_md_link(e: ft.Event[ft.Markdown]): - await page.launch_url(e.data) - - page.add( - ft.Markdown( - value=sample, - selectable=True, - extension_set=ft.MarkdownExtensionSet.GITHUB_WEB, - code_theme=ft.MarkdownCodeTheme.ATOM_ONE_DARK, - code_style_sheet=ft.MarkdownStyleSheet( - code_text_style=ft.TextStyle(font_family="Roboto Mono") - ), - on_tap_link=navigate_md_link, - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/markdown/code_syntax_highlight/main.py b/sdk/python/examples/controls/markdown/code_syntax_highlight/main.py new file mode 100644 index 0000000000..fd116fb1d4 --- /dev/null +++ b/sdk/python/examples/controls/markdown/code_syntax_highlight/main.py @@ -0,0 +1,181 @@ +import flet as ft + +sample = """ +# Flet + + + +Flet is a framework for adding server-driven UI (SDUI) experiences to existing Flutter +apps or building standalone web, mobile and desktop apps with Flutter UI. + +Add an interactive `FletApp` widget to your Flutter app whose content is controlled by a remote Python script. +It is an ideal solution for building non-core or frequently changing functionality such as product catalog, feedback form, in-app survey or support chat. Flet enables your team to ship new features faster by reducing the number of App Store validation cycles. Just re-deploy a web app hosting a Python script and your users will get an instant update! + +On the server side Flet provides an easy to learn programming model that enables Python developers without prior Flutter (or even front-end) experience to participate in development of your larger Flutter app or build their own apps with Flutter UI from scratch. + +## Getting started with Flet + +### Install `flet` Python module + +Flet requires Python 3.7 or above. To start with Flet, you need to install flet module first: + +~~~bash +pip install flet +~~~ + +### Create Python program + +Create a new Python program using Flet which will be driving the content of `FletApp` widget. + +Let's do a simple `counter.py` app similar to a Flutter new project template: + +~~~python +import flet +from flet import IconButton, Page, Row, TextField, icons + +def main(page: Page): + page.title = "Flet counter example" + page.vertical_alignment = "center" + + txt_number = TextField(value="0", text_align="right", width=100) + + def minus_click(e): + txt_number.value = int(txt_number.value) - 1 + page.update() + + def plus_click(e): + txt_number.value = int(txt_number.value) + 1 + page.update() + + page.add( + Row( + [ + IconButton(icons.REMOVE, on_click=minus_click), + txt_number, + IconButton(icons.ADD, on_click=plus_click), + ], + alignment="center", + ) + ) + +flet.app(main, port=8550) +~~~ + +Run the app: + +~~~bash +python counter.py +~~~ + +You should see the app running in a native OS window. + +There is a web server (Fletd) running in the background on a fixed port `8550`. Fletd web server is a "bridge" between Python and Flutter. + +`FletApp` widget in your Flutter application will be communicating with Fletd web server via WebSockets to receive UI updates and send user-generated UI events. + +For production use Python app along with Fletd could be [deployed to a public web host](https://docs.flet.dev/publish/web/dynamic-website/) and be accessible via HTTPS with domain name. + +### Add Flet widget to a Flutter app + +Create a new or open existing Flutter project. + +Install Flutter `flet` package: + +~~~bash +flutter pub add flet +~~~ + +For a new project replace `main.dart` with the following: + +~~~dart +import 'package:flet/flet.dart'; +import 'package:flutter/material.dart'; + +void main() async { + await setupDesktop(); + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'Flet Flutter Demo', + home: FletApp(pageUrl: "http://localhost:8550"), + ); + } +} +~~~ + +In the app above `FletApp` widget is hosted inside `MaterialApp` widget. + +If Flet app must be able to handle page route change events (web browser URL changes, mobile app deep linking) it must be the top most widget as it contains its own `MaterialApp` widget handling route changes: + +~~~dart +import 'package:flet/flet.dart'; +import 'package:flutter/material.dart'; + +void main() async { + await setupDesktop(); + runApp(const FletApp(pageUrl: "http://localhost:8550")); +} +~~~ + +Run the program and see Flet app running inside a Flutter app. + +When adding `FletApp` widget to the existing desktop Flutter app make sure `setupDesktop()` is called before `runApp()` to initialize Flet's built-in window manager. + +## Flet learning resources + +* [Getting started for Python](https://docs.flet.dev/getting-started/installation/) +* [Controls reference](https://docs.flet.dev/controls) +* [Tutorials](https://docs.flet.dev/tutorials) +* [Examples](https://github.com/flet-dev/examples/tree/main/python) + +## Flet community + +* [Discussions](https://github.com/flet-dev/flet/discussions) +* [Discord](https://discord.gg/dzWXP8SHG8) +* [Twitter](https://twitter.com/fletdev) +* [Email](mailto:hello@flet.dev) + +## FAQ + +Coming soon. + +## Adding custom Flutter widgets + +Coming soon. +""" # noqa: E501 + + +def main(page: ft.Page): + page.scroll = ft.ScrollMode.AUTO + + page.fonts = { + "Roboto Mono": "RobotoMono-VariableFont_wght.ttf", + } + + async def navigate_md_link(e: ft.Event[ft.Markdown]): + await page.launch_url(e.data) + + page.add( + ft.SafeArea( + content=ft.Markdown( + value=sample, + selectable=True, + extension_set=ft.MarkdownExtensionSet.GITHUB_WEB, + code_theme=ft.MarkdownCodeTheme.ATOM_ONE_DARK, + code_style_sheet=ft.MarkdownStyleSheet( + code_text_style=ft.TextStyle(font_family="Roboto Mono") + ), + on_tap_link=navigate_md_link, + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/markdown/code_syntax_highlight/pyproject.toml b/sdk/python/examples/controls/markdown/code_syntax_highlight/pyproject.toml new file mode 100644 index 0000000000..8acfd0fa29 --- /dev/null +++ b/sdk/python/examples/controls/markdown/code_syntax_highlight/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "markdown-code-syntax-highlight" +version = "1.0.0" +description = "Displays Markdown with syntax-highlighted code blocks and custom monospace font style." +requires-python = ">=3.10" +keywords = ["markdown", "syntax highlight", "code theme", "fonts"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Markdown"] + +[tool.flet.metadata] +title = "Code syntax highlight" +controls = ["SafeArea", "Markdown", "MarkdownStyleSheet", "TextStyle"] +layout_pattern = "article" +complexity = "basic" +features = ["code syntax highlighting", "custom code font", "link tap callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/markdown/custom_text_theme.py b/sdk/python/examples/controls/markdown/custom_text_theme.py deleted file mode 100644 index 51749a9e31..0000000000 --- a/sdk/python/examples/controls/markdown/custom_text_theme.py +++ /dev/null @@ -1,44 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.DARK - - def change_theme_mode(e: ft.Event[ft.Switch]): - if page.theme_mode == ft.ThemeMode.DARK: - page.theme_mode = ft.ThemeMode.LIGHT - switch.thumb_icon = ft.Icons.LIGHT_MODE - else: - switch.thumb_icon = ft.Icons.DARK_MODE - page.theme_mode = ft.ThemeMode.DARK - page.update() - - switch = ft.Switch(thumb_icon=ft.Icons.DARK_MODE, on_change=change_theme_mode) - - page.add( - ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - controls=[ - ft.Container( - content=ft.Markdown("I can read this!"), - bgcolor="#550000", - padding=20, - theme=ft.Theme( - text_theme=ft.TextTheme( - body_medium=ft.TextStyle(color=ft.Colors.WHITE), - body_large=ft.TextStyle(color=ft.Colors.WHITE), - body_small=ft.TextStyle(color=ft.Colors.WHITE), - ) - ), - ), - ft.Container( - content=switch, - padding=ft.Padding.only(bottom=50), - alignment=ft.Alignment.TOP_RIGHT, - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/markdown/custom_text_theme/main.py b/sdk/python/examples/controls/markdown/custom_text_theme/main.py new file mode 100644 index 0000000000..132a52d039 --- /dev/null +++ b/sdk/python/examples/controls/markdown/custom_text_theme/main.py @@ -0,0 +1,47 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.DARK + + def change_theme_mode(e: ft.Event[ft.Switch]): + if page.theme_mode == ft.ThemeMode.DARK: + page.theme_mode = ft.ThemeMode.LIGHT + switch.thumb_icon = ft.Icons.LIGHT_MODE + else: + switch.thumb_icon = ft.Icons.DARK_MODE + page.theme_mode = ft.ThemeMode.DARK + page.update() + + switch = ft.Switch(thumb_icon=ft.Icons.DARK_MODE, on_change=change_theme_mode) + + page.add( + ft.SafeArea( + content=ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + controls=[ + ft.Container( + bgcolor="#550000", + padding=20, + theme=ft.Theme( + text_theme=ft.TextTheme( + body_medium=ft.TextStyle(color=ft.Colors.WHITE), + body_large=ft.TextStyle(color=ft.Colors.WHITE), + body_small=ft.TextStyle(color=ft.Colors.WHITE), + ) + ), + content=ft.Markdown("I can read this!"), + ), + ft.Container( + padding=ft.Padding.only(bottom=50), + alignment=ft.Alignment.TOP_RIGHT, + content=switch, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/markdown/custom_text_theme/pyproject.toml b/sdk/python/examples/controls/markdown/custom_text_theme/pyproject.toml new file mode 100644 index 0000000000..ed894ad15e --- /dev/null +++ b/sdk/python/examples/controls/markdown/custom_text_theme/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "markdown-custom-text-theme" +version = "1.0.0" +description = "Applies a custom text theme to Markdown and toggles light/dark theme mode." +requires-python = ">=3.10" +keywords = ["markdown", "theme", "text theme", "dark mode", "switch"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Markdown"] + +[tool.flet.metadata] +title = "Custom text theme" +controls = ["SafeArea", "Row", "Container", "Markdown", "Switch", "Theme", "TextTheme"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["custom markdown text colors", "theme mode toggle"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/markdown/listviews.py b/sdk/python/examples/controls/markdown/listviews.py deleted file mode 100644 index 9faf353dd6..0000000000 --- a/sdk/python/examples/controls/markdown/listviews.py +++ /dev/null @@ -1,151 +0,0 @@ -import flet as ft - -sample = """ -# Markdown Example -Markdown allows you to easily include formatted text, images, and even formatted Dart -code in your app. - -## Titles - -Setext-style - -``` -This is an H1 -============= - -This is an H2 -------------- -``` - -Atx-style - -``` -# This is an H1 - -## This is an H2 - -###### This is an H6 -``` - -Select the valid headers: - -- [x] `# hello` -- [ ] `#hello` - -## Links - -[Google's Homepage][Google] - -``` -[inline-style](https://www.google.com) - -[reference-style][Google] -``` - -## Images - -![Flutter logo](/icons/icon-192.png) - -![Test image](https://picsum.photos/200/300) - -## Tables - -|Syntax |Result | -|---------------------------------------|-------------------------------------| -|`*italic 1*` |*italic 1* | -|`_italic 2_` | _italic 2_ | -|`**bold 1**` |**bold 1** | -|`__bold 2__` |__bold 2__ | -|`This is a ~~strikethrough~~` |This is a ~~strikethrough~~ | -|`***italic bold 1***` |***italic bold 1*** | -|`___italic bold 2___` |___italic bold 2___ | -|`***~~italic bold strikethrough 1~~***`|***~~italic bold strikethrough 1~~***| -|`~~***italic bold strikethrough 2***~~`|~~***italic bold strikethrough 2***~~| - -## Styling -Style text as _italic_, __bold__, ~~strikethrough~~, or `inline code`. - -- Use bulleted lists -- To better clarify -- Your points - -## Code blocks -Formatted Dart code looks really pretty too: - -``` -void main() { - runApp(MaterialApp( - home: Scaffold( - body: Markdown(data: markdownData), - ), - )); -} -``` - -## Center Title - -###### ※ ※ ※ - -_* How to implement it see main.dart#L129 in example._ - -## Custom Syntax - -NaOH + Al_2O_3 = NaAlO_2 + H_2O - -C_4H_10 = C_2H_6 + C_2H_4 - -## Markdown widget - -This is an example of how to create your own Markdown widget: - - Markdown(data: 'Hello _world_!'); - -Enjoy! - -[Google]: https://www.google.com/ - -## Line Breaks - -This is an example of how to create line breaks (tab or two whitespaces): - -line 1 - - -line 2 - - - -line 3 -""" - - -def main(page: ft.Page): - async def navigate_md_link(e: ft.Event[ft.Markdown]): - await page.launch_url(e.data) - - page.add( - ft.ListView( - expand=True, - controls=[ - ft.Markdown( - value=sample, - on_tap_link=navigate_md_link, - ) - ], - ), - ft.Divider(), - ft.ListView( - expand=True, - controls=[ - ft.Markdown( - value=sample, - selectable=True, - extension_set=ft.MarkdownExtensionSet.GITHUB_WEB, - on_tap_link=navigate_md_link, - ) - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/markdown/listviews/main.py b/sdk/python/examples/controls/markdown/listviews/main.py new file mode 100644 index 0000000000..119a5905b4 --- /dev/null +++ b/sdk/python/examples/controls/markdown/listviews/main.py @@ -0,0 +1,160 @@ +import flet as ft + +sample = """ +# Markdown Example +Markdown allows you to easily include formatted text, images, and even formatted Dart +code in your app. + +## Titles + +Setext-style + +``` +This is an H1 +============= + +This is an H2 +------------- +``` + +Atx-style + +``` +# This is an H1 + +## This is an H2 + +###### This is an H6 +``` + +Select the valid headers: + +- [x] `# hello` +- [ ] `#hello` + +## Links + +[Google's Homepage][Google] + +``` +[inline-style](https://www.google.com) + +[reference-style][Google] +``` + +## Images + +![Flutter logo](/icons/icon-192.png) + +![Test image](https://picsum.photos/200/300) + +## Tables + +|Syntax |Result | +|---------------------------------------|-------------------------------------| +|`*italic 1*` |*italic 1* | +|`_italic 2_` | _italic 2_ | +|`**bold 1**` |**bold 1** | +|`__bold 2__` |__bold 2__ | +|`This is a ~~strikethrough~~` |This is a ~~strikethrough~~ | +|`***italic bold 1***` |***italic bold 1*** | +|`___italic bold 2___` |___italic bold 2___ | +|`***~~italic bold strikethrough 1~~***`|***~~italic bold strikethrough 1~~***| +|`~~***italic bold strikethrough 2***~~`|~~***italic bold strikethrough 2***~~| + +## Styling +Style text as _italic_, __bold__, ~~strikethrough~~, or `inline code`. + +- Use bulleted lists +- To better clarify +- Your points + +## Code blocks +Formatted Dart code looks really pretty too: + +``` +void main() { + runApp(MaterialApp( + home: Scaffold( + body: Markdown(data: markdownData), + ), + )); +} +``` + +## Center Title + +###### ※ ※ ※ + +_* How to implement it see main.dart#L129 in example._ + +## Custom Syntax + +NaOH + Al_2O_3 = NaAlO_2 + H_2O + +C_4H_10 = C_2H_6 + C_2H_4 + +## Markdown widget + +This is an example of how to create your own Markdown widget: + + Markdown(data: 'Hello _world_!'); + +Enjoy! + +[Google]: https://www.google.com/ + +## Line Breaks + +This is an example of how to create line breaks (tab or two whitespaces): + +line 1 + + +line 2 + + + +line 3 +""" + + +def main(page: ft.Page): + async def navigate_md_link(e: ft.Event[ft.Markdown]): + await page.launch_url(e.data) + + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.ListView( + expand=True, + controls=[ + ft.Markdown( + value=sample, + on_tap_link=navigate_md_link, + ) + ], + ), + ft.Divider(), + ft.ListView( + expand=True, + controls=[ + ft.Markdown( + value=sample, + selectable=True, + extension_set=ft.MarkdownExtensionSet.GITHUB_WEB, + on_tap_link=navigate_md_link, + ) + ], + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/markdown/listviews/pyproject.toml b/sdk/python/examples/controls/markdown/listviews/pyproject.toml new file mode 100644 index 0000000000..3425a3daa6 --- /dev/null +++ b/sdk/python/examples/controls/markdown/listviews/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "markdown-listviews" +version = "1.0.0" +description = "Compares Markdown rendering in two ListViews, including selectable GitHub-style mode." +requires-python = ">=3.10" +keywords = ["markdown", "listview", "selectable", "comparison"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/Markdown"] + +[tool.flet.metadata] +title = "ListViews" +controls = ["SafeArea", "Column", "ListView", "Divider", "Markdown"] +layout_pattern = "comparison" +complexity = "basic" +features = ["dual markdown views", "selectable markdown", "github extension set"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/menu_bar/__init__.py b/sdk/python/examples/controls/menu_bar/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/menu_bar/nested_submenus.py b/sdk/python/examples/controls/menu_bar/nested_submenus.py deleted file mode 100644 index c52c0ebc1b..0000000000 --- a/sdk/python/examples/controls/menu_bar/nested_submenus.py +++ /dev/null @@ -1,123 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - appbar_text_ref = ft.Ref[ft.Text]() - - def handle_menu_item_click(e: ft.Event[ft.MenuItemButton]): - text = e.control.content.value - page.show_dialog(ft.SnackBar(ft.Text(f"{text} was clicked!"))) - appbar_text_ref.current.value = text - page.update() - - def handle_submenu_open(e: ft.Event[ft.SubmenuButton]): - print(f"{e.control.content.value}.on_open") - - def handle_submenu_close(e: ft.Event[ft.SubmenuButton]): - print(f"{e.control.content.value}.on_close") - - def handle_submenu_hover(e: ft.Event[ft.SubmenuButton]): - print(f"{e.control.content.value}.on_hover") - - page.appbar = ft.AppBar( - title=ft.Text("Menus", ref=appbar_text_ref), - center_title=True, - bgcolor=ft.Colors.BLUE, - ) - - page.add( - ft.Row( - controls=[ - ft.MenuBar( - expand=True, - style=ft.MenuStyle( - alignment=ft.Alignment.TOP_LEFT, - bgcolor=ft.Colors.RED_300, - mouse_cursor={ - ft.ControlState.HOVERED: ft.MouseCursor.WAIT, - ft.ControlState.DEFAULT: ft.MouseCursor.ZOOM_OUT, - }, - ), - controls=[ - ft.SubmenuButton( - content=ft.Text("File"), - on_open=handle_submenu_open, - on_close=handle_submenu_close, - on_hover=handle_submenu_hover, - controls=[ - ft.MenuItemButton( - content=ft.Text("About"), - leading=ft.Icon(ft.Icons.INFO), - on_click=handle_menu_item_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.GREEN_100 - } - ), - ), - ft.MenuItemButton( - content=ft.Text("Save"), - leading=ft.Icon(ft.Icons.SAVE), - on_click=handle_menu_item_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.GREEN_100 - } - ), - ), - ft.MenuItemButton( - content=ft.Text("Quit"), - leading=ft.Icon(ft.Icons.CLOSE), - on_click=handle_menu_item_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.GREEN_100 - } - ), - ), - ], - ), - ft.SubmenuButton( - content=ft.Text("View"), - on_open=handle_submenu_open, - on_close=handle_submenu_close, - on_hover=handle_submenu_hover, - controls=[ - ft.SubmenuButton( - content=ft.Text("Zoom"), - controls=[ - ft.MenuItemButton( - content=ft.Text("Magnify"), - leading=ft.Icon(ft.Icons.ZOOM_IN), - close_on_click=False, - on_click=handle_menu_item_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.PURPLE_200 - } - ), - ), - ft.MenuItemButton( - content=ft.Text("Minify"), - leading=ft.Icon(ft.Icons.ZOOM_OUT), - close_on_click=False, - on_click=handle_menu_item_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.PURPLE_200 - } - ), - ), - ], - ) - ], - ), - ], - ) - ] - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/menu_bar/nested_submenus/main.py b/sdk/python/examples/controls/menu_bar/nested_submenus/main.py new file mode 100644 index 0000000000..fcdc64ff0f --- /dev/null +++ b/sdk/python/examples/controls/menu_bar/nested_submenus/main.py @@ -0,0 +1,135 @@ +import flet as ft + + +def main(page: ft.Page): + appbar_text_ref = ft.Ref[ft.Text]() + + def handle_menu_item_click(e: ft.Event[ft.MenuItemButton]): + text = e.control.content.value + page.show_dialog(ft.SnackBar(ft.Text(f"{text} was clicked!"))) + appbar_text_ref.current.value = text + appbar_text_ref.current.update() + + def handle_submenu_open(e: ft.Event[ft.SubmenuButton]): + print(f"{e.control.content.value}.on_open") + + def handle_submenu_close(e: ft.Event[ft.SubmenuButton]): + print(f"{e.control.content.value}.on_close") + + def handle_submenu_hover(e: ft.Event[ft.SubmenuButton]): + print(f"{e.control.content.value}.on_hover") + + page.appbar = ft.AppBar( + title=ft.Text("Menus", ref=appbar_text_ref), + center_title=True, + bgcolor=ft.Colors.BLUE, + ) + + page.add( + ft.SafeArea( + content=ft.Row( + controls=[ + ft.MenuBar( + expand=True, + style=ft.MenuStyle( + alignment=ft.Alignment.TOP_LEFT, + bgcolor=ft.Colors.RED_300, + mouse_cursor={ + ft.ControlState.HOVERED: ft.MouseCursor.WAIT, + ft.ControlState.DEFAULT: ft.MouseCursor.ZOOM_OUT, + }, + ), + controls=[ + ft.SubmenuButton( + content=ft.Text("File"), + on_open=handle_submenu_open, + on_close=handle_submenu_close, + on_hover=handle_submenu_hover, + controls=[ + ft.MenuItemButton( + content=ft.Text("About"), + leading=ft.Icon(ft.Icons.INFO), + on_click=handle_menu_item_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ( + ft.Colors.GREEN_100 + ) + } + ), + ), + ft.MenuItemButton( + content=ft.Text("Save"), + leading=ft.Icon(ft.Icons.SAVE), + on_click=handle_menu_item_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ( + ft.Colors.GREEN_100 + ) + } + ), + ), + ft.MenuItemButton( + content=ft.Text("Quit"), + leading=ft.Icon(ft.Icons.CLOSE), + on_click=handle_menu_item_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ( + ft.Colors.GREEN_100 + ) + } + ), + ), + ], + ), + ft.SubmenuButton( + content=ft.Text("View"), + on_open=handle_submenu_open, + on_close=handle_submenu_close, + on_hover=handle_submenu_hover, + controls=[ + ft.SubmenuButton( + content=ft.Text("Zoom"), + controls=[ + ft.MenuItemButton( + content=ft.Text("Magnify"), + leading=ft.Icon(ft.Icons.ZOOM_IN), + close_on_click=False, + on_click=handle_menu_item_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ( + ft.Colors.PURPLE_200 + ) + } + ), + ), + ft.MenuItemButton( + content=ft.Text("Minify"), + leading=ft.Icon(ft.Icons.ZOOM_OUT), + close_on_click=False, + on_click=handle_menu_item_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ( + ft.Colors.PURPLE_200 + ) + } + ), + ), + ], + ) + ], + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/menu_bar/nested_submenus/pyproject.toml b/sdk/python/examples/controls/menu_bar/nested_submenus/pyproject.toml new file mode 100644 index 0000000000..36115e22b6 --- /dev/null +++ b/sdk/python/examples/controls/menu_bar/nested_submenus/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "menu-bar-nested-submenus" +version = "1.0.0" +description = "Builds a MenuBar with nested submenus, hover callbacks, and click feedback." +requires-python = ">=3.10" +keywords = ["menu bar", "submenu", "nested menu", "events", "navigation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/MenuBar"] + +[tool.flet.metadata] +title = "MenuBar with nested submenus" +controls = ["SafeArea", "Row", "MenuBar", "SubmenuButton", "MenuItemButton", "AppBar", "SnackBar", "Text", "Icon"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["nested submenus", "menu open and close callbacks", "menu item click feedback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/menu_item_button/__init__.py b/sdk/python/examples/controls/menu_item_button/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/menu_item_button/basic.py b/sdk/python/examples/controls/menu_item_button/basic.py deleted file mode 100644 index 4b15491094..0000000000 --- a/sdk/python/examples/controls/menu_item_button/basic.py +++ /dev/null @@ -1,71 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.padding = 0 - page.spacing = 0 - page.theme_mode = ft.ThemeMode.LIGHT - - def handle_color_click(e: ft.Event[ft.MenuItemButton]): - color = e.control.content.value - background_container.content.value = f"{color} background color" - background_container.bgcolor = color.lower() - page.update() - - def handle_on_hover(e: ft.Event[ft.MenuItemButton]): - print(e) - - menubar = ft.MenuBar( - expand=True, - controls=[ - ft.SubmenuButton( - content=ft.Text("BgColors"), - controls=[ - ft.MenuItemButton( - content=ft.Text("Blue"), - leading=ft.Icon(ft.Icons.COLORIZE), - style=ft.ButtonStyle( - bgcolor={ft.ControlState.HOVERED: ft.Colors.BLUE} - ), - on_click=handle_color_click, - on_hover=handle_on_hover, - ), - ft.MenuItemButton( - content=ft.Text("Green"), - leading=ft.Icon(ft.Icons.COLORIZE), - style=ft.ButtonStyle( - bgcolor={ft.ControlState.HOVERED: ft.Colors.GREEN} - ), - on_click=handle_color_click, - on_hover=handle_on_hover, - ), - ft.MenuItemButton( - content=ft.Text("Red"), - leading=ft.Icon(ft.Icons.COLORIZE), - style=ft.ButtonStyle( - bgcolor={ft.ControlState.HOVERED: ft.Colors.RED} - ), - on_click=handle_color_click, - on_hover=handle_on_hover, - ), - ], - ), - ], - ) - - page.add( - ft.Row(controls=[menubar]), - background_container := ft.Container( - expand=True, - bgcolor=ft.Colors.WHITE, - alignment=ft.Alignment.CENTER, - content=ft.Text( - value="Choose a bgcolor from the menu", - style=ft.TextStyle(weight=ft.FontWeight.W_500), - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/menu_item_button/basic/main.py b/sdk/python/examples/controls/menu_item_button/basic/main.py new file mode 100644 index 0000000000..97bf6dc256 --- /dev/null +++ b/sdk/python/examples/controls/menu_item_button/basic/main.py @@ -0,0 +1,83 @@ +import flet as ft + + +def main(page: ft.Page): + page.padding = 0 + page.spacing = 0 + page.theme_mode = ft.ThemeMode.LIGHT + + def handle_color_click(e: ft.Event[ft.MenuItemButton]): + color = e.control.content.value + background_container.content.value = f"{color} background color" + background_container.bgcolor = color.lower() + + def handle_on_hover(e: ft.Event[ft.MenuItemButton]): + print(e) + + menubar = ft.MenuBar( + expand=True, + controls=[ + ft.SubmenuButton( + content=ft.Text("BgColors"), + controls=[ + ft.MenuItemButton( + content=ft.Text("Blue"), + leading=ft.Icon(ft.Icons.COLORIZE), + style=ft.ButtonStyle( + bgcolor={ft.ControlState.HOVERED: ft.Colors.BLUE} + ), + on_click=handle_color_click, + on_hover=handle_on_hover, + ), + ft.MenuItemButton( + content=ft.Text("Green"), + leading=ft.Icon(ft.Icons.COLORIZE), + style=ft.ButtonStyle( + bgcolor={ft.ControlState.HOVERED: ft.Colors.GREEN} + ), + on_click=handle_color_click, + on_hover=handle_on_hover, + ), + ft.MenuItemButton( + content=ft.Text("Red"), + leading=ft.Icon(ft.Icons.COLORIZE), + style=ft.ButtonStyle( + bgcolor={ft.ControlState.HOVERED: ft.Colors.RED} + ), + on_click=handle_color_click, + on_hover=handle_on_hover, + ), + ], + ), + ], + ) + + page.add( + ft.SafeArea( + expand=True, + avoid_intrusions_left=False, + avoid_intrusions_top=False, + avoid_intrusions_right=False, + avoid_intrusions_bottom=False, + content=ft.Column( + expand=True, + spacing=0, + controls=[ + ft.Row(controls=[menubar]), + background_container := ft.Container( + expand=True, + bgcolor=ft.Colors.WHITE, + alignment=ft.Alignment.CENTER, + content=ft.Text( + value="Choose a bgcolor from the menu", + style=ft.TextStyle(weight=ft.FontWeight.W_500), + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/menu_item_button/basic/pyproject.toml b/sdk/python/examples/controls/menu_item_button/basic/pyproject.toml new file mode 100644 index 0000000000..b747c3d414 --- /dev/null +++ b/sdk/python/examples/controls/menu_item_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "menu-item-button-basic" +version = "1.0.0" +description = "Uses MenuItemButton items to change page background color from a submenu with hover styling." +requires-python = ">=3.10" +keywords = ["menu item button", "submenu", "hover style", "events", "background color"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/MenuItemButton"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "Row", "MenuBar", "SubmenuButton", "MenuItemButton", "Container", "Text", "Icon"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["submenu item click handling", "menu item hover styling", "dynamic background color updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/navigation_bar/__init__.py b/sdk/python/examples/controls/navigation_bar/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/navigation_bar/basic.py b/sdk/python/examples/controls/navigation_bar/basic.py deleted file mode 100644 index 2e0b3c844e..0000000000 --- a/sdk/python/examples/controls/navigation_bar/basic.py +++ /dev/null @@ -1,23 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "NavigationBar Example" - - page.navigation_bar = ft.NavigationBar( - destinations=[ - ft.NavigationBarDestination(icon=ft.Icons.EXPLORE, label="Explore"), - ft.NavigationBarDestination(icon=ft.Icons.COMMUTE, label="Commute"), - ft.NavigationBarDestination( - icon=ft.Icons.BOOKMARK_BORDER, - selected_icon=ft.Icons.BOOKMARK, - label="Favorites", - ), - ] - ) - - page.add(ft.Text("Body!")) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/navigation_bar/basic/main.py b/sdk/python/examples/controls/navigation_bar/basic/main.py new file mode 100644 index 0000000000..b10a48b2df --- /dev/null +++ b/sdk/python/examples/controls/navigation_bar/basic/main.py @@ -0,0 +1,27 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "NavigationBar Example" + + page.navigation_bar = ft.NavigationBar( + destinations=[ + ft.NavigationBarDestination(icon=ft.Icons.EXPLORE, label="Explore"), + ft.NavigationBarDestination(icon=ft.Icons.COMMUTE, label="Commute"), + ft.NavigationBarDestination( + icon=ft.Icons.BOOKMARK_BORDER, + selected_icon=ft.Icons.BOOKMARK, + label="Favorites", + ), + ] + ) + + page.add( + ft.SafeArea( + content=ft.Text("Body!"), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/navigation_bar/basic/pyproject.toml b/sdk/python/examples/controls/navigation_bar/basic/pyproject.toml new file mode 100644 index 0000000000..4457421a1c --- /dev/null +++ b/sdk/python/examples/controls/navigation_bar/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "navigation-bar-basic" +version = "1.0.0" +description = "Demonstrates a Material NavigationBar with three destination tabs." +requires-python = ">=3.10" +keywords = ["navigation bar", "bottom navigation", "destinations", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/NavigationBar"] + +[tool.flet.metadata] +title = "Basic" +controls = ["NavigationBar", "NavigationBarDestination", "SafeArea", "Text"] +layout_pattern = "tabbed-navigation" +complexity = "basic" +features = ["bottom navigation", "destination tabs"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/navigation_drawer/__init__.py b/sdk/python/examples/controls/navigation_drawer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/navigation_drawer/position_end.py b/sdk/python/examples/controls/navigation_drawer/position_end.py deleted file mode 100644 index 85800af7f3..0000000000 --- a/sdk/python/examples/controls/navigation_drawer/position_end.py +++ /dev/null @@ -1,38 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - async def handle_show_drawer(): - await page.show_end_drawer() - - def handle_dismissal(e: ft.Event[ft.NavigationDrawer]): - print("Drawer dismissed!") - - async def handle_change(e: ft.Event[ft.NavigationDrawer]): - print(f"Selected Index changed: {e.control.selected_index}") - await page.close_end_drawer() - - page.end_drawer = ft.NavigationDrawer( - on_dismiss=handle_dismissal, - on_change=handle_change, - controls=[ - ft.NavigationDrawerDestination( - icon=ft.Icons.ADD_TO_HOME_SCREEN_SHARP, - label="Item 1", - ), - ft.NavigationDrawerDestination( - icon=ft.Icon(ft.Icons.ADD_COMMENT), - label="Item 2", - ), - ], - ) - page.add( - ft.Button( - content="Show end drawer", - on_click=handle_show_drawer, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/navigation_drawer/position_end/main.py b/sdk/python/examples/controls/navigation_drawer/position_end/main.py new file mode 100644 index 0000000000..7201b80ab1 --- /dev/null +++ b/sdk/python/examples/controls/navigation_drawer/position_end/main.py @@ -0,0 +1,41 @@ +import flet as ft + + +def main(page: ft.Page): + async def handle_show_drawer(): + await page.show_end_drawer() + + def handle_dismissal(e: ft.Event[ft.NavigationDrawer]): + print("Drawer dismissed!") + + async def handle_change(e: ft.Event[ft.NavigationDrawer]): + print(f"Selected Index changed: {e.control.selected_index}") + await page.close_end_drawer() + + page.end_drawer = ft.NavigationDrawer( + on_dismiss=handle_dismissal, + on_change=handle_change, + controls=[ + ft.NavigationDrawerDestination( + icon=ft.Icons.ADD_TO_HOME_SCREEN_SHARP, + label="Item 1", + ), + ft.NavigationDrawerDestination( + icon=ft.Icon(ft.Icons.ADD_COMMENT), + label="Item 2", + ), + ], + ) + + page.add( + ft.SafeArea( + content=ft.Button( + content="Show end drawer", + on_click=handle_show_drawer, + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/navigation_drawer/position_end/pyproject.toml b/sdk/python/examples/controls/navigation_drawer/position_end/pyproject.toml new file mode 100644 index 0000000000..f649167a6b --- /dev/null +++ b/sdk/python/examples/controls/navigation_drawer/position_end/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "navigation-drawer-position-end" +version = "1.0.0" +description = "Demonstrates an end-position NavigationDrawer with destination selection handling." +requires-python = ">=3.10" +keywords = ["navigation drawer", "drawer", "navigation", "end position", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/NavigationDrawer"] + +[tool.flet.metadata] +title = "End Position" +controls = ["NavigationDrawer", "NavigationDrawerDestination", "Button", "SafeArea", "Page"] +layout_pattern = "drawer-navigation" +complexity = "basic" +features = ["end drawer", "destination callbacks", "open/close actions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/navigation_drawer/position_start.py b/sdk/python/examples/controls/navigation_drawer/position_start.py deleted file mode 100644 index 4002748777..0000000000 --- a/sdk/python/examples/controls/navigation_drawer/position_start.py +++ /dev/null @@ -1,47 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - async def handle_show_drawer(): - await page.show_drawer() - - def handle_dismissal(e: ft.Event[ft.NavigationDrawer]): - print("Drawer dismissed!") - - async def handle_change(e: ft.Event[ft.NavigationDrawer]): - print(f"Selected Index changed: {e.control.selected_index}") - await page.close_drawer() - - page.drawer = ft.NavigationDrawer( - on_dismiss=handle_dismissal, - on_change=handle_change, - controls=[ - ft.Container(height=12), - ft.NavigationDrawerDestination( - label="Item 1", - icon=ft.Icons.DOOR_BACK_DOOR_OUTLINED, - selected_icon=ft.Icon(ft.Icons.DOOR_BACK_DOOR), - ), - ft.Divider(thickness=2), - ft.NavigationDrawerDestination( - icon=ft.Icon(ft.Icons.MAIL_OUTLINED), - label="Item 2", - selected_icon=ft.Icons.MAIL, - ), - ft.NavigationDrawerDestination( - icon=ft.Icon(ft.Icons.PHONE_OUTLINED), - label="Item 3", - selected_icon=ft.Icons.PHONE, - ), - ], - ) - page.add( - ft.Button( - content="Show drawer", - on_click=handle_show_drawer, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/navigation_drawer/position_start/main.py b/sdk/python/examples/controls/navigation_drawer/position_start/main.py new file mode 100644 index 0000000000..438b560964 --- /dev/null +++ b/sdk/python/examples/controls/navigation_drawer/position_start/main.py @@ -0,0 +1,50 @@ +import flet as ft + + +def main(page: ft.Page): + async def handle_show_drawer(): + await page.show_drawer() + + def handle_dismissal(e: ft.Event[ft.NavigationDrawer]): + print("Drawer dismissed!") + + async def handle_change(e: ft.Event[ft.NavigationDrawer]): + print(f"Selected Index changed: {e.control.selected_index}") + await page.close_drawer() + + page.drawer = ft.NavigationDrawer( + on_dismiss=handle_dismissal, + on_change=handle_change, + controls=[ + ft.Container(height=12), + ft.NavigationDrawerDestination( + label="Item 1", + icon=ft.Icons.DOOR_BACK_DOOR_OUTLINED, + selected_icon=ft.Icon(ft.Icons.DOOR_BACK_DOOR), + ), + ft.Divider(thickness=2), + ft.NavigationDrawerDestination( + icon=ft.Icon(ft.Icons.MAIL_OUTLINED), + label="Item 2", + selected_icon=ft.Icons.MAIL, + ), + ft.NavigationDrawerDestination( + icon=ft.Icon(ft.Icons.PHONE_OUTLINED), + label="Item 3", + selected_icon=ft.Icons.PHONE, + ), + ], + ) + + page.add( + ft.SafeArea( + content=ft.Button( + content="Show drawer", + on_click=handle_show_drawer, + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/navigation_drawer/position_start/pyproject.toml b/sdk/python/examples/controls/navigation_drawer/position_start/pyproject.toml new file mode 100644 index 0000000000..de3b5e011f --- /dev/null +++ b/sdk/python/examples/controls/navigation_drawer/position_start/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "navigation-drawer-position-start" +version = "1.0.0" +description = "Demonstrates a start-position NavigationDrawer with labeled destinations and callbacks." +requires-python = ">=3.10" +keywords = ["navigation drawer", "drawer", "navigation", "start position", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/NavigationDrawer"] + +[tool.flet.metadata] +title = "Start Position" +controls = ["NavigationDrawer", "NavigationDrawerDestination", "Button", "SafeArea", "Container", "Divider", "Page"] +layout_pattern = "drawer-navigation" +complexity = "basic" +features = ["start drawer", "navigation destinations", "selection and dismiss callbacks"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/navigation_rail/__init__.py b/sdk/python/examples/controls/navigation_rail/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/navigation_rail/basic.py b/sdk/python/examples/controls/navigation_rail/basic.py deleted file mode 100644 index d43fee2ac8..0000000000 --- a/sdk/python/examples/controls/navigation_rail/basic.py +++ /dev/null @@ -1,53 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - rail = ft.NavigationRail( - selected_index=0, - label_type=ft.NavigationRailLabelType.ALL, - min_width=100, - min_extended_width=400, - group_alignment=-0.9, - on_change=lambda e: print("Selected destination:", e.control.selected_index), - leading=ft.FloatingActionButton( - icon=ft.Icons.CREATE, - content="Add", - on_click=lambda e: print("FAB clicked!"), - ), - destinations=[ - ft.NavigationRailDestination( - icon=ft.Icons.FAVORITE_BORDER, - selected_icon=ft.Icons.FAVORITE, - label="First", - ), - ft.NavigationRailDestination( - icon=ft.Icon(ft.Icons.BOOKMARK_BORDER), - selected_icon=ft.Icon(ft.Icons.BOOKMARK), - label="Second", - ), - ft.NavigationRailDestination( - icon=ft.Icons.SETTINGS_OUTLINED, - selected_icon=ft.Icon(ft.Icons.SETTINGS), - label=ft.Text("Settings"), - ), - ], - ) - - page.add( - ft.Row( - expand=True, - controls=[ - ft.SelectionArea(content=rail), - ft.VerticalDivider(width=1), - ft.Column( - alignment=ft.MainAxisAlignment.START, - expand=True, - controls=[ft.Text("Body!")], - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/navigation_rail/basic/main.py b/sdk/python/examples/controls/navigation_rail/basic/main.py new file mode 100644 index 0000000000..1637fcb988 --- /dev/null +++ b/sdk/python/examples/controls/navigation_rail/basic/main.py @@ -0,0 +1,56 @@ +import flet as ft + + +def main(page: ft.Page): + rail = ft.NavigationRail( + selected_index=0, + label_type=ft.NavigationRailLabelType.ALL, + min_width=100, + min_extended_width=400, + group_alignment=-0.9, + on_change=lambda e: print("Selected destination:", e.control.selected_index), + leading=ft.FloatingActionButton( + icon=ft.Icons.CREATE, + content="Add", + on_click=lambda e: print("FAB clicked!"), + ), + destinations=[ + ft.NavigationRailDestination( + icon=ft.Icons.FAVORITE_BORDER, + selected_icon=ft.Icons.FAVORITE, + label="First", + ), + ft.NavigationRailDestination( + icon=ft.Icon(ft.Icons.BOOKMARK_BORDER), + selected_icon=ft.Icon(ft.Icons.BOOKMARK), + label="Second", + ), + ft.NavigationRailDestination( + icon=ft.Icons.SETTINGS_OUTLINED, + selected_icon=ft.Icon(ft.Icons.SETTINGS), + label=ft.Text("Settings"), + ), + ], + ) + + page.add( + ft.SafeArea( + expand=True, + content=ft.Row( + expand=True, + controls=[ + ft.SelectionArea(content=rail), + ft.VerticalDivider(width=1), + ft.Column( + alignment=ft.MainAxisAlignment.START, + expand=True, + controls=[ft.Text("Body!")], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/navigation_rail/basic/pyproject.toml b/sdk/python/examples/controls/navigation_rail/basic/pyproject.toml new file mode 100644 index 0000000000..9187d09833 --- /dev/null +++ b/sdk/python/examples/controls/navigation_rail/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "navigation-rail-basic" +version = "1.0.0" +description = "Demonstrates a Material NavigationRail with three destinations and custom leading FAB." +requires-python = ">=3.10" +keywords = ["navigation rail", "material", "navigation", "rail", "destinations"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/NavigationRail"] + +[tool.flet.metadata] +title = "Basic" +controls = ["NavigationRail", "NavigationRailDestination", "SafeArea", "FloatingActionButton", "SelectionArea", "Page", "VerticalDivider", "Column", "Row", "Text"] +layout_pattern = "side-navigation" +complexity = "basic" +features = ["navigation rail", "destination selection", "leading action button"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/outlined_button/__init__.py b/sdk/python/examples/controls/outlined_button/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/outlined_button/basic.py b/sdk/python/examples/controls/outlined_button/basic.py deleted file mode 100644 index 2f9e2dcfd8..0000000000 --- a/sdk/python/examples/controls/outlined_button/basic.py +++ /dev/null @@ -1,14 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "OutlinedButton Example" - - page.add( - ft.OutlinedButton(content="Outlined button"), - ft.OutlinedButton(content="Disabled button", disabled=True), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/outlined_button/basic/main.py b/sdk/python/examples/controls/outlined_button/basic/main.py new file mode 100644 index 0000000000..796f7e046d --- /dev/null +++ b/sdk/python/examples/controls/outlined_button/basic/main.py @@ -0,0 +1,20 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "OutlinedButton Example" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.OutlinedButton(content="Outlined button"), + ft.OutlinedButton(content="Disabled button", disabled=True), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/outlined_button/basic/pyproject.toml b/sdk/python/examples/controls/outlined_button/basic/pyproject.toml new file mode 100644 index 0000000000..5a0c1b037e --- /dev/null +++ b/sdk/python/examples/controls/outlined_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "outlined-button-basic" +version = "1.0.0" +description = "Shows enabled and disabled OutlinedButton variants." +requires-python = ">=3.10" +keywords = ["outlined button", "button", "material", "disabled states"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/OutlinedButton"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "OutlinedButton"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["enabled and disabled states"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/outlined_button/custom_content.py b/sdk/python/examples/controls/outlined_button/custom_content.py deleted file mode 100644 index b56740234d..0000000000 --- a/sdk/python/examples/controls/outlined_button/custom_content.py +++ /dev/null @@ -1,37 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "OutlinedButton Example" - page.theme_mode = ft.ThemeMode.LIGHT - - page.add( - ft.OutlinedButton( - width=150, - content=ft.Row( - alignment=ft.MainAxisAlignment.SPACE_AROUND, - controls=[ - ft.Icon(ft.Icons.FAVORITE, color=ft.Colors.PINK), - ft.Icon(ft.Icons.AUDIOTRACK, color=ft.Colors.GREEN), - ft.Icon(ft.Icons.BEACH_ACCESS, color=ft.Colors.BLUE), - ], - ), - ), - ft.OutlinedButton( - content=ft.Container( - padding=ft.Padding.all(10), - content=ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - spacing=5, - controls=[ - ft.Text(value="Compound button", size=20), - ft.Text(value="This is secondary text"), - ], - ), - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/outlined_button/custom_content/main.py b/sdk/python/examples/controls/outlined_button/custom_content/main.py new file mode 100644 index 0000000000..9894601896 --- /dev/null +++ b/sdk/python/examples/controls/outlined_button/custom_content/main.py @@ -0,0 +1,43 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "OutlinedButton Example" + page.theme_mode = ft.ThemeMode.LIGHT + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.OutlinedButton( + width=150, + content=ft.Row( + alignment=ft.MainAxisAlignment.SPACE_AROUND, + controls=[ + ft.Icon(ft.Icons.FAVORITE, color=ft.Colors.PINK), + ft.Icon(ft.Icons.AUDIOTRACK, color=ft.Colors.GREEN), + ft.Icon(ft.Icons.BEACH_ACCESS, color=ft.Colors.BLUE), + ], + ), + ), + ft.OutlinedButton( + content=ft.Container( + padding=ft.Padding.all(10), + content=ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + spacing=5, + controls=[ + ft.Text(value="Compound button", size=20), + ft.Text(value="This is secondary text"), + ], + ), + ), + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/outlined_button/custom_content/pyproject.toml b/sdk/python/examples/controls/outlined_button/custom_content/pyproject.toml new file mode 100644 index 0000000000..a85e0741a0 --- /dev/null +++ b/sdk/python/examples/controls/outlined_button/custom_content/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "outlined-button-custom-content" +version = "1.0.0" +description = "Demonstrates OutlinedButtons with custom content and icon/text composition." +requires-python = ">=3.10" +keywords = ["outlined button", "custom content", "compound button", "button"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/OutlinedButton"] + +[tool.flet.metadata] +title = "Custom content" +controls = ["SafeArea", "OutlinedButton", "Row", "Column", "Icon", "Container", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["custom button content", "compound layout"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/outlined_button/handling_clicks.py b/sdk/python/examples/controls/outlined_button/handling_clicks.py deleted file mode 100644 index dab5b33bf3..0000000000 --- a/sdk/python/examples/controls/outlined_button/handling_clicks.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "OutlinedButton Example" - page.theme_mode = ft.ThemeMode.LIGHT - - def handle_button_click(e: ft.Event[ft.OutlinedButton]): - button.data += 1 - message.value = f"Button clicked {button.data} time(s)" - page.update() - - button = ft.OutlinedButton( - content="Button with 'click' event", - data=0, - on_click=handle_button_click, - ) - message = ft.Text() - - page.add( - ft.Column( - controls=[button, message], - alignment=ft.MainAxisAlignment.CENTER, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - expand=True, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/outlined_button/handling_clicks/main.py b/sdk/python/examples/controls/outlined_button/handling_clicks/main.py new file mode 100644 index 0000000000..8cb6d21646 --- /dev/null +++ b/sdk/python/examples/controls/outlined_button/handling_clicks/main.py @@ -0,0 +1,33 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "OutlinedButton Example" + page.theme_mode = ft.ThemeMode.LIGHT + + def handle_button_click(e: ft.Event[ft.OutlinedButton]): + button.data += 1 + message.value = f"Button clicked {button.data} time(s)" + page.update() + + button = ft.OutlinedButton( + content="Button with 'click' event", + data=0, + on_click=handle_button_click, + ) + message = ft.Text() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[button, message], + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + expand=True, + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/outlined_button/handling_clicks/pyproject.toml b/sdk/python/examples/controls/outlined_button/handling_clicks/pyproject.toml new file mode 100644 index 0000000000..db34e2728d --- /dev/null +++ b/sdk/python/examples/controls/outlined_button/handling_clicks/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "outlined-button-handling-clicks" +version = "1.0.0" +description = "Demonstrates OutlinedButton click handling with a live counter." +requires-python = ">=3.10" +keywords = ["outlined button", "events", "on_click", "counter", "button"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/OutlinedButton"] + +[tool.flet.metadata] +title = "Handling clicks" +controls = ["SafeArea", "Column", "OutlinedButton", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["click event handling", "dynamic label updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/outlined_button/icons.py b/sdk/python/examples/controls/outlined_button/icons.py deleted file mode 100644 index 9f1d045ce7..0000000000 --- a/sdk/python/examples/controls/outlined_button/icons.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "OutlinedButton Example" - - page.add( - ft.OutlinedButton(content="Button with icon", icon=ft.Icons.CHAIR_OUTLINED), - ft.OutlinedButton( - content="Button with colorful icon", - icon=ft.Icons.PARK_ROUNDED, - icon_color=ft.Colors.GREEN_400, - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/outlined_button/icons/main.py b/sdk/python/examples/controls/outlined_button/icons/main.py new file mode 100644 index 0000000000..733c763310 --- /dev/null +++ b/sdk/python/examples/controls/outlined_button/icons/main.py @@ -0,0 +1,26 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "OutlinedButton Example" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.OutlinedButton( + content="Button with icon", icon=ft.Icons.CHAIR_OUTLINED + ), + ft.OutlinedButton( + content="Button with colorful icon", + icon=ft.Icons.PARK_ROUNDED, + icon_color=ft.Colors.GREEN_400, + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/outlined_button/icons/pyproject.toml b/sdk/python/examples/controls/outlined_button/icons/pyproject.toml new file mode 100644 index 0000000000..bc759a083d --- /dev/null +++ b/sdk/python/examples/controls/outlined_button/icons/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "outlined-button-icons" +version = "1.0.0" +description = "Shows OutlinedButtons with icon and color variations." +requires-python = ">=3.10" +keywords = ["outlined button", "icon button", "material icons", "button"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/OutlinedButton"] + +[tool.flet.metadata] +title = "Icons" +controls = ["SafeArea", "OutlinedButton", "Icon"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["icon support"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page/app_exit_confirm_dialog.py b/sdk/python/examples/controls/page/app_exit_confirm_dialog.py deleted file mode 100644 index 7511ba0095..0000000000 --- a/sdk/python/examples/controls/page/app_exit_confirm_dialog.py +++ /dev/null @@ -1,34 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def window_event(e: ft.WindowEvent): - if e.type == ft.WindowEventType.CLOSE: - page.show_dialog(confirm_dialog) - page.update() - - page.window.prevent_close = True - page.window.on_event = window_event - - async def handle_yes_click(e: ft.Event[ft.Button]): - await page.window.destroy() - - def handle_no_click(e: ft.Event[ft.OutlinedButton]): - page.pop_dialog() - page.update() - - confirm_dialog = ft.AlertDialog( - modal=True, - title=ft.Text("Please confirm"), - content=ft.Text("Do you really want to exit this app?"), - actions=[ - ft.Button(content="Yes", on_click=handle_yes_click), - ft.OutlinedButton(content="No", on_click=handle_no_click), - ], - actions_alignment=ft.MainAxisAlignment.END, - ) - - page.add(ft.Text('Try exiting this app by clicking window\'s "Close" button!')) - - -ft.run(main) diff --git a/sdk/python/examples/controls/page/app_exit_confirm_dialog/main.py b/sdk/python/examples/controls/page/app_exit_confirm_dialog/main.py new file mode 100644 index 0000000000..8e26e87e58 --- /dev/null +++ b/sdk/python/examples/controls/page/app_exit_confirm_dialog/main.py @@ -0,0 +1,45 @@ +import flet as ft + + +def main(page: ft.Page): + def window_event(e: ft.WindowEvent): + if e.type == ft.WindowEventType.CLOSE: + page.show_dialog(confirm_dialog) + page.update() + + page.window.prevent_close = True + page.window.on_event = window_event + + async def handle_yes_click(e: ft.Event[ft.Button]): + await page.window.destroy() + + def handle_no_click(e: ft.Event[ft.OutlinedButton]): + page.pop_dialog() + page.update() + + confirm_dialog = ft.AlertDialog( + modal=True, + title=ft.Text("Please confirm"), + content=ft.Text("Do you really want to exit this app?"), + actions=[ + ft.Button(content="Yes", on_click=handle_yes_click), + ft.OutlinedButton(content="No", on_click=handle_no_click), + ], + actions_alignment=ft.MainAxisAlignment.END, + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + 'Try exiting this app by clicking window\'s "Close" button!' + ) + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page/app_exit_confirm_dialog/pyproject.toml b/sdk/python/examples/controls/page/app_exit_confirm_dialog/pyproject.toml new file mode 100644 index 0000000000..ac7c8d0668 --- /dev/null +++ b/sdk/python/examples/controls/page/app_exit_confirm_dialog/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "page-app-exit-confirm-dialog" +version = "1.0.0" +description = "Demonstrates handling window close events with a custom confirm dialog." +requires-python = ">=3.10" +keywords = ["page", "window", "close", "confirm dialog", "window event", "desktop"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Pages/Page"] + +[tool.flet.metadata] +title = "App exit confirm dialog" +controls = ["SafeArea", "Text", "AlertDialog", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["window close interception", "confirm dialog"] + +[tool.flet] +platforms = ["macos", "windows", "linux"] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page/device_locale.py b/sdk/python/examples/controls/page/device_locale.py deleted file mode 100644 index e240c53f18..0000000000 --- a/sdk/python/examples/controls/page/device_locale.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - def format_locales(locales: list[ft.Locale]) -> str: - """Format locale list for display.""" - return ", ".join(str(loc) for loc in locales) - - def handle_locale_change(e: ft.LocaleChangeEvent): - page.add(ft.Text(f"Locales changed: {format_locales(e.locales)}")) - - page.on_locale_change = handle_locale_change - page.scroll = ft.ScrollMode.AUTO - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - initial_locales = (await page.get_device_info()).locales - page.add( - ft.Text(f"Initial locales: {format_locales(initial_locales)}"), - ft.Text( - "Change your system or browser language to trigger on_locale_change.", - color=ft.Colors.BLUE, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/page/device_locale/main.py b/sdk/python/examples/controls/page/device_locale/main.py new file mode 100644 index 0000000000..28888ec8cf --- /dev/null +++ b/sdk/python/examples/controls/page/device_locale/main.py @@ -0,0 +1,34 @@ +import flet as ft + + +async def main(page: ft.Page): + def format_locales(locales: list[ft.Locale]) -> str: + """Format locale list for display.""" + return ", ".join(str(loc) for loc in locales) + + def handle_locale_change(e: ft.LocaleChangeEvent): + page.add(ft.Text(f"Locales changed: {format_locales(e.locales)}")) + + page.on_locale_change = handle_locale_change + page.scroll = ft.ScrollMode.AUTO + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + initial_locales = (await page.get_device_info()).locales + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text(f"Initial locales: {format_locales(initial_locales)}"), + ft.Text( + "Change your system or browser language to trigger " + "on_locale_change.", + color=ft.Colors.BLUE, + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page/device_locale/pyproject.toml b/sdk/python/examples/controls/page/device_locale/pyproject.toml new file mode 100644 index 0000000000..7789fa6f35 --- /dev/null +++ b/sdk/python/examples/controls/page/device_locale/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "page-device-locale" +version = "1.0.0" +description = "Demonstrates locale-change handling and initial locale reporting in a Page." +requires-python = ">=3.10" +keywords = ["page", "locale", "locales", "on_locale_change", "scroll"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Pages/Page"] + +[tool.flet.metadata] +title = "Device locale" +controls = ["SafeArea", "Column", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["locale change handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page/device_orientation.py b/sdk/python/examples/controls/page/device_orientation.py deleted file mode 100644 index 639e530f1b..0000000000 --- a/sdk/python/examples/controls/page/device_orientation.py +++ /dev/null @@ -1,61 +0,0 @@ -import flet as ft - - -def main(page: ft.Page) -> None: - page.title = "Device orientation lock" - page.appbar = ft.AppBar( - title=ft.Text("Device orientation Playground"), - center_title=True, - bgcolor=ft.Colors.BLUE, - ) - - def handle_media_change(e: ft.PageMediaData) -> None: - page.show_dialog( - ft.SnackBar( - f"I see you rotated the device to {e.orientation.name} orientation. 👀", - action="Haha!", - duration=ft.Duration(seconds=3), - ) - ) - - page.on_media_change = handle_media_change - - async def on_checkbox_change(e: ft.Event[ft.Checkbox]) -> None: - # get selection - selected = [o for o, checkbox in checkboxes.items() if checkbox.value] - # apply selection - await page.set_allowed_device_orientations(selected) - - checkboxes: dict[ft.DeviceOrientation, ft.Checkbox] = { - orientation: ft.Checkbox( - label=orientation.name, - value=True, - on_change=on_checkbox_change, - disabled=not page.platform.is_mobile(), # disabled on non-mobile platforms - ) - for orientation in list(ft.DeviceOrientation) - } - - page.add( - ft.Text( - spans=[ - # shown only on mobile platforms - ft.TextSpan( - "Select the orientations that should remain enabled for the app. " - "If no orientation is selected, the system defaults will be used.", - visible=page.platform.is_mobile(), - ), - # shown only on non-mobile platforms - ft.TextSpan( - "Please open this example on a mobile device instead.", - visible=not page.platform.is_mobile(), - style=ft.TextStyle(weight=ft.FontWeight.BOLD), - ), - ], - ), - ft.Column(controls=list(checkboxes.values())), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/page/device_orientation/main.py b/sdk/python/examples/controls/page/device_orientation/main.py new file mode 100644 index 0000000000..a9e781fb71 --- /dev/null +++ b/sdk/python/examples/controls/page/device_orientation/main.py @@ -0,0 +1,69 @@ +import flet as ft + + +def main(page: ft.Page) -> None: + page.title = "Device orientation lock" + page.appbar = ft.AppBar( + title=ft.Text("Device orientation Playground"), + center_title=True, + bgcolor=ft.Colors.BLUE, + ) + + def handle_media_change(e: ft.PageMediaData) -> None: + page.show_dialog( + ft.SnackBar( + f"I see you rotated the device to {e.orientation.name} orientation. 👀", + action="Haha!", + duration=ft.Duration(seconds=3), + ) + ) + + page.on_media_change = handle_media_change + + async def on_checkbox_change(e: ft.Event[ft.Checkbox]) -> None: + # get selection + selected = [o for o, checkbox in checkboxes.items() if checkbox.value] + # apply selection + await page.set_allowed_device_orientations(selected) + + checkboxes: dict[ft.DeviceOrientation, ft.Checkbox] = { + orientation: ft.Checkbox( + label=orientation.name, + value=True, + on_change=on_checkbox_change, + disabled=not page.platform.is_mobile(), # disabled on non-mobile platforms + ) + for orientation in list(ft.DeviceOrientation) + } + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + spans=[ + # shown only on mobile platforms + ft.TextSpan( + "Select the orientations that should remain enabled " + "for the app. " + "If no orientation is selected, " + "the system defaults will be used.", + visible=page.platform.is_mobile(), + ), + # shown only on non-mobile platforms + ft.TextSpan( + "Please open this example on a mobile device instead.", + visible=not page.platform.is_mobile(), + style=ft.TextStyle(weight=ft.FontWeight.BOLD), + ), + ], + ), + ft.Column(controls=list(checkboxes.values())), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page/device_orientation/pyproject.toml b/sdk/python/examples/controls/page/device_orientation/pyproject.toml new file mode 100644 index 0000000000..69ba50e83b --- /dev/null +++ b/sdk/python/examples/controls/page/device_orientation/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "page-device-orientation" +version = "1.0.0" +description = "Shows how to handle device orientation and lock allowed orientations at runtime." +requires-python = ">=3.10" +keywords = ["page", "orientation", "media", "checkbox", "mobile"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Pages/Page"] + +[tool.flet.metadata] +title = "Device orientation" +controls = ["SafeArea", "AppBar", "Text", "Column", "Checkbox", "Page"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["media change handling", "orientation locking"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page/keyboard_events.py b/sdk/python/examples/controls/page/keyboard_events.py deleted file mode 100644 index 91b931ad39..0000000000 --- a/sdk/python/examples/controls/page/keyboard_events.py +++ /dev/null @@ -1,48 +0,0 @@ -import flet as ft - - -class ButtonControl(ft.Container): - def __init__(self, text): - super().__init__() - self.content: ft.Text = ft.Text(text) - self.border = ft.Border.all(1, ft.Colors.BLACK_54) - self.border_radius = 3 - self.bgcolor = "0x09000000" - self.padding = 10 - self.visible = False - - -def main(page: ft.Page): - page.spacing = 50 - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def on_keyboard(e: ft.KeyboardEvent): - key.content.value = e.key - key.visible = True - shift.visible = e.shift - ctrl.visible = e.ctrl - alt.visible = e.alt - meta.visible = e.meta - page.update() - - page.on_keyboard_event = on_keyboard - - page.add( - ft.Text( - "Press any key with a combination of CTRL, ALT, SHIFT and META keys..." - ), - ft.Row( - controls=[ - key := ButtonControl(""), - shift := ButtonControl("Shift"), - ctrl := ButtonControl("Control"), - alt := ButtonControl("Alt"), - meta := ButtonControl("Meta"), - ], - alignment=ft.MainAxisAlignment.CENTER, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/page/keyboard_events/main.py b/sdk/python/examples/controls/page/keyboard_events/main.py new file mode 100644 index 0000000000..f1a745ef6f --- /dev/null +++ b/sdk/python/examples/controls/page/keyboard_events/main.py @@ -0,0 +1,56 @@ +import flet as ft + + +class ButtonControl(ft.Container): + def __init__(self, text): + super().__init__() + self.content: ft.Text = ft.Text(text) + self.border = ft.Border.all(1, ft.Colors.BLACK_54) + self.border_radius = 3 + self.bgcolor = "0x09000000" + self.padding = 10 + self.visible = False + + +def main(page: ft.Page): + page.spacing = 50 + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def on_keyboard(e: ft.KeyboardEvent): + key.content.value = e.key + key.visible = True + shift.visible = e.shift + ctrl.visible = e.ctrl + alt.visible = e.alt + meta.visible = e.meta + page.update() + + page.on_keyboard_event = on_keyboard + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Press any key with a combination of CTRL, ALT, SHIFT " + "and META keys..." + ), + ft.Row( + controls=[ + key := ButtonControl(""), + shift := ButtonControl("Shift"), + ctrl := ButtonControl("Control"), + alt := ButtonControl("Alt"), + meta := ButtonControl("Meta"), + ], + alignment=ft.MainAxisAlignment.CENTER, + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page/keyboard_events/pyproject.toml b/sdk/python/examples/controls/page/keyboard_events/pyproject.toml new file mode 100644 index 0000000000..45ef142d2e --- /dev/null +++ b/sdk/python/examples/controls/page/keyboard_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "page-keyboard-events" +version = "1.0.0" +description = "Demonstrates live updates from page keyboard events and modifier keys." +requires-python = ">=3.10" +keywords = ["page", "keyboard", "keyboard event", "shortcut", "modifier keys"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Pages/Page"] + +[tool.flet.metadata] +title = "Keyboard events" +controls = ["SafeArea", "Column", "Row", "Container", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["keyboard input handling", "modifier keys display"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page/semantics_debugger.py b/sdk/python/examples/controls/page/semantics_debugger.py deleted file mode 100644 index 91b8b8d07b..0000000000 --- a/sdk/python/examples/controls/page/semantics_debugger.py +++ /dev/null @@ -1,30 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def on_keyboard(e: ft.KeyboardEvent): - if e.shift and e.key == "S": - page.show_semantics_debugger = not page.show_semantics_debugger - page.update() - - page.on_keyboard_event = on_keyboard - - def button_click(e: ft.Event[ft.Button]): - counter.value = str(int(counter.value) + 1) - page.update() - - page.add( - counter := ft.Text("0", size=40), - ft.Text("Press Shift+S to toggle semantics debugger"), - ft.Button( - content="Increment number", - icon=ft.Icons.ADD, - on_click=button_click, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/page/semantics_debugger/main.py b/sdk/python/examples/controls/page/semantics_debugger/main.py new file mode 100644 index 0000000000..58abf08baf --- /dev/null +++ b/sdk/python/examples/controls/page/semantics_debugger/main.py @@ -0,0 +1,37 @@ +import flet as ft + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def on_keyboard(e: ft.KeyboardEvent): + if e.shift and e.key == "S": + page.show_semantics_debugger = not page.show_semantics_debugger + page.update() + + page.on_keyboard_event = on_keyboard + + def button_click(e: ft.Event[ft.Button]): + counter.value = str(int(counter.value) + 1) + page.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + counter := ft.Text("0", size=40), + ft.Text("Press Shift+S to toggle semantics debugger"), + ft.Button( + content="Increment number", + icon=ft.Icons.ADD, + on_click=button_click, + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page/semantics_debugger/pyproject.toml b/sdk/python/examples/controls/page/semantics_debugger/pyproject.toml new file mode 100644 index 0000000000..bf5916d300 --- /dev/null +++ b/sdk/python/examples/controls/page/semantics_debugger/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "page-semantics-debugger" +version = "1.0.0" +description = "Demonstrates toggling the semantics debugger using a keyboard shortcut." +requires-python = ">=3.10" +keywords = ["page", "semantics", "accessibility", "keyboard shortcut", "debugger"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Pages/Page"] + +[tool.flet.metadata] +title = "Semantics debugger" +controls = ["SafeArea", "Column", "Text", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["semantics debugger toggle", "keyboard interaction"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page/set_platform.py b/sdk/python/examples/controls/page/set_platform.py deleted file mode 100644 index 076ab89fff..0000000000 --- a/sdk/python/examples/controls/page/set_platform.py +++ /dev/null @@ -1,24 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def set_android(e: ft.Event[ft.Button]): - page.platform = ft.PagePlatform.ANDROID - page.update() - print("New platform:", page.platform) - - def set_ios(e: ft.Event[ft.Button]): - page.platform = ft.PagePlatform.IOS - page.update() - print("New platform:", page.platform) - - page.add( - ft.Switch(label="Switch A", adaptive=True), - ft.Button("Set Android", on_click=set_android), - ft.Button("Set iOS", on_click=set_ios), - ) - - print("Default platform:", page.platform) - - -ft.run(main) diff --git a/sdk/python/examples/controls/page/set_platform/main.py b/sdk/python/examples/controls/page/set_platform/main.py new file mode 100644 index 0000000000..9cea0a76d3 --- /dev/null +++ b/sdk/python/examples/controls/page/set_platform/main.py @@ -0,0 +1,31 @@ +import flet as ft + + +def main(page: ft.Page): + def set_android(e: ft.Event[ft.Button]): + page.platform = ft.PagePlatform.ANDROID + page.update() + print("New platform:", page.platform) + + def set_ios(e: ft.Event[ft.Button]): + page.platform = ft.PagePlatform.IOS + page.update() + print("New platform:", page.platform) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Switch(label="Switch A", adaptive=True), + ft.Button("Set Android", on_click=set_android), + ft.Button("Set iOS", on_click=set_ios), + ] + ) + ) + ) + + print("Default platform:", page.platform) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page/set_platform/pyproject.toml b/sdk/python/examples/controls/page/set_platform/pyproject.toml new file mode 100644 index 0000000000..15b5f71989 --- /dev/null +++ b/sdk/python/examples/controls/page/set_platform/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "page-set-platform" +version = "1.0.0" +description = "Demonstrates switching page platform at runtime between iOS and Android." +requires-python = ">=3.10" +keywords = ["page", "platform", "ios", "android", "switch"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Pages/Page"] + +[tool.flet.metadata] +title = "Set platform" +controls = ["SafeArea", "Column", "Switch", "Button"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["runtime platform switching", "page platform update"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page/splash_test.py b/sdk/python/examples/controls/page/splash_test.py deleted file mode 100644 index 7f09dbc93c..0000000000 --- a/sdk/python/examples/controls/page/splash_test.py +++ /dev/null @@ -1,23 +0,0 @@ -import asyncio - -import flet as ft - - -async def main(page: ft.Page): - async def handle_button_click(e: ft.Event[ft.Button]): - my_bar = ft.ProgressBar() - - page.overlay.append(my_bar) - btn.disabled = True - page.update() - await asyncio.sleep(3) - - page.overlay.remove(my_bar) - btn.disabled = False - page.update() - - btn = ft.Button("Do some lengthy task!", on_click=handle_button_click) - page.add(btn) - - -ft.run(main) diff --git a/sdk/python/examples/controls/page/splash_test/main.py b/sdk/python/examples/controls/page/splash_test/main.py new file mode 100644 index 0000000000..d00d4bac14 --- /dev/null +++ b/sdk/python/examples/controls/page/splash_test/main.py @@ -0,0 +1,32 @@ +import asyncio + +import flet as ft + + +async def main(page: ft.Page): + async def handle_button_click(e: ft.Event[ft.Button]): + my_bar = ft.ProgressBar() + + page.overlay.append(my_bar) + btn.disabled = True + page.update() + await asyncio.sleep(3) + + page.overlay.remove(my_bar) + btn.disabled = False + page.update() + + btn = ft.Button("Do some lengthy task!", on_click=handle_button_click) + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + btn, + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page/splash_test/pyproject.toml b/sdk/python/examples/controls/page/splash_test/pyproject.toml new file mode 100644 index 0000000000..7be0cee220 --- /dev/null +++ b/sdk/python/examples/controls/page/splash_test/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "page-splash-test" +version = "1.0.0" +description = "Shows overlay progress-bar feedback while an async task runs." +requires-python = ">=3.10" +keywords = ["page", "overlay", "async", "progress", "button"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Pages/Page"] + +[tool.flet.metadata] +title = "Splash test" +controls = ["SafeArea", "Column", "Button", "ProgressBar"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["async task execution", "page overlay usage"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page/window_hidden_on_start.py b/sdk/python/examples/controls/page/window_hidden_on_start.py deleted file mode 100644 index f66cb1c947..0000000000 --- a/sdk/python/examples/controls/page/window_hidden_on_start.py +++ /dev/null @@ -1,24 +0,0 @@ -import asyncio - -import flet as ft - - -async def main(page: ft.Page): - print("Window is hidden on start. Will show after 3 seconds...") - page.add(ft.Text("Hello!")) - - # some configuration that we want to do before showing the window - page.window.width = 300 - page.window.height = 200 - page.update() - await page.window.center() - - # wait for 3 seconds before showing the window - await asyncio.sleep(3) - - # show the window - page.window.visible = True - page.update() - - -ft.run(main, view=ft.AppView.FLET_APP_HIDDEN) diff --git a/sdk/python/examples/controls/page/window_hidden_on_start/main.py b/sdk/python/examples/controls/page/window_hidden_on_start/main.py new file mode 100644 index 0000000000..b8229c3f41 --- /dev/null +++ b/sdk/python/examples/controls/page/window_hidden_on_start/main.py @@ -0,0 +1,33 @@ +import asyncio + +import flet as ft + + +async def main(page: ft.Page): + print("Window is hidden on start. Will show after 3 seconds...") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Hello!"), + ] + ) + ) + ) + + # some configuration that we want to do before showing the window + page.window.width = 300 + page.window.height = 200 + page.update() + await page.window.center() + + # wait for 3 seconds before showing the window + await asyncio.sleep(3) + + # show the window + page.window.visible = True + page.update() + + +if __name__ == "__main__": + ft.run(main, view=ft.AppView.FLET_APP_HIDDEN) diff --git a/sdk/python/examples/controls/page/window_hidden_on_start/pyproject.toml b/sdk/python/examples/controls/page/window_hidden_on_start/pyproject.toml new file mode 100644 index 0000000000..7b1135659e --- /dev/null +++ b/sdk/python/examples/controls/page/window_hidden_on_start/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "page-window-hidden-on-start" +version = "1.0.0" +description = "Demonstrates initializing window settings before showing a hidden app window." +requires-python = ">=3.10" +keywords = ["page", "window", "hidden", "startup", "overlay", "desktop"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Pages/Page"] + +[tool.flet.metadata] +title = "Window hidden on start" +controls = ["SafeArea", "Column", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["hidden startup", "window configuration"] + +[tool.flet] +platforms = ["macos", "windows", "linux"] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page/window_resize.py b/sdk/python/examples/controls/page/window_resize.py deleted file mode 100644 index d54f7ba3d2..0000000000 --- a/sdk/python/examples/controls/page/window_resize.py +++ /dev/null @@ -1,19 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - if page.window.width is None or page.window.height is None: - page.add(ft.Text("Window size can be changed only in desktop apps.")) - return - - width = 400 - height = 300 - - chrome_width = page.window.width - page.width - chrome_height = page.window.height - page.height - page.window.width = width + chrome_width - page.window.height = height + chrome_height - page.window.update() - - -ft.run(main) diff --git a/sdk/python/examples/controls/page/window_resize/main.py b/sdk/python/examples/controls/page/window_resize/main.py new file mode 100644 index 0000000000..9a372bba20 --- /dev/null +++ b/sdk/python/examples/controls/page/window_resize/main.py @@ -0,0 +1,28 @@ +import flet as ft + + +def main(page: ft.Page): + if page.window.width is None or page.window.height is None: + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Window size can be changed only in desktop apps."), + ] + ) + ) + ) + return + + width = 400 + height = 300 + + chrome_width = page.window.width - page.width + chrome_height = page.window.height - page.height + page.window.width = width + chrome_width + page.window.height = height + chrome_height + page.window.update() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page/window_resize/pyproject.toml b/sdk/python/examples/controls/page/window_resize/pyproject.toml new file mode 100644 index 0000000000..1d2efd0311 --- /dev/null +++ b/sdk/python/examples/controls/page/window_resize/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "page-window-resize" +version = "1.0.0" +description = "Demonstrates resizing the app window programmatically in desktop contexts." +requires-python = ">=3.10" +keywords = ["page", "window", "resize", "desktop", "window metrics"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Pages/Page"] + +[tool.flet.metadata] +title = "Window resize" +controls = ["SafeArea", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["window geometry", "desktop platform behavior"] + +[tool.flet] +platforms = ["macos", "windows", "linux"] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page_view/basic.py b/sdk/python/examples/controls/page_view/basic.py deleted file mode 100644 index 7d6572646b..0000000000 --- a/sdk/python/examples/controls/page_view/basic.py +++ /dev/null @@ -1,56 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.padding = 0 - - page.add( - ft.PageView( - expand=True, - viewport_fraction=0.9, - on_change=lambda e: print(f"Currently viewing page {int(e.data)}"), - selected_index=1, - horizontal=True, - controls=[ - ft.Container( - bgcolor=ft.Colors.INDIGO_400, - content=ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text("Welcome", size=40, weight=ft.FontWeight.BOLD), - ft.Text("Swipe to see what PageView can do."), - ], - ), - ), - ft.Container( - bgcolor=ft.Colors.PINK_300, - content=ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Icon(ft.Icons.ANIMATION, size=72), - ft.Text( - "Viewport fraction lets you peek at the next slide." - ), - ], - ), - ), - ft.Container( - bgcolor=ft.Colors.TEAL_300, - content=ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Icon(ft.Icons.TOUCH_APP, size=72), - ft.Text("Use on_change to respond to page swipes."), - ], - ), - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/page_view/basic/main.py b/sdk/python/examples/controls/page_view/basic/main.py new file mode 100644 index 0000000000..61e6198b10 --- /dev/null +++ b/sdk/python/examples/controls/page_view/basic/main.py @@ -0,0 +1,59 @@ +import flet as ft + + +def main(page: ft.Page): + page.padding = 0 + + page.add( + ft.SafeArea( + expand=True, + content=ft.PageView( + expand=True, + viewport_fraction=0.9, + on_change=lambda e: print(f"Currently viewing page {int(e.data)}"), + selected_index=1, + horizontal=True, + controls=[ + ft.Container( + bgcolor=ft.Colors.INDIGO_400, + content=ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text("Welcome", size=40, weight=ft.FontWeight.BOLD), + ft.Text("Swipe to see what PageView can do."), + ], + ), + ), + ft.Container( + bgcolor=ft.Colors.PINK_300, + content=ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Icon(ft.Icons.ANIMATION, size=72), + ft.Text( + "Viewport fraction lets you peek at the next slide." + ), + ], + ), + ), + ft.Container( + bgcolor=ft.Colors.TEAL_300, + content=ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Icon(ft.Icons.TOUCH_APP, size=72), + ft.Text("Use on_change to respond to page swipes."), + ], + ), + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page_view/basic/pyproject.toml b/sdk/python/examples/controls/page_view/basic/pyproject.toml new file mode 100644 index 0000000000..7ae5f8826a --- /dev/null +++ b/sdk/python/examples/controls/page_view/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "page-view-basic" +version = "1.0.0" +description = "Demonstrates a swipeable PageView with page-change callback and peeked viewport pages." +requires-python = ">=3.10" +keywords = ["page view", "pageview", "swipe", "carousel", "callback"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/PageView"] + +[tool.flet.metadata] +title = "Basic" +controls = ["PageView", "SafeArea", "Container", "Column", "Text", "Icon"] +layout_pattern = "pager" +complexity = "basic" +features = ["horizontal swipe navigation", "page change callback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/page_view/programmatic_swipe.py b/sdk/python/examples/controls/page_view/programmatic_swipe.py deleted file mode 100644 index aaa58f653b..0000000000 --- a/sdk/python/examples/controls/page_view/programmatic_swipe.py +++ /dev/null @@ -1,69 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.padding = 0 - - async def show_previous_page(e: ft.Event[ft.FloatingActionButton]): - await view.previous_page( - animation_curve=ft.AnimationCurve.BOUNCE_OUT, - animation_duration=ft.Duration(seconds=1), - ) - - async def show_next_page(e: ft.Event[ft.FloatingActionButton]): - await view.next_page( - animation_curve=ft.AnimationCurve.BOUNCE_OUT, - animation_duration=ft.Duration(seconds=1), - ) - - page.floating_action_button = ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - wrap=True, - controls=[ - ft.FloatingActionButton( - icon=ft.Icons.SWIPE_LEFT, - on_click=show_previous_page, - tooltip="Previous Page", - ), - ft.FloatingActionButton( - icon=ft.Icons.SWIPE_RIGHT, - on_click=show_next_page, - tooltip="Next Page", - ), - ], - ) - - page.add( - view := ft.PageView( - expand=True, - viewport_fraction=0.9, - selected_index=1, - horizontal=True, - controls=[ - ft.Container( - bgcolor=bgcolor, - content=ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(f"Page {idx}", size=55, weight=ft.FontWeight.BOLD), - ], - ), - ) - for idx, bgcolor in enumerate( - [ - ft.Colors.RED_800, - ft.Colors.BLUE_800, - ft.Colors.GREEN_800, - ft.Colors.ORANGE_800, - ft.Colors.PURPLE_800, - ft.Colors.PINK_800, - ] - ) - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/page_view/programmatic_swipe/main.py b/sdk/python/examples/controls/page_view/programmatic_swipe/main.py new file mode 100644 index 0000000000..8dca9fae2a --- /dev/null +++ b/sdk/python/examples/controls/page_view/programmatic_swipe/main.py @@ -0,0 +1,78 @@ +import flet as ft + + +def main(page: ft.Page): + page.padding = 0 + + async def show_previous_page(e: ft.Event[ft.FloatingActionButton]): + await view.previous_page( + animation_curve=ft.AnimationCurve.BOUNCE_OUT, + animation_duration=ft.Duration(seconds=1), + ) + + async def show_next_page(e: ft.Event[ft.FloatingActionButton]): + await view.next_page( + animation_curve=ft.AnimationCurve.BOUNCE_OUT, + animation_duration=ft.Duration(seconds=1), + ) + + page.floating_action_button = ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + wrap=True, + controls=[ + ft.FloatingActionButton( + icon=ft.Icons.SWIPE_LEFT, + on_click=show_previous_page, + tooltip="Previous Page", + ), + ft.FloatingActionButton( + icon=ft.Icons.SWIPE_RIGHT, + on_click=show_next_page, + tooltip="Next Page", + ), + ], + ) + + page.add( + ft.SafeArea( + expand=True, + content=( + view := ft.PageView( + expand=True, + viewport_fraction=0.9, + selected_index=1, + horizontal=True, + controls=[ + ft.Container( + bgcolor=bgcolor, + content=ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text( + f"Page {idx}", + size=55, + weight=ft.FontWeight.BOLD, + ), + ], + ), + ) + for idx, bgcolor in enumerate( + [ + ft.Colors.RED_800, + ft.Colors.BLUE_800, + ft.Colors.GREEN_800, + ft.Colors.ORANGE_800, + ft.Colors.PURPLE_800, + ft.Colors.PINK_800, + ] + ) + ], + ) + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/page_view/programmatic_swipe/pyproject.toml b/sdk/python/examples/controls/page_view/programmatic_swipe/pyproject.toml new file mode 100644 index 0000000000..83b969da04 --- /dev/null +++ b/sdk/python/examples/controls/page_view/programmatic_swipe/pyproject.toml @@ -0,0 +1,40 @@ +[project] +name = "page-view-programmatic-swipe" +version = "1.0.0" +description = "Demonstrates programmatic PageView navigation via previous/next actions." +requires-python = ">=3.10" +keywords = [ + "page view", + "pageview", + "programmatic swipe", + "floating action button", + "async", +] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/PageView"] + +[tool.flet.metadata] +title = "Programmatic Swipe" +controls = [ + "PageView", + "SafeArea", + "Container", + "Column", + "Text", + "Row", + "FloatingActionButton", +] +layout_pattern = "programmatic-controls" +complexity = "basic" +features = ["programmatic page navigation", "manual swipe control"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/pagelet/__init__.py b/sdk/python/examples/controls/pagelet/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/pagelet/basic.py b/sdk/python/examples/controls/pagelet/basic.py deleted file mode 100644 index 53784803cb..0000000000 --- a/sdk/python/examples/controls/pagelet/basic.py +++ /dev/null @@ -1,79 +0,0 @@ -import asyncio - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.MainAxisAlignment.CENTER - page.vertical_alignment = ft.CrossAxisAlignment.CENTER - - async def handle_show_drawer(e: ft.Event[ft.FloatingActionButton]): - await pagelet.show_drawer() - - async def handle_show_end_drawer(e: ft.Event[ft.Button]): - await pagelet.show_end_drawer() - await asyncio.sleep(3) - await pagelet.close_end_drawer() - - page.add( - pagelet := ft.Pagelet( - width=500, - height=500, - appbar=ft.AppBar( - title=ft.Text("Pagelet AppBar"), - center_title=True, - bgcolor=ft.Colors.RED_500, - ), - content=ft.Text("Pagelet Body"), - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, - bottom_appbar=ft.BottomAppBar( - bgcolor=ft.Colors.BLUE, - shape=ft.CircularRectangleNotchShape(), - content=ft.Row( - controls=[ - ft.IconButton(icon=ft.Icons.MENU, icon_color=ft.Colors.WHITE), - ft.Container(expand=True), - ft.IconButton(icon=ft.Icons.SEARCH, icon_color=ft.Colors.WHITE), - ft.IconButton( - icon=ft.Icons.FAVORITE, icon_color=ft.Colors.WHITE - ), - ] - ), - ), - drawer=ft.NavigationDrawer( - on_dismiss=lambda e: print("Drawer dismissed"), - controls=[ - ft.NavigationDrawerDestination( - icon=ft.Icons.ADD_TO_HOME_SCREEN_SHARP, - label="Item 1", - ), - ft.NavigationDrawerDestination( - icon=ft.Icons.ADD_COMMENT, - label="Item 2", - ), - ], - ), - end_drawer=ft.NavigationDrawer( - on_dismiss=lambda e: print("End Drawer dismissed"), - controls=[ - ft.NavigationDrawerDestination( - icon=ft.Icons.SLOW_MOTION_VIDEO, - label="Item 3", - ), - ft.NavigationDrawerDestination( - icon=ft.Icons.INSERT_CHART, - label="Item 4", - ), - ], - ), - floating_action_button=ft.FloatingActionButton( - icon=ft.Icons.ADD, - shape=ft.CircleBorder(), - ), - floating_action_button_location=ft.FloatingActionButtonLocation.CENTER_DOCKED, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/pagelet/basic/main.py b/sdk/python/examples/controls/pagelet/basic/main.py new file mode 100644 index 0000000000..16aba792e2 --- /dev/null +++ b/sdk/python/examples/controls/pagelet/basic/main.py @@ -0,0 +1,87 @@ +import asyncio + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.MainAxisAlignment.CENTER + page.vertical_alignment = ft.CrossAxisAlignment.CENTER + + async def handle_show_drawer(e: ft.Event[ft.FloatingActionButton]): + await pagelet.show_drawer() + + async def handle_show_end_drawer(e: ft.Event[ft.Button]): + await pagelet.show_end_drawer() + await asyncio.sleep(3) + await pagelet.close_end_drawer() + + page.add( + ft.SafeArea( + content=( + pagelet := ft.Pagelet( + width=500, + height=500, + appbar=ft.AppBar( + title=ft.Text("Pagelet AppBar"), + center_title=True, + bgcolor=ft.Colors.RED_500, + ), + content=ft.Text("Pagelet Body"), + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, + bottom_appbar=ft.BottomAppBar( + bgcolor=ft.Colors.BLUE, + shape=ft.CircularRectangleNotchShape(), + content=ft.Row( + controls=[ + ft.IconButton( + icon=ft.Icons.MENU, icon_color=ft.Colors.WHITE + ), + ft.Container(expand=True), + ft.IconButton( + icon=ft.Icons.SEARCH, icon_color=ft.Colors.WHITE + ), + ft.IconButton( + icon=ft.Icons.FAVORITE, icon_color=ft.Colors.WHITE + ), + ], + ), + ), + drawer=ft.NavigationDrawer( + on_dismiss=lambda e: print("Drawer dismissed"), + controls=[ + ft.NavigationDrawerDestination( + icon=ft.Icons.ADD_TO_HOME_SCREEN_SHARP, + label="Item 1", + ), + ft.NavigationDrawerDestination( + icon=ft.Icons.ADD_COMMENT, + label="Item 2", + ), + ], + ), + end_drawer=ft.NavigationDrawer( + on_dismiss=lambda e: print("End Drawer dismissed"), + controls=[ + ft.NavigationDrawerDestination( + icon=ft.Icons.SLOW_MOTION_VIDEO, + label="Item 3", + ), + ft.NavigationDrawerDestination( + icon=ft.Icons.INSERT_CHART, + label="Item 4", + ), + ], + ), + floating_action_button=ft.FloatingActionButton( + icon=ft.Icons.ADD, + shape=ft.CircleBorder(), + ), + floating_action_button_location=ft.FloatingActionButtonLocation.CENTER_DOCKED, + ) + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/pagelet/basic/pyproject.toml b/sdk/python/examples/controls/pagelet/basic/pyproject.toml new file mode 100644 index 0000000000..781caf594c --- /dev/null +++ b/sdk/python/examples/controls/pagelet/basic/pyproject.toml @@ -0,0 +1,45 @@ +[project] +name = "pagelet-basic" +version = "1.0.0" +description = "Shows a Pagelet configured with app bars, drawers, and a floating action button." +requires-python = ">=3.10" +keywords = [ + "pagelet", + "navigation drawer", + "app bar", + "floating action button", + "async", +] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Pagelet"] + +[tool.flet.metadata] +title = "Basic" +controls = [ + "Pagelet", + "SafeArea", + "AppBar", + "BottomAppBar", + "NavigationDrawer", + "NavigationDrawerDestination", + "FloatingActionButton", + "Text", + "IconButton", + "Row", + "Column", + "Container", +] +layout_pattern = "app-shell" +complexity = "basic" +features = ["drawer navigation", "floating action behavior", "async"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/pagelet/basic_declarative.py b/sdk/python/examples/controls/pagelet/basic_declarative.py deleted file mode 100644 index d144440c27..0000000000 --- a/sdk/python/examples/controls/pagelet/basic_declarative.py +++ /dev/null @@ -1,75 +0,0 @@ -import flet as ft - - -@ft.component -def App(): - async def handle_show_drawer(): - await p.show_drawer() - - async def handle_show_end_drawer(): - await p.show_end_drawer() - - p = ft.Pagelet( - width=400, - height=400, - appbar=ft.AppBar( - title=ft.Text("Pagelet AppBar Title"), - bgcolor=ft.Colors.AMBER_ACCENT, - ), - content=ft.Container( - ft.Column( - [ - ft.Text("Pagelet Body"), - ft.Button("Show end drawer", on_click=handle_show_end_drawer), - ] - ), - padding=ft.Padding.all(16), - ), - bgcolor=ft.Colors.AMBER_100, - bottom_appbar=ft.BottomAppBar( - bgcolor=ft.Colors.BLUE, - shape=ft.CircularRectangleNotchShape(), - content=ft.Row( - controls=[ - ft.IconButton(icon=ft.Icons.MENU, icon_color=ft.Colors.WHITE), - ft.Container(expand=True), - ft.IconButton(icon=ft.Icons.SEARCH, icon_color=ft.Colors.WHITE), - ft.IconButton(icon=ft.Icons.FAVORITE, icon_color=ft.Colors.WHITE), - ] - ), - ), - drawer=ft.NavigationDrawer( - controls=[ - ft.NavigationDrawerDestination( - icon=ft.Icons.ADD_TO_HOME_SCREEN_SHARP, - label="Item 1", - ), - ft.NavigationDrawerDestination( - icon=ft.Icons.ADD_COMMENT, - label="Item 2", - ), - ], - ), - end_drawer=ft.NavigationDrawer( - controls=[ - ft.NavigationDrawerDestination( - icon=ft.Icons.SLOW_MOTION_VIDEO, - label="Item 3", - ), - ft.NavigationDrawerDestination( - icon=ft.Icons.INSERT_CHART, - label="Item 4", - ), - ], - ), - floating_action_button=ft.FloatingActionButton( - content="Open", - shape=ft.CircleBorder(), - on_click=handle_show_drawer, - ), - floating_action_button_location=ft.FloatingActionButtonLocation.CENTER_DOCKED, - ) - return p - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/controls/pagelet/basic_declarative/main.py b/sdk/python/examples/controls/pagelet/basic_declarative/main.py new file mode 100644 index 0000000000..a1bb723132 --- /dev/null +++ b/sdk/python/examples/controls/pagelet/basic_declarative/main.py @@ -0,0 +1,81 @@ +import flet as ft + + +@ft.component +def App(): + async def handle_show_drawer(): + await p.show_drawer() + + async def handle_show_end_drawer(): + await p.show_end_drawer() + + p = ft.Pagelet( + width=400, + height=400, + appbar=ft.AppBar( + title=ft.Text("Pagelet AppBar Title"), + bgcolor=ft.Colors.AMBER_ACCENT, + ), + content=ft.Container( + ft.Column( + [ + ft.Text("Pagelet Body"), + ft.Button("Show end drawer", on_click=handle_show_end_drawer), + ] + ), + padding=ft.Padding.all(16), + ), + bgcolor=ft.Colors.AMBER_100, + bottom_appbar=ft.BottomAppBar( + bgcolor=ft.Colors.BLUE, + shape=ft.CircularRectangleNotchShape(), + content=ft.Row( + controls=[ + ft.IconButton(icon=ft.Icons.MENU, icon_color=ft.Colors.WHITE), + ft.Container(expand=True), + ft.IconButton(icon=ft.Icons.SEARCH, icon_color=ft.Colors.WHITE), + ft.IconButton(icon=ft.Icons.FAVORITE, icon_color=ft.Colors.WHITE), + ] + ), + ), + drawer=ft.NavigationDrawer( + controls=[ + ft.NavigationDrawerDestination( + icon=ft.Icons.ADD_TO_HOME_SCREEN_SHARP, + label="Item 1", + ), + ft.NavigationDrawerDestination( + icon=ft.Icons.ADD_COMMENT, + label="Item 2", + ), + ], + ), + end_drawer=ft.NavigationDrawer( + controls=[ + ft.NavigationDrawerDestination( + icon=ft.Icons.SLOW_MOTION_VIDEO, + label="Item 3", + ), + ft.NavigationDrawerDestination( + icon=ft.Icons.INSERT_CHART, + label="Item 4", + ), + ], + ), + floating_action_button=ft.FloatingActionButton( + content="Open", + shape=ft.CircleBorder(), + on_click=handle_show_drawer, + ), + floating_action_button_location=ft.FloatingActionButtonLocation.CENTER_DOCKED, + ) + return ft.SafeArea(content=p) + + +def main(page: ft.Page): + page.title = "Pagelet example" + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/pagelet/basic_declarative/pyproject.toml b/sdk/python/examples/controls/pagelet/basic_declarative/pyproject.toml new file mode 100644 index 0000000000..4522c4dfbe --- /dev/null +++ b/sdk/python/examples/controls/pagelet/basic_declarative/pyproject.toml @@ -0,0 +1,46 @@ +[project] +name = "pagelet-basic-declarative" +version = "1.0.0" +description = "Demonstrates a declarative Pagelet with drawers and an app shell." +requires-python = ">=3.10" +keywords = [ + "pagelet", + "declarative", + "navigation drawer", + "component", + "material", + "async", +] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Pagelet"] + +[tool.flet.metadata] +title = "Basic Declarative" +controls = [ + "Pagelet", + "SafeArea", + "AppBar", + "BottomAppBar", + "NavigationDrawer", + "NavigationDrawerDestination", + "FloatingActionButton", + "Button", + "Text", + "Row", + "IconButton", + "Container", +] +layout_pattern = "app-shell" +complexity = "basic" +features = ["declarative UI composition", "drawer navigation", "async"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/placeholder/__init__.py b/sdk/python/examples/controls/placeholder/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/placeholder/basic.py b/sdk/python/examples/controls/placeholder/basic.py deleted file mode 100644 index d9473d9a5b..0000000000 --- a/sdk/python/examples/controls/placeholder/basic.py +++ /dev/null @@ -1,17 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Placeholder( - expand=True, - color=ft.Colors.GREEN_ACCENT, - fallback_height=200, - fallback_width=300, - stroke_width=20, - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/placeholder/basic/main.py b/sdk/python/examples/controls/placeholder/basic/main.py new file mode 100644 index 0000000000..999d1af536 --- /dev/null +++ b/sdk/python/examples/controls/placeholder/basic/main.py @@ -0,0 +1,20 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + expand=True, + content=ft.Placeholder( + expand=True, + color=ft.Colors.GREEN_ACCENT, + fallback_height=200, + fallback_width=300, + stroke_width=20, + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/placeholder/basic/pyproject.toml b/sdk/python/examples/controls/placeholder/basic/pyproject.toml new file mode 100644 index 0000000000..f85545a582 --- /dev/null +++ b/sdk/python/examples/controls/placeholder/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "placeholder-basic" +version = "1.0.0" +description = "Shows a basic Placeholder control with fallback dimensions and custom styling." +requires-python = ">=3.10" +keywords = ["placeholder", "layout", "fallback size", "ui shell"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Placeholder"] + +[tool.flet.metadata] +title = "Basic" +controls = ["Placeholder", "SafeArea"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["placeholder layout", "fallback dimensions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/popup_menu_button/__init__.py b/sdk/python/examples/controls/popup_menu_button/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/popup_menu_button/basic.py b/sdk/python/examples/controls/popup_menu_button/basic.py deleted file mode 100644 index c81b54bc59..0000000000 --- a/sdk/python/examples/controls/popup_menu_button/basic.py +++ /dev/null @@ -1,36 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_check_item_click(e: ft.Event[ft.PopupMenuItem]): - e.control.checked = not e.control.checked - page.update() - - page.add( - ft.PopupMenuButton( - key="popup", - items=[ - ft.PopupMenuItem(content="Item 1"), - ft.PopupMenuItem(icon=ft.Icons.POWER_INPUT, content="Check power"), - ft.PopupMenuItem( - content=ft.Row( - controls=[ - ft.Icon(ft.Icons.HOURGLASS_TOP_OUTLINED), - ft.Text("Item with a custom content"), - ] - ), - on_click=lambda _: print("Button with custom content clicked!"), - ), - ft.PopupMenuItem(), # divider - ft.PopupMenuItem( - content="Checked item", - checked=False, - on_click=handle_check_item_click, - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/popup_menu_button/basic/main.py b/sdk/python/examples/controls/popup_menu_button/basic/main.py new file mode 100644 index 0000000000..4d2474e8de --- /dev/null +++ b/sdk/python/examples/controls/popup_menu_button/basic/main.py @@ -0,0 +1,37 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_check_item_click(e: ft.Event[ft.PopupMenuItem]): + e.control.checked = not e.control.checked + + page.add( + ft.SafeArea( + content=ft.PopupMenuButton( + key="popup", + items=[ + ft.PopupMenuItem(content="Item 1"), + ft.PopupMenuItem(icon=ft.Icons.POWER_INPUT, content="Check power"), + ft.PopupMenuItem( + content=ft.Row( + controls=[ + ft.Icon(ft.Icons.HOURGLASS_TOP_OUTLINED), + ft.Text("Item with a custom content"), + ] + ), + on_click=lambda _: print("Button with custom content clicked!"), + ), + ft.PopupMenuItem(), # divider + ft.PopupMenuItem( + content="Checked item", + checked=False, + on_click=handle_check_item_click, + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/popup_menu_button/basic/pyproject.toml b/sdk/python/examples/controls/popup_menu_button/basic/pyproject.toml new file mode 100644 index 0000000000..ae70c59181 --- /dev/null +++ b/sdk/python/examples/controls/popup_menu_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "popup-menu-button-basic" +version = "1.0.0" +description = "Shows a PopupMenuButton with text, icon, custom content, and checked menu items." +requires-python = ">=3.10" +keywords = ["popup menu", "popup menu button", "menu", "material", "button"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/PopupMenuButton"] + +[tool.flet.metadata] +title = "Basic" +controls = ["PopupMenuButton", "PopupMenuItem", "SafeArea", "Row", "Icon", "Text"] +layout_pattern = "overlay-action" +complexity = "basic" +features = ["menu actions", "custom menu content", "checkable menu item"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/progress_bar/__init__.py b/sdk/python/examples/controls/progress_bar/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/progress_bar/determinate_and_indeterminate.py b/sdk/python/examples/controls/progress_bar/determinate_and_indeterminate.py deleted file mode 100644 index 62c2973ae5..0000000000 --- a/sdk/python/examples/controls/progress_bar/determinate_and_indeterminate.py +++ /dev/null @@ -1,32 +0,0 @@ -import asyncio - -import flet as ft - - -async def main(page: ft.Page): - determinate_bar = ft.ProgressBar(width=400) - determinate_message = ft.Text("Doing something...") - - page.add( - ft.Text( - value="Linear progress indicator", - theme_style=ft.TextThemeStyle.HEADLINE_SMALL, - ), - ft.Column(controls=[determinate_message, determinate_bar]), - ft.Text( - value="Indeterminate progress bar", - theme_style=ft.TextThemeStyle.HEADLINE_SMALL, - ), - ft.ProgressBar(width=400, color=ft.Colors.AMBER), - ) - - for i in range(0, 101): - determinate_bar.value = i * 0.01 - await asyncio.sleep(0.1) - if i == 100: - determinate_message.value = "Finished!" - page.update() - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/progress_bar/determinate_and_indeterminate/main.py b/sdk/python/examples/controls/progress_bar/determinate_and_indeterminate/main.py new file mode 100644 index 0000000000..fdf4f9ae5a --- /dev/null +++ b/sdk/python/examples/controls/progress_bar/determinate_and_indeterminate/main.py @@ -0,0 +1,38 @@ +import asyncio + +import flet as ft + + +async def main(page: ft.Page): + determinate_bar = ft.ProgressBar(width=400) + determinate_message = ft.Text("Doing something...") + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + value="Linear progress indicator", + theme_style=ft.TextThemeStyle.HEADLINE_SMALL, + ), + ft.Column(controls=[determinate_message, determinate_bar]), + ft.Text( + value="Indeterminate progress bar", + theme_style=ft.TextThemeStyle.HEADLINE_SMALL, + ), + ft.ProgressBar(width=400, color=ft.Colors.AMBER), + ] + ) + ) + ) + + for i in range(0, 101): + determinate_bar.value = i * 0.01 + await asyncio.sleep(0.1) + if i == 100: + determinate_message.value = "Finished!" + page.update() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/progress_bar/determinate_and_indeterminate/pyproject.toml b/sdk/python/examples/controls/progress_bar/determinate_and_indeterminate/pyproject.toml new file mode 100644 index 0000000000..89a80866c8 --- /dev/null +++ b/sdk/python/examples/controls/progress_bar/determinate_and_indeterminate/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "progress-bar-determinate-and-indeterminate" +version = "1.0.0" +description = "Animates a determinate and an indeterminate linear ProgressBar." +requires-python = ">=3.10" +keywords = ["progress bar", "progress", "linear progress", "async", "loading"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/ProgressBar"] + +[tool.flet.metadata] +title = "Determinate and Indeterminate" +controls = ["ProgressBar", "SafeArea", "Column", "Text"] +layout_pattern = "status-dashboard" +complexity = "basic" +features = ["linear progress animation", "determinate updates", "indeterminate progress", "async"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/progress_ring/__init__.py b/sdk/python/examples/controls/progress_ring/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/progress_ring/determinate_and_indeterminate.py b/sdk/python/examples/controls/progress_ring/determinate_and_indeterminate.py deleted file mode 100644 index 15133cefc1..0000000000 --- a/sdk/python/examples/controls/progress_ring/determinate_and_indeterminate.py +++ /dev/null @@ -1,39 +0,0 @@ -import asyncio - -import flet as ft - - -async def main(page: ft.Page): - page.add( - ft.Text( - value="Circular progress indicator", - theme_style=ft.TextThemeStyle.HEADLINE_SMALL, - ), - ft.Row( - controls=[ - determinate_ring := ft.ProgressRing( - width=16, height=16, stroke_width=2 - ), - determinate_message := ft.Text("Wait for the completion..."), - ] - ), - ft.Text( - value="Indeterminate cicrular progress", - theme_style=ft.TextThemeStyle.HEADLINE_SMALL, - ), - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ft.ProgressRing(), ft.Text("I'm going to run for ages...")], - ), - ) - - for i in range(0, 101): - determinate_ring.value = i * 0.01 - await asyncio.sleep(0.1) - if i == 100: - determinate_message.value = "Finished!" - page.update() - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/progress_ring/determinate_and_indeterminate/main.py b/sdk/python/examples/controls/progress_ring/determinate_and_indeterminate/main.py new file mode 100644 index 0000000000..2d8d55d955 --- /dev/null +++ b/sdk/python/examples/controls/progress_ring/determinate_and_indeterminate/main.py @@ -0,0 +1,50 @@ +import asyncio + +import flet as ft + + +async def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + value="Circular progress indicator", + theme_style=ft.TextThemeStyle.HEADLINE_SMALL, + ), + ft.Row( + controls=[ + determinate_ring := ft.ProgressRing( + width=16, height=16, stroke_width=2 + ), + determinate_message := ft.Text( + "Wait for the completion..." + ), + ] + ), + ft.Text( + value="Indeterminate circular progress", + theme_style=ft.TextThemeStyle.HEADLINE_SMALL, + ), + ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.ProgressRing(), + ft.Text("I'm going to run for ages..."), + ], + ), + ] + ) + ) + ) + + for i in range(0, 101): + determinate_ring.value = i * 0.01 + await asyncio.sleep(0.1) + if i == 100: + determinate_message.value = "Finished!" + page.update() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/progress_ring/determinate_and_indeterminate/pyproject.toml b/sdk/python/examples/controls/progress_ring/determinate_and_indeterminate/pyproject.toml new file mode 100644 index 0000000000..349fd80e13 --- /dev/null +++ b/sdk/python/examples/controls/progress_ring/determinate_and_indeterminate/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "progress-ring-determinate-and-indeterminate" +version = "1.0.0" +description = "Shows determinate and indeterminate circular progress indicators." +requires-python = ">=3.10" +keywords = ["progress ring", "circular progress", "progress", "async", "loading"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/ProgressRing"] + +[tool.flet.metadata] +title = "Determinate and Indeterminate" +controls = ["ProgressRing", "SafeArea", "Column", "Row", "Text"] +layout_pattern = "status-dashboard" +complexity = "basic" +features = ["circular progress animation", "determinate updates", "indeterminate progress", "async"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/progress_ring/gauge_with_progress.py b/sdk/python/examples/controls/progress_ring/gauge_with_progress.py deleted file mode 100644 index d4d811b18a..0000000000 --- a/sdk/python/examples/controls/progress_ring/gauge_with_progress.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Stack( - width=100, - height=100, - controls=[ - ft.Container(content=ft.Text("60%"), alignment=ft.Alignment.CENTER), - ft.ProgressRing(value=0.6, width=100, height=100), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/progress_ring/gauge_with_progress/main.py b/sdk/python/examples/controls/progress_ring/gauge_with_progress/main.py new file mode 100644 index 0000000000..be375e3a8c --- /dev/null +++ b/sdk/python/examples/controls/progress_ring/gauge_with_progress/main.py @@ -0,0 +1,20 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Stack( + width=100, + height=100, + controls=[ + ft.Container(content=ft.Text("60%"), alignment=ft.Alignment.CENTER), + ft.ProgressRing(value=0.6, width=100, height=100), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/progress_ring/gauge_with_progress/pyproject.toml b/sdk/python/examples/controls/progress_ring/gauge_with_progress/pyproject.toml new file mode 100644 index 0000000000..8fe45302f4 --- /dev/null +++ b/sdk/python/examples/controls/progress_ring/gauge_with_progress/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "progress-ring-gauge-with-progress" +version = "1.0.0" +description = "Shows a fixed gauge-style ProgressRing with an overlaid percentage label." +requires-python = ">=3.10" +keywords = ["progress ring", "gauge", "circular progress", "static progress"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Displays/ProgressRing"] + +[tool.flet.metadata] +title = "Gauge with Progress" +controls = ["ProgressRing", "SafeArea", "Stack", "Container", "Text"] +layout_pattern = "status-dashboard" +complexity = "basic" +features = ["gauge-style indicator", "overlay text"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/radio/__init__.py b/sdk/python/examples/controls/radio/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/radio/basic.py b/sdk/python/examples/controls/radio/basic.py deleted file mode 100644 index 3626560072..0000000000 --- a/sdk/python/examples/controls/radio/basic.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_button_click(e: ft.Event[ft.Button]): - message.value = f"Your favorite color is: {group.value}" - page.update() - - page.add( - ft.Text("Select your favorite color:"), - group := ft.RadioGroup( - content=ft.Column( - controls=[ - ft.Radio(value="red", label="Red"), - ft.Radio(value="green", label="Green"), - ft.Radio(value="blue", label="Blue"), - ] - ) - ), - ft.Button(content="Submit", on_click=handle_button_click), - message := ft.Text(), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/radio/basic/main.py b/sdk/python/examples/controls/radio/basic/main.py new file mode 100644 index 0000000000..796b12125a --- /dev/null +++ b/sdk/python/examples/controls/radio/basic/main.py @@ -0,0 +1,31 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_button_click(e: ft.Event[ft.Button]): + message.value = f"Your favorite color is: {group.value}" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Select your favorite color:"), + group := ft.RadioGroup( + content=ft.Column( + controls=[ + ft.Radio(value="red", label="Red"), + ft.Radio(value="green", label="Green"), + ft.Radio(value="blue", label="Blue"), + ] + ) + ), + ft.Button(content="Submit", on_click=handle_button_click), + message := ft.Text(), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/radio/basic/pyproject.toml b/sdk/python/examples/controls/radio/basic/pyproject.toml new file mode 100644 index 0000000000..5a4b37129d --- /dev/null +++ b/sdk/python/examples/controls/radio/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "radio-basic" +version = "1.0.0" +description = "Shows radio selection with a submit action and simple message display." +requires-python = ">=3.10" +keywords = ["radio", "radio group", "selection", "input controls"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Radio"] + +[tool.flet.metadata] +title = "Basic" +controls = ["RadioGroup", "Radio", "Button", "Text", "SafeArea"] +layout_pattern = "form" +complexity = "basic" +features = ["radio selection", "form submission"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/radio/handling_selection_changes.py b/sdk/python/examples/controls/radio/handling_selection_changes.py deleted file mode 100644 index fef5632f5c..0000000000 --- a/sdk/python/examples/controls/radio/handling_selection_changes.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_selection_change(e: ft.Event[ft.RadioGroup]): - message.value = f"Your favorite color is: {e.control.value}" - page.update() - - page.add( - ft.Text("Select your favorite color:"), - ft.RadioGroup( - on_change=handle_selection_change, - content=ft.Column( - controls=[ - ft.Radio(value="red", label="Red"), - ft.Radio(value="green", label="Green"), - ft.Radio(value="blue", label="Blue"), - ] - ), - ), - message := ft.Text(), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/radio/handling_selection_changes/main.py b/sdk/python/examples/controls/radio/handling_selection_changes/main.py new file mode 100644 index 0000000000..4dbe2cf358 --- /dev/null +++ b/sdk/python/examples/controls/radio/handling_selection_changes/main.py @@ -0,0 +1,31 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_selection_change(e: ft.Event[ft.RadioGroup]): + message.value = f"Your favorite color is: {e.control.value}" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Select your favorite color:"), + ft.RadioGroup( + on_change=handle_selection_change, + content=ft.Column( + controls=[ + ft.Radio(value="red", label="Red"), + ft.Radio(value="green", label="Green"), + ft.Radio(value="blue", label="Blue"), + ] + ), + ), + message := ft.Text(), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/radio/handling_selection_changes/pyproject.toml b/sdk/python/examples/controls/radio/handling_selection_changes/pyproject.toml new file mode 100644 index 0000000000..997042fc58 --- /dev/null +++ b/sdk/python/examples/controls/radio/handling_selection_changes/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "radio-handling-selection-changes" +version = "1.0.0" +description = "Shows live RadioGroup selection updates using on_change events." +requires-python = ">=3.10" +keywords = ["radio", "selection changes", "on_change", "input controls"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Radio"] + +[tool.flet.metadata] +title = "Handling Selection Changes" +controls = ["RadioGroup", "Radio", "Text", "SafeArea"] +layout_pattern = "form" +complexity = "basic" +features = ["selection change handling", "reactive feedback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/radio/styled.py b/sdk/python/examples/controls/radio/styled.py deleted file mode 100644 index 6188ee0307..0000000000 --- a/sdk/python/examples/controls/radio/styled.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.RadioGroup( - ft.Column( - controls=[ - ft.Radio(label="Radio with default style", value="1"), - ft.Radio( - label="Radio with constant fill color", - value="2", - fill_color=ft.Colors.RED, - ), - ft.Radio( - label="Radio with dynamic fill color", - value="3", - fill_color={ - ft.ControlState.HOVERED: ft.Colors.BLUE, - ft.ControlState.SELECTED: ft.Colors.GREEN, - ft.ControlState.DEFAULT: ft.Colors.RED, - }, - ), - ] - ) - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/radio/styled/main.py b/sdk/python/examples/controls/radio/styled/main.py new file mode 100644 index 0000000000..005b614e6e --- /dev/null +++ b/sdk/python/examples/controls/radio/styled/main.py @@ -0,0 +1,33 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.RadioGroup( + ft.Column( + controls=[ + ft.Radio(label="Radio with default style", value="1"), + ft.Radio( + label="Radio with constant fill color", + value="2", + fill_color=ft.Colors.RED, + ), + ft.Radio( + label="Radio with dynamic fill color", + value="3", + fill_color={ + ft.ControlState.HOVERED: ft.Colors.BLUE, + ft.ControlState.SELECTED: ft.Colors.GREEN, + ft.ControlState.DEFAULT: ft.Colors.RED, + }, + ), + ] + ) + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/radio/styled/pyproject.toml b/sdk/python/examples/controls/radio/styled/pyproject.toml new file mode 100644 index 0000000000..206a8092f6 --- /dev/null +++ b/sdk/python/examples/controls/radio/styled/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "radio-styled" +version = "1.0.0" +description = "Demonstrates styled Radio controls with static and state-based colors." +requires-python = ">=3.10" +keywords = ["radio", "styled controls", "control states", "input controls"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Radio"] + +[tool.flet.metadata] +title = "Styled" +controls = ["Radio", "RadioGroup", "SafeArea"] +layout_pattern = "form" +complexity = "basic" +features = ["state-based styling", "radio group styling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/range_slider/__init__.py b/sdk/python/examples/controls/range_slider/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/range_slider/basic.py b/sdk/python/examples/controls/range_slider/basic.py deleted file mode 100644 index 0f458f4477..0000000000 --- a/sdk/python/examples/controls/range_slider/basic.py +++ /dev/null @@ -1,32 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text( - value="Range slider with divisions and labels", - size=20, - weight=ft.FontWeight.BOLD, - ), - ft.Container(height=30), - ft.RangeSlider( - min=0, - max=50, - start_value=10, - divisions=10, - end_value=20, - inactive_color=ft.Colors.GREEN_300, - active_color=ft.Colors.GREEN_700, - overlay_color=ft.Colors.GREEN_100, - label="{value}", - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/range_slider/basic/main.py b/sdk/python/examples/controls/range_slider/basic/main.py new file mode 100644 index 0000000000..c5512ca934 --- /dev/null +++ b/sdk/python/examples/controls/range_slider/basic/main.py @@ -0,0 +1,33 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + value="Range slider with divisions and labels", + size=20, + weight=ft.FontWeight.BOLD, + ), + ft.Container(height=30), + ft.RangeSlider( + min=0, + max=50, + start_value=10, + divisions=10, + end_value=20, + inactive_color=ft.Colors.GREEN_300, + active_color=ft.Colors.GREEN_700, + overlay_color=ft.Colors.GREEN_100, + label="{value}", + ), + ] + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/range_slider/basic/pyproject.toml b/sdk/python/examples/controls/range_slider/basic/pyproject.toml new file mode 100644 index 0000000000..a51f67b788 --- /dev/null +++ b/sdk/python/examples/controls/range_slider/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "range-slider-basic" +version = "1.0.0" +description = "Shows a configured RangeSlider with labels and divisions." +requires-python = ">=3.10" +keywords = ["range slider", "slider", "input controls", "selection range"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/RangeSlider"] + +[tool.flet.metadata] +title = "Basic" +controls = ["RangeSlider", "SafeArea", "Text", "Column", "Container"] +layout_pattern = "input-panel" +complexity = "basic" +features = ["range selection", "labeled ranges"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/range_slider/handling_change_events.py b/sdk/python/examples/controls/range_slider/handling_change_events.py deleted file mode 100644 index 7e0ce549c3..0000000000 --- a/sdk/python/examples/controls/range_slider/handling_change_events.py +++ /dev/null @@ -1,46 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.scroll = ft.ScrollMode.AUTO - - def handle_slider_change_start(e: ft.Event[ft.RangeSlider]): - print(f"on_change_start: {e.control.start_value}, {e.control.end_value}") - - def handle_slider_change(e: ft.Event[ft.RangeSlider]): - print(f"on_change: {e.control.start_value}, {e.control.end_value}") - - def handle_slider_change_end(e: ft.Event[ft.RangeSlider]): - print(f"on_change_end: {e.control.start_value}, {e.control.end_value}") - message.value = f"on_change_end: {e.control.start_value}, {e.control.end_value}" - page.update() - - page.add( - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text( - value="Range slider with events", - size=20, - weight=ft.FontWeight.BOLD, - ), - ft.Container(height=30), - ft.RangeSlider( - divisions=100, - min=0, - max=100, - start_value=10, - end_value=20, - on_change_start=handle_slider_change_start, - on_change=handle_slider_change, - on_change_end=handle_slider_change_end, - label="{value}%", - ), - message := ft.Text(), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/range_slider/handling_change_events/main.py b/sdk/python/examples/controls/range_slider/handling_change_events/main.py new file mode 100644 index 0000000000..44d74e3620 --- /dev/null +++ b/sdk/python/examples/controls/range_slider/handling_change_events/main.py @@ -0,0 +1,46 @@ +import flet as ft + + +def main(page: ft.Page): + page.scroll = ft.ScrollMode.AUTO + + def handle_slider_change_start(e: ft.Event[ft.RangeSlider]): + print(f"on_change_start: {e.control.start_value}, {e.control.end_value}") + + def handle_slider_change(e: ft.Event[ft.RangeSlider]): + print(f"on_change: {e.control.start_value}, {e.control.end_value}") + + def handle_slider_change_end(e: ft.Event[ft.RangeSlider]): + print(f"on_change_end: {e.control.start_value}, {e.control.end_value}") + message.value = f"on_change_end: {e.control.start_value}, {e.control.end_value}" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + value="Range slider with events", + size=20, + weight=ft.FontWeight.BOLD, + ), + ft.Container(height=30), + ft.RangeSlider( + divisions=100, + min=0, + max=100, + start_value=10, + end_value=20, + on_change_start=handle_slider_change_start, + on_change=handle_slider_change, + on_change_end=handle_slider_change_end, + label="{value}%", + ), + message := ft.Text(), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/range_slider/handling_change_events/pyproject.toml b/sdk/python/examples/controls/range_slider/handling_change_events/pyproject.toml new file mode 100644 index 0000000000..4f02ef78b0 --- /dev/null +++ b/sdk/python/examples/controls/range_slider/handling_change_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "range-slider-handling-change-events" +version = "1.0.0" +description = "Shows event callbacks for RangeSlider value changes." +requires-python = ">=3.10" +keywords = ["range slider", "events", "on_change", "async", "input controls"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/RangeSlider"] + +[tool.flet.metadata] +title = "Handling Change Events" +controls = ["RangeSlider", "SafeArea", "Text", "Column", "Container"] +layout_pattern = "input-panel" +complexity = "basic" +features = ["range change events", "callback updates", "scrollable content"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/reorderable_drag_handle/basic.py b/sdk/python/examples/controls/reorderable_drag_handle/basic.py deleted file mode 100644 index fc14b5f182..0000000000 --- a/sdk/python/examples/controls/reorderable_drag_handle/basic.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def on_reorder(e: ft.OnReorderEvent): - # Reorder controls list to match the UI change - e.control.controls.insert(e.new_index, e.control.controls.pop(e.old_index)) - - page.add( - ft.ReorderableListView( - expand=True, - show_default_drag_handles=False, - on_reorder=on_reorder, - controls=[ - ft.ListTile( - title=ft.Text(f"Draggable Item {i}", color=ft.Colors.BLACK), - leading=ft.ReorderableDragHandle( - content=ft.Icon(ft.Icons.DRAG_INDICATOR, color=ft.Colors.RED), - mouse_cursor=ft.MouseCursor.GRAB, - ), - bgcolor=ft.Colors.ERROR - if i % 2 == 0 - else ft.Colors.ON_ERROR_CONTAINER, - ) - for i in range(10) - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/reorderable_drag_handle/basic/main.py b/sdk/python/examples/controls/reorderable_drag_handle/basic/main.py new file mode 100644 index 0000000000..373661436d --- /dev/null +++ b/sdk/python/examples/controls/reorderable_drag_handle/basic/main.py @@ -0,0 +1,36 @@ +import flet as ft + + +def main(page: ft.Page): + def on_reorder(e: ft.OnReorderEvent): + # Reorder controls list to match the UI change + e.control.controls.insert(e.new_index, e.control.controls.pop(e.old_index)) + + page.add( + ft.SafeArea( + content=ft.ReorderableListView( + expand=True, + show_default_drag_handles=False, + on_reorder=on_reorder, + controls=[ + ft.ListTile( + title=ft.Text(f"Draggable Item {i}", color=ft.Colors.BLACK), + leading=ft.ReorderableDragHandle( + content=ft.Icon( + ft.Icons.DRAG_INDICATOR, color=ft.Colors.RED + ), + mouse_cursor=ft.MouseCursor.GRAB, + ), + bgcolor=ft.Colors.ERROR + if i % 2 == 0 + else ft.Colors.ON_ERROR_CONTAINER, + ) + for i in range(10) + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/reorderable_drag_handle/basic/pyproject.toml b/sdk/python/examples/controls/reorderable_drag_handle/basic/pyproject.toml new file mode 100644 index 0000000000..dc348247ad --- /dev/null +++ b/sdk/python/examples/controls/reorderable_drag_handle/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "reorderable-drag-handle-basic" +version = "1.0.0" +description = "Shows a ReorderableListView with custom drag handles." +requires-python = ">=3.10" +keywords = ["reorderable drag handle", "drag handle", "reorderable list"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Reorderable"] + +[tool.flet.metadata] +title = "Basic" +controls = ["ReorderableListView", "ReorderableDragHandle", "ListTile", "SafeArea"] +layout_pattern = "draggable-list" +complexity = "basic" +features = ["custom drag handles", "reorderable list items"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/reorderable_list_view/__init__.py b/sdk/python/examples/controls/reorderable_list_view/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/reorderable_list_view/horizontal_and_vertical.py b/sdk/python/examples/controls/reorderable_list_view/horizontal_and_vertical.py deleted file mode 100644 index 3f030d934a..0000000000 --- a/sdk/python/examples/controls/reorderable_list_view/horizontal_and_vertical.py +++ /dev/null @@ -1,51 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - # the primary color is the color of the reorder handle - page.theme = page.dark_theme = ft.Theme( - color_scheme=ft.ColorScheme(primary=ft.Colors.BLUE) - ) - - def handle_reorder(e: ft.OnReorderEvent): - # Reorder controls list to match the UI change - e.control.controls.insert(e.new_index, e.control.controls.pop(e.old_index)) - - def get_color(i): - return ft.Colors.ERROR if i % 2 == 0 else ft.Colors.ON_ERROR_CONTAINER - - page.add( - # horizontal - ft.ReorderableListView( - expand=True, - horizontal=True, - on_reorder=handle_reorder, - controls=[ - ft.Container( - content=ft.Text(f"Item {i}", color=ft.Colors.BLACK), - bgcolor=get_color(i), - margin=ft.Margin.symmetric(horizontal=5, vertical=10), - width=100, - alignment=ft.Alignment.CENTER, - ) - for i in range(10) - ], - ), - # vertical - ft.ReorderableListView( - expand=True, - on_reorder=handle_reorder, - controls=[ - ft.ListTile( - title=ft.Text(f"Item {i}", color=ft.Colors.BLACK), - leading=ft.Icon(ft.Icons.CHECK, color=ft.Colors.RED), - bgcolor=get_color(i), - ) - for i in range(10) - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/reorderable_list_view/horizontal_and_vertical/main.py b/sdk/python/examples/controls/reorderable_list_view/horizontal_and_vertical/main.py new file mode 100644 index 0000000000..13b121a95a --- /dev/null +++ b/sdk/python/examples/controls/reorderable_list_view/horizontal_and_vertical/main.py @@ -0,0 +1,57 @@ +import flet as ft + + +def main(page: ft.Page): + # the primary color is the color of the reorder handle + page.theme = page.dark_theme = ft.Theme( + color_scheme=ft.ColorScheme(primary=ft.Colors.BLUE) + ) + + def handle_reorder(e: ft.OnReorderEvent): + # Reorder controls list to match the UI change + e.control.controls.insert(e.new_index, e.control.controls.pop(e.old_index)) + + def get_color(i): + return ft.Colors.ERROR if i % 2 == 0 else ft.Colors.ON_ERROR_CONTAINER + + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + [ # horizontal + ft.ReorderableListView( + expand=True, + horizontal=True, + on_reorder=handle_reorder, + controls=[ + ft.Container( + content=ft.Text(f"Item {i}", color=ft.Colors.BLACK), + bgcolor=get_color(i), + margin=ft.Margin.symmetric(horizontal=5, vertical=10), + width=100, + alignment=ft.Alignment.CENTER, + ) + for i in range(10) + ], + ), + # vertical + ft.ReorderableListView( + expand=True, + on_reorder=handle_reorder, + controls=[ + ft.ListTile( + title=ft.Text(f"Item {i}", color=ft.Colors.BLACK), + leading=ft.Icon(ft.Icons.CHECK, color=ft.Colors.RED), + bgcolor=get_color(i), + ) + for i in range(10) + ], + ), + ] + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/responsive_row/__init__.py b/sdk/python/examples/controls/responsive_row/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/responsive_row/basic.py b/sdk/python/examples/controls/responsive_row/basic.py deleted file mode 100644 index 099d098c73..0000000000 --- a/sdk/python/examples/controls/responsive_row/basic.py +++ /dev/null @@ -1,82 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_page_resize(e: ft.PageResizeEvent): - pw.value = f"{page.width} px" - pw.update() - page.update() - - page.on_resize = handle_page_resize - - pw = ft.Text(text_align=ft.TextAlign.END, style=ft.TextTheme.display_small) - # page.overlay.append(pw) - - page.add( - ft.ResponsiveRow( - controls=[ - ft.Container( - content=ft.Text("Column 1"), - padding=5, - bgcolor=ft.Colors.YELLOW, - col={ - ft.ResponsiveRowBreakpoint.XS: 12, - ft.ResponsiveRowBreakpoint.MD: 6, - ft.ResponsiveRowBreakpoint.LG: 3, - }, - ), - ft.Container( - content=ft.Text("Column 2"), - padding=5, - bgcolor=ft.Colors.GREEN, - col={ - ft.ResponsiveRowBreakpoint.XS: 12, - ft.ResponsiveRowBreakpoint.MD: 6, - ft.ResponsiveRowBreakpoint.LG: 3, - }, - ), - ft.Container( - content=ft.Text("Column 3"), - padding=5, - bgcolor=ft.Colors.BLUE, - col={ - ft.ResponsiveRowBreakpoint.XS: 12, - ft.ResponsiveRowBreakpoint.MD: 6, - ft.ResponsiveRowBreakpoint.LG: 3, - }, - ), - ft.Container( - content=ft.Text("Column 4"), - padding=5, - bgcolor=ft.Colors.PINK_300, - col={ - ft.ResponsiveRowBreakpoint.XS: 12, - ft.ResponsiveRowBreakpoint.MD: 6, - ft.ResponsiveRowBreakpoint.LG: 3, - }, - ), - ], - ), - ft.ResponsiveRow( - run_spacing={ft.ResponsiveRowBreakpoint.XS: 10}, - controls=[ - ft.TextField( - label="TextField 1", - col={ft.ResponsiveRowBreakpoint.MD: 4}, - ), - ft.TextField( - label="TextField 2", - col={ft.ResponsiveRowBreakpoint.MD: 4}, - ), - ft.TextField( - label="TextField 3", - col={ft.ResponsiveRowBreakpoint.MD: 4}, - ), - ], - ), - pw, - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/responsive_row/basic/main.py b/sdk/python/examples/controls/responsive_row/basic/main.py new file mode 100644 index 0000000000..3ae27d498f --- /dev/null +++ b/sdk/python/examples/controls/responsive_row/basic/main.py @@ -0,0 +1,86 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_page_resize(e: ft.PageResizeEvent): + pw.value = f"{page.width} px" + pw.update() + + page.on_resize = handle_page_resize + + pw = ft.Text(text_align=ft.TextAlign.END, style=ft.TextTheme.display_small) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.ResponsiveRow( + controls=[ + ft.Container( + content=ft.Text("Column 1"), + padding=5, + bgcolor=ft.Colors.YELLOW, + col={ + ft.ResponsiveRowBreakpoint.XS: 12, + ft.ResponsiveRowBreakpoint.MD: 6, + ft.ResponsiveRowBreakpoint.LG: 3, + }, + ), + ft.Container( + content=ft.Text("Column 2"), + padding=5, + bgcolor=ft.Colors.GREEN, + col={ + ft.ResponsiveRowBreakpoint.XS: 12, + ft.ResponsiveRowBreakpoint.MD: 6, + ft.ResponsiveRowBreakpoint.LG: 3, + }, + ), + ft.Container( + content=ft.Text("Column 3"), + padding=5, + bgcolor=ft.Colors.BLUE, + col={ + ft.ResponsiveRowBreakpoint.XS: 12, + ft.ResponsiveRowBreakpoint.MD: 6, + ft.ResponsiveRowBreakpoint.LG: 3, + }, + ), + ft.Container( + content=ft.Text("Column 4"), + padding=5, + bgcolor=ft.Colors.PINK_300, + col={ + ft.ResponsiveRowBreakpoint.XS: 12, + ft.ResponsiveRowBreakpoint.MD: 6, + ft.ResponsiveRowBreakpoint.LG: 3, + }, + ), + ], + ), + ft.ResponsiveRow( + run_spacing={ft.ResponsiveRowBreakpoint.XS: 10}, + controls=[ + ft.TextField( + label="TextField 1", + col={ft.ResponsiveRowBreakpoint.MD: 4}, + ), + ft.TextField( + label="TextField 2", + col={ft.ResponsiveRowBreakpoint.MD: 4}, + ), + ft.TextField( + label="TextField 3", + col={ft.ResponsiveRowBreakpoint.MD: 4}, + ), + ], + ), + pw, + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/responsive_row/basic/pyproject.toml b/sdk/python/examples/controls/responsive_row/basic/pyproject.toml new file mode 100644 index 0000000000..3651bdfbd5 --- /dev/null +++ b/sdk/python/examples/controls/responsive_row/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "responsive-row-basic" +version = "1.0.0" +description = "Builds a four-column responsive layout and form row that adapt to breakpoints." +requires-python = ">=3.10" +keywords = ["responsive row", "layout", "breakpoints", "grid", "adaptive columns"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ResponsiveRow"] + +[tool.flet.metadata] +title = "Basic" +controls = ["SafeArea", "Column", "ResponsiveRow", "Container", "TextField", "Text"] +layout_pattern = "responsive-grid" +complexity = "basic" +features = ["responsive columns", "text field reflow", "window-aware spacing"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/responsive_row/custom_breakpoint.py b/sdk/python/examples/controls/responsive_row/custom_breakpoint.py deleted file mode 100644 index 54f28a366b..0000000000 --- a/sdk/python/examples/controls/responsive_row/custom_breakpoint.py +++ /dev/null @@ -1,103 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "ResponsiveRow with custom breakpoints" - page.padding = 16 - - breakpoints = { - "phone": 0, - "tablet": 540, - "desktop": 800, - } - - sorted_breakpoints = sorted(breakpoints.items(), key=lambda item: item[1]) - - breakpoint_labels = { - name: ft.Text(f"{name}: \u2265 {value}px", weight=ft.FontWeight.W_500) - for name, value in sorted_breakpoints - } - - width_label = ft.Text() - breakpoint_label = ft.Text() - - def update_status(_=None): - width = ( - (page.window.width if page.window and page.window.width else None) - or page.width - or 0 - ) - width_label.value = f"Page width: {width:.0f}px" - active_breakpoint = max( - (bp for bp, min_width in breakpoints.items() if width >= min_width), - key=lambda bp: breakpoints[bp], - default="phone", - ) - breakpoint_label.value = f"Active breakpoint: {active_breakpoint}" - for name, label in breakpoint_labels.items(): - is_active = name == active_breakpoint - label.color = ft.Colors.BLUE_700 if is_active else None - label.weight = ft.FontWeight.W_700 if is_active else ft.FontWeight.W_400 - label.update() - width_label.update() - breakpoint_label.update() - - page.on_resize = update_status - - page.add( - ft.Text("Resize the window to see custom breakpoints in action."), - ft.Text("Cards switch column spans at phone, tablet, and desktop widths."), - ft.Column( - [ - ft.Text( - "Custom breakpoints (min widths):", - weight=ft.FontWeight.W_600, - ), - ft.Column(list(breakpoint_labels.values()), spacing=2), - ], - spacing=6, - ), - ft.ResponsiveRow( - breakpoints=breakpoints, - columns={"phone": 4, "tablet": 8, "desktop": 12}, - spacing=10, - run_spacing=10, - controls=[ - ft.Container( - content=ft.Text("Card 1", size=16, weight=ft.FontWeight.W_600), - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.AMBER_200, - height=60, - col={"phone": 4, "tablet": 4, "desktop": 3}, - ), - ft.Container( - content=ft.Text("Card 2", size=16, weight=ft.FontWeight.W_600), - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.GREEN_200, - height=60, - col={"phone": 4, "tablet": 4, "desktop": 3}, - ), - ft.Container( - content=ft.Text("Card 3", size=16, weight=ft.FontWeight.W_600), - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.BLUE_200, - height=60, - col={"phone": 4, "tablet": 4, "desktop": 3}, - ), - ft.Container( - content=ft.Text("Card 4", size=16, weight=ft.FontWeight.W_600), - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.PINK_200, - height=60, - col={"phone": 4, "tablet": 4, "desktop": 3}, - ), - ], - ), - width_label, - breakpoint_label, - ) - update_status() - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/responsive_row/custom_breakpoint/main.py b/sdk/python/examples/controls/responsive_row/custom_breakpoint/main.py new file mode 100644 index 0000000000..9df8c13beb --- /dev/null +++ b/sdk/python/examples/controls/responsive_row/custom_breakpoint/main.py @@ -0,0 +1,120 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "ResponsiveRow with custom breakpoints" + page.padding = 16 + + breakpoints = { + "phone": 0, + "tablet": 540, + "desktop": 800, + } + + sorted_breakpoints = sorted(breakpoints.items(), key=lambda item: item[1]) + + breakpoint_labels = { + name: ft.Text(f"{name}: >= {value}px", weight=ft.FontWeight.W_500) + for name, value in sorted_breakpoints + } + + width_label = ft.Text() + breakpoint_label = ft.Text() + + def update_status(_=None): + width = ( + (page.window.width if page.window and page.window.width else None) + or page.width + or 0 + ) + width_label.value = f"Page width: {width:.0f}px" + active_breakpoint = max( + (bp for bp, min_width in breakpoints.items() if width >= min_width), + key=lambda bp: breakpoints[bp], + default="phone", + ) + breakpoint_label.value = f"Active breakpoint: {active_breakpoint}" + for name, label in breakpoint_labels.items(): + is_active = name == active_breakpoint + label.color = ft.Colors.BLUE_700 if is_active else None + label.weight = ft.FontWeight.W_700 if is_active else ft.FontWeight.W_400 + label.update() + width_label.update() + breakpoint_label.update() + + page.on_resize = update_status + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Resize the window to see custom breakpoints in action."), + ft.Text( + "Cards switch column spans at phone, tablet, and " + "desktop widths." + ), + ft.Column( + [ + ft.Text( + "Custom breakpoints (min widths):", + weight=ft.FontWeight.W_600, + ), + ft.Column(list(breakpoint_labels.values()), spacing=2), + ], + spacing=6, + ), + ft.ResponsiveRow( + breakpoints=breakpoints, + columns={"phone": 4, "tablet": 8, "desktop": 12}, + spacing=10, + run_spacing=10, + controls=[ + ft.Container( + content=ft.Text( + "Card 1", size=16, weight=ft.FontWeight.W_600 + ), + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.AMBER_200, + height=60, + col={"phone": 4, "tablet": 4, "desktop": 3}, + ), + ft.Container( + content=ft.Text( + "Card 2", size=16, weight=ft.FontWeight.W_600 + ), + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.GREEN_200, + height=60, + col={"phone": 4, "tablet": 4, "desktop": 3}, + ), + ft.Container( + content=ft.Text( + "Card 3", size=16, weight=ft.FontWeight.W_600 + ), + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.BLUE_200, + height=60, + col={"phone": 4, "tablet": 4, "desktop": 3}, + ), + ft.Container( + content=ft.Text( + "Card 4", size=16, weight=ft.FontWeight.W_600 + ), + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.PINK_200, + height=60, + col={"phone": 4, "tablet": 4, "desktop": 3}, + ), + ], + ), + width_label, + breakpoint_label, + ] + ) + ) + ) + update_status() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/responsive_row/custom_breakpoint/pyproject.toml b/sdk/python/examples/controls/responsive_row/custom_breakpoint/pyproject.toml new file mode 100644 index 0000000000..8cb8f41ecb --- /dev/null +++ b/sdk/python/examples/controls/responsive_row/custom_breakpoint/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "responsive-row-custom-breakpoint" +version = "1.0.0" +description = "Shows custom breakpoint values and column layouts for ResponsiveRow." +requires-python = ">=3.10" +keywords = ["responsive row", "custom breakpoints", "layout", "adaptive UI"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/ResponsiveRow"] + +[tool.flet.metadata] +title = "Custom Breakpoints" +controls = ["SafeArea", "Column", "ResponsiveRow", "Container", "Text", "TextField"] +layout_pattern = "responsive-dashboard" +complexity = "basic" +features = ["custom breakpoints", "adaptive columns", "resize indicator"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/rive/example_1.py b/sdk/python/examples/controls/rive/example_1.py deleted file mode 100644 index e1aec8addc..0000000000 --- a/sdk/python/examples/controls/rive/example_1.py +++ /dev/null @@ -1,22 +0,0 @@ -import flet as ft -import flet_rive as ftr - - -def main(page: ft.Page): - page.add( - ftr.Rive( - src="https://cdn.rive.app/animations/vehicles.riv", - placeholder=ft.ProgressBar(), - width=300, - height=200, - ), - ftr.Rive( - src="vehicles.riv", - placeholder=ft.ProgressBar(), - width=300, - height=200, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/rive/assets/vehicles.riv b/sdk/python/examples/controls/rive/example_1/assets/vehicles.riv similarity index 100% rename from sdk/python/examples/controls/rive/assets/vehicles.riv rename to sdk/python/examples/controls/rive/example_1/assets/vehicles.riv diff --git a/sdk/python/examples/controls/rive/example_1/main.py b/sdk/python/examples/controls/rive/example_1/main.py new file mode 100644 index 0000000000..689775fe3c --- /dev/null +++ b/sdk/python/examples/controls/rive/example_1/main.py @@ -0,0 +1,29 @@ +import flet as ft +import flet_rive as ftr + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ftr.Rive( + src="https://cdn.rive.app/animations/vehicles.riv", + placeholder=ft.ProgressBar(), + width=300, + height=200, + ), + ftr.Rive( + src="vehicles.riv", + placeholder=ft.ProgressBar(), + width=300, + height=200, + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/rive/example_1/pyproject.toml b/sdk/python/examples/controls/rive/example_1/pyproject.toml new file mode 100644 index 0000000000..3198cd20b2 --- /dev/null +++ b/sdk/python/examples/controls/rive/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "rive-example-1" +version = "1.0.0" +description = "Plays a remote Rive animation and a local Rive asset." +requires-python = ">=3.10" +keywords = ["rive", "animation", "extension", "assets", "local file", "remote file"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-rive"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Media/Rive"] + +[tool.flet.metadata] +title = "Example 1" +controls = ["SafeArea", "Column", "Rive", "ProgressBar"] +layout_pattern = "media-showcase" +complexity = "basic" +features = ["remote animations", "local asset playback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/rotated_box/__init__.py b/sdk/python/examples/controls/rotated_box/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/rotated_box/basic.py b/sdk/python/examples/controls/rotated_box/basic.py deleted file mode 100644 index 5faad3fbd5..0000000000 --- a/sdk/python/examples/controls/rotated_box/basic.py +++ /dev/null @@ -1,87 +0,0 @@ -import flet as ft - - -def _demo_control(content: ft.Control) -> ft.Container: - return ft.Container( - padding=10, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - border_radius=8, - content=content, - ) - - -def _lane(title: str, controls: list[ft.Control]) -> ft.Container: - return ft.Container( - width=540, - padding=12, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - border_radius=12, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(title, size=16, weight=ft.FontWeight.BOLD), - ft.Divider(height=1), - ft.Row( - spacing=14, - vertical_alignment=ft.CrossAxisAlignment.START, - controls=controls, - ), - ], - ), - ) - - -def main(page: ft.Page): - page.padding = 24 - page.scroll = ft.ScrollMode.AUTO - page.add( - ft.Text( - "RotatedBox rotates before layout. Compare occupied space below:", - size=16, - weight=ft.FontWeight.W_500, - ), - ft.Column( - spacing=16, - controls=[ - _lane( - "Normal controls", - [ - _demo_control(ft.Text("Text", size=26)), - _demo_control( - ft.ProgressBar(width=170, value=0.65, color=ft.Colors.GREEN) - ), - _demo_control(ft.Button("Button")), - ], - ), - _lane( - "RotatedBox quarter_turns=1", - [ - _demo_control( - ft.RotatedBox( - quarter_turns=1, - content=ft.Text("Text", size=26), - ) - ), - _demo_control( - ft.RotatedBox( - quarter_turns=1, - content=ft.ProgressBar( - width=170, value=0.65, color=ft.Colors.GREEN - ), - ) - ), - _demo_control( - ft.RotatedBox( - quarter_turns=1, - content=ft.Button("Button"), - ) - ), - ], - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/rotated_box/basic/main.py b/sdk/python/examples/controls/rotated_box/basic/main.py new file mode 100644 index 0000000000..3b93edb5cc --- /dev/null +++ b/sdk/python/examples/controls/rotated_box/basic/main.py @@ -0,0 +1,98 @@ +import flet as ft + + +def _demo_control(content: ft.Control) -> ft.Container: + return ft.Container( + padding=10, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + border_radius=8, + content=content, + ) + + +def _lane(title: str, controls: list[ft.Control]) -> ft.Container: + return ft.Container( + width=540, + padding=12, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + border_radius=12, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(title, size=16, weight=ft.FontWeight.BOLD), + ft.Divider(height=1), + ft.Row( + spacing=14, + vertical_alignment=ft.CrossAxisAlignment.START, + controls=controls, + ), + ], + ), + ) + + +def main(page: ft.Page): + page.padding = 24 + page.scroll = ft.ScrollMode.AUTO + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "RotatedBox rotates before layout. Compare occupied " + "space below:", + size=16, + weight=ft.FontWeight.W_500, + ), + ft.Column( + spacing=16, + controls=[ + _lane( + "Normal controls", + [ + _demo_control(ft.Text("Text", size=26)), + _demo_control( + ft.ProgressBar( + width=170, value=0.65, color=ft.Colors.GREEN + ) + ), + _demo_control(ft.Button("Button")), + ], + ), + _lane( + "RotatedBox quarter_turns=1", + [ + _demo_control( + ft.RotatedBox( + quarter_turns=1, + content=ft.Text("Text", size=26), + ) + ), + _demo_control( + ft.RotatedBox( + quarter_turns=1, + content=ft.ProgressBar( + width=170, + value=0.65, + color=ft.Colors.GREEN, + ), + ) + ), + _demo_control( + ft.RotatedBox( + quarter_turns=1, + content=ft.Button("Button"), + ) + ), + ], + ), + ], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/rotated_box/basic/pyproject.toml b/sdk/python/examples/controls/rotated_box/basic/pyproject.toml new file mode 100644 index 0000000000..302a2e7cf6 --- /dev/null +++ b/sdk/python/examples/controls/rotated_box/basic/pyproject.toml @@ -0,0 +1,39 @@ +[project] +name = "rotated-box-basic" +version = "1.0.0" +description = "Compares normal layouted controls with quarter_turns RotatedBox rendering." +requires-python = ">=3.10" +keywords = ["rotated box", "layout", "rotation", "transform", "animation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/RotatedBox"] + +[tool.flet.metadata] +title = "Basic" +controls = [ + "SafeArea", + "Column", + "Row", + "Container", + "RotatedBox", + "Text", + "Button", + "ProgressBar", +] +layout_pattern = "compare-layout" +complexity = "basic" +features = [ + "rotation before layout", + "visual space comparison", + "before-after examples", +] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/row/__init__.py b/sdk/python/examples/controls/row/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/row/alignment.py b/sdk/python/examples/controls/row/alignment.py deleted file mode 100644 index 00b25a8bfc..0000000000 --- a/sdk/python/examples/controls/row/alignment.py +++ /dev/null @@ -1,48 +0,0 @@ -import flet as ft - - -class RowWithAlignment(ft.Column): - def __init__(self, alignment: ft.MainAxisAlignment): - super().__init__() - self.controls = [ - ft.Text(str(alignment), size=16), - ft.Container( - content=ft.Row(self.generate_items(3), alignment=alignment), - bgcolor=ft.Colors.AMBER_100, - ), - ] - - @staticmethod - def generate_items(count: int): - return [ - ft.Container( - content=ft.Text(value=str(i)), - alignment=ft.Alignment.CENTER, - width=50, - height=50, - bgcolor=ft.Colors.AMBER_500, - ) - for i in range(1, count + 1) - ] - - -def main(page: ft.Page): - page.scroll = ft.ScrollMode.AUTO - - page.add( - ft.Column( - scroll=ft.ScrollMode.AUTO, - controls=[ - RowWithAlignment(ft.MainAxisAlignment.START), - RowWithAlignment(ft.MainAxisAlignment.CENTER), - RowWithAlignment(ft.MainAxisAlignment.END), - RowWithAlignment(ft.MainAxisAlignment.SPACE_BETWEEN), - RowWithAlignment(ft.MainAxisAlignment.SPACE_AROUND), - RowWithAlignment(ft.MainAxisAlignment.SPACE_EVENLY), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/row/alignment/main.py b/sdk/python/examples/controls/row/alignment/main.py new file mode 100644 index 0000000000..270670e0e3 --- /dev/null +++ b/sdk/python/examples/controls/row/alignment/main.py @@ -0,0 +1,54 @@ +import flet as ft + + +@ft.control +class RowWithAlignment(ft.Column): + alignment: ft.MainAxisAlignment = ft.MainAxisAlignment.START + + def init(self): + self.controls = [ + ft.Text(str(self.alignment), size=16), + ft.Container( + bgcolor=ft.Colors.AMBER_100, + content=ft.Row( + alignment=self.alignment, + controls=self.generate_items(3), + ), + ), + ] + + @staticmethod + def generate_items(count: int): + return [ + ft.Container( + content=ft.Text(value=str(i)), + alignment=ft.Alignment.CENTER, + width=50, + height=50, + bgcolor=ft.Colors.AMBER_500, + ) + for i in range(1, count + 1) + ] + + +def main(page: ft.Page): + page.scroll = ft.ScrollMode.AUTO + page.add( + ft.SafeArea( + content=ft.Column( + scroll=ft.ScrollMode.AUTO, + controls=[ + RowWithAlignment(alignment=ft.MainAxisAlignment.START), + RowWithAlignment(alignment=ft.MainAxisAlignment.CENTER), + RowWithAlignment(alignment=ft.MainAxisAlignment.END), + RowWithAlignment(alignment=ft.MainAxisAlignment.SPACE_BETWEEN), + RowWithAlignment(alignment=ft.MainAxisAlignment.SPACE_AROUND), + RowWithAlignment(alignment=ft.MainAxisAlignment.SPACE_EVENLY), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/row/alignment/pyproject.toml b/sdk/python/examples/controls/row/alignment/pyproject.toml new file mode 100644 index 0000000000..c5be8678a2 --- /dev/null +++ b/sdk/python/examples/controls/row/alignment/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "row-alignment" +version = "1.0.0" +description = "Row main-axis alignment modes demonstrated side by side." +requires-python = ">=3.10" +keywords = ["row", "layout", "alignment", "mainaxisalignment", "scroll"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Row"] + +[tool.flet.metadata] +title = "Row alignment" +controls = ["Column", "Row", "Container", "Text"] +layout_pattern = "comparison-grid" +complexity = "basic" +features = ["main axis alignment", "layout comparison", "vertical scrolling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/row/spacing.py b/sdk/python/examples/controls/row/spacing.py deleted file mode 100644 index 007d624484..0000000000 --- a/sdk/python/examples/controls/row/spacing.py +++ /dev/null @@ -1,44 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def generate_items(count: int): - return [ - ft.Container( - content=ft.Text(value=str(i)), - alignment=ft.Alignment.CENTER, - width=50, - height=50, - bgcolor=ft.Colors.AMBER, - border_radius=ft.BorderRadius.all(5), - ) - for i in range(1, count + 1) - ] - - def handle_slider_change(e: ft.Event[ft.Slider]): - row.spacing = int(e.control.value) - row.update() - - page.add( - ft.Column( - controls=[ - ft.Text("Spacing between items"), - ft.Slider( - key="slider", - min=0, - max=50, - divisions=50, - value=0, - label="{value}", - on_change=handle_slider_change, - ), - ] - ), - row := ft.Row( - spacing=0, controls=generate_items(10), scroll=ft.ScrollMode.AUTO - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/row/spacing/main.py b/sdk/python/examples/controls/row/spacing/main.py new file mode 100644 index 0000000000..d0f30660a0 --- /dev/null +++ b/sdk/python/examples/controls/row/spacing/main.py @@ -0,0 +1,50 @@ +import flet as ft + + +def main(page: ft.Page): + def generate_items(count: int): + return [ + ft.Container( + content=ft.Text(value=str(i)), + alignment=ft.Alignment.CENTER, + width=50, + height=50, + bgcolor=ft.Colors.AMBER, + border_radius=ft.BorderRadius.all(5), + ) + for i in range(1, count + 1) + ] + + def handle_slider_change(e: ft.Event[ft.Slider]): + row.spacing = int(e.control.value) + row.update() + + row = ft.Row(spacing=0, scroll=ft.ScrollMode.AUTO, controls=generate_items(10)) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + controls=[ + ft.Text("Spacing between items"), + ft.Slider( + key="slider", + min=0, + max=50, + divisions=50, + value=0, + label="{value}", + on_change=handle_slider_change, + ), + ] + ), + row, + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/row/spacing/pyproject.toml b/sdk/python/examples/controls/row/spacing/pyproject.toml new file mode 100644 index 0000000000..984bfb5dab --- /dev/null +++ b/sdk/python/examples/controls/row/spacing/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "row-spacing" +version = "1.0.0" +description = "Adjust row spacing interactively with a slider." +requires-python = ">=3.10" +keywords = ["row", "layout", "spacing", "slider", "interactive"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Row"] + +[tool.flet.metadata] +title = "Row spacing" +controls = ["Column", "Row", "Container", "Text", "Slider"] +layout_pattern = "control-panel" +complexity = "basic" +features = ["slider-driven spacing", "dynamic layout updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/row/vertical_alignment.py b/sdk/python/examples/controls/row/vertical_alignment.py deleted file mode 100644 index 6a4d240a8b..0000000000 --- a/sdk/python/examples/controls/row/vertical_alignment.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -class RowWithVerticalAlignment(ft.Column): - def __init__(self, alignment: ft.CrossAxisAlignment): - super().__init__() - self.controls = [ - ft.Text(str(alignment), size=16), - ft.Container( - content=ft.Row(self.generate_items(3), vertical_alignment=alignment), - bgcolor=ft.Colors.AMBER_100, - height=150, - ), - ] - - @staticmethod - def generate_items(count: int): - return [ - ft.Container( - content=ft.Text(value=str(i)), - alignment=ft.Alignment.CENTER, - width=50, - height=50, - bgcolor=ft.Colors.AMBER_500, - ) - for i in range(1, count + 1) - ] - - -def main(page: ft.Page): - page.add( - RowWithVerticalAlignment(ft.CrossAxisAlignment.START), - RowWithVerticalAlignment(ft.CrossAxisAlignment.CENTER), - RowWithVerticalAlignment(ft.CrossAxisAlignment.END), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/row/vertical_alignment/main.py b/sdk/python/examples/controls/row/vertical_alignment/main.py new file mode 100644 index 0000000000..8a6c45df27 --- /dev/null +++ b/sdk/python/examples/controls/row/vertical_alignment/main.py @@ -0,0 +1,50 @@ +import flet as ft + + +@ft.control +class RowWithVerticalAlignment(ft.Column): + alignment: ft.CrossAxisAlignment = ft.CrossAxisAlignment.START + + def init(self): + self.controls = [ + ft.Text(str(self.alignment), size=16), + ft.Container( + bgcolor=ft.Colors.AMBER_100, + height=150, + content=ft.Row( + vertical_alignment=self.alignment, + controls=self.generate_items(3), + ), + ), + ] + + @staticmethod + def generate_items(count: int): + return [ + ft.Container( + content=ft.Text(value=str(i)), + alignment=ft.Alignment.CENTER, + width=50, + height=50, + bgcolor=ft.Colors.AMBER_500, + ) + for i in range(1, count + 1) + ] + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + RowWithVerticalAlignment(alignment=ft.CrossAxisAlignment.START), + RowWithVerticalAlignment(alignment=ft.CrossAxisAlignment.CENTER), + RowWithVerticalAlignment(alignment=ft.CrossAxisAlignment.END), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/row/vertical_alignment/pyproject.toml b/sdk/python/examples/controls/row/vertical_alignment/pyproject.toml new file mode 100644 index 0000000000..a64f580540 --- /dev/null +++ b/sdk/python/examples/controls/row/vertical_alignment/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "row-vertical-alignment" +version = "1.0.0" +description = "Row cross-axis alignment modes with visual comparison." +requires-python = ">=3.10" +keywords = ["row", "layout", "crossaxisalignment", "alignment"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Row"] + +[tool.flet.metadata] +title = "Row vertical alignment" +controls = ["Column", "Row", "Container", "Text"] +layout_pattern = "comparison-grid" +complexity = "basic" +features = ["cross axis alignment", "layout comparison"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/row/wrap.py b/sdk/python/examples/controls/row/wrap.py deleted file mode 100644 index 19f75886a7..0000000000 --- a/sdk/python/examples/controls/row/wrap.py +++ /dev/null @@ -1,49 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def generate_items(count: int): - return [ - ft.Container( - content=ft.Text(value=str(i)), - alignment=ft.Alignment.CENTER, - width=50, - height=50, - bgcolor=ft.Colors.AMBER, - border_radius=ft.BorderRadius.all(5), - ) - for i in range(1, count + 1) - ] - - def handle_slider_change(e: ft.Event[ft.Slider]): - row.width = float(e.control.value) - row.update() - - page.add( - ft.Column( - controls=[ - ft.Text( - "Change the row width to see how child items wrap onto multiple rows:" - ), - ft.Slider( - min=0, - max=page.window.width, - divisions=20, - value=page.window.width, - label="{value}", - on_change=handle_slider_change, - ), - ] - ), - row := ft.Row( - wrap=True, - spacing=10, - run_spacing=10, - controls=generate_items(30), - width=page.window.width, - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/row/wrap/main.py b/sdk/python/examples/controls/row/wrap/main.py new file mode 100644 index 0000000000..5102616595 --- /dev/null +++ b/sdk/python/examples/controls/row/wrap/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def main(page: ft.Page): + def generate_items(count: int): + return [ + ft.Container( + content=ft.Text(value=str(i)), + alignment=ft.Alignment.CENTER, + width=50, + height=50, + bgcolor=ft.Colors.AMBER, + border_radius=ft.BorderRadius.all(5), + ) + for i in range(1, count + 1) + ] + + def handle_slider_change(e: ft.Event[ft.Slider]): + row.width = float(e.control.value) + row.update() + + row = ft.Row( + wrap=True, + spacing=10, + run_spacing=10, + width=page.window.width, + controls=generate_items(30), + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + controls=[ + ft.Text( + "Change the row width to see how child items wrap " + "onto multiple rows:" + ), + ft.Slider( + min=0, + max=page.window.width, + divisions=20, + value=page.window.width, + label="{value}", + on_change=handle_slider_change, + ), + ] + ), + row, + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/row/wrap/pyproject.toml b/sdk/python/examples/controls/row/wrap/pyproject.toml new file mode 100644 index 0000000000..37aefedc32 --- /dev/null +++ b/sdk/python/examples/controls/row/wrap/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "row-wrap" +version = "1.0.0" +description = "Row wrapping behavior controlled by dynamic width." +requires-python = ">=3.10" +keywords = ["row", "layout", "wrap", "slider", "responsive"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Row"] + +[tool.flet.metadata] +title = "Row wrapping" +controls = ["Column", "Row", "Container", "Text", "Slider"] +layout_pattern = "control-panel" +complexity = "basic" +features = ["wrap behavior", "width-driven layout", "slider interaction"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/safe_area/basic.py b/sdk/python/examples/controls/safe_area/basic.py deleted file mode 100644 index 649efc1712..0000000000 --- a/sdk/python/examples/controls/safe_area/basic.py +++ /dev/null @@ -1,32 +0,0 @@ -import flet as ft - - -class State: - counter = 0 - - -def main(page: ft.Page): - state = State() - - def handle_button_click(e: ft.Event[ft.FloatingActionButton]): - state.counter += 1 - message.value = str(state.counter) - message.update() - - page.floating_action_button = ft.FloatingActionButton( - icon=ft.Icons.ADD, - on_click=handle_button_click, - ) - - page.add( - ft.SafeArea( - expand=True, - content=ft.Container( - message := ft.Text("0", size=50), - alignment=ft.Alignment.CENTER, - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/safe_area/basic/main.py b/sdk/python/examples/controls/safe_area/basic/main.py new file mode 100644 index 0000000000..85bf4d58ff --- /dev/null +++ b/sdk/python/examples/controls/safe_area/basic/main.py @@ -0,0 +1,34 @@ +import flet as ft + + +class State: + counter = 0 + + +def main(page: ft.Page): + state = State() + message = ft.Text("0", size=50) + + def handle_button_click(e: ft.Event[ft.FloatingActionButton]): + state.counter += 1 + message.value = str(state.counter) + message.update() + + page.floating_action_button = ft.FloatingActionButton( + icon=ft.Icons.ADD, + on_click=handle_button_click, + ) + + page.add( + ft.SafeArea( + expand=True, + content=ft.Container( + alignment=ft.Alignment.CENTER, + content=message, + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/safe_area/basic/pyproject.toml b/sdk/python/examples/controls/safe_area/basic/pyproject.toml new file mode 100644 index 0000000000..d4fcdd2bdf --- /dev/null +++ b/sdk/python/examples/controls/safe_area/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "safe-area-basic" +version = "1.0.0" +description = "Keep centered content inside device safe insets while updating a counter." +requires-python = ">=3.10" +keywords = ["safearea", "layout", "counter", "floatingactionbutton", "mobile"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/SafeArea"] + +[tool.flet.metadata] +title = "SafeArea basic" +controls = ["SafeArea", "Container", "Text", "FloatingActionButton"] +layout_pattern = "centered-single-action" +complexity = "basic" +features = ["safe insets", "floating action button", "counter updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/screenshot/taking_screenshot.py b/sdk/python/examples/controls/screenshot/taking_screenshot.py deleted file mode 100644 index 89f32507d1..0000000000 --- a/sdk/python/examples/controls/screenshot/taking_screenshot.py +++ /dev/null @@ -1,24 +0,0 @@ -from pathlib import Path - -import flet as ft -from flet.utils.files import get_current_script_dir - - -def main(page: ft.Page): - async def take_screenshot(): - image = await scr.capture() - with open(Path(get_current_script_dir(), "screenshot.png"), "wb") as f: - f.write(image) - - page.add( - scr := ft.Screenshot( - ft.Container( - ft.Button("Hello, world!", bgcolor=ft.Colors.BLUE, elevation=10), - padding=10, - ) - ), - ft.Button("Take screenshot", on_click=take_screenshot), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/screenshot/taking_screenshot/main.py b/sdk/python/examples/controls/screenshot/taking_screenshot/main.py new file mode 100644 index 0000000000..c61bca220b --- /dev/null +++ b/sdk/python/examples/controls/screenshot/taking_screenshot/main.py @@ -0,0 +1,35 @@ +from pathlib import Path + +import flet as ft +from flet.utils.files import get_current_script_dir + + +def main(page: ft.Page): + async def take_screenshot(e: ft.Event[ft.Button]): + image = await scr.capture() + with open(Path(get_current_script_dir(), "screenshot.png"), "wb") as f: + f.write(image) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + scr := ft.Screenshot( + content=ft.Container( + padding=10, + content=ft.Button( + "Hello, world!", + bgcolor=ft.Colors.BLUE, + elevation=10, + ), + ) + ), + ft.Button("Take screenshot", on_click=take_screenshot), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/screenshot/taking_screenshot/pyproject.toml b/sdk/python/examples/controls/screenshot/taking_screenshot/pyproject.toml new file mode 100644 index 0000000000..d43f346b42 --- /dev/null +++ b/sdk/python/examples/controls/screenshot/taking_screenshot/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "screenshot-taking-screenshot" +version = "1.0.0" +description = "Capture a Screenshot control asynchronously and save the image to a file." +requires-python = ">=3.10" +keywords = ["screenshot", "capture", "button", "async", "file"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Screenshot"] + +[tool.flet.metadata] +title = "Taking screenshot" +controls = ["Screenshot", "Container", "Button", "Column", "SafeArea"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["async capture", "save to file", "button interaction"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/screenshot/taking_screenshot/screenshot.png b/sdk/python/examples/controls/screenshot/taking_screenshot/screenshot.png new file mode 100644 index 0000000000..9000215d7f Binary files /dev/null and b/sdk/python/examples/controls/screenshot/taking_screenshot/screenshot.png differ diff --git a/sdk/python/examples/controls/search_bar/__init__.py b/sdk/python/examples/controls/search_bar/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/search_bar/basic.py b/sdk/python/examples/controls/search_bar/basic.py deleted file mode 100644 index 379ea3f330..0000000000 --- a/sdk/python/examples/controls/search_bar/basic.py +++ /dev/null @@ -1,59 +0,0 @@ -import flet as ft - -colors = [ - "Amber", - "Blue Grey", - "Brown", - "Deep Orange", - "Green", - "Light Blue", - "Orange", - "Red", -] - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def build_tiles(items: list[str]) -> list[ft.Control]: - return [ - ft.ListTile( - title=ft.Text(item), - data=item, - on_click=handle_tile_click, - ) - for item in items - ] - - async def handle_tile_click(e: ft.Event[ft.ListTile]): - await anchor.close_view() - - async def handle_change(e: ft.Event[ft.SearchBar]): - query = e.control.value.strip().lower() - matching = ( - [color for color in colors if query in color.lower()] if query else colors - ) - anchor.controls = build_tiles(matching) - - def handle_submit(e: ft.Event[ft.SearchBar]): - print(f"Submit: {e.data}") - - async def handle_tap(e: ft.Event[ft.SearchBar]): - await anchor.open_view() - - page.add( - anchor := ft.SearchBar( - view_elevation=4, - divider_color=ft.Colors.AMBER, - bar_hint_text="Search colors...", - view_hint_text="Choose a color from the suggestions...", - on_change=handle_change, - on_submit=handle_submit, - on_tap=handle_tap, - controls=build_tiles(colors), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/search_bar/basic/main.py b/sdk/python/examples/controls/search_bar/basic/main.py new file mode 100644 index 0000000000..aabcf48592 --- /dev/null +++ b/sdk/python/examples/controls/search_bar/basic/main.py @@ -0,0 +1,59 @@ +import flet as ft + +colors = [ + "Amber", + "Blue Grey", + "Brown", + "Deep Orange", + "Green", + "Light Blue", + "Orange", + "Red", +] + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def build_tiles(items: list[str]) -> list[ft.Control]: + return [ + ft.ListTile( + title=ft.Text(item), + data=item, + on_click=handle_tile_click, + ) + for item in items + ] + + async def handle_tile_click(e: ft.Event[ft.ListTile]): + await anchor.close_view() + + async def handle_change(e: ft.Event[ft.SearchBar]): + query = e.control.value.strip().lower() + matching = ( + [color for color in colors if query in color.lower()] if query else colors + ) + anchor.controls = build_tiles(matching) + + def handle_submit(e: ft.Event[ft.SearchBar]): + print(f"Submit: {e.data}") + + async def handle_tap(e: ft.Event[ft.SearchBar]): + await anchor.open_view() + + anchor = ft.SearchBar( + view_elevation=4, + divider_color=ft.Colors.AMBER, + bar_hint_text="Search colors...", + view_hint_text="Choose a color from the suggestions...", + on_change=handle_change, + on_submit=handle_submit, + on_tap=handle_tap, + controls=build_tiles(colors), + ) + + page.add(ft.SafeArea(content=anchor)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/search_bar/basic/pyproject.toml b/sdk/python/examples/controls/search_bar/basic/pyproject.toml new file mode 100644 index 0000000000..12aa2fd6ad --- /dev/null +++ b/sdk/python/examples/controls/search_bar/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "search-bar-basic" +version = "1.0.0" +description = "Search colors with async suggestions shown inside a SearchBar view." +requires-python = ">=3.10" +keywords = ["searchbar", "search", "suggestions", "listtile", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/SearchBar"] + +[tool.flet.metadata] +title = "SearchBar basic" +controls = ["SearchBar", "ListTile", "Text", "SafeArea"] +layout_pattern = "search-overlay" +complexity = "basic" +features = ["async suggestions", "live filtering", "search submit"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/segmented_button/__init__.py b/sdk/python/examples/controls/segmented_button/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/segmented_button/single_multiple_selection.py b/sdk/python/examples/controls/segmented_button/single_multiple_selection.py deleted file mode 100644 index 9a0766eb61..0000000000 --- a/sdk/python/examples/controls/segmented_button/single_multiple_selection.py +++ /dev/null @@ -1,70 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_selection_change(e: ft.Event[ft.SegmentedButton]): - print(e) - - page.add( - ft.SegmentedButton( - on_change=handle_selection_change, - selected_icon=ft.Icon(ft.Icons.CHECK_SHARP), - selected=["1", "4"], - allow_empty_selection=True, - allow_multiple_selection=True, - segments=[ - ft.Segment( - value="1", - label=ft.Text("One"), - icon=ft.Icon(ft.Icons.LOOKS_ONE), - ), - ft.Segment( - value="2", - label=ft.Text("Two"), - icon=ft.Icon(ft.Icons.LOOKS_TWO), - ), - ft.Segment( - value="3", - label=ft.Text("Three"), - icon=ft.Icon(ft.Icons.LOOKS_3), - ), - ft.Segment( - value="4", - label=ft.Text("Four"), - icon=ft.Icon(ft.Icons.LOOKS_4), - ), - ], - ), - ft.SegmentedButton( - on_change=handle_selection_change, - selected_icon=ft.Icon(ft.Icons.CHECK_SHARP), - selected=["2"], - allow_multiple_selection=False, - segments=[ - ft.Segment( - value="1", - label=ft.Text("One"), - icon=ft.Icon(ft.Icons.LOOKS_ONE), - ), - ft.Segment( - value="2", - label=ft.Text("Two"), - icon=ft.Icon(ft.Icons.LOOKS_TWO), - ), - ft.Segment( - value="3", - label=ft.Text("Three"), - icon=ft.Icon(ft.Icons.LOOKS_3), - ), - ft.Segment( - value="4", - label=ft.Text("Four"), - icon=ft.Icon(ft.Icons.LOOKS_4), - ), - ], - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/segmented_button/single_multiple_selection/main.py b/sdk/python/examples/controls/segmented_button/single_multiple_selection/main.py new file mode 100644 index 0000000000..f0dd892d73 --- /dev/null +++ b/sdk/python/examples/controls/segmented_button/single_multiple_selection/main.py @@ -0,0 +1,76 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_selection_change(e: ft.Event[ft.SegmentedButton]): + print(e) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.SegmentedButton( + on_change=handle_selection_change, + selected_icon=ft.Icon(ft.Icons.CHECK_SHARP), + selected=["1", "4"], + allow_empty_selection=True, + allow_multiple_selection=True, + segments=[ + ft.Segment( + value="1", + label=ft.Text("One"), + icon=ft.Icon(ft.Icons.LOOKS_ONE), + ), + ft.Segment( + value="2", + label=ft.Text("Two"), + icon=ft.Icon(ft.Icons.LOOKS_TWO), + ), + ft.Segment( + value="3", + label=ft.Text("Three"), + icon=ft.Icon(ft.Icons.LOOKS_3), + ), + ft.Segment( + value="4", + label=ft.Text("Four"), + icon=ft.Icon(ft.Icons.LOOKS_4), + ), + ], + ), + ft.SegmentedButton( + on_change=handle_selection_change, + selected_icon=ft.Icon(ft.Icons.CHECK_SHARP), + selected=["2"], + allow_multiple_selection=False, + segments=[ + ft.Segment( + value="1", + label=ft.Text("One"), + icon=ft.Icon(ft.Icons.LOOKS_ONE), + ), + ft.Segment( + value="2", + label=ft.Text("Two"), + icon=ft.Icon(ft.Icons.LOOKS_TWO), + ), + ft.Segment( + value="3", + label=ft.Text("Three"), + icon=ft.Icon(ft.Icons.LOOKS_3), + ), + ft.Segment( + value="4", + label=ft.Text("Four"), + icon=ft.Icon(ft.Icons.LOOKS_4), + ), + ], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/segmented_button/single_multiple_selection/pyproject.toml b/sdk/python/examples/controls/segmented_button/single_multiple_selection/pyproject.toml new file mode 100644 index 0000000000..26195e36e7 --- /dev/null +++ b/sdk/python/examples/controls/segmented_button/single_multiple_selection/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "segmented-button-single-multiple-selection" +version = "1.0.0" +description = "Compare single and multiple selection behavior with SegmentedButton." +requires-python = ">=3.10" +keywords = ["segmentedbutton", "selection", "multiple", "single", "icons"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/SegmentedButton"] + +[tool.flet.metadata] +title = "SegmentedButton single and multiple selection" +controls = ["SegmentedButton", "Segment", "Icon", "Text", "Column", "SafeArea"] +layout_pattern = "comparison-stack" +complexity = "basic" +features = ["single selection", "multiple selection", "selection change"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/selection_area/__init__.py b/sdk/python/examples/controls/selection_area/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/selection_area/basic.py b/sdk/python/examples/controls/selection_area/basic.py deleted file mode 100644 index 7c0670d903..0000000000 --- a/sdk/python/examples/controls/selection_area/basic.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft - -TEXT_STYLE = ft.TextStyle( - size=22, - weight=ft.FontWeight.W_600, - decoration=ft.TextDecoration( - ft.TextDecoration.UNDERLINE | ft.TextDecoration.OVERLINE - ), - decoration_style=ft.TextDecorationStyle.WAVY, -) - - -def main(page: ft.Page): - page.add( - ft.SelectionArea( - content=ft.Column( - controls=[ - ft.Text( - "Selectable text", - color=ft.Colors.GREEN, - style=TEXT_STYLE, - key="selectable", - ), - ft.Text("Also selectable", color=ft.Colors.GREEN, style=TEXT_STYLE), - ] - ) - ) - ) - - page.add( - ft.Text( - "Not selectable", - color=ft.Colors.RED, - style=TEXT_STYLE, - key="non-selectable", - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/selection_area/basic/main.py b/sdk/python/examples/controls/selection_area/basic/main.py new file mode 100644 index 0000000000..8349f9d283 --- /dev/null +++ b/sdk/python/examples/controls/selection_area/basic/main.py @@ -0,0 +1,48 @@ +import flet as ft + +TEXT_STYLE = ft.TextStyle( + size=22, + weight=ft.FontWeight.W_600, + decoration=ft.TextDecoration( + ft.TextDecoration.UNDERLINE | ft.TextDecoration.OVERLINE + ), + decoration_style=ft.TextDecorationStyle.WAVY, +) + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.SelectionArea( + content=ft.Column( + controls=[ + ft.Text( + "Selectable text", + color=ft.Colors.GREEN, + style=TEXT_STYLE, + key="selectable", + ), + ft.Text( + "Also selectable", + color=ft.Colors.GREEN, + style=TEXT_STYLE, + ), + ] + ) + ), + ft.Text( + "Not selectable", + color=ft.Colors.RED, + style=TEXT_STYLE, + key="non-selectable", + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/selection_area/basic/pyproject.toml b/sdk/python/examples/controls/selection_area/basic/pyproject.toml new file mode 100644 index 0000000000..586ad28711 --- /dev/null +++ b/sdk/python/examples/controls/selection_area/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "selection-area-basic" +version = "1.0.0" +description = "Compare selectable and non-selectable text using a SelectionArea wrapper." +requires-python = ">=3.10" +keywords = ["selectionarea", "text", "selection", "typography"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/SelectionArea"] + +[tool.flet.metadata] +title = "SelectionArea basic" +controls = ["SelectionArea", "Column", "Text", "SafeArea"] +layout_pattern = "comparison-stack" +complexity = "basic" +features = ["text selection", "selectable content", "styled text"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/semantics/basic.py b/sdk/python/examples/controls/semantics/basic.py deleted file mode 100644 index 6540f4b080..0000000000 --- a/sdk/python/examples/controls/semantics/basic.py +++ /dev/null @@ -1,30 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_gain_accessibility_focus(e: ft.Event[ft.Semantics]): - print("focus gained") - - def handle_lose_accessibility_focus(e: ft.Event[ft.Semantics]): - print("focus lost") - - page.add( - ft.Column( - controls=[ - ft.Semantics( - label="Input your occupation", - on_did_gain_accessibility_focus=handle_gain_accessibility_focus, - on_did_lose_accessibility_focus=handle_lose_accessibility_focus, - content=ft.TextField( - label="Occupation", - hint_text="Use 20 words or less", - value="What is your occupation?", - ), - ), - ft.Icon(ft.Icons.SETTINGS, color="#c1c1c1"), - ] - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/semantics/basic/main.py b/sdk/python/examples/controls/semantics/basic/main.py new file mode 100644 index 0000000000..2626ace4f3 --- /dev/null +++ b/sdk/python/examples/controls/semantics/basic/main.py @@ -0,0 +1,33 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_gain_accessibility_focus(e: ft.Event[ft.Semantics]): + print("focus gained") + + def handle_lose_accessibility_focus(e: ft.Event[ft.Semantics]): + print("focus lost") + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Semantics( + label="Input your occupation", + on_did_gain_accessibility_focus=handle_gain_accessibility_focus, + on_did_lose_accessibility_focus=handle_lose_accessibility_focus, + content=ft.TextField( + label="Occupation", + hint_text="Use 20 words or less", + value="What is your occupation?", + ), + ), + ft.Icon(ft.Icons.SETTINGS, color="#c1c1c1"), + ] + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/semantics/basic/pyproject.toml b/sdk/python/examples/controls/semantics/basic/pyproject.toml new file mode 100644 index 0000000000..556dfc77d7 --- /dev/null +++ b/sdk/python/examples/controls/semantics/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "semantics-basic" +version = "1.0.0" +description = "Attach semantics labels and accessibility focus events to a text field." +requires-python = ">=3.10" +keywords = ["semantics", "accessibility", "textfield", "events"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Semantics"] + +[tool.flet.metadata] +title = "Semantics basic" +controls = ["Semantics", "TextField", "Icon", "Column", "SafeArea"] +layout_pattern = "form-field" +complexity = "basic" +features = ["accessibility labels", "focus events"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/shader_mask/__init__.py b/sdk/python/examples/controls/shader_mask/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/shader_mask/fade_out_image_bottom.py b/sdk/python/examples/controls/shader_mask/fade_out_image_bottom.py deleted file mode 100644 index 903b6c4fa2..0000000000 --- a/sdk/python/examples/controls/shader_mask/fade_out_image_bottom.py +++ /dev/null @@ -1,27 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Row( - controls=[ - ft.ShaderMask( - content=ft.Image( - src="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" - ), - blend_mode=ft.BlendMode.DST_IN, - border_radius=10, - shader=ft.LinearGradient( - begin=ft.Alignment.TOP_CENTER, - end=ft.Alignment.BOTTOM_CENTER, - colors=[ft.Colors.BLACK, ft.Colors.TRANSPARENT], - stops=[0.5, 1.0], - ), - ), - ] - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/shader_mask/fade_out_image_bottom/main.py b/sdk/python/examples/controls/shader_mask/fade_out_image_bottom/main.py new file mode 100644 index 0000000000..b8bd1b39b3 --- /dev/null +++ b/sdk/python/examples/controls/shader_mask/fade_out_image_bottom/main.py @@ -0,0 +1,29 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Row( + controls=[ + ft.ShaderMask( + content=ft.Image( + src="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" + ), + blend_mode=ft.BlendMode.DST_IN, + border_radius=10, + shader=ft.LinearGradient( + begin=ft.Alignment.TOP_CENTER, + end=ft.Alignment.BOTTOM_CENTER, + colors=[ft.Colors.BLACK, ft.Colors.TRANSPARENT], + stops=[0.5, 1.0], + ), + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/shader_mask/fade_out_image_bottom/pyproject.toml b/sdk/python/examples/controls/shader_mask/fade_out_image_bottom/pyproject.toml new file mode 100644 index 0000000000..0f5b6ef4ed --- /dev/null +++ b/sdk/python/examples/controls/shader_mask/fade_out_image_bottom/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "shader-mask-fade-out-image-bottom" +version = "1.0.0" +description = "Fade the bottom of an image using a linear ShaderMask transparency gradient." +requires-python = ">=3.10" +keywords = ["shadermask", "image", "lineargradient", "fade", "transparency"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Effects/ShaderMask"] + +[tool.flet.metadata] +title = "ShaderMask fade out image bottom" +controls = ["ShaderMask", "Image", "Row", "SafeArea"] +layout_pattern = "single-focus" +complexity = "basic" +features = ["linear gradient", "image fade", "blend mode"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/shader_mask/linear_and_radial_gradients.py b/sdk/python/examples/controls/shader_mask/linear_and_radial_gradients.py deleted file mode 100644 index fcb8fe88a9..0000000000 --- a/sdk/python/examples/controls/shader_mask/linear_and_radial_gradients.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Row( - controls=[ - ft.ShaderMask( - blend_mode=ft.BlendMode.COLOR_BURN, - shader=ft.RadialGradient( - center=ft.Alignment.TOP_LEFT, - radius=1.0, - colors=[ft.Colors.YELLOW, ft.Colors.DEEP_ORANGE_900], - tile_mode=ft.GradientTileMode.CLAMP, - ), - content=ft.Image( - src="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", - width=300, - height=300, - fit=ft.BoxFit.FILL, - ), - ), - ft.ShaderMask( - blend_mode=ft.BlendMode.DST_IN, - shader=ft.LinearGradient( - begin=ft.Alignment.TOP_CENTER, - end=ft.Alignment.BOTTOM_CENTER, - colors=[ft.Colors.BLACK, ft.Colors.TRANSPARENT], - stops=[0.5, 1.0], - ), - content=ft.Image( - src="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" - ), - ), - ] - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/shader_mask/linear_and_radial_gradients/main.py b/sdk/python/examples/controls/shader_mask/linear_and_radial_gradients/main.py new file mode 100644 index 0000000000..4cc30d5adf --- /dev/null +++ b/sdk/python/examples/controls/shader_mask/linear_and_radial_gradients/main.py @@ -0,0 +1,43 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Row( + controls=[ + ft.ShaderMask( + blend_mode=ft.BlendMode.COLOR_BURN, + shader=ft.RadialGradient( + center=ft.Alignment.TOP_LEFT, + radius=1.0, + colors=[ft.Colors.YELLOW, ft.Colors.DEEP_ORANGE_900], + tile_mode=ft.GradientTileMode.CLAMP, + ), + content=ft.Image( + src="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", + width=300, + height=300, + fit=ft.BoxFit.FILL, + ), + ), + ft.ShaderMask( + blend_mode=ft.BlendMode.DST_IN, + shader=ft.LinearGradient( + begin=ft.Alignment.TOP_CENTER, + end=ft.Alignment.BOTTOM_CENTER, + colors=[ft.Colors.BLACK, ft.Colors.TRANSPARENT], + stops=[0.5, 1.0], + ), + content=ft.Image( + src="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" + ), + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/shader_mask/linear_and_radial_gradients/pyproject.toml b/sdk/python/examples/controls/shader_mask/linear_and_radial_gradients/pyproject.toml new file mode 100644 index 0000000000..a51447c94a --- /dev/null +++ b/sdk/python/examples/controls/shader_mask/linear_and_radial_gradients/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "shader-mask-linear-and-radial-gradients" +version = "1.0.0" +description = "Compare radial and linear ShaderMask gradients applied to images." +requires-python = ">=3.10" +keywords = ["shadermask", "image", "gradients", "lineargradient", "radialgradient"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Effects/ShaderMask"] + +[tool.flet.metadata] +title = "ShaderMask linear and radial gradients" +controls = ["ShaderMask", "Image", "Row", "SafeArea"] +layout_pattern = "comparison-grid" +complexity = "basic" +features = ["linear gradient", "radial gradient", "image masking"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/shader_mask/pink_radial_glow.py b/sdk/python/examples/controls/shader_mask/pink_radial_glow.py deleted file mode 100644 index 41724b689b..0000000000 --- a/sdk/python/examples/controls/shader_mask/pink_radial_glow.py +++ /dev/null @@ -1,35 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Row( - controls=[ - ft.Image( - src="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", - width=300, - height=300, - fit=ft.BoxFit.FILL, - ), - ft.ShaderMask( - blend_mode=ft.BlendMode.MULTIPLY, - shader=ft.RadialGradient( - center=ft.Alignment.CENTER, - radius=0.5, - colors=[ft.Colors.WHITE, ft.Colors.PINK], - tile_mode=ft.GradientTileMode.CLAMP, - ), - content=ft.Image( - src="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", - width=300, - height=300, - fit=ft.BoxFit.FILL, - ), - ), - ] - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/shader_mask/pink_radial_glow/main.py b/sdk/python/examples/controls/shader_mask/pink_radial_glow/main.py new file mode 100644 index 0000000000..2368f7d093 --- /dev/null +++ b/sdk/python/examples/controls/shader_mask/pink_radial_glow/main.py @@ -0,0 +1,37 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Row( + controls=[ + ft.Image( + src="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", + width=300, + height=300, + fit=ft.BoxFit.FILL, + ), + ft.ShaderMask( + blend_mode=ft.BlendMode.MULTIPLY, + shader=ft.RadialGradient( + center=ft.Alignment.CENTER, + radius=0.5, + colors=[ft.Colors.WHITE, ft.Colors.PINK], + tile_mode=ft.GradientTileMode.CLAMP, + ), + content=ft.Image( + src="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", + width=300, + height=300, + fit=ft.BoxFit.FILL, + ), + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/shader_mask/pink_radial_glow/pyproject.toml b/sdk/python/examples/controls/shader_mask/pink_radial_glow/pyproject.toml new file mode 100644 index 0000000000..59632826e0 --- /dev/null +++ b/sdk/python/examples/controls/shader_mask/pink_radial_glow/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "shader-mask-pink-radial-glow" +version = "1.0.0" +description = "Apply a radial ShaderMask glow to an image using a burn blend mode." +requires-python = ">=3.10" +keywords = ["shadermask", "image", "radialgradient", "blendmode", "visual-effect"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Effects/ShaderMask"] + +[tool.flet.metadata] +title = "ShaderMask pink radial glow" +controls = ["ShaderMask", "Image", "Row", "SafeArea"] +layout_pattern = "single-focus" +complexity = "basic" +features = ["radial gradient", "blend mode", "image masking"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/shimmer/__init__.py b/sdk/python/examples/controls/shimmer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/shimmer/basic.py b/sdk/python/examples/controls/shimmer/basic.py deleted file mode 100644 index 77b6a49f23..0000000000 --- a/sdk/python/examples/controls/shimmer/basic.py +++ /dev/null @@ -1,21 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Shimmer( - base_color=ft.Colors.with_opacity(0.3, ft.Colors.GREY_400), - highlight_color=ft.Colors.WHITE, - content=ft.Column( - controls=[ - ft.Container(height=80, bgcolor=ft.Colors.GREY_300), - ft.Container(height=80, bgcolor=ft.Colors.GREY_300), - ft.Container(height=80, bgcolor=ft.Colors.GREY_300), - ], - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/shimmer/basic/main.py b/sdk/python/examples/controls/shimmer/basic/main.py new file mode 100644 index 0000000000..d1bc09c7e2 --- /dev/null +++ b/sdk/python/examples/controls/shimmer/basic/main.py @@ -0,0 +1,23 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Shimmer( + base_color=ft.Colors.with_opacity(0.3, ft.Colors.GREY_400), + highlight_color=ft.Colors.WHITE, + content=ft.Column( + controls=[ + ft.Container(height=80, bgcolor=ft.Colors.GREY_300), + ft.Container(height=80, bgcolor=ft.Colors.GREY_300), + ft.Container(height=80, bgcolor=ft.Colors.GREY_300), + ], + ), + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/shimmer/basic/pyproject.toml b/sdk/python/examples/controls/shimmer/basic/pyproject.toml new file mode 100644 index 0000000000..a5684337c7 --- /dev/null +++ b/sdk/python/examples/controls/shimmer/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "shimmer-basic" +version = "1.0.0" +description = "Show a basic Shimmer loading effect over stacked placeholder blocks." +requires-python = ">=3.10" +keywords = ["shimmer", "loading", "placeholder", "animation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Effects/Shimmer"] + +[tool.flet.metadata] +title = "Shimmer basic" +controls = ["Shimmer", "Column", "Container", "SafeArea"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["loading placeholders", "animated highlight"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/shimmer/basic_placeholder.py b/sdk/python/examples/controls/shimmer/basic_placeholder.py deleted file mode 100644 index ed491f8653..0000000000 --- a/sdk/python/examples/controls/shimmer/basic_placeholder.py +++ /dev/null @@ -1,68 +0,0 @@ -import flet as ft - - -def _line(width: int, height: int = 12) -> ft.Control: - return ft.Container( - width=width, - height=height, - bgcolor=ft.Colors.GREY_400, - border_radius=ft.BorderRadius.all(height), - ) - - -def _placeholder_tile() -> ft.Control: - return ft.Container( - padding=ft.Padding.all(16), - bgcolor=ft.Colors.with_opacity(0.3, ft.Colors.WHITE), - border_radius=ft.BorderRadius.all(20), - content=ft.Row( - spacing=16, - vertical_alignment=ft.CrossAxisAlignment.START, - controls=[ - ft.Container( - width=48, - height=48, - bgcolor=ft.Colors.with_opacity(0.5, ft.Colors.GREY_400), - border_radius=ft.BorderRadius.all(24), - content=ft.Icon(ft.Icons.PERSON, color=ft.Colors.GREY_500), - ), - ft.Column( - expand=True, - spacing=10, - controls=[ - _line(160), - _line(120), - ft.Row( - spacing=10, - vertical_alignment=ft.CrossAxisAlignment.CENTER, - controls=[_line(70, 10), _line(90, 10)], - ), - ], - ), - ft.Container( - width=32, - height=32, - bgcolor=ft.Colors.GREY_200, - border_radius=ft.BorderRadius.all(16), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.title = "Shimmer - loading placeholders" - - page.add( - ft.Shimmer( - base_color=ft.Colors.with_opacity(0.3, ft.Colors.GREY_400), - highlight_color=ft.Colors.WHITE, - content=ft.Column( - controls=[_placeholder_tile() for _ in range(3)], - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/shimmer/basic_placeholder/main.py b/sdk/python/examples/controls/shimmer/basic_placeholder/main.py new file mode 100644 index 0000000000..d1b3132b52 --- /dev/null +++ b/sdk/python/examples/controls/shimmer/basic_placeholder/main.py @@ -0,0 +1,70 @@ +import flet as ft + + +def _line(width: int, height: int = 12) -> ft.Control: + return ft.Container( + width=width, + height=height, + bgcolor=ft.Colors.GREY_400, + border_radius=ft.BorderRadius.all(height), + ) + + +def _placeholder_tile() -> ft.Control: + return ft.Container( + padding=ft.Padding.all(16), + bgcolor=ft.Colors.with_opacity(0.3, ft.Colors.WHITE), + border_radius=ft.BorderRadius.all(20), + content=ft.Row( + spacing=16, + vertical_alignment=ft.CrossAxisAlignment.START, + controls=[ + ft.Container( + width=48, + height=48, + bgcolor=ft.Colors.with_opacity(0.5, ft.Colors.GREY_400), + border_radius=ft.BorderRadius.all(24), + content=ft.Icon(ft.Icons.PERSON, color=ft.Colors.GREY_500), + ), + ft.Column( + expand=True, + spacing=10, + controls=[ + _line(160), + _line(120), + ft.Row( + spacing=10, + vertical_alignment=ft.CrossAxisAlignment.CENTER, + controls=[_line(70, 10), _line(90, 10)], + ), + ], + ), + ft.Container( + width=32, + height=32, + bgcolor=ft.Colors.GREY_200, + border_radius=ft.BorderRadius.all(16), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.title = "Shimmer - loading placeholders" + + page.add( + ft.SafeArea( + content=ft.Shimmer( + base_color=ft.Colors.with_opacity(0.3, ft.Colors.GREY_400), + highlight_color=ft.Colors.WHITE, + content=ft.Column( + controls=[_placeholder_tile() for _ in range(3)], + ), + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/shimmer/basic_placeholder/pyproject.toml b/sdk/python/examples/controls/shimmer/basic_placeholder/pyproject.toml new file mode 100644 index 0000000000..e23a646165 --- /dev/null +++ b/sdk/python/examples/controls/shimmer/basic_placeholder/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "shimmer-basic-placeholder" +version = "1.0.0" +description = "Animate card-style loading placeholders with a Shimmer effect." +requires-python = ">=3.10" +keywords = ["shimmer", "placeholder", "loading", "card", "skeleton"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Effects/Shimmer"] + +[tool.flet.metadata] +title = "Shimmer basic placeholder" +controls = ["Shimmer", "Column", "Row", "Container", "Icon", "SafeArea"] +layout_pattern = "list-detail" +complexity = "basic" +features = ["skeleton placeholders", "animated highlight", "card layout"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/shimmer/custom_gradient.py b/sdk/python/examples/controls/shimmer/custom_gradient.py deleted file mode 100644 index 13f22c6992..0000000000 --- a/sdk/python/examples/controls/shimmer/custom_gradient.py +++ /dev/null @@ -1,69 +0,0 @@ -import flet as ft - - -def _stat_block(title: str, subtitle: str) -> ft.Control: - def metric(width: int, height: int = 14) -> ft.Control: - return ft.Container( - width=width, - height=height, - bgcolor=ft.Colors.WHITE, - opacity=0.6, - border_radius=ft.BorderRadius.all(height), - ) - - return ft.Container( - width=200, - padding=ft.Padding.all(20), - bgcolor=ft.Colors.with_opacity(0.1, ft.Colors.BLACK), - border_radius=ft.BorderRadius.all(24), - content=ft.Column( - spacing=16, - controls=[ - metric(140), - ft.Row(spacing=10, controls=[metric(60, 10), metric(90, 10)]), - ft.Container( - border_radius=ft.BorderRadius.all(16), - bgcolor=ft.Colors.WHITE, - opacity=0.35, - ), - ft.Column(spacing=8, controls=[metric(120, 12), metric(160, 12)]), - ft.Text(title, weight=ft.FontWeight.W_600), - ft.Text(subtitle, size=12), - ], - ), - ) - - -def main(page: ft.Page): - page.title = "Shimmer - custom gradients" - page.bgcolor = "#0e0e18" - accent = ft.LinearGradient( - begin=ft.Alignment(-1.0, -0.5), - end=ft.Alignment(1.0, 0.5), - colors=[ - ft.Colors.PURPLE, - ft.Colors.PURPLE, - ft.Colors.AMBER_200, - ft.Colors.PURPLE, - ft.Colors.PURPLE, - ], - stops=[0.0, 0.35, 0.5, 0.65, 1.0], - ) - - cards = ft.Row( - wrap=True, - controls=[ - ft.Shimmer( - gradient=accent, - direction=ft.ShimmerDirection.TTB, - period=2200, - content=_stat_block("Recent activity", "Smooth top-to-bottom sweep"), - ), - ], - ) - - page.add(cards) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/shimmer/custom_gradient/main.py b/sdk/python/examples/controls/shimmer/custom_gradient/main.py new file mode 100644 index 0000000000..8ac6e48b0a --- /dev/null +++ b/sdk/python/examples/controls/shimmer/custom_gradient/main.py @@ -0,0 +1,69 @@ +import flet as ft + + +def _stat_block(title: str, subtitle: str) -> ft.Control: + def metric(width: int, height: int = 14) -> ft.Control: + return ft.Container( + width=width, + height=height, + bgcolor=ft.Colors.WHITE, + opacity=0.6, + border_radius=ft.BorderRadius.all(height), + ) + + return ft.Container( + width=200, + padding=ft.Padding.all(20), + bgcolor=ft.Colors.with_opacity(0.1, ft.Colors.BLACK), + border_radius=ft.BorderRadius.all(24), + content=ft.Column( + spacing=16, + controls=[ + metric(140), + ft.Row(spacing=10, controls=[metric(60, 10), metric(90, 10)]), + ft.Container( + border_radius=ft.BorderRadius.all(16), + bgcolor=ft.Colors.WHITE, + opacity=0.35, + ), + ft.Column(spacing=8, controls=[metric(120, 12), metric(160, 12)]), + ft.Text(title, weight=ft.FontWeight.W_600), + ft.Text(subtitle, size=12), + ], + ), + ) + + +def main(page: ft.Page): + page.title = "Shimmer - custom gradients" + page.bgcolor = "#0e0e18" + accent = ft.LinearGradient( + begin=ft.Alignment(-1.0, -0.5), + end=ft.Alignment(1.0, 0.5), + colors=[ + ft.Colors.PURPLE, + ft.Colors.PURPLE, + ft.Colors.AMBER_200, + ft.Colors.PURPLE, + ft.Colors.PURPLE, + ], + stops=[0.0, 0.35, 0.5, 0.65, 1.0], + ) + + cards = ft.Row( + wrap=True, + controls=[ + ft.Shimmer( + gradient=accent, + direction=ft.ShimmerDirection.TTB, + period=2200, + content=_stat_block("Recent activity", "Smooth top-to-bottom sweep"), + ), + ], + ) + + page.add(ft.SafeArea(content=cards)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/shimmer/custom_gradient/pyproject.toml b/sdk/python/examples/controls/shimmer/custom_gradient/pyproject.toml new file mode 100644 index 0000000000..ec46c8b0d9 --- /dev/null +++ b/sdk/python/examples/controls/shimmer/custom_gradient/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "shimmer-custom-gradient" +version = "1.0.0" +description = "Use a custom gradient Shimmer animation over a dashboard-style stat card." +requires-python = ">=3.10" +keywords = ["shimmer", "gradient", "animation", "dashboard", "card"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Effects/Shimmer"] + +[tool.flet.metadata] +title = "Shimmer custom gradient" +controls = ["Shimmer", "Row", "Column", "Container", "Text", "SafeArea"] +layout_pattern = "dashboard" +complexity = "basic" +features = ["custom gradient", "animated highlight", "dashboard card"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/slider/__init__.py b/sdk/python/examples/controls/slider/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/slider/basic.py b/sdk/python/examples/controls/slider/basic.py deleted file mode 100644 index 4ba9492c32..0000000000 --- a/sdk/python/examples/controls/slider/basic.py +++ /dev/null @@ -1,14 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Text("Default slider:"), - ft.Slider(), - ft.Text("Default disabled slider:"), - ft.Slider(disabled=True), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/slider/basic/main.py b/sdk/python/examples/controls/slider/basic/main.py new file mode 100644 index 0000000000..a0e344977e --- /dev/null +++ b/sdk/python/examples/controls/slider/basic/main.py @@ -0,0 +1,20 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Default slider:"), + ft.Slider(), + ft.Text("Default disabled slider:"), + ft.Slider(disabled=True), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/slider/basic/pyproject.toml b/sdk/python/examples/controls/slider/basic/pyproject.toml new file mode 100644 index 0000000000..fc7205e833 --- /dev/null +++ b/sdk/python/examples/controls/slider/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "slider-basic" +version = "1.0.0" +description = "Compare enabled and disabled Slider controls with the default settings." +requires-python = ">=3.10" +keywords = ["slider", "input", "disabled", "basic"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Slider"] + +[tool.flet.metadata] +title = "Slider basic" +controls = ["Slider", "Text", "Column", "SafeArea"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["enabled and disabled states"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/slider/custom_label.py b/sdk/python/examples/controls/slider/custom_label.py deleted file mode 100644 index e5de954a77..0000000000 --- a/sdk/python/examples/controls/slider/custom_label.py +++ /dev/null @@ -1,14 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Text("Slider with value:"), - ft.Slider(value=0.3), - ft.Text("Slider with a custom range and label:"), - ft.Slider(min=0, max=100, divisions=10, label="{value}%"), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/slider/custom_label/main.py b/sdk/python/examples/controls/slider/custom_label/main.py new file mode 100644 index 0000000000..f9899ba202 --- /dev/null +++ b/sdk/python/examples/controls/slider/custom_label/main.py @@ -0,0 +1,20 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Slider with value:"), + ft.Slider(value=0.3), + ft.Text("Slider with a custom range and label:"), + ft.Slider(min=0, max=100, divisions=10, label="{value}%"), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/slider/custom_label/pyproject.toml b/sdk/python/examples/controls/slider/custom_label/pyproject.toml new file mode 100644 index 0000000000..d056ba6632 --- /dev/null +++ b/sdk/python/examples/controls/slider/custom_label/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "slider-custom-label" +version = "1.0.0" +description = "Show Slider values with a preset position and a custom percentage label." +requires-python = ">=3.10" +keywords = ["slider", "label", "divisions", "range"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Slider"] + +[tool.flet.metadata] +title = "Slider custom label" +controls = ["Slider", "Text", "Column", "SafeArea"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["custom label", "custom range", "divisions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/slider/handling_events.py b/sdk/python/examples/controls/slider/handling_events.py deleted file mode 100644 index 818c6d7a4f..0000000000 --- a/sdk/python/examples/controls/slider/handling_events.py +++ /dev/null @@ -1,24 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def slider_changed(e: ft.Event[ft.Slider]): - message.value = f"Slider changed to {e.control.value}" - message.update() - - page.add( - ft.Text("Slider with 'on_change' event:"), - ft.Slider( - key="slider", - min=0, - max=100, - divisions=10, - label="{value}%", - on_change=slider_changed, - ), - message := ft.Text(), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/slider/handling_events/main.py b/sdk/python/examples/controls/slider/handling_events/main.py new file mode 100644 index 0000000000..ef9aaa4f7a --- /dev/null +++ b/sdk/python/examples/controls/slider/handling_events/main.py @@ -0,0 +1,30 @@ +import flet as ft + + +def main(page: ft.Page): + def slider_changed(e: ft.Event[ft.Slider]): + message.value = f"Slider changed to {e.control.value}" + message.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Slider with 'on_change' event:"), + ft.Slider( + key="slider", + min=0, + max=100, + divisions=10, + label="{value}%", + on_change=slider_changed, + ), + message := ft.Text(), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/slider/handling_events/pyproject.toml b/sdk/python/examples/controls/slider/handling_events/pyproject.toml new file mode 100644 index 0000000000..089cc264e5 --- /dev/null +++ b/sdk/python/examples/controls/slider/handling_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "slider-handling-events" +version = "1.0.0" +description = "Update a text label live when a Slider value changes." +requires-python = ">=3.10" +keywords = ["slider", "events", "on_change", "text"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Slider"] + +[tool.flet.metadata] +title = "Slider handling events" +controls = ["Slider", "Text", "Column", "SafeArea"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["value change handling", "live updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/slider/random_values.py b/sdk/python/examples/controls/slider/random_values.py deleted file mode 100644 index 5e929b3501..0000000000 --- a/sdk/python/examples/controls/slider/random_values.py +++ /dev/null @@ -1,27 +0,0 @@ -import asyncio -import random - -import flet as ft - - -async def main(page: ft.Page): - def handle_slider_change(e: ft.Event[ft.Slider]): - message.value = f"Slider.value changed to {e.control.value}" - message.update() - - page.add( - ft.Text("Slider with 'on_change' event:"), - slider := ft.Slider(label="{value}", on_change=handle_slider_change), - message := ft.Text(), - ) - - while True: - await asyncio.sleep(1) - slider.value = random.random() - event = ft.Event("_", slider, data=slider.value) - handle_slider_change(event) - slider.update() - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/slider/random_values/main.py b/sdk/python/examples/controls/slider/random_values/main.py new file mode 100644 index 0000000000..4a325aff2c --- /dev/null +++ b/sdk/python/examples/controls/slider/random_values/main.py @@ -0,0 +1,35 @@ +import asyncio +import random + +import flet as ft + + +async def main(page: ft.Page): + def handle_slider_change(e: ft.Event[ft.Slider]): + message.value = f"Slider.value changed to {e.control.value}" + message.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Slider with 'on_change' event:"), + slider := ft.Slider( + label="{value}", on_change=handle_slider_change + ), + message := ft.Text(), + ] + ) + ) + ) + + while True: + await asyncio.sleep(1) + slider.value = random.random() + event = ft.Event("_", slider, data=slider.value) + handle_slider_change(event) + slider.update() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/slider/random_values/pyproject.toml b/sdk/python/examples/controls/slider/random_values/pyproject.toml new file mode 100644 index 0000000000..18847f2766 --- /dev/null +++ b/sdk/python/examples/controls/slider/random_values/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "slider-random-values" +version = "1.0.0" +description = "Animate a Slider with random values and mirror each update in a text label." +requires-python = ">=3.10" +keywords = ["slider", "random", "animation", "events", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Slider"] + +[tool.flet.metadata] +title = "Slider random values" +controls = ["Slider", "Text", "Column", "SafeArea"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["async updates", "random values", "live updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/snack_bar/__init__.py b/sdk/python/examples/controls/snack_bar/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/snack_bar/action.py b/sdk/python/examples/controls/snack_bar/action.py deleted file mode 100644 index 8bc496c38f..0000000000 --- a/sdk/python/examples/controls/snack_bar/action.py +++ /dev/null @@ -1,35 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def open_simple_action(e: ft.Event[ft.Button]): - page.show_dialog( - ft.SnackBar( - ft.Text("The file has been deleted."), - action="Undo", - on_action=lambda e: print("Simple Undo clicked"), - ) - ) - - def open_custom_action(e: ft.Event[ft.Button]): - page.show_dialog( - ft.SnackBar( - ft.Text("The directory has been deleted."), - persist=False, - action=ft.SnackBarAction( - label="Undo delete", - text_color=ft.Colors.YELLOW, - bgcolor=ft.Colors.BLUE, - on_click=lambda e: print("Custom Undo clicked"), - ), - ) - ) - - page.add( - ft.Button("Open SnackBar with a Simple action", on_click=open_simple_action), - ft.Button("Open SnackBar with a Custom action", on_click=open_custom_action), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/snack_bar/action/main.py b/sdk/python/examples/controls/snack_bar/action/main.py new file mode 100644 index 0000000000..73925c694c --- /dev/null +++ b/sdk/python/examples/controls/snack_bar/action/main.py @@ -0,0 +1,47 @@ +import flet as ft + + +def main(page: ft.Page): + def open_simple_action(e: ft.Event[ft.Button]): + page.show_dialog( + ft.SnackBar( + ft.Text("The file has been deleted."), + action="Undo", + on_action=lambda e: print("Simple Undo clicked"), + ) + ) + + def open_custom_action(e: ft.Event[ft.Button]): + page.show_dialog( + ft.SnackBar( + ft.Text("The directory has been deleted."), + persist=False, + action=ft.SnackBarAction( + label="Undo delete", + text_color=ft.Colors.YELLOW, + bgcolor=ft.Colors.BLUE, + on_click=lambda e: print("Custom Undo clicked"), + ), + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + "Open SnackBar with a Simple action", + on_click=open_simple_action, + ), + ft.Button( + "Open SnackBar with a Custom action", + on_click=open_custom_action, + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/snack_bar/action/pyproject.toml b/sdk/python/examples/controls/snack_bar/action/pyproject.toml new file mode 100644 index 0000000000..d02f8e012b --- /dev/null +++ b/sdk/python/examples/controls/snack_bar/action/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "snack-bar-action" +version = "1.0.0" +description = "Compare simple and custom SnackBar actions opened from separate buttons." +requires-python = ">=3.10" +keywords = ["snackbar", "action", "button", "feedback"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/SnackBar"] + +[tool.flet.metadata] +title = "SnackBar action" +controls = ["SnackBar", "SnackBarAction", "Button", "Column", "SafeArea"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["simple action", "custom action"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/snack_bar/basic.py b/sdk/python/examples/controls/snack_bar/basic.py deleted file mode 100644 index ffd692e09b..0000000000 --- a/sdk/python/examples/controls/snack_bar/basic.py +++ /dev/null @@ -1,12 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def on_click(e: ft.Event[ft.Button]): - page.show_dialog(ft.SnackBar(ft.Text("Hello, world!"))) - - page.add(ft.Button("Open SnackBar", on_click=on_click)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/snack_bar/basic/main.py b/sdk/python/examples/controls/snack_bar/basic/main.py new file mode 100644 index 0000000000..01a5947ad2 --- /dev/null +++ b/sdk/python/examples/controls/snack_bar/basic/main.py @@ -0,0 +1,12 @@ +import flet as ft + + +def main(page: ft.Page): + def on_click(e: ft.Event[ft.Button]): + page.show_dialog(ft.SnackBar(ft.Text("Hello, world!"))) + + page.add(ft.SafeArea(content=ft.Button("Open SnackBar", on_click=on_click))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/snack_bar/basic/pyproject.toml b/sdk/python/examples/controls/snack_bar/basic/pyproject.toml new file mode 100644 index 0000000000..935c517c00 --- /dev/null +++ b/sdk/python/examples/controls/snack_bar/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "snack-bar-basic" +version = "1.0.0" +description = "Open a basic SnackBar from a button tap." +requires-python = ">=3.10" +keywords = ["snackbar", "dialog", "button", "feedback"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/SnackBar"] + +[tool.flet.metadata] +title = "SnackBar basic" +controls = ["SnackBar", "Button", "Text", "SafeArea"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["dialog open"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/snack_bar/counter.py b/sdk/python/examples/controls/snack_bar/counter.py deleted file mode 100644 index cbe9759c8a..0000000000 --- a/sdk/python/examples/controls/snack_bar/counter.py +++ /dev/null @@ -1,38 +0,0 @@ -import flet as ft - - -class Data: - def __init__(self) -> None: - self.counter = 0 - - def increment(self): - self.counter += 1 - - def decrement(self): - self.counter -= 1 - - -data = Data() - - -def main(page: ft.Page): - page.title = "SnackBar Example" - - snack_bar = ft.SnackBar( - content=ft.Text("You did it!"), - action="Undo it!", - on_action=lambda e: data.decrement(), - ) - - def handle_button_click(e: ft.Event[ft.Button]): - data.increment() - snack_bar.content.value = f"You did it x {data.counter}" - if not snack_bar.open: - page.show_dialog(snack_bar) - page.update() - - page.add(ft.Button("Open SnackBar", on_click=handle_button_click)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/snack_bar/counter/main.py b/sdk/python/examples/controls/snack_bar/counter/main.py new file mode 100644 index 0000000000..6b527d8518 --- /dev/null +++ b/sdk/python/examples/controls/snack_bar/counter/main.py @@ -0,0 +1,40 @@ +import flet as ft + + +class Data: + def __init__(self) -> None: + self.counter = 0 + + def increment(self): + self.counter += 1 + + def decrement(self): + self.counter -= 1 + + +data = Data() + + +def main(page: ft.Page): + page.title = "SnackBar Example" + + snack_bar = ft.SnackBar( + content=ft.Text("You did it!"), + action="Undo it!", + on_action=lambda e: data.decrement(), + ) + + def handle_button_click(e: ft.Event[ft.Button]): + data.increment() + snack_bar.content.value = f"You did it x {data.counter}" + if not snack_bar.open: + page.show_dialog(snack_bar) + page.update() + + page.add( + ft.SafeArea(content=ft.Button("Open SnackBar", on_click=handle_button_click)) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/snack_bar/counter/pyproject.toml b/sdk/python/examples/controls/snack_bar/counter/pyproject.toml new file mode 100644 index 0000000000..801708e796 --- /dev/null +++ b/sdk/python/examples/controls/snack_bar/counter/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "snack-bar-counter" +version = "1.0.0" +description = "Count repeated SnackBar opens and support undoing the latest increment." +requires-python = ">=3.10" +keywords = ["snackbar", "counter", "undo", "feedback"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/SnackBar"] + +[tool.flet.metadata] +title = "SnackBar counter" +controls = ["SnackBar", "Button", "Text", "SafeArea"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["counter updates", "undo action"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/stack/__init__.py b/sdk/python/examples/controls/stack/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/stack/absolute_positioning.py b/sdk/python/examples/controls/stack/absolute_positioning.py deleted file mode 100644 index 7b02c41874..0000000000 --- a/sdk/python/examples/controls/stack/absolute_positioning.py +++ /dev/null @@ -1,65 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - page.add( - ft.Container( - border_radius=8, - padding=5, - width=200, - height=200, - bgcolor=ft.Colors.BLACK, - content=ft.Stack( - controls=[ - ft.Container( - width=20, - height=20, - bgcolor=ft.Colors.RED, - border_radius=5, - ), - ft.Container( - width=20, - height=20, - bgcolor=ft.Colors.YELLOW, - border_radius=5, - right=0, - ), - ft.Container( - width=20, - height=20, - bgcolor=ft.Colors.BLUE, - border_radius=5, - right=0, - bottom=0, - ), - ft.Container( - width=20, - height=20, - bgcolor=ft.Colors.GREEN, - border_radius=5, - left=0, - bottom=0, - ), - ft.Column( - left=85, - top=85, - controls=[ - ft.Container( - width=20, - height=20, - bgcolor=ft.Colors.PURPLE, - border_radius=5, - ) - ], - ), - ] - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/stack/absolute_positioning/main.py b/sdk/python/examples/controls/stack/absolute_positioning/main.py new file mode 100644 index 0000000000..16bf50da08 --- /dev/null +++ b/sdk/python/examples/controls/stack/absolute_positioning/main.py @@ -0,0 +1,67 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + page.add( + ft.SafeArea( + content=ft.Container( + border_radius=8, + padding=5, + width=200, + height=200, + bgcolor=ft.Colors.BLACK, + content=ft.Stack( + controls=[ + ft.Container( + width=20, + height=20, + bgcolor=ft.Colors.RED, + border_radius=5, + ), + ft.Container( + width=20, + height=20, + bgcolor=ft.Colors.YELLOW, + border_radius=5, + right=0, + ), + ft.Container( + width=20, + height=20, + bgcolor=ft.Colors.BLUE, + border_radius=5, + right=0, + bottom=0, + ), + ft.Container( + width=20, + height=20, + bgcolor=ft.Colors.GREEN, + border_radius=5, + left=0, + bottom=0, + ), + ft.Column( + left=85, + top=85, + controls=[ + ft.Container( + width=20, + height=20, + bgcolor=ft.Colors.PURPLE, + border_radius=5, + ) + ], + ), + ] + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/stack/absolute_positioning/pyproject.toml b/sdk/python/examples/controls/stack/absolute_positioning/pyproject.toml new file mode 100644 index 0000000000..19508589b2 --- /dev/null +++ b/sdk/python/examples/controls/stack/absolute_positioning/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "stack-absolute-positioning" +version = "1.0.0" +description = "Places colored containers at fixed corners and center positions inside a Stack." +requires-python = ">=3.10" +keywords = ["stack", "layout", "positioning", "absolute", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Stack"] + +[tool.flet.metadata] +title = "Absolute positioning" +controls = ["SafeArea", "Container", "Stack", "Column"] +layout_pattern = "center-stage" +complexity = "basic" +features = ["absolute positioning"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/stack/online_avatar.py b/sdk/python/examples/controls/stack/online_avatar.py deleted file mode 100644 index 328d4c9683..0000000000 --- a/sdk/python/examples/controls/stack/online_avatar.py +++ /dev/null @@ -1,23 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Stack( - width=40, - height=40, - controls=[ - ft.CircleAvatar( - foreground_image_src="https://avatars.githubusercontent.com/u/5041459?s=88&v=4" - ), - ft.Container( - content=ft.CircleAvatar(bgcolor=ft.Colors.GREEN, radius=5), - alignment=ft.Alignment.BOTTOM_LEFT, - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/stack/online_avatar/main.py b/sdk/python/examples/controls/stack/online_avatar/main.py new file mode 100644 index 0000000000..d78318471a --- /dev/null +++ b/sdk/python/examples/controls/stack/online_avatar/main.py @@ -0,0 +1,25 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Stack( + width=40, + height=40, + controls=[ + ft.CircleAvatar( + foreground_image_src="https://avatars.githubusercontent.com/u/5041459?s=88&v=4" + ), + ft.Container( + alignment=ft.Alignment.BOTTOM_LEFT, + content=ft.CircleAvatar(bgcolor=ft.Colors.GREEN, radius=5), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/stack/online_avatar/pyproject.toml b/sdk/python/examples/controls/stack/online_avatar/pyproject.toml new file mode 100644 index 0000000000..6eb6c68f12 --- /dev/null +++ b/sdk/python/examples/controls/stack/online_avatar/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "stack-online-avatar" +version = "1.0.0" +description = "Overlays an online-status badge on top of a profile avatar using Stack." +requires-python = ">=3.10" +keywords = ["stack", "avatar", "status", "overlay", "badge"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Stack"] + +[tool.flet.metadata] +title = "Online avatar" +controls = ["SafeArea", "Stack", "CircleAvatar", "Container"] +layout_pattern = "overlay" +complexity = "basic" +features = ["status indicator"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/stack/text_on_image.py b/sdk/python/examples/controls/stack/text_on_image.py deleted file mode 100644 index dfff5b0e43..0000000000 --- a/sdk/python/examples/controls/stack/text_on_image.py +++ /dev/null @@ -1,33 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Stack( - width=300, - height=300, - controls=[ - ft.Image( - src="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", - width=300, - height=300, - fit=ft.BoxFit.CONTAIN, - ), - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Text( - value="Image title", - color=ft.Colors.SURFACE_TINT, - size=40, - weight=ft.FontWeight.BOLD, - opacity=0.5, - ) - ], - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/stack/text_on_image/main.py b/sdk/python/examples/controls/stack/text_on_image/main.py new file mode 100644 index 0000000000..d86b58f4d5 --- /dev/null +++ b/sdk/python/examples/controls/stack/text_on_image/main.py @@ -0,0 +1,36 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Stack( + width=300, + height=300, + controls=[ + ft.Image( + src="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", + width=300, + height=300, + fit=ft.BoxFit.CONTAIN, + ), + ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Text( + value="Image title", + color=ft.Colors.SURFACE_TINT, + size=40, + weight=ft.FontWeight.BOLD, + opacity=0.5, + ) + ], + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/stack/text_on_image/pyproject.toml b/sdk/python/examples/controls/stack/text_on_image/pyproject.toml new file mode 100644 index 0000000000..c4860cfeb3 --- /dev/null +++ b/sdk/python/examples/controls/stack/text_on_image/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "stack-text-on-image" +version = "1.0.0" +description = "Layers a centered title over a base64-backed image inside a Stack." +requires-python = ">=3.10" +keywords = ["stack", "image", "text", "overlay", "base64"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Stack"] + +[tool.flet.metadata] +title = "Text on image" +controls = ["SafeArea", "Stack", "Image", "Row", "Text"] +layout_pattern = "overlay" +complexity = "basic" +features = ["text overlay"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/submenu_button/__init__.py b/sdk/python/examples/controls/submenu_button/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/submenu_button/basic.py b/sdk/python/examples/controls/submenu_button/basic.py deleted file mode 100644 index 7c460ce4c1..0000000000 --- a/sdk/python/examples/controls/submenu_button/basic.py +++ /dev/null @@ -1,140 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.padding = 0 - page.spacing = 0 - - def handle_color_click(e: ft.Event[ft.MenuItemButton]): - color = e.control.content.value - background_container.content.value = f"{color} background color" - background_container.bgcolor = color.lower() - page.update() - - def handle_alignment_click(e: ft.Event[ft.MenuItemButton]): - print( - f"bg_container.alignment: {background_container.alignment}, bg_container.content: {background_container.content}" - ) - background_container.alignment = e.control.data - print( - f"e.control.content.value: {e.control.content.value}, e.control.data: {e.control.data}" - ) - page.update() - - def handle_on_hover(e: ft.Event[ft.MenuItemButton]): - print(f"{e.control.content.value}.on_hover") - - menubar = ft.MenuBar( - expand=True, - controls=[ - ft.SubmenuButton( - content=ft.Text("Change Body"), - key="submenubutton", - controls=[ - ft.SubmenuButton( - content=ft.Text("BG Color"), - leading=ft.Icon(ft.Icons.COLORIZE), - controls=[ - ft.MenuItemButton( - content=ft.Text("Blue"), - on_click=handle_color_click, - on_hover=handle_on_hover, - style=ft.ButtonStyle( - bgcolor={ft.ControlState.HOVERED: ft.Colors.BLUE} - ), - ), - ft.MenuItemButton( - content=ft.Text("Green"), - on_click=handle_color_click, - on_hover=handle_on_hover, - style=ft.ButtonStyle( - bgcolor={ft.ControlState.HOVERED: ft.Colors.GREEN} - ), - ), - ft.MenuItemButton( - content=ft.Text("Red"), - on_click=handle_color_click, - on_hover=handle_on_hover, - style=ft.ButtonStyle( - bgcolor={ft.ControlState.HOVERED: ft.Colors.RED} - ), - ), - ], - ), - ft.SubmenuButton( - content=ft.Text("Text alignment"), - leading=ft.Icon(ft.Icons.LOCATION_PIN), - controls=[ - ft.MenuItemButton( - content=ft.Text("bottom_center"), - data=ft.Alignment.BOTTOM_CENTER, - on_click=handle_alignment_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.GREY_100 - } - ), - ), - ft.MenuItemButton( - content=ft.Text("bottom_left"), - data=ft.Alignment.BOTTOM_LEFT, - on_click=handle_alignment_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.GREY_100 - } - ), - ), - ft.MenuItemButton( - content=ft.Text("top_center"), - data=ft.Alignment.TOP_CENTER, - on_click=handle_alignment_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.GREY_100 - } - ), - ), - ft.MenuItemButton( - content=ft.Text("center_left"), - data=ft.Alignment.CENTER_LEFT, - on_click=handle_alignment_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.GREY_100 - } - ), - ), - ft.MenuItemButton( - content=ft.Text("center_right"), - data=ft.Alignment.CENTER_RIGHT, - on_click=handle_alignment_click, - style=ft.ButtonStyle( - bgcolor={ - ft.ControlState.HOVERED: ft.Colors.GREY_100 - } - ), - ), - ], - ), - ], - ) - ], - ) - - page.add( - ft.Row(controls=[menubar]), - background_container := ft.Container( - expand=True, - bgcolor=ft.Colors.SURFACE_TINT, - alignment=ft.Alignment.CENTER, - content=ft.Text( - value="Choose a bgcolor from the menu", - style=ft.TextStyle(size=24, weight=ft.FontWeight.BOLD), - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/submenu_button/basic/main.py b/sdk/python/examples/controls/submenu_button/basic/main.py new file mode 100644 index 0000000000..ab6e627de2 --- /dev/null +++ b/sdk/python/examples/controls/submenu_button/basic/main.py @@ -0,0 +1,141 @@ +import flet as ft + + +def main(page: ft.Page): + page.padding = 0 + page.spacing = 0 + + def handle_color_click(e: ft.Event[ft.MenuItemButton]): + color = e.control.content.value + background_container.content.value = f"{color} background color" + background_container.bgcolor = color.lower() + + def handle_alignment_click(e: ft.Event[ft.MenuItemButton]): + background_container.alignment = e.control.data + + def handle_on_hover(e: ft.Event[ft.MenuItemButton]): + print(f"{e.control.content.value}.on_hover") + + menubar = ft.MenuBar( + expand=True, + controls=[ + ft.SubmenuButton( + content=ft.Text("Change Body"), + key="submenubutton", + controls=[ + ft.SubmenuButton( + content=ft.Text("BG Color"), + leading=ft.Icon(ft.Icons.COLORIZE), + controls=[ + ft.MenuItemButton( + content=ft.Text("Blue"), + on_click=handle_color_click, + on_hover=handle_on_hover, + style=ft.ButtonStyle( + bgcolor={ft.ControlState.HOVERED: ft.Colors.BLUE} + ), + ), + ft.MenuItemButton( + content=ft.Text("Green"), + on_click=handle_color_click, + on_hover=handle_on_hover, + style=ft.ButtonStyle( + bgcolor={ft.ControlState.HOVERED: ft.Colors.GREEN} + ), + ), + ft.MenuItemButton( + content=ft.Text("Red"), + on_click=handle_color_click, + on_hover=handle_on_hover, + style=ft.ButtonStyle( + bgcolor={ft.ControlState.HOVERED: ft.Colors.RED} + ), + ), + ], + ), + ft.SubmenuButton( + content=ft.Text("Text alignment"), + leading=ft.Icon(ft.Icons.LOCATION_PIN), + controls=[ + ft.MenuItemButton( + content=ft.Text("bottom_center"), + data=ft.Alignment.BOTTOM_CENTER, + on_click=handle_alignment_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ft.Colors.GREY_100 + } + ), + ), + ft.MenuItemButton( + content=ft.Text("bottom_left"), + data=ft.Alignment.BOTTOM_LEFT, + on_click=handle_alignment_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ft.Colors.GREY_100 + } + ), + ), + ft.MenuItemButton( + content=ft.Text("top_center"), + data=ft.Alignment.TOP_CENTER, + on_click=handle_alignment_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ft.Colors.GREY_100 + } + ), + ), + ft.MenuItemButton( + content=ft.Text("center_left"), + data=ft.Alignment.CENTER_LEFT, + on_click=handle_alignment_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ft.Colors.GREY_100 + } + ), + ), + ft.MenuItemButton( + content=ft.Text("center_right"), + data=ft.Alignment.CENTER_RIGHT, + on_click=handle_alignment_click, + style=ft.ButtonStyle( + bgcolor={ + ft.ControlState.HOVERED: ft.Colors.GREY_100 + } + ), + ), + ], + ), + ], + ) + ], + ) + + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + spacing=0, + controls=[ + ft.Row(controls=[menubar]), + background_container := ft.Container( + expand=True, + bgcolor=ft.Colors.SURFACE_TINT, + alignment=ft.Alignment.CENTER, + content=ft.Text( + value="Choose a bgcolor from the menu", + style=ft.TextStyle(size=24, weight=ft.FontWeight.BOLD), + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/submenu_button/basic/pyproject.toml b/sdk/python/examples/controls/submenu_button/basic/pyproject.toml new file mode 100644 index 0000000000..43a34f8d99 --- /dev/null +++ b/sdk/python/examples/controls/submenu_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "submenu-button-basic" +version = "1.0.0" +description = "Changes background color and text alignment from nested SubmenuButton menu actions." +requires-python = ">=3.10" +keywords = ["submenu button", "menu", "nested", "alignment", "color"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/SubmenuButton"] + +[tool.flet.metadata] +title = "SubmenuButton basic" +controls = ["SafeArea", "Column", "Row", "MenuBar", "SubmenuButton", "MenuItemButton", "Container", "Text", "Icon"] +layout_pattern = "toolbar-content" +complexity = "basic" +features = ["nested menus", "live style updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/submenu_button/standalone.py b/sdk/python/examples/controls/submenu_button/standalone.py deleted file mode 100644 index 1b99645e3e..0000000000 --- a/sdk/python/examples/controls/submenu_button/standalone.py +++ /dev/null @@ -1,48 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - smb = ft.SubmenuButton( - content=ft.Text("Text Styles"), - key="smbutton", - menu_style=ft.MenuStyle( - alignment=ft.Alignment.CENTER_RIGHT, side=ft.BorderSide(1) - ), - controls=[ - ft.MenuItemButton( - content=ft.Text("Underlined"), - on_click=lambda e: print(f"{e.control.content.value}.on_click"), - style=ft.ButtonStyle( - text_style={ - ft.ControlState.HOVERED: ft.TextStyle( - decoration=ft.TextDecoration.UNDERLINE - ) - } - ), - ), - ft.MenuItemButton( - content=ft.Text("Bold"), - on_click=lambda e: print(f"{e.control.content.value}.on_click"), - style=ft.ButtonStyle( - text_style={ - ft.ControlState.HOVERED: ft.TextStyle(weight=ft.FontWeight.BOLD) - } - ), - ), - ft.MenuItemButton( - content=ft.Text("Italic"), - on_click=lambda e: print(f"{e.control.content.value}.on_click"), - style=ft.ButtonStyle( - text_style={ft.ControlState.HOVERED: ft.TextStyle(italic=True)} - ), - ), - ], - ) - - page.add(ft.Row(controls=[smb])) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/submenu_button/standalone/main.py b/sdk/python/examples/controls/submenu_button/standalone/main.py new file mode 100644 index 0000000000..b4810f5a66 --- /dev/null +++ b/sdk/python/examples/controls/submenu_button/standalone/main.py @@ -0,0 +1,48 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + smb = ft.SubmenuButton( + content=ft.Text("Text Styles"), + key="smbutton", + menu_style=ft.MenuStyle( + alignment=ft.Alignment.CENTER_RIGHT, side=ft.BorderSide(1) + ), + controls=[ + ft.MenuItemButton( + content=ft.Text("Underlined"), + on_click=lambda e: print(f"{e.control.content.value}.on_click"), + style=ft.ButtonStyle( + text_style={ + ft.ControlState.HOVERED: ft.TextStyle( + decoration=ft.TextDecoration.UNDERLINE + ) + } + ), + ), + ft.MenuItemButton( + content=ft.Text("Bold"), + on_click=lambda e: print(f"{e.control.content.value}.on_click"), + style=ft.ButtonStyle( + text_style={ + ft.ControlState.HOVERED: ft.TextStyle(weight=ft.FontWeight.BOLD) + } + ), + ), + ft.MenuItemButton( + content=ft.Text("Italic"), + on_click=lambda e: print(f"{e.control.content.value}.on_click"), + style=ft.ButtonStyle( + text_style={ft.ControlState.HOVERED: ft.TextStyle(italic=True)} + ), + ), + ], + ) + + page.add(ft.SafeArea(content=ft.Row(controls=[smb]))) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/submenu_button/standalone/pyproject.toml b/sdk/python/examples/controls/submenu_button/standalone/pyproject.toml new file mode 100644 index 0000000000..a48b321cf5 --- /dev/null +++ b/sdk/python/examples/controls/submenu_button/standalone/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "submenu-button-standalone" +version = "1.0.0" +description = "Shows a standalone SubmenuButton with hover-driven text style previews." +requires-python = ">=3.10" +keywords = ["submenu button", "menu", "text style", "hover", "standalone"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Navigation/SubmenuButton"] + +[tool.flet.metadata] +title = "Standalone SubmenuButton" +controls = ["SafeArea", "Row", "SubmenuButton", "MenuItemButton", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["hover styling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/switch/__init__.py b/sdk/python/examples/controls/switch/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/switch/basic.py b/sdk/python/examples/controls/switch/basic.py deleted file mode 100644 index 3c8eeeef39..0000000000 --- a/sdk/python/examples/controls/switch/basic.py +++ /dev/null @@ -1,25 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_button_click(e: ft.Event[ft.Button]): - message.value = ( - f"Switch values are: {c1.value}, {c2.value}, {c3.value}, {c4.value}." - ) - page.update() - - page.add( - c1 := ft.Switch(label="Unchecked switch", value=False), - c2 := ft.Switch(label="Checked switch", value=True), - c3 := ft.Switch(label="Disabled switch", disabled=True), - c4 := ft.Switch( - label="Switch with rendered label_position='left'", - label_position=ft.LabelPosition.LEFT, - ), - ft.Button(content="Submit", on_click=handle_button_click), - message := ft.Text(), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/switch/basic/main.py b/sdk/python/examples/controls/switch/basic/main.py new file mode 100644 index 0000000000..e559bcdea2 --- /dev/null +++ b/sdk/python/examples/controls/switch/basic/main.py @@ -0,0 +1,30 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_button_click(e: ft.Event[ft.Button]): + message.value = ( + f"Switch values are: {c1.value}, {c2.value}, {c3.value}, {c4.value}." + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + c1 := ft.Switch(label="Unchecked switch", value=False), + c2 := ft.Switch(label="Checked switch", value=True), + c3 := ft.Switch(label="Disabled switch", disabled=True), + c4 := ft.Switch( + label="Switch with rendered label_position='left'", + label_position=ft.LabelPosition.LEFT, + ), + ft.Button(content="Submit", on_click=handle_button_click), + message := ft.Text(), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/switch/basic/pyproject.toml b/sdk/python/examples/controls/switch/basic/pyproject.toml new file mode 100644 index 0000000000..2e517dff88 --- /dev/null +++ b/sdk/python/examples/controls/switch/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "switch-basic" +version = "1.0.0" +description = "Compares checked, unchecked, disabled, and left-labeled Switch controls and reports their values." +requires-python = ">=3.10" +keywords = ["switch", "input", "states", "button", "label"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Switch"] + +[tool.flet.metadata] +title = "Switch basic" +controls = ["SafeArea", "Column", "Switch", "Button", "Text"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["multiple states", "value summary"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/switch/handling_events.py b/sdk/python/examples/controls/switch/handling_events.py deleted file mode 100644 index cdd9768f03..0000000000 --- a/sdk/python/examples/controls/switch/handling_events.py +++ /dev/null @@ -1,23 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_switch_change(e: ft.Event[ft.Switch]): - page.theme_mode = ( - ft.ThemeMode.DARK - if page.theme_mode == ft.ThemeMode.LIGHT - else ft.ThemeMode.LIGHT - ) - e.control.label = ( - "Light ThemeMode" - if page.theme_mode == ft.ThemeMode.LIGHT - else "Dark ThemeMode" - ) - page.update() - - page.theme_mode = ft.ThemeMode.LIGHT - page.add(ft.Switch(label="Light ThemeMode", on_change=handle_switch_change)) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/switch/handling_events/main.py b/sdk/python/examples/controls/switch/handling_events/main.py new file mode 100644 index 0000000000..2eef4ef3ed --- /dev/null +++ b/sdk/python/examples/controls/switch/handling_events/main.py @@ -0,0 +1,22 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_switch_change(e: ft.Event[ft.Switch]): + page.theme_mode = ft.ThemeMode.DARK if e.control.value else ft.ThemeMode.LIGHT + e.control.label = ( + "Light ThemeMode" + if page.theme_mode == ft.ThemeMode.LIGHT + else "Dark ThemeMode" + ) + + page.theme_mode = ft.ThemeMode.LIGHT + page.add( + ft.SafeArea( + content=ft.Switch(label="Light ThemeMode", on_change=handle_switch_change) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/switch/handling_events/pyproject.toml b/sdk/python/examples/controls/switch/handling_events/pyproject.toml new file mode 100644 index 0000000000..45ce19a929 --- /dev/null +++ b/sdk/python/examples/controls/switch/handling_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "switch-handling-events" +version = "1.0.0" +description = "Toggles page theme mode and updates the label when a Switch value changes." +requires-python = ">=3.10" +keywords = ["switch", "events", "theme", "toggle", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/Switch"] + +[tool.flet.metadata] +title = "Handling events" +controls = ["SafeArea", "Switch"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["change events", "theme toggle"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/tabs/__init__.py b/sdk/python/examples/controls/tabs/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/tabs/basic.py b/sdk/python/examples/controls/tabs/basic.py deleted file mode 100644 index 05ad7b2baa..0000000000 --- a/sdk/python/examples/controls/tabs/basic.py +++ /dev/null @@ -1,48 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Tabs( - selected_index=1, - length=3, - expand=True, - content=ft.Column( - expand=True, - controls=[ - ft.TabBar( - tabs=[ - ft.Tab(label="Tab 1", icon=ft.Icons.SETTINGS_PHONE), - ft.Tab(label="Tab 2", icon=ft.Icons.SETTINGS), - ft.Tab( - label=ft.CircleAvatar( - foreground_image_src="https://avatars.githubusercontent.com/u/102273996?s=200&v=4", - ), - ), - ] - ), - ft.TabBarView( - expand=True, - controls=[ - ft.Container( - content=ft.Text("This is Tab 1"), - alignment=ft.Alignment.CENTER, - ), - ft.Container( - content=ft.Text("This is Tab 2"), - alignment=ft.Alignment.CENTER, - ), - ft.Container( - content=ft.Text("This is Tab 3"), - alignment=ft.Alignment.CENTER, - ), - ], - ), - ], - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/tabs/basic/main.py b/sdk/python/examples/controls/tabs/basic/main.py new file mode 100644 index 0000000000..dcca9fd589 --- /dev/null +++ b/sdk/python/examples/controls/tabs/basic/main.py @@ -0,0 +1,51 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + expand=True, + content=ft.Tabs( + selected_index=1, + length=3, + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.TabBar( + tabs=[ + ft.Tab(label="Tab 1", icon=ft.Icons.SETTINGS_PHONE), + ft.Tab(label="Tab 2", icon=ft.Icons.SETTINGS), + ft.Tab( + label=ft.CircleAvatar( + foreground_image_src="https://avatars.githubusercontent.com/u/102273996?s=200&v=4", + ), + ), + ] + ), + ft.TabBarView( + expand=True, + controls=[ + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("This is Tab 1"), + ), + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("This is Tab 2"), + ), + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("This is Tab 3"), + ), + ], + ), + ], + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/tabs/basic/pyproject.toml b/sdk/python/examples/controls/tabs/basic/pyproject.toml new file mode 100644 index 0000000000..ce99e923ec --- /dev/null +++ b/sdk/python/examples/controls/tabs/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "tabs-basic" +version = "1.0.0" +description = "Displays a basic Tabs layout with icons, an avatar tab label, and matching tab content." +requires-python = ">=3.10" +keywords = ["tabs", "navigation", "icons", "avatar", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Tabs"] + +[tool.flet.metadata] +title = "Tabs basic" +controls = ["SafeArea", "Tabs", "Column", "TabBar", "TabBarView", "Tab", "Container", "Text", "CircleAvatar"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["icon tabs", "avatar tab label"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/tabs/custom_indicator.py b/sdk/python/examples/controls/tabs/custom_indicator.py deleted file mode 100644 index d0459fffad..0000000000 --- a/sdk/python/examples/controls/tabs/custom_indicator.py +++ /dev/null @@ -1,42 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Tabs( - length=2, - expand=True, - content=ft.Column( - expand=True, - controls=[ - ft.TabBar( - tab_alignment=ft.TabAlignment.START, - indicator_animation=ft.TabIndicatorAnimation.ELASTIC, - indicator_size=ft.TabBarIndicatorSize.LABEL, - indicator=ft.UnderlineTabIndicator( - border_side=ft.BorderSide(5, color=ft.Colors.RED), - border_radius=ft.BorderRadius.all(1), - insets=ft.Padding.only(bottom=5), - ), - # indicator_thickness=5, - # indicator_color=ft.Colors.RED, - tabs=[ - ft.Tab(label=ft.Text("Home")), - ft.Tab(label=ft.Text("My Account")), - ], - ), - ft.TabBarView( - expand=True, - controls=[ - ft.Text("Home Tab Content"), - ft.Text("Profile Tab Content"), - ], - ), - ], - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/tabs/custom_indicator/main.py b/sdk/python/examples/controls/tabs/custom_indicator/main.py new file mode 100644 index 0000000000..937e794a74 --- /dev/null +++ b/sdk/python/examples/controls/tabs/custom_indicator/main.py @@ -0,0 +1,43 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + expand=True, + content=ft.Tabs( + length=2, + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.TabBar( + tab_alignment=ft.TabAlignment.START, + indicator_animation=ft.TabIndicatorAnimation.ELASTIC, + indicator_size=ft.TabBarIndicatorSize.LABEL, + indicator=ft.UnderlineTabIndicator( + border_side=ft.BorderSide(5, color=ft.Colors.RED), + border_radius=ft.BorderRadius.all(1), + insets=ft.Padding.only(bottom=5), + ), + tabs=[ + ft.Tab(label=ft.Text("Home")), + ft.Tab(label=ft.Text("My Account")), + ], + ), + ft.TabBarView( + expand=True, + controls=[ + ft.Text("Home Tab Content"), + ft.Text("Profile Tab Content"), + ], + ), + ], + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/tabs/custom_indicator/pyproject.toml b/sdk/python/examples/controls/tabs/custom_indicator/pyproject.toml new file mode 100644 index 0000000000..881c26f48a --- /dev/null +++ b/sdk/python/examples/controls/tabs/custom_indicator/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "tabs-custom-indicator" +version = "1.0.0" +description = "Customizes the tab underline indicator with elastic animation, rounded corners, and insets." +requires-python = ">=3.10" +keywords = ["tabs", "indicator", "underline", "animation", "styling"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Tabs"] + +[tool.flet.metadata] +title = "Custom tab indicator" +controls = ["SafeArea", "Tabs", "Column", "TabBar", "TabBarView", "Tab", "Text", "UnderlineTabIndicator"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["custom indicator", "elastic indicator animation"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/tabs/dynamic_tab_addition.py b/sdk/python/examples/controls/tabs/dynamic_tab_addition.py deleted file mode 100644 index b419abab6f..0000000000 --- a/sdk/python/examples/controls/tabs/dynamic_tab_addition.py +++ /dev/null @@ -1,58 +0,0 @@ -import flet as ft - - -class MyContainer(ft.Container): - def __init__(self, text): - super().__init__( - height=100, - bgcolor=ft.Colors.random(), - alignment=ft.Alignment.CENTER, - ) - self.content = ft.Text(text) - - -def main(page: ft.Page): - def handle_new_tab(e: ft.Event[ft.CupertinoFilledButton]): - tab_count = len(tab_bar.tabs) + 1 - tab_bar.tabs.append(ft.Tab(label=ft.Text(f"Tab {tab_count}"))) - tab_view.controls.append(MyContainer(text=f"Tab {tab_count} content")) - tabs.length = len(tab_bar.tabs) - - page.add( - tabs := ft.Tabs( - length=2, - expand=True, - content=ft.Column( - expand=True, - controls=[ - tab_bar := ft.TabBar( - tab_alignment=ft.TabAlignment.CENTER, - tabs=[ - ft.Tab(label=ft.Text("Tab 1")), - ft.Tab(label=ft.Text("Tab 2")), - ], - ), - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.CupertinoFilledButton( - content="Add New Tab", - icon=ft.Icons.ADD, - on_click=handle_new_tab, - ), - ], - ), - tab_view := ft.TabBarView( - expand=True, - controls=[ - MyContainer(text="Tab 1 content"), - MyContainer(text="Tab 2 content"), - ], - ), - ], - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/tabs/dynamic_tab_addition/main.py b/sdk/python/examples/controls/tabs/dynamic_tab_addition/main.py new file mode 100644 index 0000000000..2377a757fe --- /dev/null +++ b/sdk/python/examples/controls/tabs/dynamic_tab_addition/main.py @@ -0,0 +1,65 @@ +import flet as ft + + +@ft.control +class MyContainer(ft.Container): + text: str = "" + height: int = 100 + alignment: ft.Alignment = ft.Alignment.CENTER + + def init(self): + self.bgcolor = ft.Colors.random() + self.content = ft.Text(self.text) + + +def main(page: ft.Page): + def handle_new_tab(e: ft.Event[ft.CupertinoFilledButton]): + tab_count = len(tab_bar.tabs) + 1 + tab_bar.tabs.append(ft.Tab(label=ft.Text(f"Tab {tab_count}"))) + tab_view.controls.append(MyContainer(text=f"Tab {tab_count} content")) + tabs.length = len(tab_bar.tabs) + + tabs = ft.Tabs( + length=2, + expand=True, + content=ft.Column( + expand=True, + controls=[ + tab_bar := ft.TabBar( + tab_alignment=ft.TabAlignment.CENTER, + tabs=[ + ft.Tab(label=ft.Text("Tab 1")), + ft.Tab(label=ft.Text("Tab 2")), + ], + ), + ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.CupertinoFilledButton( + content="Add New Tab", + icon=ft.Icons.ADD, + on_click=handle_new_tab, + ), + ], + ), + tab_view := ft.TabBarView( + expand=True, + controls=[ + MyContainer(text="Tab 1 content"), + MyContainer(text="Tab 2 content"), + ], + ), + ], + ), + ) + + page.add( + ft.SafeArea( + expand=True, + content=tabs, + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/tabs/dynamic_tab_addition/pyproject.toml b/sdk/python/examples/controls/tabs/dynamic_tab_addition/pyproject.toml new file mode 100644 index 0000000000..6677164a2b --- /dev/null +++ b/sdk/python/examples/controls/tabs/dynamic_tab_addition/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "tabs-dynamic-tab-addition" +version = "1.0.0" +description = "Adds new tabs and matching content panels dynamically from a button click." +requires-python = ">=3.10" +keywords = ["tabs", "dynamic", "add tab", "button", "state"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Tabs"] + +[tool.flet.metadata] +title = "Dynamic tab addition" +controls = ["SafeArea", "Tabs", "Column", "TabBar", "TabBarView", "Tab", "Row", "CupertinoFilledButton", "Container", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["dynamic tabs", "button-driven updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/tabs/move_to.py b/sdk/python/examples/controls/tabs/move_to.py deleted file mode 100644 index 9859fed3cf..0000000000 --- a/sdk/python/examples/controls/tabs/move_to.py +++ /dev/null @@ -1,77 +0,0 @@ -import random - -import flet as ft - - -def main(page: ft.Page): - async def handle_move_to_random(e: ft.Event[ft.FloatingActionButton]): - # random index, excluding the current one - i = random.choice([i for i in range(tabs.length) if i != tabs.selected_index]) - - await tabs.move_to( - index=i, - animation_curve=ft.AnimationCurve.FAST_OUT_SLOWIN, - animation_duration=ft.Duration(seconds=3), - ) - - page.floating_action_button = ft.FloatingActionButton( - icon=ft.Icons.MOVE_UP, - content="Move to a random tab", - on_click=handle_move_to_random, - ) - - page.add( - tabs := ft.Tabs( - length=6, - selected_index=5, - expand=True, - content=ft.Column( - expand=True, - controls=[ - ft.TabBar( - tab_alignment=ft.TabAlignment.CENTER, - tabs=[ - ft.Tab(label=ft.Text("Tab 1")), - ft.Tab(label=ft.Text("Tab 2")), - ft.Tab(label=ft.Text("Tab 3")), - ft.Tab(label=ft.Text("Tab 4")), - ft.Tab(label=ft.Text("Tab 5")), - ft.Tab(label=ft.Text("Tab 6")), - ], - ), - ft.TabBarView( - expand=True, - controls=[ - ft.Container( - content=ft.Text("Tab 1 content"), - alignment=ft.Alignment.CENTER, - ), - ft.Container( - content=ft.Text("Tab 2 content"), - alignment=ft.Alignment.CENTER, - ), - ft.Container( - content=ft.Text("Tab 3 content"), - alignment=ft.Alignment.CENTER, - ), - ft.Container( - content=ft.Text("Tab 4 content"), - alignment=ft.Alignment.CENTER, - ), - ft.Container( - content=ft.Text("Tab 5 content"), - alignment=ft.Alignment.CENTER, - ), - ft.Container( - content=ft.Text("Tab 6 content"), - alignment=ft.Alignment.CENTER, - ), - ], - ), - ], - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/tabs/move_to/main.py b/sdk/python/examples/controls/tabs/move_to/main.py new file mode 100644 index 0000000000..c8b961e0e7 --- /dev/null +++ b/sdk/python/examples/controls/tabs/move_to/main.py @@ -0,0 +1,78 @@ +import random + +import flet as ft + + +def main(page: ft.Page): + async def handle_move_to_random(e: ft.Event[ft.FloatingActionButton]): + # random index, excluding the current one + i = random.choice([i for i in range(tabs.length) if i != tabs.selected_index]) + + await tabs.move_to( + index=i, + animation_curve=ft.AnimationCurve.FAST_OUT_SLOWIN, + animation_duration=ft.Duration(seconds=3), + ) + + page.floating_action_button = ft.FloatingActionButton( + icon=ft.Icons.MOVE_UP, + content="Move to a random tab", + on_click=handle_move_to_random, + ) + + tabs = ft.Tabs( + length=6, + selected_index=5, + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.TabBar( + tab_alignment=ft.TabAlignment.CENTER, + tabs=[ + ft.Tab(label=ft.Text("Tab 1")), + ft.Tab(label=ft.Text("Tab 2")), + ft.Tab(label=ft.Text("Tab 3")), + ft.Tab(label=ft.Text("Tab 4")), + ft.Tab(label=ft.Text("Tab 5")), + ft.Tab(label=ft.Text("Tab 6")), + ], + ), + ft.TabBarView( + expand=True, + controls=[ + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("Tab 1 content"), + ), + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("Tab 2 content"), + ), + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("Tab 3 content"), + ), + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("Tab 4 content"), + ), + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("Tab 5 content"), + ), + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("Tab 6 content"), + ), + ], + ), + ], + ), + ) + + page.add(ft.SafeArea(expand=True, content=tabs)) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/tabs/move_to/pyproject.toml b/sdk/python/examples/controls/tabs/move_to/pyproject.toml new file mode 100644 index 0000000000..c975d28d7c --- /dev/null +++ b/sdk/python/examples/controls/tabs/move_to/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "tabs-move-to" +version = "1.0.0" +description = "Animates programmatic tab switching to a random tab using Tabs.move_to." +requires-python = ">=3.10" +keywords = ["tabs", "move_to", "animation", "async", "navigation"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Tabs"] + +[tool.flet.metadata] +title = "Programmatic tab switch" +controls = ["SafeArea", "Tabs", "Column", "TabBar", "TabBarView", "Tab", "FloatingActionButton", "Container", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["programmatic tab switching", "animated transitions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/tabs/nested.py b/sdk/python/examples/controls/tabs/nested.py deleted file mode 100644 index f17fb5f7b0..0000000000 --- a/sdk/python/examples/controls/tabs/nested.py +++ /dev/null @@ -1,55 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Tabs( - length=2, - selected_index=1, - expand=True, - content=ft.Column( - expand=True, - controls=[ - ft.TabBar( - tabs=[ - ft.Tab(label=ft.Text("Main Tab 1")), - ft.Tab(label=ft.Text("Main Tab 2")), - ], - ), - ft.TabBarView( - expand=True, - controls=[ - ft.Text("Main Tab 1 content"), - ft.Tabs( - length=2, - expand=True, - content=ft.Column( - expand=True, - controls=[ - ft.TabBar( - secondary=True, - tabs=[ - ft.Tab(label=ft.Text("SubTab 1")), - ft.Tab(label=ft.Text("SubTab 2")), - ], - ), - ft.TabBarView( - expand=True, - controls=[ - ft.Text("Nested Tab 1 content"), - ft.Text("Nested Tab 2 content"), - ], - ), - ], - ), - ), - ], - ), - ], - ), - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/tabs/nested/main.py b/sdk/python/examples/controls/tabs/nested/main.py new file mode 100644 index 0000000000..3bf34173eb --- /dev/null +++ b/sdk/python/examples/controls/tabs/nested/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + expand=True, + content=ft.Tabs( + length=2, + selected_index=1, + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.TabBar( + tabs=[ + ft.Tab(label=ft.Text("Main Tab 1")), + ft.Tab(label=ft.Text("Main Tab 2")), + ], + ), + ft.TabBarView( + expand=True, + controls=[ + ft.Text("Main Tab 1 content"), + ft.Tabs( + length=2, + expand=True, + content=ft.Column( + expand=True, + controls=[ + ft.TabBar( + secondary=True, + tabs=[ + ft.Tab(label=ft.Text("SubTab 1")), + ft.Tab(label=ft.Text("SubTab 2")), + ], + ), + ft.TabBarView( + expand=True, + controls=[ + ft.Text("Nested Tab 1 content"), + ft.Text("Nested Tab 2 content"), + ], + ), + ], + ), + ), + ], + ), + ], + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/tabs/nested/pyproject.toml b/sdk/python/examples/controls/tabs/nested/pyproject.toml new file mode 100644 index 0000000000..c0278892dc --- /dev/null +++ b/sdk/python/examples/controls/tabs/nested/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "tabs-nested" +version = "1.0.0" +description = "Embeds a secondary Tabs control inside a parent tab to demonstrate nested navigation." +requires-python = ">=3.10" +keywords = ["tabs", "nested", "secondary", "navigation", "layout"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/Tabs"] + +[tool.flet.metadata] +title = "Nested tabs" +controls = ["SafeArea", "Tabs", "Column", "TabBar", "TabBarView", "Tab", "Text"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["nested tabs", "secondary tab bar"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text/custom_styles.py b/sdk/python/examples/controls/text/custom_styles.py deleted file mode 100644 index fa7824d3ec..0000000000 --- a/sdk/python/examples/controls/text/custom_styles.py +++ /dev/null @@ -1,68 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Text custom styles" - page.scroll = ft.ScrollMode.ADAPTIVE - - page.add( - ft.Text("Size 10", size=10), - ft.Text("Size 30, Italic", size=30, color=ft.Colors.PINK_600, italic=True), - ft.Text( - value="Size 40, w100", - size=40, - color=ft.Colors.WHITE, - bgcolor=ft.Colors.BLUE_600, - weight=ft.FontWeight.W_100, - ), - ft.Text( - value="Size 50, Normal", - size=50, - color=ft.Colors.WHITE, - bgcolor=ft.Colors.ORANGE_800, - weight=ft.FontWeight.NORMAL, - ), - ft.Text( - value="Size 60, Bold, Italic", - size=50, - color=ft.Colors.WHITE, - bgcolor=ft.Colors.GREEN_700, - weight=ft.FontWeight.BOLD, - italic=True, - ), - ft.Text( - value="Size 70, w900, selectable", - size=70, - weight=ft.FontWeight.W_900, - selectable=True, - ), - ft.Text( - value="Limit long text to 1 line with ellipsis", - theme_style=ft.TextThemeStyle.HEADLINE_SMALL, - ), - ft.Text( - value="Proin rutrum, purus sit amet elementum volutpat, nunc lacus vulputate orci, cursus ultrices neque dui quis purus. Ut ultricies purus nec nibh bibendum, eget vestibulum metus various. Duis convallis maximus justo, eu rutrum libero maximus id. Donec ullamcorper arcu in sapien molestie, non pellentesque tellus pellentesque. Nulla nec tristique ex. Maecenas euismod nisl enim, a convallis arcu laoreet at. Ut at tortor finibus, rutrum massa sit amet, pulvinar velit. Phasellus diam lorem, viverra vitae leo vitae, consequat suscipit lorem.", - max_lines=1, - overflow=ft.TextOverflow.ELLIPSIS, - ), - ft.Text( - value="Limit long text to 2 lines and fading", - theme_style=ft.TextThemeStyle.HEADLINE_SMALL, - ), - ft.Text( - value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur quis nibh vitae purus consectetur facilisis sed vitae ipsum. Quisque faucibus sed nulla placerat sagittis. Phasellus condimentum risus vitae nulla vestibulum auctor. Curabitur scelerisque, nibh eget imperdiet consequat, odio ante tempus diam, sed volutpat nisl erat eget turpis. Sed viverra, diam sit amet blandit vulputate, mi tellus dapibus lorem, vitae vehicula diam mauris placerat diam. Morbi sit amet pretium turpis, et consequat ligula. Nulla velit sem, suscipit sit amet dictum non, tincidunt sed nulla. Aenean pellentesque odio porttitor sagittis aliquam. Name various at metus vitae vulputate. Praesent faucibus nibh lorem, eu pretium dolor dictum nec. Phasellus eget dui laoreet, viverra magna vitae, pellentesque diam.", - max_lines=2, - ), - ft.Text( - value="Limit the width and height of long text", - theme_style=ft.TextThemeStyle.HEADLINE_SMALL, - ), - ft.Text( - value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur quis nibh vitae purus consectetur facilisis sed vitae ipsum. Quisque faucibus sed nulla placerat sagittis. Phasellus condimentum risus vitae nulla vestibulum auctor. Curabitur scelerisque, nibh eget imperdiet consequat, odio ante tempus diam, sed volutpat nisl erat eget turpis. Sed viverra, diam sit amet blandit vulputate, mi tellus dapibus lorem, vitae vehicula diam mauris placerat diam. Morbi sit amet pretium turpis, et consequat ligula. Nulla velit sem, suscipit sit amet dictum non, tincidunt sed nulla. Aenean pellentesque odio porttitor sagittis aliquam. Name various at metus vitae vulputate. Praesent faucibus nibh lorem, eu pretium dolor dictum nec. Phasellus eget dui laoreet, viverra magna vitae, pellentesque diam.", - width=700, - height=100, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text/custom_styles/main.py b/sdk/python/examples/controls/text/custom_styles/main.py new file mode 100644 index 0000000000..ef783de13a --- /dev/null +++ b/sdk/python/examples/controls/text/custom_styles/main.py @@ -0,0 +1,125 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Text custom styles" + page.scroll = ft.ScrollMode.ADAPTIVE + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Size 10", size=10), + ft.Text( + "Size 30, Italic", + size=30, + color=ft.Colors.PINK_600, + italic=True, + ), + ft.Text( + value="Size 40, w100", + size=40, + color=ft.Colors.WHITE, + bgcolor=ft.Colors.BLUE_600, + weight=ft.FontWeight.W_100, + ), + ft.Text( + value="Size 50, Normal", + size=50, + color=ft.Colors.WHITE, + bgcolor=ft.Colors.ORANGE_800, + weight=ft.FontWeight.NORMAL, + ), + ft.Text( + value="Size 60, Bold, Italic", + size=50, + color=ft.Colors.WHITE, + bgcolor=ft.Colors.GREEN_700, + weight=ft.FontWeight.BOLD, + italic=True, + ), + ft.Text( + value="Size 70, w900, selectable", + size=70, + weight=ft.FontWeight.W_900, + selectable=True, + ), + ft.Text( + value="Limit long text to 1 line with ellipsis", + theme_style=ft.TextThemeStyle.HEADLINE_SMALL, + ), + ft.Text( + value=( + "Proin rutrum, purus sit amet elementum volutpat, nunc " + "lacus vulputate orci, cursus ultrices neque dui quis " + "purus. Ut ultricies purus nec nibh bibendum, eget " + "vestibulum metus various. Duis convallis maximus justo, " + "eu rutrum libero maximus id. Donec ullamcorper arcu in " + "sapien molestie, non pellentesque tellus pellentesque. " + "Nulla nec tristique ex. Maecenas euismod nisl enim, a " + "convallis arcu laoreet at. Ut at tortor finibus, rutrum " + "massa sit amet, pulvinar velit. Phasellus diam lorem, " + "viverra vitae leo vitae, consequat suscipit lorem." + ), + max_lines=1, + overflow=ft.TextOverflow.ELLIPSIS, + ), + ft.Text( + value="Limit long text to 2 lines and fading", + theme_style=ft.TextThemeStyle.HEADLINE_SMALL, + ), + ft.Text( + value=( + "Lorem ipsum dolor sit amet, consectetur adipiscing " + "elit. Curabitur quis nibh vitae purus consectetur " + "facilisis sed vitae ipsum. Quisque faucibus sed nulla " + "placerat sagittis. Phasellus condimentum risus vitae " + "nulla vestibulum auctor. Curabitur scelerisque, nibh " + "eget imperdiet consequat, odio ante tempus diam, sed " + "volutpat nisl erat eget turpis. Sed viverra, diam sit " + "amet blandit vulputate, mi tellus dapibus lorem, vitae " + "vehicula diam mauris placerat diam. Morbi sit amet " + "pretium turpis, et consequat ligula. Nulla velit sem, " + "suscipit sit amet dictum non, tincidunt sed nulla. " + "Aenean pellentesque odio porttitor sagittis aliquam. " + "Name various at metus vitae vulputate. Praesent " + "faucibus nibh lorem, eu pretium dolor dictum nec. " + "Phasellus eget dui laoreet, viverra magna vitae, " + "pellentesque diam." + ), + max_lines=2, + ), + ft.Text( + value="Limit the width and height of long text", + theme_style=ft.TextThemeStyle.HEADLINE_SMALL, + ), + ft.Text( + value=( + "Lorem ipsum dolor sit amet, consectetur adipiscing " + "elit. Curabitur quis nibh vitae purus consectetur " + "facilisis sed vitae ipsum. Quisque faucibus sed nulla " + "placerat sagittis. Phasellus condimentum risus vitae " + "nulla vestibulum auctor. Curabitur scelerisque, nibh " + "eget imperdiet consequat, odio ante tempus diam, sed " + "volutpat nisl erat eget turpis. Sed viverra, diam sit " + "amet blandit vulputate, mi tellus dapibus lorem, vitae " + "vehicula diam mauris placerat diam. Morbi sit amet " + "pretium turpis, et consequat ligula. Nulla velit sem, " + "suscipit sit amet dictum non, tincidunt sed nulla. " + "Aenean pellentesque odio porttitor sagittis aliquam. " + "Name various at metus vitae vulputate. Praesent " + "faucibus nibh lorem, eu pretium dolor dictum nec. " + "Phasellus eget dui laoreet, viverra magna vitae, " + "pellentesque diam." + ), + width=700, + height=100, + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text/custom_styles/pyproject.toml b/sdk/python/examples/controls/text/custom_styles/pyproject.toml new file mode 100644 index 0000000000..bde8060435 --- /dev/null +++ b/sdk/python/examples/controls/text/custom_styles/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-custom-styles" +version = "1.0.0" +description = "Demonstrates Text size, color, weight, selection, and overflow styling variations." +requires-python = ">=3.10" +keywords = ["text", "styles", "typography", "overflow", "selection"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Display/Text"] + +[tool.flet.metadata] +title = "Custom text styles" +controls = ["SafeArea", "Column", "Text"] +layout_pattern = "stacked-content" +complexity = "basic" +features = ["custom text styling", "overflow handling", "selectable text"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text/rich_text_basic.py b/sdk/python/examples/controls/text/rich_text_basic.py deleted file mode 100644 index 44e8cb2f6e..0000000000 --- a/sdk/python/examples/controls/text/rich_text_basic.py +++ /dev/null @@ -1,103 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Text("Plain text with default style"), - ft.Text("Selectable plain text with default style", selectable=True), - ft.Text( - value="Some text", - selectable=True, - size=30, - spans=[ - ft.TextSpan( - text="here goes italic", - style=ft.TextStyle(italic=True, size=20, color=ft.Colors.GREEN), - spans=[ - ft.TextSpan( - text="bold and italic", - style=ft.TextStyle(weight=ft.FontWeight.BOLD), - ), - ft.TextSpan( - text="just italic", - spans=[ - ft.TextSpan("smaller italic", ft.TextStyle(size=15)) - ], - ), - ], - ) - ], - ), - ft.Text( - disabled=False, - spans=[ - ft.TextSpan( - text="underlined and clickable", - style=ft.TextStyle(decoration=ft.TextDecoration.UNDERLINE), - on_click=lambda e: print(f"Clicked span: {e.control}"), - on_enter=lambda e: print(f"Entered span: {e.control}"), - on_exit=lambda e: print(f"Exited span: {e.control}"), - ), - ft.TextSpan(text=" "), - ft.TextSpan( - text="underlined red wavy", - style=ft.TextStyle( - decoration=ft.TextDecoration.UNDERLINE, - decoration_color=ft.Colors.RED, - decoration_style=ft.TextDecorationStyle.WAVY, - ), - on_enter=lambda e: print(f"Entered span: {e.control}"), - on_exit=lambda e: print(f"Exited span: {e.control}"), - ), - ft.TextSpan(text=" "), - ft.TextSpan( - text="overlined blue", - style=ft.TextStyle( - decoration=ft.TextDecoration.OVERLINE, decoration_color="blue" - ), - ), - ft.TextSpan(text=" "), - ft.TextSpan( - text="overlined and underlined", - style=ft.TextStyle( - decoration=ft.TextDecoration.OVERLINE - | ft.TextDecoration.UNDERLINE - ), - ), - ft.TextSpan(text=" "), - ft.TextSpan( - text="line through thick", - style=ft.TextStyle( - decoration=ft.TextDecoration.LINE_THROUGH, - decoration_thickness=3, - ), - ), - ], - ), - ) - - def handle_link_highlight(e: ft.Event[ft.TextSpan]): - e.control.style.color = ft.Colors.BLUE - e.control.update() - - def handle_link_unhighlight(e: ft.Event[ft.TextSpan]): - e.control.style.color = None - e.control.update() - - page.add( - ft.Text( - disabled=False, - spans=[ - ft.TextSpan( - text="Go to Google", - style=ft.TextStyle(decoration=ft.TextDecoration.UNDERLINE), - url="https://google.com", - on_enter=handle_link_highlight, - on_exit=handle_link_unhighlight, - ) - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text/rich_text_basic/main.py b/sdk/python/examples/controls/text/rich_text_basic/main.py new file mode 100644 index 0000000000..d60c089721 --- /dev/null +++ b/sdk/python/examples/controls/text/rich_text_basic/main.py @@ -0,0 +1,123 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Plain text with default style"), + ft.Text( + "Selectable plain text with default style", + selectable=True, + ), + ft.Text( + value="Some text", + selectable=True, + size=30, + spans=[ + ft.TextSpan( + text="here goes italic", + style=ft.TextStyle( + italic=True, + size=20, + color=ft.Colors.GREEN, + ), + spans=[ + ft.TextSpan( + text="bold and italic", + style=ft.TextStyle(weight=ft.FontWeight.BOLD), + ), + ft.TextSpan( + text="just italic", + spans=[ + ft.TextSpan( + "smaller italic", + ft.TextStyle(size=15), + ) + ], + ), + ], + ) + ], + ), + ft.Text( + disabled=False, + spans=[ + ft.TextSpan( + text="underlined and clickable", + style=ft.TextStyle( + decoration=ft.TextDecoration.UNDERLINE + ), + on_click=lambda e: print(f"Clicked span: {e.control}"), + on_enter=lambda e: print(f"Entered span: {e.control}"), + on_exit=lambda e: print(f"Exited span: {e.control}"), + ), + ft.TextSpan(text=" "), + ft.TextSpan( + text="underlined red wavy", + style=ft.TextStyle( + decoration=ft.TextDecoration.UNDERLINE, + decoration_color=ft.Colors.RED, + decoration_style=ft.TextDecorationStyle.WAVY, + ), + on_enter=lambda e: print(f"Entered span: {e.control}"), + on_exit=lambda e: print(f"Exited span: {e.control}"), + ), + ft.TextSpan(text=" "), + ft.TextSpan( + text="overlined blue", + style=ft.TextStyle( + decoration=ft.TextDecoration.OVERLINE, + decoration_color="blue", + ), + ), + ft.TextSpan(text=" "), + ft.TextSpan( + text="overlined and underlined", + style=ft.TextStyle( + decoration=ft.TextDecoration.OVERLINE + | ft.TextDecoration.UNDERLINE + ), + ), + ft.TextSpan(text=" "), + ft.TextSpan( + text="line through thick", + style=ft.TextStyle( + decoration=ft.TextDecoration.LINE_THROUGH, + decoration_thickness=3, + ), + ), + ], + ), + ], + ), + ), + ) + + def handle_link_highlight(e: ft.Event[ft.TextSpan]): + e.control.style.color = ft.Colors.BLUE + e.control.update() + + def handle_link_unhighlight(e: ft.Event[ft.TextSpan]): + e.control.style.color = None + e.control.update() + + page.add( + ft.Text( + disabled=False, + spans=[ + ft.TextSpan( + text="Go to Google", + style=ft.TextStyle(decoration=ft.TextDecoration.UNDERLINE), + url="https://google.com", + on_enter=handle_link_highlight, + on_exit=handle_link_unhighlight, + ) + ], + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text/rich_text_basic/pyproject.toml b/sdk/python/examples/controls/text/rich_text_basic/pyproject.toml new file mode 100644 index 0000000000..a1de30b4db --- /dev/null +++ b/sdk/python/examples/controls/text/rich_text_basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-rich-text-basic" +version = "1.0.0" +description = "Combines nested TextSpan styles, interactive spans, and an external link in rich text." +requires-python = ">=3.10" +keywords = ["text", "rich text", "textspan", "link", "styling"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Display/Text"] + +[tool.flet.metadata] +title = "Basic rich text" +controls = ["SafeArea", "Column", "Text", "TextSpan"] +layout_pattern = "stacked-content" +complexity = "basic" +features = ["nested text spans", "clickable spans", "external links"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text/rich_text_border_stroke.py b/sdk/python/examples/controls/text/rich_text_border_stroke.py deleted file mode 100644 index 4a702e657c..0000000000 --- a/sdk/python/examples/controls/text/rich_text_border_stroke.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Stack( - controls=[ - ft.Text( - spans=[ - ft.TextSpan( - text="Greetings, planet!", - style=ft.TextStyle( - size=40, - weight=ft.FontWeight.BOLD, - foreground=ft.Paint( - color=ft.Colors.BLUE_700, - stroke_width=6, - style=ft.PaintingStyle.STROKE, - ), - ), - ), - ], - ), - ft.Text( - spans=[ - ft.TextSpan( - text="Greetings, planet!", - style=ft.TextStyle( - size=40, - weight=ft.FontWeight.BOLD, - color=ft.Colors.GREY_300, - ), - ), - ], - ), - ] - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text/rich_text_border_stroke/main.py b/sdk/python/examples/controls/text/rich_text_border_stroke/main.py new file mode 100644 index 0000000000..8ccbded7c4 --- /dev/null +++ b/sdk/python/examples/controls/text/rich_text_border_stroke/main.py @@ -0,0 +1,44 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Stack( + controls=[ + ft.Text( + spans=[ + ft.TextSpan( + text="Greetings, planet!", + style=ft.TextStyle( + size=40, + weight=ft.FontWeight.BOLD, + foreground=ft.Paint( + color=ft.Colors.BLUE_700, + stroke_width=6, + style=ft.PaintingStyle.STROKE, + ), + ), + ), + ], + ), + ft.Text( + spans=[ + ft.TextSpan( + text="Greetings, planet!", + style=ft.TextStyle( + size=40, + weight=ft.FontWeight.BOLD, + color=ft.Colors.GREY_300, + ), + ), + ], + ), + ] + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text/rich_text_border_stroke/pyproject.toml b/sdk/python/examples/controls/text/rich_text_border_stroke/pyproject.toml new file mode 100644 index 0000000000..486e979df2 --- /dev/null +++ b/sdk/python/examples/controls/text/rich_text_border_stroke/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-rich-text-border-stroke" +version = "1.0.0" +description = "Layers stroked and filled rich text in a Stack to create an outlined title effect." +requires-python = ">=3.10" +keywords = ["text", "rich text", "stroke", "outline", "stack"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Display/Text"] + +[tool.flet.metadata] +title = "Rich text border stroke" +controls = ["SafeArea", "Stack", "Text", "TextSpan"] +layout_pattern = "overlay" +complexity = "basic" +features = ["stroked text effect"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text/rich_text_gradient.py b/sdk/python/examples/controls/text/rich_text_gradient.py deleted file mode 100644 index 9941aaaf46..0000000000 --- a/sdk/python/examples/controls/text/rich_text_gradient.py +++ /dev/null @@ -1,27 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Text( - spans=[ - ft.TextSpan( - text="Greetings, planet!", - style=ft.TextStyle( - size=40, - weight=ft.FontWeight.BOLD, - foreground=ft.Paint( - gradient=ft.PaintLinearGradient( - begin=(0, 20), - end=(150, 20), - colors=[ft.Colors.RED, ft.Colors.YELLOW], - ) - ), - ), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text/rich_text_gradient/main.py b/sdk/python/examples/controls/text/rich_text_gradient/main.py new file mode 100644 index 0000000000..613acd0559 --- /dev/null +++ b/sdk/python/examples/controls/text/rich_text_gradient/main.py @@ -0,0 +1,30 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Text( + spans=[ + ft.TextSpan( + text="Greetings, planet!", + style=ft.TextStyle( + size=40, + weight=ft.FontWeight.BOLD, + foreground=ft.Paint( + gradient=ft.PaintLinearGradient( + begin=(0, 20), + end=(150, 20), + colors=[ft.Colors.RED, ft.Colors.YELLOW], + ) + ), + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text/rich_text_gradient/pyproject.toml b/sdk/python/examples/controls/text/rich_text_gradient/pyproject.toml new file mode 100644 index 0000000000..371a207ccb --- /dev/null +++ b/sdk/python/examples/controls/text/rich_text_gradient/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-rich-text-gradient" +version = "1.0.0" +description = "Applies a linear gradient paint to rich text for a multicolor title." +requires-python = ">=3.10" +keywords = ["text", "rich text", "gradient", "paint", "styling"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Display/Text"] + +[tool.flet.metadata] +title = "Rich text gradient" +controls = ["SafeArea", "Text", "TextSpan"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["gradient text effect"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text/text_theme_styles.py b/sdk/python/examples/controls/text/text_theme_styles.py deleted file mode 100644 index cd156bd24c..0000000000 --- a/sdk/python/examples/controls/text/text_theme_styles.py +++ /dev/null @@ -1,27 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Text theme styles" - page.scroll = ft.ScrollMode.ADAPTIVE - - page.add( - ft.Text("Display Large", theme_style=ft.TextThemeStyle.DISPLAY_LARGE), - ft.Text("Display Medium", theme_style=ft.TextThemeStyle.DISPLAY_MEDIUM), - ft.Text("Display Small", theme_style=ft.TextThemeStyle.DISPLAY_SMALL), - ft.Text("Headline Large", theme_style=ft.TextThemeStyle.HEADLINE_LARGE), - ft.Text("Headline Medium", theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM), - ft.Text("Headline Small", theme_style=ft.TextThemeStyle.HEADLINE_SMALL), - ft.Text("Title Large", theme_style=ft.TextThemeStyle.TITLE_LARGE), - ft.Text("Title Medium", theme_style=ft.TextThemeStyle.TITLE_MEDIUM), - ft.Text("Title Small", theme_style=ft.TextThemeStyle.TITLE_SMALL), - ft.Text("Label Large", theme_style=ft.TextThemeStyle.LABEL_LARGE), - ft.Text("Label Medium", theme_style=ft.TextThemeStyle.LABEL_MEDIUM), - ft.Text("Label Small", theme_style=ft.TextThemeStyle.LABEL_SMALL), - ft.Text("Body Large", theme_style=ft.TextThemeStyle.BODY_LARGE), - ft.Text("Body Medium", theme_style=ft.TextThemeStyle.BODY_MEDIUM), - ft.Text("Body Small", theme_style=ft.TextThemeStyle.BODY_SMALL), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text/text_theme_styles/main.py b/sdk/python/examples/controls/text/text_theme_styles/main.py new file mode 100644 index 0000000000..7d6fc1f00a --- /dev/null +++ b/sdk/python/examples/controls/text/text_theme_styles/main.py @@ -0,0 +1,55 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Text theme styles" + page.scroll = ft.ScrollMode.ADAPTIVE + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Display Large", + theme_style=ft.TextThemeStyle.DISPLAY_LARGE, + ), + ft.Text( + "Display Medium", + theme_style=ft.TextThemeStyle.DISPLAY_MEDIUM, + ), + ft.Text( + "Display Small", + theme_style=ft.TextThemeStyle.DISPLAY_SMALL, + ), + ft.Text( + "Headline Large", + theme_style=ft.TextThemeStyle.HEADLINE_LARGE, + ), + ft.Text( + "Headline Medium", + theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM, + ), + ft.Text( + "Headline Small", + theme_style=ft.TextThemeStyle.HEADLINE_SMALL, + ), + ft.Text("Title Large", theme_style=ft.TextThemeStyle.TITLE_LARGE), + ft.Text("Title Medium", theme_style=ft.TextThemeStyle.TITLE_MEDIUM), + ft.Text("Title Small", theme_style=ft.TextThemeStyle.TITLE_SMALL), + ft.Text("Label Large", theme_style=ft.TextThemeStyle.LABEL_LARGE), + ft.Text( + "Label Medium", + theme_style=ft.TextThemeStyle.LABEL_MEDIUM, + ), + ft.Text("Label Small", theme_style=ft.TextThemeStyle.LABEL_SMALL), + ft.Text("Body Large", theme_style=ft.TextThemeStyle.BODY_LARGE), + ft.Text("Body Medium", theme_style=ft.TextThemeStyle.BODY_MEDIUM), + ft.Text("Body Small", theme_style=ft.TextThemeStyle.BODY_SMALL), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text/text_theme_styles/pyproject.toml b/sdk/python/examples/controls/text/text_theme_styles/pyproject.toml new file mode 100644 index 0000000000..584681cb20 --- /dev/null +++ b/sdk/python/examples/controls/text/text_theme_styles/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-theme-styles" +version = "1.0.0" +description = "Shows the built-in TextThemeStyle variants from display styles through body and labels." +requires-python = ">=3.10" +keywords = ["text", "theme", "typography", "styles", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Display/Text"] + +[tool.flet.metadata] +title = "Theme text styles" +controls = ["SafeArea", "Column", "Text"] +layout_pattern = "stacked-content" +complexity = "basic" +features = ["theme typography"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text/variable_font_weight.py b/sdk/python/examples/controls/text/variable_font_weight.py deleted file mode 100644 index 91b1debdba..0000000000 --- a/sdk/python/examples/controls/text/variable_font_weight.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.fonts = { - "RobotoSlab": "https://github.com/google/fonts/raw/main/apache/robotoslab/RobotoSlab%5Bwght%5D.ttf" - } - - def handle_slider_change(e): - text.weight = f"w{int(e.control.value)}" # noqa - text.update() - - page.add( - text := ft.Text( - "This is rendered with Roboto Slab", - size=30, - font_family="RobotoSlab", - weight=ft.FontWeight.W_100, - ), - ft.Slider( - min=100, - max=900, - divisions=8, - label="Weight = {value}", - width=500, - on_change=handle_slider_change, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text/variable_font_weight/main.py b/sdk/python/examples/controls/text/variable_font_weight/main.py new file mode 100644 index 0000000000..9e8f2aa871 --- /dev/null +++ b/sdk/python/examples/controls/text/variable_font_weight/main.py @@ -0,0 +1,38 @@ +import flet as ft + + +def main(page: ft.Page): + page.fonts = { + "RobotoSlab": "https://github.com/google/fonts/raw/main/apache/robotoslab/RobotoSlab%5Bwght%5D.ttf" + } + + def handle_slider_change(e): + text.weight = f"w{int(e.control.value)}" # noqa + text.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + text := ft.Text( + "This is rendered with Roboto Slab", + size=30, + font_family="RobotoSlab", + weight=ft.FontWeight.W_100, + ), + ft.Slider( + min=100, + max=900, + divisions=8, + label="Weight = {value}", + width=500, + on_change=handle_slider_change, + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text/variable_font_weight/pyproject.toml b/sdk/python/examples/controls/text/variable_font_weight/pyproject.toml new file mode 100644 index 0000000000..a2eb6ab740 --- /dev/null +++ b/sdk/python/examples/controls/text/variable_font_weight/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-variable-font-weight" +version = "1.0.0" +description = "Adjusts a variable font weight interactively with a slider." +requires-python = ">=3.10" +keywords = ["text", "font", "variable", "weight", "slider"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Display/Text"] + +[tool.flet.metadata] +title = "Variable font weight" +controls = ["SafeArea", "Column", "Text", "Slider"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["interactive font weight control"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_button/__init__.py b/sdk/python/examples/controls/text_button/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/text_button/basic.py b/sdk/python/examples/controls/text_button/basic.py deleted file mode 100644 index d140f1af8a..0000000000 --- a/sdk/python/examples/controls/text_button/basic.py +++ /dev/null @@ -1,14 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Basic text buttons" - - page.add( - ft.TextButton(content="Text button"), - ft.TextButton(content="Disabled button", disabled=True), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/text_button/basic/main.py b/sdk/python/examples/controls/text_button/basic/main.py new file mode 100644 index 0000000000..b9f8c11117 --- /dev/null +++ b/sdk/python/examples/controls/text_button/basic/main.py @@ -0,0 +1,20 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Basic text buttons" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.TextButton(content="Text button"), + ft.TextButton(content="Disabled button", disabled=True), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_button/basic/pyproject.toml b/sdk/python/examples/controls/text_button/basic/pyproject.toml new file mode 100644 index 0000000000..aa5b7a2bb7 --- /dev/null +++ b/sdk/python/examples/controls/text_button/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-button-basic" +version = "1.0.0" +description = "Shows enabled and disabled TextButton controls with the default appearance." +requires-python = ">=3.10" +keywords = ["text button", "button", "disabled", "basic", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/TextButton"] + +[tool.flet.metadata] +title = "TextButton basic" +controls = ["SafeArea", "Column", "TextButton"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["enabled and disabled states"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_button/custom_content.py b/sdk/python/examples/controls/text_button/custom_content.py deleted file mode 100644 index 7b402714fb..0000000000 --- a/sdk/python/examples/controls/text_button/custom_content.py +++ /dev/null @@ -1,36 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "TextButtons with custom content" - - page.add( - ft.TextButton( - width=150, - content=ft.Row( - alignment=ft.MainAxisAlignment.SPACE_AROUND, - controls=[ - ft.Icon(ft.Icons.FAVORITE, color=ft.Colors.PINK), - ft.Icon(ft.Icons.AUDIOTRACK, color=ft.Colors.GREEN), - ft.Icon(ft.Icons.BEACH_ACCESS, color=ft.Colors.BLUE), - ], - ), - ), - ft.TextButton( - content=ft.Container( - padding=ft.Padding.all(10), - content=ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - spacing=5, - controls=[ - ft.Text(value="Compound button", size=20), - ft.Text(value="This is secondary text"), - ], - ), - ), - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/text_button/custom_content/main.py b/sdk/python/examples/controls/text_button/custom_content/main.py new file mode 100644 index 0000000000..82642c013e --- /dev/null +++ b/sdk/python/examples/controls/text_button/custom_content/main.py @@ -0,0 +1,42 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "TextButtons with custom content" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.TextButton( + width=150, + content=ft.Row( + alignment=ft.MainAxisAlignment.SPACE_AROUND, + controls=[ + ft.Icon(ft.Icons.FAVORITE, color=ft.Colors.PINK), + ft.Icon(ft.Icons.AUDIOTRACK, color=ft.Colors.GREEN), + ft.Icon(ft.Icons.BEACH_ACCESS, color=ft.Colors.BLUE), + ], + ), + ), + ft.TextButton( + content=ft.Container( + padding=ft.Padding.all(10), + content=ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + spacing=5, + controls=[ + ft.Text(value="Compound button", size=20), + ft.Text(value="This is secondary text"), + ], + ), + ), + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_button/custom_content/pyproject.toml b/sdk/python/examples/controls/text_button/custom_content/pyproject.toml new file mode 100644 index 0000000000..57fdaafae0 --- /dev/null +++ b/sdk/python/examples/controls/text_button/custom_content/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-button-custom-content" +version = "1.0.0" +description = "Builds TextButton controls with rows of icons and compound multi-line content." +requires-python = ">=3.10" +keywords = ["text button", "custom content", "icons", "compound", "button"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/TextButton"] + +[tool.flet.metadata] +title = "TextButton custom content" +controls = ["SafeArea", "Column", "TextButton", "Row", "Container", "Column", "Icon", "Text"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["custom button content", "compound button layout"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_button/handling_clicks.py b/sdk/python/examples/controls/text_button/handling_clicks.py deleted file mode 100644 index 9cc25baff3..0000000000 --- a/sdk/python/examples/controls/text_button/handling_clicks.py +++ /dev/null @@ -1,25 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "TextButton with 'click' event" - - def button_clicked(e): - button.data += 1 - message_text.value = f"Button clicked {button.data} time(s)" - page.update() - - message_text = ft.Text() - page.add( - button := ft.TextButton( - key="TextButton", - content="Button with 'click' event", - data=0, - on_click=button_clicked, - ), - message := ft.Container(content=message_text, padding=ft.Padding(left=12)), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/text_button/handling_clicks/main.py b/sdk/python/examples/controls/text_button/handling_clicks/main.py new file mode 100644 index 0000000000..1e8cb47b9d --- /dev/null +++ b/sdk/python/examples/controls/text_button/handling_clicks/main.py @@ -0,0 +1,33 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "TextButton with 'click' event" + + def button_clicked(e): + button.data += 1 + message_text.value = f"Button clicked {button.data} time(s)" + + message_text = ft.Text() + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + button := ft.TextButton( + key="TextButton", + content="Button with 'click' event", + data=0, + on_click=button_clicked, + ), + ft.Container( + padding=ft.Padding(left=12), + content=message_text, + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_button/handling_clicks/pyproject.toml b/sdk/python/examples/controls/text_button/handling_clicks/pyproject.toml new file mode 100644 index 0000000000..88527e5907 --- /dev/null +++ b/sdk/python/examples/controls/text_button/handling_clicks/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-button-handling-clicks" +version = "1.0.0" +description = "Updates a counter message each time a TextButton click event fires." +requires-python = ">=3.10" +keywords = ["text button", "events", "click", "counter", "button"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/TextButton"] + +[tool.flet.metadata] +title = "TextButton handling clicks" +controls = ["SafeArea", "Column", "TextButton", "Container", "Text"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["click handling", "live counter updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_button/icons.py b/sdk/python/examples/controls/text_button/icons.py deleted file mode 100644 index 3205f15353..0000000000 --- a/sdk/python/examples/controls/text_button/icons.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "TextButtons with icons" - - page.add( - ft.TextButton(content="Button with icon", icon=ft.Icons.WAVES_OUTLINED), - ft.TextButton( - content="Button with colorful icon", - icon=ft.Icons.PARK_ROUNDED, - icon_color=ft.Colors.GREEN_400, - ), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/text_button/icons/main.py b/sdk/python/examples/controls/text_button/icons/main.py new file mode 100644 index 0000000000..9e1e445ab0 --- /dev/null +++ b/sdk/python/examples/controls/text_button/icons/main.py @@ -0,0 +1,27 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "TextButtons with icons" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.TextButton( + content="Button with icon", + icon=ft.Icons.WAVES_OUTLINED, + ), + ft.TextButton( + content="Button with colorful icon", + icon=ft.Icons.PARK_ROUNDED, + icon_color=ft.Colors.GREEN_400, + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_button/icons/pyproject.toml b/sdk/python/examples/controls/text_button/icons/pyproject.toml new file mode 100644 index 0000000000..df13c044b9 --- /dev/null +++ b/sdk/python/examples/controls/text_button/icons/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-button-icons" +version = "1.0.0" +description = "Adds standard and custom-colored icons to TextButton controls." +requires-python = ">=3.10" +keywords = ["text button", "icons", "button", "material", "styling"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Buttons/TextButton"] + +[tool.flet.metadata] +title = "TextButton icons" +controls = ["SafeArea", "Column", "TextButton"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["icon buttons", "custom icon colors"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_field/basic.py b/sdk/python/examples/controls/text_field/basic.py deleted file mode 100644 index e592be84af..0000000000 --- a/sdk/python/examples/controls/text_field/basic.py +++ /dev/null @@ -1,23 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_button_click(e: ft.Event[ft.Button]): - message.value = f"Textboxes values are: '{tb1.value}', '{tb2.value}', " - f"'{tb3.value}', '{tb4.value}', '{tb5.value}'." - page.update() - - page.add( - tb1 := ft.TextField(label="Standard"), - tb2 := ft.TextField(label="Disabled", disabled=True, value="First name"), - tb3 := ft.TextField(label="Read-only", read_only=True, value="Last name"), - tb4 := ft.TextField( - label="With placeholder", hint_text="Please enter text here" - ), - tb5 := ft.TextField(label="With an icon", icon=ft.Icons.EMOJI_EMOTIONS), - ft.Button(content="Submit", on_click=handle_button_click), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text_field/basic/main.py b/sdk/python/examples/controls/text_field/basic/main.py new file mode 100644 index 0000000000..6cece3e499 --- /dev/null +++ b/sdk/python/examples/controls/text_field/basic/main.py @@ -0,0 +1,43 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_button_click(e: ft.Event[ft.Button]): + message.value = ( + f"Textboxes values are: '{tb1.value}', '{tb2.value}', " + f"'{tb3.value}', '{tb4.value}', '{tb5.value}'." + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + tb1 := ft.TextField(label="Standard"), + tb2 := ft.TextField( + label="Disabled", + disabled=True, + value="First name", + ), + tb3 := ft.TextField( + label="Read-only", + read_only=True, + value="Last name", + ), + tb4 := ft.TextField( + label="With placeholder", + hint_text="Please enter text here", + ), + tb5 := ft.TextField( + label="With an icon", + icon=ft.Icons.EMOJI_EMOTIONS, + ), + ft.Button(content="Submit", on_click=handle_button_click), + message := ft.Text(), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_field/basic/pyproject.toml b/sdk/python/examples/controls/text_field/basic/pyproject.toml new file mode 100644 index 0000000000..b4d729ec59 --- /dev/null +++ b/sdk/python/examples/controls/text_field/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-field-basic" +version = "1.0.0" +description = "Compares standard, disabled, read-only, placeholder, and icon TextField states." +requires-python = ">=3.10" +keywords = ["text field", "input", "disabled", "read-only", "basic"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TextField"] + +[tool.flet.metadata] +title = "TextField basic" +controls = ["SafeArea", "Column", "TextField", "Button", "Text"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["multiple field states", "value summary"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_field/handling_change_events.py b/sdk/python/examples/controls/text_field/handling_change_events.py deleted file mode 100644 index 1dbf5585ef..0000000000 --- a/sdk/python/examples/controls/text_field/handling_change_events.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_field_change(e: ft.Event[ft.TextField]): - message.value = e.control.value - page.update() - - page.add( - ft.TextField( - label="Textbox with 'change' event:", - on_change=handle_field_change, - ), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text_field/handling_change_events/main.py b/sdk/python/examples/controls/text_field/handling_change_events/main.py new file mode 100644 index 0000000000..dc72dc0cfd --- /dev/null +++ b/sdk/python/examples/controls/text_field/handling_change_events/main.py @@ -0,0 +1,24 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_field_change(e: ft.Event[ft.TextField]): + message.value = e.control.value + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.TextField( + label="Textbox with 'change' event:", + on_change=handle_field_change, + ), + message := ft.Text(), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_field/handling_change_events/pyproject.toml b/sdk/python/examples/controls/text_field/handling_change_events/pyproject.toml new file mode 100644 index 0000000000..87de8505cd --- /dev/null +++ b/sdk/python/examples/controls/text_field/handling_change_events/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-field-handling-change-events" +version = "1.0.0" +description = "Echoes TextField input live as the value changes." +requires-python = ">=3.10" +keywords = ["text field", "events", "change", "input", "live"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TextField"] + +[tool.flet.metadata] +title = "Handling change events" +controls = ["SafeArea", "Column", "TextField", "Text"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["change events", "live updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_field/label_hint_helper_counter.py b/sdk/python/examples/controls/text_field/label_hint_helper_counter.py deleted file mode 100644 index 66f426a819..0000000000 --- a/sdk/python/examples/controls/text_field/label_hint_helper_counter.py +++ /dev/null @@ -1,55 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - - def handle_field_change(e: ft.Event[ft.TextField]): - message.value = e.control.value - page.update() - - page.add( - ft.TextField( - on_change=handle_field_change, - text_style=ft.TextStyle( - size=15, - italic=True, - color=ft.Colors.DEEP_ORANGE_600, - bgcolor=ft.Colors.LIME_ACCENT_200, - ), - label="Label", - label_style=ft.TextStyle( - size=17, - weight=ft.FontWeight.BOLD, - italic=True, - color=ft.Colors.BLUE, - bgcolor=ft.Colors.RED_700, - ), - hint_text="Hint", - hint_style=ft.TextStyle( - size=15, - weight=ft.FontWeight.BOLD, - italic=True, - color=ft.Colors.PINK_ACCENT, - bgcolor=ft.Colors.BROWN_400, - ), - helper="Helper", - helper_style=ft.TextStyle( - size=14, - weight=ft.FontWeight.BOLD, - color=ft.Colors.DEEP_PURPLE, - bgcolor=ft.Colors.BLUE_50, - ), - counter="Counter", - counter_style=ft.TextStyle( - size=14, - italic=True, - color=ft.Colors.YELLOW, - bgcolor=ft.Colors.GREEN_500, - ), - ), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text_field/label_hint_helper_counter/main.py b/sdk/python/examples/controls/text_field/label_hint_helper_counter/main.py new file mode 100644 index 0000000000..6760ab9bec --- /dev/null +++ b/sdk/python/examples/controls/text_field/label_hint_helper_counter/main.py @@ -0,0 +1,61 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + + def handle_field_change(e: ft.Event[ft.TextField]): + message.value = e.control.value + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.TextField( + on_change=handle_field_change, + text_style=ft.TextStyle( + size=15, + italic=True, + color=ft.Colors.DEEP_ORANGE_600, + bgcolor=ft.Colors.LIME_ACCENT_200, + ), + label="Label", + label_style=ft.TextStyle( + size=17, + weight=ft.FontWeight.BOLD, + italic=True, + color=ft.Colors.BLUE, + bgcolor=ft.Colors.RED_700, + ), + hint_text="Hint", + hint_style=ft.TextStyle( + size=15, + weight=ft.FontWeight.BOLD, + italic=True, + color=ft.Colors.PINK_ACCENT, + bgcolor=ft.Colors.BROWN_400, + ), + helper="Helper", + helper_style=ft.TextStyle( + size=14, + weight=ft.FontWeight.BOLD, + color=ft.Colors.DEEP_PURPLE, + bgcolor=ft.Colors.BLUE_50, + ), + counter="Counter", + counter_style=ft.TextStyle( + size=14, + italic=True, + color=ft.Colors.YELLOW, + bgcolor=ft.Colors.GREEN_500, + ), + ), + message := ft.Text(), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_field/label_hint_helper_counter/pyproject.toml b/sdk/python/examples/controls/text_field/label_hint_helper_counter/pyproject.toml new file mode 100644 index 0000000000..79f08fb3a5 --- /dev/null +++ b/sdk/python/examples/controls/text_field/label_hint_helper_counter/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-field-label-hint-helper-counter" +version = "1.0.0" +description = "Styles TextField label, hint, helper, and counter text independently." +requires-python = ">=3.10" +keywords = ["text field", "label", "hint", "helper", "counter"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TextField"] + +[tool.flet.metadata] +title = "Custom label, hint, helper, and counter texts and styles" +controls = ["SafeArea", "Column", "TextField", "Text"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["custom label styling", "custom hint styling", "custom helper and counter styling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_field/multiline.py b/sdk/python/examples/controls/text_field/multiline.py deleted file mode 100644 index 8b980a11aa..0000000000 --- a/sdk/python/examples/controls/text_field/multiline.py +++ /dev/null @@ -1,25 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.TextField( - label="Standard", - multiline=True, - ), - ft.TextField( - label="Disabled", - multiline=True, - disabled=True, - value="line1\nline2\nline3\nline4\nline5", - ), - ft.TextField( - label="Auto adjusted height with max lines", - multiline=True, - min_lines=1, - max_lines=3, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text_field/multiline/main.py b/sdk/python/examples/controls/text_field/multiline/main.py new file mode 100644 index 0000000000..1f51a32f10 --- /dev/null +++ b/sdk/python/examples/controls/text_field/multiline/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.TextField( + label="Standard", + multiline=True, + ), + ft.TextField( + label="Disabled", + multiline=True, + disabled=True, + value="line1\nline2\nline3\nline4\nline5", + ), + ft.TextField( + label="Auto adjusted height with max lines", + multiline=True, + min_lines=1, + max_lines=3, + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_field/multiline/pyproject.toml b/sdk/python/examples/controls/text_field/multiline/pyproject.toml new file mode 100644 index 0000000000..32d2ae4e32 --- /dev/null +++ b/sdk/python/examples/controls/text_field/multiline/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-field-multiline" +version = "1.0.0" +description = "Compares multiline TextField configurations including disabled and auto-sized fields." +requires-python = ">=3.10" +keywords = ["text field", "multiline", "input", "disabled", "autosize"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TextField"] + +[tool.flet.metadata] +title = "Multiline fields" +controls = ["SafeArea", "Column", "TextField"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["multiline input", "auto-sized height"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_field/password.py b/sdk/python/examples/controls/text_field/password.py deleted file mode 100644 index a15b063f94..0000000000 --- a/sdk/python/examples/controls/text_field/password.py +++ /dev/null @@ -1,14 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.TextField( - label="Password with reveal button", - password=True, - can_reveal_password=True, - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text_field/password/main.py b/sdk/python/examples/controls/text_field/password/main.py new file mode 100644 index 0000000000..91c423d901 --- /dev/null +++ b/sdk/python/examples/controls/text_field/password/main.py @@ -0,0 +1,17 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.TextField( + label="Password with reveal button", + password=True, + can_reveal_password=True, + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_field/password/pyproject.toml b/sdk/python/examples/controls/text_field/password/pyproject.toml new file mode 100644 index 0000000000..e26b76d4e5 --- /dev/null +++ b/sdk/python/examples/controls/text_field/password/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-field-password" +version = "1.0.0" +description = "Shows a password TextField with a built-in reveal button." +requires-python = ">=3.10" +keywords = ["text field", "password", "secure input", "reveal", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TextField"] + +[tool.flet.metadata] +title = "Password with reveal button" +controls = ["SafeArea", "TextField"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["password reveal"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_field/prefix_and_suffix.py b/sdk/python/examples/controls/text_field/prefix_and_suffix.py deleted file mode 100644 index 0007346b47..0000000000 --- a/sdk/python/examples/controls/text_field/prefix_and_suffix.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_button_click(e: ft.Event[ft.Button]): - message.value = ( - "Textboxes values are: " - f"'{prefix_field.value}', " - f"'{suffix_field.value}', " - f"'{prefix_suffix_field.value}', " - f"'{color_field.value}'." - ) - page.update() - - page.add( - prefix_field := ft.TextField(label="With prefix", prefix="https://"), - suffix_field := ft.TextField(label="With suffix", suffix=".com"), - prefix_suffix_field := ft.TextField( - label="With prefix and suffix", - prefix="https://", - suffix=".com", - enable_interactive_selection=True, - ), - color_field := ft.TextField( - label="My favorite color", - icon=ft.Icons.FORMAT_SIZE, - hint_text="Type your favorite color", - helper="You can type only one color", - counter="{value_length}/{max_length} chars used", - prefix_icon=ft.Icons.COLOR_LENS, - suffix="...is your color", - max_length=20, - ), - ft.Button(content="Submit", on_click=handle_button_click), - message := ft.Text(), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text_field/prefix_and_suffix/main.py b/sdk/python/examples/controls/text_field/prefix_and_suffix/main.py new file mode 100644 index 0000000000..503eb87009 --- /dev/null +++ b/sdk/python/examples/controls/text_field/prefix_and_suffix/main.py @@ -0,0 +1,51 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_button_click(e: ft.Event[ft.Button]): + message.value = ( + "Textboxes values are: " + f"'{prefix_field.value}', " + f"'{suffix_field.value}', " + f"'{prefix_suffix_field.value}', " + f"'{color_field.value}'." + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + prefix_field := ft.TextField( + label="With prefix", + prefix="https://", + ), + suffix_field := ft.TextField( + label="With suffix", + suffix=".com", + ), + prefix_suffix_field := ft.TextField( + label="With prefix and suffix", + prefix="https://", + suffix=".com", + enable_interactive_selection=True, + ), + color_field := ft.TextField( + label="My favorite color", + icon=ft.Icons.FORMAT_SIZE, + hint_text="Type your favorite color", + helper="You can type only one color", + counter="{value_length}/{max_length} chars used", + prefix_icon=ft.Icons.COLOR_LENS, + suffix="...is your color", + max_length=20, + ), + ft.Button(content="Submit", on_click=handle_button_click), + message := ft.Text(), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_field/prefix_and_suffix/pyproject.toml b/sdk/python/examples/controls/text_field/prefix_and_suffix/pyproject.toml new file mode 100644 index 0000000000..baef30c0c8 --- /dev/null +++ b/sdk/python/examples/controls/text_field/prefix_and_suffix/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-field-prefix-and-suffix" +version = "1.0.0" +description = "Adds prefixes, suffixes, counters, and helper text to TextField inputs." +requires-python = ">=3.10" +keywords = ["text field", "prefix", "suffix", "counter", "helper"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TextField"] + +[tool.flet.metadata] +title = "Setting prefixes and suffixes" +controls = ["SafeArea", "Column", "TextField", "Button", "Text"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["prefix and suffix text", "helper text", "counters"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_field/selection_change.py b/sdk/python/examples/controls/text_field/selection_change.py deleted file mode 100644 index 43d6fa5abe..0000000000 --- a/sdk/python/examples/controls/text_field/selection_change.py +++ /dev/null @@ -1,52 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Text selection" - - def handle_selection_change(e: ft.TextSelectionChangeEvent[ft.TextField]): - selection.value = ( - f"Selection: '{e.selected_text}'" if e.selected_text else "No selection." - ) - selection_details.value = f"start={e.selection.start}, end={e.selection.end}" - caret.value = f"Caret position: {e.selection.end}" - - async def select_characters(e: ft.Event[ft.Button]): - await field.focus() - field.selection = ft.TextSelection( - base_offset=0, extent_offset=len(field.value) - ) - - async def move_caret(e: ft.Event[ft.Button]): - await field.focus() - field.selection = ft.TextSelection(base_offset=0, extent_offset=0) - - page.add( - ft.Column( - spacing=10, - controls=[ - field := ft.TextField( - value="Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - multiline=True, - min_lines=3, - autofocus=True, - on_selection_change=handle_selection_change, - ), - selection := ft.Text("Select some text from the field."), - selection_details := ft.Text(), - caret := ft.Text("Caret position: -"), - ft.Button( - content="Select all text", - on_click=select_characters, - ), - ft.Button( - content="Move caret to start", - on_click=move_caret, - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/text_field/selection_change/main.py b/sdk/python/examples/controls/text_field/selection_change/main.py new file mode 100644 index 0000000000..8589f7ae77 --- /dev/null +++ b/sdk/python/examples/controls/text_field/selection_change/main.py @@ -0,0 +1,56 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Text selection" + + def handle_selection_change(e: ft.TextSelectionChangeEvent[ft.TextField]): + selection.value = ( + f"Selection: '{e.selected_text}'" if e.selected_text else "No selection." + ) + selection_details.value = f"start={e.selection.start}, end={e.selection.end}" + caret.value = f"Caret position: {e.selection.end}" + + async def select_characters(e: ft.Event[ft.Button]): + await field.focus() + field.selection = ft.TextSelection( + base_offset=0, extent_offset=len(field.value) + ) + + async def move_caret(e: ft.Event[ft.Button]): + await field.focus() + field.selection = ft.TextSelection(base_offset=0, extent_offset=0) + + page.add( + ft.SafeArea( + content=ft.Column( + spacing=10, + controls=[ + field := ft.TextField( + value=( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ), + multiline=True, + min_lines=3, + autofocus=True, + on_selection_change=handle_selection_change, + ), + selection := ft.Text("Select some text from the field."), + selection_details := ft.Text(), + caret := ft.Text("Caret position: -"), + ft.Button( + content="Select all text", + on_click=select_characters, + ), + ft.Button( + content="Move caret to start", + on_click=move_caret, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_field/selection_change/pyproject.toml b/sdk/python/examples/controls/text_field/selection_change/pyproject.toml new file mode 100644 index 0000000000..710da215d8 --- /dev/null +++ b/sdk/python/examples/controls/text_field/selection_change/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-field-selection-change" +version = "1.0.0" +description = "Tracks TextField selection ranges and moves the caret with buttons." +requires-python = ">=3.10" +keywords = ["text field", "selection", "caret", "async", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TextField"] + +[tool.flet.metadata] +title = "Handling selection changes" +controls = ["SafeArea", "Column", "TextField", "Text", "Button"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["selection events", "caret control"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_field/styled.py b/sdk/python/examples/controls/text_field/styled.py deleted file mode 100644 index fe2aca1f4c..0000000000 --- a/sdk/python/examples/controls/text_field/styled.py +++ /dev/null @@ -1,27 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - page.padding = 50 - - page.add( - tf := ft.TextField( - text_size=30, - cursor_color=ft.Colors.RED, - selection_color=ft.Colors.YELLOW, - color=ft.Colors.PINK, - bgcolor=ft.Colors.BLACK_26, - filled=True, - focused_color=ft.Colors.GREEN, - focused_bgcolor=ft.Colors.CYAN_200, - border_radius=30, - border_color=ft.Colors.GREEN_800, - focused_border_color=ft.Colors.GREEN_ACCENT_400, - max_length=20, - capitalization=ft.TextCapitalization.CHARACTERS, - ) - ) - await tf.focus() - - -ft.run(main) diff --git a/sdk/python/examples/controls/text_field/styled/main.py b/sdk/python/examples/controls/text_field/styled/main.py new file mode 100644 index 0000000000..ba34ffdf60 --- /dev/null +++ b/sdk/python/examples/controls/text_field/styled/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +async def main(page: ft.Page): + page.padding = 50 + + tf = ft.TextField( + text_size=30, + cursor_color=ft.Colors.RED, + selection_color=ft.Colors.YELLOW, + color=ft.Colors.PINK, + bgcolor=ft.Colors.BLACK_26, + filled=True, + focused_color=ft.Colors.GREEN, + focused_bgcolor=ft.Colors.CYAN_200, + border_radius=30, + border_color=ft.Colors.GREEN_800, + focused_border_color=ft.Colors.GREEN_ACCENT_400, + max_length=20, + capitalization=ft.TextCapitalization.CHARACTERS, + ) + + page.add( + ft.SafeArea( + content=tf, + ) + ) + await tf.focus() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_field/styled/pyproject.toml b/sdk/python/examples/controls/text_field/styled/pyproject.toml new file mode 100644 index 0000000000..652ce7e042 --- /dev/null +++ b/sdk/python/examples/controls/text_field/styled/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-field-styled" +version = "1.0.0" +description = "Customizes TextField colors, cursor, borders, capitalization, and focus behavior." +requires-python = ">=3.10" +keywords = ["text field", "styling", "focus", "async", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TextField"] + +[tool.flet.metadata] +title = "Styled TextField" +controls = ["SafeArea", "TextField"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["custom field styling", "programmatic focus"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/text_field/underlined_and_borderless.py b/sdk/python/examples/controls/text_field/underlined_and_borderless.py deleted file mode 100644 index 24c0d67e44..0000000000 --- a/sdk/python/examples/controls/text_field/underlined_and_borderless.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.TextField( - label="Underlined", - border=ft.InputBorder.UNDERLINE, - hint_text="Enter text here", - ), - ft.TextField( - label="Underlined filled", - border=ft.InputBorder.UNDERLINE, - filled=True, - hint_text="Enter text here", - ), - ft.TextField( - label="Borderless", - border=ft.InputBorder.NONE, - hint_text="Enter text here", - ), - ft.TextField( - label="Borderless filled", - border=ft.InputBorder.NONE, - filled=True, - hint_text="Enter text here", - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/text_field/underlined_and_borderless/main.py b/sdk/python/examples/controls/text_field/underlined_and_borderless/main.py new file mode 100644 index 0000000000..9d792c93af --- /dev/null +++ b/sdk/python/examples/controls/text_field/underlined_and_borderless/main.py @@ -0,0 +1,38 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.TextField( + label="Underlined", + border=ft.InputBorder.UNDERLINE, + hint_text="Enter text here", + ), + ft.TextField( + label="Underlined filled", + border=ft.InputBorder.UNDERLINE, + filled=True, + hint_text="Enter text here", + ), + ft.TextField( + label="Borderless", + border=ft.InputBorder.NONE, + hint_text="Enter text here", + ), + ft.TextField( + label="Borderless filled", + border=ft.InputBorder.NONE, + filled=True, + hint_text="Enter text here", + ), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/text_field/underlined_and_borderless/pyproject.toml b/sdk/python/examples/controls/text_field/underlined_and_borderless/pyproject.toml new file mode 100644 index 0000000000..11e7d234c4 --- /dev/null +++ b/sdk/python/examples/controls/text_field/underlined_and_borderless/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "text-field-underlined-and-borderless" +version = "1.0.0" +description = "Shows underlined and borderless TextField styles with filled and unfilled variants." +requires-python = ">=3.10" +keywords = ["text field", "border", "underlined", "borderless", "styling"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TextField"] + +[tool.flet.metadata] +title = "Underlined and borderless TextFields" +controls = ["SafeArea", "Column", "TextField"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["underlined border", "borderless style"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/time_picker/__init__.py b/sdk/python/examples/controls/time_picker/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/time_picker/basic.py b/sdk/python/examples/controls/time_picker/basic.py deleted file mode 100644 index b330848165..0000000000 --- a/sdk/python/examples/controls/time_picker/basic.py +++ /dev/null @@ -1,41 +0,0 @@ -from datetime import time - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def handle_change(e: ft.Event[ft.TimePicker]): - selection.value = f"Selection: {time_picker.value}" - page.show_dialog(ft.SnackBar(f"TimePicker change: {time_picker.value}")) - - def handle_dismissal(e: ft.Event[ft.DialogControl]): - page.show_dialog(ft.SnackBar("TimePicker dismissed!")) - - def handle_entry_mode_change(e: ft.TimePickerEntryModeChangeEvent): - page.show_dialog(ft.SnackBar(f"Entry mode changed: {time_picker.entry_mode}")) - - time_picker = ft.TimePicker( - value=time(hour=19, minute=30), - confirm_text="Confirm", - error_invalid_text="Time out of range", - help_text="Pick your time slot", - entry_mode=ft.TimePickerEntryMode.DIAL, - on_change=handle_change, - on_dismiss=handle_dismissal, - on_entry_mode_change=handle_entry_mode_change, - ) - - page.add( - ft.Button( - content="Pick time", - icon=ft.Icons.TIME_TO_LEAVE, - on_click=lambda: page.show_dialog(time_picker), - ), - selection := ft.Text(weight=ft.FontWeight.BOLD), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/time_picker/basic/main.py b/sdk/python/examples/controls/time_picker/basic/main.py new file mode 100644 index 0000000000..852027192a --- /dev/null +++ b/sdk/python/examples/controls/time_picker/basic/main.py @@ -0,0 +1,48 @@ +from datetime import time + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def handle_change(e: ft.Event[ft.TimePicker]): + selection.value = f"Selection: {time_picker.value}" + page.show_dialog(ft.SnackBar(f"TimePicker change: {time_picker.value}")) + + def handle_dismissal(e: ft.Event[ft.DialogControl]): + page.show_dialog(ft.SnackBar("TimePicker dismissed!")) + + def handle_entry_mode_change(e: ft.TimePickerEntryModeChangeEvent): + page.show_dialog(ft.SnackBar(f"Entry mode changed: {time_picker.entry_mode}")) + + time_picker = ft.TimePicker( + value=time(hour=19, minute=30), + confirm_text="Confirm", + error_invalid_text="Time out of range", + help_text="Pick your time slot", + entry_mode=ft.TimePickerEntryMode.DIAL, + on_change=handle_change, + on_dismiss=handle_dismissal, + on_entry_mode_change=handle_entry_mode_change, + ) + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Button( + content="Pick time", + icon=ft.Icons.TIME_TO_LEAVE, + on_click=lambda: page.show_dialog(time_picker), + ), + selection := ft.Text(weight=ft.FontWeight.BOLD), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/time_picker/basic/pyproject.toml b/sdk/python/examples/controls/time_picker/basic/pyproject.toml new file mode 100644 index 0000000000..e0215ded66 --- /dev/null +++ b/sdk/python/examples/controls/time_picker/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "time-picker-basic" +version = "1.0.0" +description = "Opens a TimePicker dialog and reports selection, dismissal, and entry mode changes." +requires-python = ">=3.10" +keywords = ["time picker", "dialog", "events", "selection", "material"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/TimePicker"] + +[tool.flet.metadata] +title = "TimePicker basic" +controls = ["SafeArea", "Column", "Button", "Text", "TimePicker", "SnackBar"] +layout_pattern = "stacked-actions" +complexity = "basic" +features = ["dialog opening", "change callbacks", "dismiss callbacks"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/time_picker/custom_locale.py b/sdk/python/examples/controls/time_picker/custom_locale.py deleted file mode 100644 index 956065d26b..0000000000 --- a/sdk/python/examples/controls/time_picker/custom_locale.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.add( - ft.Button( - content="Pick time (zh_Hans locale)", - icon=ft.Icons.CALENDAR_MONTH, - on_click=lambda e: page.show_dialog( - ft.TimePicker(locale=ft.Locale("zh", "Hans")) - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/time_picker/custom_locale/main.py b/sdk/python/examples/controls/time_picker/custom_locale/main.py new file mode 100644 index 0000000000..75d58281e5 --- /dev/null +++ b/sdk/python/examples/controls/time_picker/custom_locale/main.py @@ -0,0 +1,21 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.add( + ft.SafeArea( + content=ft.Button( + content="Pick time (zh_Hans locale)", + icon=ft.Icons.CALENDAR_MONTH, + on_click=lambda e: page.show_dialog( + ft.TimePicker(locale=ft.Locale("zh", "Hans")) + ), + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/time_picker/custom_locale/pyproject.toml b/sdk/python/examples/controls/time_picker/custom_locale/pyproject.toml new file mode 100644 index 0000000000..ed1d04a6dd --- /dev/null +++ b/sdk/python/examples/controls/time_picker/custom_locale/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "time-picker-custom-locale" +version = "1.0.0" +description = "Opens a TimePicker dialog using a custom zh_Hans locale." +requires-python = ">=3.10" +keywords = ["time picker", "locale", "dialog", "internationalization", "button"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/TimePicker"] + +[tool.flet.metadata] +title = "Custom locale" +controls = ["SafeArea", "Button", "TimePicker"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["custom locale"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/time_picker/hour_formats.py b/sdk/python/examples/controls/time_picker/hour_formats.py deleted file mode 100644 index 8013f3ca0f..0000000000 --- a/sdk/python/examples/controls/time_picker/hour_formats.py +++ /dev/null @@ -1,70 +0,0 @@ -from datetime import time - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def get_system_hour_format(): - """Returns the current system's hour format.""" - return "24h" if page.media.always_use_24_hour_format else "12h" - - def format_time(value: time) -> str: - """Returns a formatted time string based on TimePicker and system settings.""" - use_24h = time_picker.hour_format == ft.TimePickerHourFormat.H24 or ( - time_picker.hour_format == ft.TimePickerHourFormat.SYSTEM - and page.media.always_use_24_hour_format - ) - return value.strftime("%H:%M" if use_24h else "%I:%M %p") - - def handle_change(e: ft.Event[ft.TimePicker]): - selection.value = f"Selection: {format_time(time_picker.value)}" - - time_picker = ft.TimePicker( - value=time(hour=19, minute=30), - help_text="Choose your meeting time", - on_change=handle_change, - ) - - def open_picker(e: ft.Event[ft.Button]): - choice = format_dropdown.value - hour_format_map = { - "system": ft.TimePickerHourFormat.SYSTEM, - "12h": ft.TimePickerHourFormat.H12, - "24h": ft.TimePickerHourFormat.H24, - } - time_picker.hour_format = hour_format_map[choice] - page.show_dialog(time_picker) - - page.add( - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - format_dropdown := ft.Dropdown( - label="Hour format", - value="system", - width=260, - key="dd", - options=[ - ft.DropdownOption( - key="system", - text=f"System default ({get_system_hour_format()})", - ), - ft.DropdownOption(key="12h", text="12-hour clock"), - ft.DropdownOption(key="24h", text="24-hour clock"), - ], - ), - ft.Button( - "Open TimePicker", - icon=ft.Icons.SCHEDULE, - on_click=open_picker, - ), - ], - ), - selection := ft.Text(weight=ft.FontWeight.BOLD), - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/time_picker/hour_formats/main.py b/sdk/python/examples/controls/time_picker/hour_formats/main.py new file mode 100644 index 0000000000..ae0ef4d7b4 --- /dev/null +++ b/sdk/python/examples/controls/time_picker/hour_formats/main.py @@ -0,0 +1,80 @@ +from datetime import time + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def get_system_hour_format(): + """Returns the current system's hour format.""" + return "24h" if page.media.always_use_24_hour_format else "12h" + + def format_time(value: time) -> str: + """Returns a formatted time string based on TimePicker and system settings.""" + use_24h = time_picker.hour_format == ft.TimePickerHourFormat.H24 or ( + time_picker.hour_format == ft.TimePickerHourFormat.SYSTEM + and page.media.always_use_24_hour_format + ) + return value.strftime("%H:%M" if use_24h else "%I:%M %p") + + def handle_change(e: ft.Event[ft.TimePicker]): + selection.value = f"Selection: {format_time(time_picker.value)}" + + time_picker = ft.TimePicker( + value=time(hour=19, minute=30), + help_text="Choose your meeting time", + on_change=handle_change, + ) + + def open_picker(e: ft.Event[ft.Button]): + choice = format_dropdown.value + hour_format_map = { + "system": ft.TimePickerHourFormat.SYSTEM, + "12h": ft.TimePickerHourFormat.H12, + "24h": ft.TimePickerHourFormat.H24, + } + time_picker.hour_format = hour_format_map[choice] + page.show_dialog(time_picker) + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + format_dropdown := ft.Dropdown( + label="Hour format", + value="system", + width=260, + key="dd", + options=[ + ft.DropdownOption( + key="system", + text=( + f"System default " + f"({get_system_hour_format()})" + ), + ), + ft.DropdownOption(key="12h", text="12-hour clock"), + ft.DropdownOption(key="24h", text="24-hour clock"), + ], + ), + ft.Button( + "Open TimePicker", + icon=ft.Icons.SCHEDULE, + on_click=open_picker, + ), + ], + ), + selection := ft.Text(weight=ft.FontWeight.BOLD), + ], + ), + ), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/time_picker/hour_formats/pyproject.toml b/sdk/python/examples/controls/time_picker/hour_formats/pyproject.toml new file mode 100644 index 0000000000..979eb6eaf5 --- /dev/null +++ b/sdk/python/examples/controls/time_picker/hour_formats/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "time-picker-hour-formats" +version = "1.0.0" +description = "Switches a TimePicker between system, 12-hour, and 24-hour display formats." +requires-python = ">=3.10" +keywords = ["time picker", "hour format", "dropdown", "12h", "24h"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Dialogs/TimePicker"] + +[tool.flet.metadata] +title = "Hour formats" +controls = ["SafeArea", "Column", "Row", "Dropdown", "Button", "Text", "TimePicker"] +layout_pattern = "toolbar-content" +complexity = "basic" +features = ["hour format switching", "system format detection"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/transparent_pointer/basic.py b/sdk/python/examples/controls/transparent_pointer/basic.py deleted file mode 100644 index cce31e3e27..0000000000 --- a/sdk/python/examples/controls/transparent_pointer/basic.py +++ /dev/null @@ -1,29 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def button_clicked(e): - print("transparent pointer button clicked") - - page.add( - ft.Stack( - expand=True, - controls=[ - ft.GestureDetector( - on_tap=lambda _: print("TAP!"), - multi_tap_touches=3, - on_multi_tap=lambda e: print("MULTI TAP:", e.global_position), - on_multi_long_press=lambda _: print("Multi tap long press"), - ), - ft.TransparentPointer( - content=ft.Container( - content=ft.Button("Test button", on_click=button_clicked), - padding=50, - ) - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/transparent_pointer/basic/main.py b/sdk/python/examples/controls/transparent_pointer/basic/main.py new file mode 100644 index 0000000000..e5781d0ca3 --- /dev/null +++ b/sdk/python/examples/controls/transparent_pointer/basic/main.py @@ -0,0 +1,36 @@ +import flet as ft + + +def main(page: ft.Page): + def button_clicked(e): + print("transparent pointer button clicked") + + page.add( + ft.SafeArea( + expand=True, + content=ft.Stack( + expand=True, + controls=[ + ft.GestureDetector( + on_tap=lambda _: print("TAP!"), + multi_tap_touches=3, + on_multi_tap=lambda e: print("MULTI TAP:", e.global_position), + on_multi_long_press=lambda _: print("Multi tap long press"), + ), + ft.TransparentPointer( + content=ft.Container( + padding=50, + content=ft.Button( + "Test button", + on_click=button_clicked, + ), + ) + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/transparent_pointer/basic/pyproject.toml b/sdk/python/examples/controls/transparent_pointer/basic/pyproject.toml new file mode 100644 index 0000000000..58f14d2f79 --- /dev/null +++ b/sdk/python/examples/controls/transparent_pointer/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "transparent-pointer-basic" +version = "1.0.0" +description = "Lets pointer events pass through an overlaid button using TransparentPointer." +requires-python = ">=3.10" +keywords = ["transparent pointer", "gesture", "overlay", "stack", "input"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Input/TransparentPointer"] + +[tool.flet.metadata] +title = "TransparentPointer basic" +controls = ["SafeArea", "Stack", "GestureDetector", "TransparentPointer", "Container", "Button"] +layout_pattern = "overlay" +complexity = "basic" +features = ["pointer pass-through", "gesture handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/alignment/container.py b/sdk/python/examples/controls/types/alignment/container.py deleted file mode 100644 index b6f0f1ca35..0000000000 --- a/sdk/python/examples/controls/types/alignment/container.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Containers with different alignments" - - page.add( - ft.Row( - controls=[ - ft.Container( - content=ft.Button("Center"), - bgcolor=ft.Colors.AMBER, - padding=15, - alignment=ft.Alignment.CENTER, - width=150, - height=150, - ), - ft.Container( - content=ft.Button("Top left"), - bgcolor=ft.Colors.AMBER, - padding=15, - alignment=ft.Alignment.TOP_LEFT, - width=150, - height=150, - ), - ft.Container( - content=ft.Button("-0.5, -0.5"), - bgcolor=ft.Colors.AMBER, - padding=15, - alignment=ft.Alignment(-0.5, -0.5), - width=150, - height=150, - ), - ] - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/alignment/container/main.py b/sdk/python/examples/controls/types/alignment/container/main.py new file mode 100644 index 0000000000..95a29be797 --- /dev/null +++ b/sdk/python/examples/controls/types/alignment/container/main.py @@ -0,0 +1,47 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Containers with different alignments" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + controls=[ + ft.Container( + bgcolor=ft.Colors.AMBER, + padding=15, + alignment=ft.Alignment.CENTER, + width=150, + height=150, + content=ft.Button("Center"), + ), + ft.Container( + bgcolor=ft.Colors.AMBER, + padding=15, + alignment=ft.Alignment.TOP_LEFT, + width=150, + height=150, + content=ft.Button("Top left"), + ), + ft.Container( + bgcolor=ft.Colors.AMBER, + padding=15, + alignment=ft.Alignment(-0.5, -0.5), + width=150, + height=150, + content=ft.Button("-0.5, -0.5"), + ), + ], + wrap=True, + ) + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/alignment/container/pyproject.toml b/sdk/python/examples/controls/types/alignment/container/pyproject.toml new file mode 100644 index 0000000000..cb8c384639 --- /dev/null +++ b/sdk/python/examples/controls/types/alignment/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-alignment-container" +version = "1.0.0" +description = "Shows how container content placement changes with preset and custom alignment values." +requires-python = ">=3.10" +keywords = ["alignment", "types", "container", "layout", "positioning"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Container alignment" +controls = ["SafeArea", "Column", "Row", "Container", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["preset alignment", "custom alignment coordinates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/animated_switcher_transition/showcase.py b/sdk/python/examples/controls/types/animated_switcher_transition/showcase.py deleted file mode 100644 index 2a29132850..0000000000 --- a/sdk/python/examples/controls/types/animated_switcher_transition/showcase.py +++ /dev/null @@ -1,74 +0,0 @@ -import flet as ft - - -def showcase_card(transition: ft.AnimatedSwitcherTransition) -> ft.Container: - state = 0 - values = ["A", "B"] - - switcher = ft.AnimatedSwitcher( - duration=500, - reverse_duration=300, - transition=transition, - content=ft.Text( - values[0], - size=40, - weight=ft.FontWeight.BOLD, - ), - ) - - def swap(_): - nonlocal state - state = 1 - state - switcher.content = ft.Text( - values[state], - size=40, - weight=ft.FontWeight.BOLD, - ) - switcher.update() - - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(transition.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=240, - height=90, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - alignment=ft.Alignment.CENTER, - content=switcher, - ), - ft.Button("Swap", on_click=swap), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="AnimatedSwitcherTransition Showcase") - page.add( - ft.Text("Swap content to compare switcher transition effects."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(transition) - for transition in ft.AnimatedSwitcherTransition - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/animated_switcher_transition/showcase/main.py b/sdk/python/examples/controls/types/animated_switcher_transition/showcase/main.py new file mode 100644 index 0000000000..9802998e0d --- /dev/null +++ b/sdk/python/examples/controls/types/animated_switcher_transition/showcase/main.py @@ -0,0 +1,81 @@ +import flet as ft + + +def showcase_card(transition: ft.AnimatedSwitcherTransition) -> ft.Container: + state = 0 + values = ["A", "B"] + + switcher = ft.AnimatedSwitcher( + duration=500, + reverse_duration=300, + transition=transition, + content=ft.Text( + values[0], + size=40, + weight=ft.FontWeight.BOLD, + ), + ) + + def swap(_): + nonlocal state + state = 1 - state + switcher.content = ft.Text( + values[state], + size=40, + weight=ft.FontWeight.BOLD, + ) + switcher.update() + + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(transition.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=240, + height=90, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + alignment=ft.Alignment.CENTER, + content=switcher, + ), + ft.Button("Swap", on_click=swap), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="AnimatedSwitcherTransition Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Swap content to compare switcher transition effects."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(transition) + for transition in ft.AnimatedSwitcherTransition + ], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/animated_switcher_transition/showcase/pyproject.toml b/sdk/python/examples/controls/types/animated_switcher_transition/showcase/pyproject.toml new file mode 100644 index 0000000000..e7cba6411e --- /dev/null +++ b/sdk/python/examples/controls/types/animated_switcher_transition/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-animated-switcher-transition-showcase" +version = "1.0.0" +description = "Compares Animated Switcher Transition values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["animated switcher transition", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Animated Switcher Transition showcase" +controls = ["SafeArea", "Column", "Container", "AnimatedSwitcher", "Text", "Button", "Page", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/animation_curve/showcase.py b/sdk/python/examples/controls/types/animation_curve/showcase.py deleted file mode 100644 index a7373b4ac0..0000000000 --- a/sdk/python/examples/controls/types/animation_curve/showcase.py +++ /dev/null @@ -1,158 +0,0 @@ -import asyncio - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - duration_ms = 1200 - track_width = 248 - racer_size = 26 - lane_padding = 10 - lane_inner_width = track_width - lane_width = lane_inner_width + lane_padding * 2 - travel_units = (lane_inner_width - racer_size) / racer_size - card_animations: list = [] - - def showcase_card(curve: ft.AnimationCurve) -> ft.Container: - state = {"forward": False} - progress = ft.Container( - width=0, - height=6, - border_radius=3, - bgcolor=ft.Colors.PRIMARY_CONTAINER, - animate=ft.Animation(duration_ms, curve=curve), - ) - racer = ft.Container( - width=racer_size, - height=racer_size, - border_radius=13, - bgcolor=ft.Colors.PRIMARY, - shadow=ft.BoxShadow( - blur_radius=12, spread_radius=1, color=ft.Colors.PRIMARY - ), - alignment=ft.Alignment.CENTER, - content=ft.Icon(ft.Icons.BOLT, size=14, color=ft.Colors.ON_PRIMARY), - offset=ft.Offset(0, 0), - rotate=0, - scale=1, - animate_offset=ft.Animation(duration_ms, curve=curve), - animate_rotation=ft.Animation(duration_ms, curve=curve), - animate_scale=ft.Animation(duration_ms, curve=curve), - ) - status = ft.Text("idle", size=11, color=ft.Colors.ON_SURFACE_VARIANT) - - def animate(forward: bool): - state["forward"] = forward - racer.offset = ft.Offset(travel_units if forward else 0, 0) - racer.rotate = 1 if forward else 0 - racer.scale = 1.25 if forward else 1 - progress.width = track_width if forward else 0 - status.value = "forward" if forward else "reverse" - racer.update() - progress.update() - status.update() - - def replay(e): - animate(not state["forward"]) - - card_animations.append(animate) - - return ft.Container( - width=340, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=9, - controls=[ - ft.Text(curve.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=track_width, - height=6, - border_radius=3, - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGH, - content=progress, - ), - ft.Container( - width=lane_width, - height=48, - padding=lane_padding, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - clip_behavior=ft.ClipBehavior.HARD_EDGE, - bgcolor=ft.Colors.SURFACE, - content=ft.Stack( - controls=[ - ft.Row( - spacing=0, - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - controls=[ - ft.Container( - width=4, - height=22, - bgcolor=ft.Colors.OUTLINE, - ), - ft.Container( - width=4, - height=22, - bgcolor=ft.Colors.OUTLINE, - ), - ], - ), - racer, - ], - ), - ), - ft.Row( - alignment=ft.MainAxisAlignment.SPACE_BETWEEN, - controls=[ - status, - ft.Button("Replay", icon=ft.Icons.REPLAY, on_click=replay), - ], - ), - ], - ), - ) - - def play_all(e): - for animate in card_animations: - animate(True) - - def reverse_all(e): - for animate in card_animations: - animate(False) - - async def wave_all(): - for animate in card_animations: - animate(True) - await asyncio.sleep(0.04) - - page.appbar = ft.AppBar(title="AnimationCurve Showcase") - page.add( - ft.Text( - "Curve Lab: compare timing profiles across motion, progress, and spin." - ), - ft.Row( - wrap=True, - spacing=8, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Button("Play all", icon=ft.Icons.PLAY_ARROW, on_click=play_all), - ft.Button("Reverse all", icon=ft.Icons.REPLAY, on_click=reverse_all), - ft.Button("Wave", on_click=lambda e: page.run_task(wave_all)), - ], - ), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(curve) for curve in ft.AnimationCurve], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/animation_curve/showcase/main.py b/sdk/python/examples/controls/types/animation_curve/showcase/main.py new file mode 100644 index 0000000000..3cdbfac2f1 --- /dev/null +++ b/sdk/python/examples/controls/types/animation_curve/showcase/main.py @@ -0,0 +1,174 @@ +import asyncio + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + duration_ms = 1200 + track_width = 248 + racer_size = 26 + lane_padding = 10 + lane_inner_width = track_width + lane_width = lane_inner_width + lane_padding * 2 + travel_units = (lane_inner_width - racer_size) / racer_size + card_animations: list = [] + + def showcase_card(curve: ft.AnimationCurve) -> ft.Container: + state = {"forward": False} + progress = ft.Container( + width=0, + height=6, + border_radius=3, + bgcolor=ft.Colors.PRIMARY_CONTAINER, + animate=ft.Animation(duration_ms, curve=curve), + ) + racer = ft.Container( + width=racer_size, + height=racer_size, + border_radius=13, + bgcolor=ft.Colors.PRIMARY, + shadow=ft.BoxShadow( + blur_radius=12, spread_radius=1, color=ft.Colors.PRIMARY + ), + alignment=ft.Alignment.CENTER, + content=ft.Icon(ft.Icons.BOLT, size=14, color=ft.Colors.ON_PRIMARY), + offset=ft.Offset(0, 0), + rotate=0, + scale=1, + animate_offset=ft.Animation(duration_ms, curve=curve), + animate_rotation=ft.Animation(duration_ms, curve=curve), + animate_scale=ft.Animation(duration_ms, curve=curve), + ) + status = ft.Text("idle", size=11, color=ft.Colors.ON_SURFACE_VARIANT) + + def animate(forward: bool): + state["forward"] = forward + racer.offset = ft.Offset(travel_units if forward else 0, 0) + racer.rotate = 1 if forward else 0 + racer.scale = 1.25 if forward else 1 + progress.width = track_width if forward else 0 + status.value = "forward" if forward else "reverse" + racer.update() + progress.update() + status.update() + + def replay(e): + animate(not state["forward"]) + + card_animations.append(animate) + + return ft.Container( + width=340, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=9, + controls=[ + ft.Text(curve.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=track_width, + height=6, + border_radius=3, + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGH, + content=progress, + ), + ft.Container( + width=lane_width, + height=48, + padding=lane_padding, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + clip_behavior=ft.ClipBehavior.HARD_EDGE, + bgcolor=ft.Colors.SURFACE, + content=ft.Stack( + controls=[ + ft.Row( + spacing=0, + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + controls=[ + ft.Container( + width=4, + height=22, + bgcolor=ft.Colors.OUTLINE, + ), + ft.Container( + width=4, + height=22, + bgcolor=ft.Colors.OUTLINE, + ), + ], + ), + racer, + ], + ), + ), + ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + controls=[ + status, + ft.Button("Replay", icon=ft.Icons.REPLAY, on_click=replay), + ], + ), + ], + ), + ) + + def play_all(e): + for animate in card_animations: + animate(True) + + def reverse_all(e): + for animate in card_animations: + animate(False) + + async def wave_all(): + for animate in card_animations: + animate(True) + await asyncio.sleep(0.04) + + page.appbar = ft.AppBar(title="AnimationCurve Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Curve Lab: compare timing profiles across motion, " + "progress, and spin." + ), + ft.Row( + wrap=True, + spacing=8, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Button( + "Play all", icon=ft.Icons.PLAY_ARROW, on_click=play_all + ), + ft.Button( + "Reverse all", + icon=ft.Icons.REPLAY, + on_click=reverse_all, + ), + ft.Button( + "Wave", on_click=lambda e: page.run_task(wave_all) + ), + ], + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(curve) for curve in ft.AnimationCurve], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/animation_curve/showcase/pyproject.toml b/sdk/python/examples/controls/types/animation_curve/showcase/pyproject.toml new file mode 100644 index 0000000000..7a876fda14 --- /dev/null +++ b/sdk/python/examples/controls/types/animation_curve/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-animation-curve-showcase" +version = "1.0.0" +description = "Compares Animation Curve values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["animation curve", "showcase", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Animation Curve showcase" +controls = ["SafeArea", "Column", "Page", "AnimationCurve", "Container", "Icon", "Text", "Stack"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/app_lifecycle_state/showcase.py b/sdk/python/examples/controls/types/app_lifecycle_state/showcase.py deleted file mode 100644 index d105fba482..0000000000 --- a/sdk/python/examples/controls/types/app_lifecycle_state/showcase.py +++ /dev/null @@ -1,55 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - log = ft.Column( - spacing=4, - scroll=ft.ScrollMode.AUTO, - height=220, - ) - - def add_log(message: str): - log.controls.insert(0, ft.Text(message, size=12)) - if len(log.controls) > 20: - log.controls.pop() - log.update() - - def on_lifecycle(e: ft.AppLifecycleStateChangeEvent): - add_log(f"Received: {e.state.name}") - - page.on_app_lifecycle_state_change = on_lifecycle - - page.appbar = ft.AppBar(title="AppLifecycleState Showcase") - page.add( - ft.Text("Switch app focus/visibility to see lifecycle state changes."), - ft.Text( - "Ex: minimize/restore app, switch tabs, or background/foreground app.", - size=11, - ), - ft.Row( - wrap=True, - spacing=8, - controls=[ - ft.Container( - padding=ft.Padding.symmetric(horizontal=8, vertical=4), - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=12, - content=ft.Text(state.name, size=11), - ) - for state in ft.AppLifecycleState - ], - ), - ft.Container( - width=720, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=log, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/app_lifecycle_state/showcase/main.py b/sdk/python/examples/controls/types/app_lifecycle_state/showcase/main.py new file mode 100644 index 0000000000..6600f230ce --- /dev/null +++ b/sdk/python/examples/controls/types/app_lifecycle_state/showcase/main.py @@ -0,0 +1,65 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + log = ft.Column( + spacing=4, + scroll=ft.ScrollMode.AUTO, + height=220, + ) + + def add_log(message: str): + log.controls.insert(0, ft.Text(message, size=12)) + if len(log.controls) > 20: + log.controls.pop() + log.update() + + def on_lifecycle(e: ft.AppLifecycleStateChangeEvent): + add_log(f"Received: {e.state.name}") + + page.on_app_lifecycle_state_change = on_lifecycle + + page.appbar = ft.AppBar(title="AppLifecycleState Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Switch app focus/visibility to see lifecycle state changes." + ), + ft.Text( + "Ex: minimize/restore app, switch tabs, or " + "background/foreground app.", + size=11, + ), + ft.Row( + wrap=True, + spacing=8, + controls=[ + ft.Container( + padding=ft.Padding.symmetric(horizontal=8, vertical=4), + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=12, + content=ft.Text(state.name, size=11), + ) + for state in ft.AppLifecycleState + ], + ), + ft.Container( + width=720, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=log, + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/app_lifecycle_state/showcase/pyproject.toml b/sdk/python/examples/controls/types/app_lifecycle_state/showcase/pyproject.toml new file mode 100644 index 0000000000..8c0cf98a29 --- /dev/null +++ b/sdk/python/examples/controls/types/app_lifecycle_state/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-app-lifecycle-state-showcase" +version = "1.0.0" +description = "Compares App Lifecycle State values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["app lifecycle state", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "App Lifecycle State showcase" +controls = ["SafeArea", "Column", "Page", "Text", "AppBar", "Row", "Container"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/assertiveness/showcase.py b/sdk/python/examples/controls/types/assertiveness/showcase.py deleted file mode 100644 index 92c921e96e..0000000000 --- a/sdk/python/examples/controls/types/assertiveness/showcase.py +++ /dev/null @@ -1,66 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - semantics = ft.SemanticsService() - - def showcase_card(assertiveness: ft.Assertiveness) -> ft.Container: - status = ft.Text("Send an accessibility announcement.", size=11) - - async def announce(): - try: - await semantics.announce_message( - f"Announcement with {assertiveness.name} assertiveness.", - assertiveness=assertiveness, - ) - status.value = f"Sent with {assertiveness.value}." - except Exception as ex: - status.value = f"Error: {ex}" - status.update() - - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(assertiveness.name, weight=ft.FontWeight.BOLD), - ft.Text( - assertiveness.value, - size=11, - color=ft.Colors.ON_SURFACE_VARIANT, - ), - ft.Button( - "Announce message", - icon=ft.Icons.RECORD_VOICE_OVER, - on_click=lambda: page.run_task(announce), - ), - status, - ], - ), - ) - - page.appbar = ft.AppBar(title="Assertiveness Showcase") - page.add( - ft.Text( - "Compare announcement assertiveness levels. " - "Enable a screen reader to hear the difference." - ), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(assertiveness) for assertiveness in ft.Assertiveness - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/assertiveness/showcase/main.py b/sdk/python/examples/controls/types/assertiveness/showcase/main.py new file mode 100644 index 0000000000..d1aeaa04aa --- /dev/null +++ b/sdk/python/examples/controls/types/assertiveness/showcase/main.py @@ -0,0 +1,74 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + semantics = ft.SemanticsService() + + def showcase_card(assertiveness: ft.Assertiveness) -> ft.Container: + status = ft.Text("Send an accessibility announcement.", size=11) + + async def announce(): + try: + await semantics.announce_message( + f"Announcement with {assertiveness.name} assertiveness.", + assertiveness=assertiveness, + ) + status.value = f"Sent with {assertiveness.value}." + except Exception as ex: + status.value = f"Error: {ex}" + status.update() + + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(assertiveness.name, weight=ft.FontWeight.BOLD), + ft.Text( + assertiveness.value, + size=11, + color=ft.Colors.ON_SURFACE_VARIANT, + ), + ft.Button( + "Announce message", + icon=ft.Icons.RECORD_VOICE_OVER, + on_click=lambda: page.run_task(announce), + ), + status, + ], + ), + ) + + page.appbar = ft.AppBar(title="Assertiveness Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare announcement assertiveness levels. " + "Enable a screen reader to hear the difference." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(assertiveness) + for assertiveness in ft.Assertiveness + ], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/assertiveness/showcase/pyproject.toml b/sdk/python/examples/controls/types/assertiveness/showcase/pyproject.toml new file mode 100644 index 0000000000..a49eaf7e2d --- /dev/null +++ b/sdk/python/examples/controls/types/assertiveness/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-assertiveness-showcase" +version = "1.0.0" +description = "Compares Assertiveness values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["assertiveness", "showcase", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Assertiveness showcase" +controls = ["SafeArea", "Column", "Page", "SemanticsService", "Container", "Text", "Button", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/axis/showcase.py b/sdk/python/examples/controls/types/axis/showcase.py deleted file mode 100644 index fda6502f09..0000000000 --- a/sdk/python/examples/controls/types/axis/showcase.py +++ /dev/null @@ -1,47 +0,0 @@ -import flet as ft - - -def showcase_card(axis: ft.Axis) -> ft.Container: - return ft.Container( - width=350 if axis == ft.Axis.HORIZONTAL else 220, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(axis.name, weight=ft.FontWeight.BOLD), - ft.SegmentedButton( - direction=axis, - selected=["medium"], - segments=[ - ft.Segment(value="small", label="Small"), - ft.Segment(value="medium", label="Medium"), - ft.Segment(value="large", label="Large"), - ], - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="Axis Showcase") - page.add( - ft.Text("Compare horizontal vs vertical segment layout."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(axis) for axis in ft.Axis], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/axis/showcase/main.py b/sdk/python/examples/controls/types/axis/showcase/main.py new file mode 100644 index 0000000000..16f7cb0cd2 --- /dev/null +++ b/sdk/python/examples/controls/types/axis/showcase/main.py @@ -0,0 +1,54 @@ +import flet as ft + + +def showcase_card(axis: ft.Axis) -> ft.Container: + return ft.Container( + width=350 if axis == ft.Axis.HORIZONTAL else 220, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text(axis.name, weight=ft.FontWeight.BOLD), + ft.SegmentedButton( + direction=axis, + selected=["medium"], + segments=[ + ft.Segment(value="small", label="Small"), + ft.Segment(value="medium", label="Medium"), + ft.Segment(value="large", label="Large"), + ], + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="Axis Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare horizontal vs vertical segment layout."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(axis) for axis in ft.Axis], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/axis/showcase/pyproject.toml b/sdk/python/examples/controls/types/axis/showcase/pyproject.toml new file mode 100644 index 0000000000..edaeeff750 --- /dev/null +++ b/sdk/python/examples/controls/types/axis/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-axis-showcase" +version = "1.0.0" +description = "Compares Axis values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["axis", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Axis showcase" +controls = ["SafeArea", "Column", "Container", "Text", "SegmentedButton", "Segment", "Page", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/blend_mode/showcase.py b/sdk/python/examples/controls/types/blend_mode/showcase.py deleted file mode 100644 index 1b7a33f838..0000000000 --- a/sdk/python/examples/controls/types/blend_mode/showcase.py +++ /dev/null @@ -1,46 +0,0 @@ -import flet as ft - - -def showcase_card(blend_mode: ft.BlendMode) -> ft.Container: - return ft.Container( - width=280, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(blend_mode.name, weight=ft.FontWeight.BOLD), - ft.Image( - src="https://picsum.photos/id/237/200/300", - width=240, - height=130, - fit=ft.BoxFit.COVER, - color=ft.Colors.LIGHT_GREEN_200, - color_blend_mode=blend_mode, - border_radius=8, - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="BlendMode Showcase") - page.add( - ft.Text("Compare color blending results for each BlendMode value."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(blend_mode) for blend_mode in ft.BlendMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/blend_mode/showcase/main.py b/sdk/python/examples/controls/types/blend_mode/showcase/main.py new file mode 100644 index 0000000000..233bb45fd0 --- /dev/null +++ b/sdk/python/examples/controls/types/blend_mode/showcase/main.py @@ -0,0 +1,55 @@ +import flet as ft + + +def showcase_card(blend_mode: ft.BlendMode) -> ft.Container: + return ft.Container( + width=280, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(blend_mode.name, weight=ft.FontWeight.BOLD), + ft.Image( + src="https://picsum.photos/id/237/200/300", + width=240, + height=130, + fit=ft.BoxFit.COVER, + color=ft.Colors.LIGHT_GREEN_200, + color_blend_mode=blend_mode, + border_radius=8, + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="BlendMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare color blending results for each BlendMode value."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(blend_mode) for blend_mode in ft.BlendMode + ], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/blend_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/blend_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..21446b3b29 --- /dev/null +++ b/sdk/python/examples/controls/types/blend_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-blend-mode-showcase" +version = "1.0.0" +description = "Compares Blend Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["blend mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Blend Mode showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Image", "Page", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/blur/container.py b/sdk/python/examples/controls/types/blur/container.py deleted file mode 100644 index b2ea37d6a3..0000000000 --- a/sdk/python/examples/controls/types/blur/container.py +++ /dev/null @@ -1,61 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.LIGHT - i = 1 - - img_container = ft.Container( - image=ft.DecorationImage(src="https://picsum.photos/300/300"), - width=300, - height=300, - ) - - def handle_button_click(e: ft.Event[ft.Button]): - nonlocal i - img_container.image = ft.DecorationImage( - src=f"https://picsum.photos/300/300?random={i}" - ) - i += 1 - page.update() - - page.add( - ft.Stack( - controls=[ - img_container, - ft.Container( - width=100, - height=100, - blur=10, - bgcolor="#22CCCC00", - ), - ft.Container( - width=100, - height=100, - left=20, - top=120, - blur=(0, 10), - ), - ft.Container( - top=50, - right=10, - blur=ft.Blur(10, 0, ft.BlurTileMode.MIRROR), - width=100, - height=100, - bgcolor="#44CCCCCC", - border_radius=10, - border=ft.Border.all(2, ft.Colors.BLACK), - ), - ft.Button( - content="Change Background", - bottom=5, - right=5, - # style=ft.ButtonStyle(text_style=ft.TextStyle(size=8)), - on_click=handle_button_click, - ), - ] - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/blur/container/main.py b/sdk/python/examples/controls/types/blur/container/main.py new file mode 100644 index 0000000000..dbb97a746f --- /dev/null +++ b/sdk/python/examples/controls/types/blur/container/main.py @@ -0,0 +1,68 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.LIGHT + i = 1 + + img_container = ft.Container( + image=ft.DecorationImage(src="https://picsum.photos/300/300"), + width=300, + height=300, + ) + + def handle_button_click(e: ft.Event[ft.Button]): + nonlocal i + img_container.image = ft.DecorationImage( + src=f"https://picsum.photos/300/300?random={i}" + ) + i += 1 + page.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Stack( + controls=[ + img_container, + ft.Container( + width=100, + height=100, + blur=10, + bgcolor="#22CCCC00", + ), + ft.Container( + width=100, + height=100, + left=20, + top=120, + blur=(0, 10), + ), + ft.Container( + top=50, + right=10, + blur=ft.Blur(10, 0, ft.BlurTileMode.MIRROR), + width=100, + height=100, + bgcolor="#44CCCCCC", + border_radius=10, + border=ft.Border.all(2, ft.Colors.BLACK), + ), + ft.Button( + content="Change Background", + bottom=5, + right=5, + # style=ft.ButtonStyle(text_style=ft.TextStyle(size=8)), + on_click=handle_button_click, + ), + ] + ) + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/blur/container/pyproject.toml b/sdk/python/examples/controls/types/blur/container/pyproject.toml new file mode 100644 index 0000000000..6b2b3aa4b7 --- /dev/null +++ b/sdk/python/examples/controls/types/blur/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-blur-container" +version = "1.0.0" +description = "Demonstrates how Blur values change container presentation and layout." +requires-python = ">=3.10" +keywords = ["blur", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Blur" +controls = ["SafeArea", "Column", "Page", "Container", "Button", "Stack", "ButtonStyle"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["blur preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/blur_style/showcase.py b/sdk/python/examples/controls/types/blur_style/showcase.py deleted file mode 100644 index 15e61f8820..0000000000 --- a/sdk/python/examples/controls/types/blur_style/showcase.py +++ /dev/null @@ -1,57 +0,0 @@ -import flet as ft - - -def showcase_card(style: ft.BlurStyle) -> ft.Container: - sample = ft.Container( - width=130, - height=90, - border_radius=12, - bgcolor=ft.Colors.BLUE_400, - shadow=ft.BoxShadow( - blur_radius=28, - spread_radius=3, - color=ft.Colors.RED_400, - offset=ft.Offset(10, 10), - blur_style=style, - ), - ) - - return ft.Container( - width=300, - height=180, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=12, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(style.name, weight=ft.FontWeight.BOLD), - sample, - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="BlurStyle Showcase") - page.add( - ft.Text( - "Compare shadow blur rendering styles. " - "The blue box uses red shadow with selected blur style." - ), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(style) for style in ft.BlurStyle], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/blur_style/showcase/main.py b/sdk/python/examples/controls/types/blur_style/showcase/main.py new file mode 100644 index 0000000000..70efd25917 --- /dev/null +++ b/sdk/python/examples/controls/types/blur_style/showcase/main.py @@ -0,0 +1,64 @@ +import flet as ft + + +def showcase_card(style: ft.BlurStyle) -> ft.Container: + sample = ft.Container( + width=130, + height=90, + border_radius=12, + bgcolor=ft.Colors.BLUE_400, + shadow=ft.BoxShadow( + blur_radius=28, + spread_radius=3, + color=ft.Colors.RED_400, + offset=ft.Offset(10, 10), + blur_style=style, + ), + ) + + return ft.Container( + width=300, + height=180, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=12, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text(style.name, weight=ft.FontWeight.BOLD), + sample, + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="BlurStyle Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare shadow blur rendering styles. " + "The blue box uses red shadow with selected blur style." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(style) for style in ft.BlurStyle], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/blur_style/showcase/pyproject.toml b/sdk/python/examples/controls/types/blur_style/showcase/pyproject.toml new file mode 100644 index 0000000000..5a22be6753 --- /dev/null +++ b/sdk/python/examples/controls/types/blur_style/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-blur-style-showcase" +version = "1.0.0" +description = "Compares Blur Style values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["blur style", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Blur Style showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Page", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/blur_tile_mode/showcase.py b/sdk/python/examples/controls/types/blur_tile_mode/showcase.py deleted file mode 100644 index e92665761f..0000000000 --- a/sdk/python/examples/controls/types/blur_tile_mode/showcase.py +++ /dev/null @@ -1,82 +0,0 @@ -import flet as ft - - -def checkerboard() -> ft.Column: - colors = [ft.Colors.TEAL_300, ft.Colors.AMBER_300] - return ft.Column( - spacing=0, - controls=[ - ft.Row( - spacing=0, - controls=[ - ft.Container( - width=24, - height=24, - bgcolor=colors[(row + col) % 2], - ) - for col in range(9) - ], - ) - for row in range(6) - ], - ) - - -def showcase_card(mode: ft.BlurTileMode) -> ft.Container: - preview = ft.Container( - width=216, - height=144, - border_radius=8, - clip_behavior=ft.ClipBehavior.ANTI_ALIAS, - content=ft.Stack( - controls=[ - checkerboard(), - ft.Container( - left=8, - top=28, - width=120, - height=80, - blur=ft.Blur(8, 8, mode), - bgcolor="#55FFFFFF", - border_radius=8, - border=ft.Border.all(1, ft.Colors.WHITE), - ), - ] - ), - ) - - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=10, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(mode.name, weight=ft.FontWeight.BOLD), - preview, - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="BlurTileMode Showcase") - page.add( - ft.Text("Compare blur edge sampling outside source bounds."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(mode) for mode in ft.BlurTileMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/blur_tile_mode/showcase/main.py b/sdk/python/examples/controls/types/blur_tile_mode/showcase/main.py new file mode 100644 index 0000000000..1cbce512d1 --- /dev/null +++ b/sdk/python/examples/controls/types/blur_tile_mode/showcase/main.py @@ -0,0 +1,89 @@ +import flet as ft + + +def checkerboard() -> ft.Column: + colors = [ft.Colors.TEAL_300, ft.Colors.AMBER_300] + return ft.Column( + spacing=0, + controls=[ + ft.Row( + spacing=0, + controls=[ + ft.Container( + width=24, + height=24, + bgcolor=colors[(row + col) % 2], + ) + for col in range(9) + ], + ) + for row in range(6) + ], + ) + + +def showcase_card(mode: ft.BlurTileMode) -> ft.Container: + preview = ft.Container( + width=216, + height=144, + border_radius=8, + clip_behavior=ft.ClipBehavior.ANTI_ALIAS, + content=ft.Stack( + controls=[ + checkerboard(), + ft.Container( + left=8, + top=28, + width=120, + height=80, + blur=ft.Blur(8, 8, mode), + bgcolor="#55FFFFFF", + border_radius=8, + border=ft.Border.all(1, ft.Colors.WHITE), + ), + ] + ), + ) + + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=10, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text(mode.name, weight=ft.FontWeight.BOLD), + preview, + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="BlurTileMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare blur edge sampling outside source bounds."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(mode) for mode in ft.BlurTileMode], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/blur_tile_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/blur_tile_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..56c78a1c12 --- /dev/null +++ b/sdk/python/examples/controls/types/blur_tile_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-blur-tile-mode-showcase" +version = "1.0.0" +description = "Compares Blur Tile Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["blur tile mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Blur Tile Mode showcase" +controls = ["SafeArea", "Column", "Row", "Container", "Stack", "Text", "Page", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/border/container.py b/sdk/python/examples/controls/types/border/container.py deleted file mode 100644 index a8cc860faa..0000000000 --- a/sdk/python/examples/controls/types/border/container.py +++ /dev/null @@ -1,40 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Containers with different borders" - - page.add( - ft.Row( - controls=[ - ft.Container( - bgcolor=ft.Colors.AMBER, - padding=15, - border=ft.Border.all(10, ft.Colors.PINK_600), - border_radius=ft.border_radius.all(30), - width=150, - height=150, - ), - ft.Container( - bgcolor=ft.Colors.DEEP_PURPLE, - padding=15, - border=ft.Border.all(3, ft.Colors.LIGHT_GREEN_ACCENT), - border_radius=ft.border_radius.only(top_left=10, bottom_right=10), - width=150, - height=150, - ), - ft.Container( - bgcolor=ft.Colors.BLUE_GREY_900, - padding=15, - border=ft.Border.symmetric( - vertical=ft.BorderSide(8, ft.Colors.YELLOW_800) - ), - width=150, - height=150, - ), - ] - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/border/container/main.py b/sdk/python/examples/controls/types/border/container/main.py new file mode 100644 index 0000000000..364dbd21eb --- /dev/null +++ b/sdk/python/examples/controls/types/border/container/main.py @@ -0,0 +1,49 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Containers with different borders" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + controls=[ + ft.Container( + bgcolor=ft.Colors.AMBER, + padding=15, + border=ft.Border.all(10, ft.Colors.PINK_600), + border_radius=ft.border_radius.all(30), + width=150, + height=150, + ), + ft.Container( + bgcolor=ft.Colors.DEEP_PURPLE, + padding=15, + border=ft.Border.all(3, ft.Colors.LIGHT_GREEN_ACCENT), + border_radius=ft.border_radius.only( + top_left=10, bottom_right=10 + ), + width=150, + height=150, + ), + ft.Container( + bgcolor=ft.Colors.BLUE_GREY_900, + padding=15, + border=ft.Border.symmetric( + vertical=ft.BorderSide(8, ft.Colors.YELLOW_800) + ), + width=150, + height=150, + ), + ] + ) + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/border/container/pyproject.toml b/sdk/python/examples/controls/types/border/container/pyproject.toml new file mode 100644 index 0000000000..c0ec73a8f7 --- /dev/null +++ b/sdk/python/examples/controls/types/border/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-border-container" +version = "1.0.0" +description = "Demonstrates how Border values change container presentation and layout." +requires-python = ">=3.10" +keywords = ["border", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Border" +controls = ["SafeArea", "Column", "Page", "Row", "Container"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["border preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/border_side_stroke_align/showcase.py b/sdk/python/examples/controls/types/border_side_stroke_align/showcase.py deleted file mode 100644 index a233c24569..0000000000 --- a/sdk/python/examples/controls/types/border_side_stroke_align/showcase.py +++ /dev/null @@ -1,68 +0,0 @@ -import flet as ft - - -def showcase_card(align: ft.BorderSideStrokeAlign) -> ft.Container: - preview = ft.Container( - width=150, - height=150, - alignment=ft.Alignment.CENTER, - content=ft.Stack( - controls=[ - ft.Container( - width=90, - height=90, - border_radius=45, - bgcolor=ft.Colors.ON_SURFACE, - ), - ft.Container( - width=90, - height=90, - border_radius=45, - border=ft.Border.all( - side=ft.BorderSide( - width=18, - color=ft.Colors.RED, - stroke_align=align, - ) - ), - ), - ], - ), - ) - - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(align.name, weight=ft.FontWeight.BOLD), - preview, - ft.Text(f"value={align.value}", size=11), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="BorderSideStrokeAlign Showcase") - page.add( - ft.Text("Compare how thick borders are painted relative to the shape path."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(align) for align in ft.BorderSideStrokeAlign], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/border_side_stroke_align/showcase/main.py b/sdk/python/examples/controls/types/border_side_stroke_align/showcase/main.py new file mode 100644 index 0000000000..8c832d3b77 --- /dev/null +++ b/sdk/python/examples/controls/types/border_side_stroke_align/showcase/main.py @@ -0,0 +1,80 @@ +import flet as ft + + +def showcase_card(align: ft.BorderSideStrokeAlign) -> ft.Container: + preview = ft.Container( + width=150, + height=150, + alignment=ft.Alignment.CENTER, + content=ft.Stack( + controls=[ + ft.Container( + width=90, + height=90, + border_radius=45, + bgcolor=ft.Colors.ON_SURFACE, + ), + ft.Container( + width=90, + height=90, + border_radius=45, + border=ft.Border.all( + side=ft.BorderSide( + width=18, + color=ft.Colors.RED, + stroke_align=align, + ) + ), + ), + ], + ), + ) + + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text(align.name, weight=ft.FontWeight.BOLD), + preview, + ft.Text(f"value={align.value}", size=11), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="BorderSideStrokeAlign Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare how thick borders are painted relative to " + "the shape path." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(align) for align in ft.BorderSideStrokeAlign + ], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/border_side_stroke_align/showcase/pyproject.toml b/sdk/python/examples/controls/types/border_side_stroke_align/showcase/pyproject.toml new file mode 100644 index 0000000000..3fd5b5b17b --- /dev/null +++ b/sdk/python/examples/controls/types/border_side_stroke_align/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-border-side-stroke-align-showcase" +version = "1.0.0" +description = "Compares Border Side Stroke Align values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["border side stroke align", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Border Side Stroke Align showcase" +controls = ["SafeArea", "Column", "BorderSideStrokeAlign", "Container", "Stack", "Text", "Page", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/border_style/showcase.py b/sdk/python/examples/controls/types/border_style/showcase.py deleted file mode 100644 index 7f2f27f73c..0000000000 --- a/sdk/python/examples/controls/types/border_style/showcase.py +++ /dev/null @@ -1,49 +0,0 @@ -import flet as ft - - -def showcase_card(style: ft.BorderStyle) -> ft.Container: - side = ft.BorderSide(width=4, color=ft.Colors.PRIMARY, style=style) - border = ft.Border(left=side, top=side, right=side, bottom=side) - - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(style.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=240, - height=100, - border=border, - border_radius=8, - bgcolor=ft.Colors.SURFACE, - alignment=ft.Alignment.CENTER, - content=ft.Text("Border preview"), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="BorderStyle Showcase") - page.add( - ft.Text("Compare rendered border sides for each BorderStyle value."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(style) for style in ft.BorderStyle], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/border_style/showcase/main.py b/sdk/python/examples/controls/types/border_style/showcase/main.py new file mode 100644 index 0000000000..65e973d67f --- /dev/null +++ b/sdk/python/examples/controls/types/border_style/showcase/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def showcase_card(style: ft.BorderStyle) -> ft.Container: + side = ft.BorderSide(width=4, color=ft.Colors.PRIMARY, style=style) + border = ft.Border(left=side, top=side, right=side, bottom=side) + + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(style.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=240, + height=100, + border=border, + border_radius=8, + bgcolor=ft.Colors.SURFACE, + alignment=ft.Alignment.CENTER, + content=ft.Text("Border preview"), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="BorderStyle Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare rendered border sides for each BorderStyle value." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(style) for style in ft.BorderStyle], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/border_style/showcase/pyproject.toml b/sdk/python/examples/controls/types/border_style/showcase/pyproject.toml new file mode 100644 index 0000000000..b455c82b18 --- /dev/null +++ b/sdk/python/examples/controls/types/border_style/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-border-style-showcase" +version = "1.0.0" +description = "Compares Border Style values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["border style", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Border Style showcase" +controls = ["SafeArea", "Column", "BorderStyle", "Container", "Text", "Page", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/box_fit/showcase.py b/sdk/python/examples/controls/types/box_fit/showcase.py deleted file mode 100644 index 7bc772f7c6..0000000000 --- a/sdk/python/examples/controls/types/box_fit/showcase.py +++ /dev/null @@ -1,51 +0,0 @@ -import flet as ft - - -def showcase_card(fit: ft.BoxFit) -> ft.Container: - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(fit.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=240, - height=120, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - clip_behavior=ft.ClipBehavior.HARD_EDGE, - bgcolor=ft.Colors.SURFACE, - content=ft.Image( - src="https://picsum.photos/id/1025/420/220", - width=240, - height=120, - fit=fit, - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="BoxFit Showcase") - page.add( - ft.Text("Compare how the same image is inscribed into a fixed frame."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(fit) for fit in ft.BoxFit], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/box_fit/showcase/main.py b/sdk/python/examples/controls/types/box_fit/showcase/main.py new file mode 100644 index 0000000000..f471940b63 --- /dev/null +++ b/sdk/python/examples/controls/types/box_fit/showcase/main.py @@ -0,0 +1,60 @@ +import flet as ft + + +def showcase_card(fit: ft.BoxFit) -> ft.Container: + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(fit.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=240, + height=120, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + clip_behavior=ft.ClipBehavior.HARD_EDGE, + bgcolor=ft.Colors.SURFACE, + content=ft.Image( + src="https://picsum.photos/id/1025/420/220", + width=240, + height=120, + fit=fit, + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="BoxFit Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare how the same image is inscribed into a fixed frame." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(fit) for fit in ft.BoxFit], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/box_fit/showcase/pyproject.toml b/sdk/python/examples/controls/types/box_fit/showcase/pyproject.toml new file mode 100644 index 0000000000..46a276254f --- /dev/null +++ b/sdk/python/examples/controls/types/box_fit/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-box-fit-showcase" +version = "1.0.0" +description = "Compares Box Fit values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["box fit", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Box Fit showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Image", "Page", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/box_shadow/container.py b/sdk/python/examples/controls/types/box_shadow/container.py deleted file mode 100644 index eb44b9fc18..0000000000 --- a/sdk/python/examples/controls/types/box_shadow/container.py +++ /dev/null @@ -1,42 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Container( - bgcolor=ft.Colors.YELLOW, - width=100, - height=100, - border_radius=50, - shadow=[ - ft.BoxShadow( - spread_radius=1, - blur_radius=15, - color=ft.Colors.WHITE, - offset=ft.Offset(-5, -5), - ), - ft.BoxShadow( - spread_radius=1, - blur_radius=15, - color=ft.Colors.GREY_600, - offset=ft.Offset(5, 5), - ), - ], - ), - ft.Container( - # bgcolor=ft.Colors.WHITE, - border_radius=10, - width=100, - height=100, - shadow=ft.BoxShadow( - spread_radius=1, - blur_radius=15, - color=ft.Colors.BLUE_GREY_300, - offset=ft.Offset(0, 0), - blur_style=ft.BlurStyle.OUTER, - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/box_shadow/container/main.py b/sdk/python/examples/controls/types/box_shadow/container/main.py new file mode 100644 index 0000000000..5f96f16579 --- /dev/null +++ b/sdk/python/examples/controls/types/box_shadow/container/main.py @@ -0,0 +1,49 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Container( + bgcolor=ft.Colors.YELLOW, + width=100, + height=100, + border_radius=50, + shadow=[ + ft.BoxShadow( + spread_radius=1, + blur_radius=15, + color=ft.Colors.WHITE, + offset=ft.Offset(-5, -5), + ), + ft.BoxShadow( + spread_radius=1, + blur_radius=15, + color=ft.Colors.GREY_600, + offset=ft.Offset(5, 5), + ), + ], + ), + ft.Container( + # bgcolor=ft.Colors.WHITE, + border_radius=10, + width=100, + height=100, + shadow=ft.BoxShadow( + spread_radius=1, + blur_radius=15, + color=ft.Colors.BLUE_GREY_300, + offset=ft.Offset(0, 0), + blur_style=ft.BlurStyle.OUTER, + ), + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/box_shadow/container/pyproject.toml b/sdk/python/examples/controls/types/box_shadow/container/pyproject.toml new file mode 100644 index 0000000000..99f780214d --- /dev/null +++ b/sdk/python/examples/controls/types/box_shadow/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-box-shadow-container" +version = "1.0.0" +description = "Demonstrates how Box Shadow values change container presentation and layout." +requires-python = ">=3.10" +keywords = ["box shadow", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Box Shadow" +controls = ["SafeArea", "Column", "Page", "Container"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["box shadow preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/box_shape/showcase.py b/sdk/python/examples/controls/types/box_shape/showcase.py deleted file mode 100644 index 3ba603d176..0000000000 --- a/sdk/python/examples/controls/types/box_shape/showcase.py +++ /dev/null @@ -1,63 +0,0 @@ -import flet as ft - - -def showcase_card(shape: ft.BoxShape) -> ft.Container: - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(shape.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=200, - height=120, - alignment=ft.Alignment.CENTER, - bgcolor=ft.Colors.SURFACE, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - content=ft.Container( - width=120, - height=90, - alignment=ft.Alignment.CENTER, - shape=shape, - bgcolor=ft.Colors.PRIMARY_CONTAINER, - border=ft.Border.all(2, ft.Colors.PRIMARY), - border_radius=( - ft.BorderRadius.all(16) - if shape == ft.BoxShape.RECTANGLE - else None - ), - content=ft.Text( - "Shape", - color=ft.Colors.ON_PRIMARY_CONTAINER, - weight=ft.FontWeight.BOLD, - ), - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="BoxShape Showcase") - page.add( - ft.Text("Compare rectangular and circular box decoration shapes."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(shape) for shape in ft.BoxShape], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/box_shape/showcase/main.py b/sdk/python/examples/controls/types/box_shape/showcase/main.py new file mode 100644 index 0000000000..07a86207c3 --- /dev/null +++ b/sdk/python/examples/controls/types/box_shape/showcase/main.py @@ -0,0 +1,70 @@ +import flet as ft + + +def showcase_card(shape: ft.BoxShape) -> ft.Container: + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(shape.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=200, + height=120, + alignment=ft.Alignment.CENTER, + bgcolor=ft.Colors.SURFACE, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + content=ft.Container( + width=120, + height=90, + alignment=ft.Alignment.CENTER, + shape=shape, + bgcolor=ft.Colors.PRIMARY_CONTAINER, + border=ft.Border.all(2, ft.Colors.PRIMARY), + border_radius=( + ft.BorderRadius.all(16) + if shape == ft.BoxShape.RECTANGLE + else None + ), + content=ft.Text( + "Shape", + color=ft.Colors.ON_PRIMARY_CONTAINER, + weight=ft.FontWeight.BOLD, + ), + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="BoxShape Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare rectangular and circular box decoration shapes."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(shape) for shape in ft.BoxShape], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/box_shape/showcase/pyproject.toml b/sdk/python/examples/controls/types/box_shape/showcase/pyproject.toml new file mode 100644 index 0000000000..e90da7bae5 --- /dev/null +++ b/sdk/python/examples/controls/types/box_shape/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-box-shape-showcase" +version = "1.0.0" +description = "Compares Box Shape values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["box shape", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Box Shape showcase" +controls = ["SafeArea", "Column", "Container", "Text", "BorderRadius", "Page", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/card_variant/showcase.py b/sdk/python/examples/controls/types/card_variant/showcase.py deleted file mode 100644 index 2da8c2c270..0000000000 --- a/sdk/python/examples/controls/types/card_variant/showcase.py +++ /dev/null @@ -1,56 +0,0 @@ -import flet as ft - - -def showcase_card(variant: ft.CardVariant) -> ft.Container: - card = ft.Card( - variant=variant, - content=ft.Container( - width=260, - padding=12, - content=ft.Column( - spacing=6, - controls=[ - ft.Text("Quarterly Report", weight=ft.FontWeight.BOLD), - ft.Text( - "Revenue increased by 18% compared to last quarter.", - size=12, - ), - ], - ), - ), - ) - - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(variant.name, weight=ft.FontWeight.BOLD), - card, - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="CardVariant Showcase") - page.add( - ft.Text("Compare Material card visual variants."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(variant) for variant in ft.CardVariant], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/card_variant/showcase/main.py b/sdk/python/examples/controls/types/card_variant/showcase/main.py new file mode 100644 index 0000000000..9f167c1328 --- /dev/null +++ b/sdk/python/examples/controls/types/card_variant/showcase/main.py @@ -0,0 +1,63 @@ +import flet as ft + + +def showcase_card(variant: ft.CardVariant) -> ft.Container: + card = ft.Card( + variant=variant, + content=ft.Container( + width=260, + padding=12, + content=ft.Column( + spacing=6, + controls=[ + ft.Text("Quarterly Report", weight=ft.FontWeight.BOLD), + ft.Text( + "Revenue increased by 18% compared to last quarter.", + size=12, + ), + ], + ), + ), + ) + + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(variant.name, weight=ft.FontWeight.BOLD), + card, + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="CardVariant Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare Material card visual variants."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(variant) for variant in ft.CardVariant], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/card_variant/showcase/pyproject.toml b/sdk/python/examples/controls/types/card_variant/showcase/pyproject.toml new file mode 100644 index 0000000000..de76ead71d --- /dev/null +++ b/sdk/python/examples/controls/types/card_variant/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-card-variant-showcase" +version = "1.0.0" +description = "Compares Card Variant values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["card variant", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Card Variant showcase" +controls = ["SafeArea", "Column", "Container", "Card", "Text", "Page", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/clip_behavior/showcase.py b/sdk/python/examples/controls/types/clip_behavior/showcase.py deleted file mode 100644 index 19d7373174..0000000000 --- a/sdk/python/examples/controls/types/clip_behavior/showcase.py +++ /dev/null @@ -1,79 +0,0 @@ -import flet as ft - - -def clip_preview(clip_behavior: ft.ClipBehavior) -> ft.Container: - return ft.Container( - width=240, - height=130, - border=ft.Border.all(2, ft.Colors.OUTLINE), - border_radius=10, - bgcolor=ft.Colors.SURFACE, - content=ft.Stack( - clip_behavior=clip_behavior, - controls=[ - ft.Container( - width=160, - height=70, - left=-25, - top=30, - bgcolor=ft.Colors.PRIMARY_CONTAINER, - border_radius=16, - ), - ft.Container( - width=90, - height=90, - left=150, - top=-20, - bgcolor=ft.Colors.TERTIARY_CONTAINER, - border_radius=45, - ), - ft.Container( - width=90, - height=90, - left=70, - top=22, - bgcolor=ft.Colors.SECONDARY, - border_radius=45, - alignment=ft.Alignment.CENTER, - content=ft.Icon(ft.Icons.CROP, color=ft.Colors.WHITE), - ), - ], - ), - ) - - -def showcase_card(clip_behavior: ft.ClipBehavior) -> ft.Container: - return ft.Container( - width=280, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(clip_behavior.name, weight=ft.FontWeight.BOLD), - clip_preview(clip_behavior), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="ClipBehavior Showcase") - page.add( - ft.Text("Compare how overflow is clipped for each ClipBehavior value."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(cb) for cb in ft.ClipBehavior], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/clip_behavior/showcase/main.py b/sdk/python/examples/controls/types/clip_behavior/showcase/main.py new file mode 100644 index 0000000000..6494919a06 --- /dev/null +++ b/sdk/python/examples/controls/types/clip_behavior/showcase/main.py @@ -0,0 +1,88 @@ +import flet as ft + + +def clip_preview(clip_behavior: ft.ClipBehavior) -> ft.Container: + return ft.Container( + width=240, + height=130, + border=ft.Border.all(2, ft.Colors.OUTLINE), + border_radius=10, + bgcolor=ft.Colors.SURFACE, + content=ft.Stack( + clip_behavior=clip_behavior, + controls=[ + ft.Container( + width=160, + height=70, + left=-25, + top=30, + bgcolor=ft.Colors.PRIMARY_CONTAINER, + border_radius=16, + ), + ft.Container( + width=90, + height=90, + left=150, + top=-20, + bgcolor=ft.Colors.TERTIARY_CONTAINER, + border_radius=45, + ), + ft.Container( + width=90, + height=90, + left=70, + top=22, + bgcolor=ft.Colors.SECONDARY, + border_radius=45, + alignment=ft.Alignment.CENTER, + content=ft.Icon(ft.Icons.CROP, color=ft.Colors.WHITE), + ), + ], + ), + ) + + +def showcase_card(clip_behavior: ft.ClipBehavior) -> ft.Container: + return ft.Container( + width=280, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(clip_behavior.name, weight=ft.FontWeight.BOLD), + clip_preview(clip_behavior), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="ClipBehavior Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare how overflow is clipped for each ClipBehavior value." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(cb) for cb in ft.ClipBehavior], + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/clip_behavior/showcase/pyproject.toml b/sdk/python/examples/controls/types/clip_behavior/showcase/pyproject.toml new file mode 100644 index 0000000000..97fcf2a8f1 --- /dev/null +++ b/sdk/python/examples/controls/types/clip_behavior/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-clip-behavior-showcase" +version = "1.0.0" +description = "Compares Clip Behavior values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["clip behavior", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Clip Behavior showcase" +controls = ["SafeArea", "Column", "Container", "Stack", "Icon", "Text", "Page", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/context/disable_auto_update.py b/sdk/python/examples/controls/types/context/disable_auto_update.py deleted file mode 100644 index 171892877e..0000000000 --- a/sdk/python/examples/controls/types/context/disable_auto_update.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def button_click(): - ft.context.disable_auto_update() - b.content = "Button clicked!" - # update just the button - b.update() - - page.controls.append(ft.Text("This won't appear")) - # no page.update() will be called here - - page.controls.append(b := ft.Button("Action!", on_click=button_click)) - # page.update() - auto-update is enabled by default - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/context/disable_auto_update/main.py b/sdk/python/examples/controls/types/context/disable_auto_update/main.py new file mode 100644 index 0000000000..38e4b09ad2 --- /dev/null +++ b/sdk/python/examples/controls/types/context/disable_auto_update/main.py @@ -0,0 +1,25 @@ +import flet as ft + + +def main(page: ft.Page): + def button_click(): + ft.context.disable_auto_update() + b.content = "Button clicked!" + # update just the button + b.update() + + page.controls.append(ft.Text("This won't appear")) + # no page.update() will be called here + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[b := ft.Button("Action!", on_click=button_click)] + ) + ) + ) + # page.update() - auto-update is enabled by default + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/context/disable_auto_update/pyproject.toml b/sdk/python/examples/controls/types/context/disable_auto_update/pyproject.toml new file mode 100644 index 0000000000..81793bc37a --- /dev/null +++ b/sdk/python/examples/controls/types/context/disable_auto_update/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-context-disable-auto-update" +version = "1.0.0" +description = "Shows how disabling automatic updates requires explicit control refreshes." +requires-python = ">=3.10" +keywords = ["context", "disable auto update"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types"] + +[tool.flet.metadata] +title = "Disable auto update" +controls = ["SafeArea", "Column", "Page", "Text", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["manual updates", "context access"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/context/get_page.py b/sdk/python/examples/controls/types/context/get_page.py deleted file mode 100644 index 39d5d8331f..0000000000 --- a/sdk/python/examples/controls/types/context/get_page.py +++ /dev/null @@ -1,11 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def button_click(e): - print("Page width:", ft.context.page.width) - - page.add(ft.Button("Get page width", on_click=button_click)) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/context/get_page/main.py b/sdk/python/examples/controls/types/context/get_page/main.py new file mode 100644 index 0000000000..092e600c82 --- /dev/null +++ b/sdk/python/examples/controls/types/context/get_page/main.py @@ -0,0 +1,18 @@ +import flet as ft + + +def main(page: ft.Page): + def button_click(e): + print("Page width:", ft.context.page.width) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ft.Button("Get page width", on_click=button_click)] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/context/get_page/pyproject.toml b/sdk/python/examples/controls/types/context/get_page/pyproject.toml new file mode 100644 index 0000000000..2e4fb7a761 --- /dev/null +++ b/sdk/python/examples/controls/types/context/get_page/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-context-get-page" +version = "1.0.0" +description = "Reads the current page from ft.context inside an event handler." +requires-python = ">=3.10" +keywords = ["context", "get page"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types"] + +[tool.flet.metadata] +title = "Get page from context" +controls = ["SafeArea", "Column", "Page", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["context access", "event handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/context_menu_trigger/showcase.py b/sdk/python/examples/controls/types/context_menu_trigger/showcase.py deleted file mode 100644 index c60db87f7a..0000000000 --- a/sdk/python/examples/controls/types/context_menu_trigger/showcase.py +++ /dev/null @@ -1,79 +0,0 @@ -import flet as ft - - -def showcase_card(trigger: ft.ContextMenuTrigger) -> ft.Container: - def on_select(e: ft.ContextMenuSelectEvent): - item = e.item.content if e.item else f"item #{e.item_index}" - status.value = f"Selected: {item}" - status.update() - - def on_dismiss(_): - if status.value == "Open the menu in the area below.": - status.value = "Menu dismissed." - status.update() - - menu = ft.ContextMenu( - primary_trigger=trigger, - primary_items=[ - ft.PopupMenuItem(icon=ft.Icons.EDIT, content="Edit"), - ft.PopupMenuItem(icon=ft.Icons.CONTENT_COPY, content="Duplicate"), - ft.PopupMenuItem(icon=ft.Icons.DELETE, content="Delete"), - ], - on_select=on_select, - on_dismiss=on_dismiss, - content=ft.Container( - width=280, - height=120, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - alignment=ft.Alignment.CENTER, - content=ft.Text( - text_align=ft.TextAlign.CENTER, - value=( - "Press and hold inside the area." - if trigger == ft.ContextMenuTrigger.LONG_PRESS - else "Press mouse button down inside the area." - ), - ), - ), - ) - - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(trigger.name, weight=ft.FontWeight.BOLD), - menu, - status := ft.Text("Open the menu in the area below.", size=11), - ], - ), - ) - - -async def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - if page.web: - await ft.BrowserContextMenu().disable() - - page.appbar = ft.AppBar(title="ContextMenuTrigger Showcase") - page.add( - ft.Text("Compare context-menu open behavior for primary trigger modes."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(trigger) for trigger in ft.ContextMenuTrigger], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/context_menu_trigger/showcase/main.py b/sdk/python/examples/controls/types/context_menu_trigger/showcase/main.py new file mode 100644 index 0000000000..39203d11b4 --- /dev/null +++ b/sdk/python/examples/controls/types/context_menu_trigger/showcase/main.py @@ -0,0 +1,90 @@ +import flet as ft + + +def showcase_card(trigger: ft.ContextMenuTrigger) -> ft.Container: + def on_select(e: ft.ContextMenuSelectEvent): + item = e.item.content if e.item else f"item #{e.item_index}" + status.value = f"Selected: {item}" + status.update() + + def on_dismiss(_): + if status.value == "Open the menu in the area below.": + status.value = "Menu dismissed." + status.update() + + menu = ft.ContextMenu( + primary_trigger=trigger, + primary_items=[ + ft.PopupMenuItem(icon=ft.Icons.EDIT, content="Edit"), + ft.PopupMenuItem(icon=ft.Icons.CONTENT_COPY, content="Duplicate"), + ft.PopupMenuItem(icon=ft.Icons.DELETE, content="Delete"), + ], + on_select=on_select, + on_dismiss=on_dismiss, + content=ft.Container( + width=280, + height=120, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + alignment=ft.Alignment.CENTER, + content=ft.Text( + text_align=ft.TextAlign.CENTER, + value=( + "Press and hold inside the area." + if trigger == ft.ContextMenuTrigger.LONG_PRESS + else "Press mouse button down inside the area." + ), + ), + ), + ) + + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(trigger.name, weight=ft.FontWeight.BOLD), + menu, + status := ft.Text("Open the menu in the area below.", size=11), + ], + ), + ) + + +async def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + if page.web: + await ft.BrowserContextMenu().disable() + + page.appbar = ft.AppBar(title="ContextMenuTrigger Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare context-menu open behavior for primary trigger modes." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(trigger) for trigger in ft.ContextMenuTrigger + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/context_menu_trigger/showcase/pyproject.toml b/sdk/python/examples/controls/types/context_menu_trigger/showcase/pyproject.toml new file mode 100644 index 0000000000..e45759a38c --- /dev/null +++ b/sdk/python/examples/controls/types/context_menu_trigger/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-context-menu-trigger-showcase" +version = "1.0.0" +description = "Compares Context Menu Trigger values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["context menu trigger", "showcase", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Context Menu Trigger showcase" +controls = ["SafeArea", "Column", "Container", "ContextMenu", "PopupMenuItem", "Text", "BrowserContextMenu", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/cross_axis_alignment/showcase.py b/sdk/python/examples/controls/types/cross_axis_alignment/showcase.py deleted file mode 100644 index 5126081a8d..0000000000 --- a/sdk/python/examples/controls/types/cross_axis_alignment/showcase.py +++ /dev/null @@ -1,90 +0,0 @@ -import flet as ft - - -def showcase_card(alignment: ft.CrossAxisAlignment) -> ft.Container: - if alignment == ft.CrossAxisAlignment.STRETCH: - preview = ft.Row( - alignment=ft.MainAxisAlignment.SPACE_AROUND, - vertical_alignment=alignment, - controls=[ - ft.Container(width=40, bgcolor=ft.Colors.PRIMARY_CONTAINER), - ft.Container(width=40, bgcolor=ft.Colors.TERTIARY_CONTAINER), - ft.Container(width=40, bgcolor=ft.Colors.SECONDARY_CONTAINER), - ], - ) - else: - effective_alignment = ( - ft.CrossAxisAlignment.CENTER - if alignment == ft.CrossAxisAlignment.BASELINE - else alignment - ) - preview = ft.Row( - alignment=ft.MainAxisAlignment.SPACE_AROUND, - vertical_alignment=effective_alignment, - controls=[ - ft.Container( - width=40, - height=24, - bgcolor=ft.Colors.PRIMARY_CONTAINER, - ), - ft.Container( - width=40, - height=46, - bgcolor=ft.Colors.TERTIARY_CONTAINER, - ), - ft.Container( - width=40, - height=34, - bgcolor=ft.Colors.SECONDARY_CONTAINER, - ), - ], - ) - - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(alignment.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=250, - height=120, - padding=8, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=preview, - ), - ft.Text( - "Baseline uses center fallback in this showcase.", - size=11, - visible=alignment == ft.CrossAxisAlignment.BASELINE, - color=ft.Colors.ON_SURFACE_VARIANT, - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="CrossAxisAlignment Showcase") - page.add( - ft.Text("Compare vertical alignment behavior inside the same row height."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(alignment) for alignment in ft.CrossAxisAlignment], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/cross_axis_alignment/showcase/main.py b/sdk/python/examples/controls/types/cross_axis_alignment/showcase/main.py new file mode 100644 index 0000000000..34986862a2 --- /dev/null +++ b/sdk/python/examples/controls/types/cross_axis_alignment/showcase/main.py @@ -0,0 +1,103 @@ +import flet as ft + + +def showcase_card(alignment: ft.CrossAxisAlignment) -> ft.Container: + if alignment == ft.CrossAxisAlignment.STRETCH: + preview = ft.Row( + alignment=ft.MainAxisAlignment.SPACE_AROUND, + vertical_alignment=alignment, + controls=[ + ft.Container(width=40, bgcolor=ft.Colors.PRIMARY_CONTAINER), + ft.Container(width=40, bgcolor=ft.Colors.TERTIARY_CONTAINER), + ft.Container(width=40, bgcolor=ft.Colors.SECONDARY_CONTAINER), + ], + ) + else: + effective_alignment = ( + ft.CrossAxisAlignment.CENTER + if alignment == ft.CrossAxisAlignment.BASELINE + else alignment + ) + preview = ft.Row( + alignment=ft.MainAxisAlignment.SPACE_AROUND, + vertical_alignment=effective_alignment, + controls=[ + ft.Container( + width=40, + height=24, + bgcolor=ft.Colors.PRIMARY_CONTAINER, + ), + ft.Container( + width=40, + height=46, + bgcolor=ft.Colors.TERTIARY_CONTAINER, + ), + ft.Container( + width=40, + height=34, + bgcolor=ft.Colors.SECONDARY_CONTAINER, + ), + ], + ) + + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(alignment.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=250, + height=120, + padding=8, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=preview, + ), + ft.Text( + "Baseline uses center fallback in this showcase.", + size=11, + visible=alignment == ft.CrossAxisAlignment.BASELINE, + color=ft.Colors.ON_SURFACE_VARIANT, + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="CrossAxisAlignment Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare vertical alignment behavior inside the same " + "row height." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(alignment) + for alignment in ft.CrossAxisAlignment + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/cross_axis_alignment/showcase/pyproject.toml b/sdk/python/examples/controls/types/cross_axis_alignment/showcase/pyproject.toml new file mode 100644 index 0000000000..7c67354a36 --- /dev/null +++ b/sdk/python/examples/controls/types/cross_axis_alignment/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-cross-axis-alignment-showcase" +version = "1.0.0" +description = "Compares Cross Axis Alignment values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["cross axis alignment", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Cross Axis Alignment showcase" +controls = ["SafeArea", "Column", "Container", "Row", "Text", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/cupertino_button_size/showcase.py b/sdk/python/examples/controls/types/cupertino_button_size/showcase.py deleted file mode 100644 index 20b05de1c5..0000000000 --- a/sdk/python/examples/controls/types/cupertino_button_size/showcase.py +++ /dev/null @@ -1,45 +0,0 @@ -import flet as ft - - -def showcase_card(size: ft.CupertinoButtonSize) -> ft.Container: - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=10, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(size.name, weight=ft.FontWeight.BOLD), - ft.CupertinoButton( - content="Continue", - icon=ft.CupertinoIcons.RIGHT_CHEVRON, - size=size, - bgcolor=ft.Colors.BLUE_600, - color=ft.Colors.WHITE, - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="CupertinoButtonSize Showcase") - page.add( - ft.Text("Compare iOS button size presets."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(size) for size in ft.CupertinoButtonSize], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/cupertino_button_size/showcase/main.py b/sdk/python/examples/controls/types/cupertino_button_size/showcase/main.py new file mode 100644 index 0000000000..8313c3e541 --- /dev/null +++ b/sdk/python/examples/controls/types/cupertino_button_size/showcase/main.py @@ -0,0 +1,54 @@ +import flet as ft + + +def showcase_card(size: ft.CupertinoButtonSize) -> ft.Container: + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=10, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text(size.name, weight=ft.FontWeight.BOLD), + ft.CupertinoButton( + content="Continue", + icon=ft.CupertinoIcons.RIGHT_CHEVRON, + size=size, + bgcolor=ft.Colors.BLUE_600, + color=ft.Colors.WHITE, + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="CupertinoButtonSize Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare iOS button size presets."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(size) for size in ft.CupertinoButtonSize + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/cupertino_button_size/showcase/pyproject.toml b/sdk/python/examples/controls/types/cupertino_button_size/showcase/pyproject.toml new file mode 100644 index 0000000000..a188a4b0d0 --- /dev/null +++ b/sdk/python/examples/controls/types/cupertino_button_size/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-cupertino-button-size-showcase" +version = "1.0.0" +description = "Compares Cupertino Button Size values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["cupertino button size", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Cupertino Button Size showcase" +controls = ["SafeArea", "Column", "Container", "Text", "CupertinoButton", "CupertinoIcons", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/cupertino_date_picker_date_order/showcase.py b/sdk/python/examples/controls/types/cupertino_date_picker_date_order/showcase.py deleted file mode 100644 index ce0f1c575d..0000000000 --- a/sdk/python/examples/controls/types/cupertino_date_picker_date_order/showcase.py +++ /dev/null @@ -1,60 +0,0 @@ -from datetime import datetime - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def showcase_card(date_order: ft.CupertinoDatePickerDateOrder) -> ft.Container: - def open_picker(_): - picker = ft.CupertinoDatePicker( - date_picker_mode=ft.CupertinoDatePickerMode.DATE, - date_order=date_order, - value=datetime(2024, 3, 12, 10, 0), - ) - page.show_dialog( - ft.CupertinoBottomSheet( - content=picker, - height=216, - padding=ft.Padding.only(top=6), - ) - ) - - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(date_order.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Open picker", - icon=ft.CupertinoIcons.CALENDAR, - on_click=open_picker, - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="CupertinoDatePickerDateOrder Showcase") - page.add( - ft.Text("Open each variant in CupertinoBottomSheet."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(date_order) - for date_order in ft.CupertinoDatePickerDateOrder - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/cupertino_date_picker_date_order/showcase/main.py b/sdk/python/examples/controls/types/cupertino_date_picker_date_order/showcase/main.py new file mode 100644 index 0000000000..96b6619de6 --- /dev/null +++ b/sdk/python/examples/controls/types/cupertino_date_picker_date_order/showcase/main.py @@ -0,0 +1,67 @@ +from datetime import datetime + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def showcase_card(date_order: ft.CupertinoDatePickerDateOrder) -> ft.Container: + def open_picker(_): + picker = ft.CupertinoDatePicker( + date_picker_mode=ft.CupertinoDatePickerMode.DATE, + date_order=date_order, + value=datetime(2024, 3, 12, 10, 0), + ) + page.show_dialog( + ft.CupertinoBottomSheet( + content=picker, + height=216, + padding=ft.Padding.only(top=6), + ) + ) + + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(date_order.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Open picker", + icon=ft.CupertinoIcons.CALENDAR, + on_click=open_picker, + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="CupertinoDatePickerDateOrder Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Open each variant in CupertinoBottomSheet."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(date_order) + for date_order in ft.CupertinoDatePickerDateOrder + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/cupertino_date_picker_date_order/showcase/pyproject.toml b/sdk/python/examples/controls/types/cupertino_date_picker_date_order/showcase/pyproject.toml new file mode 100644 index 0000000000..6ddc495dee --- /dev/null +++ b/sdk/python/examples/controls/types/cupertino_date_picker_date_order/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-cupertino-date-picker-date-order-showcase" +version = "1.0.0" +description = "Compares Cupertino Date Picker Date Order values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["cupertino date picker date order", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Cupertino Date Picker Date Order showcase" +controls = ["SafeArea", "Column", "Container", "CupertinoDatePicker", "CupertinoBottomSheet", "Text", "Button", "CupertinoIcons"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/cupertino_date_picker_mode/showcase.py b/sdk/python/examples/controls/types/cupertino_date_picker_mode/showcase.py deleted file mode 100644 index abb3824722..0000000000 --- a/sdk/python/examples/controls/types/cupertino_date_picker_mode/showcase.py +++ /dev/null @@ -1,57 +0,0 @@ -from datetime import datetime - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def showcase_card(mode: ft.CupertinoDatePickerMode) -> ft.Container: - def open_picker(_): - picker = ft.CupertinoDatePicker( - date_picker_mode=mode, - value=datetime(2024, 7, 13, 16, 14), - use_24h_format=True, - ) - page.show_dialog( - ft.CupertinoBottomSheet( - content=picker, - height=216, - padding=ft.Padding.only(top=6), - ) - ) - - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(mode.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Open picker", - icon=ft.CupertinoIcons.CALENDAR, - on_click=open_picker, - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="CupertinoDatePickerMode Showcase") - page.add( - ft.Text("Open each mode in CupertinoBottomSheet to compare behavior."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(mode) for mode in ft.CupertinoDatePickerMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/cupertino_date_picker_mode/showcase/main.py b/sdk/python/examples/controls/types/cupertino_date_picker_mode/showcase/main.py new file mode 100644 index 0000000000..9df0fffb59 --- /dev/null +++ b/sdk/python/examples/controls/types/cupertino_date_picker_mode/showcase/main.py @@ -0,0 +1,68 @@ +from datetime import datetime + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def showcase_card(mode: ft.CupertinoDatePickerMode) -> ft.Container: + def open_picker(_): + picker = ft.CupertinoDatePicker( + date_picker_mode=mode, + value=datetime(2024, 7, 13, 16, 14), + use_24h_format=True, + ) + page.show_dialog( + ft.CupertinoBottomSheet( + content=picker, + height=216, + padding=ft.Padding.only(top=6), + ) + ) + + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(mode.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Open picker", + icon=ft.CupertinoIcons.CALENDAR, + on_click=open_picker, + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="CupertinoDatePickerMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Open each mode in CupertinoBottomSheet to compare behavior." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(mode) for mode in ft.CupertinoDatePickerMode + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/cupertino_date_picker_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/cupertino_date_picker_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..5a7430ea0f --- /dev/null +++ b/sdk/python/examples/controls/types/cupertino_date_picker_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-cupertino-date-picker-mode-showcase" +version = "1.0.0" +description = "Compares Cupertino Date Picker Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["cupertino date picker mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Cupertino Date Picker Mode showcase" +controls = ["SafeArea", "Column", "Container", "CupertinoDatePicker", "CupertinoBottomSheet", "Text", "Button", "CupertinoIcons"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/cupertino_timer_picker_mode/showcase.py b/sdk/python/examples/controls/types/cupertino_timer_picker_mode/showcase.py deleted file mode 100644 index 70d9d71519..0000000000 --- a/sdk/python/examples/controls/types/cupertino_timer_picker_mode/showcase.py +++ /dev/null @@ -1,41 +0,0 @@ -import flet as ft - - -def showcase_card(mode: ft.CupertinoTimerPickerMode) -> ft.Container: - return ft.Container( - width=340, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(mode.name, weight=ft.FontWeight.BOLD), - ft.CupertinoTimerPicker( - mode=mode, - value=ft.Duration(seconds=754), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="CupertinoTimerPickerMode Showcase") - page.add( - ft.Text("Compare timer picker layouts."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(mode) for mode in ft.CupertinoTimerPickerMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/cupertino_timer_picker_mode/showcase/main.py b/sdk/python/examples/controls/types/cupertino_timer_picker_mode/showcase/main.py new file mode 100644 index 0000000000..570a06b79b --- /dev/null +++ b/sdk/python/examples/controls/types/cupertino_timer_picker_mode/showcase/main.py @@ -0,0 +1,50 @@ +import flet as ft + + +def showcase_card(mode: ft.CupertinoTimerPickerMode) -> ft.Container: + return ft.Container( + width=340, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(mode.name, weight=ft.FontWeight.BOLD), + ft.CupertinoTimerPicker( + mode=mode, + value=ft.Duration(seconds=754), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="CupertinoTimerPickerMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare timer picker layouts."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(mode) for mode in ft.CupertinoTimerPickerMode + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/cupertino_timer_picker_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/cupertino_timer_picker_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..f0a01dd84d --- /dev/null +++ b/sdk/python/examples/controls/types/cupertino_timer_picker_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-cupertino-timer-picker-mode-showcase" +version = "1.0.0" +description = "Compares Cupertino Timer Picker Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["cupertino timer picker mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Cupertino Timer Picker Mode showcase" +controls = ["SafeArea", "Column", "Container", "Text", "CupertinoTimerPicker", "Duration", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/date_picker_entry_mode/showcase.py b/sdk/python/examples/controls/types/date_picker_entry_mode/showcase.py deleted file mode 100644 index e29813d6c3..0000000000 --- a/sdk/python/examples/controls/types/date_picker_entry_mode/showcase.py +++ /dev/null @@ -1,54 +0,0 @@ -import datetime - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - today = datetime.datetime.now() - picker = ft.DatePicker( - first_date=datetime.datetime(year=today.year - 1, month=1, day=1), - last_date=datetime.datetime(year=today.year + 1, month=12, day=31), - ) - - def open_picker(entry_mode: ft.DatePickerEntryMode): - picker.entry_mode = entry_mode - picker.date_picker_mode = ft.DatePickerMode.DAY - page.show_dialog(picker) - - def showcase_card(entry_mode: ft.DatePickerEntryMode) -> ft.Container: - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(entry_mode.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Open DatePicker", - icon=ft.Icons.CALENDAR_MONTH, - on_click=lambda _, m=entry_mode: open_picker(m), - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="DatePickerEntryMode Showcase") - page.add( - ft.Text("Open the picker to compare calendar and input entry modes."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(m) for m in ft.DatePickerEntryMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/date_picker_entry_mode/showcase/main.py b/sdk/python/examples/controls/types/date_picker_entry_mode/showcase/main.py new file mode 100644 index 0000000000..96213f1327 --- /dev/null +++ b/sdk/python/examples/controls/types/date_picker_entry_mode/showcase/main.py @@ -0,0 +1,63 @@ +import datetime + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + today = datetime.datetime.now() + picker = ft.DatePicker( + first_date=datetime.datetime(year=today.year - 1, month=1, day=1), + last_date=datetime.datetime(year=today.year + 1, month=12, day=31), + ) + + def open_picker(entry_mode: ft.DatePickerEntryMode): + picker.entry_mode = entry_mode + picker.date_picker_mode = ft.DatePickerMode.DAY + page.show_dialog(picker) + + def showcase_card(entry_mode: ft.DatePickerEntryMode) -> ft.Container: + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(entry_mode.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Open DatePicker", + icon=ft.Icons.CALENDAR_MONTH, + on_click=lambda _, m=entry_mode: open_picker(m), + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="DatePickerEntryMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Open the picker to compare calendar and input entry modes." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(m) for m in ft.DatePickerEntryMode], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/date_picker_entry_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/date_picker_entry_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..0b5e787b43 --- /dev/null +++ b/sdk/python/examples/controls/types/date_picker_entry_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-date-picker-entry-mode-showcase" +version = "1.0.0" +description = "Compares Date Picker Entry Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["date picker entry mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Date Picker Entry Mode showcase" +controls = ["SafeArea", "Column", "DatePicker", "Container", "Text", "Button", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/date_picker_mode/showcase.py b/sdk/python/examples/controls/types/date_picker_mode/showcase.py deleted file mode 100644 index 108b1ac274..0000000000 --- a/sdk/python/examples/controls/types/date_picker_mode/showcase.py +++ /dev/null @@ -1,54 +0,0 @@ -import datetime - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - today = datetime.datetime.now() - picker = ft.DatePicker( - first_date=datetime.datetime(year=today.year - 1, month=1, day=1), - last_date=datetime.datetime(year=today.year + 1, month=12, day=31), - entry_mode=ft.DatePickerEntryMode.CALENDAR, - ) - - def open_picker(date_picker_mode: ft.DatePickerMode): - picker.date_picker_mode = date_picker_mode - page.show_dialog(picker) - - def showcase_card(date_picker_mode: ft.DatePickerMode) -> ft.Container: - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(date_picker_mode.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Open DatePicker", - icon=ft.Icons.CALENDAR_MONTH, - on_click=lambda _, m=date_picker_mode: open_picker(m), - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="DatePickerMode Showcase") - page.add( - ft.Text("Open the picker to compare initial day vs year display mode."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(m) for m in ft.DatePickerMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/date_picker_mode/showcase/main.py b/sdk/python/examples/controls/types/date_picker_mode/showcase/main.py new file mode 100644 index 0000000000..48a991503b --- /dev/null +++ b/sdk/python/examples/controls/types/date_picker_mode/showcase/main.py @@ -0,0 +1,63 @@ +import datetime + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + today = datetime.datetime.now() + picker = ft.DatePicker( + first_date=datetime.datetime(year=today.year - 1, month=1, day=1), + last_date=datetime.datetime(year=today.year + 1, month=12, day=31), + entry_mode=ft.DatePickerEntryMode.CALENDAR, + ) + + def open_picker(date_picker_mode: ft.DatePickerMode): + picker.date_picker_mode = date_picker_mode + page.show_dialog(picker) + + def showcase_card(date_picker_mode: ft.DatePickerMode) -> ft.Container: + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(date_picker_mode.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Open DatePicker", + icon=ft.Icons.CALENDAR_MONTH, + on_click=lambda _, m=date_picker_mode: open_picker(m), + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="DatePickerMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Open the picker to compare initial day vs year display mode." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(m) for m in ft.DatePickerMode], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/date_picker_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/date_picker_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..05b4f2f387 --- /dev/null +++ b/sdk/python/examples/controls/types/date_picker_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-date-picker-mode-showcase" +version = "1.0.0" +description = "Compares Date Picker Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["date picker mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Date Picker Mode showcase" +controls = ["SafeArea", "Column", "DatePicker", "Container", "Text", "Button", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/dismiss_direction/showcase.py b/sdk/python/examples/controls/types/dismiss_direction/showcase.py deleted file mode 100644 index 8d31d62adb..0000000000 --- a/sdk/python/examples/controls/types/dismiss_direction/showcase.py +++ /dev/null @@ -1,89 +0,0 @@ -import flet as ft - - -def showcase_card(direction: ft.DismissDirection) -> ft.Container: - status = ft.Text("Swipe the tile", size=12, color=ft.Colors.ON_SURFACE_VARIANT) - - def create_item() -> ft.Dismissible: - return ft.Dismissible( - dismiss_direction=direction, - on_dismiss=lambda _: on_dismiss(), - background=ft.Container( - bgcolor=ft.Colors.GREEN_200, - alignment=ft.Alignment.CENTER_LEFT, - padding=10, - content=ft.Icon(ft.Icons.CHECK, color=ft.Colors.GREEN_900), - ), - secondary_background=ft.Container( - bgcolor=ft.Colors.RED_200, - alignment=ft.Alignment.CENTER_RIGHT, - padding=10, - content=ft.Icon(ft.Icons.DELETE, color=ft.Colors.RED_900), - ), - content=ft.Container( - height=52, - border_radius=8, - bgcolor=ft.Colors.SURFACE, - border=ft.Border.all(1, ft.Colors.OUTLINE), - alignment=ft.Alignment.CENTER, - content=ft.Text("Dismiss me"), - ), - ) - - def on_dismiss(): - slot.content = ft.Container( - height=52, - border_radius=8, - bgcolor=ft.Colors.SURFACE, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - alignment=ft.Alignment.CENTER, - content=ft.Text("Dismissed"), - ) - status.value = f"Dismissed via {direction.name}" - slot.update() - status.update() - - def reset_item(_): - slot.content = create_item() - status.value = "Swipe the tile" - slot.update() - status.update() - - slot = ft.Container(content=create_item()) - - return ft.Container( - width=360, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(direction.name, weight=ft.FontWeight.BOLD), - status, - slot, - ft.Button("Reset", on_click=reset_item), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="DismissDirection Showcase") - page.add( - ft.Text("Try swipe directions to see which ones are allowed."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(direction) for direction in ft.DismissDirection], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/dismiss_direction/showcase/main.py b/sdk/python/examples/controls/types/dismiss_direction/showcase/main.py new file mode 100644 index 0000000000..c04aed797c --- /dev/null +++ b/sdk/python/examples/controls/types/dismiss_direction/showcase/main.py @@ -0,0 +1,99 @@ +import flet as ft + + +def showcase_card(direction: ft.DismissDirection) -> ft.Container: + status = ft.Text("Swipe the tile", size=12, color=ft.Colors.ON_SURFACE_VARIANT) + + def create_item() -> ft.Dismissible: + return ft.Dismissible( + dismiss_direction=direction, + on_dismiss=lambda _: on_dismiss(), + background=ft.Container( + bgcolor=ft.Colors.GREEN_200, + alignment=ft.Alignment.CENTER_LEFT, + padding=10, + content=ft.Icon(ft.Icons.CHECK, color=ft.Colors.GREEN_900), + ), + secondary_background=ft.Container( + bgcolor=ft.Colors.RED_200, + alignment=ft.Alignment.CENTER_RIGHT, + padding=10, + content=ft.Icon(ft.Icons.DELETE, color=ft.Colors.RED_900), + ), + content=ft.Container( + height=52, + border_radius=8, + bgcolor=ft.Colors.SURFACE, + border=ft.Border.all(1, ft.Colors.OUTLINE), + alignment=ft.Alignment.CENTER, + content=ft.Text("Dismiss me"), + ), + ) + + def on_dismiss(): + slot.content = ft.Container( + height=52, + border_radius=8, + bgcolor=ft.Colors.SURFACE, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + alignment=ft.Alignment.CENTER, + content=ft.Text("Dismissed"), + ) + status.value = f"Dismissed via {direction.name}" + slot.update() + status.update() + + def reset_item(_): + slot.content = create_item() + status.value = "Swipe the tile" + slot.update() + status.update() + + slot = ft.Container(content=create_item()) + + return ft.Container( + width=360, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(direction.name, weight=ft.FontWeight.BOLD), + status, + slot, + ft.Button("Reset", on_click=reset_item), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="DismissDirection Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Try swipe directions to see which ones are allowed."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(direction) + for direction in ft.DismissDirection + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/dismiss_direction/showcase/pyproject.toml b/sdk/python/examples/controls/types/dismiss_direction/showcase/pyproject.toml new file mode 100644 index 0000000000..a80ee93ad7 --- /dev/null +++ b/sdk/python/examples/controls/types/dismiss_direction/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-dismiss-direction-showcase" +version = "1.0.0" +description = "Compares Dismiss Direction values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["dismiss direction", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Dismiss Direction showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Dismissible", "Icon", "Button", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/filter_quality/showcase.py b/sdk/python/examples/controls/types/filter_quality/showcase.py deleted file mode 100644 index 126d7e9492..0000000000 --- a/sdk/python/examples/controls/types/filter_quality/showcase.py +++ /dev/null @@ -1,54 +0,0 @@ -import flet as ft - -IMAGE_URL = "https://picsum.photos/id/1025/64/64" - - -def showcase_card(quality: ft.FilterQuality) -> ft.Container: - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(quality.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=240, - height=120, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - clip_behavior=ft.ClipBehavior.HARD_EDGE, - bgcolor=ft.Colors.SURFACE, - content=ft.Image( - src=IMAGE_URL, - width=240, - height=120, - fit=ft.BoxFit.FILL, - filter_quality=quality, - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="FilterQuality Showcase") - page.add( - ft.Text("Compare image sampling quality while scaling the same source image."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(quality) for quality in ft.FilterQuality], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/filter_quality/showcase/main.py b/sdk/python/examples/controls/types/filter_quality/showcase/main.py new file mode 100644 index 0000000000..71b37e7189 --- /dev/null +++ b/sdk/python/examples/controls/types/filter_quality/showcase/main.py @@ -0,0 +1,66 @@ +import flet as ft + +IMAGE_URL = "https://picsum.photos/id/1025/64/64" + + +def showcase_card(quality: ft.FilterQuality) -> ft.Container: + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(quality.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=240, + height=120, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + clip_behavior=ft.ClipBehavior.HARD_EDGE, + bgcolor=ft.Colors.SURFACE, + content=ft.Image( + src=IMAGE_URL, + width=240, + height=120, + fit=ft.BoxFit.FILL, + filter_quality=quality, + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="FilterQuality Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare image sampling quality while scaling the " + "same source image." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(quality) for quality in ft.FilterQuality + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/filter_quality/showcase/pyproject.toml b/sdk/python/examples/controls/types/filter_quality/showcase/pyproject.toml new file mode 100644 index 0000000000..3d0cef4a9e --- /dev/null +++ b/sdk/python/examples/controls/types/filter_quality/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-filter-quality-showcase" +version = "1.0.0" +description = "Compares Filter Quality values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["filter quality", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Filter Quality showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Image", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/floating_action_button_location/showcase.py b/sdk/python/examples/controls/types/floating_action_button_location/showcase.py deleted file mode 100644 index 9d3225e7dc..0000000000 --- a/sdk/python/examples/controls/types/floating_action_button_location/showcase.py +++ /dev/null @@ -1,65 +0,0 @@ -import flet as ft - - -def showcase_card(location: ft.FloatingActionButtonLocation) -> ft.Container: - mini = location.name.startswith("MINI_") - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(location.name, weight=ft.FontWeight.BOLD), - ft.Pagelet( - width=260, - height=200, - bgcolor=ft.Colors.SURFACE, - appbar=ft.AppBar( - title=ft.Text("AppBar", size=12), - center_title=True, - toolbar_height=38, - bgcolor=ft.Colors.PRIMARY_CONTAINER, - ), - content=ft.Container( - alignment=ft.Alignment.CENTER, - content=ft.Text("Body", size=12), - ), - bottom_appbar=ft.BottomAppBar( - height=42, - bgcolor=ft.Colors.SECONDARY_CONTAINER, - ), - floating_action_button=ft.FloatingActionButton( - icon=ft.Icons.ADD, - mini=mini, - bgcolor=ft.Colors.LIGHT_BLUE_400, - ), - floating_action_button_location=location, - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="FloatingActionButtonLocation Showcase") - page.add( - ft.Text("Compare FloatingActionButton placement presets."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(location) for location in ft.FloatingActionButtonLocation - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/floating_action_button_location/showcase/main.py b/sdk/python/examples/controls/types/floating_action_button_location/showcase/main.py new file mode 100644 index 0000000000..c762d3264c --- /dev/null +++ b/sdk/python/examples/controls/types/floating_action_button_location/showcase/main.py @@ -0,0 +1,73 @@ +import flet as ft + + +def showcase_card(location: ft.FloatingActionButtonLocation) -> ft.Container: + mini = location.name.startswith("MINI_") + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(location.name, weight=ft.FontWeight.BOLD), + ft.Pagelet( + width=260, + height=200, + bgcolor=ft.Colors.SURFACE, + appbar=ft.AppBar( + title=ft.Text("AppBar", size=12), + center_title=True, + toolbar_height=38, + bgcolor=ft.Colors.PRIMARY_CONTAINER, + ), + content=ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text("Body", size=12), + ), + bottom_appbar=ft.BottomAppBar( + height=42, + bgcolor=ft.Colors.SECONDARY_CONTAINER, + ), + floating_action_button=ft.FloatingActionButton( + icon=ft.Icons.ADD, + mini=mini, + bgcolor=ft.Colors.LIGHT_BLUE_400, + ), + floating_action_button_location=location, + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="FloatingActionButtonLocation Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare FloatingActionButton placement presets."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(location) + for location in ft.FloatingActionButtonLocation + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/floating_action_button_location/showcase/pyproject.toml b/sdk/python/examples/controls/types/floating_action_button_location/showcase/pyproject.toml new file mode 100644 index 0000000000..c972d830d5 --- /dev/null +++ b/sdk/python/examples/controls/types/floating_action_button_location/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-floating-action-button-location-showcase" +version = "1.0.0" +description = "Compares Floating Action Button Location values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["floating action button location", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Floating Action Button Location showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Pagelet", "AppBar", "BottomAppBar", "FloatingActionButton"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/font_weight/showcase.py b/sdk/python/examples/controls/types/font_weight/showcase.py deleted file mode 100644 index 1163ca05ba..0000000000 --- a/sdk/python/examples/controls/types/font_weight/showcase.py +++ /dev/null @@ -1,40 +0,0 @@ -import flet as ft - -SAMPLE_TEXT = "Sphinx of black quartz" - - -def showcase_card(weight: ft.FontWeight) -> ft.Container: - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(weight.name, size=12, color=ft.Colors.ON_SURFACE_VARIANT), - ft.Text(SAMPLE_TEXT, weight=weight, size=24), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="FontWeight Showcase") - page.add( - ft.Text("Compare text thickness across all FontWeight values."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(weight) for weight in ft.FontWeight], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/font_weight/showcase/main.py b/sdk/python/examples/controls/types/font_weight/showcase/main.py new file mode 100644 index 0000000000..daaf4a2203 --- /dev/null +++ b/sdk/python/examples/controls/types/font_weight/showcase/main.py @@ -0,0 +1,47 @@ +import flet as ft + +SAMPLE_TEXT = "Sphinx of black quartz" + + +def showcase_card(weight: ft.FontWeight) -> ft.Container: + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(weight.name, size=12, color=ft.Colors.ON_SURFACE_VARIANT), + ft.Text(SAMPLE_TEXT, weight=weight, size=24), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="FontWeight Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare text thickness across all FontWeight values."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(weight) for weight in ft.FontWeight], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/font_weight/showcase/pyproject.toml b/sdk/python/examples/controls/types/font_weight/showcase/pyproject.toml new file mode 100644 index 0000000000..bf4622eb0f --- /dev/null +++ b/sdk/python/examples/controls/types/font_weight/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-font-weight-showcase" +version = "1.0.0" +description = "Compares Font Weight values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["font weight", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Font Weight showcase" +controls = ["SafeArea", "Column", "Container", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/gradient/container.py b/sdk/python/examples/controls/types/gradient/container.py deleted file mode 100644 index deb3a4fc82..0000000000 --- a/sdk/python/examples/controls/types/gradient/container.py +++ /dev/null @@ -1,95 +0,0 @@ -import math - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - page.add( - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - scroll=ft.ScrollMode.AUTO, - controls=[ - ft.Container( - content=ft.Text("Linear gradient"), - padding=10, - alignment=ft.Alignment.CENTER, - width=200, - height=200, - border_radius=10, - gradient=ft.LinearGradient( - begin=ft.Alignment.TOP_LEFT, - end=ft.Alignment(0.8, 1), - tile_mode=ft.GradientTileMode.MIRROR, - rotation=math.pi / 3, - colors=[ - "0xff1f005c", - "0xff5b0060", - "0xff870160", - "0xffac255e", - "0xffca485c", - "0xffe16b5c", - "0xfff39060", - "0xffffb56b", - ], - ), - ), - ft.Container( - content=ft.Text("Linear gradient with stops"), - padding=10, - alignment=ft.Alignment.CENTER, - width=200, - height=200, - border_radius=10, - gradient=ft.LinearGradient( - begin=ft.Alignment.CENTER_LEFT, - end=ft.Alignment.CENTER_RIGHT, - tile_mode=ft.GradientTileMode.MIRROR, - stops=[0.1, 0.2, 1.0], - colors=[ft.Colors.RED, ft.Colors.GREEN, ft.Colors.BLUE], - ), - ), - ft.Container( - content=ft.Text("Radial gradient"), - padding=10, - alignment=ft.Alignment.CENTER, - width=200, - height=200, - border_radius=10, - gradient=ft.RadialGradient( - center=ft.Alignment(0.7, -0.6), - radius=0.2, - stops=[0.4, 1.0], - colors=["0xFFFFFF00", "0xFF0099FF"], - ), - ), - ft.Container( - content=ft.Text("Sweep gradient"), - padding=10, - alignment=ft.Alignment.CENTER, - width=200, - height=200, - border_radius=10, - gradient=ft.SweepGradient( - center=ft.Alignment.CENTER, - start_angle=0.0, - end_angle=math.pi * 2, - rotation=math.pi / 4, - stops=[0.0, 0.25, 0.5, 0.75, 1.0], - colors=[ - "0xFF4285F4", - "0xFF34A853", - "0xFFFBBC05", - "0xFFEA4335", - "0xFF4285F4", - ], - ), - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient/container/main.py b/sdk/python/examples/controls/types/gradient/container/main.py new file mode 100644 index 0000000000..d7414b5231 --- /dev/null +++ b/sdk/python/examples/controls/types/gradient/container/main.py @@ -0,0 +1,106 @@ +import math + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + scroll=ft.ScrollMode.AUTO, + controls=[ + ft.Container( + content=ft.Text("Linear gradient"), + padding=10, + alignment=ft.Alignment.CENTER, + width=200, + height=200, + border_radius=10, + gradient=ft.LinearGradient( + begin=ft.Alignment.TOP_LEFT, + end=ft.Alignment(0.8, 1), + tile_mode=ft.GradientTileMode.MIRROR, + rotation=math.pi / 3, + colors=[ + "0xff1f005c", + "0xff5b0060", + "0xff870160", + "0xffac255e", + "0xffca485c", + "0xffe16b5c", + "0xfff39060", + "0xffffb56b", + ], + ), + ), + ft.Container( + content=ft.Text("Linear gradient with stops"), + padding=10, + alignment=ft.Alignment.CENTER, + width=200, + height=200, + border_radius=10, + gradient=ft.LinearGradient( + begin=ft.Alignment.CENTER_LEFT, + end=ft.Alignment.CENTER_RIGHT, + tile_mode=ft.GradientTileMode.MIRROR, + stops=[0.1, 0.2, 1.0], + colors=[ + ft.Colors.RED, + ft.Colors.GREEN, + ft.Colors.BLUE, + ], + ), + ), + ft.Container( + content=ft.Text("Radial gradient"), + padding=10, + alignment=ft.Alignment.CENTER, + width=200, + height=200, + border_radius=10, + gradient=ft.RadialGradient( + center=ft.Alignment(0.7, -0.6), + radius=0.2, + stops=[0.4, 1.0], + colors=["0xFFFFFF00", "0xFF0099FF"], + ), + ), + ft.Container( + content=ft.Text("Sweep gradient"), + padding=10, + alignment=ft.Alignment.CENTER, + width=200, + height=200, + border_radius=10, + gradient=ft.SweepGradient( + center=ft.Alignment.CENTER, + start_angle=0.0, + end_angle=math.pi * 2, + rotation=math.pi / 4, + stops=[0.0, 0.25, 0.5, 0.75, 1.0], + colors=[ + "0xFF4285F4", + "0xFF34A853", + "0xFFFBBC05", + "0xFFEA4335", + "0xFF4285F4", + ], + ), + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient/container/pyproject.toml b/sdk/python/examples/controls/types/gradient/container/pyproject.toml new file mode 100644 index 0000000000..738ddbc64e --- /dev/null +++ b/sdk/python/examples/controls/types/gradient/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-gradient-container" +version = "1.0.0" +description = "Demonstrates how Gradient values change container presentation and layout." +requires-python = ">=3.10" +keywords = ["gradient", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Gradient" +controls = ["SafeArea", "Column", "Row", "Container", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["gradient preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/gradient/linear_gradient/container.py b/sdk/python/examples/controls/types/gradient/linear_gradient/container.py deleted file mode 100644 index e90a2a46e2..0000000000 --- a/sdk/python/examples/controls/types/gradient/linear_gradient/container.py +++ /dev/null @@ -1,33 +0,0 @@ -import math - -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Container( - alignment=ft.Alignment.CENTER, - width=150, - height=150, - border_radius=ft.BorderRadius.all(5), - gradient=ft.LinearGradient( - begin=ft.Alignment.TOP_LEFT, - end=ft.Alignment(0.8, 1), - tile_mode=ft.GradientTileMode.MIRROR, - rotation=math.pi / 3, - colors=[ - "0xff1f005c", - "0xff5b0060", - "0xff870160", - "0xffac255e", - "0xffca485c", - "0xffe16b5c", - "0xfff39060", - "0xffffb56b", - ], - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient/linear_gradient/container/main.py b/sdk/python/examples/controls/types/gradient/linear_gradient/container/main.py new file mode 100644 index 0000000000..faf1aaaf4e --- /dev/null +++ b/sdk/python/examples/controls/types/gradient/linear_gradient/container/main.py @@ -0,0 +1,40 @@ +import math + +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Container( + alignment=ft.Alignment.CENTER, + width=150, + height=150, + border_radius=ft.BorderRadius.all(5), + gradient=ft.LinearGradient( + begin=ft.Alignment.TOP_LEFT, + end=ft.Alignment(0.8, 1), + tile_mode=ft.GradientTileMode.MIRROR, + rotation=math.pi / 3, + colors=[ + "0xff1f005c", + "0xff5b0060", + "0xff870160", + "0xffac255e", + "0xffca485c", + "0xffe16b5c", + "0xfff39060", + "0xffffb56b", + ], + ), + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient/linear_gradient/container/pyproject.toml b/sdk/python/examples/controls/types/gradient/linear_gradient/container/pyproject.toml new file mode 100644 index 0000000000..8105824260 --- /dev/null +++ b/sdk/python/examples/controls/types/gradient/linear_gradient/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-gradient-linear-gradient-container" +version = "1.0.0" +description = "Demonstrates how Linear Gradient values change container presentation and layout." +requires-python = ">=3.10" +keywords = ["gradient", "linear gradient", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Linear Gradient" +controls = ["SafeArea", "Column", "Container", "BorderRadius"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["linear gradient preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/gradient/radial_gradient/container.py b/sdk/python/examples/controls/types/gradient/radial_gradient/container.py deleted file mode 100644 index 9412477196..0000000000 --- a/sdk/python/examples/controls/types/gradient/radial_gradient/container.py +++ /dev/null @@ -1,21 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Container( - alignment=ft.Alignment.CENTER, - width=150, - height=150, - border_radius=ft.BorderRadius.all(5), - gradient=ft.RadialGradient( - center=ft.Alignment(0.7, -0.6), - radius=0.2, - colors=["0xFFFFFF00", "0xFF0099FF"], - stops=[0.4, 1.0], - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient/radial_gradient/container/main.py b/sdk/python/examples/controls/types/gradient/radial_gradient/container/main.py new file mode 100644 index 0000000000..3777d923b9 --- /dev/null +++ b/sdk/python/examples/controls/types/gradient/radial_gradient/container/main.py @@ -0,0 +1,28 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Container( + alignment=ft.Alignment.CENTER, + width=150, + height=150, + border_radius=ft.BorderRadius.all(5), + gradient=ft.RadialGradient( + center=ft.Alignment(0.7, -0.6), + radius=0.2, + colors=["0xFFFFFF00", "0xFF0099FF"], + stops=[0.4, 1.0], + ), + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient/radial_gradient/container/pyproject.toml b/sdk/python/examples/controls/types/gradient/radial_gradient/container/pyproject.toml new file mode 100644 index 0000000000..b4b9938c0f --- /dev/null +++ b/sdk/python/examples/controls/types/gradient/radial_gradient/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-gradient-radial-gradient-container" +version = "1.0.0" +description = "Demonstrates how Radial Gradient values change container presentation and layout." +requires-python = ">=3.10" +keywords = ["gradient", "radial gradient", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Radial Gradient" +controls = ["SafeArea", "Column", "Container", "BorderRadius"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["radial gradient preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/gradient/sweep_gradient/container.py b/sdk/python/examples/controls/types/gradient/sweep_gradient/container.py deleted file mode 100644 index 6e85fb51f8..0000000000 --- a/sdk/python/examples/controls/types/gradient/sweep_gradient/container.py +++ /dev/null @@ -1,30 +0,0 @@ -import math - -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Container( - alignment=ft.Alignment.CENTER, - width=150, - height=150, - border_radius=ft.BorderRadius.all(5), - gradient=ft.SweepGradient( - center=ft.Alignment.CENTER, - start_angle=0.0, - end_angle=math.pi * 2, - stops=[0.0, 0.25, 0.5, 0.75, 1.0], - colors=[ - "0xFF4285F4", - "0xFF34A853", - "0xFFFBBC05", - "0xFFEA4335", - "0xFF4285F4", - ], - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient/sweep_gradient/container/main.py b/sdk/python/examples/controls/types/gradient/sweep_gradient/container/main.py new file mode 100644 index 0000000000..cdc0b70e86 --- /dev/null +++ b/sdk/python/examples/controls/types/gradient/sweep_gradient/container/main.py @@ -0,0 +1,37 @@ +import math + +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Container( + alignment=ft.Alignment.CENTER, + width=150, + height=150, + border_radius=ft.BorderRadius.all(5), + gradient=ft.SweepGradient( + center=ft.Alignment.CENTER, + start_angle=0.0, + end_angle=math.pi * 2, + stops=[0.0, 0.25, 0.5, 0.75, 1.0], + colors=[ + "0xFF4285F4", + "0xFF34A853", + "0xFFFBBC05", + "0xFFEA4335", + "0xFF4285F4", + ], + ), + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient/sweep_gradient/container/pyproject.toml b/sdk/python/examples/controls/types/gradient/sweep_gradient/container/pyproject.toml new file mode 100644 index 0000000000..2e72b54881 --- /dev/null +++ b/sdk/python/examples/controls/types/gradient/sweep_gradient/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-gradient-sweep-gradient-container" +version = "1.0.0" +description = "Demonstrates how Sweep Gradient values change container presentation and layout." +requires-python = ">=3.10" +keywords = ["gradient", "sweep gradient", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Sweep Gradient" +controls = ["SafeArea", "Column", "Container", "BorderRadius"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["sweep gradient preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/gradient_tile_mode/showcase.py b/sdk/python/examples/controls/types/gradient_tile_mode/showcase.py deleted file mode 100644 index ce980fe2a8..0000000000 --- a/sdk/python/examples/controls/types/gradient_tile_mode/showcase.py +++ /dev/null @@ -1,48 +0,0 @@ -import flet as ft - - -def showcase_card(mode: ft.GradientTileMode) -> ft.Container: - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(mode.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=240, - height=120, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - gradient=ft.RadialGradient( - radius=0.22, - colors=[ft.Colors.PRIMARY, ft.Colors.AMBER_400], - tile_mode=mode, - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="GradientTileMode Showcase") - page.add( - ft.Text("Compare how gradients behave outside their defined paint region."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(mode) for mode in ft.GradientTileMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient_tile_mode/showcase/main.py b/sdk/python/examples/controls/types/gradient_tile_mode/showcase/main.py new file mode 100644 index 0000000000..3ebfd0af4a --- /dev/null +++ b/sdk/python/examples/controls/types/gradient_tile_mode/showcase/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def showcase_card(mode: ft.GradientTileMode) -> ft.Container: + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(mode.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=240, + height=120, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + gradient=ft.RadialGradient( + radius=0.22, + colors=[ft.Colors.PRIMARY, ft.Colors.AMBER_400], + tile_mode=mode, + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="GradientTileMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare how gradients behave outside their defined " + "paint region." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(mode) for mode in ft.GradientTileMode], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/gradient_tile_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/gradient_tile_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..416906ec13 --- /dev/null +++ b/sdk/python/examples/controls/types/gradient_tile_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-gradient-tile-mode-showcase" +version = "1.0.0" +description = "Compares Gradient Tile Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["gradient tile mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Gradient Tile Mode showcase" +controls = ["SafeArea", "Column", "Container", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/image_repeat/showcase.py b/sdk/python/examples/controls/types/image_repeat/showcase.py deleted file mode 100644 index caeba1548c..0000000000 --- a/sdk/python/examples/controls/types/image_repeat/showcase.py +++ /dev/null @@ -1,50 +0,0 @@ -import flet as ft - - -def showcase_card(repeat: ft.ImageRepeat) -> ft.Container: - return ft.Container( - width=280, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(repeat.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=240, - height=130, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.Image( - src="https://picsum.photos/id/237/200/300", - width=240, - height=130, - repeat=repeat, - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="ImageRepeat Showcase") - page.add( - ft.Text("Compare how an image fills uncovered space using repeat modes."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(repeat) for repeat in ft.ImageRepeat], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/image_repeat/showcase/main.py b/sdk/python/examples/controls/types/image_repeat/showcase/main.py new file mode 100644 index 0000000000..c6a57acd17 --- /dev/null +++ b/sdk/python/examples/controls/types/image_repeat/showcase/main.py @@ -0,0 +1,59 @@ +import flet as ft + + +def showcase_card(repeat: ft.ImageRepeat) -> ft.Container: + return ft.Container( + width=280, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(repeat.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=240, + height=130, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.Image( + src="https://picsum.photos/id/237/200/300", + width=240, + height=130, + repeat=repeat, + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="ImageRepeat Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare how an image fills uncovered space using repeat modes." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(repeat) for repeat in ft.ImageRepeat], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/image_repeat/showcase/pyproject.toml b/sdk/python/examples/controls/types/image_repeat/showcase/pyproject.toml new file mode 100644 index 0000000000..3a00b03bd0 --- /dev/null +++ b/sdk/python/examples/controls/types/image_repeat/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-image-repeat-showcase" +version = "1.0.0" +description = "Compares Image Repeat values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["image repeat", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Image Repeat showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Image", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/label_position/showcase.py b/sdk/python/examples/controls/types/label_position/showcase.py deleted file mode 100644 index 1383f40b30..0000000000 --- a/sdk/python/examples/controls/types/label_position/showcase.py +++ /dev/null @@ -1,61 +0,0 @@ -import flet as ft - - -def showcase_card(label_position: ft.LabelPosition) -> ft.Container: - return ft.Container( - width=280, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=10, - controls=[ - ft.Text(label_position.name, weight=ft.FontWeight.BOLD), - ft.Checkbox( - label="Checkbox label", - value=True, - label_position=label_position, - ), - ft.RadioGroup( - content=ft.Row( - controls=[ - ft.Radio( - label=f"{i}", - value=f"{i}", - label_position=label_position, - ) - for i in range(1, 4) - ], - ) - ), - ft.Switch( - label="Switch label", - value=True, - label_position=label_position, - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="LabelPosition Showcase") - page.add( - ft.Text("Compare left/right label placement for form controls."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(label_position) for label_position in ft.LabelPosition - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/label_position/showcase/main.py b/sdk/python/examples/controls/types/label_position/showcase/main.py new file mode 100644 index 0000000000..b8b7ab0f05 --- /dev/null +++ b/sdk/python/examples/controls/types/label_position/showcase/main.py @@ -0,0 +1,69 @@ +import flet as ft + + +def showcase_card(label_position: ft.LabelPosition) -> ft.Container: + return ft.Container( + width=280, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=10, + controls=[ + ft.Text(label_position.name, weight=ft.FontWeight.BOLD), + ft.Checkbox( + label="Checkbox label", + value=True, + label_position=label_position, + ), + ft.RadioGroup( + content=ft.Row( + controls=[ + ft.Radio( + label=f"{i}", + value=f"{i}", + label_position=label_position, + ) + for i in range(1, 4) + ], + ) + ), + ft.Switch( + label="Switch label", + value=True, + label_position=label_position, + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="LabelPosition Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare left/right label placement for form controls."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(label_position) + for label_position in ft.LabelPosition + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/label_position/showcase/pyproject.toml b/sdk/python/examples/controls/types/label_position/showcase/pyproject.toml new file mode 100644 index 0000000000..653d3cb7eb --- /dev/null +++ b/sdk/python/examples/controls/types/label_position/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-label-position-showcase" +version = "1.0.0" +description = "Compares Label Position values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["label position", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Label Position showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Checkbox", "RadioGroup", "Row", "Radio"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/launch_mode/showcase.py b/sdk/python/examples/controls/types/launch_mode/showcase.py deleted file mode 100644 index d3e3942c29..0000000000 --- a/sdk/python/examples/controls/types/launch_mode/showcase.py +++ /dev/null @@ -1,56 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - launcher = ft.UrlLauncher() - - def showcase_card(mode: ft.LaunchMode) -> ft.Container: - status = ft.Text("Click to check support on this platform.", size=11) - - async def check_support(): - try: - supported = await launcher.supports_launch_mode(mode) - closable = await launcher.supports_close_for_launch_mode(mode) - status.value = ( - f"supports_launch_mode={supported} / supports_close={closable}" - ) - except Exception as ex: - status.value = f"Error: {ex}" - status.update() - - return ft.Container( - width=360, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(mode.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Check support", - icon=ft.Icons.CHECK_CIRCLE_OUTLINE, - on_click=lambda: page.run_task(check_support), - ), - status, - ], - ), - ) - - page.appbar = ft.AppBar(title="LaunchMode Showcase") - page.add( - ft.Text("Check launch-mode support reported by the current platform."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(mode) for mode in ft.LaunchMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/launch_mode/showcase/main.py b/sdk/python/examples/controls/types/launch_mode/showcase/main.py new file mode 100644 index 0000000000..ac43a8254c --- /dev/null +++ b/sdk/python/examples/controls/types/launch_mode/showcase/main.py @@ -0,0 +1,65 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + launcher = ft.UrlLauncher() + + def showcase_card(mode: ft.LaunchMode) -> ft.Container: + status = ft.Text("Click to check support on this platform.", size=11) + + async def check_support(): + try: + supported = await launcher.supports_launch_mode(mode) + closable = await launcher.supports_close_for_launch_mode(mode) + status.value = ( + f"supports_launch_mode={supported} / supports_close={closable}" + ) + except Exception as ex: + status.value = f"Error: {ex}" + status.update() + + return ft.Container( + width=360, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(mode.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Check support", + icon=ft.Icons.CHECK_CIRCLE_OUTLINE, + on_click=lambda: page.run_task(check_support), + ), + status, + ], + ), + ) + + page.appbar = ft.AppBar(title="LaunchMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Check launch-mode support reported by the current platform." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(mode) for mode in ft.LaunchMode], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/launch_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/launch_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..5ebbee44c5 --- /dev/null +++ b/sdk/python/examples/controls/types/launch_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-launch-mode-showcase" +version = "1.0.0" +description = "Compares Launch Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["launch mode", "showcase", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Launch Mode showcase" +controls = ["SafeArea", "Column", "UrlLauncher", "Container", "Text", "Button", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/list_tile_style/showcase.py b/sdk/python/examples/controls/types/list_tile_style/showcase.py deleted file mode 100644 index 57d20e386e..0000000000 --- a/sdk/python/examples/controls/types/list_tile_style/showcase.py +++ /dev/null @@ -1,49 +0,0 @@ -import flet as ft - - -def showcase_card(style: ft.ListTileStyle) -> ft.Container: - return ft.Container( - width=360, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(style.name, weight=ft.FontWeight.BOLD), - ft.Container( - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.ListTile( - style=style, - leading=ft.Icon(ft.Icons.PERSON), - title=ft.Text("Jane Doe"), - subtitle=ft.Text("Product Manager"), - trailing=ft.Icon(ft.Icons.CHEVRON_RIGHT), - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="ListTileStyle Showcase") - page.add( - ft.Text("Compare list tile typography presets."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(style) for style in ft.ListTileStyle], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/list_tile_style/showcase/main.py b/sdk/python/examples/controls/types/list_tile_style/showcase/main.py new file mode 100644 index 0000000000..1005eb83bd --- /dev/null +++ b/sdk/python/examples/controls/types/list_tile_style/showcase/main.py @@ -0,0 +1,56 @@ +import flet as ft + + +def showcase_card(style: ft.ListTileStyle) -> ft.Container: + return ft.Container( + width=360, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(style.name, weight=ft.FontWeight.BOLD), + ft.Container( + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.ListTile( + style=style, + leading=ft.Icon(ft.Icons.PERSON), + title=ft.Text("Jane Doe"), + subtitle=ft.Text("Product Manager"), + trailing=ft.Icon(ft.Icons.CHEVRON_RIGHT), + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="ListTileStyle Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare list tile typography presets."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(style) for style in ft.ListTileStyle], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/list_tile_style/showcase/pyproject.toml b/sdk/python/examples/controls/types/list_tile_style/showcase/pyproject.toml new file mode 100644 index 0000000000..30a155f0cb --- /dev/null +++ b/sdk/python/examples/controls/types/list_tile_style/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-list-tile-style-showcase" +version = "1.0.0" +description = "Compares List Tile Style values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["list tile style", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "List Tile Style showcase" +controls = ["SafeArea", "Column", "Container", "Text", "ListTile", "Icon", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/list_tile_title_alignment/showcase.py b/sdk/python/examples/controls/types/list_tile_title_alignment/showcase.py deleted file mode 100644 index a5c95c98fd..0000000000 --- a/sdk/python/examples/controls/types/list_tile_title_alignment/showcase.py +++ /dev/null @@ -1,54 +0,0 @@ -import flet as ft - - -def showcase_card(alignment: ft.ListTileTitleAlignment) -> ft.Container: - return ft.Container( - width=380, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(alignment.name, weight=ft.FontWeight.BOLD), - ft.Container( - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.ListTile( - title_alignment=alignment, - leading=ft.CircleAvatar(content=ft.Text("JD")), - title=ft.Text("Jane Doe"), - subtitle=ft.Text( - "This subtitle helps visualize vertical alignment." - ), - is_three_line=True, - trailing=ft.Icon(ft.Icons.MORE_VERT), - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="ListTileTitleAlignment Showcase") - page.add( - ft.Text("Compare leading/trailing alignment against title area."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(alignment) for alignment in ft.ListTileTitleAlignment - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/list_tile_title_alignment/showcase/main.py b/sdk/python/examples/controls/types/list_tile_title_alignment/showcase/main.py new file mode 100644 index 0000000000..64aa6dc654 --- /dev/null +++ b/sdk/python/examples/controls/types/list_tile_title_alignment/showcase/main.py @@ -0,0 +1,62 @@ +import flet as ft + + +def showcase_card(alignment: ft.ListTileTitleAlignment) -> ft.Container: + return ft.Container( + width=380, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(alignment.name, weight=ft.FontWeight.BOLD), + ft.Container( + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.ListTile( + title_alignment=alignment, + leading=ft.CircleAvatar(content=ft.Text("JD")), + title=ft.Text("Jane Doe"), + subtitle=ft.Text( + "This subtitle helps visualize vertical alignment." + ), + is_three_line=True, + trailing=ft.Icon(ft.Icons.MORE_VERT), + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="ListTileTitleAlignment Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare leading/trailing alignment against title area."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(alignment) + for alignment in ft.ListTileTitleAlignment + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/list_tile_title_alignment/showcase/pyproject.toml b/sdk/python/examples/controls/types/list_tile_title_alignment/showcase/pyproject.toml new file mode 100644 index 0000000000..d670e3fb69 --- /dev/null +++ b/sdk/python/examples/controls/types/list_tile_title_alignment/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-list-tile-title-alignment-showcase" +version = "1.0.0" +description = "Compares List Tile Title Alignment values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["list tile title alignment", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "List Tile Title Alignment showcase" +controls = ["SafeArea", "Column", "Container", "Text", "ListTile", "CircleAvatar", "Icon", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/main_axis_alignment/showcase.py b/sdk/python/examples/controls/types/main_axis_alignment/showcase.py deleted file mode 100644 index 3fb3faed23..0000000000 --- a/sdk/python/examples/controls/types/main_axis_alignment/showcase.py +++ /dev/null @@ -1,61 +0,0 @@ -import flet as ft - - -def dot(label: str) -> ft.Container: - return ft.Container( - width=36, - height=36, - border_radius=18, - bgcolor=ft.Colors.PRIMARY_CONTAINER, - alignment=ft.Alignment.CENTER, - content=ft.Text(label, size=12, color=ft.Colors.ON_PRIMARY_CONTAINER), - ) - - -def showcase_card(alignment: ft.MainAxisAlignment) -> ft.Container: - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(alignment.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=250, - height=70, - padding=8, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.Row( - alignment=alignment, - vertical_alignment=ft.CrossAxisAlignment.CENTER, - controls=[dot("1"), dot("2"), dot("3")], - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="MainAxisAlignment Showcase") - page.add( - ft.Text("Compare horizontal distribution of children in a fixed-width row."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(alignment) for alignment in ft.MainAxisAlignment], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/main_axis_alignment/showcase/main.py b/sdk/python/examples/controls/types/main_axis_alignment/showcase/main.py new file mode 100644 index 0000000000..ae73750d80 --- /dev/null +++ b/sdk/python/examples/controls/types/main_axis_alignment/showcase/main.py @@ -0,0 +1,74 @@ +import flet as ft + + +def dot(label: str) -> ft.Container: + return ft.Container( + width=36, + height=36, + border_radius=18, + bgcolor=ft.Colors.PRIMARY_CONTAINER, + alignment=ft.Alignment.CENTER, + content=ft.Text(label, size=12, color=ft.Colors.ON_PRIMARY_CONTAINER), + ) + + +def showcase_card(alignment: ft.MainAxisAlignment) -> ft.Container: + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(alignment.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=250, + height=70, + padding=8, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.Row( + alignment=alignment, + vertical_alignment=ft.CrossAxisAlignment.CENTER, + controls=[dot("1"), dot("2"), dot("3")], + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="MainAxisAlignment Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare horizontal distribution of children in a " + "fixed-width row." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(alignment) + for alignment in ft.MainAxisAlignment + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/main_axis_alignment/showcase/pyproject.toml b/sdk/python/examples/controls/types/main_axis_alignment/showcase/pyproject.toml new file mode 100644 index 0000000000..5333d6c4a5 --- /dev/null +++ b/sdk/python/examples/controls/types/main_axis_alignment/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-main-axis-alignment-showcase" +version = "1.0.0" +description = "Compares Main Axis Alignment values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["main axis alignment", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Main Axis Alignment showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Row", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/margin/container.py b/sdk/python/examples/controls/types/margin/container.py deleted file mode 100644 index 694ee35e41..0000000000 --- a/sdk/python/examples/controls/types/margin/container.py +++ /dev/null @@ -1,48 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Margin Example" - - page.add( - ft.Row( - spacing=0, - controls=[ - ft.Container( - content=ft.Button("container_1"), - bgcolor=ft.Colors.AMBER, - # padding=ft.Padding.all(10), - margin=ft.Margin.all(10), - width=200, - height=200, - ), - ft.Container( - content=ft.Button("container_2"), - bgcolor=ft.Colors.AMBER, - # padding=ft.Padding.all(20), - margin=ft.Margin.all(20), - width=200, - height=200, - ), - ft.Container( - content=ft.Button("container_3"), - bgcolor=ft.Colors.AMBER, - # padding=ft.Padding.symmetric(horizontal=10), - margin=ft.Margin.symmetric(vertical=10), - width=200, - height=200, - ), - ft.Container( - content=ft.Button("container_4"), - bgcolor=ft.Colors.AMBER, - # padding=ft.Padding.only(left=10), - margin=ft.Margin.only(left=10), - width=200, - height=200, - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/margin/container/main.py b/sdk/python/examples/controls/types/margin/container/main.py new file mode 100644 index 0000000000..1f758862e8 --- /dev/null +++ b/sdk/python/examples/controls/types/margin/container/main.py @@ -0,0 +1,55 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Margin Example" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + spacing=0, + controls=[ + ft.Container( + content=ft.Button("container_1"), + bgcolor=ft.Colors.AMBER, + # padding=ft.Padding.all(10), + margin=ft.Margin.all(10), + width=200, + height=200, + ), + ft.Container( + content=ft.Button("container_2"), + bgcolor=ft.Colors.AMBER, + # padding=ft.Padding.all(20), + margin=ft.Margin.all(20), + width=200, + height=200, + ), + ft.Container( + content=ft.Button("container_3"), + bgcolor=ft.Colors.AMBER, + # padding=ft.Padding.symmetric(horizontal=10), + margin=ft.Margin.symmetric(vertical=10), + width=200, + height=200, + ), + ft.Container( + content=ft.Button("container_4"), + bgcolor=ft.Colors.AMBER, + # padding=ft.Padding.only(left=10), + margin=ft.Margin.only(left=10), + width=200, + height=200, + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/margin/container/pyproject.toml b/sdk/python/examples/controls/types/margin/container/pyproject.toml new file mode 100644 index 0000000000..fd0bc98b32 --- /dev/null +++ b/sdk/python/examples/controls/types/margin/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-margin-container" +version = "1.0.0" +description = "Demonstrates how Margin values change container presentation and layout." +requires-python = ">=3.10" +keywords = ["margin", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Margin" +controls = ["SafeArea", "Column", "Row", "Container", "Button", "Margin"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["margin preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/mouse_cursor/showcase.py b/sdk/python/examples/controls/types/mouse_cursor/showcase.py deleted file mode 100644 index 1550036d46..0000000000 --- a/sdk/python/examples/controls/types/mouse_cursor/showcase.py +++ /dev/null @@ -1,46 +0,0 @@ -import flet as ft - - -def showcase_card(cursor: ft.MouseCursor) -> ft.GestureDetector: - return ft.GestureDetector( - mouse_cursor=cursor, - content=ft.Container( - width=250, - height=100, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=4, - alignment=ft.MainAxisAlignment.CENTER, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(cursor.name, weight=ft.FontWeight.BOLD), - ], - ), - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="MouseCursor Showcase") - page.add( - ft.Text( - "Hover each card to compare cursor styles. " - "Cursor rendering can vary by OS and browser.", - ), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(cursor) for cursor in ft.MouseCursor], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/mouse_cursor/showcase/main.py b/sdk/python/examples/controls/types/mouse_cursor/showcase/main.py new file mode 100644 index 0000000000..fb4ab60f81 --- /dev/null +++ b/sdk/python/examples/controls/types/mouse_cursor/showcase/main.py @@ -0,0 +1,53 @@ +import flet as ft + + +def showcase_card(cursor: ft.MouseCursor) -> ft.GestureDetector: + return ft.GestureDetector( + mouse_cursor=cursor, + content=ft.Container( + width=250, + height=100, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=4, + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text(cursor.name, weight=ft.FontWeight.BOLD), + ], + ), + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="MouseCursor Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Hover each card to compare cursor styles. " + "Cursor rendering can vary by OS and browser.", + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(cursor) for cursor in ft.MouseCursor], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/mouse_cursor/showcase/pyproject.toml b/sdk/python/examples/controls/types/mouse_cursor/showcase/pyproject.toml new file mode 100644 index 0000000000..21fff09eb8 --- /dev/null +++ b/sdk/python/examples/controls/types/mouse_cursor/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-mouse-cursor-showcase" +version = "1.0.0" +description = "Compares Mouse Cursor values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["mouse cursor", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Mouse Cursor showcase" +controls = ["SafeArea", "Column", "GestureDetector", "Container", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/navigation_bar_label_behavior/showcase.py b/sdk/python/examples/controls/types/navigation_bar_label_behavior/showcase.py deleted file mode 100644 index cbe4c5aa95..0000000000 --- a/sdk/python/examples/controls/types/navigation_bar_label_behavior/showcase.py +++ /dev/null @@ -1,50 +0,0 @@ -import flet as ft - - -def showcase_card(behavior: ft.NavigationBarLabelBehavior) -> ft.Container: - bar = ft.NavigationBar( - label_behavior=behavior, - selected_index=1, - destinations=[ - ft.NavigationBarDestination(icon=ft.Icons.HOME, label="Home"), - ft.NavigationBarDestination(icon=ft.Icons.SEARCH, label="Search"), - ft.NavigationBarDestination(icon=ft.Icons.PERSON, label="Profile"), - ], - ) - - return ft.Container( - width=380, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(behavior.name, weight=ft.FontWeight.BOLD), - bar, - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="NavigationBarLabelBehavior Showcase") - page.add( - ft.Text("Compare destination label visibility strategies."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(behavior) for behavior in ft.NavigationBarLabelBehavior - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/navigation_bar_label_behavior/showcase/main.py b/sdk/python/examples/controls/types/navigation_bar_label_behavior/showcase/main.py new file mode 100644 index 0000000000..7471161a3b --- /dev/null +++ b/sdk/python/examples/controls/types/navigation_bar_label_behavior/showcase/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def showcase_card(behavior: ft.NavigationBarLabelBehavior) -> ft.Container: + bar = ft.NavigationBar( + label_behavior=behavior, + selected_index=1, + destinations=[ + ft.NavigationBarDestination(icon=ft.Icons.HOME, label="Home"), + ft.NavigationBarDestination(icon=ft.Icons.SEARCH, label="Search"), + ft.NavigationBarDestination(icon=ft.Icons.PERSON, label="Profile"), + ], + ) + + return ft.Container( + width=380, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(behavior.name, weight=ft.FontWeight.BOLD), + bar, + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="NavigationBarLabelBehavior Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare destination label visibility strategies."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(behavior) + for behavior in ft.NavigationBarLabelBehavior + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/navigation_bar_label_behavior/showcase/pyproject.toml b/sdk/python/examples/controls/types/navigation_bar_label_behavior/showcase/pyproject.toml new file mode 100644 index 0000000000..8464025443 --- /dev/null +++ b/sdk/python/examples/controls/types/navigation_bar_label_behavior/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-navigation-bar-label-behavior-showcase" +version = "1.0.0" +description = "Compares Navigation Bar Label Behavior values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["navigation bar label behavior", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Navigation Bar Label Behavior showcase" +controls = ["SafeArea", "Column", "Container", "NavigationBar", "NavigationBarDestination", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/navigation_rail_label_type/showcase.py b/sdk/python/examples/controls/types/navigation_rail_label_type/showcase.py deleted file mode 100644 index 366bb316e5..0000000000 --- a/sdk/python/examples/controls/types/navigation_rail_label_type/showcase.py +++ /dev/null @@ -1,51 +0,0 @@ -import flet as ft - - -def showcase_card(label_type: ft.NavigationRailLabelType) -> ft.Container: - rail = ft.NavigationRail( - width=100, - height=220, - selected_index=1, - label_type=label_type, - destinations=[ - ft.NavigationRailDestination(icon=ft.Icons.HOME, label="Home"), - ft.NavigationRailDestination(icon=ft.Icons.SEARCH, label="Search"), - ft.NavigationRailDestination(icon=ft.Icons.PERSON, label="Profile"), - ], - ) - - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Text(label_type.name, weight=ft.FontWeight.BOLD), - rail, - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="NavigationRailLabelType Showcase") - page.add( - ft.Text("Compare label visibility in compact navigation rail."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(t) for t in ft.NavigationRailLabelType], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/navigation_rail_label_type/showcase/main.py b/sdk/python/examples/controls/types/navigation_rail_label_type/showcase/main.py new file mode 100644 index 0000000000..7ad4568f5c --- /dev/null +++ b/sdk/python/examples/controls/types/navigation_rail_label_type/showcase/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def showcase_card(label_type: ft.NavigationRailLabelType) -> ft.Container: + rail = ft.NavigationRail( + width=100, + height=220, + selected_index=1, + label_type=label_type, + destinations=[ + ft.NavigationRailDestination(icon=ft.Icons.HOME, label="Home"), + ft.NavigationRailDestination(icon=ft.Icons.SEARCH, label="Search"), + ft.NavigationRailDestination(icon=ft.Icons.PERSON, label="Profile"), + ], + ) + + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text(label_type.name, weight=ft.FontWeight.BOLD), + rail, + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="NavigationRailLabelType Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare label visibility in compact navigation rail."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(t) for t in ft.NavigationRailLabelType], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/navigation_rail_label_type/showcase/pyproject.toml b/sdk/python/examples/controls/types/navigation_rail_label_type/showcase/pyproject.toml new file mode 100644 index 0000000000..c675a018d6 --- /dev/null +++ b/sdk/python/examples/controls/types/navigation_rail_label_type/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-navigation-rail-label-type-showcase" +version = "1.0.0" +description = "Compares Navigation Rail Label Type values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["navigation rail label type", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Navigation Rail Label Type showcase" +controls = ["SafeArea", "Column", "Container", "NavigationRail", "NavigationRailDestination", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/orientation/showcase.py b/sdk/python/examples/controls/types/orientation/showcase.py deleted file mode 100644 index 2b6e3b7c67..0000000000 --- a/sdk/python/examples/controls/types/orientation/showcase.py +++ /dev/null @@ -1,49 +0,0 @@ -from datetime import time - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def showcase_card(orientation: ft.Orientation) -> ft.Container: - picker = ft.TimePicker( - value=time(hour=10, minute=30), - help_text=f"{orientation.name} time picker", - orientation=orientation, - ) - - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(orientation.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Open TimePicker", - icon=ft.Icons.ACCESS_TIME, - on_click=lambda _, p=picker: page.show_dialog(p), - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="Orientation Showcase") - page.add( - ft.Text("TimePicker supports PORTRAIT and LANDSCAPE layouts."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(orientation) for orientation in ft.Orientation], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/orientation/showcase/main.py b/sdk/python/examples/controls/types/orientation/showcase/main.py new file mode 100644 index 0000000000..5edf88fd5e --- /dev/null +++ b/sdk/python/examples/controls/types/orientation/showcase/main.py @@ -0,0 +1,58 @@ +from datetime import time + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def showcase_card(orientation: ft.Orientation) -> ft.Container: + picker = ft.TimePicker( + value=time(hour=10, minute=30), + help_text=f"{orientation.name} time picker", + orientation=orientation, + ) + + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(orientation.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Open TimePicker", + icon=ft.Icons.ACCESS_TIME, + on_click=lambda _, p=picker: page.show_dialog(p), + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="Orientation Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("TimePicker supports PORTRAIT and LANDSCAPE layouts."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(orientation) for orientation in ft.Orientation + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/orientation/showcase/pyproject.toml b/sdk/python/examples/controls/types/orientation/showcase/pyproject.toml new file mode 100644 index 0000000000..3c15afd632 --- /dev/null +++ b/sdk/python/examples/controls/types/orientation/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-orientation-showcase" +version = "1.0.0" +description = "Compares Orientation values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["orientation", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Orientation showcase" +controls = ["SafeArea", "Column", "Container", "TimePicker", "Text", "Button", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/overlay_visibility_mode/showcase.py b/sdk/python/examples/controls/types/overlay_visibility_mode/showcase.py deleted file mode 100644 index 2652401f6e..0000000000 --- a/sdk/python/examples/controls/types/overlay_visibility_mode/showcase.py +++ /dev/null @@ -1,52 +0,0 @@ -import flet as ft - - -def showcase_card(mode: ft.OverlayVisibilityMode) -> ft.Container: - return ft.Container( - width=380, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(mode.name, weight=ft.FontWeight.BOLD), - ft.Text("Prefix/suffix visibility (prefilled):", size=11), - ft.CupertinoTextField( - value="Flet", - placeholder_text="Search", - prefix=ft.Icon(ft.CupertinoIcons.SEARCH), - suffix=ft.Icon(ft.CupertinoIcons.MIC), - prefix_visibility_mode=mode, - suffix_visibility_mode=mode, - ), - ft.Text("Clear button visibility:", size=11), - ft.CupertinoTextField( - value="Clear me", - placeholder_text="Type text", - clear_button_visibility_mode=mode, - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="OverlayVisibilityMode Showcase") - page.add( - ft.Text("Compare when Cupertino text field overlays appear."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(mode) for mode in ft.OverlayVisibilityMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/overlay_visibility_mode/showcase/main.py b/sdk/python/examples/controls/types/overlay_visibility_mode/showcase/main.py new file mode 100644 index 0000000000..7fcb7b0265 --- /dev/null +++ b/sdk/python/examples/controls/types/overlay_visibility_mode/showcase/main.py @@ -0,0 +1,61 @@ +import flet as ft + + +def showcase_card(mode: ft.OverlayVisibilityMode) -> ft.Container: + return ft.Container( + width=380, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(mode.name, weight=ft.FontWeight.BOLD), + ft.Text("Prefix/suffix visibility (prefilled):", size=11), + ft.CupertinoTextField( + value="Flet", + placeholder_text="Search", + prefix=ft.Icon(ft.CupertinoIcons.SEARCH), + suffix=ft.Icon(ft.CupertinoIcons.MIC), + prefix_visibility_mode=mode, + suffix_visibility_mode=mode, + ), + ft.Text("Clear button visibility:", size=11), + ft.CupertinoTextField( + value="Clear me", + placeholder_text="Type text", + clear_button_visibility_mode=mode, + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="OverlayVisibilityMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare when Cupertino text field overlays appear."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(mode) for mode in ft.OverlayVisibilityMode + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/overlay_visibility_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/overlay_visibility_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..e5d493d06b --- /dev/null +++ b/sdk/python/examples/controls/types/overlay_visibility_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-overlay-visibility-mode-showcase" +version = "1.0.0" +description = "Compares Overlay Visibility Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["overlay visibility mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Overlay Visibility Mode showcase" +controls = ["SafeArea", "Column", "Container", "Text", "CupertinoTextField", "Icon", "CupertinoIcons", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/padding/container.py b/sdk/python/examples/controls/types/padding/container.py deleted file mode 100644 index 89f578601d..0000000000 --- a/sdk/python/examples/controls/types/padding/container.py +++ /dev/null @@ -1,43 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.title = "Containers with different padding" - - page.add( - ft.Row( - controls=[ - ft.Container( - content=ft.Button("container_1"), - bgcolor=ft.Colors.AMBER, - padding=ft.Padding.all(10), - width=150, - height=150, - ), - ft.Container( - content=ft.Button("container_2"), - bgcolor=ft.Colors.AMBER, - padding=ft.Padding.all(20), - width=150, - height=150, - ), - ft.Container( - content=ft.Button("container_3"), - bgcolor=ft.Colors.AMBER, - padding=ft.Padding.symmetric(horizontal=10), - width=150, - height=150, - ), - ft.Container( - content=ft.Button("container_4"), - bgcolor=ft.Colors.AMBER, - padding=ft.Padding.only(left=10), - width=150, - height=150, - ), - ] - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/padding/container/main.py b/sdk/python/examples/controls/types/padding/container/main.py new file mode 100644 index 0000000000..053c724922 --- /dev/null +++ b/sdk/python/examples/controls/types/padding/container/main.py @@ -0,0 +1,50 @@ +import flet as ft + + +def main(page: ft.Page): + page.title = "Containers with different padding" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + controls=[ + ft.Container( + content=ft.Button("container_1"), + bgcolor=ft.Colors.AMBER, + padding=ft.Padding.all(10), + width=150, + height=150, + ), + ft.Container( + content=ft.Button("container_2"), + bgcolor=ft.Colors.AMBER, + padding=ft.Padding.all(20), + width=150, + height=150, + ), + ft.Container( + content=ft.Button("container_3"), + bgcolor=ft.Colors.AMBER, + padding=ft.Padding.symmetric(horizontal=10), + width=150, + height=150, + ), + ft.Container( + content=ft.Button("container_4"), + bgcolor=ft.Colors.AMBER, + padding=ft.Padding.only(left=10), + width=150, + height=150, + ), + ] + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/padding/container/pyproject.toml b/sdk/python/examples/controls/types/padding/container/pyproject.toml new file mode 100644 index 0000000000..60644494af --- /dev/null +++ b/sdk/python/examples/controls/types/padding/container/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-padding-container" +version = "1.0.0" +description = "Demonstrates how Padding values change container presentation and layout." +requires-python = ">=3.10" +keywords = ["padding", "container"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Padding" +controls = ["SafeArea", "Column", "Row", "Container", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["padding preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/paint_gradient/canvas_paint.py b/sdk/python/examples/controls/types/paint_gradient/canvas_paint.py deleted file mode 100644 index fe2b161bda..0000000000 --- a/sdk/python/examples/controls/types/paint_gradient/canvas_paint.py +++ /dev/null @@ -1,71 +0,0 @@ -import math - -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - page.add( - cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Rect( - x=10, - y=10, - width=100, - height=100, - border_radius=5, - paint=ft.Paint( - style=ft.PaintingStyle.FILL, - gradient=ft.PaintLinearGradient( - begin=(0, 10), - end=(100, 50), - colors=[ft.Colors.BLUE, ft.Colors.YELLOW], - ), - ), - ), - cv.Circle( - x=60, - y=170, - radius=50, - paint=ft.Paint( - style=ft.PaintingStyle.FILL, - gradient=ft.PaintRadialGradient( - radius=50, - center=(60, 170), - colors=[ft.Colors.YELLOW, ft.Colors.BLUE], - ), - ), - ), - cv.Path( - elements=[ - cv.Path.Arc( - x=10, - y=230, - width=100, - height=100, - start_angle=3 * math.pi / 4, - sweep_angle=3 * math.pi / 2, - ), - ], - paint=ft.Paint( - stroke_width=15, - stroke_join=ft.StrokeJoin.ROUND, - style=ft.PaintingStyle.STROKE, - gradient=ft.PaintSweepGradient( - start_angle=0, - end_angle=math.pi * 2, - rotation=3 * math.pi / 4, - center=(60, 280), - colors=[ft.Colors.YELLOW, ft.Colors.PURPLE], - color_stops=[0.0, 1.0], - ), - ), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/paint_gradient/canvas_paint/main.py b/sdk/python/examples/controls/types/paint_gradient/canvas_paint/main.py new file mode 100644 index 0000000000..399cae0f65 --- /dev/null +++ b/sdk/python/examples/controls/types/paint_gradient/canvas_paint/main.py @@ -0,0 +1,78 @@ +import math + +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Rect( + x=10, + y=10, + width=100, + height=100, + border_radius=5, + paint=ft.Paint( + style=ft.PaintingStyle.FILL, + gradient=ft.PaintLinearGradient( + begin=(0, 10), + end=(100, 50), + colors=[ft.Colors.BLUE, ft.Colors.YELLOW], + ), + ), + ), + cv.Circle( + x=60, + y=170, + radius=50, + paint=ft.Paint( + style=ft.PaintingStyle.FILL, + gradient=ft.PaintRadialGradient( + radius=50, + center=(60, 170), + colors=[ft.Colors.YELLOW, ft.Colors.BLUE], + ), + ), + ), + cv.Path( + elements=[ + cv.Path.Arc( + x=10, + y=230, + width=100, + height=100, + start_angle=3 * math.pi / 4, + sweep_angle=3 * math.pi / 2, + ), + ], + paint=ft.Paint( + stroke_width=15, + stroke_join=ft.StrokeJoin.ROUND, + style=ft.PaintingStyle.STROKE, + gradient=ft.PaintSweepGradient( + start_angle=0, + end_angle=math.pi * 2, + rotation=3 * math.pi / 4, + center=(60, 280), + colors=[ft.Colors.YELLOW, ft.Colors.PURPLE], + color_stops=[0.0, 1.0], + ), + ), + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/paint_gradient/canvas_paint/pyproject.toml b/sdk/python/examples/controls/types/paint_gradient/canvas_paint/pyproject.toml new file mode 100644 index 0000000000..a1d64c3203 --- /dev/null +++ b/sdk/python/examples/controls/types/paint_gradient/canvas_paint/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-paint-gradient-canvas-paint" +version = "1.0.0" +description = "Draws a canvas preview using Paint Gradient gradient settings and paint objects." +requires-python = ">=3.10" +keywords = ["paint gradient", "canvas paint"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Canvas/Shapes"] + +[tool.flet.metadata] +title = "Paint Gradient" +controls = ["SafeArea", "Column", "Canvas", "Rect", "Circle", "Path"] +layout_pattern = "canvas" +complexity = "basic" +features = ["canvas drawing", "gradient paint preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint.py b/sdk/python/examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint.py deleted file mode 100644 index 088a985cff..0000000000 --- a/sdk/python/examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint.py +++ /dev/null @@ -1,31 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - page.add( - cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Rect( - x=10, - y=10, - width=100, - height=100, - border_radius=5, - paint=ft.Paint( - style=ft.PaintingStyle.FILL, - gradient=ft.PaintLinearGradient( - begin=(0, 10), - end=(100, 50), - colors=[ft.Colors.BLUE, ft.Colors.YELLOW], - ), - ), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint/main.py b/sdk/python/examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint/main.py new file mode 100644 index 0000000000..e318a22fe6 --- /dev/null +++ b/sdk/python/examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint/main.py @@ -0,0 +1,38 @@ +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Rect( + x=10, + y=10, + width=100, + height=100, + border_radius=5, + paint=ft.Paint( + style=ft.PaintingStyle.FILL, + gradient=ft.PaintLinearGradient( + begin=(0, 10), + end=(100, 50), + colors=[ft.Colors.BLUE, ft.Colors.YELLOW], + ), + ), + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint/pyproject.toml b/sdk/python/examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint/pyproject.toml new file mode 100644 index 0000000000..bd2ab5d01c --- /dev/null +++ b/sdk/python/examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-paint-gradient-paint-linear-gradient-canvas-paint" +version = "1.0.0" +description = "Draws a canvas preview using Paint Linear Gradient gradient settings and paint objects." +requires-python = ">=3.10" +keywords = ["paint gradient", "paint linear gradient", "canvas paint"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Canvas/Shapes"] + +[tool.flet.metadata] +title = "Paint Linear Gradient" +controls = ["SafeArea", "Column", "Canvas", "Rect"] +layout_pattern = "canvas" +complexity = "basic" +features = ["canvas drawing", "gradient paint preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint.py b/sdk/python/examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint.py deleted file mode 100644 index 8ccae77728..0000000000 --- a/sdk/python/examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint.py +++ /dev/null @@ -1,29 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - page.add( - cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Circle( - x=60, - y=170, - radius=50, - paint=ft.Paint( - style=ft.PaintingStyle.FILL, - gradient=ft.PaintRadialGradient( - radius=50, - center=(60, 170), - colors=[ft.Colors.YELLOW, ft.Colors.BLUE], - ), - ), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint/main.py b/sdk/python/examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint/main.py new file mode 100644 index 0000000000..6e8470470a --- /dev/null +++ b/sdk/python/examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint/main.py @@ -0,0 +1,36 @@ +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Circle( + x=60, + y=170, + radius=50, + paint=ft.Paint( + style=ft.PaintingStyle.FILL, + gradient=ft.PaintRadialGradient( + radius=50, + center=(60, 170), + colors=[ft.Colors.YELLOW, ft.Colors.BLUE], + ), + ), + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint/pyproject.toml b/sdk/python/examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint/pyproject.toml new file mode 100644 index 0000000000..061c411171 --- /dev/null +++ b/sdk/python/examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-paint-gradient-paint-radial-gradient-canvas-paint" +version = "1.0.0" +description = "Draws a canvas preview using Paint Radial Gradient gradient settings and paint objects." +requires-python = ">=3.10" +keywords = ["paint gradient", "paint radial gradient", "canvas paint"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Canvas/Shapes"] + +[tool.flet.metadata] +title = "Paint Radial Gradient" +controls = ["SafeArea", "Column", "Canvas", "Circle"] +layout_pattern = "canvas" +complexity = "basic" +features = ["canvas drawing", "gradient paint preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint.py b/sdk/python/examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint.py deleted file mode 100644 index 29fc05ce64..0000000000 --- a/sdk/python/examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint.py +++ /dev/null @@ -1,43 +0,0 @@ -import math - -import flet as ft -import flet.canvas as cv - - -def main(page: ft.Page): - page.add( - cv.Canvas( - width=float("inf"), - expand=True, - shapes=[ - cv.Path( - elements=[ - cv.Path.Arc( - x=10, - y=230, - width=100, - height=100, - start_angle=3 * math.pi / 4, - sweep_angle=3 * math.pi / 2, - ), - ], - paint=ft.Paint( - stroke_width=15, - stroke_join=ft.StrokeJoin.ROUND, - style=ft.PaintingStyle.STROKE, - gradient=ft.PaintSweepGradient( - start_angle=0, - end_angle=math.pi * 2, - rotation=3 * math.pi / 4, - center=(60, 280), - colors=[ft.Colors.YELLOW, ft.Colors.PURPLE], - color_stops=[0.0, 1.0], - ), - ), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint/main.py b/sdk/python/examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint/main.py new file mode 100644 index 0000000000..f8c6b76e4a --- /dev/null +++ b/sdk/python/examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint/main.py @@ -0,0 +1,50 @@ +import math + +import flet as ft +import flet.canvas as cv + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + cv.Canvas( + width=float("inf"), + expand=True, + shapes=[ + cv.Path( + elements=[ + cv.Path.Arc( + x=10, + y=230, + width=100, + height=100, + start_angle=3 * math.pi / 4, + sweep_angle=3 * math.pi / 2, + ), + ], + paint=ft.Paint( + stroke_width=15, + stroke_join=ft.StrokeJoin.ROUND, + style=ft.PaintingStyle.STROKE, + gradient=ft.PaintSweepGradient( + start_angle=0, + end_angle=math.pi * 2, + rotation=3 * math.pi / 4, + center=(60, 280), + colors=[ft.Colors.YELLOW, ft.Colors.PURPLE], + color_stops=[0.0, 1.0], + ), + ), + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint/pyproject.toml b/sdk/python/examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint/pyproject.toml new file mode 100644 index 0000000000..ee12b75ce1 --- /dev/null +++ b/sdk/python/examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-paint-gradient-paint-sweep-gradient-canvas-paint" +version = "1.0.0" +description = "Draws a canvas preview using Paint Sweep Gradient gradient settings and paint objects." +requires-python = ">=3.10" +keywords = ["paint gradient", "paint sweep gradient", "canvas paint"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Canvas/Shapes"] + +[tool.flet.metadata] +title = "Paint Sweep Gradient" +controls = ["SafeArea", "Column", "Canvas", "Path"] +layout_pattern = "canvas" +complexity = "basic" +features = ["canvas drawing", "gradient paint preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/painting_style/showcase.py b/sdk/python/examples/controls/types/painting_style/showcase.py deleted file mode 100644 index 4cf44871fe..0000000000 --- a/sdk/python/examples/controls/types/painting_style/showcase.py +++ /dev/null @@ -1,54 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -def showcase_card(style: ft.PaintingStyle) -> ft.Container: - return ft.Container( - width=250, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(style.name, weight=ft.FontWeight.BOLD), - cv.Canvas( - width=240, - height=110, - shapes=[ - cv.Circle( - x=120, - y=55, - radius=34, - paint=ft.Paint( - style=style, - stroke_width=10, - color=ft.Colors.PRIMARY, - ), - ) - ], - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="PaintingStyle Showcase") - page.add( - ft.Text("Compare filled vs outlined rendering for the same shape."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(style) for style in ft.PaintingStyle], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/painting_style/showcase/main.py b/sdk/python/examples/controls/types/painting_style/showcase/main.py new file mode 100644 index 0000000000..7166d110cd --- /dev/null +++ b/sdk/python/examples/controls/types/painting_style/showcase/main.py @@ -0,0 +1,61 @@ +import flet as ft +import flet.canvas as cv + + +def showcase_card(style: ft.PaintingStyle) -> ft.Container: + return ft.Container( + width=250, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(style.name, weight=ft.FontWeight.BOLD), + cv.Canvas( + width=240, + height=110, + shapes=[ + cv.Circle( + x=120, + y=55, + radius=34, + paint=ft.Paint( + style=style, + stroke_width=10, + color=ft.Colors.PRIMARY, + ), + ) + ], + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="PaintingStyle Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare filled vs outlined rendering for the same shape."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(style) for style in ft.PaintingStyle], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/painting_style/showcase/pyproject.toml b/sdk/python/examples/controls/types/painting_style/showcase/pyproject.toml new file mode 100644 index 0000000000..5cdfabdc10 --- /dev/null +++ b/sdk/python/examples/controls/types/painting_style/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-painting-style-showcase" +version = "1.0.0" +description = "Compares Painting Style values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["painting style", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Canvas/Shapes"] + +[tool.flet.metadata] +title = "Painting Style showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Canvas", "Circle", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/point_mode/showcase.py b/sdk/python/examples/controls/types/point_mode/showcase.py deleted file mode 100644 index 912f36213e..0000000000 --- a/sdk/python/examples/controls/types/point_mode/showcase.py +++ /dev/null @@ -1,63 +0,0 @@ -import flet as ft -import flet.canvas as cv - -POINTS = [ - (25, 75), - (70, 30), - (115, 75), - (160, 30), - (205, 75), -] - - -def showcase_card(mode: cv.PointMode) -> ft.Container: - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(mode.name, weight=ft.FontWeight.BOLD), - cv.Canvas( - width=240, - height=110, - shapes=[ - cv.Points( - points=POINTS, - point_mode=mode, - paint=ft.Paint( - color=ft.Colors.PRIMARY, - stroke_width=12, - stroke_cap=ft.StrokeCap.ROUND, - ), - ) - ], - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="PointMode Showcase") - page.add( - ft.Text( - "Compare how the same coordinate list is interpreted by each point mode." - ), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(mode) for mode in cv.PointMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/point_mode/showcase/main.py b/sdk/python/examples/controls/types/point_mode/showcase/main.py new file mode 100644 index 0000000000..cd3d733724 --- /dev/null +++ b/sdk/python/examples/controls/types/point_mode/showcase/main.py @@ -0,0 +1,71 @@ +import flet as ft +import flet.canvas as cv + +POINTS = [ + (25, 75), + (70, 30), + (115, 75), + (160, 30), + (205, 75), +] + + +def showcase_card(mode: cv.PointMode) -> ft.Container: + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(mode.name, weight=ft.FontWeight.BOLD), + cv.Canvas( + width=240, + height=110, + shapes=[ + cv.Points( + points=POINTS, + point_mode=mode, + paint=ft.Paint( + color=ft.Colors.PRIMARY, + stroke_width=12, + stroke_cap=ft.StrokeCap.ROUND, + ), + ) + ], + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="PointMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare how the same coordinate list is interpreted " + "by each point mode." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(mode) for mode in cv.PointMode], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/point_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/point_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..8524348a99 --- /dev/null +++ b/sdk/python/examples/controls/types/point_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-point-mode-showcase" +version = "1.0.0" +description = "Compares Point Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["point mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Canvas/Shapes"] + +[tool.flet.metadata] +title = "Point Mode showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Canvas", "Points", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/popup_menu_position/showcase.py b/sdk/python/examples/controls/types/popup_menu_position/showcase.py deleted file mode 100644 index ba875fab69..0000000000 --- a/sdk/python/examples/controls/types/popup_menu_position/showcase.py +++ /dev/null @@ -1,51 +0,0 @@ -import flet as ft - - -def showcase_card(position: ft.PopupMenuPosition) -> ft.Container: - menu = ft.PopupMenuButton( - menu_position=position, - icon=ft.Text("Click me to open menu", color=ft.Colors.BLUE), - items=[ - ft.PopupMenuItem(icon=ft.Icons.EDIT, content="Rename"), - ft.PopupMenuItem(icon=ft.Icons.CONTENT_COPY, content="Duplicate"), - ft.PopupMenuItem(icon=ft.Icons.DELETE, content="Delete"), - ], - ) - - return ft.Container( - width=300, - height=220, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Text(position.name, weight=ft.FontWeight.BOLD), - menu, - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="PopupMenuPosition Showcase") - page.add( - ft.Text("Open each popup menu to compare their positioning."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(position) for position in ft.PopupMenuPosition], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/popup_menu_position/showcase/main.py b/sdk/python/examples/controls/types/popup_menu_position/showcase/main.py new file mode 100644 index 0000000000..b04aa79b57 --- /dev/null +++ b/sdk/python/examples/controls/types/popup_menu_position/showcase/main.py @@ -0,0 +1,60 @@ +import flet as ft + + +def showcase_card(position: ft.PopupMenuPosition) -> ft.Container: + menu = ft.PopupMenuButton( + menu_position=position, + icon=ft.Text("Click me to open menu", color=ft.Colors.BLUE), + items=[ + ft.PopupMenuItem(icon=ft.Icons.EDIT, content="Rename"), + ft.PopupMenuItem(icon=ft.Icons.CONTENT_COPY, content="Duplicate"), + ft.PopupMenuItem(icon=ft.Icons.DELETE, content="Delete"), + ], + ) + + return ft.Container( + width=300, + height=220, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Text(position.name, weight=ft.FontWeight.BOLD), + menu, + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="PopupMenuPosition Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Open each popup menu to compare their positioning."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(position) for position in ft.PopupMenuPosition + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/popup_menu_position/showcase/pyproject.toml b/sdk/python/examples/controls/types/popup_menu_position/showcase/pyproject.toml new file mode 100644 index 0000000000..185e99d110 --- /dev/null +++ b/sdk/python/examples/controls/types/popup_menu_position/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-popup-menu-position-showcase" +version = "1.0.0" +description = "Compares Popup Menu Position values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["popup menu position", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Popup Menu Position showcase" +controls = ["SafeArea", "Column", "Container", "PopupMenuButton", "Text", "PopupMenuItem", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/scroll_bar/showcase.py b/sdk/python/examples/controls/types/scroll_bar/showcase.py deleted file mode 100644 index a5ad78e154..0000000000 --- a/sdk/python/examples/controls/types/scroll_bar/showcase.py +++ /dev/null @@ -1,190 +0,0 @@ -from typing import Optional - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.appbar = ft.AppBar(title="Scrollbar Dataclass Showcase") - - def get_scrollbar() -> ft.Scrollbar: - def str_as_bool(value: Optional[str]) -> Optional[bool]: - return True if value == "true" else False if value == "false" else None - - return ft.Scrollbar( - thumb_visibility=str_as_bool(thumb_visibility.value), - track_visibility=str_as_bool(track_visibility.value), - interactive=str_as_bool(interactive.value), - thickness=thickness_value.value if use_thickness.value else None, - radius=radius_value.value if use_radius.value else None, - orientation=None - if orientation.value == "none" - else ft.ScrollbarOrientation(orientation.value), - ) - - def get_preview_content(scrollbar: ft.Scrollbar) -> tuple[str, ft.Control]: - selected_orientation = scrollbar.orientation - is_horizontal = selected_orientation in ( - ft.ScrollbarOrientation.TOP, - ft.ScrollbarOrientation.BOTTOM, - ) - if is_horizontal: - return ( - "Horizontal preview (TOP/BOTTOM orientation)", - ft.Row( - spacing=8, - scroll=scrollbar, - controls=[ - ft.Container( - width=110, - height=80, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - border_radius=8, - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, - alignment=ft.Alignment.CENTER, - content=ft.Text(f"Tile {i + 1}"), - ) - for i in range(18) - ], - ), - ) - - return ( - "Vertical preview (None/LEFT/RIGHT orientation)", - ft.Column( - spacing=4, - scroll=scrollbar, - controls=[ft.Text(f"Item {i + 1}") for i in range(40)], - ), - ) - - def update_preview(): - thickness_value.disabled = not use_thickness.value - radius_value.disabled = not use_radius.value - - scrollbar = get_scrollbar() - title, content = get_preview_content(scrollbar) - preview_title.value = title - preview_viewport.content = content - page.update() - - page.add( - ft.Text( - "Interactive playground for the Scrollbar dataclass. Change each property " - "and inspect the live result." - ), - ft.Row( - wrap=True, - spacing=12, - run_spacing=12, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Container( - width=360, - padding=12, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=10, - controls=[ - ft.Text("Configuration", weight=ft.FontWeight.BOLD), - thumb_visibility := ft.Dropdown( - label="thumb_visibility", - value="none", - on_select=update_preview, - options=[ - ft.DropdownOption("none", "None (theme default)"), - ft.DropdownOption("true", "True"), - ft.DropdownOption("false", "False"), - ], - ), - track_visibility := ft.Dropdown( - label="track_visibility", - value="none", - on_select=update_preview, - options=[ - ft.DropdownOption("none", "None (theme default)"), - ft.DropdownOption("true", "True"), - ft.DropdownOption("false", "False"), - ], - ), - interactive := ft.Dropdown( - label="interactive", - value="none", - on_select=update_preview, - options=[ - ft.DropdownOption( - "none", "None (platform default)" - ), - ft.DropdownOption("true", "True"), - ft.DropdownOption("false", "False"), - ], - ), - orientation := ft.Dropdown( - label="orientation", - value="none", - on_select=update_preview, - options=[ft.DropdownOption("none", "None (auto side)")] - + [ - ft.DropdownOption(o.value, o.name) - for o in ft.ScrollbarOrientation - ], - ), - use_thickness := ft.Checkbox( - label="Set thickness", - value=False, - on_change=update_preview, - ), - thickness_value := ft.Slider( - min=0, - max=20, - divisions=20, - value=8, - label="{value}", - on_change=update_preview, - ), - use_radius := ft.Checkbox( - label="Set radius", - value=False, - on_change=update_preview, - ), - radius_value := ft.Slider( - min=0, - max=20, - divisions=20, - value=8, - label="{value}", - on_change=update_preview, - ), - ], - ), - ), - ft.Container( - width=420, - padding=12, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=10, - controls=[ - ft.Text("Live preview", weight=ft.FontWeight.BOLD), - preview_title := ft.Text(weight=ft.FontWeight.BOLD), - preview_viewport := ft.Container( - height=260, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - padding=8, - ), - ], - ), - ), - ], - ), - ) - - update_preview() - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_bar/showcase/main.py b/sdk/python/examples/controls/types/scroll_bar/showcase/main.py new file mode 100644 index 0000000000..ad2c09a7a9 --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_bar/showcase/main.py @@ -0,0 +1,211 @@ +from typing import Optional + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.appbar = ft.AppBar(title="Scrollbar Dataclass Showcase") + + def get_scrollbar() -> ft.Scrollbar: + def str_as_bool(value: Optional[str]) -> Optional[bool]: + return True if value == "true" else False if value == "false" else None + + return ft.Scrollbar( + thumb_visibility=str_as_bool(thumb_visibility.value), + track_visibility=str_as_bool(track_visibility.value), + interactive=str_as_bool(interactive.value), + thickness=thickness_value.value if use_thickness.value else None, + radius=radius_value.value if use_radius.value else None, + orientation=None + if orientation.value == "none" + else ft.ScrollbarOrientation(orientation.value), + ) + + def get_preview_content(scrollbar: ft.Scrollbar) -> tuple[str, ft.Control]: + selected_orientation = scrollbar.orientation + is_horizontal = selected_orientation in ( + ft.ScrollbarOrientation.TOP, + ft.ScrollbarOrientation.BOTTOM, + ) + if is_horizontal: + return ( + "Horizontal preview (TOP/BOTTOM orientation)", + ft.Row( + spacing=8, + scroll=scrollbar, + controls=[ + ft.Container( + width=110, + height=80, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + border_radius=8, + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, + alignment=ft.Alignment.CENTER, + content=ft.Text(f"Tile {i + 1}"), + ) + for i in range(18) + ], + ), + ) + + return ( + "Vertical preview (None/LEFT/RIGHT orientation)", + ft.Column( + spacing=4, + scroll=scrollbar, + controls=[ft.Text(f"Item {i + 1}") for i in range(40)], + ), + ) + + def update_preview(): + thickness_value.disabled = not use_thickness.value + radius_value.disabled = not use_radius.value + + scrollbar = get_scrollbar() + title, content = get_preview_content(scrollbar) + preview_title.value = title + preview_viewport.content = content + page.update() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Interactive playground for the Scrollbar dataclass. " + "Change each property and inspect the live result." + ), + ft.Row( + wrap=True, + spacing=12, + run_spacing=12, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Container( + width=360, + padding=12, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=10, + controls=[ + ft.Text( + "Configuration", weight=ft.FontWeight.BOLD + ), + thumb_visibility := ft.Dropdown( + label="thumb_visibility", + value="none", + on_select=update_preview, + options=[ + ft.DropdownOption( + "none", "None (theme default)" + ), + ft.DropdownOption("true", "True"), + ft.DropdownOption("false", "False"), + ], + ), + track_visibility := ft.Dropdown( + label="track_visibility", + value="none", + on_select=update_preview, + options=[ + ft.DropdownOption( + "none", "None (theme default)" + ), + ft.DropdownOption("true", "True"), + ft.DropdownOption("false", "False"), + ], + ), + interactive := ft.Dropdown( + label="interactive", + value="none", + on_select=update_preview, + options=[ + ft.DropdownOption( + "none", "None (platform default)" + ), + ft.DropdownOption("true", "True"), + ft.DropdownOption("false", "False"), + ], + ), + orientation := ft.Dropdown( + label="orientation", + value="none", + on_select=update_preview, + options=[ + ft.DropdownOption( + "none", "None (auto side)" + ) + ] + + [ + ft.DropdownOption(o.value, o.name) + for o in ft.ScrollbarOrientation + ], + ), + use_thickness := ft.Checkbox( + label="Set thickness", + value=False, + on_change=update_preview, + ), + thickness_value := ft.Slider( + min=0, + max=20, + divisions=20, + value=8, + label="{value}", + on_change=update_preview, + ), + use_radius := ft.Checkbox( + label="Set radius", + value=False, + on_change=update_preview, + ), + radius_value := ft.Slider( + min=0, + max=20, + divisions=20, + value=8, + label="{value}", + on_change=update_preview, + ), + ], + ), + ), + ft.Container( + width=420, + padding=12, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=10, + controls=[ + ft.Text( + "Live preview", weight=ft.FontWeight.BOLD + ), + preview_title := ft.Text( + weight=ft.FontWeight.BOLD + ), + preview_viewport := ft.Container( + height=260, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + padding=8, + ), + ], + ), + ), + ], + ), + ], + ), + ) + ) + + update_preview() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_bar/showcase/pyproject.toml b/sdk/python/examples/controls/types/scroll_bar/showcase/pyproject.toml new file mode 100644 index 0000000000..bcb3d5f2cb --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_bar/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-scroll-bar-showcase" +version = "1.0.0" +description = "Compares Scroll Bar values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["scroll bar", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Scroll Bar showcase" +controls = ["SafeArea", "Column", "AppBar", "Scrollbar", "Row", "Container", "Text", "Dropdown"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/scroll_bar_orientation/showcase.py b/sdk/python/examples/controls/types/scroll_bar_orientation/showcase.py deleted file mode 100644 index ea8c463407..0000000000 --- a/sdk/python/examples/controls/types/scroll_bar_orientation/showcase.py +++ /dev/null @@ -1,91 +0,0 @@ -import flet as ft - - -def showcase_card(orientation: ft.ScrollbarOrientation) -> ft.Container: - is_vertical = orientation in ( - ft.ScrollbarOrientation.LEFT, - ft.ScrollbarOrientation.RIGHT, - ) - scrollbar = ft.Scrollbar( - orientation=orientation, - thumb_visibility=True, - track_visibility=True, - thickness=10, - radius=8, - ) - - if is_vertical: - viewport = ft.Container( - height=220, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - padding=8, - content=ft.Column( - spacing=4, - scroll=scrollbar, - controls=[ft.Text(f"Item {i + 1}") for i in range(35)], - ), - ) - else: - viewport = ft.Container( - height=130, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - padding=8, - content=ft.Row( - spacing=8, - scroll=scrollbar, - controls=[ - ft.Container( - width=84, - height=72, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - border_radius=8, - bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, - alignment=ft.Alignment.CENTER, - content=ft.Text(f"{i + 1}"), - ) - for i in range(20) - ], - ), - ) - - return ft.Container( - width=330, - padding=12, - border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(orientation.name, weight=ft.FontWeight.BOLD), - viewport, - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="ScrollbarOrientation Showcase") - page.add( - ft.Text( - "LEFT/RIGHT apply to vertical scrollables, TOP/BOTTOM apply to " - "horizontal scrollables.", - ), - ft.Row( - wrap=True, - spacing=12, - run_spacing=12, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(orientation) for orientation in ft.ScrollbarOrientation - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_bar_orientation/showcase/main.py b/sdk/python/examples/controls/types/scroll_bar_orientation/showcase/main.py new file mode 100644 index 0000000000..f4705443e0 --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_bar_orientation/showcase/main.py @@ -0,0 +1,99 @@ +import flet as ft + + +def showcase_card(orientation: ft.ScrollbarOrientation) -> ft.Container: + is_vertical = orientation in ( + ft.ScrollbarOrientation.LEFT, + ft.ScrollbarOrientation.RIGHT, + ) + scrollbar = ft.Scrollbar( + orientation=orientation, + thumb_visibility=True, + track_visibility=True, + thickness=10, + radius=8, + ) + + if is_vertical: + viewport = ft.Container( + height=220, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + padding=8, + content=ft.Column( + spacing=4, + scroll=scrollbar, + controls=[ft.Text(f"Item {i + 1}") for i in range(35)], + ), + ) + else: + viewport = ft.Container( + height=130, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + padding=8, + content=ft.Row( + spacing=8, + scroll=scrollbar, + controls=[ + ft.Container( + width=84, + height=72, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + border_radius=8, + bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST, + alignment=ft.Alignment.CENTER, + content=ft.Text(f"{i + 1}"), + ) + for i in range(20) + ], + ), + ) + + return ft.Container( + width=330, + padding=12, + border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(orientation.name, weight=ft.FontWeight.BOLD), + viewport, + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="ScrollbarOrientation Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "LEFT/RIGHT apply to vertical scrollables, TOP/BOTTOM apply to " + "horizontal scrollables.", + ), + ft.Row( + wrap=True, + spacing=12, + run_spacing=12, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(orientation) + for orientation in ft.ScrollbarOrientation + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_bar_orientation/showcase/pyproject.toml b/sdk/python/examples/controls/types/scroll_bar_orientation/showcase/pyproject.toml new file mode 100644 index 0000000000..c4cf0a87d6 --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_bar_orientation/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-scroll-bar-orientation-showcase" +version = "1.0.0" +description = "Compares Scroll Bar Orientation values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["scroll bar orientation", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Scroll Bar Orientation showcase" +controls = ["SafeArea", "Column", "Container", "Scrollbar", "Text", "Row", "AppBar"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/scroll_direction/showcase.py b/sdk/python/examples/controls/types/scroll_direction/showcase.py deleted file mode 100644 index b41c0ac979..0000000000 --- a/sdk/python/examples/controls/types/scroll_direction/showcase.py +++ /dev/null @@ -1,48 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - counts = {direction: 0 for direction in ft.ScrollDirection} - count_labels = { - direction: ft.Text(f"{direction.name}: 0") for direction in ft.ScrollDirection - } - - def on_scroll(e: ft.OnScrollEvent): - if e.event_type == ft.ScrollType.USER and e.direction is not None: - counts[e.direction] += 1 - count_labels[ - e.direction - ].value = f"{e.direction.name}: {counts[e.direction]}" - last.value = f"Last USER direction: {e.direction.name}" - for label in count_labels.values(): - label.update() - last.update() - - page.appbar = ft.AppBar(title="ScrollDirection Showcase") - page.add( - ft.Text("Scroll the list and watch USER direction notifications."), - last := ft.Text("Last USER direction: none"), - ft.Row( - wrap=True, - spacing=10, - controls=[count_labels[d] for d in ft.ScrollDirection], - alignment=ft.MainAxisAlignment.CENTER, - ), - ft.Container( - width=360, - height=240, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - padding=8, - content=ft.Column( - scroll=ft.ScrollMode.ALWAYS, - on_scroll=on_scroll, - controls=[ft.Text(f"Scrollable item {i + 1}") for i in range(60)], - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_direction/showcase/main.py b/sdk/python/examples/controls/types/scroll_direction/showcase/main.py new file mode 100644 index 0000000000..b3294e3e50 --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_direction/showcase/main.py @@ -0,0 +1,57 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + counts = {direction: 0 for direction in ft.ScrollDirection} + count_labels = { + direction: ft.Text(f"{direction.name}: 0") for direction in ft.ScrollDirection + } + + def on_scroll(e: ft.OnScrollEvent): + if e.event_type == ft.ScrollType.USER and e.direction is not None: + counts[e.direction] += 1 + count_labels[ + e.direction + ].value = f"{e.direction.name}: {counts[e.direction]}" + last.value = f"Last USER direction: {e.direction.name}" + for label in count_labels.values(): + label.update() + last.update() + + page.appbar = ft.AppBar(title="ScrollDirection Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Scroll the list and watch USER direction notifications."), + last := ft.Text("Last USER direction: none"), + ft.Row( + wrap=True, + spacing=10, + controls=[count_labels[d] for d in ft.ScrollDirection], + alignment=ft.MainAxisAlignment.CENTER, + ), + ft.Container( + width=360, + height=240, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + padding=8, + content=ft.Column( + scroll=ft.ScrollMode.ALWAYS, + on_scroll=on_scroll, + controls=[ + ft.Text(f"Scrollable item {i + 1}") for i in range(60) + ], + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_direction/showcase/pyproject.toml b/sdk/python/examples/controls/types/scroll_direction/showcase/pyproject.toml new file mode 100644 index 0000000000..e1052da622 --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_direction/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-scroll-direction-showcase" +version = "1.0.0" +description = "Compares Scroll Direction values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["scroll direction", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Scroll Direction showcase" +controls = ["SafeArea", "Column", "Text", "OnScrollEvent", "AppBar", "Row", "Container"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/scroll_mode/showcase.py b/sdk/python/examples/controls/types/scroll_mode/showcase.py deleted file mode 100644 index 1964e2f87b..0000000000 --- a/sdk/python/examples/controls/types/scroll_mode/showcase.py +++ /dev/null @@ -1,50 +0,0 @@ -import flet as ft - - -def showcase_card(scroll_mode: ft.ScrollMode) -> ft.Container: - return ft.Container( - width=280, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(scroll_mode.name, weight=ft.FontWeight.BOLD), - ft.Text("Scroll inside this panel", size=12), - ft.Container( - height=170, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - padding=8, - content=ft.Column( - spacing=4, - scroll=scroll_mode, - controls=[ft.Text(f"Item {i + 1}") for i in range(24)], - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="ScrollMode Showcase") - page.add( - ft.Text("Compare scrollbar visibility and scrolling behavior modes."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(scroll_mode) for scroll_mode in ft.ScrollMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_mode/showcase/main.py b/sdk/python/examples/controls/types/scroll_mode/showcase/main.py new file mode 100644 index 0000000000..28fb6062ac --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_mode/showcase/main.py @@ -0,0 +1,61 @@ +import flet as ft + + +def showcase_card(scroll_mode: ft.ScrollMode) -> ft.Container: + return ft.Container( + width=280, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(scroll_mode.name, weight=ft.FontWeight.BOLD), + ft.Text("Scroll inside this panel", size=12), + ft.Container( + height=170, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + padding=8, + content=ft.Column( + spacing=4, + scroll=scroll_mode, + controls=[ft.Text(f"Item {i + 1}") for i in range(24)], + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="ScrollMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare scrollbar visibility and scrolling behavior modes." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(scroll_mode) for scroll_mode in ft.ScrollMode + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/scroll_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..43e22593b7 --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-scroll-mode-showcase" +version = "1.0.0" +description = "Compares Scroll Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["scroll mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Scroll Mode showcase" +controls = ["SafeArea", "Column", "Container", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/scroll_type/showcase.py b/sdk/python/examples/controls/types/scroll_type/showcase.py deleted file mode 100644 index f08cdaf0be..0000000000 --- a/sdk/python/examples/controls/types/scroll_type/showcase.py +++ /dev/null @@ -1,50 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - counts = {event_type: 0 for event_type in ft.ScrollType} - count_labels = { - event_type: ft.Text(f"{event_type.name}: 0") for event_type in ft.ScrollType - } - - def on_scroll(e: ft.OnScrollEvent): - counts[e.event_type] += 1 - count_labels[ - e.event_type - ].value = f"{e.event_type.name}: {counts[e.event_type]}" - last.value = ( - f"Last event: {e.event_type.name}, pixels={round(e.pixels, 1)}" - f"{', direction=' if e.direction else ''}" - ) - for label in count_labels.values(): - label.update() - last.update() - - page.appbar = ft.AppBar(title="ScrollType Showcase") - page.add( - ft.Text("Scroll the list to generate start/update/user/end notifications."), - last := ft.Text(), - ft.Row( - wrap=True, - spacing=10, - controls=[count_labels[t] for t in ft.ScrollType], - alignment=ft.MainAxisAlignment.CENTER, - ), - ft.Container( - width=360, - height=240, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - padding=8, - content=ft.Column( - scroll=ft.ScrollMode.ALWAYS, - on_scroll=on_scroll, - controls=[ft.Text(f"Scrollable item {i + 1}") for i in range(60)], - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_type/showcase/main.py b/sdk/python/examples/controls/types/scroll_type/showcase/main.py new file mode 100644 index 0000000000..919aa2a67a --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_type/showcase/main.py @@ -0,0 +1,62 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + counts = {event_type: 0 for event_type in ft.ScrollType} + count_labels = { + event_type: ft.Text(f"{event_type.name}: 0") for event_type in ft.ScrollType + } + + def on_scroll(e: ft.OnScrollEvent): + counts[e.event_type] += 1 + count_labels[ + e.event_type + ].value = f"{e.event_type.name}: {counts[e.event_type]}" + last.value = ( + f"Last event: {e.event_type.name}, pixels={round(e.pixels, 1)}" + f"{', direction=' if e.direction else ''}" + ) + for label in count_labels.values(): + label.update() + last.update() + + page.appbar = ft.AppBar(title="ScrollType Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Scroll the list to generate start/update/user/end " + "notifications." + ), + last := ft.Text(), + ft.Row( + wrap=True, + spacing=10, + controls=[count_labels[t] for t in ft.ScrollType], + alignment=ft.MainAxisAlignment.CENTER, + ), + ft.Container( + width=360, + height=240, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + padding=8, + content=ft.Column( + scroll=ft.ScrollMode.ALWAYS, + on_scroll=on_scroll, + controls=[ + ft.Text(f"Scrollable item {i + 1}") for i in range(60) + ], + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/scroll_type/showcase/pyproject.toml b/sdk/python/examples/controls/types/scroll_type/showcase/pyproject.toml new file mode 100644 index 0000000000..8c83f4e473 --- /dev/null +++ b/sdk/python/examples/controls/types/scroll_type/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-scroll-type-showcase" +version = "1.0.0" +description = "Compares Scroll Type values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["scroll type", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Scroll Type showcase" +controls = ["SafeArea", "Column", "Text", "OnScrollEvent", "AppBar", "Row", "Container"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/slider_interaction/showcase.py b/sdk/python/examples/controls/types/slider_interaction/showcase.py deleted file mode 100644 index 12481437a6..0000000000 --- a/sdk/python/examples/controls/types/slider_interaction/showcase.py +++ /dev/null @@ -1,60 +0,0 @@ -import flet as ft - - -def showcase_card(interaction: ft.SliderInteraction) -> ft.Container: - value_text = ft.Text("Value: 50") - - def on_change(e: ft.Event[ft.Slider]): - value_text.value = f"Value: {round(e.control.value)}" - value_text.update() - - return ft.Container( - width=340, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(interaction.name, weight=ft.FontWeight.BOLD), - value_text, - ft.Container( - width=260, - padding=ft.Padding.symmetric(horizontal=8), - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.Slider( - min=0, - max=100, - value=50, - divisions=20, - interaction=interaction, - on_change=on_change, - ), - ), - ft.Text("Try tapping track and dragging thumb.", size=11), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="SliderInteraction Showcase") - page.add( - ft.Text("Compare which gestures are accepted by each slider mode."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(i) for i in ft.SliderInteraction], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/slider_interaction/showcase/main.py b/sdk/python/examples/controls/types/slider_interaction/showcase/main.py new file mode 100644 index 0000000000..1f10f9589c --- /dev/null +++ b/sdk/python/examples/controls/types/slider_interaction/showcase/main.py @@ -0,0 +1,67 @@ +import flet as ft + + +def showcase_card(interaction: ft.SliderInteraction) -> ft.Container: + value_text = ft.Text("Value: 50") + + def on_change(e: ft.Event[ft.Slider]): + value_text.value = f"Value: {round(e.control.value)}" + value_text.update() + + return ft.Container( + width=340, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(interaction.name, weight=ft.FontWeight.BOLD), + value_text, + ft.Container( + width=260, + padding=ft.Padding.symmetric(horizontal=8), + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.Slider( + min=0, + max=100, + value=50, + divisions=20, + interaction=interaction, + on_change=on_change, + ), + ), + ft.Text("Try tapping track and dragging thumb.", size=11), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="SliderInteraction Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare which gestures are accepted by each slider mode."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(i) for i in ft.SliderInteraction], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/slider_interaction/showcase/pyproject.toml b/sdk/python/examples/controls/types/slider_interaction/showcase/pyproject.toml new file mode 100644 index 0000000000..f88c501c93 --- /dev/null +++ b/sdk/python/examples/controls/types/slider_interaction/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-slider-interaction-showcase" +version = "1.0.0" +description = "Compares Slider Interaction values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["slider interaction", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Slider Interaction showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Slider", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/snack_bar_behavior/showcase.py b/sdk/python/examples/controls/types/snack_bar_behavior/showcase.py deleted file mode 100644 index a2c6e95bf9..0000000000 --- a/sdk/python/examples/controls/types/snack_bar_behavior/showcase.py +++ /dev/null @@ -1,51 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def open_snack(behavior: ft.SnackBarBehavior): - page.show_dialog( - ft.SnackBar( - content=ft.Text(f"Behavior: {behavior.name}"), - behavior=behavior, - margin=ft.Margin.only(bottom=100), - action=ft.SnackBarAction(label="Close"), - ) - ) - - def showcase_card(behavior: ft.SnackBarBehavior) -> ft.Container: - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(behavior.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Open SnackBar", - icon=ft.Icons.MESSAGE, - on_click=lambda _, b=behavior: open_snack(b), - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="SnackBarBehavior Showcase") - page.add( - ft.Text("Compare snack bar placement: fixed vs floating."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(behavior) for behavior in ft.SnackBarBehavior], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/snack_bar_behavior/showcase/main.py b/sdk/python/examples/controls/types/snack_bar_behavior/showcase/main.py new file mode 100644 index 0000000000..b8ef114c9b --- /dev/null +++ b/sdk/python/examples/controls/types/snack_bar_behavior/showcase/main.py @@ -0,0 +1,60 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def open_snack(behavior: ft.SnackBarBehavior): + page.show_dialog( + ft.SnackBar( + content=ft.Text(f"Behavior: {behavior.name}"), + behavior=behavior, + margin=ft.Margin.only(bottom=100), + action=ft.SnackBarAction(label="Close"), + ) + ) + + def showcase_card(behavior: ft.SnackBarBehavior) -> ft.Container: + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(behavior.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Open SnackBar", + icon=ft.Icons.MESSAGE, + on_click=lambda _, b=behavior: open_snack(b), + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="SnackBarBehavior Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare snack bar placement: fixed vs floating."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(behavior) for behavior in ft.SnackBarBehavior + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/snack_bar_behavior/showcase/pyproject.toml b/sdk/python/examples/controls/types/snack_bar_behavior/showcase/pyproject.toml new file mode 100644 index 0000000000..08ef2b070b --- /dev/null +++ b/sdk/python/examples/controls/types/snack_bar_behavior/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-snack-bar-behavior-showcase" +version = "1.0.0" +description = "Compares Snack Bar Behavior values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["snack bar behavior", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Snack Bar Behavior showcase" +controls = ["SafeArea", "Column", "SnackBar", "Text", "Margin", "SnackBarAction", "Container", "Button"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/stroke_cap/showcase.py b/sdk/python/examples/controls/types/stroke_cap/showcase.py deleted file mode 100644 index 16137049a9..0000000000 --- a/sdk/python/examples/controls/types/stroke_cap/showcase.py +++ /dev/null @@ -1,69 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -def showcase_card(stroke_cap: ft.StrokeCap) -> ft.Container: - return ft.Container( - width=280, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(stroke_cap.name, weight=ft.FontWeight.BOLD), - cv.Canvas( - width=240, - height=90, - shapes=[ - cv.Line( - x1=40, - y1=45, - x2=200, - y2=45, - paint=ft.Paint( - stroke_width=24, - color=ft.Colors.PRIMARY, - stroke_cap=stroke_cap, - ), - ), - cv.Line( - x1=40, - y1=16, - x2=40, - y2=74, - paint=ft.Paint(stroke_width=2, color=ft.Colors.RED), - ), - cv.Line( - x1=200, - y1=16, - x2=200, - y2=74, - paint=ft.Paint(stroke_width=2, color=ft.Colors.RED), - ), - ], - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="StrokeCap Showcase") - page.add( - ft.Text("Compare line endings for each StrokeCap value."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(stroke_cap) for stroke_cap in ft.StrokeCap], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/stroke_cap/showcase/main.py b/sdk/python/examples/controls/types/stroke_cap/showcase/main.py new file mode 100644 index 0000000000..1629021e79 --- /dev/null +++ b/sdk/python/examples/controls/types/stroke_cap/showcase/main.py @@ -0,0 +1,78 @@ +import flet as ft +import flet.canvas as cv + + +def showcase_card(stroke_cap: ft.StrokeCap) -> ft.Container: + return ft.Container( + width=280, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(stroke_cap.name, weight=ft.FontWeight.BOLD), + cv.Canvas( + width=240, + height=90, + shapes=[ + cv.Line( + x1=40, + y1=45, + x2=200, + y2=45, + paint=ft.Paint( + stroke_width=24, + color=ft.Colors.PRIMARY, + stroke_cap=stroke_cap, + ), + ), + cv.Line( + x1=40, + y1=16, + x2=40, + y2=74, + paint=ft.Paint(stroke_width=2, color=ft.Colors.RED), + ), + cv.Line( + x1=200, + y1=16, + x2=200, + y2=74, + paint=ft.Paint(stroke_width=2, color=ft.Colors.RED), + ), + ], + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="StrokeCap Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare line endings for each StrokeCap value."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(stroke_cap) for stroke_cap in ft.StrokeCap + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/stroke_cap/showcase/pyproject.toml b/sdk/python/examples/controls/types/stroke_cap/showcase/pyproject.toml new file mode 100644 index 0000000000..e2a1be6d26 --- /dev/null +++ b/sdk/python/examples/controls/types/stroke_cap/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-stroke-cap-showcase" +version = "1.0.0" +description = "Compares Stroke Cap values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["stroke cap", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Canvas/Shapes"] + +[tool.flet.metadata] +title = "Stroke Cap showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Canvas", "Line", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/stroke_join/showcase.py b/sdk/python/examples/controls/types/stroke_join/showcase.py deleted file mode 100644 index 99269e5b49..0000000000 --- a/sdk/python/examples/controls/types/stroke_join/showcase.py +++ /dev/null @@ -1,58 +0,0 @@ -import flet as ft -import flet.canvas as cv - - -def showcase_card(stroke_join: ft.StrokeJoin) -> ft.Container: - return ft.Container( - width=280, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(stroke_join.name, weight=ft.FontWeight.BOLD), - cv.Canvas( - width=240, - height=120, - shapes=[ - cv.Path( - elements=[ - cv.Path.MoveTo(40, 95), - cv.Path.LineTo(120, 25), - cv.Path.LineTo(200, 95), - ], - paint=ft.Paint( - style=ft.PaintingStyle.STROKE, - stroke_width=24, - color=ft.Colors.PRIMARY, - stroke_cap=ft.StrokeCap.BUTT, - stroke_join=stroke_join, - ), - ) - ], - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="StrokeJoin Showcase") - page.add( - ft.Text("Compare corner rendering for each StrokeJoin value."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(stroke_join) for stroke_join in ft.StrokeJoin], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/stroke_join/showcase/main.py b/sdk/python/examples/controls/types/stroke_join/showcase/main.py new file mode 100644 index 0000000000..57a5bea667 --- /dev/null +++ b/sdk/python/examples/controls/types/stroke_join/showcase/main.py @@ -0,0 +1,67 @@ +import flet as ft +import flet.canvas as cv + + +def showcase_card(stroke_join: ft.StrokeJoin) -> ft.Container: + return ft.Container( + width=280, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(stroke_join.name, weight=ft.FontWeight.BOLD), + cv.Canvas( + width=240, + height=120, + shapes=[ + cv.Path( + elements=[ + cv.Path.MoveTo(40, 95), + cv.Path.LineTo(120, 25), + cv.Path.LineTo(200, 95), + ], + paint=ft.Paint( + style=ft.PaintingStyle.STROKE, + stroke_width=24, + color=ft.Colors.PRIMARY, + stroke_cap=ft.StrokeCap.BUTT, + stroke_join=stroke_join, + ), + ) + ], + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="StrokeJoin Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare corner rendering for each StrokeJoin value."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(stroke_join) for stroke_join in ft.StrokeJoin + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/stroke_join/showcase/pyproject.toml b/sdk/python/examples/controls/types/stroke_join/showcase/pyproject.toml new file mode 100644 index 0000000000..e11faa95c4 --- /dev/null +++ b/sdk/python/examples/controls/types/stroke_join/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-stroke-join-showcase" +version = "1.0.0" +description = "Compares Stroke Join values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["stroke join", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Canvas/Shapes"] + +[tool.flet.metadata] +title = "Stroke Join showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Canvas", "Path", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/tab_alignment/showcase.py b/sdk/python/examples/controls/types/tab_alignment/showcase.py deleted file mode 100644 index 2fedb81458..0000000000 --- a/sdk/python/examples/controls/types/tab_alignment/showcase.py +++ /dev/null @@ -1,68 +0,0 @@ -import flet as ft - - -def showcase_card(alignment: ft.TabAlignment) -> ft.Container: - scrollable = alignment != ft.TabAlignment.FILL - tabs_count = 4 - - tab_bar = ft.TabBar( - scrollable=scrollable, - tab_alignment=alignment, - tabs=[ft.Tab(label=f"Tab {i + 1}") for i in range(tabs_count)], - ) - - tab_view = ft.Container( - height=70, - alignment=ft.Alignment.CENTER, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.TabBarView( - controls=[ - ft.Container( - alignment=ft.Alignment.CENTER, - content=ft.Text(f"View {i + 1}"), - ) - for i in range(tabs_count) - ] - ), - ) - - return ft.Container( - width=380, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(alignment.name, weight=ft.FontWeight.BOLD), - ft.Tabs( - length=tabs_count, - selected_index=1, - content=ft.Column(spacing=8, controls=[tab_bar, tab_view]), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="TabAlignment Showcase") - page.add( - ft.Text("Compare how tabs are positioned within the tab bar."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(alignment) for alignment in ft.TabAlignment], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/tab_alignment/showcase/main.py b/sdk/python/examples/controls/types/tab_alignment/showcase/main.py new file mode 100644 index 0000000000..89d73b37d5 --- /dev/null +++ b/sdk/python/examples/controls/types/tab_alignment/showcase/main.py @@ -0,0 +1,77 @@ +import flet as ft + + +def showcase_card(alignment: ft.TabAlignment) -> ft.Container: + scrollable = alignment != ft.TabAlignment.FILL + tabs_count = 4 + + tab_bar = ft.TabBar( + scrollable=scrollable, + tab_alignment=alignment, + tabs=[ft.Tab(label=f"Tab {i + 1}") for i in range(tabs_count)], + ) + + tab_view = ft.Container( + height=70, + alignment=ft.Alignment.CENTER, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.TabBarView( + controls=[ + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text(f"View {i + 1}"), + ) + for i in range(tabs_count) + ] + ), + ) + + return ft.Container( + width=380, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(alignment.name, weight=ft.FontWeight.BOLD), + ft.Tabs( + length=tabs_count, + selected_index=1, + content=ft.Column(spacing=8, controls=[tab_bar, tab_view]), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="TabAlignment Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare how tabs are positioned within the tab bar."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(alignment) for alignment in ft.TabAlignment + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/tab_alignment/showcase/pyproject.toml b/sdk/python/examples/controls/types/tab_alignment/showcase/pyproject.toml new file mode 100644 index 0000000000..05fabd4b47 --- /dev/null +++ b/sdk/python/examples/controls/types/tab_alignment/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-tab-alignment-showcase" +version = "1.0.0" +description = "Compares Tab Alignment values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["tab alignment", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Navigation/Tabs"] + +[tool.flet.metadata] +title = "Tab Alignment showcase" +controls = ["SafeArea", "Column", "Container", "TabBar", "Tab", "TabBarView", "Text", "Tabs"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/tab_bar_indicator_size/showcase.py b/sdk/python/examples/controls/types/tab_bar_indicator_size/showcase.py deleted file mode 100644 index 75fc094ff7..0000000000 --- a/sdk/python/examples/controls/types/tab_bar_indicator_size/showcase.py +++ /dev/null @@ -1,77 +0,0 @@ -import flet as ft - - -def showcase_card(indicator_size: ft.TabBarIndicatorSize) -> ft.Container: - tabs_count = 3 - - tab_bar = ft.TabBar( - indicator_size=indicator_size, - indicator=ft.UnderlineTabIndicator( - border_side=ft.BorderSide(width=4, color=ft.Colors.PRIMARY), - insets=ft.Padding.only(bottom=3), - ), - tabs=[ - ft.Tab(label="A"), - ft.Tab(label="Long Label"), - ft.Tab(label="Mid"), - ], - ) - - tab_view = ft.Container( - height=70, - alignment=ft.Alignment.CENTER, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.TabBarView( - controls=[ - ft.Container( - alignment=ft.Alignment.CENTER, - content=ft.Text(f"View {i + 1}"), - ) - for i in range(tabs_count) - ] - ), - ) - - return ft.Container( - width=380, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(indicator_size.name, weight=ft.FontWeight.BOLD), - ft.Tabs( - length=tabs_count, - selected_index=1, - content=ft.Column(spacing=8, controls=[tab_bar, tab_view]), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="TabBarIndicatorSize Showcase") - page.add( - ft.Text("Compare indicator width when matching tab bounds vs label bounds."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(indicator_size) - for indicator_size in ft.TabBarIndicatorSize - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/tab_bar_indicator_size/showcase/main.py b/sdk/python/examples/controls/types/tab_bar_indicator_size/showcase/main.py new file mode 100644 index 0000000000..76b09a4ba7 --- /dev/null +++ b/sdk/python/examples/controls/types/tab_bar_indicator_size/showcase/main.py @@ -0,0 +1,87 @@ +import flet as ft + + +def showcase_card(indicator_size: ft.TabBarIndicatorSize) -> ft.Container: + tabs_count = 3 + + tab_bar = ft.TabBar( + indicator_size=indicator_size, + indicator=ft.UnderlineTabIndicator( + border_side=ft.BorderSide(width=4, color=ft.Colors.PRIMARY), + insets=ft.Padding.only(bottom=3), + ), + tabs=[ + ft.Tab(label="A"), + ft.Tab(label="Long Label"), + ft.Tab(label="Mid"), + ], + ) + + tab_view = ft.Container( + height=70, + alignment=ft.Alignment.CENTER, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.TabBarView( + controls=[ + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text(f"View {i + 1}"), + ) + for i in range(tabs_count) + ] + ), + ) + + return ft.Container( + width=380, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(indicator_size.name, weight=ft.FontWeight.BOLD), + ft.Tabs( + length=tabs_count, + selected_index=1, + content=ft.Column(spacing=8, controls=[tab_bar, tab_view]), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="TabBarIndicatorSize Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare indicator width when matching tab bounds vs " + "label bounds." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(indicator_size) + for indicator_size in ft.TabBarIndicatorSize + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/tab_bar_indicator_size/showcase/pyproject.toml b/sdk/python/examples/controls/types/tab_bar_indicator_size/showcase/pyproject.toml new file mode 100644 index 0000000000..0c37e1c753 --- /dev/null +++ b/sdk/python/examples/controls/types/tab_bar_indicator_size/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-tab-bar-indicator-size-showcase" +version = "1.0.0" +description = "Compares Tab Bar Indicator Size values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["tab bar indicator size", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Navigation/Tabs"] + +[tool.flet.metadata] +title = "Tab Bar Indicator Size showcase" +controls = ["SafeArea", "Column", "Container", "TabBar", "UnderlineTabIndicator", "Tab", "TabBarView", "Text"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/tab_indicator_animation/showcase.py b/sdk/python/examples/controls/types/tab_indicator_animation/showcase.py deleted file mode 100644 index a81654df91..0000000000 --- a/sdk/python/examples/controls/types/tab_indicator_animation/showcase.py +++ /dev/null @@ -1,76 +0,0 @@ -import flet as ft - - -def showcase_card(animation: ft.TabIndicatorAnimation) -> ft.Container: - tabs_count = 3 - - tab_bar = ft.TabBar( - indicator_animation=animation, - indicator=ft.UnderlineTabIndicator( - border_side=ft.BorderSide(width=4, color=ft.Colors.PRIMARY), - insets=ft.Padding.only(bottom=3), - ), - tabs=[ - ft.Tab(label="Home"), - ft.Tab(label="Search"), - ft.Tab(label="Profile"), - ], - ) - - tab_view = ft.Container( - height=70, - alignment=ft.Alignment.CENTER, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.TabBarView( - controls=[ - ft.Container( - alignment=ft.Alignment.CENTER, - content=ft.Text(f"View {i + 1}"), - ) - for i in range(tabs_count) - ] - ), - ) - - return ft.Container( - width=360, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(animation.name, weight=ft.FontWeight.BOLD), - ft.Tabs( - length=tabs_count, - selected_index=1, - content=ft.Column(spacing=8, controls=[tab_bar, tab_view]), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="TabIndicatorAnimation Showcase") - page.add( - ft.Text("Click tabs to compare indicator movement animation."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(animation) for animation in ft.TabIndicatorAnimation - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/tab_indicator_animation/showcase/main.py b/sdk/python/examples/controls/types/tab_indicator_animation/showcase/main.py new file mode 100644 index 0000000000..cd4faea466 --- /dev/null +++ b/sdk/python/examples/controls/types/tab_indicator_animation/showcase/main.py @@ -0,0 +1,84 @@ +import flet as ft + + +def showcase_card(animation: ft.TabIndicatorAnimation) -> ft.Container: + tabs_count = 3 + + tab_bar = ft.TabBar( + indicator_animation=animation, + indicator=ft.UnderlineTabIndicator( + border_side=ft.BorderSide(width=4, color=ft.Colors.PRIMARY), + insets=ft.Padding.only(bottom=3), + ), + tabs=[ + ft.Tab(label="Home"), + ft.Tab(label="Search"), + ft.Tab(label="Profile"), + ], + ) + + tab_view = ft.Container( + height=70, + alignment=ft.Alignment.CENTER, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.TabBarView( + controls=[ + ft.Container( + alignment=ft.Alignment.CENTER, + content=ft.Text(f"View {i + 1}"), + ) + for i in range(tabs_count) + ] + ), + ) + + return ft.Container( + width=360, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(animation.name, weight=ft.FontWeight.BOLD), + ft.Tabs( + length=tabs_count, + selected_index=1, + content=ft.Column(spacing=8, controls=[tab_bar, tab_view]), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="TabIndicatorAnimation Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Click tabs to compare indicator movement animation."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(animation) + for animation in ft.TabIndicatorAnimation + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/tab_indicator_animation/showcase/pyproject.toml b/sdk/python/examples/controls/types/tab_indicator_animation/showcase/pyproject.toml new file mode 100644 index 0000000000..9e38d6dda5 --- /dev/null +++ b/sdk/python/examples/controls/types/tab_indicator_animation/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-tab-indicator-animation-showcase" +version = "1.0.0" +description = "Compares Tab Indicator Animation values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["tab indicator animation", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Navigation/Tabs"] + +[tool.flet.metadata] +title = "Tab Indicator Animation showcase" +controls = ["SafeArea", "Column", "Container", "TabBar", "UnderlineTabIndicator", "Tab", "TabBarView", "Text"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/text_align/showcase.py b/sdk/python/examples/controls/types/text_align/showcase.py deleted file mode 100644 index 4ade0fc511..0000000000 --- a/sdk/python/examples/controls/types/text_align/showcase.py +++ /dev/null @@ -1,49 +0,0 @@ -import flet as ft - - -def showcase_card(text_align: ft.TextAlign) -> ft.Container: - return ft.Container( - width=280, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(text_align.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=240, - height=130, - padding=10, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.Text( - text_align=text_align, - value="Flet helps you build cross-platform Python apps from one codebase.", # noqa: E501 - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="TextAlign Showcase") - page.add( - ft.Text("Compare horizontal text alignment modes in the same text block."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(text_align) for text_align in ft.TextAlign], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/text_align/showcase/main.py b/sdk/python/examples/controls/types/text_align/showcase/main.py new file mode 100644 index 0000000000..5ecc9fdfba --- /dev/null +++ b/sdk/python/examples/controls/types/text_align/showcase/main.py @@ -0,0 +1,61 @@ +import flet as ft + + +def showcase_card(text_align: ft.TextAlign) -> ft.Container: + return ft.Container( + width=280, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(text_align.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=240, + height=130, + padding=10, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.Text( + text_align=text_align, + value="Flet helps you build cross-platform Python apps from one codebase.", # noqa: E501 + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="TextAlign Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare horizontal text alignment modes in the same " + "text block." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(text_align) for text_align in ft.TextAlign + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/text_align/showcase/pyproject.toml b/sdk/python/examples/controls/types/text_align/showcase/pyproject.toml new file mode 100644 index 0000000000..a11ba3fea4 --- /dev/null +++ b/sdk/python/examples/controls/types/text_align/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-text-align-showcase" +version = "1.0.0" +description = "Compares Text Align values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["text align", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Text Align showcase" +controls = ["SafeArea", "Column", "Container", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/text_capitalization/showcase.py b/sdk/python/examples/controls/types/text_capitalization/showcase.py deleted file mode 100644 index f3988cc0e1..0000000000 --- a/sdk/python/examples/controls/types/text_capitalization/showcase.py +++ /dev/null @@ -1,45 +0,0 @@ -import flet as ft - - -def showcase_card(cap: ft.TextCapitalization) -> ft.Container: - field = ft.TextField( - width=260, - label="Type here", - capitalization=cap, - border=ft.InputBorder.OUTLINE, - ) - - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(cap.name, weight=ft.FontWeight.BOLD), - field, - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="TextCapitalization Showcase") - page.add( - ft.Text("Compare keyboard capitalization preferences for text fields."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(c) for c in ft.TextCapitalization], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/text_capitalization/showcase/main.py b/sdk/python/examples/controls/types/text_capitalization/showcase/main.py new file mode 100644 index 0000000000..c0679c974a --- /dev/null +++ b/sdk/python/examples/controls/types/text_capitalization/showcase/main.py @@ -0,0 +1,54 @@ +import flet as ft + + +def showcase_card(cap: ft.TextCapitalization) -> ft.Container: + field = ft.TextField( + width=260, + label="Type here", + capitalization=cap, + border=ft.InputBorder.OUTLINE, + ) + + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(cap.name, weight=ft.FontWeight.BOLD), + field, + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="TextCapitalization Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare keyboard capitalization preferences for text fields." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(c) for c in ft.TextCapitalization], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/text_capitalization/showcase/pyproject.toml b/sdk/python/examples/controls/types/text_capitalization/showcase/pyproject.toml new file mode 100644 index 0000000000..d9f09d775c --- /dev/null +++ b/sdk/python/examples/controls/types/text_capitalization/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-text-capitalization-showcase" +version = "1.0.0" +description = "Compares Text Capitalization values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["text capitalization", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Text Capitalization showcase" +controls = ["SafeArea", "Column", "Container", "TextField", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/text_decoration_style/showcase.py b/sdk/python/examples/controls/types/text_decoration_style/showcase.py deleted file mode 100644 index c67aa2cb00..0000000000 --- a/sdk/python/examples/controls/types/text_decoration_style/showcase.py +++ /dev/null @@ -1,55 +0,0 @@ -import flet as ft - - -def showcase_card(style: ft.TextDecorationStyle) -> ft.Container: - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(style.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=240, - height=80, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - alignment=ft.Alignment.CENTER, - content=ft.Text( - "Decorated text sample", - style=ft.TextStyle( - size=22, - decoration=ft.TextDecoration.UNDERLINE, - decoration_style=style, - decoration_thickness=2.4, - decoration_color=ft.Colors.PRIMARY, - ), - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="TextDecorationStyle Showcase") - page.add( - ft.Text("Compare underline rendering for each decoration style."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(style) for style in ft.TextDecorationStyle], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/text_decoration_style/showcase/main.py b/sdk/python/examples/controls/types/text_decoration_style/showcase/main.py new file mode 100644 index 0000000000..fea90b1a74 --- /dev/null +++ b/sdk/python/examples/controls/types/text_decoration_style/showcase/main.py @@ -0,0 +1,64 @@ +import flet as ft + + +def showcase_card(style: ft.TextDecorationStyle) -> ft.Container: + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(style.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=240, + height=80, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + alignment=ft.Alignment.CENTER, + content=ft.Text( + "Decorated text sample", + style=ft.TextStyle( + size=22, + decoration=ft.TextDecoration.UNDERLINE, + decoration_style=style, + decoration_thickness=2.4, + decoration_color=ft.Colors.PRIMARY, + ), + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="TextDecorationStyle Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare underline rendering for each decoration style."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(style) for style in ft.TextDecorationStyle + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/text_decoration_style/showcase/pyproject.toml b/sdk/python/examples/controls/types/text_decoration_style/showcase/pyproject.toml new file mode 100644 index 0000000000..9f639038e5 --- /dev/null +++ b/sdk/python/examples/controls/types/text_decoration_style/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-text-decoration-style-showcase" +version = "1.0.0" +description = "Compares Text Decoration Style values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["text decoration style", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Text Decoration Style showcase" +controls = ["SafeArea", "Column", "Container", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/text_overflow/showcase.py b/sdk/python/examples/controls/types/text_overflow/showcase.py deleted file mode 100644 index 55e2ecdee7..0000000000 --- a/sdk/python/examples/controls/types/text_overflow/showcase.py +++ /dev/null @@ -1,50 +0,0 @@ -import flet as ft - - -def showcase_card(overflow: ft.TextOverflow) -> ft.Container: - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(overflow.name, weight=ft.FontWeight.BOLD), - ft.Container( - width=250, - height=46, - padding=8, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=8, - bgcolor=ft.Colors.SURFACE, - content=ft.Text( - "The quick brown fox jumps over the lazy dog near the Flet bridge.", # noqa: E501 - max_lines=1, - overflow=overflow, - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="TextOverflow Showcase") - page.add( - ft.Text("Compare how one-line text behaves when content exceeds width."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(overflow) for overflow in ft.TextOverflow], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/text_overflow/showcase/main.py b/sdk/python/examples/controls/types/text_overflow/showcase/main.py new file mode 100644 index 0000000000..461c94e0dc --- /dev/null +++ b/sdk/python/examples/controls/types/text_overflow/showcase/main.py @@ -0,0 +1,61 @@ +import flet as ft + + +def showcase_card(overflow: ft.TextOverflow) -> ft.Container: + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(overflow.name, weight=ft.FontWeight.BOLD), + ft.Container( + width=250, + height=46, + padding=8, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=8, + bgcolor=ft.Colors.SURFACE, + content=ft.Text( + "The quick brown fox jumps over the lazy dog near the Flet bridge.", # noqa: E501 + max_lines=1, + overflow=overflow, + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="TextOverflow Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare how one-line text behaves when content exceeds width." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(overflow) for overflow in ft.TextOverflow + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/text_overflow/showcase/pyproject.toml b/sdk/python/examples/controls/types/text_overflow/showcase/pyproject.toml new file mode 100644 index 0000000000..45827b6df9 --- /dev/null +++ b/sdk/python/examples/controls/types/text_overflow/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-text-overflow-showcase" +version = "1.0.0" +description = "Compares Text Overflow values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["text overflow", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Text Overflow showcase" +controls = ["SafeArea", "Column", "Container", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/theme_mode/showcase.py b/sdk/python/examples/controls/types/theme_mode/showcase.py deleted file mode 100644 index 75b9913f98..0000000000 --- a/sdk/python/examples/controls/types/theme_mode/showcase.py +++ /dev/null @@ -1,63 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.theme_mode = ft.ThemeMode.SYSTEM - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - def apply_theme_mode(theme_mode: ft.ThemeMode): - page.theme_mode = theme_mode - status.value = f"Active mode: {theme_mode.name}" - page.update() - - def showcase_card(theme_mode: ft.ThemeMode) -> ft.Container: - return ft.Container( - width=250, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(theme_mode.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Apply", - on_click=lambda _, m=theme_mode: apply_theme_mode(m), - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="ThemeMode Showcase") - page.add( - ft.Text("Switch the app theme mode and inspect the preview below."), - status := ft.Text(f"Active mode: {page.theme_mode.name}"), - ft.Container( - width=500, - padding=12, - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=10, - content=ft.Row( - alignment=ft.MainAxisAlignment.SPACE_AROUND, - controls=[ - ft.OutlinedButton("Outlined"), - ft.FilledButton("Filled"), - ft.Switch(label="Switch", value=True), - ], - ), - ), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(theme_mode) for theme_mode in ft.ThemeMode], - ), - ) - - apply_theme_mode(ft.ThemeMode.SYSTEM) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/theme_mode/showcase/main.py b/sdk/python/examples/controls/types/theme_mode/showcase/main.py new file mode 100644 index 0000000000..8534532bdd --- /dev/null +++ b/sdk/python/examples/controls/types/theme_mode/showcase/main.py @@ -0,0 +1,72 @@ +import flet as ft + + +def main(page: ft.Page): + page.theme_mode = ft.ThemeMode.SYSTEM + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def apply_theme_mode(theme_mode: ft.ThemeMode): + page.theme_mode = theme_mode + status.value = f"Active mode: {theme_mode.name}" + page.update() + + def showcase_card(theme_mode: ft.ThemeMode) -> ft.Container: + return ft.Container( + width=250, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(theme_mode.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Apply", + on_click=lambda _, m=theme_mode: apply_theme_mode(m), + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="ThemeMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Switch the app theme mode and inspect the preview below."), + status := ft.Text(f"Active mode: {page.theme_mode.name}"), + ft.Container( + width=500, + padding=12, + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=10, + content=ft.Row( + alignment=ft.MainAxisAlignment.SPACE_AROUND, + controls=[ + ft.OutlinedButton("Outlined"), + ft.FilledButton("Filled"), + ft.Switch(label="Switch", value=True), + ], + ), + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(theme_mode) for theme_mode in ft.ThemeMode + ], + ), + ], + ), + ) + ) + + apply_theme_mode(ft.ThemeMode.SYSTEM) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/theme_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/theme_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..0557b5ccac --- /dev/null +++ b/sdk/python/examples/controls/types/theme_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-theme-mode-showcase" +version = "1.0.0" +description = "Compares Theme Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["theme mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Theme Mode showcase" +controls = ["SafeArea", "Column", "Container", "Text", "Button", "AppBar", "Row", "OutlinedButton"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/tile_affinity/showcase.py b/sdk/python/examples/controls/types/tile_affinity/showcase.py deleted file mode 100644 index 90c0d5c351..0000000000 --- a/sdk/python/examples/controls/types/tile_affinity/showcase.py +++ /dev/null @@ -1,49 +0,0 @@ -import flet as ft - - -def showcase_card(affinity: ft.TileAffinity) -> ft.Container: - tile = ft.ExpansionTile( - title="Project settings", - subtitle="Compare where the expand arrow appears.", - affinity=affinity, - expanded=True, - controls=[ - ft.ListTile(title="General"), - ft.ListTile(title="Notifications"), - ], - ) - - return ft.Container( - width=360, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(affinity.name, weight=ft.FontWeight.BOLD), - tile, - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="TileAffinity Showcase") - page.add( - ft.Text("Compare expand-arrow placement in ExpansionTile."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(affinity) for affinity in ft.TileAffinity], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/tile_affinity/showcase/main.py b/sdk/python/examples/controls/types/tile_affinity/showcase/main.py new file mode 100644 index 0000000000..2ae4ca3e84 --- /dev/null +++ b/sdk/python/examples/controls/types/tile_affinity/showcase/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def showcase_card(affinity: ft.TileAffinity) -> ft.Container: + tile = ft.ExpansionTile( + title="Project settings", + subtitle="Compare where the expand arrow appears.", + affinity=affinity, + expanded=True, + controls=[ + ft.ListTile(title="General"), + ft.ListTile(title="Notifications"), + ], + ) + + return ft.Container( + width=360, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(affinity.name, weight=ft.FontWeight.BOLD), + tile, + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="TileAffinity Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Compare expand-arrow placement in ExpansionTile."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(affinity) for affinity in ft.TileAffinity + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/tile_affinity/showcase/pyproject.toml b/sdk/python/examples/controls/types/tile_affinity/showcase/pyproject.toml new file mode 100644 index 0000000000..70c6f3b9af --- /dev/null +++ b/sdk/python/examples/controls/types/tile_affinity/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-tile-affinity-showcase" +version = "1.0.0" +description = "Compares Tile Affinity values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["tile affinity", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Tile Affinity showcase" +controls = ["SafeArea", "Column", "Container", "ExpansionTile", "ListTile", "Text", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/time_picker_entry_mode/showcase.py b/sdk/python/examples/controls/types/time_picker_entry_mode/showcase.py deleted file mode 100644 index 7f13ac974b..0000000000 --- a/sdk/python/examples/controls/types/time_picker_entry_mode/showcase.py +++ /dev/null @@ -1,49 +0,0 @@ -from datetime import time - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - picker = ft.TimePicker(value=time(hour=19, minute=30)) - - def open_picker(entry_mode: ft.TimePickerEntryMode): - picker.entry_mode = entry_mode - page.show_dialog(picker) - - def showcase_card(entry_mode: ft.TimePickerEntryMode) -> ft.Container: - return ft.Container( - width=320, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(entry_mode.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Open TimePicker", - icon=ft.Icons.SCHEDULE, - on_click=lambda _, m=entry_mode: open_picker(m), - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="TimePickerEntryMode Showcase") - page.add( - ft.Text("Open the picker to compare dial/input entry modes."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(m) for m in ft.TimePickerEntryMode], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/time_picker_entry_mode/showcase/main.py b/sdk/python/examples/controls/types/time_picker_entry_mode/showcase/main.py new file mode 100644 index 0000000000..702e7e9d80 --- /dev/null +++ b/sdk/python/examples/controls/types/time_picker_entry_mode/showcase/main.py @@ -0,0 +1,56 @@ +from datetime import time + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + picker = ft.TimePicker(value=time(hour=19, minute=30)) + + def open_picker(entry_mode: ft.TimePickerEntryMode): + picker.entry_mode = entry_mode + page.show_dialog(picker) + + def showcase_card(entry_mode: ft.TimePickerEntryMode) -> ft.Container: + return ft.Container( + width=320, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(entry_mode.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Open TimePicker", + icon=ft.Icons.SCHEDULE, + on_click=lambda _, m=entry_mode: open_picker(m), + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="TimePickerEntryMode Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Open the picker to compare dial/input entry modes."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(m) for m in ft.TimePickerEntryMode], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/time_picker_entry_mode/showcase/pyproject.toml b/sdk/python/examples/controls/types/time_picker_entry_mode/showcase/pyproject.toml new file mode 100644 index 0000000000..c12e5cc6a0 --- /dev/null +++ b/sdk/python/examples/controls/types/time_picker_entry_mode/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-time-picker-entry-mode-showcase" +version = "1.0.0" +description = "Compares Time Picker Entry Mode values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["time picker entry mode", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Time Picker Entry Mode showcase" +controls = ["SafeArea", "Column", "TimePicker", "Container", "Text", "Button", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/time_picker_hour_format/showcase.py b/sdk/python/examples/controls/types/time_picker_hour_format/showcase.py deleted file mode 100644 index de4b77318d..0000000000 --- a/sdk/python/examples/controls/types/time_picker_hour_format/showcase.py +++ /dev/null @@ -1,54 +0,0 @@ -from datetime import time - -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - time_picker = ft.TimePicker( - value=time(hour=19, minute=30), - help_text="Pick meeting time", - ) - - def open_picker(hour_format: ft.TimePickerHourFormat): - time_picker.hour_format = hour_format - page.show_dialog(time_picker) - - def showcase_card(hour_format: ft.TimePickerHourFormat) -> ft.Container: - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(hour_format.name, weight=ft.FontWeight.BOLD), - ft.Button( - "Open TimePicker", - icon=ft.Icons.SCHEDULE, - on_click=lambda _, f=hour_format: open_picker(f), - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="TimePickerHourFormat Showcase") - page.add( - ft.Text("Open the picker to compare 12h, 24h, and system modes."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - showcase_card(hour_format) for hour_format in ft.TimePickerHourFormat - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/time_picker_hour_format/showcase/main.py b/sdk/python/examples/controls/types/time_picker_hour_format/showcase/main.py new file mode 100644 index 0000000000..7fe31551b1 --- /dev/null +++ b/sdk/python/examples/controls/types/time_picker_hour_format/showcase/main.py @@ -0,0 +1,62 @@ +from datetime import time + +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + time_picker = ft.TimePicker( + value=time(hour=19, minute=30), + help_text="Pick meeting time", + ) + + def open_picker(hour_format: ft.TimePickerHourFormat): + time_picker.hour_format = hour_format + page.show_dialog(time_picker) + + def showcase_card(hour_format: ft.TimePickerHourFormat) -> ft.Container: + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(hour_format.name, weight=ft.FontWeight.BOLD), + ft.Button( + "Open TimePicker", + icon=ft.Icons.SCHEDULE, + on_click=lambda _, f=hour_format: open_picker(f), + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="TimePickerHourFormat Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Open the picker to compare 12h, 24h, and system modes."), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + showcase_card(hour_format) + for hour_format in ft.TimePickerHourFormat + ], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/time_picker_hour_format/showcase/pyproject.toml b/sdk/python/examples/controls/types/time_picker_hour_format/showcase/pyproject.toml new file mode 100644 index 0000000000..37c6ed004c --- /dev/null +++ b/sdk/python/examples/controls/types/time_picker_hour_format/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-time-picker-hour-format-showcase" +version = "1.0.0" +description = "Compares Time Picker Hour Format values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["time picker hour format", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Time Picker Hour Format showcase" +controls = ["SafeArea", "Column", "TimePicker", "Container", "Text", "Button", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/tooltip/with_decoration.py b/sdk/python/examples/controls/types/tooltip/with_decoration.py deleted file mode 100644 index dc035b81ca..0000000000 --- a/sdk/python/examples/controls/types/tooltip/with_decoration.py +++ /dev/null @@ -1,39 +0,0 @@ -import math - -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Text("Hover to see the simple tooltip", tooltip="This is a simple tooltip"), - ft.Text( - value="Hover to see the complex tooltip", - tooltip=ft.Tooltip( - message="This is a complex tooltip", - padding=20, - text_style=ft.TextStyle(size=20, color=ft.Colors.WHITE), - decoration=ft.BoxDecoration( - border_radius=10, - gradient=ft.LinearGradient( - begin=ft.Alignment.TOP_LEFT, - end=ft.Alignment(0.8, 1), - tile_mode=ft.GradientTileMode.MIRROR, - rotation=math.pi / 3, - colors=[ - "0xff1f005c", - "0xff5b0060", - "0xff870160", - "0xffac255e", - "0xffca485c", - "0xffe16b5c", - "0xfff39060", - "0xffffb56b", - ], - ), - ), - ), - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/tooltip/with_decoration/main.py b/sdk/python/examples/controls/types/tooltip/with_decoration/main.py new file mode 100644 index 0000000000..0aa6566bc5 --- /dev/null +++ b/sdk/python/examples/controls/types/tooltip/with_decoration/main.py @@ -0,0 +1,49 @@ +import math + +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Hover to see the simple tooltip", + tooltip="This is a simple tooltip", + ), + ft.Text( + value="Hover to see the complex tooltip", + tooltip=ft.Tooltip( + message="This is a complex tooltip", + padding=20, + text_style=ft.TextStyle(size=20, color=ft.Colors.WHITE), + decoration=ft.BoxDecoration( + border_radius=10, + gradient=ft.LinearGradient( + begin=ft.Alignment.TOP_LEFT, + end=ft.Alignment(0.8, 1), + tile_mode=ft.GradientTileMode.MIRROR, + rotation=math.pi / 3, + colors=[ + "0xff1f005c", + "0xff5b0060", + "0xff870160", + "0xffac255e", + "0xffca485c", + "0xffe16b5c", + "0xfff39060", + "0xffffb56b", + ], + ), + ), + ), + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/tooltip/with_decoration/pyproject.toml b/sdk/python/examples/controls/types/tooltip/with_decoration/pyproject.toml new file mode 100644 index 0000000000..00c209a71e --- /dev/null +++ b/sdk/python/examples/controls/types/tooltip/with_decoration/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-tooltip-with-decoration" +version = "1.0.0" +description = "Shows simple and decorated tooltips with custom gradient styling." +requires-python = ">=3.10" +keywords = ["tooltip", "with decoration"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Displays/Tooltip"] + +[tool.flet.metadata] +title = "Tooltip with decoration" +controls = ["SafeArea", "Column", "Text", "Tooltip", "BoxDecoration"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["tooltip styling", "gradient decoration"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/url_target/showcase.py b/sdk/python/examples/controls/types/url_target/showcase.py deleted file mode 100644 index 979c826adb..0000000000 --- a/sdk/python/examples/controls/types/url_target/showcase.py +++ /dev/null @@ -1,49 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - async def open_url(target: ft.UrlTarget): - url = "https://flet.dev" - await ft.UrlLauncher().launch_url(ft.Url(url=url, target=target)) - status.value = f"Opened {url} with target {target.name}" - status.update() - - def showcase_card(target: ft.UrlTarget) -> ft.Container: - return ft.Container( - width=280, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=ft.Column( - spacing=8, - controls=[ - ft.Text(target.name, weight=ft.FontWeight.BOLD), - ft.Text(target.value, size=12, color=ft.Colors.ON_SURFACE_VARIANT), - ft.Button( - "Open flet.dev", - icon=ft.Icons.OPEN_IN_NEW, - on_click=lambda: page.run_task(open_url, target), - ), - ], - ), - ) - - page.appbar = ft.AppBar(title="UrlTarget Showcase") - page.add( - ft.Text("Click a card to launch URL with the selected browser target."), - status := ft.Text(), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[showcase_card(target) for target in ft.UrlTarget], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/url_target/showcase/main.py b/sdk/python/examples/controls/types/url_target/showcase/main.py new file mode 100644 index 0000000000..f77405a13e --- /dev/null +++ b/sdk/python/examples/controls/types/url_target/showcase/main.py @@ -0,0 +1,58 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + async def open_url(target: ft.UrlTarget): + url = "https://flet.dev" + await ft.UrlLauncher().launch_url(ft.Url(url=url, target=target)) + status.value = f"Opened {url} with target {target.name}" + status.update() + + def showcase_card(target: ft.UrlTarget) -> ft.Container: + return ft.Container( + width=280, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=ft.Column( + spacing=8, + controls=[ + ft.Text(target.name, weight=ft.FontWeight.BOLD), + ft.Text(target.value, size=12, color=ft.Colors.ON_SURFACE_VARIANT), + ft.Button( + "Open flet.dev", + icon=ft.Icons.OPEN_IN_NEW, + on_click=lambda: page.run_task(open_url, target), + ), + ], + ), + ) + + page.appbar = ft.AppBar(title="UrlTarget Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Click a card to launch URL with the selected browser target." + ), + status := ft.Text(), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[showcase_card(target) for target in ft.UrlTarget], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/url_target/showcase/pyproject.toml b/sdk/python/examples/controls/types/url_target/showcase/pyproject.toml new file mode 100644 index 0000000000..d887d3879f --- /dev/null +++ b/sdk/python/examples/controls/types/url_target/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-url-target-showcase" +version = "1.0.0" +description = "Compares URL Target values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["url target", "showcase", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "URL Target showcase" +controls = ["SafeArea", "Column", "UrlLauncher", "Container", "Text", "Button", "AppBar", "Row"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/visual_density/showcase.py b/sdk/python/examples/controls/types/visual_density/showcase.py deleted file mode 100644 index 6f26af3a78..0000000000 --- a/sdk/python/examples/controls/types/visual_density/showcase.py +++ /dev/null @@ -1,53 +0,0 @@ -import flet as ft - - -def density_card(visual_density: ft.VisualDensity) -> ft.Container: - return ft.Container( - width=300, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - theme=ft.Theme(visual_density=visual_density), - dark_theme=ft.Theme(visual_density=visual_density), - content=ft.Column( - spacing=8, - controls=[ - ft.Text(visual_density.name, weight=ft.FontWeight.BOLD), - ft.IconButton(icon=ft.Icons.ADD), - ft.Checkbox(label="Checkbox", value=True), - ft.Chip( - label="Explore topics", - leading=ft.Icon(ft.Icons.EXPLORE_OUTLINED), - ), - ft.RadioGroup( - value="1", - content=ft.Row( - controls=[ - ft.Radio(label=f"{i}", value=f"{i}") for i in range(1, 4) - ], - ), - ), - ], - ), - ) - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - page.appbar = ft.AppBar(title="VisualDensity Showcase") - page.add( - ft.Text("Compare component density presets across Material controls."), - ft.Row( - wrap=True, - spacing=12, - expand=True, - scroll=ft.ScrollMode.AUTO, - alignment=ft.MainAxisAlignment.CENTER, - controls=[density_card(vd) for vd in ft.VisualDensity], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/visual_density/showcase/main.py b/sdk/python/examples/controls/types/visual_density/showcase/main.py new file mode 100644 index 0000000000..e95d06311e --- /dev/null +++ b/sdk/python/examples/controls/types/visual_density/showcase/main.py @@ -0,0 +1,62 @@ +import flet as ft + + +def density_card(visual_density: ft.VisualDensity) -> ft.Container: + return ft.Container( + width=300, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + theme=ft.Theme(visual_density=visual_density), + dark_theme=ft.Theme(visual_density=visual_density), + content=ft.Column( + spacing=8, + controls=[ + ft.Text(visual_density.name, weight=ft.FontWeight.BOLD), + ft.IconButton(icon=ft.Icons.ADD), + ft.Checkbox(label="Checkbox", value=True), + ft.Chip( + label="Explore topics", + leading=ft.Icon(ft.Icons.EXPLORE_OUTLINED), + ), + ft.RadioGroup( + value="1", + content=ft.Row( + controls=[ + ft.Radio(label=f"{i}", value=f"{i}") for i in range(1, 4) + ], + ), + ), + ], + ), + ) + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + page.appbar = ft.AppBar(title="VisualDensity Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Compare component density presets across Material controls." + ), + ft.Row( + wrap=True, + spacing=12, + expand=True, + scroll=ft.ScrollMode.AUTO, + alignment=ft.MainAxisAlignment.CENTER, + controls=[density_card(vd) for vd in ft.VisualDensity], + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/visual_density/showcase/pyproject.toml b/sdk/python/examples/controls/types/visual_density/showcase/pyproject.toml new file mode 100644 index 0000000000..9304ed08ae --- /dev/null +++ b/sdk/python/examples/controls/types/visual_density/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-visual-density-showcase" +version = "1.0.0" +description = "Compares Visual Density values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["visual density", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Visual Density showcase" +controls = ["SafeArea", "Column", "Container", "Theme", "Text", "IconButton", "Checkbox", "Chip"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/types/window_event_type/showcase.py b/sdk/python/examples/controls/types/window_event_type/showcase.py deleted file mode 100644 index c6a4584a7b..0000000000 --- a/sdk/python/examples/controls/types/window_event_type/showcase.py +++ /dev/null @@ -1,55 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - log = ft.Column( - spacing=4, - scroll=ft.ScrollMode.AUTO, - height=220, - ) - - def add_log(message: str): - log.controls.insert(0, ft.Text(message, size=12)) - if len(log.controls) > 20: - log.controls.pop() - log.update() - - def on_window_event(e: ft.WindowEvent): - add_log(f"Received: {e.type.name}") - - page.window.on_event = on_window_event - - page.appbar = ft.AppBar(title="WindowEventType Showcase") - page.add( - ft.Text("Interact with the app window and watch incoming event types."), - ft.Text( - "Desktop only. Try focus, blur, resize, move, minimize, maximize.", - size=11, - ), - ft.Row( - wrap=True, - spacing=8, - controls=[ - ft.Container( - padding=ft.Padding.symmetric(horizontal=8, vertical=4), - border=ft.Border.all(1, ft.Colors.OUTLINE), - border_radius=12, - content=ft.Text(event_type.name, size=11), - ) - for event_type in ft.WindowEventType - ], - ), - ft.Container( - width=720, - padding=12, - border=ft.Border.all(1, ft.Colors.RED), - border_radius=10, - bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, - content=log, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/types/window_event_type/showcase/main.py b/sdk/python/examples/controls/types/window_event_type/showcase/main.py new file mode 100644 index 0000000000..f55feb9479 --- /dev/null +++ b/sdk/python/examples/controls/types/window_event_type/showcase/main.py @@ -0,0 +1,65 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + log = ft.Column( + spacing=4, + scroll=ft.ScrollMode.AUTO, + height=220, + ) + + def add_log(message: str): + log.controls.insert(0, ft.Text(message, size=12)) + if len(log.controls) > 20: + log.controls.pop() + log.update() + + def on_window_event(e: ft.WindowEvent): + add_log(f"Received: {e.type.name}") + + page.window.on_event = on_window_event + + page.appbar = ft.AppBar(title="WindowEventType Showcase") + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Interact with the app window and watch incoming event types." + ), + ft.Text( + "Desktop only. Try focus, blur, resize, move, " + "minimize, maximize.", + size=11, + ), + ft.Row( + wrap=True, + spacing=8, + controls=[ + ft.Container( + padding=ft.Padding.symmetric(horizontal=8, vertical=4), + border=ft.Border.all(1, ft.Colors.OUTLINE), + border_radius=12, + content=ft.Text(event_type.name, size=11), + ) + for event_type in ft.WindowEventType + ], + ), + ft.Container( + width=720, + padding=12, + border=ft.Border.all(1, ft.Colors.RED), + border_radius=10, + bgcolor=ft.Colors.SURFACE_CONTAINER_LOW, + content=log, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/types/window_event_type/showcase/pyproject.toml b/sdk/python/examples/controls/types/window_event_type/showcase/pyproject.toml new file mode 100644 index 0000000000..fb22960c3e --- /dev/null +++ b/sdk/python/examples/controls/types/window_event_type/showcase/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "types-window-event-type-showcase" +version = "1.0.0" +description = "Compares Window Event Type values by applying them to a live control preview." +requires-python = ">=3.10" +keywords = ["window event type", "showcase"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Utility/Types", "Layout/Container"] + +[tool.flet.metadata] +title = "Window Event Type showcase" +controls = ["SafeArea", "Column", "Text", "AppBar", "Row", "Container"] +layout_pattern = "gallery" +complexity = "basic" +features = ["enum comparison", "live preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/vertical_divider/__init__.py b/sdk/python/examples/controls/vertical_divider/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/vertical_divider/basic.py b/sdk/python/examples/controls/vertical_divider/basic.py deleted file mode 100644 index ec017dda0a..0000000000 --- a/sdk/python/examples/controls/vertical_divider/basic.py +++ /dev/null @@ -1,40 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.add( - ft.Row( - width=180, - height=100, - spacing=0, - controls=[ - ft.Container( - bgcolor=ft.Colors.ORANGE_300, - alignment=ft.Alignment.CENTER, - expand=True, - ), - ft.VerticalDivider(), - ft.Container( - bgcolor=ft.Colors.BROWN_400, - alignment=ft.Alignment.CENTER, - expand=True, - ), - ft.VerticalDivider(width=1, color=ft.Colors.WHITE), - ft.Container( - bgcolor=ft.Colors.BLUE_300, - alignment=ft.Alignment.CENTER, - expand=True, - ), - ft.VerticalDivider(width=9, thickness=3), - ft.Container( - bgcolor=ft.Colors.GREEN_300, - alignment=ft.Alignment.CENTER, - expand=True, - ), - ], - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/vertical_divider/basic/main.py b/sdk/python/examples/controls/vertical_divider/basic/main.py new file mode 100644 index 0000000000..5bbe82841f --- /dev/null +++ b/sdk/python/examples/controls/vertical_divider/basic/main.py @@ -0,0 +1,42 @@ +import flet as ft + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + content=ft.Row( + width=180, + height=100, + spacing=0, + controls=[ + ft.Container( + bgcolor=ft.Colors.ORANGE_300, + alignment=ft.Alignment.CENTER, + expand=True, + ), + ft.VerticalDivider(), + ft.Container( + bgcolor=ft.Colors.BROWN_400, + alignment=ft.Alignment.CENTER, + expand=True, + ), + ft.VerticalDivider(width=1, color=ft.Colors.WHITE), + ft.Container( + bgcolor=ft.Colors.BLUE_300, + alignment=ft.Alignment.CENTER, + expand=True, + ), + ft.VerticalDivider(width=9, thickness=3), + ft.Container( + bgcolor=ft.Colors.GREEN_300, + alignment=ft.Alignment.CENTER, + expand=True, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/vertical_divider/basic/pyproject.toml b/sdk/python/examples/controls/vertical_divider/basic/pyproject.toml new file mode 100644 index 0000000000..776000ff46 --- /dev/null +++ b/sdk/python/examples/controls/vertical_divider/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "vertical-divider-basic" +version = "1.0.0" +description = "Separates colored containers in a row using VerticalDivider variants." +requires-python = ">=3.10" +keywords = ["vertical divider", "layout", "row", "spacing", "divider"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/VerticalDivider"] + +[tool.flet.metadata] +title = "VerticalDivider basic" +controls = ["SafeArea", "Row", "VerticalDivider", "Container"] +layout_pattern = "single-row" +complexity = "basic" +features = ["divider width and thickness variants"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/video/example_1.py b/sdk/python/examples/controls/video/example_1.py deleted file mode 100644 index 49b67cd638..0000000000 --- a/sdk/python/examples/controls/video/example_1.py +++ /dev/null @@ -1,140 +0,0 @@ -import random - -import flet as ft -import flet_video as ftv - -sample_media = [ - ftv.VideoMedia( - "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4" - ), - ftv.VideoMedia( - "https://user-images.githubusercontent.com/28951144/229373718-86ce5e1d-d195-45d5-baa6-ef94041d0b90.mp4" - ), - ftv.VideoMedia( - "https://user-images.githubusercontent.com/28951144/229373716-76da0a4e-225a-44e4-9ee7-3e9006dbc3e3.mp4" - ), - ftv.VideoMedia( - "https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4" - ), - ftv.VideoMedia( - "https://user-images.githubusercontent.com/28951144/229373709-603a7a89-2105-4e1b-a5a5-a6c3567c9a59.mp4", - extras={ - "artist": "Thousand Foot Krutch", - "album": "The End Is Where We Begin", - }, - http_headers={ - "Foo": "Bar", - "Accept": "*/*", - }, - ), -] - - -def main(page: ft.Page): - page.spacing = 20 - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - async def handle_pause(e: ft.Event[ft.Button]): - await video.pause() - - async def handle_play_or_pause(e: ft.Event[ft.Button]): - await video.play_or_pause() - - async def handle_play(e: ft.Event[ft.Button]): - await video.play() - - async def handle_stop(e: ft.Event[ft.Button]): - await video.stop() - - async def handle_next(e: ft.Event[ft.Button]): - await video.next() - - async def handle_previous(e: ft.Event[ft.Button]): - await video.previous() - - def handle_volume_change(e: ft.Event[ft.Slider]): - video.volume = e.control.value - - def handle_playback_rate_change(e: ft.Event[ft.Slider]): - video.playback_rate = e.control.value - - async def handle_seek(e: ft.Event[ft.Button]): - await video.seek(10000) - - async def handle_add_media(e: ft.Event[ft.Button]): - await video.playlist_add(random.choice(sample_media)) - - async def handle_remove_media(e: ft.Event[ft.Button]): - r = random.randint(0, len(video.playlist) - 1) - await video.playlist_remove(r) - - async def handle_jump(e: ft.Event[ft.Button]): - await video.jump_to(0) - - async def handle_fullscreen(e: ft.Event[ft.Button]): - video.fullscreen = True - - page.add( - ft.SafeArea( - expand=True, - content=ft.Column( - expand=True, - controls=[ - video := ftv.Video( - expand=True, - playlist=sample_media[0:2], - playlist_mode=ftv.PlaylistMode.LOOP, - fill_color=ft.Colors.BLUE_400, - aspect_ratio=16 / 9, - volume=100, - autoplay=False, - filter_quality=ft.FilterQuality.HIGH, - muted=False, - on_load=lambda e: print("Video loaded successfully!"), - on_enter_fullscreen=lambda e: print("Entered fullscreen!"), - on_exit_fullscreen=lambda e: print("Exited fullscreen!"), - ), - ft.Row( - wrap=True, - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Button("Play", on_click=handle_play), - ft.Button("Pause", on_click=handle_pause), - ft.Button("Play Or Pause", on_click=handle_play_or_pause), - ft.Button("Stop", on_click=handle_stop), - ft.Button("Next", on_click=handle_next), - ft.Button("Previous", on_click=handle_previous), - ft.Button("Seek s=10", on_click=handle_seek), - ft.Button("Jump to first Media", on_click=handle_jump), - ft.Button("Add Random Media", on_click=handle_add_media), - ft.Button( - "Remove Random Media", on_click=handle_remove_media - ), - ft.Button("Enter Fullscreen", on_click=handle_fullscreen), - ], - ), - ft.Slider( - min=0, - value=100, - max=100, - label="Volume = {value}%", - divisions=10, - width=400, - on_change=handle_volume_change, - ), - ft.Slider( - min=1, - value=1, - max=3, - label="Playback rate = {value}X", - divisions=6, - width=400, - on_change=handle_playback_rate_change, - ), - ], - ), - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/video/example_1/main.py b/sdk/python/examples/controls/video/example_1/main.py new file mode 100644 index 0000000000..16e0cada1e --- /dev/null +++ b/sdk/python/examples/controls/video/example_1/main.py @@ -0,0 +1,141 @@ +import random + +import flet as ft +import flet_video as ftv + +sample_media = [ + ftv.VideoMedia( + "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4" + ), + ftv.VideoMedia( + "https://user-images.githubusercontent.com/28951144/229373718-86ce5e1d-d195-45d5-baa6-ef94041d0b90.mp4" + ), + ftv.VideoMedia( + "https://user-images.githubusercontent.com/28951144/229373716-76da0a4e-225a-44e4-9ee7-3e9006dbc3e3.mp4" + ), + ftv.VideoMedia( + "https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4" + ), + ftv.VideoMedia( + "https://user-images.githubusercontent.com/28951144/229373709-603a7a89-2105-4e1b-a5a5-a6c3567c9a59.mp4", + extras={ + "artist": "Thousand Foot Krutch", + "album": "The End Is Where We Begin", + }, + http_headers={ + "Foo": "Bar", + "Accept": "*/*", + }, + ), +] + + +def main(page: ft.Page): + page.spacing = 20 + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + async def handle_pause(e: ft.Event[ft.Button]): + await video.pause() + + async def handle_play_or_pause(e: ft.Event[ft.Button]): + await video.play_or_pause() + + async def handle_play(e: ft.Event[ft.Button]): + await video.play() + + async def handle_stop(e: ft.Event[ft.Button]): + await video.stop() + + async def handle_next(e: ft.Event[ft.Button]): + await video.next() + + async def handle_previous(e: ft.Event[ft.Button]): + await video.previous() + + def handle_volume_change(e: ft.Event[ft.Slider]): + video.volume = e.control.value + + def handle_playback_rate_change(e: ft.Event[ft.Slider]): + video.playback_rate = e.control.value + + async def handle_seek(e: ft.Event[ft.Button]): + await video.seek(10000) + + async def handle_add_media(e: ft.Event[ft.Button]): + await video.playlist_add(random.choice(sample_media)) + + async def handle_remove_media(e: ft.Event[ft.Button]): + r = random.randint(0, len(video.playlist) - 1) + await video.playlist_remove(r) + + async def handle_jump(e: ft.Event[ft.Button]): + await video.jump_to(0) + + async def handle_fullscreen(e: ft.Event[ft.Button]): + video.fullscreen = True + + page.add( + ft.SafeArea( + expand=True, + content=ft.Column( + expand=True, + controls=[ + video := ftv.Video( + expand=True, + playlist=sample_media[0:2], + playlist_mode=ftv.PlaylistMode.LOOP, + fill_color=ft.Colors.BLUE_400, + aspect_ratio=16 / 9, + volume=100, + autoplay=False, + filter_quality=ft.FilterQuality.HIGH, + muted=False, + on_load=lambda e: print("Video loaded successfully!"), + on_enter_fullscreen=lambda e: print("Entered fullscreen!"), + on_exit_fullscreen=lambda e: print("Exited fullscreen!"), + ), + ft.Row( + wrap=True, + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Button("Play", on_click=handle_play), + ft.Button("Pause", on_click=handle_pause), + ft.Button("Play Or Pause", on_click=handle_play_or_pause), + ft.Button("Stop", on_click=handle_stop), + ft.Button("Next", on_click=handle_next), + ft.Button("Previous", on_click=handle_previous), + ft.Button("Seek s=10", on_click=handle_seek), + ft.Button("Jump to first Media", on_click=handle_jump), + ft.Button("Add Random Media", on_click=handle_add_media), + ft.Button( + "Remove Random Media", on_click=handle_remove_media + ), + ft.Button("Enter Fullscreen", on_click=handle_fullscreen), + ], + ), + ft.Slider( + min=0, + value=100, + max=100, + label="Volume = {value}%", + divisions=10, + width=400, + on_change=handle_volume_change, + ), + ft.Slider( + min=1, + value=1, + max=3, + label="Playback rate = {value}X", + divisions=6, + width=400, + on_change=handle_playback_rate_change, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/video/example_1/pyproject.toml b/sdk/python/examples/controls/video/example_1/pyproject.toml new file mode 100644 index 0000000000..df62d0f886 --- /dev/null +++ b/sdk/python/examples/controls/video/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "video-example-1" +version = "1.0.0" +description = "Controls video playback, playlist management, volume, and fullscreen in a Flet video player." +requires-python = ">=3.10" +keywords = ["video", "media", "playlist", "fullscreen", "player", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-video"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Media/Video"] + +[tool.flet.metadata] +title = "Video example 1" +controls = ["SafeArea", "Column", "Row", "Button", "Slider", "Video"] +layout_pattern = "dashboard" +complexity = "basic" +features = ["playlist controls", "volume control", "playback rate control", "fullscreen"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/webview/example_1.py b/sdk/python/examples/controls/webview/example_1.py deleted file mode 100644 index 32495aa5ed..0000000000 --- a/sdk/python/examples/controls/webview/example_1.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet_webview as fwv - -import flet as ft - - -def main(page: ft.Page): - page.add( - fwv.WebView( - url="https://flet.dev", - on_page_started=lambda _: print("Page started"), - on_page_ended=lambda _: print("Page ended"), - on_web_resource_error=lambda e: print("WebView error:", e.data), - expand=True, - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/controls/webview/example_1/main.py b/sdk/python/examples/controls/webview/example_1/main.py new file mode 100644 index 0000000000..7bc13ebda5 --- /dev/null +++ b/sdk/python/examples/controls/webview/example_1/main.py @@ -0,0 +1,21 @@ +import flet as ft +import flet_webview as fwv + + +def main(page: ft.Page): + page.add( + ft.SafeArea( + expand=True, + content=fwv.WebView( + url="https://flet.dev", + on_page_started=lambda _: print("Page started"), + on_page_ended=lambda _: print("Page ended"), + on_web_resource_error=lambda e: print("WebView error:", e.data), + expand=True, + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/webview/example_1/pyproject.toml b/sdk/python/examples/controls/webview/example_1/pyproject.toml new file mode 100644 index 0000000000..5bf2c696b1 --- /dev/null +++ b/sdk/python/examples/controls/webview/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "webview-example-1" +version = "1.0.0" +description = "Displays a web page in a WebView and logs page lifecycle events." +requires-python = ">=3.10" +keywords = ["webview", "browser", "web", "events", "embedded"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-webview"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Display/WebView"] + +[tool.flet.metadata] +title = "WebView example 1" +controls = ["SafeArea", "WebView"] +layout_pattern = "single-panel" +complexity = "basic" +features = ["page lifecycle events"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/controls/window_drag_area/__init__.py b/sdk/python/examples/controls/window_drag_area/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sdk/python/examples/controls/window_drag_area/no_frame_window.py b/sdk/python/examples/controls/window_drag_area/no_frame_window.py deleted file mode 100644 index d40ee59670..0000000000 --- a/sdk/python/examples/controls/window_drag_area/no_frame_window.py +++ /dev/null @@ -1,32 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.window.title_bar_hidden = True - page.window.title_bar_buttons_hidden = True - - async def handle_window_close(e: ft.Event[ft.IconButton]): - await page.window.close() - - page.add( - ft.Row( - controls=[ - ft.WindowDragArea( - expand=True, - content=ft.Container( - bgcolor=ft.Colors.AMBER_300, - padding=10, - content=ft.Text( - "Drag this area to move, maximize and " - "restore application window." - ), - ), - ), - ft.IconButton(ft.Icons.CLOSE, on_click=handle_window_close), - ] - ) - ) - - -if __name__ == "__main__": - ft.run(main) diff --git a/sdk/python/examples/controls/window_drag_area/no_frame_window/main.py b/sdk/python/examples/controls/window_drag_area/no_frame_window/main.py new file mode 100644 index 0000000000..3e403a4f3c --- /dev/null +++ b/sdk/python/examples/controls/window_drag_area/no_frame_window/main.py @@ -0,0 +1,34 @@ +import flet as ft + + +def main(page: ft.Page): + page.window.title_bar_hidden = True + page.window.title_bar_buttons_hidden = True + + async def handle_window_close(e: ft.Event[ft.IconButton]): + await page.window.close() + + page.add( + ft.SafeArea( + content=ft.Row( + controls=[ + ft.WindowDragArea( + expand=True, + content=ft.Container( + bgcolor=ft.Colors.AMBER_300, + padding=10, + content=ft.Text( + "Drag this area to move, maximize and " + "restore application window." + ), + ), + ), + ft.IconButton(ft.Icons.CLOSE, on_click=handle_window_close), + ] + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/window_drag_area/no_frame_window/pyproject.toml b/sdk/python/examples/controls/window_drag_area/no_frame_window/pyproject.toml new file mode 100644 index 0000000000..631e8805ee --- /dev/null +++ b/sdk/python/examples/controls/window_drag_area/no_frame_window/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "window-drag-area-no-frame-window" +version = "1.0.0" +description = "Creates a frameless window with a custom draggable title area and close button." +requires-python = ">=3.10" +keywords = ["window drag area", "desktop", "window", "frameless", "drag"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Layout/WindowDragArea"] + +[tool.flet.metadata] +title = "No frame window" +controls = ["SafeArea", "Row", "WindowDragArea", "Container", "Text", "IconButton"] +layout_pattern = "toolbar-content" +complexity = "basic" +features = ["frameless window", "custom window dragging"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/publish-gallery.sh b/sdk/python/examples/publish-gallery.sh index 592eb87a5f..dd6e97bb0b 100644 --- a/sdk/python/examples/publish-gallery.sh +++ b/sdk/python/examples/publish-gallery.sh @@ -1,12 +1,12 @@ flet --version DIST_PATH=$PWD/dist -flet publish apps/todo/todo.py --distpath $DIST_PATH/todo --base-url todo --app-name "Flet To-Do" --app-description "A classic To-Do app inspired by TodoMVC project." +flet publish apps/todo/basic/main.py --distpath $DIST_PATH/todo --base-url todo --app-name "Flet To-Do" --app-description "A classic To-Do app inspired by TodoMVC project." flet publish apps/icons_browser/main.py --distpath $DIST_PATH/icons_browser --base-url icons_browser --app-name "Flet Icons Browser" --app-description "Quickly search through icons collection to use in your app." flet publish tutorials/calculator/calc.py --distpath $DIST_PATH/calculator --base-url calculator --app-name "Calculator" --app-description "A simple calculator app written in Flet." flet publish tutorials/solitaire_declarative/solitaire-final/main.py --distpath $DIST_PATH/solitaire --base-url solitaire --assets assets --app-name "Solitaire" --app-description "Learn how to handle gestures and position controls on a page." -flet publish apps/trolli-declarative/src/main.py --distpath $DIST_PATH/trolli --base-url trolli --assets assets --route-url-strategy "hash" --app-name "Trolli" --app-description "A clone of Trello." -flet publish apps/routing_navigation/home_store.py --distpath $DIST_PATH/simple_routing --base-url simple_routing --route-url-strategy "hash" --app-name "Flet routing example" --app-description "An example of routing in Flet." -flet publish apps/counter/counter.py --distpath $DIST_PATH/counter --base-url counter --app-name "Counter" --app-description "Counter to get an idea of Flet." +flet publish apps/declarative/trolli/main.py --distpath $DIST_PATH/trolli --base-url trolli --assets assets --route-url-strategy "hash" --app-name "Trolli" --app-description "A clone of Trello." +flet publish apps/routing_navigation/home_store/main.py --distpath $DIST_PATH/simple_routing --base-url simple_routing --route-url-strategy "hash" --app-name "Flet routing example" --app-description "An example of routing in Flet." +flet publish apps/counter/basic/main.py --distpath $DIST_PATH/counter --base-url counter --app-name "Counter" --app-description "Counter to get an idea of Flet." flet publish apps/flet_animation/main.py --distpath $DIST_PATH/flet_animation --base-url flet_animation --app-name "Flet animation" --app-description "An example of implicit animations in Flet." -flet publish apps/greeter/greeter.py --distpath $DIST_PATH/greeter --base-url greeter --app-name "Greeter" --app-description "A basic example of interactive forms in Flet." -flet publish apps/hello_world/hello.py --distpath $DIST_PATH/hello_world --base-url hello_world --app-name "Hello, world!" --app-description "A very minimal example of Flet app." +flet publish apps/greeter/basic/main.py --distpath $DIST_PATH/greeter --base-url greeter --app-name "Greeter" --app-description "A basic example of interactive forms in Flet." +flet publish apps/hello_world/basic/main.py --distpath $DIST_PATH/hello_world --base-url hello_world --app-name "Hello, world!" --app-description "A very minimal example of Flet app." diff --git a/sdk/python/examples/services/accelerometer/basic.py b/sdk/python/examples/services/accelerometer/basic.py deleted file mode 100644 index e88f09475d..0000000000 --- a/sdk/python/examples/services/accelerometer/basic.py +++ /dev/null @@ -1,27 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_reading(e: ft.AccelerometerReadingEvent): - reading.value = f"x={e.x:.2f} m/s^2, y={e.y:.2f} m/s^2, z={e.z:.2f} m/s^2" - page.update() - - def handle_error(e: ft.SensorErrorEvent): - page.add(ft.Text(f"Accelerometer error: {e.message}")) - - page.services.append( - ft.Accelerometer( - on_reading=handle_reading, - on_error=handle_error, - interval=ft.Duration(milliseconds=100), - cancel_on_error=False, - ) - ) - - page.add( - ft.Text("Move your device to see accelerometer readings."), - reading := ft.Text("Waiting for data..."), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/accelerometer/basic/main.py b/sdk/python/examples/services/accelerometer/basic/main.py new file mode 100644 index 0000000000..3e2a60a515 --- /dev/null +++ b/sdk/python/examples/services/accelerometer/basic/main.py @@ -0,0 +1,33 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_reading(e: ft.AccelerometerReadingEvent): + reading.value = f"x={e.x:.2f} m/s^2, y={e.y:.2f} m/s^2, z={e.z:.2f} m/s^2" + + def handle_error(e: ft.SensorErrorEvent): + page.add(ft.Text(f"Accelerometer error: {e.message}")) + + page.services.append( + ft.Accelerometer( + on_reading=handle_reading, + on_error=handle_error, + interval=ft.Duration(milliseconds=100), + cancel_on_error=False, + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Move your device to see accelerometer readings."), + reading := ft.Text("Waiting for data..."), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/accelerometer/basic/pyproject.toml b/sdk/python/examples/services/accelerometer/basic/pyproject.toml new file mode 100644 index 0000000000..d1fcfb3317 --- /dev/null +++ b/sdk/python/examples/services/accelerometer/basic/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-accelerometer-basic" +version = "1.0.0" +description = "Streams live accelerometer readings and displays sensor errors from the device." +requires-python = ">=3.10" +keywords = ["services", "accelerometer", "sensor", "device", "readings"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Accelerometer"] + +[tool.flet.metadata] +title = "Basic accelerometer" +controls = ["SafeArea", "Column", "Text", "Accelerometer"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["live sensor readings", "sensor error handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["android", "ios", "web"] diff --git a/sdk/python/examples/services/audio/declarative_1.py b/sdk/python/examples/services/audio/declarative_1.py deleted file mode 100644 index 70ad139e79..0000000000 --- a/sdk/python/examples/services/audio/declarative_1.py +++ /dev/null @@ -1,43 +0,0 @@ -import flet as ft -import flet_audio as fta - -# https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3 -# https://luan.xyz/files/audio/ambient_c_motion.mp3 -# https://github.com/mdn/webaudio-examples/blob/main/audio-analyser/viper.mp3?raw=true - - -@ft.component -def App(): - duration, set_duration = ft.use_state(0.0) - position, set_position = ft.use_state(0.0) - - audio, _ = ft.use_state( - lambda: fta.Audio( - src="https://github.com/flet-dev/media/raw/refs/heads/main/sounds/sweet-life-luxury-chill-438146.mp3", - autoplay=False, - on_duration_change=lambda e: set_duration(e.duration.in_milliseconds), - on_position_change=lambda e: set_position(e.position), - ) - ) - - print("duration:", duration) - print("position:", position) - - async def play(): - await audio.play() - - async def pause(): - await audio.pause() - - async def resume(): - await audio.resume() - - return [ - ft.ProgressBar(position / duration if duration > 0 else 0), - ft.Button("Play", disabled=duration == 0, on_click=play), - ft.Button("Pause", disabled=duration == 0, on_click=pause), - ft.Button("Resume", disabled=duration == 0, on_click=resume), - ] - - -ft.run(lambda page: page.render(App)) diff --git a/sdk/python/examples/services/audio/declarative_1/main.py b/sdk/python/examples/services/audio/declarative_1/main.py new file mode 100644 index 0000000000..3c721cfd86 --- /dev/null +++ b/sdk/python/examples/services/audio/declarative_1/main.py @@ -0,0 +1,47 @@ +import flet as ft +import flet_audio as fta + + +@ft.component +def App(): + duration, set_duration = ft.use_state(0.0) + position, set_position = ft.use_state(0.0) + + audio, _ = ft.use_state( + lambda: fta.Audio( + src="https://github.com/flet-dev/media/raw/refs/heads/main/sounds/sweet-life-luxury-chill-438146.mp3", + autoplay=False, + on_duration_change=lambda e: set_duration(e.duration.in_milliseconds), + on_position_change=lambda e: set_position(e.position), + ) + ) + + ft.on_mounted(lambda: ft.context.page.services.append(audio)) + + async def play(): + await audio.play() + + async def pause(): + await audio.pause() + + async def resume(): + await audio.resume() + + return ft.SafeArea( + content=ft.Column( + controls=[ + ft.ProgressBar(position / duration if duration > 0 else 0), + ft.Button("Play", disabled=duration == 0, on_click=play), + ft.Button("Pause", disabled=duration == 0, on_click=pause), + ft.Button("Resume", disabled=duration == 0, on_click=resume), + ] + ) + ) + + +def main(page: ft.Page): + page.render(App) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/audio/declarative_1/pyproject.toml b/sdk/python/examples/services/audio/declarative_1/pyproject.toml new file mode 100644 index 0000000000..857c68174f --- /dev/null +++ b/sdk/python/examples/services/audio/declarative_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-audio-declarative-1" +version = "1.0.0" +description = "Builds a declarative audio player with playback controls and live progress updates." +requires-python = ">=3.10" +keywords = ["services", "audio", "declarative", "progress", "playback", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-audio"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Audio"] + +[tool.flet.metadata] +title = "Declarative audio player" +controls = ["SafeArea", "Column", "ProgressBar", "Button", "Audio"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["declarative state", "audio playback", "live progress updates"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/audio/example_1.py b/sdk/python/examples/services/audio/example_1.py deleted file mode 100644 index e1051c8b16..0000000000 --- a/sdk/python/examples/services/audio/example_1.py +++ /dev/null @@ -1,73 +0,0 @@ -import flet as ft -import flet_audio as fta - - -def main(page: ft.Page): - url = "https://github.com/mdn/webaudio-examples/blob/main/audio-analyser/viper.mp3?raw=true" - - async def play(): - await audio.play() - - async def pause(): - await audio.pause() - - async def resume(): - await audio.resume() - - async def release(): - await audio.release() - - def set_volume(value: float): - audio.volume += value - - def set_balance(value: float): - audio.balance += value - - async def seek_2s(): - await audio.seek(ft.Duration(seconds=2)) - - async def get_duration(): - duration = await audio.get_duration() - print("Duration:", duration) - - async def on_get_current_position(): - position = await audio.get_current_position() - print("Current position:", position) - - audio = fta.Audio( - src=url, - autoplay=False, - volume=1, - balance=0, - release_mode=fta.ReleaseMode.STOP, - on_loaded=lambda _: print("Loaded"), - on_duration_change=lambda e: print("Duration changed:", e.duration), - on_position_change=lambda e: print("Position changed:", e.position), - on_state_change=lambda e: print("State changed:", e.state), - on_seek_complete=lambda _: print("Seek complete"), - ) - - page.add( - ft.Button("Play", on_click=play), - ft.Button("Pause", on_click=pause), - ft.Button("Resume", on_click=resume), - ft.Button("Release", on_click=release), - ft.Button("Seek 2s", on_click=seek_2s), - ft.Row( - controls=[ - ft.Button("Volume down", on_click=lambda _: set_volume(-0.1)), - ft.Button("Volume up", on_click=lambda _: set_volume(0.1)), - ] - ), - ft.Row( - controls=[ - ft.Button("Balance left", on_click=lambda _: set_balance(-0.1)), - ft.Button("Balance right", on_click=lambda _: set_balance(0.1)), - ] - ), - ft.Button("Get duration", on_click=get_duration), - ft.Button("Get current position", on_click=on_get_current_position), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/audio/example_1/main.py b/sdk/python/examples/services/audio/example_1/main.py new file mode 100644 index 0000000000..eb58bbc180 --- /dev/null +++ b/sdk/python/examples/services/audio/example_1/main.py @@ -0,0 +1,93 @@ +import flet as ft +import flet_audio as fta + + +def main(page: ft.Page): + url = "https://github.com/mdn/webaudio-examples/blob/main/audio-analyser/viper.mp3?raw=true" + + async def play(): + await audio.play() + + async def pause(): + await audio.pause() + + async def resume(): + await audio.resume() + + async def release(): + await audio.release() + + def set_volume(value: float): + audio.volume += value + + def set_balance(value: float): + audio.balance += value + + async def seek_2s(): + await audio.seek(ft.Duration(seconds=2)) + + async def get_duration(): + duration = await audio.get_duration() + print("Duration:", duration) + + async def on_get_current_position(): + position = await audio.get_current_position() + print("Current position:", position) + + audio = fta.Audio( + src=url, + autoplay=False, + volume=1, + balance=0, + release_mode=fta.ReleaseMode.STOP, + on_loaded=lambda _: print("Loaded"), + on_duration_change=lambda e: print("Duration changed:", e.duration), + on_position_change=lambda e: print("Position changed:", e.position), + on_state_change=lambda e: print("State changed:", e.state), + on_seek_complete=lambda _: print("Seek complete"), + ) + page.services.append(audio) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button("Play", on_click=play), + ft.Button("Pause", on_click=pause), + ft.Button("Resume", on_click=resume), + ft.Button("Release", on_click=release), + ft.Button("Seek 2s", on_click=seek_2s), + ft.Row( + controls=[ + ft.Button( + "Volume down", + on_click=lambda _: set_volume(-0.1), + ), + ft.Button("Volume up", on_click=lambda _: set_volume(0.1)), + ] + ), + ft.Row( + controls=[ + ft.Button( + "Balance left", + on_click=lambda _: set_balance(-0.1), + ), + ft.Button( + "Balance right", + on_click=lambda _: set_balance(0.1), + ), + ] + ), + ft.Button("Get duration", on_click=get_duration), + ft.Button( + "Get current position", + on_click=on_get_current_position, + ), + ] + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/audio/example_1/pyproject.toml b/sdk/python/examples/services/audio/example_1/pyproject.toml new file mode 100644 index 0000000000..2a709202d3 --- /dev/null +++ b/sdk/python/examples/services/audio/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-audio-example-1" +version = "1.0.0" +description = "Controls audio playback, seek position, volume, and balance for a remote track." +requires-python = ">=3.10" +keywords = ["services", "audio", "playback", "seek", "volume", "balance", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-audio"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Audio"] + +[tool.flet.metadata] +title = "Audio playback controls" +controls = ["SafeArea", "Column", "Row", "Button", "Audio"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["audio playback", "seek control", "volume adjustment", "balance adjustment"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/audio_recorder/example_1.py b/sdk/python/examples/services/audio_recorder/example_1.py deleted file mode 100644 index 9a0502ec15..0000000000 --- a/sdk/python/examples/services/audio_recorder/example_1.py +++ /dev/null @@ -1,67 +0,0 @@ -import flet as ft -import flet_audio_recorder as far - - -def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.appbar = ft.AppBar(title=ft.Text("Audio Recorder"), center_title=True) - - path = "test-audio-file.wav" - - def show_snackbar(message): - page.show_dialog(ft.SnackBar(ft.Text(message))) - - async def handle_recording_start(e: ft.Event[ft.Button]): - show_snackbar("Starting recording...") - await recorder.start_recording(output_path=path) - - async def handle_recording_stop(e: ft.Event[ft.Button]): - output_path = await recorder.stop_recording() - show_snackbar(f"Stopped recording. Output Path: {output_path}") - if page.web and output_path is not None: - await page.launch_url(output_path) - - async def handle_list_devices(e: ft.Event[ft.Button]): - o = await recorder.get_input_devices() - show_snackbar(f"Input Devices: {', '.join([f'{d.id}:{d.label}' for d in o])}") - - async def handle_has_permission(e: ft.Event[ft.Button]): - try: - status = await recorder.has_permission() - show_snackbar(f"Audio Recording Permission status: {status}") - except Exception as e: - show_snackbar(f"Error checking permission: {e}") - - async def handle_pause(e: ft.Event[ft.Button]): - print(f"isRecording: {await recorder.is_recording()}") - if await recorder.is_recording(): - await recorder.pause_recording() - - async def handle_resume(e: ft.Event[ft.Button]): - print(f"isPaused: {await recorder.is_paused()}") - if await recorder.is_paused(): - await recorder.resume_recording() - - async def handle_audio_encoder_test(e: ft.Event[ft.Button]): - print(await recorder.is_supported_encoder(far.AudioEncoder.WAV)) - - recorder = far.AudioRecorder( - configuration=far.AudioRecorderConfiguration(encoder=far.AudioEncoder.WAV), - on_state_change=lambda e: print(f"State Changed: {e.data}"), - ) - - page.add( - ft.Button(content="Start Audio Recorder", on_click=handle_recording_start), - ft.Button(content="Stop Audio Recorder", on_click=handle_recording_stop), - ft.Button(content="List Devices", on_click=handle_list_devices), - ft.Button(content="Pause Recording", on_click=handle_pause), - ft.Button(content="Resume Recording", on_click=handle_resume), - ft.Button(content="WAV Encoder Support", on_click=handle_audio_encoder_test), - ft.Button( - content="Get Audio Recording Permission Status", - on_click=handle_has_permission, - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/audio_recorder/example_1/main.py b/sdk/python/examples/services/audio_recorder/example_1/main.py new file mode 100644 index 0000000000..c2d4b8b5bb --- /dev/null +++ b/sdk/python/examples/services/audio_recorder/example_1/main.py @@ -0,0 +1,81 @@ +import flet as ft +import flet_audio_recorder as far + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.appbar = ft.AppBar(title=ft.Text("Audio Recorder"), center_title=True) + + path = "test-audio-file.wav" + + def show_snackbar(message): + page.show_dialog(ft.SnackBar(ft.Text(message))) + + async def handle_recording_start(e: ft.Event[ft.Button]): + show_snackbar("Starting recording...") + await recorder.start_recording(output_path=path) + + async def handle_recording_stop(e: ft.Event[ft.Button]): + output_path = await recorder.stop_recording() + show_snackbar(f"Stopped recording. Output Path: {output_path}") + if page.web and output_path is not None: + await page.launch_url(output_path) + + async def handle_list_devices(e: ft.Event[ft.Button]): + o = await recorder.get_input_devices() + show_snackbar(f"Input Devices: {', '.join([f'{d.id}:{d.label}' for d in o])}") + + async def handle_has_permission(e: ft.Event[ft.Button]): + try: + status = await recorder.has_permission() + show_snackbar(f"Audio Recording Permission status: {status}") + except Exception as e: + show_snackbar(f"Error checking permission: {e}") + + async def handle_pause(e: ft.Event[ft.Button]): + print(f"isRecording: {await recorder.is_recording()}") + if await recorder.is_recording(): + await recorder.pause_recording() + + async def handle_resume(e: ft.Event[ft.Button]): + print(f"isPaused: {await recorder.is_paused()}") + if await recorder.is_paused(): + await recorder.resume_recording() + + async def handle_audio_encoder_test(e: ft.Event[ft.Button]): + print(await recorder.is_supported_encoder(far.AudioEncoder.WAV)) + + recorder = far.AudioRecorder( + configuration=far.AudioRecorderConfiguration(encoder=far.AudioEncoder.WAV), + on_state_change=lambda e: print(f"State Changed: {e.data}"), + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + content="Start Audio Recorder", on_click=handle_recording_start + ), + ft.Button( + content="Stop Audio Recorder", on_click=handle_recording_stop + ), + ft.Button(content="List Devices", on_click=handle_list_devices), + ft.Button(content="Pause Recording", on_click=handle_pause), + ft.Button(content="Resume Recording", on_click=handle_resume), + ft.Button( + content="WAV Encoder Support", + on_click=handle_audio_encoder_test, + ), + ft.Button( + content="Get Audio Recording Permission Status", + on_click=handle_has_permission, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/audio_recorder/example_1/pyproject.toml b/sdk/python/examples/services/audio_recorder/example_1/pyproject.toml new file mode 100644 index 0000000000..4f77381365 --- /dev/null +++ b/sdk/python/examples/services/audio_recorder/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-audio-recorder-example-1" +version = "1.0.0" +description = "Records audio, lists input devices, and checks recorder permissions from one screen." +requires-python = ">=3.10" +keywords = ["audio recorder", "example 1", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-audio-recorder"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/AudioRecorder"] + +[tool.flet.metadata] +title = "Audio recorder example" +controls = ["SafeArea", "Column", "Page", "AppBar", "Text", "Button", "AudioRecorder"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["audio recording", "permission check", "device listing"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/barometer/basic.py b/sdk/python/examples/services/barometer/basic.py deleted file mode 100644 index 5242711a46..0000000000 --- a/sdk/python/examples/services/barometer/basic.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_reading(e: ft.BarometerReadingEvent): - reading.value = f"{e.pressure:.2f} hPa" - page.update() - - def handle_error(e: ft.SensorErrorEvent): - page.add(ft.Text(f"Barometer error: {e.message}")) - - page.services.append( - ft.Barometer( - on_reading=handle_reading, - on_error=handle_error, - interval=ft.Duration(milliseconds=500), - ) - ) - - page.add( - ft.Text("Atmospheric pressure (hPa)."), - reading := ft.Text("Waiting for data..."), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/barometer/basic/main.py b/sdk/python/examples/services/barometer/basic/main.py new file mode 100644 index 0000000000..36fd76ff3a --- /dev/null +++ b/sdk/python/examples/services/barometer/basic/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_reading(e: ft.BarometerReadingEvent): + reading.value = f"{e.pressure:.2f} hPa" + + def handle_error(e: ft.SensorErrorEvent): + page.add(ft.Text(f"Barometer error: {e.message}")) + + page.services.append( + ft.Barometer( + on_reading=handle_reading, + on_error=handle_error, + interval=ft.Duration(milliseconds=500), + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Atmospheric pressure (hPa)."), + reading := ft.Text("Waiting for data..."), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/barometer/basic/pyproject.toml b/sdk/python/examples/services/barometer/basic/pyproject.toml new file mode 100644 index 0000000000..e6a25849a6 --- /dev/null +++ b/sdk/python/examples/services/barometer/basic/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-barometer-basic" +version = "1.0.0" +description = "Streams atmospheric pressure readings and reports barometer errors from the device sensor." +requires-python = ">=3.10" +keywords = ["barometer", "basic", "services"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Barometer"] + +[tool.flet.metadata] +title = "Basic barometer" +controls = ["SafeArea", "Column", "Page", "BarometerReadingEvent", "Text", "Barometer"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["live sensor readings", "sensor error handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["android", "ios"] diff --git a/sdk/python/examples/services/battery/basic.py b/sdk/python/examples/services/battery/basic.py deleted file mode 100644 index 8ec1891842..0000000000 --- a/sdk/python/examples/services/battery/basic.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - async def refresh_info(e: ft.Event[ft.Button] = None): - level = await battery.get_battery_level() - state = await battery.get_battery_state() - save_mode = await battery.is_in_battery_save_mode() - info.value = ( - f"Battery level: {level}% \n" - f"Battery state: {state.name} \n" - f"Battery saver: {'ON' if save_mode else 'OFF'}" - ) - - async def on_state_change(e: ft.BatteryStateChangeEvent): - print(f"State changed: {e.state}") - await refresh_info() - - battery = ft.Battery(on_state_change=on_state_change) - page.services.append(battery) # need to keep a reference to the service - - page.add( - ft.SafeArea( - content=ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - info := ft.Text(), - ft.Button("Refresh battery info", on_click=refresh_info), - ], - ) - ) - ) - await refresh_info() - - -ft.run(main) diff --git a/sdk/python/examples/services/battery/basic/main.py b/sdk/python/examples/services/battery/basic/main.py new file mode 100644 index 0000000000..53816b5a4d --- /dev/null +++ b/sdk/python/examples/services/battery/basic/main.py @@ -0,0 +1,40 @@ +import flet as ft + + +async def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + async def refresh_info(e: ft.Event[ft.Button] = None): + level = await battery.get_battery_level() + state = await battery.get_battery_state() + save_mode = await battery.is_in_battery_save_mode() + info.value = ( + f"Battery level: {level}% \n" + f"Battery state: {state.name} \n" + f"Battery saver: {'ON' if save_mode else 'OFF'}" + ) + + async def on_state_change(e: ft.BatteryStateChangeEvent): + print(f"State changed: {e.state}") + await refresh_info() + + battery = ft.Battery(on_state_change=on_state_change) + page.services.append(battery) # need to keep a reference to the service + + page.add( + ft.SafeArea( + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + info := ft.Text(), + ft.Button("Refresh battery info", on_click=refresh_info), + ], + ) + ) + ) + await refresh_info() + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/battery/basic/pyproject.toml b/sdk/python/examples/services/battery/basic/pyproject.toml new file mode 100644 index 0000000000..2297da7f8d --- /dev/null +++ b/sdk/python/examples/services/battery/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-battery-basic" +version = "1.0.0" +description = "Shows battery level, battery state, and battery saver status with a refresh action." +requires-python = ">=3.10" +keywords = ["battery", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Battery"] + +[tool.flet.metadata] +title = "Basic battery" +controls = ["SafeArea", "Column", "Page", "Button", "Battery", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["battery state", "battery saver status"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/browsercontextmenu/basic.py b/sdk/python/examples/services/browsercontextmenu/basic.py deleted file mode 100644 index f2225a0e14..0000000000 --- a/sdk/python/examples/services/browsercontextmenu/basic.py +++ /dev/null @@ -1,32 +0,0 @@ -# -# Run this example as a web app using the command: -# -# flet run --web basic.py -# - - -import flet as ft - - -async def main(page: ft.Page): - bcm = ft.BrowserContextMenu() - - async def disable_context_menu(): - await bcm.disable() - - async def enable_context_menu(): - await bcm.enable() - - page.add( - ft.Column( - [ - ft.Button( - "Disable browser context menu", on_click=disable_context_menu - ), - ft.Button("Enable browser context menu", on_click=enable_context_menu), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/browsercontextmenu/basic/main.py b/sdk/python/examples/services/browsercontextmenu/basic/main.py new file mode 100644 index 0000000000..0956cf47ca --- /dev/null +++ b/sdk/python/examples/services/browsercontextmenu/basic/main.py @@ -0,0 +1,39 @@ +# +# Run this example as a web app using the command: +# +# flet run --web main.py +# + + +import flet as ft + + +async def main(page: ft.Page): + bcm = ft.BrowserContextMenu() + + async def disable_context_menu(): + await bcm.disable() + + async def enable_context_menu(): + await bcm.enable() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + "Disable browser context menu", + on_click=disable_context_menu, + ), + ft.Button( + "Enable browser context menu", + on_click=enable_context_menu, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/browsercontextmenu/basic/pyproject.toml b/sdk/python/examples/services/browsercontextmenu/basic/pyproject.toml new file mode 100644 index 0000000000..85ea479917 --- /dev/null +++ b/sdk/python/examples/services/browsercontextmenu/basic/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-browsercontextmenu-basic" +version = "1.0.0" +description = "Enables and disables the browser context menu from a simple web control panel." +requires-python = ">=3.10" +keywords = ["browsercontextmenu", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/BrowserContextMenu"] + +[tool.flet.metadata] +title = "Basic browser context menu" +controls = ["SafeArea", "Column", "Page", "BrowserContextMenu", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["web context menu toggle"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["web"] diff --git a/sdk/python/examples/services/clipboard/files.py b/sdk/python/examples/services/clipboard/files.py deleted file mode 100644 index a35b453fcc..0000000000 --- a/sdk/python/examples/services/clipboard/files.py +++ /dev/null @@ -1,106 +0,0 @@ -import os -import subprocess -import sys -import tempfile -from pathlib import Path - -import flet as ft - - -def open_file(_, path: str): - """Open a desktop file in the default application.""" - if os.path.isfile(path): - if sys.platform == "darwin": # macOS - subprocess.run(["open", path]) - elif sys.platform == "win32": # Windows - os.startfile(path) - else: # Linux / BSD - subprocess.run(["xdg-open", path]) - - -async def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - # supported-platform checkers - supports_set_files = not page.web and page.platform.is_desktop() - supports_get_files = not page.web and ( - page.platform.is_desktop() or page.platform == ft.PagePlatform.ANDROID - ) - - def get_sample_files() -> list[str]: - # create temporary files - sample_dir = Path(tempfile.gettempdir()) / "flet_clipboard_files_example" - sample_dir.mkdir(parents=True, exist_ok=True) - sample_files = [ - sample_dir / "sample_1.txt", - sample_dir / "sample_2.txt", - ] - sample_files[0].write_text("Clipboard sample file #1\n", encoding="utf-8") - sample_files[1].write_text("Clipboard sample file #2\n", encoding="utf-8") - return [str(p) for p in sample_files] - - async def set_files_to_clipboard(e: ft.Event[ft.Button]): - files = get_sample_files() - ok = await ft.Clipboard().set_files(files) - status.value = ( - f"Set {len(files)} file references to clipboard (result: {ok})." - if ok - else "Failed to set file references to clipboard." - ) - - async def get_files_from_clipboard(e: ft.Event[ft.Button]): - files = await ft.Clipboard().get_files() - if files: - files_lv.controls = [ - ft.Row( - alignment=ft.MainAxisAlignment.CENTER, - controls=[ - ft.Text(f, selectable=True), - ft.IconButton( - icon=ft.Icons.OPEN_IN_NEW, - icon_size=15, - on_click=lambda e, f=f: open_file(e, f), - tooltip="Open file", - ), - ], - ) - for f in files - ] - else: - files_lv.controls = [] - status.value = f"Read {len(files)} file reference(s) from clipboard." - - page.add( - ft.SafeArea( - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Button( - "Set example files to clipboard", - on_click=set_files_to_clipboard, - disabled=not supports_set_files, - tooltip="Supported on desktop platforms only." - if not supports_set_files - else None, - ), - ft.Button( - "Get files from clipboard", - on_click=get_files_from_clipboard, - disabled=not supports_get_files, - tooltip="Supported on Android and desktop platforms only." - if not supports_get_files - else None, - ), - status := ft.Text(), - files_lv := ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - spacing=0, - controls=[], - ), - ], - ) - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/clipboard/files/main.py b/sdk/python/examples/services/clipboard/files/main.py new file mode 100644 index 0000000000..cbef745747 --- /dev/null +++ b/sdk/python/examples/services/clipboard/files/main.py @@ -0,0 +1,107 @@ +import os +import subprocess +import sys +import tempfile +from pathlib import Path + +import flet as ft + + +def open_file(_, path: str): + """Open a desktop file in the default application.""" + if os.path.isfile(path): + if sys.platform == "darwin": # macOS + subprocess.run(["open", path]) + elif sys.platform == "win32": # Windows + os.startfile(path) + else: # Linux / BSD + subprocess.run(["xdg-open", path]) + + +async def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + # supported-platform checkers + supports_set_files = not page.web and page.platform.is_desktop() + supports_get_files = not page.web and ( + page.platform.is_desktop() or page.platform == ft.PagePlatform.ANDROID + ) + + def get_sample_files() -> list[str]: + # create temporary files + sample_dir = Path(tempfile.gettempdir()) / "flet_clipboard_files_example" + sample_dir.mkdir(parents=True, exist_ok=True) + sample_files = [ + sample_dir / "sample_1.txt", + sample_dir / "sample_2.txt", + ] + sample_files[0].write_text("Clipboard sample file #1\n", encoding="utf-8") + sample_files[1].write_text("Clipboard sample file #2\n", encoding="utf-8") + return [str(p) for p in sample_files] + + async def set_files_to_clipboard(e: ft.Event[ft.Button]): + files = get_sample_files() + ok = await ft.Clipboard().set_files(files) + status.value = ( + f"Set {len(files)} file references to clipboard (result: {ok})." + if ok + else "Failed to set file references to clipboard." + ) + + async def get_files_from_clipboard(e: ft.Event[ft.Button]): + files = await ft.Clipboard().get_files() + if files: + files_lv.controls = [ + ft.Row( + alignment=ft.MainAxisAlignment.CENTER, + controls=[ + ft.Text(f, selectable=True), + ft.IconButton( + icon=ft.Icons.OPEN_IN_NEW, + icon_size=15, + on_click=lambda e, f=f: open_file(e, f), + tooltip="Open file", + ), + ], + ) + for f in files + ] + else: + files_lv.controls = [] + status.value = f"Read {len(files)} file reference(s) from clipboard." + + page.add( + ft.SafeArea( + ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Button( + "Set example files to clipboard", + on_click=set_files_to_clipboard, + disabled=not supports_set_files, + tooltip="Supported on desktop platforms only." + if not supports_set_files + else None, + ), + ft.Button( + "Get files from clipboard", + on_click=get_files_from_clipboard, + disabled=not supports_get_files, + tooltip="Supported on Android and desktop platforms only." + if not supports_get_files + else None, + ), + status := ft.Text(), + files_lv := ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + spacing=0, + controls=[], + ), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/clipboard/files/pyproject.toml b/sdk/python/examples/services/clipboard/files/pyproject.toml new file mode 100644 index 0000000000..ae4b274107 --- /dev/null +++ b/sdk/python/examples/services/clipboard/files/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-clipboard-files" +version = "1.0.0" +description = "Writes sample file references to the clipboard and reads clipboard file entries back." +requires-python = ">=3.10" +keywords = ["clipboard", "files", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Clipboard"] + +[tool.flet.metadata] +title = "Clipboard files" +controls = ["SafeArea", "Column", "Page", "Button", "Clipboard", "Row", "Text", "IconButton"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["clipboard file references", "desktop file open"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/clipboard/images.py b/sdk/python/examples/services/clipboard/images.py deleted file mode 100644 index 77d4328600..0000000000 --- a/sdk/python/examples/services/clipboard/images.py +++ /dev/null @@ -1,55 +0,0 @@ -import base64 - -import flet as ft - -SAMPLE_PNG = base64.b64decode( - "iVBORw0KGgoAAAANSUhEUgAAABkAAAAgCAYAAADnnNMGAAAACXBIWXMAAAORAAADkQFnq8zdAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA6dJREFUSImllltoHFUYx3/fzOzm0lt23ZrQ1AQbtBehNpvQohgkBYVo410RwQctNE3Sh0IfiiBoIAjqi6TYrKnFy4O3oiiRavDJFi3mXomIBmOxNZe63ay52GR3Zj4f2sTEzmx3m//TYf7/c35zvgPnO6KqrESXqpq3muocAikv6m+/zytj3ejik1VN21G31YA9CgJ6xC+bMyQZPVCuarciPAMYC99V6Vw5pLbFSibHmlVoRVj9P3cmPBM8tSJI/M6mzabpfoAQ9fIF7WK4bd5vvuFnLGgy2vi0abg94A0AcJGvMq3hDxGRyar9r4F+iLAm0yIiRk8m37tctS1WsrIhhrI30+Srmg+J87OXUf3lWGS1q89dC6ltsSanxk4Aj2QBABii96300g87P/rtlrWr8l+vyDMfdlXSyyEikqxsiOUAQJCBhfHdXRfCq1LSsSlcWG+KBAGStvvrMkgiuv8lUc2mREukPwLUfHG+uTQv8Eown7VL3XlbBxYhf1c17hbVF3MDwA9bts280TnaU1YYqPby07aeFlUlHt27wSQ4CLo+F8AvoTCvHmyKF+ZbEb/M77P2LgvAwmrTHAHflN3KZxVbMC2jMFNOpgPnrMSOhvvFkMezXdwV4ePbtvHtxnJAMQ0j4JtVnO+eLb5oiSlt5HDbv7t1O90lpYCCCKbhfzW5kAIwUAazR0BlfII8Ow0I6uoVmI9MyAMwbMs8CExmDbk4zgu931MyO4OI4KrYflkRjOoTI+uM9d1vjotwKPu9QMk/sxzuO8POiVFcdZ1M2YBVsMEAKOqLvaPIe7mACuw0z/80SMH58SMplxlfiDhVi7dw2pltRhjKBQTQdrSja2KKTfE551NHuaZ0QVPvWYQUn31/Vm2nDvgjF4grVJx6suSvrvrSJ/6cSW2Oz9mf264uNrB806xZ1k/CZ49dUKgDEtlCROX2hfHpx8pGuuo3PpqYulw8fjndOp1yhgtNKRevJ1FyR2Ola+jXAjdnwTkZ6o896GdWdxDw7IxFg+0DpmXchTKSBWQnIuJn9u4j7dt+13UfHXEkXQOcuQ4kMhVtqsgUyPiQiPQfHw1NB2sRjmXKuTg1NwwBYLhtPtQX26eqTwGXPDOqvmcC4Hnwfrrad94GrVsOYTqUTkQY+iTlNe/6O1miSP/x0VB/+wMIDwHn/vtV1iQC4Xv95uUEWVCoL9Y5Z+gdovoyMHUFJHv88jmVy0vTuw7cZNv2YaA61Bfb7ZX5F8SaUv2xwZevAAAAAElFTkSuQmCC" -) - - -async def main(page: ft.Page): - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - - async def set_image_to_clipboard(e: ft.Event[ft.Button]): - await ft.Clipboard().set_image(SAMPLE_PNG) - status.value = f"Sample image copied to clipboard ({len(SAMPLE_PNG)} bytes)." - preview.content = ft.Image(src=SAMPLE_PNG) - - async def get_image_from_clipboard(e: ft.Event[ft.Button]): - clipboard_image = await ft.Clipboard().get_image() - if clipboard_image is None: - status.value = "No image found in clipboard." - preview.content = None - return - else: - preview.content = ft.Image(src=clipboard_image) - status.value = ( - f"Image loaded from clipboard ({len(clipboard_image)} bytes)." - ) - - page.add( - ft.SafeArea( - ft.Column( - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - controls=[ - ft.Button( - "Set example image to clipboard", - on_click=set_image_to_clipboard, - disabled=not (page.web or page.platform.is_mobile()), - tooltip="Supported on mobile platforms only." - if not (page.web or page.platform.is_mobile()) - else None, - ), - ft.Button( - "Get image from clipboard", - on_click=get_image_from_clipboard, - ), - status := ft.Text(), - preview := ft.Container(width=360, height=240), - ], - ) - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/clipboard/images/main.py b/sdk/python/examples/services/clipboard/images/main.py new file mode 100644 index 0000000000..8f544359ba --- /dev/null +++ b/sdk/python/examples/services/clipboard/images/main.py @@ -0,0 +1,56 @@ +import base64 + +import flet as ft + +SAMPLE_PNG = base64.b64decode( + "iVBORw0KGgoAAAANSUhEUgAAABkAAAAgCAYAAADnnNMGAAAACXBIWXMAAAORAAADkQFnq8zdAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA6dJREFUSImllltoHFUYx3/fzOzm0lt23ZrQ1AQbtBehNpvQohgkBYVo410RwQctNE3Sh0IfiiBoIAjqi6TYrKnFy4O3oiiRavDJFi3mXomIBmOxNZe63ay52GR3Zj4f2sTEzmx3m//TYf7/c35zvgPnO6KqrESXqpq3muocAikv6m+/zytj3ejik1VN21G31YA9CgJ6xC+bMyQZPVCuarciPAMYC99V6Vw5pLbFSibHmlVoRVj9P3cmPBM8tSJI/M6mzabpfoAQ9fIF7WK4bd5vvuFnLGgy2vi0abg94A0AcJGvMq3hDxGRyar9r4F+iLAm0yIiRk8m37tctS1WsrIhhrI30+Srmg+J87OXUf3lWGS1q89dC6ltsSanxk4Aj2QBABii96300g87P/rtlrWr8l+vyDMfdlXSyyEikqxsiOUAQJCBhfHdXRfCq1LSsSlcWG+KBAGStvvrMkgiuv8lUc2mREukPwLUfHG+uTQv8Eown7VL3XlbBxYhf1c17hbVF3MDwA9bts280TnaU1YYqPby07aeFlUlHt27wSQ4CLo+F8AvoTCvHmyKF+ZbEb/M77P2LgvAwmrTHAHflN3KZxVbMC2jMFNOpgPnrMSOhvvFkMezXdwV4ePbtvHtxnJAMQ0j4JtVnO+eLb5oiSlt5HDbv7t1O90lpYCCCKbhfzW5kAIwUAazR0BlfII8Ow0I6uoVmI9MyAMwbMs8CExmDbk4zgu931MyO4OI4KrYflkRjOoTI+uM9d1vjotwKPu9QMk/sxzuO8POiVFcdZ1M2YBVsMEAKOqLvaPIe7mACuw0z/80SMH58SMplxlfiDhVi7dw2pltRhjKBQTQdrSja2KKTfE551NHuaZ0QVPvWYQUn31/Vm2nDvgjF4grVJx6suSvrvrSJ/6cSW2Oz9mf264uNrB806xZ1k/CZ49dUKgDEtlCROX2hfHpx8pGuuo3PpqYulw8fjndOp1yhgtNKRevJ1FyR2Ola+jXAjdnwTkZ6o896GdWdxDw7IxFg+0DpmXchTKSBWQnIuJn9u4j7dt+13UfHXEkXQOcuQ4kMhVtqsgUyPiQiPQfHw1NB2sRjmXKuTg1NwwBYLhtPtQX26eqTwGXPDOqvmcC4Hnwfrrad94GrVsOYTqUTkQY+iTlNe/6O1miSP/x0VB/+wMIDwHn/vtV1iQC4Xv95uUEWVCoL9Y5Z+gdovoyMHUFJHv88jmVy0vTuw7cZNv2YaA61Bfb7ZX5F8SaUv2xwZevAAAAAElFTkSuQmCC" +) + + +async def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + async def set_image_to_clipboard(e: ft.Event[ft.Button]): + await ft.Clipboard().set_image(SAMPLE_PNG) + status.value = f"Sample image copied to clipboard ({len(SAMPLE_PNG)} bytes)." + preview.content = ft.Image(src=SAMPLE_PNG) + + async def get_image_from_clipboard(e: ft.Event[ft.Button]): + clipboard_image = await ft.Clipboard().get_image() + if clipboard_image is None: + status.value = "No image found in clipboard." + preview.content = None + return + else: + preview.content = ft.Image(src=clipboard_image) + status.value = ( + f"Image loaded from clipboard ({len(clipboard_image)} bytes)." + ) + + page.add( + ft.SafeArea( + ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Button( + "Set example image to clipboard", + on_click=set_image_to_clipboard, + disabled=not (page.web or page.platform.is_mobile()), + tooltip="Supported on mobile platforms only." + if not (page.web or page.platform.is_mobile()) + else None, + ), + ft.Button( + "Get image from clipboard", + on_click=get_image_from_clipboard, + ), + status := ft.Text(), + preview := ft.Container(width=360, height=240), + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/clipboard/images/pyproject.toml b/sdk/python/examples/services/clipboard/images/pyproject.toml new file mode 100644 index 0000000000..d6d10f773e --- /dev/null +++ b/sdk/python/examples/services/clipboard/images/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-clipboard-images" +version = "1.0.0" +description = "Copies a sample image to the clipboard and previews image data loaded from the clipboard." +requires-python = ">=3.10" +keywords = ["clipboard", "images", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Clipboard"] + +[tool.flet.metadata] +title = "Clipboard images" +controls = ["SafeArea", "Column", "Page", "Button", "Clipboard", "Image", "Text", "Container"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["copy image", "clipboard image preview"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/clipboard/text.py b/sdk/python/examples/services/clipboard/text.py deleted file mode 100644 index bdf01a1d04..0000000000 --- a/sdk/python/examples/services/clipboard/text.py +++ /dev/null @@ -1,25 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - async def set_to_clipboard(): - await ft.Clipboard().set(text_to_copy.value) - text_to_copy.value = "" - page.show_dialog(ft.SnackBar("Text copied to clipboard")) - - async def get_from_clipboard(): - contents = await ft.Clipboard().get() - page.add(ft.Text(f"Clipboard contents: {contents}")) - - page.add( - ft.Column( - controls=[ - text_to_copy := ft.TextField(label="Text to copy"), - ft.Button("Set to clipboard", on_click=set_to_clipboard), - ft.Button("Get from clipboard", on_click=get_from_clipboard), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/clipboard/text/main.py b/sdk/python/examples/services/clipboard/text/main.py new file mode 100644 index 0000000000..a2113c092f --- /dev/null +++ b/sdk/python/examples/services/clipboard/text/main.py @@ -0,0 +1,34 @@ +import flet as ft + + +async def main(page: ft.Page): + async def set_to_clipboard(): + await ft.Clipboard().set(text_to_copy.value) + text_to_copy.value = "" + page.show_dialog(ft.SnackBar("Text copied to clipboard")) + + async def get_from_clipboard(): + contents = await ft.Clipboard().get() + page.add(ft.Text(f"Clipboard contents: {contents}")) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + controls=[ + text_to_copy := ft.TextField(label="Text to copy"), + ft.Button("Set to clipboard", on_click=set_to_clipboard), + ft.Button( + "Get from clipboard", on_click=get_from_clipboard + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/clipboard/text/pyproject.toml b/sdk/python/examples/services/clipboard/text/pyproject.toml new file mode 100644 index 0000000000..119a9a9f0d --- /dev/null +++ b/sdk/python/examples/services/clipboard/text/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-clipboard-text" +version = "1.0.0" +description = "Copies text to the clipboard and reads the current clipboard text back into the app." +requires-python = ">=3.10" +keywords = ["clipboard", "text", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Clipboard"] + +[tool.flet.metadata] +title = "Clipboard text" +controls = ["SafeArea", "Column", "Page", "Clipboard", "Text", "TextField", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["copy text", "read clipboard"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/connectivity/basic.py b/sdk/python/examples/services/connectivity/basic.py deleted file mode 100644 index 895c15f1ad..0000000000 --- a/sdk/python/examples/services/connectivity/basic.py +++ /dev/null @@ -1,35 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - connectivity = ft.Connectivity() - - status = ft.Text() - changes = ft.Text() - - async def refresh(_=None): - results = await connectivity.get_connectivity() - status.value = "Current connectivity: " + ", ".join(r.value for r in results) - - async def on_change(e: ft.ConnectivityChangeEvent): - changes.value = "Connectivity changed: " + ", ".join( - r.value for r in e.connectivity - ) - await refresh() - - connectivity.on_change = on_change - - await refresh() - - page.add( - ft.Column( - [ - status, - ft.Button("Refresh connectivity", on_click=refresh), - changes, - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/connectivity/basic/main.py b/sdk/python/examples/services/connectivity/basic/main.py new file mode 100644 index 0000000000..f7314d8ca3 --- /dev/null +++ b/sdk/python/examples/services/connectivity/basic/main.py @@ -0,0 +1,42 @@ +import flet as ft + + +async def main(page: ft.Page): + connectivity = ft.Connectivity() + + status = ft.Text() + changes = ft.Text() + + async def refresh(_=None): + results = await connectivity.get_connectivity() + status.value = "Current connectivity: " + ", ".join(r.value for r in results) + + async def on_change(e: ft.ConnectivityChangeEvent): + changes.value = "Connectivity changed: " + ", ".join( + r.value for r in e.connectivity + ) + await refresh() + + connectivity.on_change = on_change + + await refresh() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + [ + status, + ft.Button("Refresh connectivity", on_click=refresh), + changes, + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/connectivity/basic/pyproject.toml b/sdk/python/examples/services/connectivity/basic/pyproject.toml new file mode 100644 index 0000000000..f258dc1988 --- /dev/null +++ b/sdk/python/examples/services/connectivity/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-connectivity-basic" +version = "1.0.0" +description = "Displays current connectivity state and listens for connectivity change events." +requires-python = ">=3.10" +keywords = ["connectivity", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Connectivity"] + +[tool.flet.metadata] +title = "Basic connectivity" +controls = ["SafeArea", "Column", "Page", "Connectivity", "Text", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["connectivity status", "change events"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/file_picker/pick_and_save_text_content.py b/sdk/python/examples/services/file_picker/pick_and_save_text_content.py deleted file mode 100644 index eca794d165..0000000000 --- a/sdk/python/examples/services/file_picker/pick_and_save_text_content.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# Example of picking and saving text content with FilePicker. -# -# Run this example with: -# uv run flet run --web pick_and_save_text_content.py -# -import flet as ft - - -def main(page: ft.Page): - selected_file_name = ft.Text("No file selected") - selected_file_content = ft.TextField( - label="Selected file content", - multiline=True, - min_lines=8, - max_lines=14, - ) - save_status = ft.Text() - - async def pick_text_file(_: ft.Event[ft.Button]): - files = await ft.FilePicker().pick_files( - allow_multiple=False, - with_data=True, - file_type=ft.FilePickerFileType.CUSTOM, - allowed_extensions=["txt", "md"], - ) - if not files: - selected_file_name.value = "Selection cancelled" - selected_file_content.value = "" - page.update() - return - - selected = files[0] - selected_file_name.value = f"Selected: {selected.name} ({selected.size} bytes)" - selected_file_content.value = ( - selected.bytes.decode("utf-8", errors="replace") if selected.bytes else "" - ) - save_status.value = "" - page.update() - - async def save_text_file(_: ft.Event[ft.Button]): - file_name = "flet_text_content.txt" - file_path = await ft.FilePicker().save_file( - file_name=file_name, - file_type=ft.FilePickerFileType.CUSTOM, - allowed_extensions=["txt"], - src_bytes=selected_file_content.value.encode("utf-8"), - ) - if page.web: - save_status.value = f"Downloaded as {file_name}" - else: - save_status.value = ( - f"Saved to: {file_path}" if file_path else "Save cancelled" - ) - page.update() - - page.add( - ft.Text("Pick a .txt/.md file and load its text from FilePickerFile.bytes"), - ft.Button( - content="Pick text file", - icon=ft.Icons.UPLOAD_FILE, - on_click=pick_text_file, - ), - selected_file_name, - selected_file_content, - ft.Button( - content="Save / Download text", - icon=ft.Icons.DOWNLOAD, - on_click=save_text_file, - ), - save_status, - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/file_picker/pick_and_save_text_content/main.py b/sdk/python/examples/services/file_picker/pick_and_save_text_content/main.py new file mode 100644 index 0000000000..5109eb7369 --- /dev/null +++ b/sdk/python/examples/services/file_picker/pick_and_save_text_content/main.py @@ -0,0 +1,82 @@ +# +# Example of picking and saving text content with FilePicker. +# +# Run this example with: +# uv run flet run --web main.py +# +import flet as ft + + +def main(page: ft.Page): + selected_file_name = ft.Text("No file selected") + selected_file_content = ft.TextField( + label="Selected file content", + multiline=True, + min_lines=8, + max_lines=14, + ) + save_status = ft.Text() + + async def pick_text_file(_: ft.Event[ft.Button]): + files = await ft.FilePicker().pick_files( + allow_multiple=False, + with_data=True, + file_type=ft.FilePickerFileType.CUSTOM, + allowed_extensions=["txt", "md"], + ) + if not files: + selected_file_name.value = "Selection cancelled" + selected_file_content.value = "" + return + + selected = files[0] + selected_file_name.value = f"Selected: {selected.name} ({selected.size} bytes)" + selected_file_content.value = ( + selected.bytes.decode("utf-8", errors="replace") if selected.bytes else "" + ) + save_status.value = "" + + async def save_text_file(_: ft.Event[ft.Button]): + file_name = "flet_text_content.txt" + file_path = await ft.FilePicker().save_file( + file_name=file_name, + file_type=ft.FilePickerFileType.CUSTOM, + allowed_extensions=["txt"], + src_bytes=selected_file_content.value.encode("utf-8"), + ) + if page.web: + save_status.value = f"Downloaded as {file_name}" + else: + save_status.value = ( + f"Saved to: {file_path}" if file_path else "Save cancelled" + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Pick a .txt/.md file and load its text from " + "FilePickerFile.bytes" + ), + ft.Button( + content="Pick text file", + icon=ft.Icons.UPLOAD_FILE, + on_click=pick_text_file, + ), + selected_file_name, + selected_file_content, + ft.Button( + content="Save / Download text", + icon=ft.Icons.DOWNLOAD, + on_click=save_text_file, + ), + save_status, + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/file_picker/pick_and_save_text_content/pyproject.toml b/sdk/python/examples/services/file_picker/pick_and_save_text_content/pyproject.toml new file mode 100644 index 0000000000..e766700ea7 --- /dev/null +++ b/sdk/python/examples/services/file_picker/pick_and_save_text_content/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-file-picker-pick-and-save-text-content" +version = "1.0.0" +description = "Loads text file contents from picked files and saves or downloads edited text output." +requires-python = ">=3.10" +keywords = ["file picker", "pick and save text content", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/FilePicker"] + +[tool.flet.metadata] +title = "Pick and save text content" +controls = ["SafeArea", "Column", "Page", "Text", "TextField", "Button", "FilePicker"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["file picking", "save to file"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/file_picker/pick_and_upload.py b/sdk/python/examples/services/file_picker/pick_and_upload.py deleted file mode 100644 index aabadd4f0d..0000000000 --- a/sdk/python/examples/services/file_picker/pick_and_upload.py +++ /dev/null @@ -1,86 +0,0 @@ -# -# Example of picking and uploading files with progress indication -# -# Run this example with: -# export FLET_SECRET_KEY= -# uv run flet run --web examples/services/file_picker/pick_and_upload.py -# -from dataclasses import dataclass, field - -import flet as ft - - -@dataclass -class State: - file_picker: ft.FilePicker | None = None - picked_files: list[ft.FilePickerFile] = field(default_factory=list) - - -state = State() - - -def main(page: ft.Page): - if not page.web: - page.add( - ft.Text( - "This example is only available in Flet Web mode.\n" - "\n" - "Run this example with:\n" - " export FLET_SECRET_KEY=\n" - " flet run --web " - "examples/services/file_picker/pick_and_upload.py", - color=ft.Colors.RED, - selectable=True, - ) - ) - return - - prog_bars: dict[str, ft.ProgressRing] = {} - - def on_upload_progress(e: ft.FilePickerUploadEvent): - prog_bars[e.file_name].value = e.progress - - async def handle_files_pick(e: ft.Event[ft.Button]): - state.file_picker = ft.FilePicker(on_upload=on_upload_progress) - files = await state.file_picker.pick_files(allow_multiple=True) - print("Picked files:", files) - state.picked_files = files - - # update progress bars - upload_button.disabled = len(files) == 0 - prog_bars.clear() - upload_progress.controls.clear() - for f in files: - prog = ft.ProgressRing(value=0, bgcolor="#eeeeee", width=20, height=20) - prog_bars[f.name] = prog - upload_progress.controls.append(ft.Row([prog, ft.Text(f.name)])) - - async def handle_file_upload(e: ft.Event[ft.Button]): - upload_button.disabled = True - await state.file_picker.upload( - files=[ - ft.FilePickerUploadFile( - name=file.name, - upload_url=page.get_upload_url(f"dir/{file.name}", 60), - ) - for file in state.picked_files - ] - ) - - page.add( - ft.Button( - content="Select files...", - icon=ft.Icons.FOLDER_OPEN, - on_click=handle_files_pick, - ), - upload_progress := ft.Column(), - upload_button := ft.Button( - content="Upload", - icon=ft.Icons.UPLOAD, - on_click=handle_file_upload, - disabled=True, - ), - ) - - -ft.run(main, upload_dir="examples") diff --git a/sdk/python/examples/services/file_picker/pick_and_upload/main.py b/sdk/python/examples/services/file_picker/pick_and_upload/main.py new file mode 100644 index 0000000000..a521cca52d --- /dev/null +++ b/sdk/python/examples/services/file_picker/pick_and_upload/main.py @@ -0,0 +1,99 @@ +# +# Example of picking and uploading files with progress indication +# +# Run this example with: +# export FLET_SECRET_KEY= +# uv run flet run --web examples/services/file_picker/pick_and_upload/main.py +# +from dataclasses import dataclass, field + +import flet as ft + + +@dataclass +class State: + file_picker: ft.FilePicker | None = None + picked_files: list[ft.FilePickerFile] = field(default_factory=list) + + +state = State() + + +def main(page: ft.Page): + if not page.web: + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "This example is only available in Flet Web mode.\n" + "\n" + "Run this example with:\n" + " export FLET_SECRET_KEY=\n" + " flet run --web " + "examples/services/file_picker/pick_and_upload/main.py", + color=ft.Colors.RED, + selectable=True, + ) + ], + ), + ) + ) + return + + prog_bars: dict[str, ft.ProgressRing] = {} + + def on_upload_progress(e: ft.FilePickerUploadEvent): + prog_bars[e.file_name].value = e.progress + + async def handle_files_pick(e: ft.Event[ft.Button]): + state.file_picker = ft.FilePicker(on_upload=on_upload_progress) + files = await state.file_picker.pick_files(allow_multiple=True) + print("Picked files:", files) + state.picked_files = files + + # update progress bars + upload_button.disabled = len(files) == 0 + prog_bars.clear() + upload_progress.controls.clear() + for f in files: + prog = ft.ProgressRing(value=0, bgcolor="#eeeeee", width=20, height=20) + prog_bars[f.name] = prog + upload_progress.controls.append(ft.Row([prog, ft.Text(f.name)])) + + async def handle_file_upload(e: ft.Event[ft.Button]): + upload_button.disabled = True + await state.file_picker.upload( + files=[ + ft.FilePickerUploadFile( + name=file.name, + upload_url=page.get_upload_url(f"dir/{file.name}", 60), + ) + for file in state.picked_files + ] + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button( + content="Select files...", + icon=ft.Icons.FOLDER_OPEN, + on_click=handle_files_pick, + ), + upload_progress := ft.Column(), + upload_button := ft.Button( + content="Upload", + icon=ft.Icons.UPLOAD, + on_click=handle_file_upload, + disabled=True, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main, upload_dir="examples") diff --git a/sdk/python/examples/services/file_picker/pick_and_upload/pyproject.toml b/sdk/python/examples/services/file_picker/pick_and_upload/pyproject.toml new file mode 100644 index 0000000000..16f310155b --- /dev/null +++ b/sdk/python/examples/services/file_picker/pick_and_upload/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-file-picker-pick-and-upload" +version = "1.0.0" +description = "Picks multiple files in web mode and uploads them with live progress indicators." +requires-python = ">=3.10" +keywords = ["file picker", "pick and upload", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/FilePicker"] + +[tool.flet.metadata] +title = "Pick and upload files" +controls = ["SafeArea", "Column", "FilePicker", "FilePickerFile", "Page", "Text", "ProgressRing", "FilePickerUploadEvent"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["file upload", "upload progress"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["web"] diff --git a/sdk/python/examples/services/file_picker/pick_save_and_get_directory_path.py b/sdk/python/examples/services/file_picker/pick_save_and_get_directory_path.py deleted file mode 100644 index f9677e67cd..0000000000 --- a/sdk/python/examples/services/file_picker/pick_save_and_get_directory_path.py +++ /dev/null @@ -1,53 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - async def handle_pick_files(e: ft.Event[ft.Button]): - files = await ft.FilePicker().pick_files(allow_multiple=True) - selected_files.value = ( - ", ".join(map(lambda f: f.name, files)) if files else "Cancelled!" - ) - - async def handle_save_file(e: ft.Event[ft.Button]): - save_file_path.value = await ft.FilePicker().save_file() - - async def handle_get_directory_path(e: ft.Event[ft.Button]): - directory_path.value = await ft.FilePicker().get_directory_path() - - page.add( - ft.Row( - controls=[ - ft.Button( - content="Pick files", - icon=ft.Icons.UPLOAD_FILE, - on_click=handle_pick_files, - ), - selected_files := ft.Text(), - ] - ), - ft.Row( - controls=[ - ft.Button( - content="Save file", - icon=ft.Icons.SAVE, - on_click=handle_save_file, - disabled=page.web, # disable this button in web mode - ), - save_file_path := ft.Text(), - ] - ), - ft.Row( - controls=[ - ft.Button( - content="Open directory", - icon=ft.Icons.FOLDER_OPEN, - on_click=handle_get_directory_path, - disabled=page.web, # disable this button in web mode - ), - directory_path := ft.Text(), - ] - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/file_picker/pick_save_and_get_directory_path/main.py b/sdk/python/examples/services/file_picker/pick_save_and_get_directory_path/main.py new file mode 100644 index 0000000000..855604f2b9 --- /dev/null +++ b/sdk/python/examples/services/file_picker/pick_save_and_get_directory_path/main.py @@ -0,0 +1,60 @@ +import flet as ft + + +def main(page: ft.Page): + async def handle_pick_files(e: ft.Event[ft.Button]): + files = await ft.FilePicker().pick_files(allow_multiple=True) + selected_files.value = ( + ", ".join(map(lambda f: f.name, files)) if files else "Cancelled!" + ) + + async def handle_save_file(e: ft.Event[ft.Button]): + save_file_path.value = await ft.FilePicker().save_file() + + async def handle_get_directory_path(e: ft.Event[ft.Button]): + directory_path.value = await ft.FilePicker().get_directory_path() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + controls=[ + ft.Button( + content="Pick files", + icon=ft.Icons.UPLOAD_FILE, + on_click=handle_pick_files, + ), + selected_files := ft.Text(), + ] + ), + ft.Row( + controls=[ + ft.Button( + content="Save file", + icon=ft.Icons.SAVE, + on_click=handle_save_file, + disabled=page.web, # disable this button in web mode + ), + save_file_path := ft.Text(), + ] + ), + ft.Row( + controls=[ + ft.Button( + content="Open directory", + icon=ft.Icons.FOLDER_OPEN, + on_click=handle_get_directory_path, + disabled=page.web, # disable this button in web mode + ), + directory_path := ft.Text(), + ] + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/file_picker/pick_save_and_get_directory_path/pyproject.toml b/sdk/python/examples/services/file_picker/pick_save_and_get_directory_path/pyproject.toml new file mode 100644 index 0000000000..7fd8487e16 --- /dev/null +++ b/sdk/python/examples/services/file_picker/pick_save_and_get_directory_path/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-file-picker-pick-save-and-get-directory-path" +version = "1.0.0" +description = "Demonstrates file picking, save dialogs, and directory selection in one example." +requires-python = ">=3.10" +keywords = ["file picker", "pick save and get directory path", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/FilePicker"] + +[tool.flet.metadata] +title = "Pick, save, and browse directories" +controls = ["SafeArea", "Column", "Page", "Button", "FilePicker", "Row", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["file picking", "save dialog", "directory picker"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/flashlight/example_1.py b/sdk/python/examples/services/flashlight/example_1.py deleted file mode 100644 index cb098b18f0..0000000000 --- a/sdk/python/examples/services/flashlight/example_1.py +++ /dev/null @@ -1,18 +0,0 @@ -import flet as ft -import flet_flashlight as ffl - - -def main(page: ft.Page): - async def turn_on_flashlight(): - await ffl.Flashlight().on() - - async def turn_off_flashlight(): - await ffl.Flashlight().off() - - page.add( - ft.Button("Turn On Flashlight", on_click=turn_on_flashlight), - ft.Button("Turn Off Flashlight", on_click=turn_off_flashlight), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/flashlight/example_1/main.py b/sdk/python/examples/services/flashlight/example_1/main.py new file mode 100644 index 0000000000..453c7682be --- /dev/null +++ b/sdk/python/examples/services/flashlight/example_1/main.py @@ -0,0 +1,25 @@ +import flet as ft +import flet_flashlight as ffl + + +def main(page: ft.Page): + async def turn_on_flashlight(): + await ffl.Flashlight().on() + + async def turn_off_flashlight(): + await ffl.Flashlight().off() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button("Turn On Flashlight", on_click=turn_on_flashlight), + ft.Button("Turn Off Flashlight", on_click=turn_off_flashlight), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/flashlight/example_1/pyproject.toml b/sdk/python/examples/services/flashlight/example_1/pyproject.toml new file mode 100644 index 0000000000..4e44c44f1f --- /dev/null +++ b/sdk/python/examples/services/flashlight/example_1/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-flashlight-example-1" +version = "1.0.0" +description = "Turns the device flashlight on and off with two simple actions." +requires-python = ">=3.10" +keywords = ["flashlight", "example 1", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-flashlight"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Flashlight"] + +[tool.flet.metadata] +title = "Flashlight example" +controls = ["SafeArea", "Column", "Page", "Flashlight", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["flashlight toggle"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["ios", "android"] diff --git a/sdk/python/examples/services/geolocator/example_1.py b/sdk/python/examples/services/geolocator/example_1.py deleted file mode 100644 index eef035f3c1..0000000000 --- a/sdk/python/examples/services/geolocator/example_1.py +++ /dev/null @@ -1,117 +0,0 @@ -from typing import Callable - -import flet as ft -import flet_geolocator as ftg - - -async def main(page: ft.Page): - page.scroll = ft.ScrollMode.ADAPTIVE - page.appbar = ft.AppBar(title=ft.Text("Geolocator Tests")) - - def handle_position_change(e: ftg.GeolocatorPositionChangeEvent): - page.add(ft.Text(f"New position: {e.position.latitude} {e.position.longitude}")) - - def get_dialog(handler: Callable): - return ft.AlertDialog( - adaptive=True, - title="Opening Location Settings...", - content=ft.Text( - "You are about to be redirected to the location/app settings. " - "Please locate this app and grant it location permissions." - ), - actions=[ft.TextButton("Take me there", on_click=handler)], - actions_alignment=ft.MainAxisAlignment.CENTER, - ) - - def show_snackbar(message): - page.show_dialog(ft.SnackBar(ft.Text(message))) - - async def handle_permission_request(e: ft.Event[ft.OutlinedButton]): - p = await geo.request_permission(timeout=60) - page.add(ft.Text(f"request_permission: {p}")) - show_snackbar(f"Permission request sent: {p}") - - async def handle_get_permission_status(e: ft.Event[ft.OutlinedButton]): - p = await geo.get_permission_status() - show_snackbar(f"Permission status: {p}") - - async def handle_get_current_position(e: ft.Event[ft.OutlinedButton]): - p = await geo.get_current_position() - show_snackbar(f"Current position: ({p.latitude}, {p.longitude})") - - async def handle_get_last_known_position(e): - p = await geo.get_last_known_position() - show_snackbar(f"Last known position: ({p.latitude}, {p.longitude})") - - async def handle_location_service_enabled(e): - p = await geo.is_location_service_enabled() - show_snackbar(f"Location service enabled: {p}") - - async def handle_open_location_settings(e: ft.Event[ft.OutlinedButton]): - p = await geo.open_location_settings() - page.pop_dialog() - if p is True: - show_snackbar("Location settings opened successfully.") - else: - show_snackbar("Location settings could not be opened.") - - async def handle_open_app_settings(e: ft.Event[ft.OutlinedButton]): - p = await geo.open_app_settings() - page.pop_dialog() - if p: - show_snackbar("App settings opened successfully.") - else: - show_snackbar("App settings could not be opened.") - - location_settings_dlg = get_dialog(handle_open_location_settings) - app_settings_dlg = get_dialog(handle_open_app_settings) - - geo = ftg.Geolocator( - configuration=ftg.GeolocatorConfiguration( - accuracy=ftg.GeolocatorPositionAccuracy.LOW - ), - on_position_change=handle_position_change, - on_error=lambda e: page.add(ft.Text(f"Error: {e.data}")), - ) - - page.add( - ft.Row( - wrap=True, - controls=[ - ft.OutlinedButton( - content="Request Permission", - on_click=handle_permission_request, - ), - ft.OutlinedButton( - content="Get Permission Status", - on_click=handle_get_permission_status, - ), - ft.OutlinedButton( - content="Get Current Position", - on_click=handle_get_current_position, - ), - ft.OutlinedButton( - content="Get Last Known Position", - visible=not page.web, - on_click=handle_get_last_known_position, - ), - ft.OutlinedButton( - content="Is Location Service Enabled", - on_click=handle_location_service_enabled, - ), - ft.OutlinedButton( - content="Open Location Settings", - visible=not page.web, # (1)! - on_click=lambda e: page.show_dialog(location_settings_dlg), - ), - ft.OutlinedButton( - content="Open App Settings", - visible=not page.web, # (1)! - on_click=lambda e: page.show_dialog(app_settings_dlg), - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/geolocator/example_1/main.py b/sdk/python/examples/services/geolocator/example_1/main.py new file mode 100644 index 0000000000..cb8de409fd --- /dev/null +++ b/sdk/python/examples/services/geolocator/example_1/main.py @@ -0,0 +1,126 @@ +from typing import Callable + +import flet as ft +import flet_geolocator as ftg + + +async def main(page: ft.Page): + page.scroll = ft.ScrollMode.ADAPTIVE + page.appbar = ft.AppBar(title=ft.Text("Geolocator Tests")) + + def handle_position_change(e: ftg.GeolocatorPositionChangeEvent): + page.add(ft.Text(f"New position: {e.position.latitude} {e.position.longitude}")) + + def get_dialog(handler: Callable): + return ft.AlertDialog( + adaptive=True, + title="Opening Location Settings...", + content=ft.Text( + "You are about to be redirected to the location/app settings. " + "Please locate this app and grant it location permissions." + ), + actions=[ft.TextButton("Take me there", on_click=handler)], + actions_alignment=ft.MainAxisAlignment.CENTER, + ) + + def show_snackbar(message): + page.show_dialog(ft.SnackBar(ft.Text(message))) + + async def handle_permission_request(e: ft.Event[ft.OutlinedButton]): + p = await geo.request_permission(timeout=60) + page.add(ft.Text(f"request_permission: {p}")) + show_snackbar(f"Permission request sent: {p}") + + async def handle_get_permission_status(e: ft.Event[ft.OutlinedButton]): + p = await geo.get_permission_status() + show_snackbar(f"Permission status: {p}") + + async def handle_get_current_position(e: ft.Event[ft.OutlinedButton]): + p = await geo.get_current_position() + show_snackbar(f"Current position: ({p.latitude}, {p.longitude})") + + async def handle_get_last_known_position(e): + p = await geo.get_last_known_position() + show_snackbar(f"Last known position: ({p.latitude}, {p.longitude})") + + async def handle_location_service_enabled(e): + p = await geo.is_location_service_enabled() + show_snackbar(f"Location service enabled: {p}") + + async def handle_open_location_settings(e: ft.Event[ft.OutlinedButton]): + p = await geo.open_location_settings() + page.pop_dialog() + if p is True: + show_snackbar("Location settings opened successfully.") + else: + show_snackbar("Location settings could not be opened.") + + async def handle_open_app_settings(e: ft.Event[ft.OutlinedButton]): + p = await geo.open_app_settings() + page.pop_dialog() + if p: + show_snackbar("App settings opened successfully.") + else: + show_snackbar("App settings could not be opened.") + + location_settings_dlg = get_dialog(handle_open_location_settings) + app_settings_dlg = get_dialog(handle_open_app_settings) + + geo = ftg.Geolocator( + configuration=ftg.GeolocatorConfiguration( + accuracy=ftg.GeolocatorPositionAccuracy.LOW + ), + on_position_change=handle_position_change, + on_error=lambda e: page.add(ft.Text(f"Error: {e.data}")), + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Row( + wrap=True, + controls=[ + ft.OutlinedButton( + content="Request Permission", + on_click=handle_permission_request, + ), + ft.OutlinedButton( + content="Get Permission Status", + on_click=handle_get_permission_status, + ), + ft.OutlinedButton( + content="Get Current Position", + on_click=handle_get_current_position, + ), + ft.OutlinedButton( + content="Get Last Known Position", + visible=not page.web, + on_click=handle_get_last_known_position, + ), + ft.OutlinedButton( + content="Is Location Service Enabled", + on_click=handle_location_service_enabled, + ), + ft.OutlinedButton( + content="Open Location Settings", + visible=not page.web, # (1)! + on_click=lambda e: page.show_dialog( + location_settings_dlg + ), + ), + ft.OutlinedButton( + content="Open App Settings", + visible=not page.web, # (1)! + on_click=lambda e: page.show_dialog(app_settings_dlg), + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/geolocator/example_1/pyproject.toml b/sdk/python/examples/services/geolocator/example_1/pyproject.toml new file mode 100644 index 0000000000..4092dee562 --- /dev/null +++ b/sdk/python/examples/services/geolocator/example_1/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-geolocator-example-1" +version = "1.0.0" +description = "Requests location permissions and exercises geolocator actions from one dashboard." +requires-python = ">=3.10" +keywords = ["geolocator", "example 1", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-geolocator"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Geolocator"] + +[tool.flet.metadata] +title = "Geolocator example" +controls = ["SafeArea", "Column", "Page", "AppBar", "Text", "GeolocatorPositionChangeEvent", "TextButton", "OutlinedButton"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["permission handling", "location actions"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/gyroscope/basic.py b/sdk/python/examples/services/gyroscope/basic.py deleted file mode 100644 index a54c9d9b7e..0000000000 --- a/sdk/python/examples/services/gyroscope/basic.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_reading(e: ft.GyroscopeReadingEvent): - reading.value = f"x={e.x:.2f} rad/s, y={e.y:.2f} rad/s, z={e.z:.2f} rad/s" - page.update() - - def handle_error(e: ft.SensorErrorEvent): - page.add(ft.Text(f"Gyroscope error: {e.message}")) - - page.services.append( - ft.Gyroscope( - on_reading=handle_reading, - on_error=handle_error, - interval=ft.Duration(milliseconds=100), - ) - ) - - page.add( - ft.Text("Rotate your device to see gyroscope readings."), - reading := ft.Text("Waiting for data..."), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/gyroscope/basic/main.py b/sdk/python/examples/services/gyroscope/basic/main.py new file mode 100644 index 0000000000..9221e246cd --- /dev/null +++ b/sdk/python/examples/services/gyroscope/basic/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_reading(e: ft.GyroscopeReadingEvent): + reading.value = f"x={e.x:.2f} rad/s, y={e.y:.2f} rad/s, z={e.z:.2f} rad/s" + + def handle_error(e: ft.SensorErrorEvent): + page.add(ft.Text(f"Gyroscope error: {e.message}")) + + page.services.append( + ft.Gyroscope( + on_reading=handle_reading, + on_error=handle_error, + interval=ft.Duration(milliseconds=100), + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Rotate your device to see gyroscope readings."), + reading := ft.Text("Waiting for data..."), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/gyroscope/basic/pyproject.toml b/sdk/python/examples/services/gyroscope/basic/pyproject.toml new file mode 100644 index 0000000000..6d4b9c2ca3 --- /dev/null +++ b/sdk/python/examples/services/gyroscope/basic/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-gyroscope-basic" +version = "1.0.0" +description = "Streams gyroscope rotation readings and reports sensor errors from supported devices." +requires-python = ">=3.10" +keywords = ["gyroscope", "basic", "services"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Gyroscope"] + +[tool.flet.metadata] +title = "Basic gyroscope" +controls = ["SafeArea", "Column", "Page", "GyroscopeReadingEvent", "Text", "Gyroscope"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["live sensor readings", "sensor error handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["android", "ios", "web"] diff --git a/sdk/python/examples/services/haptic_feedback/basic.py b/sdk/python/examples/services/haptic_feedback/basic.py deleted file mode 100644 index 158586aac9..0000000000 --- a/sdk/python/examples/services/haptic_feedback/basic.py +++ /dev/null @@ -1,27 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - hf = ft.HapticFeedback() - - async def heavy_impact(): - await hf.heavy_impact() - - async def medium_impact(): - await hf.medium_impact() - - async def light_impact(): - await hf.light_impact() - - async def vibrate(): - await hf.vibrate() - - page.add( - ft.Button("Heavy impact", on_click=heavy_impact), - ft.Button("Medium impact", on_click=medium_impact), - ft.Button("Light impact", on_click=light_impact), - ft.Button("Vibrate", on_click=vibrate), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/haptic_feedback/basic/main.py b/sdk/python/examples/services/haptic_feedback/basic/main.py new file mode 100644 index 0000000000..1c5654e46e --- /dev/null +++ b/sdk/python/examples/services/haptic_feedback/basic/main.py @@ -0,0 +1,34 @@ +import flet as ft + + +def main(page: ft.Page): + hf = ft.HapticFeedback() + + async def heavy_impact(): + await hf.heavy_impact() + + async def medium_impact(): + await hf.medium_impact() + + async def light_impact(): + await hf.light_impact() + + async def vibrate(): + await hf.vibrate() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Button("Heavy impact", on_click=heavy_impact), + ft.Button("Medium impact", on_click=medium_impact), + ft.Button("Light impact", on_click=light_impact), + ft.Button("Vibrate", on_click=vibrate), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/haptic_feedback/basic/pyproject.toml b/sdk/python/examples/services/haptic_feedback/basic/pyproject.toml new file mode 100644 index 0000000000..740c7167ca --- /dev/null +++ b/sdk/python/examples/services/haptic_feedback/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-haptic-feedback-basic" +version = "1.0.0" +description = "Triggers heavy, medium, light, and vibration haptic feedback actions." +requires-python = ">=3.10" +keywords = ["haptic feedback", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/HapticFeedback"] + +[tool.flet.metadata] +title = "Basic haptic feedback" +controls = ["SafeArea", "Column", "Page", "HapticFeedback", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["haptic feedback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/magnetometer/basic.py b/sdk/python/examples/services/magnetometer/basic.py deleted file mode 100644 index 5790dc85ae..0000000000 --- a/sdk/python/examples/services/magnetometer/basic.py +++ /dev/null @@ -1,26 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_reading(e: ft.MagnetometerReadingEvent): - reading.value = f"x={e.x:.2f} uT, y={e.y:.2f} uT, z={e.z:.2f} uT" - page.update() - - def handle_error(e: ft.SensorErrorEvent): - page.add(ft.Text(f"Magnetometer error: {e.message}")) - - page.services.append( - ft.Magnetometer( - on_reading=handle_reading, - on_error=handle_error, - interval=ft.Duration(milliseconds=200), - ) - ) - - page.add( - ft.Text("Monitor the ambient magnetic field (uT)."), - reading := ft.Text("Waiting for data..."), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/magnetometer/basic/main.py b/sdk/python/examples/services/magnetometer/basic/main.py new file mode 100644 index 0000000000..06241d144e --- /dev/null +++ b/sdk/python/examples/services/magnetometer/basic/main.py @@ -0,0 +1,32 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_reading(e: ft.MagnetometerReadingEvent): + reading.value = f"x={e.x:.2f} uT, y={e.y:.2f} uT, z={e.z:.2f} uT" + + def handle_error(e: ft.SensorErrorEvent): + page.add(ft.Text(f"Magnetometer error: {e.message}")) + + page.services.append( + ft.Magnetometer( + on_reading=handle_reading, + on_error=handle_error, + interval=ft.Duration(milliseconds=200), + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text("Monitor the ambient magnetic field (uT)."), + reading := ft.Text("Waiting for data..."), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/magnetometer/basic/pyproject.toml b/sdk/python/examples/services/magnetometer/basic/pyproject.toml new file mode 100644 index 0000000000..835c5d923f --- /dev/null +++ b/sdk/python/examples/services/magnetometer/basic/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-magnetometer-basic" +version = "1.0.0" +description = "Streams ambient magnetic field readings and reports magnetometer errors." +requires-python = ">=3.10" +keywords = ["magnetometer", "basic", "services"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Magnetometer"] + +[tool.flet.metadata] +title = "Basic magnetometer" +controls = ["SafeArea", "Column", "Page", "MagnetometerReadingEvent", "Text", "Magnetometer"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["live sensor readings", "sensor error handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["android", "ios"] diff --git a/sdk/python/examples/services/permission_handler/example_1.py b/sdk/python/examples/services/permission_handler/example_1.py deleted file mode 100644 index 44188a3231..0000000000 --- a/sdk/python/examples/services/permission_handler/example_1.py +++ /dev/null @@ -1,34 +0,0 @@ -import flet as ft -import flet_permission_handler as fph - - -def main(page: ft.Page): - page.appbar = ft.AppBar(title="PermissionHandler Playground") - - def show_snackbar(message: str): - page.show_dialog(ft.SnackBar(ft.Text(message))) - - async def get_permission_status(e: ft.Event[ft.OutlinedButton]): - status = await ph.get_status(fph.Permission.MICROPHONE) - show_snackbar(f"Microphone permission status: {status.name}") - - async def request_permission(e: ft.Event[ft.OutlinedButton]): - status = await ph.request(fph.Permission.MICROPHONE) - show_snackbar(f"Requested microphone permission: {status.name}") - - async def open_app_settings(e: ft.Event[ft.OutlinedButton]): - show_snackbar("Opening app settings...") - await ph.open_app_settings() - - ph = fph.PermissionHandler() - - page.add( - ft.OutlinedButton("Open app settings", on_click=open_app_settings), - ft.OutlinedButton("Request Microphone permission", on_click=request_permission), - ft.OutlinedButton( - "Get Microphone permission status", on_click=get_permission_status - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/permission_handler/example_1/main.py b/sdk/python/examples/services/permission_handler/example_1/main.py new file mode 100644 index 0000000000..3903d1ba1a --- /dev/null +++ b/sdk/python/examples/services/permission_handler/example_1/main.py @@ -0,0 +1,44 @@ +import flet as ft +import flet_permission_handler as fph + + +def main(page: ft.Page): + page.appbar = ft.AppBar(title="PermissionHandler Playground") + + def show_snackbar(message: str): + page.show_dialog(ft.SnackBar(ft.Text(message))) + + async def get_permission_status(e: ft.Event[ft.OutlinedButton]): + status = await ph.get_status(fph.Permission.MICROPHONE) + show_snackbar(f"Microphone permission status: {status.name}") + + async def request_permission(e: ft.Event[ft.OutlinedButton]): + status = await ph.request(fph.Permission.MICROPHONE) + show_snackbar(f"Requested microphone permission: {status.name}") + + async def open_app_settings(e: ft.Event[ft.OutlinedButton]): + show_snackbar("Opening app settings...") + await ph.open_app_settings() + + ph = fph.PermissionHandler() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.OutlinedButton("Open app settings", on_click=open_app_settings), + ft.OutlinedButton( + "Request Microphone permission", on_click=request_permission + ), + ft.OutlinedButton( + "Get Microphone permission status", + on_click=get_permission_status, + ), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/permission_handler/example_1/pyproject.toml b/sdk/python/examples/services/permission_handler/example_1/pyproject.toml new file mode 100644 index 0000000000..5f13c388a1 --- /dev/null +++ b/sdk/python/examples/services/permission_handler/example_1/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-permission-handler-example-1" +version = "1.0.0" +description = "Requests microphone permission, reads its status, and opens app settings." +requires-python = ">=3.10" +keywords = ["permission handler", "example 1", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-permission-handler"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/PermissionHandler"] + +[tool.flet.metadata] +title = "Permission handler example" +controls = ["SafeArea", "Column", "Page", "AppBar", "Text", "OutlinedButton", "PermissionHandler"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["permission request", "settings shortcut"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["windows", "ios", "android", "web"] diff --git a/sdk/python/examples/services/screen_brightness/basic.py b/sdk/python/examples/services/screen_brightness/basic.py deleted file mode 100644 index c89585ad4f..0000000000 --- a/sdk/python/examples/services/screen_brightness/basic.py +++ /dev/null @@ -1,98 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - sb = ft.ScreenBrightness() - page.services.append(sb) - - info = ft.Text() - system_change = ft.Text() - app_change = ft.Text() - - level = ft.Slider(min=0, max=1, divisions=20, value=0.5, label="Brightness") - animate_switch = ft.Switch( - label="Animate application changes", value=True, on_change=None - ) - auto_reset_switch = ft.Switch( - label="Auto-reset application brightness on lifecycle changes", - value=True, - on_change=None, - ) - - async def refresh_info(): - system_brightness = await sb.get_system_screen_brightness() - app_brightness = await sb.get_application_screen_brightness() - can_change_system = await sb.can_change_system_screen_brightness() - - animate_switch.value = await sb.is_animate() - auto_reset_switch.value = await sb.is_auto_reset() - - info.value = ( - f"System: {system_brightness:.2f} | " - f"Application: {app_brightness:.2f} | " - f"Can change system: {can_change_system}" - ) - - async def set_application_brightness(_): - await sb.set_application_screen_brightness(level.value) - await refresh_info() - - async def set_system_brightness(_): - await sb.set_system_screen_brightness(level.value) - await refresh_info() - - async def reset_application_brightness(_): - await sb.reset_application_screen_brightness() - await refresh_info() - - async def toggle_animate(e: ft.Event[ft.Switch]): - await sb.set_animate(e.control.value) - await refresh_info() - - async def toggle_auto_reset(e: ft.Event[ft.Switch]): - await sb.set_auto_reset(e.control.value) - await refresh_info() - - async def on_system_change(e: ft.ScreenBrightnessChangeEvent): - system_change.value = f"System brightness changed: {e.brightness:.2f}" - - async def on_application_change(e: ft.ScreenBrightnessChangeEvent): - app_change.value = f"Application brightness changed: {e.brightness:.2f}" - - sb.on_system_screen_brightness_change = on_system_change - sb.on_application_screen_brightness_change = on_application_change - - await refresh_info() - - animate_switch.on_change = toggle_animate - auto_reset_switch.on_change = toggle_auto_reset - - page.add( - ft.Column( - [ - info, - level, - ft.Row( - [ - ft.Button( - "Set application", on_click=set_application_brightness - ), - ft.Button("Set system", on_click=set_system_brightness), - ft.TextButton( - "Reset application", on_click=reset_application_brightness - ), - ], - wrap=True, - ), - animate_switch, - auto_reset_switch, - ft.Divider(), - system_change, - app_change, - ], - spacing=12, - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/screen_brightness/basic/main.py b/sdk/python/examples/services/screen_brightness/basic/main.py new file mode 100644 index 0000000000..d02d36cfb3 --- /dev/null +++ b/sdk/python/examples/services/screen_brightness/basic/main.py @@ -0,0 +1,109 @@ +import flet as ft + + +async def main(page: ft.Page): + sb = ft.ScreenBrightness() + page.services.append(sb) + + info = ft.Text() + system_change = ft.Text() + app_change = ft.Text() + + level = ft.Slider(min=0, max=1, divisions=20, value=0.5, label="Brightness") + animate_switch = ft.Switch( + label="Animate application changes", value=True, on_change=None + ) + auto_reset_switch = ft.Switch( + label="Auto-reset application brightness on lifecycle changes", + value=True, + on_change=None, + ) + + async def refresh_info(): + system_brightness = await sb.get_system_screen_brightness() + app_brightness = await sb.get_application_screen_brightness() + can_change_system = await sb.can_change_system_screen_brightness() + + animate_switch.value = await sb.is_animate() + auto_reset_switch.value = await sb.is_auto_reset() + + info.value = ( + f"System: {system_brightness:.2f} | " + f"Application: {app_brightness:.2f} | " + f"Can change system: {can_change_system}" + ) + + async def set_application_brightness(_): + await sb.set_application_screen_brightness(level.value) + await refresh_info() + + async def set_system_brightness(_): + await sb.set_system_screen_brightness(level.value) + await refresh_info() + + async def reset_application_brightness(_): + await sb.reset_application_screen_brightness() + await refresh_info() + + async def toggle_animate(e: ft.Event[ft.Switch]): + await sb.set_animate(e.control.value) + await refresh_info() + + async def toggle_auto_reset(e: ft.Event[ft.Switch]): + await sb.set_auto_reset(e.control.value) + await refresh_info() + + async def on_system_change(e: ft.ScreenBrightnessChangeEvent): + system_change.value = f"System brightness changed: {e.brightness:.2f}" + + async def on_application_change(e: ft.ScreenBrightnessChangeEvent): + app_change.value = f"Application brightness changed: {e.brightness:.2f}" + + sb.on_system_screen_brightness_change = on_system_change + sb.on_application_screen_brightness_change = on_application_change + + await refresh_info() + + animate_switch.on_change = toggle_animate + auto_reset_switch.on_change = toggle_auto_reset + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + [ + info, + level, + ft.Row( + [ + ft.Button( + "Set application", + on_click=set_application_brightness, + ), + ft.Button( + "Set system", on_click=set_system_brightness + ), + ft.TextButton( + "Reset application", + on_click=reset_application_brightness, + ), + ], + wrap=True, + ), + animate_switch, + auto_reset_switch, + ft.Divider(), + system_change, + app_change, + ], + spacing=12, + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/screen_brightness/basic/pyproject.toml b/sdk/python/examples/services/screen_brightness/basic/pyproject.toml new file mode 100644 index 0000000000..cb1b2bb5eb --- /dev/null +++ b/sdk/python/examples/services/screen_brightness/basic/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-screen-brightness-basic" +version = "1.0.0" +description = "Reads and adjusts system and application brightness with live change notifications." +requires-python = ">=3.10" +keywords = ["screen brightness", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/ScreenBrightness"] + +[tool.flet.metadata] +title = "Basic screen brightness" +controls = ["SafeArea", "Column", "Page", "ScreenBrightness", "Text", "Slider", "Switch", "Row"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["brightness control", "change events"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["android", "ios"] diff --git a/sdk/python/examples/services/secure_storage/basic.py b/sdk/python/examples/services/secure_storage/basic.py deleted file mode 100644 index 6e65a34fba..0000000000 --- a/sdk/python/examples/services/secure_storage/basic.py +++ /dev/null @@ -1,94 +0,0 @@ -import base64 -import os - -import flet as ft -import flet_secure_storage as fss - - -def main(page: ft.Page): - page.vertical_alignment = ft.MainAxisAlignment.CENTER - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - storage = fss.SecureStorage( - web_options=fss.WebOptions( - db_name="customstorage", - public_key="publickey", - wrap_key=base64.urlsafe_b64encode(os.urandom(32)).decode(), - wrap_key_iv=base64.urlsafe_b64encode(os.urandom(16)).decode(), - ), - android_options=fss.AndroidOptions( - reset_on_error=True, - migrate_on_algorithm_change=True, - enforce_biometrics=True, - key_cipher_algorithm=fss.KeyCipherAlgorithm.AES_GCM_NO_PADDING, - storage_cipher_algorithm=fss.StorageCipherAlgorithm.AES_GCM_NO_PADDING, - ), - ) - - key = ft.TextField(label="Key", value="example_key") - value = ft.TextField(label="Value", value="secret_value") - result = ft.Text(theme_style=ft.TextThemeStyle.TITLE_LARGE) - - async def set_value(e): - await storage.set(key.value, value.value) - result.value = "Value saved" - page.update() - - async def get_value(e): - result.value = await storage.get(key.value) - page.update() - - async def remove_value(e): - await storage.remove(key.value) - result.value = "Value removed" - page.update() - - async def clear_storage(e): - await storage.clear() - result.value = "Storage cleared" - page.update() - - async def contains_key(e): - exists = await storage.contains_key(key.value) - result.value = f"Key exists: {exists}" - page.update() - - async def get_availability(e): - is_availability = await storage.get_availability() - page.show_dialog( - ft.SnackBar( - content=ft.Text( - value=f"Protected data available: {is_availability}" - if is_availability - else "Protected data available: None" - ) - ) - ) - page.update() - - page.add( - ft.Column( - alignment=ft.MainAxisAlignment.CENTER, - horizontal_alignment=ft.CrossAxisAlignment.CENTER, - spacing=10, - controls=[ - result, - key, - value, - ft.Row( - width=300, - wrap=True, - controls=[ - ft.Button("Set", on_click=set_value), - ft.Button("Get", on_click=get_value), - ft.Button("Contains key", on_click=contains_key), - ft.Button("Remove", on_click=remove_value), - ft.Button("Clear", on_click=clear_storage), - ft.Button("Check Data Availability", on_click=get_availability), - ], - ), - ], - ), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/secure_storage/basic/main.py b/sdk/python/examples/services/secure_storage/basic/main.py new file mode 100644 index 0000000000..178b664f94 --- /dev/null +++ b/sdk/python/examples/services/secure_storage/basic/main.py @@ -0,0 +1,98 @@ +import base64 +import os + +import flet as ft +import flet_secure_storage as fss + + +def main(page: ft.Page): + page.vertical_alignment = ft.MainAxisAlignment.CENTER + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + storage = fss.SecureStorage( + web_options=fss.WebOptions( + db_name="customstorage", + public_key="publickey", + wrap_key=base64.urlsafe_b64encode(os.urandom(32)).decode(), + wrap_key_iv=base64.urlsafe_b64encode(os.urandom(16)).decode(), + ), + android_options=fss.AndroidOptions( + reset_on_error=True, + migrate_on_algorithm_change=True, + enforce_biometrics=True, + key_cipher_algorithm=fss.KeyCipherAlgorithm.AES_GCM_NO_PADDING, + storage_cipher_algorithm=fss.StorageCipherAlgorithm.AES_GCM_NO_PADDING, + ), + ) + + key = ft.TextField(label="Key", value="example_key") + value = ft.TextField(label="Value", value="secret_value") + result = ft.Text(theme_style=ft.TextThemeStyle.TITLE_LARGE) + + async def set_value(e): + await storage.set(key.value, value.value) + result.value = "Value saved" + + async def get_value(e): + result.value = await storage.get(key.value) + + async def remove_value(e): + await storage.remove(key.value) + result.value = "Value removed" + + async def clear_storage(e): + await storage.clear() + result.value = "Storage cleared" + + async def contains_key(e): + exists = await storage.contains_key(key.value) + result.value = f"Key exists: {exists}" + + async def get_availability(e): + is_availability = await storage.get_availability() + page.show_dialog( + ft.SnackBar( + content=ft.Text( + value=f"Protected data available: {is_availability}" + if is_availability + else "Protected data available: None" + ) + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + spacing=10, + controls=[ + result, + key, + value, + ft.Row( + width=300, + wrap=True, + controls=[ + ft.Button("Set", on_click=set_value), + ft.Button("Get", on_click=get_value), + ft.Button("Contains key", on_click=contains_key), + ft.Button("Remove", on_click=remove_value), + ft.Button("Clear", on_click=clear_storage), + ft.Button( + "Check Data Availability", + on_click=get_availability, + ), + ], + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/secure_storage/basic/pyproject.toml b/sdk/python/examples/services/secure_storage/basic/pyproject.toml new file mode 100644 index 0000000000..3edbb5372f --- /dev/null +++ b/sdk/python/examples/services/secure_storage/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-secure-storage-basic" +version = "1.0.0" +description = "Stores, reads, removes, and clears protected key-value entries in secure storage." +requires-python = ">=3.10" +keywords = ["secure storage", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet", "flet-secure-storage"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/SecureStorage"] + +[tool.flet.metadata] +title = "Basic secure storage" +controls = ["SafeArea", "Column", "Page", "SecureStorage", "TextField", "Text", "Row", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["protected storage", "key-value operations"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/semantics_service/accessibility_features.py b/sdk/python/examples/services/semantics_service/accessibility_features.py deleted file mode 100644 index 3c5fe50e12..0000000000 --- a/sdk/python/examples/services/semantics_service/accessibility_features.py +++ /dev/null @@ -1,33 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - page.title = "SemanticsService - Accessibility features" - page.horizontal_alignment = ft.CrossAxisAlignment.CENTER - page.vertical_alignment = ft.MainAxisAlignment.CENTER - - async def get_features() -> str: - features = await ft.SemanticsService().get_accessibility_features() - return "\n".join( - [ - f"Accessible navigation: {features.accessible_navigation}", - f"Bold text: {features.bold_text}", - f"Disable animations: {features.disable_animations}", - f"High contrast: {features.high_contrast}", - f"Invert colors: {features.invert_colors}", - f"Reduce motion: {features.reduce_motion}", - f"On/off switch labels: {features.on_off_switch_labels}", - f"Supports announcements: {features.supports_announcements}", - ] - ) - - async def refresh_features(e: ft.Event[ft.Button]): - info.value = await get_features() - - page.add( - info := ft.Text(await get_features()), - ft.Button("Refresh features", on_click=refresh_features), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/semantics_service/accessibility_features/main.py b/sdk/python/examples/services/semantics_service/accessibility_features/main.py new file mode 100644 index 0000000000..768bf9f44b --- /dev/null +++ b/sdk/python/examples/services/semantics_service/accessibility_features/main.py @@ -0,0 +1,40 @@ +import flet as ft + + +async def main(page: ft.Page): + page.title = "SemanticsService - Accessibility features" + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.vertical_alignment = ft.MainAxisAlignment.CENTER + + async def get_features() -> str: + features = await ft.SemanticsService().get_accessibility_features() + return "\n".join( + [ + f"Accessible navigation: {features.accessible_navigation}", + f"Bold text: {features.bold_text}", + f"Disable animations: {features.disable_animations}", + f"High contrast: {features.high_contrast}", + f"Invert colors: {features.invert_colors}", + f"Reduce motion: {features.reduce_motion}", + f"On/off switch labels: {features.on_off_switch_labels}", + f"Supports announcements: {features.supports_announcements}", + ] + ) + + async def refresh_features(e: ft.Event[ft.Button]): + info.value = await get_features() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + info := ft.Text(await get_features()), + ft.Button("Refresh features", on_click=refresh_features), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/semantics_service/accessibility_features/pyproject.toml b/sdk/python/examples/services/semantics_service/accessibility_features/pyproject.toml new file mode 100644 index 0000000000..bd2f6a04da --- /dev/null +++ b/sdk/python/examples/services/semantics_service/accessibility_features/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-semantics-service-accessibility-features" +version = "1.0.0" +description = "Reads accessibility feature flags from SemanticsService and refreshes them on demand." +requires-python = ">=3.10" +keywords = ["semantics service", "accessibility features", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/SemanticsService"] + +[tool.flet.metadata] +title = "Accessibility features" +controls = ["SafeArea", "Column", "Page", "SemanticsService", "Button", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["accessibility features"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/shake_detector/basic.py b/sdk/python/examples/services/shake_detector/basic.py deleted file mode 100644 index 09d0a63c89..0000000000 --- a/sdk/python/examples/services/shake_detector/basic.py +++ /dev/null @@ -1,17 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - page.services.append( - ft.ShakeDetector( - minimum_shake_count=2, - shake_slop_time_ms=300, - shake_count_reset_time_ms=1000, - on_shake=lambda _: page.add(ft.Text("Shake detected!")), - ) - ) - - page.add(ft.Text("Shake your device!")) - - -ft.run(main) diff --git a/sdk/python/examples/services/shake_detector/basic/main.py b/sdk/python/examples/services/shake_detector/basic/main.py new file mode 100644 index 0000000000..eb4b47ecc5 --- /dev/null +++ b/sdk/python/examples/services/shake_detector/basic/main.py @@ -0,0 +1,24 @@ +import flet as ft + + +def main(page: ft.Page): + page.services.append( + ft.ShakeDetector( + minimum_shake_count=2, + shake_slop_time_ms=300, + shake_count_reset_time_ms=1000, + on_shake=lambda _: page.add(ft.Text("Shake detected!")), + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ft.Text("Shake your device!")], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/shake_detector/basic/pyproject.toml b/sdk/python/examples/services/shake_detector/basic/pyproject.toml new file mode 100644 index 0000000000..f0efd2e0ef --- /dev/null +++ b/sdk/python/examples/services/shake_detector/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-shake-detector-basic" +version = "1.0.0" +description = "Detects shake gestures and appends a message each time a shake is recognized." +requires-python = ">=3.10" +keywords = ["shake detector", "basic", "services"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/ShakeDetector"] + +[tool.flet.metadata] +title = "Basic shake detector" +controls = ["SafeArea", "Column", "Page", "ShakeDetector", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["shake detection"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/share/basic.py b/sdk/python/examples/services/share/basic.py deleted file mode 100644 index f600389ccd..0000000000 --- a/sdk/python/examples/services/share/basic.py +++ /dev/null @@ -1,83 +0,0 @@ -import os - -import flet as ft - - -async def main(page: ft.Page): - share = ft.Share() - - status = ft.Text() - result_raw = ft.Text() - - async def do_share_text(): - result = await share.share_text( - "Hello from Flet!", - subject="Greeting", - title="Share greeting", - ) - status.value = f"Share status: {result.status}" - result_raw.value = f"Raw: {result.raw}" - - async def do_share_uri(): - result = await share.share_uri("https://flet.dev") - status.value = f"Share status: {result.status}" - result_raw.value = f"Raw: {result.raw}" - - async def do_share_files_from_bytes(): - file = ft.ShareFile.from_bytes( - b"Sample content from memory", - mime_type="text/plain", - name="sample.txt", - ) - result = await share.share_files( - [file], - text="Sharing a file from memory", - ) - status.value = f"Share status: {result.status}" - result_raw.value = f"Raw: {result.raw}" - - async def do_share_files_from_paths(): - if page.web: - status.value = "File sharing from paths is not supported on the web." - return - # - temp_dir = await ft.StoragePaths().get_temporary_directory() - file_path = os.path.join(temp_dir, "sample_from_path.txt") - with open(file_path, "wb") as f: - f.write(b"Sample content from file path") - - result = await share.share_files( - [ft.ShareFile.from_path(file_path)], - text="Sharing a file from memory", - ) - status.value = f"Share status: {result.status}" - result_raw.value = f"Raw: {result.raw}" - - page.add( - ft.SafeArea( - ft.Column( - [ - ft.Row( - [ - ft.Button("Share text", on_click=do_share_text), - ft.Button("Share link", on_click=do_share_uri), - ft.Button( - "Share file from bytes", - on_click=do_share_files_from_bytes, - ), - ft.Button( - "Share file from path", - on_click=do_share_files_from_paths, - ), - ], - wrap=True, - ), - status, - result_raw, - ], - ) - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/share/basic/main.py b/sdk/python/examples/services/share/basic/main.py new file mode 100644 index 0000000000..56a339d125 --- /dev/null +++ b/sdk/python/examples/services/share/basic/main.py @@ -0,0 +1,84 @@ +import os + +import flet as ft + + +async def main(page: ft.Page): + share = ft.Share() + + status = ft.Text() + result_raw = ft.Text() + + async def do_share_text(): + result = await share.share_text( + "Hello from Flet!", + subject="Greeting", + title="Share greeting", + ) + status.value = f"Share status: {result.status}" + result_raw.value = f"Raw: {result.raw}" + + async def do_share_uri(): + result = await share.share_uri("https://flet.dev") + status.value = f"Share status: {result.status}" + result_raw.value = f"Raw: {result.raw}" + + async def do_share_files_from_bytes(): + file = ft.ShareFile.from_bytes( + b"Sample content from memory", + mime_type="text/plain", + name="sample.txt", + ) + result = await share.share_files( + [file], + text="Sharing a file from memory", + ) + status.value = f"Share status: {result.status}" + result_raw.value = f"Raw: {result.raw}" + + async def do_share_files_from_paths(): + if page.web: + status.value = "File sharing from paths is not supported on the web." + return + # + temp_dir = await ft.StoragePaths().get_temporary_directory() + file_path = os.path.join(temp_dir, "sample_from_path.txt") + with open(file_path, "wb") as f: + f.write(b"Sample content from file path") + + result = await share.share_files( + [ft.ShareFile.from_path(file_path)], + text="Sharing a file from memory", + ) + status.value = f"Share status: {result.status}" + result_raw.value = f"Raw: {result.raw}" + + page.add( + ft.SafeArea( + ft.Column( + [ + ft.Row( + [ + ft.Button("Share text", on_click=do_share_text), + ft.Button("Share link", on_click=do_share_uri), + ft.Button( + "Share file from bytes", + on_click=do_share_files_from_bytes, + ), + ft.Button( + "Share file from path", + on_click=do_share_files_from_paths, + ), + ], + wrap=True, + ), + status, + result_raw, + ], + ) + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/share/basic/pyproject.toml b/sdk/python/examples/services/share/basic/pyproject.toml new file mode 100644 index 0000000000..14f7b4d308 --- /dev/null +++ b/sdk/python/examples/services/share/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-share-basic" +version = "1.0.0" +description = "Shares text, links, and files while showing the raw share result status." +requires-python = ">=3.10" +keywords = ["share", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Share"] + +[tool.flet.metadata] +title = "Basic share" +controls = ["SafeArea", "Column", "Page", "Share", "Text", "Row", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["share sheet", "file sharing"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/sharedpreferences/basic.py b/sdk/python/examples/services/sharedpreferences/basic.py deleted file mode 100644 index 4222b97cd2..0000000000 --- a/sdk/python/examples/services/sharedpreferences/basic.py +++ /dev/null @@ -1,37 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - async def set_value(): - await ft.SharedPreferences().set(store_key.value, store_value.value) - get_key.value = store_key.value - store_key.value = "" - store_value.value = "" - page.show_dialog(ft.SnackBar("Value saved to SharedPreferences")) - - async def get_value(): - contents = await ft.SharedPreferences().get(get_key.value) - page.add(ft.Text(f"SharedPreferences contents: {contents}")) - - page.add( - ft.Column( - [ - ft.Row( - [ - store_key := ft.TextField(label="Key"), - store_value := ft.TextField(label="Value"), - ft.Button("Set", on_click=set_value), - ] - ), - ft.Row( - [ - get_key := ft.TextField(label="Key"), - ft.Button("Get", on_click=get_value), - ] - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/sharedpreferences/basic/main.py b/sdk/python/examples/services/sharedpreferences/basic/main.py new file mode 100644 index 0000000000..89f93cb338 --- /dev/null +++ b/sdk/python/examples/services/sharedpreferences/basic/main.py @@ -0,0 +1,44 @@ +import flet as ft + + +async def main(page: ft.Page): + async def set_value(): + await ft.SharedPreferences().set(store_key.value, store_value.value) + get_key.value = store_key.value + store_key.value = "" + store_value.value = "" + page.show_dialog(ft.SnackBar("Value saved to SharedPreferences")) + + async def get_value(): + contents = await ft.SharedPreferences().get(get_key.value) + page.add(ft.Text(f"SharedPreferences contents: {contents}")) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + [ + ft.Row( + [ + store_key := ft.TextField(label="Key"), + store_value := ft.TextField(label="Value"), + ft.Button("Set", on_click=set_value), + ] + ), + ft.Row( + [ + get_key := ft.TextField(label="Key"), + ft.Button("Get", on_click=get_value), + ] + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/sharedpreferences/basic/pyproject.toml b/sdk/python/examples/services/sharedpreferences/basic/pyproject.toml new file mode 100644 index 0000000000..16c3f3b86c --- /dev/null +++ b/sdk/python/examples/services/sharedpreferences/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-sharedpreferences-basic" +version = "1.0.0" +description = "Writes and reads plain SharedPreferences values using keyed inputs." +requires-python = ">=3.10" +keywords = ["sharedpreferences", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/SharedPreferences"] + +[tool.flet.metadata] +title = "Basic shared preferences" +controls = ["SafeArea", "Column", "Page", "SharedPreferences", "Text", "Row", "TextField", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["key-value storage"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/storagepaths/basic.py b/sdk/python/examples/services/storagepaths/basic.py deleted file mode 100644 index d9ab1e06e3..0000000000 --- a/sdk/python/examples/services/storagepaths/basic.py +++ /dev/null @@ -1,55 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - storage_paths = ft.StoragePaths() - - items = [] - for label, method in [ - ("Application cache directory", storage_paths.get_application_cache_directory), - ( - "Application documents directory", - storage_paths.get_application_documents_directory, - ), - ( - "Application support directory", - storage_paths.get_application_support_directory, - ), - ("Downloads directory", storage_paths.get_downloads_directory), - ("External cache directories", storage_paths.get_external_cache_directories), - ( - "External storage directories", - storage_paths.get_external_storage_directories, - ), - ("Library directory", storage_paths.get_library_directory), - ("External storage directory", storage_paths.get_external_storage_directory), - ("Temporary directory", storage_paths.get_temporary_directory), - ("Console log filename", storage_paths.get_console_log_filename), - ]: - try: - value = await method() - except ft.FletUnsupportedPlatformException as e: - value = f"Not supported: {e}" - except Exception as e: - value = f"Error: {e}" - else: - if isinstance(value, list): - value = ", ".join(value) - elif value is None: - value = "Unavailable" - - items.append( - ft.Text( - spans=[ - ft.TextSpan( - f"{label}: ", style=ft.TextStyle(weight=ft.FontWeight.BOLD) - ), - ft.TextSpan(value), - ] - ) - ) - - page.add(ft.Column(items, spacing=5)) - - -ft.run(main) diff --git a/sdk/python/examples/services/storagepaths/basic/main.py b/sdk/python/examples/services/storagepaths/basic/main.py new file mode 100644 index 0000000000..2ceacefd62 --- /dev/null +++ b/sdk/python/examples/services/storagepaths/basic/main.py @@ -0,0 +1,62 @@ +import flet as ft + + +async def main(page: ft.Page): + storage_paths = ft.StoragePaths() + + items = [] + for label, method in [ + ("Application cache directory", storage_paths.get_application_cache_directory), + ( + "Application documents directory", + storage_paths.get_application_documents_directory, + ), + ( + "Application support directory", + storage_paths.get_application_support_directory, + ), + ("Downloads directory", storage_paths.get_downloads_directory), + ("External cache directories", storage_paths.get_external_cache_directories), + ( + "External storage directories", + storage_paths.get_external_storage_directories, + ), + ("Library directory", storage_paths.get_library_directory), + ("External storage directory", storage_paths.get_external_storage_directory), + ("Temporary directory", storage_paths.get_temporary_directory), + ("Console log filename", storage_paths.get_console_log_filename), + ]: + try: + value = await method() + except ft.FletUnsupportedPlatformException as e: + value = f"Not supported: {e}" + except Exception as e: + value = f"Error: {e}" + else: + if isinstance(value, list): + value = ", ".join(value) + elif value is None: + value = "Unavailable" + + items.append( + ft.Text( + spans=[ + ft.TextSpan( + f"{label}: ", style=ft.TextStyle(weight=ft.FontWeight.BOLD) + ), + ft.TextSpan(value), + ] + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ft.Column(items, spacing=5)], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/storagepaths/basic/pyproject.toml b/sdk/python/examples/services/storagepaths/basic/pyproject.toml new file mode 100644 index 0000000000..e17156a753 --- /dev/null +++ b/sdk/python/examples/services/storagepaths/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-storagepaths-basic" +version = "1.0.0" +description = "Lists storage path APIs and shows unsupported-path errors on the current platform." +requires-python = ">=3.10" +keywords = ["storagepaths", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Storagepaths"] + +[tool.flet.metadata] +title = "Basic storage paths" +controls = ["SafeArea", "Column", "Page", "Text"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["path discovery", "platform fallback"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/urllauncher/basic.py b/sdk/python/examples/services/urllauncher/basic.py deleted file mode 100644 index 874cfa3551..0000000000 --- a/sdk/python/examples/services/urllauncher/basic.py +++ /dev/null @@ -1,81 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - url_launcher = ft.UrlLauncher() - - url = ft.TextField(label="URL to open", value="https://flet.dev", expand=True) - status = ft.Text() - - async def can_launch(): - can = await url_launcher.can_launch_url(url.value) - status.value = f"Can launch: {can}" - - async def launch_default(): - await url_launcher.launch_url(url.value) - - async def launch_in_app_webview(): - await url_launcher.launch_url( - url.value, - mode=ft.LaunchMode.IN_APP_WEB_VIEW, - web_view_configuration=ft.WebViewConfiguration( - enable_javascript=True, enable_dom_storage=True - ), - ) - - async def launch_in_app_browser_view(): - await url_launcher.launch_url( - url.value, - mode=ft.LaunchMode.IN_APP_BROWSER_VIEW, - browser_configuration=ft.BrowserConfiguration(show_title=True), - ) - - async def launch_external(): - await url_launcher.launch_url( - url.value, - mode=ft.LaunchMode.EXTERNAL_APPLICATION, - web_only_window_name="_blank", - ) - - async def launch_popup(): - await url_launcher.open_window( - url.value, title="Flet popup", width=480, height=640 - ) - - async def close_webview(_): - supported = await url_launcher.supports_close_for_launch_mode( - ft.LaunchMode.IN_APP_WEB_VIEW - ) - if supported: - await url_launcher.close_in_app_web_view() - else: - status.value = "Close in-app web view not supported on this platform" - - page.add( - ft.Column( - [ - url, - ft.Row( - [ - ft.Button("Launch URL", on_click=launch_default), - ft.Button( - "Launch in-app webview", on_click=launch_in_app_webview - ), - ft.Button( - "Launch in-app browser view", - on_click=launch_in_app_browser_view, - ), - ft.Button("Launch external/new tab", on_click=launch_external), - ft.Button("Open popup window (web)", on_click=launch_popup), - ft.Button("Can launch?", on_click=can_launch), - ft.Button("Close in-app webview", on_click=close_webview), - ], - wrap=True, - ), - status, - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/urllauncher/basic/main.py b/sdk/python/examples/services/urllauncher/basic/main.py new file mode 100644 index 0000000000..87a65e52e7 --- /dev/null +++ b/sdk/python/examples/services/urllauncher/basic/main.py @@ -0,0 +1,96 @@ +import flet as ft + + +async def main(page: ft.Page): + url_launcher = ft.UrlLauncher() + + url = ft.TextField(label="URL to open", value="https://flet.dev", expand=True) + status = ft.Text() + + async def can_launch(): + can = await url_launcher.can_launch_url(url.value) + status.value = f"Can launch: {can}" + + async def launch_default(): + await url_launcher.launch_url(url.value) + + async def launch_in_app_webview(): + await url_launcher.launch_url( + url.value, + mode=ft.LaunchMode.IN_APP_WEB_VIEW, + web_view_configuration=ft.WebViewConfiguration( + enable_javascript=True, enable_dom_storage=True + ), + ) + + async def launch_in_app_browser_view(): + await url_launcher.launch_url( + url.value, + mode=ft.LaunchMode.IN_APP_BROWSER_VIEW, + browser_configuration=ft.BrowserConfiguration(show_title=True), + ) + + async def launch_external(): + await url_launcher.launch_url( + url.value, + mode=ft.LaunchMode.EXTERNAL_APPLICATION, + web_only_window_name="_blank", + ) + + async def launch_popup(): + await url_launcher.open_window( + url.value, title="Flet popup", width=480, height=640 + ) + + async def close_webview(_): + supported = await url_launcher.supports_close_for_launch_mode( + ft.LaunchMode.IN_APP_WEB_VIEW + ) + if supported: + await url_launcher.close_in_app_web_view() + else: + status.value = "Close in-app web view not supported on this platform" + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + [ + url, + ft.Row( + [ + ft.Button("Launch URL", on_click=launch_default), + ft.Button( + "Launch in-app webview", + on_click=launch_in_app_webview, + ), + ft.Button( + "Launch in-app browser view", + on_click=launch_in_app_browser_view, + ), + ft.Button( + "Launch external/new tab", + on_click=launch_external, + ), + ft.Button( + "Open popup window (web)", on_click=launch_popup + ), + ft.Button("Can launch?", on_click=can_launch), + ft.Button( + "Close in-app webview", on_click=close_webview + ), + ], + wrap=True, + ), + status, + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/urllauncher/basic/pyproject.toml b/sdk/python/examples/services/urllauncher/basic/pyproject.toml new file mode 100644 index 0000000000..55cbfbbd63 --- /dev/null +++ b/sdk/python/examples/services/urllauncher/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-urllauncher-basic" +version = "1.0.0" +description = "Launches URLs in multiple modes including web view, browser view, and popup windows." +requires-python = ">=3.10" +keywords = ["urllauncher", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/UrlLauncher"] + +[tool.flet.metadata] +title = "Basic URL launcher" +controls = ["SafeArea", "Column", "Page", "UrlLauncher", "TextField", "Text", "LaunchMode", "WebViewConfiguration"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["URL launch modes", "popup window"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/examples/services/user_accelerometer/basic.py b/sdk/python/examples/services/user_accelerometer/basic.py deleted file mode 100644 index db6067e7e3..0000000000 --- a/sdk/python/examples/services/user_accelerometer/basic.py +++ /dev/null @@ -1,29 +0,0 @@ -import flet as ft - - -def main(page: ft.Page): - def handle_reading(e: ft.UserAccelerometerReadingEvent): - reading.value = f"x={e.x:.2f} m/s^2, y={e.y:.2f} m/s^2, z={e.z:.2f} m/s^2" - page.update() - - def handle_error(e: ft.SensorErrorEvent): - page.add(ft.Text(f"UserAccelerometer error: {e.message}")) - - page.services.append( - ft.UserAccelerometer( - on_reading=handle_reading, - on_error=handle_error, - interval=ft.Duration(milliseconds=100), - ) - ) - - page.add( - ft.Text( - "Linear acceleration without gravity. " - "Keep the app running on a device with motion sensors." - ), - reading := ft.Text("Waiting for data..."), - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/user_accelerometer/basic/main.py b/sdk/python/examples/services/user_accelerometer/basic/main.py new file mode 100644 index 0000000000..e95fd81062 --- /dev/null +++ b/sdk/python/examples/services/user_accelerometer/basic/main.py @@ -0,0 +1,35 @@ +import flet as ft + + +def main(page: ft.Page): + def handle_reading(e: ft.UserAccelerometerReadingEvent): + reading.value = f"x={e.x:.2f} m/s^2, y={e.y:.2f} m/s^2, z={e.z:.2f} m/s^2" + + def handle_error(e: ft.SensorErrorEvent): + page.add(ft.Text(f"UserAccelerometer error: {e.message}")) + + page.services.append( + ft.UserAccelerometer( + on_reading=handle_reading, + on_error=handle_error, + interval=ft.Duration(milliseconds=100), + ) + ) + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Text( + "Linear acceleration without gravity. " + "Keep the app running on a device with motion sensors." + ), + reading := ft.Text("Waiting for data..."), + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/user_accelerometer/basic/pyproject.toml b/sdk/python/examples/services/user_accelerometer/basic/pyproject.toml new file mode 100644 index 0000000000..42270d9aa1 --- /dev/null +++ b/sdk/python/examples/services/user_accelerometer/basic/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "services-user-accelerometer-basic" +version = "1.0.0" +description = "Streams linear acceleration readings without gravity and reports sensor errors." +requires-python = ">=3.10" +keywords = ["user accelerometer", "basic", "services"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/UserAccelerometer"] + +[tool.flet.metadata] +title = "Basic user accelerometer" +controls = ["SafeArea", "Column", "Page", "UserAccelerometerReadingEvent", "Text", "UserAccelerometer"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["live sensor readings", "sensor error handling"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" +platforms = ["android", "ios", "web"] diff --git a/sdk/python/examples/services/wakelock/basic.py b/sdk/python/examples/services/wakelock/basic.py deleted file mode 100644 index 1adf678d6b..0000000000 --- a/sdk/python/examples/services/wakelock/basic.py +++ /dev/null @@ -1,39 +0,0 @@ -import flet as ft - - -async def main(page: ft.Page): - wakelock = ft.Wakelock() - - status = ft.Text() - - async def update_status(): - enabled = await wakelock.is_enabled() - status.value = f"Wakelock enabled: {enabled}" - - async def enable_lock(): - await wakelock.enable() - await update_status() - - async def disable_lock(): - await wakelock.disable() - await update_status() - - await update_status() - - page.add( - ft.Column( - [ - status, - ft.Row( - [ - ft.Button("Enable wakelock", on_click=enable_lock), - ft.Button("Disable wakelock", on_click=disable_lock), - ], - wrap=True, - ), - ], - ) - ) - - -ft.run(main) diff --git a/sdk/python/examples/services/wakelock/basic/main.py b/sdk/python/examples/services/wakelock/basic/main.py new file mode 100644 index 0000000000..bff1d937d5 --- /dev/null +++ b/sdk/python/examples/services/wakelock/basic/main.py @@ -0,0 +1,48 @@ +import flet as ft + + +async def main(page: ft.Page): + wakelock = ft.Wakelock() + + status = ft.Text() + + async def update_status(): + enabled = await wakelock.is_enabled() + status.value = f"Wakelock enabled: {enabled}" + + async def enable_lock(): + await wakelock.enable() + await update_status() + + async def disable_lock(): + await wakelock.disable() + await update_status() + + await update_status() + + page.add( + ft.SafeArea( + content=ft.Column( + controls=[ + ft.Column( + [ + status, + ft.Row( + [ + ft.Button("Enable wakelock", on_click=enable_lock), + ft.Button( + "Disable wakelock", on_click=disable_lock + ), + ], + wrap=True, + ), + ], + ) + ], + ), + ) + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/services/wakelock/basic/pyproject.toml b/sdk/python/examples/services/wakelock/basic/pyproject.toml new file mode 100644 index 0000000000..209319d073 --- /dev/null +++ b/sdk/python/examples/services/wakelock/basic/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "services-wakelock-basic" +version = "1.0.0" +description = "Enables and disables wakelock while showing the current wakelock state." +requires-python = ">=3.10" +keywords = ["wakelock", "basic", "services", "async"] +authors = [{ name = "Flet team", email = "hello@flet.dev" }] +dependencies = ["flet"] + +[dependency-groups] +dev = ["flet-cli", "flet-desktop", "flet-web"] + +[tool.flet.gallery] +categories = ["Services/Wakelock"] + +[tool.flet.metadata] +title = "Basic wakelock" +controls = ["SafeArea", "Column", "Page", "Wakelock", "Text", "Row", "Button"] +layout_pattern = "inline-actions" +complexity = "basic" +features = ["wakelock toggle"] + +[tool.flet] +org = "dev.flet" +company = "Flet" +copyright = "Copyright (C) 2023-2026 by Flet" diff --git a/sdk/python/packages/flet/docs/ads/bannerad.md b/sdk/python/packages/flet/docs/ads/bannerad.md index f0458af358..78c8cb9c7a 100644 --- a/sdk/python/packages/flet/docs/ads/bannerad.md +++ b/sdk/python/packages/flet/docs/ads/bannerad.md @@ -11,7 +11,7 @@ example_images: ../examples/controls/ads/media ### Example 1 ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.gif", width="80%") }} diff --git a/sdk/python/packages/flet/docs/ads/index.md b/sdk/python/packages/flet/docs/ads/index.md index 96e17dbf1d..5cd6abc047 100644 --- a/sdk/python/packages/flet/docs/ads/index.md +++ b/sdk/python/packages/flet/docs/ads/index.md @@ -101,7 +101,7 @@ testing purposes. ## Example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.gif", width="80%") }} diff --git a/sdk/python/packages/flet/docs/ads/interstitialad.md b/sdk/python/packages/flet/docs/ads/interstitialad.md index a97bf026ef..a7612db469 100644 --- a/sdk/python/packages/flet/docs/ads/interstitialad.md +++ b/sdk/python/packages/flet/docs/ads/interstitialad.md @@ -11,7 +11,7 @@ example_images: ../examples/controls/ads/media ### Example 1 ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.gif", width="80%") }} diff --git a/sdk/python/packages/flet/docs/audio/index.md b/sdk/python/packages/flet/docs/audio/index.md index 154086be69..34e35cf70b 100644 --- a/sdk/python/packages/flet/docs/audio/index.md +++ b/sdk/python/packages/flet/docs/audio/index.md @@ -65,7 +65,7 @@ for installing on other Linux distributions. ### Basic example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ## Description diff --git a/sdk/python/packages/flet/docs/audio_recorder/index.md b/sdk/python/packages/flet/docs/audio_recorder/index.md index 035aa9b6a0..d3d72a93fd 100644 --- a/sdk/python/packages/flet/docs/audio_recorder/index.md +++ b/sdk/python/packages/flet/docs/audio_recorder/index.md @@ -152,7 +152,7 @@ permissions = ["microphone"] ## Example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ## Description diff --git a/sdk/python/packages/flet/docs/camera/index.md b/sdk/python/packages/flet/docs/camera/index.md index 6177f4e600..c5bc62abb3 100644 --- a/sdk/python/packages/flet/docs/camera/index.md +++ b/sdk/python/packages/flet/docs/camera/index.md @@ -115,7 +115,7 @@ permissions = ["camera", "microphone"] ## Example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ## Description diff --git a/sdk/python/packages/flet/docs/charts/barchart.md b/sdk/python/packages/flet/docs/charts/barchart.md index 015d390ec1..d09db6f1a7 100644 --- a/sdk/python/packages/flet/docs/charts/barchart.md +++ b/sdk/python/packages/flet/docs/charts/barchart.md @@ -12,7 +12,7 @@ diagram: assets/bar-chart-diagram.svg ### Example 1 ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.png", width="80%") }} @@ -20,7 +20,7 @@ diagram: assets/bar-chart-diagram.svg ### Example 2 ```python ---8<-- "{{ examples }}/example_2.py" +--8<-- "{{ examples }}/example_2/main.py" ``` {{ image(example_images + "/example_2.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/charts/candlestickchart.md b/sdk/python/packages/flet/docs/charts/candlestickchart.md index 1784c561f2..a74ae51e93 100644 --- a/sdk/python/packages/flet/docs/charts/candlestickchart.md +++ b/sdk/python/packages/flet/docs/charts/candlestickchart.md @@ -11,7 +11,7 @@ example_images: ../test-images-charts/examples/golden/macos/candlestick_chart ### Basic Candlestick Chart ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/charts/linechart.md b/sdk/python/packages/flet/docs/charts/linechart.md index f8bfbd0205..5418b48e38 100644 --- a/sdk/python/packages/flet/docs/charts/linechart.md +++ b/sdk/python/packages/flet/docs/charts/linechart.md @@ -12,7 +12,7 @@ diagram: assets/line-chart-diagram.svg ### Example 1 ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.png", width="80%") }} @@ -20,7 +20,7 @@ diagram: assets/line-chart-diagram.svg ### Example 2 ```python ---8<-- "{{ examples }}/example_2.py" +--8<-- "{{ examples }}/example_2/main.py" ``` {{ image(example_images + "/example_2.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/charts/matplotlibchart.md b/sdk/python/packages/flet/docs/charts/matplotlibchart.md index 5a295172b0..fe75709286 100644 --- a/sdk/python/packages/flet/docs/charts/matplotlibchart.md +++ b/sdk/python/packages/flet/docs/charts/matplotlibchart.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/charts/matplotlib_chart/media Based on an official [Matplotlib example](https://matplotlib.org/stable/gallery/lines_bars_and_markers/bar_colors.html#sphx-glr-gallery-lines-bars-and-markers-bar-colors-py). ```python ---8<-- "{{ examples }}/bar_chart.py" +--8<-- "{{ examples }}/bar_chart/main.py" ``` {{ image(example_images + "/bar_chart.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/charts/matplotlibchartwithtoolbar.md b/sdk/python/packages/flet/docs/charts/matplotlibchartwithtoolbar.md index 13759dc822..262ad9ed9c 100644 --- a/sdk/python/packages/flet/docs/charts/matplotlibchartwithtoolbar.md +++ b/sdk/python/packages/flet/docs/charts/matplotlibchartwithtoolbar.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/charts/matplotlib_chart/media Based on an official [Matplotlib example](https://matplotlib.org/stable/gallery/lines_bars_and_markers/cohere.html#sphx-glr-gallery-lines-bars-and-markers-cohere-py). ```python ---8<-- "{{ examples }}/toolbar.py" +--8<-- "{{ examples }}/toolbar/main.py" ``` {{ image(example_images + "/toolbar.png", width="80%") }} @@ -22,7 +22,7 @@ Based on an official [Matplotlib example](https://matplotlib.org/stable/gallery/ ### 3D chart ```python ---8<-- "{{ examples }}/three_d.py" +--8<-- "{{ examples }}/three_d/main.py" ``` {{ image(example_images + "/three_d.png", width="80%") }} @@ -30,7 +30,7 @@ Based on an official [Matplotlib example](https://matplotlib.org/stable/gallery/ ### Handle events ```python ---8<-- "{{ examples }}/handle_events.py" +--8<-- "{{ examples }}/handle_events/main.py" ``` {{ image(example_images + "/handle_events.png", width="80%") }} @@ -38,7 +38,7 @@ Based on an official [Matplotlib example](https://matplotlib.org/stable/gallery/ ### Animated chart ```python ---8<-- "{{ examples }}/animate.py" +--8<-- "{{ examples }}/animate/main.py" ``` {{ image(example_media + "/animate.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/charts/piechart.md b/sdk/python/packages/flet/docs/charts/piechart.md index d0976a1356..bc4a849c73 100644 --- a/sdk/python/packages/flet/docs/charts/piechart.md +++ b/sdk/python/packages/flet/docs/charts/piechart.md @@ -12,7 +12,7 @@ diagram: assets/pie-chart-diagram.svg ### Example 1 ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.png", width="80%") }} @@ -20,7 +20,7 @@ diagram: assets/pie-chart-diagram.svg ### Example 2 ```python ---8<-- "{{ examples }}/example_2.py" +--8<-- "{{ examples }}/example_2/main.py" ``` {{ image(example_images + "/example_2.png", width="80%") }} @@ -28,7 +28,7 @@ diagram: assets/pie-chart-diagram.svg ### Example 3 ```python ---8<-- "{{ examples }}/example_3.py" +--8<-- "{{ examples }}/example_3/main.py" ``` {{ image(example_images + "/example_3.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/charts/plotlychart.md b/sdk/python/packages/flet/docs/charts/plotlychart.md index ee67751676..99a2849704 100644 --- a/sdk/python/packages/flet/docs/charts/plotlychart.md +++ b/sdk/python/packages/flet/docs/charts/plotlychart.md @@ -13,7 +13,7 @@ example_images: ../test-images-charts/examples/golden/macos/plotly_chart Based on an official [Plotly example](https://plotly.com/python/line-charts). ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.png", width="80%") }} @@ -23,7 +23,7 @@ Based on an official [Plotly example](https://plotly.com/python/line-charts). Based on an official [Plotly example](https://plotly.com/python/bar-charts). ```python ---8<-- "{{ examples }}/example_2.py" +--8<-- "{{ examples }}/example_2/main.py" ``` {{ image(example_images + "/example_2.png", width="80%") }} @@ -33,7 +33,7 @@ Based on an official [Plotly example](https://plotly.com/python/bar-charts). Based on an official [Plotly example](https://plotly.com/python/pie-charts). ```python ---8<-- "{{ examples }}/example_3.py" +--8<-- "{{ examples }}/example_3/main.py" ``` {{ image(example_images + "/example_3.png", width="80%") }} @@ -43,7 +43,7 @@ Based on an official [Plotly example](https://plotly.com/python/pie-charts). Based on an official [Plotly example](https://plotly.com/python/box-plots). ```python ---8<-- "{{ examples }}/example_4.py" +--8<-- "{{ examples }}/example_4/main.py" ``` {{ image(example_images + "/example_4.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/charts/radarchart.md b/sdk/python/packages/flet/docs/charts/radarchart.md index 04c50c3d90..2c615cc8b4 100644 --- a/sdk/python/packages/flet/docs/charts/radarchart.md +++ b/sdk/python/packages/flet/docs/charts/radarchart.md @@ -11,7 +11,7 @@ example_images: ../test-images-charts/examples/golden/macos/radar_chart ### Example 1 ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/charts/scatterchart.md b/sdk/python/packages/flet/docs/charts/scatterchart.md index 4f29b1a9dd..704d61b952 100644 --- a/sdk/python/packages/flet/docs/charts/scatterchart.md +++ b/sdk/python/packages/flet/docs/charts/scatterchart.md @@ -11,7 +11,7 @@ example_images: ../test-images-charts/examples/golden/macos/scatter_chart ### Basic Scatter Chart ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/codeeditor/index.md b/sdk/python/packages/flet/docs/codeeditor/index.md index 4b97006ed8..3be94accae 100644 --- a/sdk/python/packages/flet/docs/codeeditor/index.md +++ b/sdk/python/packages/flet/docs/codeeditor/index.md @@ -28,7 +28,7 @@ pip install flet-code-editor # (1)! ### Basic example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ image(example_images + "/example_1.png", alt="code-editor-example-1", width="80%") }} @@ -36,7 +36,7 @@ pip install flet-code-editor # (1)! ### Selection handling ```python ---8<-- "{{ examples }}/example_2.py" +--8<-- "{{ examples }}/example_2/main.py" ``` {{ image(example_images + "/example_2.png", alt="code-editor-example-2", width="80%") }} @@ -44,7 +44,7 @@ pip install flet-code-editor # (1)! ### Folding and initial selection ```python ---8<-- "{{ examples }}/example_3.py" +--8<-- "{{ examples }}/example_3/main.py" ``` {{ image(example_images + "/example_3.png", alt="code-editor-example-3", width="80%") }} diff --git a/sdk/python/packages/flet/docs/colorpickers/blockpicker.md b/sdk/python/packages/flet/docs/colorpickers/blockpicker.md index e1d87a53a5..71b6f36f38 100644 --- a/sdk/python/packages/flet/docs/colorpickers/blockpicker.md +++ b/sdk/python/packages/flet/docs/colorpickers/blockpicker.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/extensions/color_pickers/golden/macos/co ## Example ```python ---8<-- "{{ examples }}/example_5.py" +--8<-- "{{ examples }}/example_5/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/colorpickers/colorpicker.md b/sdk/python/packages/flet/docs/colorpickers/colorpicker.md index ea716db5f9..79e2136e59 100644 --- a/sdk/python/packages/flet/docs/colorpickers/colorpicker.md +++ b/sdk/python/packages/flet/docs/colorpickers/colorpicker.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/extensions/color_pickers/golden/macos/co ## Example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/colorpickers/hueringpicker.md b/sdk/python/packages/flet/docs/colorpickers/hueringpicker.md index 8ec1983b3d..953b34fbe6 100644 --- a/sdk/python/packages/flet/docs/colorpickers/hueringpicker.md +++ b/sdk/python/packages/flet/docs/colorpickers/hueringpicker.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/extensions/color_pickers/golden/macos/co ## Example ```python ---8<-- "{{ examples }}/example_2.py" +--8<-- "{{ examples }}/example_2/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/colorpickers/materialpicker.md b/sdk/python/packages/flet/docs/colorpickers/materialpicker.md index 810f31be64..9b7a996891 100644 --- a/sdk/python/packages/flet/docs/colorpickers/materialpicker.md +++ b/sdk/python/packages/flet/docs/colorpickers/materialpicker.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/extensions/color_pickers/golden/macos/co ## Example ```python ---8<-- "{{ examples }}/example_4.py" +--8<-- "{{ examples }}/example_4/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/colorpickers/multiplechoiceblockpicker.md b/sdk/python/packages/flet/docs/colorpickers/multiplechoiceblockpicker.md index df323be40e..5e57efc3b1 100644 --- a/sdk/python/packages/flet/docs/colorpickers/multiplechoiceblockpicker.md +++ b/sdk/python/packages/flet/docs/colorpickers/multiplechoiceblockpicker.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/extensions/color_pickers/golden/macos/co ## Example ```python ---8<-- "{{ examples }}/example_6.py" +--8<-- "{{ examples }}/example_6/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/colorpickers/slidepicker.md b/sdk/python/packages/flet/docs/colorpickers/slidepicker.md index 83388f24bb..c0432cb7b9 100644 --- a/sdk/python/packages/flet/docs/colorpickers/slidepicker.md +++ b/sdk/python/packages/flet/docs/colorpickers/slidepicker.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/extensions/color_pickers/golden/macos/co ## Example ```python ---8<-- "{{ examples }}/example_3.py" +--8<-- "{{ examples }}/example_3/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/alertdialog.md b/sdk/python/packages/flet/docs/controls/alertdialog.md index f911ec31cb..b77db22267 100644 --- a/sdk/python/packages/flet/docs/controls/alertdialog.md +++ b/sdk/python/packages/flet/docs/controls/alertdialog.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/alert_dialog ### Modal and non-modal dialogs ```python ---8<-- "{{ examples }}/modal_and_non_modal.py" +--8<-- "{{ examples }}/modal_and_non_modal/main.py" ``` {{ image(example_images + "/alert_dialog_flow.gif", alt="Modal and non-modal dialogs", caption="Modal and non-modal dialogs",width="50%") }} diff --git a/sdk/python/packages/flet/docs/controls/animatedswitcher.md b/sdk/python/packages/flet/docs/controls/animatedswitcher.md index 44ee2ca039..cce198f6a0 100644 --- a/sdk/python/packages/flet/docs/controls/animatedswitcher.md +++ b/sdk/python/packages/flet/docs/controls/animatedswitcher.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/animated_switcher/media ### Animated switching between two containers with scale effect ```python ---8<-- "{{ examples }}/scale_effect.py" +--8<-- "{{ examples }}/scale_effect/main.py" ``` {{ image(example_images + "/scale_effect.gif", alt="scale-effect", width="80%") }} @@ -22,13 +22,13 @@ example_images: ../examples/controls/animated_switcher/media ### Animate Image switch ```python ---8<-- "{{ examples }}/image_switch.py" +--8<-- "{{ examples }}/image_switch/main.py" ``` ### Animate Image switch buffered ```python ---8<-- "{{ examples }}/image_switch_buffered.py" +--8<-- "{{ examples }}/image_switch_buffered/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/appbar.md b/sdk/python/packages/flet/docs/controls/appbar.md index 205c076c7b..98fb0c00a4 100644 --- a/sdk/python/packages/flet/docs/controls/appbar.md +++ b/sdk/python/packages/flet/docs/controls/appbar.md @@ -14,16 +14,16 @@ example_media: ../examples/controls/app_bar/media ### Actions and Popup Menu ```python ---8<-- "{{ examples }}/actions_and_popup_menu.py" +--8<-- "{{ examples }}/actions_and_popup_menu/main.py" ``` {{ image(example_media + "/actions_and_popup_menu.gif", alt="actions-and-popup-menu", width="80%") }} -### Theme and Material Mode Toggles +### Theme Mode Toggle ```python ---8<-- "{{ examples }}/theme_and_material_mode_toggles.py" +--8<-- "{{ examples }}/theme_mode_toggle/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/autocomplete.md b/sdk/python/packages/flet/docs/controls/autocomplete.md index 117b806fe0..cf38ec1a76 100644 --- a/sdk/python/packages/flet/docs/controls/autocomplete.md +++ b/sdk/python/packages/flet/docs/controls/autocomplete.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/auto_complete/media ### Basic example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` diff --git a/sdk/python/packages/flet/docs/controls/autofillgroup.md b/sdk/python/packages/flet/docs/controls/autofillgroup.md index bf62110a5f..64ff1c4e8f 100644 --- a/sdk/python/packages/flet/docs/controls/autofillgroup.md +++ b/sdk/python/packages/flet/docs/controls/autofillgroup.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/autofill_group/media ### Basic example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.gif", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/banner.md b/sdk/python/packages/flet/docs/controls/banner.md index 242aad0e43..72baf19628 100644 --- a/sdk/python/packages/flet/docs/controls/banner.md +++ b/sdk/python/packages/flet/docs/controls/banner.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/banner/media ### Basic example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/bottomappbar.md b/sdk/python/packages/flet/docs/controls/bottomappbar.md index e2b500afe5..d469bd9918 100644 --- a/sdk/python/packages/flet/docs/controls/bottomappbar.md +++ b/sdk/python/packages/flet/docs/controls/bottomappbar.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/bottom_app_bar ### Notched `FloatingActionButton` ```python ---8<-- "{{ examples }}/notched_fab.py" +--8<-- "{{ examples }}/notched_fab/main.py" ``` {{ image(example_images + "/notched_fab.png", width="80%") }} @@ -21,7 +21,7 @@ example_images: ../test-images/examples/material/golden/macos/bottom_app_bar ### Custom border radius ```python ---8<-- "{{ examples }}/border_radius.py" +--8<-- "{{ examples }}/border_radius/main.py" ``` {{ image(example_images + "/border_radius.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/bottomsheet.md b/sdk/python/packages/flet/docs/controls/bottomsheet.md index 53d4e5feb8..6c66c0c423 100644 --- a/sdk/python/packages/flet/docs/controls/bottomsheet.md +++ b/sdk/python/packages/flet/docs/controls/bottomsheet.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/bottom_sheet ### Basic example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.gif", width="60%") }} @@ -21,7 +21,7 @@ example_images: ../test-images/examples/material/golden/macos/bottom_sheet ### Fullscreen ```python ---8<-- "{{ examples }}/fullscreen.py" +--8<-- "{{ examples }}/fullscreen/main.py" ``` {{ image(example_images + "/fullscreen.gif", width="60%") }} diff --git a/sdk/python/packages/flet/docs/controls/button.md b/sdk/python/packages/flet/docs/controls/button.md index 181b9a35e7..a42b40b218 100644 --- a/sdk/python/packages/flet/docs/controls/button.md +++ b/sdk/python/packages/flet/docs/controls/button.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/button ### Button ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="Basic button", width="50%") }} @@ -22,7 +22,7 @@ example_images: ../test-images/examples/material/golden/macos/button ### Icons ```python ---8<-- "{{ examples }}/icons.py" +--8<-- "{{ examples }}/icons/main.py" ``` {{ image(example_images + "/icons.png", alt="Basic button", width="50%") }} @@ -31,7 +31,7 @@ example_images: ../test-images/examples/material/golden/macos/button ### Handling clicks ```python ---8<-- "{{ examples }}/handling_clicks.py" +--8<-- "{{ examples }}/handling_clicks/main.py" ``` {{ image(example_images + "/handling_clicks.png", alt="Handling clicks", width="50%") }} @@ -40,7 +40,7 @@ example_images: ../test-images/examples/material/golden/macos/button ### Custom content ```python ---8<-- "{{ examples }}/custom_content.py" +--8<-- "{{ examples }}/custom_content/main.py" ``` {{ image(example_images + "/custom_content.png", alt="Buttons with custom content", width="50%") }} @@ -49,7 +49,7 @@ example_images: ../test-images/examples/material/golden/macos/button ### Shapes ```python ---8<-- "{{ examples }}/button_shapes.py" +--8<-- "{{ examples }}/button_shapes/main.py" ``` {{ image(example_images + "/button_shapes.png", alt="Buttons with different shapes", width="50%") }} @@ -58,7 +58,7 @@ example_images: ../test-images/examples/material/golden/macos/button ### Styling ```python ---8<-- "{{ examples }}/styling.py" +--8<-- "{{ examples }}/styling/main.py" ``` {{ image(example_images + "/styled_initial.png", alt="Styled button - default state", width="50%", caption="Default state") }} @@ -70,7 +70,7 @@ example_images: ../test-images/examples/material/golden/macos/button ### Animate on hover ```python ---8<-- "{{ examples }}/animate_on_hover.py" +--8<-- "{{ examples }}/animate_on_hover/main.py" ``` {{ image(example_images + "/animate_on_hover_initial.png", alt="Unhovered button", width="50%", caption="Normal button") }} diff --git a/sdk/python/packages/flet/docs/controls/canvas/index.md b/sdk/python/packages/flet/docs/controls/canvas/index.md index cb5ce993a1..a20ec623ad 100644 --- a/sdk/python/packages/flet/docs/controls/canvas/index.md +++ b/sdk/python/packages/flet/docs/controls/canvas/index.md @@ -16,7 +16,7 @@ example_media: ../../examples/controls/canvas/media ### Smiling face ```python ---8<-- "{{ examples }}/smiling_face.py" +--8<-- "{{ examples }}/smiling_face/main.py" ``` {{ image(example_media + "/smiling_face.png", alt="smiling-face", width="80%") }} @@ -25,7 +25,7 @@ example_media: ../../examples/controls/canvas/media ### Flet logo ```python ---8<-- "{{ examples }}/flet_logo.py" +--8<-- "{{ examples }}/flet_logo/main.py" ``` {{ image(example_media + "/flet_logo.png", alt="flet-logo", width="80%") }} @@ -34,7 +34,7 @@ example_media: ../../examples/controls/canvas/media ### Triangles ```python ---8<-- "{{ examples }}/triangles.py" +--8<-- "{{ examples }}/triangles/main.py" ``` {{ image(example_media + "/triangles.png", alt="triangles", width="80%") }} @@ -43,7 +43,7 @@ example_media: ../../examples/controls/canvas/media ### Bezier curves ```python ---8<-- "{{ examples }}/bezier_curves.py" +--8<-- "{{ examples }}/bezier_curves/main.py" ``` {{ image(example_media + "/bezier_curves.png", alt="bezier-curves", width="80%") }} @@ -52,7 +52,7 @@ example_media: ../../examples/controls/canvas/media ### Text ```python ---8<-- "{{ examples }}/text.py" +--8<-- "{{ examples }}/text/main.py" ``` {{ image(example_media + "/text.png", alt="text", width="80%") }} @@ -61,13 +61,13 @@ example_media: ../../examples/controls/canvas/media ### Free-hand drawing with image capture ```python ---8<-- "{{ examples }}/brush.py" +--8<-- "{{ examples }}/brush/main.py" ``` ### Gradients ```python ---8<-- "{{ examples }}/gradients.py" +--8<-- "{{ examples }}/gradients/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/card.md b/sdk/python/packages/flet/docs/controls/card.md index bfa5caad0a..883ac92420 100644 --- a/sdk/python/packages/flet/docs/controls/card.md +++ b/sdk/python/packages/flet/docs/controls/card.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/material/golden/macos/card [Live example](https://flet-controls-gallery.fly.dev/layout/card) ```python ---8<-- "{{ examples }}/music_info.py" +--8<-- "{{ examples }}/music_info/main.py" ``` {{ image(example_images + "/music_info.png", alt="music-info", width="50%") }} diff --git a/sdk/python/packages/flet/docs/controls/checkbox.md b/sdk/python/packages/flet/docs/controls/checkbox.md index 401d0eaea2..63e8eac04f 100644 --- a/sdk/python/packages/flet/docs/controls/checkbox.md +++ b/sdk/python/packages/flet/docs/controls/checkbox.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/checkbox ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="50%", caption="After clicking Submit") }} @@ -22,7 +22,7 @@ example_images: ../test-images/examples/material/golden/macos/checkbox ### Handling events ```python ---8<-- "{{ examples }}/handling_events.py" +--8<-- "{{ examples }}/handling_events/main.py" ``` {{ image(example_images + "/handling_events.png", alt="handling-events", width="50%", caption="After three clicks") }} @@ -31,7 +31,7 @@ example_images: ../test-images/examples/material/golden/macos/checkbox ### Styled checkboxes ```python ---8<-- "{{ examples }}/styled.py" +--8<-- "{{ examples }}/styled/main.py" ``` {{ image(example_images + "/styled_checkboxes.png", alt="Styled checkboxes", width="50%") }} diff --git a/sdk/python/packages/flet/docs/controls/chip.md b/sdk/python/packages/flet/docs/controls/chip.md index a353f33e68..6a3f0eabf3 100644 --- a/sdk/python/packages/flet/docs/controls/chip.md +++ b/sdk/python/packages/flet/docs/controls/chip.md @@ -21,7 +21,7 @@ They represent smart or automated actions that appear dynamically and contextual An alternative to assist chips are buttons, which should appear persistently and consistently. ```python ---8<-- "{{ examples }}/assist_chips.py" +--8<-- "{{ examples }}/assist_chips/main.py" ``` {{ image(example_media + "/assist_chips.png", alt="assist-chips", width="80%") }} @@ -35,7 +35,7 @@ They use tags or descriptive words provided in the [`label`][flet.Chip.label] to They can be a good alternative to switches or checkboxes. ```python ---8<-- "{{ examples }}/filter_chips.py" +--8<-- "{{ examples }}/filter_chips/main.py" ``` {{ image(example_media + "/filter_chips.png", alt="filter-chips", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/circleavatar.md b/sdk/python/packages/flet/docs/controls/circleavatar.md index 3578e912e8..b06b9237a2 100644 --- a/sdk/python/packages/flet/docs/controls/circleavatar.md +++ b/sdk/python/packages/flet/docs/controls/circleavatar.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/circle_avatar/media ### User avatars ```python ---8<-- "{{ examples }}/user_avatars.py" +--8<-- "{{ examples }}/user_avatars/main.py" ``` {{ image(example_media + "/user_avatars.png", alt="user-avatars", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/column.md b/sdk/python/packages/flet/docs/controls/column.md index bc0b3523a1..49f019951d 100644 --- a/sdk/python/packages/flet/docs/controls/column.md +++ b/sdk/python/packages/flet/docs/controls/column.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/column/media ### Column `spacing` ```python ---8<-- "{{ examples }}/spacing.py" +--8<-- "{{ examples }}/spacing/main.py" ``` {{ image(example_media + "/spacing.gif", alt="spacing", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/column/media ### Column wrapping ```python ---8<-- "{{ examples }}/wrap.py" +--8<-- "{{ examples }}/wrap/main.py" ``` {{ image(example_media + "/wrap.gif", alt="wrap", width="80%") }} @@ -32,7 +32,7 @@ example_media: ../examples/controls/column/media ### Column vertical alignments ```python ---8<-- "{{ examples }}/alignment.py" +--8<-- "{{ examples }}/alignment/main.py" ``` {{ image(example_media + "/alignment.png", alt="alignment", width="80%") }} @@ -41,7 +41,7 @@ example_media: ../examples/controls/column/media ### Column horizontal alignments ```python ---8<-- "{{ examples }}/horizontal_alignment.py" +--8<-- "{{ examples }}/horizontal_alignment/main.py" ``` {{ image(example_media + "/horizontal_alignment.png", alt="horizontal-alignment", width="80%") }} @@ -53,7 +53,7 @@ This example demonstrates adding of list items on-the-fly, as user scroll to the creating the illusion of infinite list: ```python ---8<-- "{{ examples }}/infinite_scrolling.py" +--8<-- "{{ examples }}/infinite_scrolling/main.py" ``` ### Scrolling programmatically @@ -61,7 +61,7 @@ creating the illusion of infinite list: This example shows how to use [`scroll_to()`][flet.Column.scroll_to] to programmatically scroll a column: ```python ---8<-- "{{ examples }}/programmatic_scroll.py" +--8<-- "{{ examples }}/programmatic_scroll/main.py" ``` {{ image(example_media + "/programmatic_scroll.png", alt="programmatic-scroll", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/container.md b/sdk/python/packages/flet/docs/controls/container.md index fbc8b479a5..8c76c1e07e 100644 --- a/sdk/python/packages/flet/docs/controls/container.md +++ b/sdk/python/packages/flet/docs/controls/container.md @@ -14,7 +14,7 @@ example_images: ../test-images/examples/material/golden/macos/container ### Clickable container ```python ---8<-- "{{ examples }}/clickable.py" +--8<-- "{{ examples }}/clickable/main.py" ``` {{ image(example_media + "/clickable.gif", width="80%") }} @@ -22,7 +22,7 @@ example_images: ../test-images/examples/material/golden/macos/container ### Handling clicks ```python ---8<-- "{{ examples }}/handling_clicks.py" +--8<-- "{{ examples }}/handling_clicks/main.py" ``` {{ image(example_media + "/handling_clicks.gif", width="80%") }} @@ -31,7 +31,7 @@ example_images: ../test-images/examples/material/golden/macos/container ### Handling hovers ```python ---8<-- "{{ examples }}/handling_hovers.py" +--8<-- "{{ examples }}/handling_hovers/main.py" ``` {{ image(example_media + "/handling_hovers.gif", width="80%") }} @@ -40,7 +40,7 @@ example_images: ../test-images/examples/material/golden/macos/container ### Animate 1 ```python ---8<-- "{{ examples }}/animate_1.py" +--8<-- "{{ examples }}/animate_1/main.py" ``` {{ image(example_media + "/animate_1.gif", width="80%") }} @@ -49,25 +49,19 @@ example_images: ../test-images/examples/material/golden/macos/container ### Animate 2 ```python ---8<-- "{{ examples }}/animate_2.py" +--8<-- "{{ examples }}/animate_2/main.py" ``` ### Animate 3 ```python ---8<-- "{{ examples }}/animate_3.py" -``` - -### Animate 4 - -```python ---8<-- "{{ examples }}/animate_4.py" +--8<-- "{{ examples }}/animate_3/main.py" ``` ### Nested themes 1 ```python ---8<-- "{{ examples }}/nested_themes_1.py" +--8<-- "{{ examples }}/nested_themes_1/main.py" ``` {{ image(example_images + "/nested_themes_1.png", width="80%") }} @@ -75,7 +69,7 @@ example_images: ../test-images/examples/material/golden/macos/container ### Nested themes 2 ```python ---8<-- "{{ examples }}/nested_themes_2.py" +--8<-- "{{ examples }}/nested_themes_2/main.py" ``` {{ image(example_images + "/nested_themes_2.png", width="80%") }} @@ -83,7 +77,7 @@ example_images: ../test-images/examples/material/golden/macos/container ### Nested themes 3 ```python ---8<-- "{{ examples }}/nested_themes_3.py" +--8<-- "{{ examples }}/nested_themes_3/main.py" ``` {{ image(example_media + "/nested_themes_3.gif", width="80%") }} @@ -92,7 +86,7 @@ example_images: ../test-images/examples/material/golden/macos/container ### Size aware ```python ---8<-- "{{ examples }}/size_aware.py" +--8<-- "{{ examples }}/size_aware/main.py" ``` {{ image(example_images + "/size_aware.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/contextmenu.md b/sdk/python/packages/flet/docs/controls/contextmenu.md index 9b86b016b7..1dbc5de055 100644 --- a/sdk/python/packages/flet/docs/controls/contextmenu.md +++ b/sdk/python/packages/flet/docs/controls/contextmenu.md @@ -11,13 +11,13 @@ example_images: ../test-images/examples/material/golden/macos/context_menu ### Triggers ```python ---8<-- "{{ examples }}/triggers.py" +--8<-- "{{ examples }}/triggers/main.py" ``` ## Programmatic open ```python ---8<-- "{{ examples }}/programmatic_open.py" +--8<-- "{{ examples }}/programmatic_open/main.py" ``` {{ image(example_images + "/programmatic_open.png", width="80%") }} @@ -25,7 +25,7 @@ example_images: ../test-images/examples/material/golden/macos/context_menu ## Programmatic open with custom trigger ```python ---8<-- "{{ examples }}/custom_trigger.py" +--8<-- "{{ examples }}/custom_trigger/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinoactionsheet/index.md b/sdk/python/packages/flet/docs/controls/cupertinoactionsheet/index.md index 8381af463e..c44b13041a 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinoactionsheet/index.md +++ b/sdk/python/packages/flet/docs/controls/cupertinoactionsheet/index.md @@ -16,7 +16,7 @@ example_media: ../../examples/controls/cupertino_action_sheet/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinoactivityindicator.md b/sdk/python/packages/flet/docs/controls/cupertinoactivityindicator.md index fe223956bd..957a76d0ed 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinoactivityindicator.md +++ b/sdk/python/packages/flet/docs/controls/cupertinoactivityindicator.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_activity_indicator/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinoalertdialog.md b/sdk/python/packages/flet/docs/controls/cupertinoalertdialog.md deleted file mode 100644 index e0787a7e7e..0000000000 --- a/sdk/python/packages/flet/docs/controls/cupertinoalertdialog.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -class_name: flet.CupertinoAlertDialog -examples: ../../examples/controls/cupertino_alert_dialog -example_images: ../examples/controls/cupertino_alert_dialog/media ---- - -{{ class_summary(class_name) }} - -## Examples - -[Live example](https://flet-controls-gallery.fly.dev/dialogs/cupertinoalertdialog) - -### File deletion confirmation - -```python ---8<-- "{{ examples }}/file_deletion_confirmation.py" -``` - -{{ image(example_images + "/file_deletion_confirmation.png", alt="file-deletion-confirmation", width="80%") }} - - -### Cupertino, material and adaptive alert dialogs - -```python ---8<-- "{{ examples }}/cupertino_material_and_adaptive.py" -``` - -{{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinoalertdialog/index.md b/sdk/python/packages/flet/docs/controls/cupertinoalertdialog/index.md new file mode 100644 index 0000000000..a22c54cb61 --- /dev/null +++ b/sdk/python/packages/flet/docs/controls/cupertinoalertdialog/index.md @@ -0,0 +1,28 @@ +--- +class_name: flet.CupertinoAlertDialog +examples: ../../examples/controls/cupertino_alert_dialog +example_images: ../examples/controls/cupertino_alert_dialog/media +--- + +{{ class_summary(class_name) }} + +## Examples + +[Live example](https://flet-controls-gallery.fly.dev/dialogs/cupertinoalertdialog) + +### File deletion confirmation + +```python +--8<-- "{{ examples }}/file_deletion_confirmation/main.py" +``` + +{{ image(example_images + "/file_deletion_confirmation.png", alt="file-deletion-confirmation", width="80%") }} + + +### Cupertino, material and adaptive alert dialogs + +```python +--8<-- "{{ examples }}/cupertino_material_and_adaptive/main.py" +``` + +{{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinoappbar.md b/sdk/python/packages/flet/docs/controls/cupertinoappbar.md index 55f6c671af..dd44704b70 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinoappbar.md +++ b/sdk/python/packages/flet/docs/controls/cupertinoappbar.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/cupertino_app_bar/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} @@ -22,7 +22,7 @@ example_images: ../examples/controls/cupertino_app_bar/media ### App bar with theme mode toggle ```python ---8<-- "{{ examples }}/theme_mode_toggle.py" +--8<-- "{{ examples }}/theme_mode_toggle/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinobottomsheet.md b/sdk/python/packages/flet/docs/controls/cupertinobottomsheet.md index 4b9d7688c7..5e82474fd6 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinobottomsheet.md +++ b/sdk/python/packages/flet/docs/controls/cupertinobottomsheet.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/cupertino_action_sheet/media ### Displaying a `CupertinoActionSheet` ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="cupertinoactionsheet", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinobutton.md b/sdk/python/packages/flet/docs/controls/cupertinobutton.md index 77e0626954..05bd182b97 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinobutton.md +++ b/sdk/python/packages/flet/docs/controls/cupertinobutton.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_button/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinocheckbox.md b/sdk/python/packages/flet/docs/controls/cupertinocheckbox.md index bfaa1cf744..26bf32ac8a 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinocheckbox.md +++ b/sdk/python/packages/flet/docs/controls/cupertinocheckbox.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_checkbox/media ### Cupertino, Material and Adaptive Checkboxes ```python ---8<-- "{{ examples }}/cupertino_material_and_adaptive.py" +--8<-- "{{ examples }}/cupertino_material_and_adaptive/main.py" ``` {{ image(example_media + "/cupertino_material_and_adaptive.png", alt="cupertino-material-and-adaptive", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/cupertino_checkbox/media ### Styled checkboxes ```python ---8<-- "{{ examples }}/styled.py" +--8<-- "{{ examples }}/styled/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinocontextmenu/index.md b/sdk/python/packages/flet/docs/controls/cupertinocontextmenu/index.md index 52f1ecf94a..8a94324bb5 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinocontextmenu/index.md +++ b/sdk/python/packages/flet/docs/controls/cupertinocontextmenu/index.md @@ -15,7 +15,7 @@ example_images: ../../examples/controls/cupertino_context_menu/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.gif", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinodatepicker.md b/sdk/python/packages/flet/docs/controls/cupertinodatepicker.md index 55c770a045..1b523aa269 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinodatepicker.md +++ b/sdk/python/packages/flet/docs/controls/cupertinodatepicker.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/cupertino_date_picker/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinodialogaction.md b/sdk/python/packages/flet/docs/controls/cupertinodialogaction.md index 3a0efb5d44..f3f92a2f64 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinodialogaction.md +++ b/sdk/python/packages/flet/docs/controls/cupertinodialogaction.md @@ -2,8 +2,4 @@ class_name: flet.CupertinoDialogAction --- -## Examples - -See [these](cupertinoalertdialog.md#examples). - {{ class_all_options(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinofilledbutton.md b/sdk/python/packages/flet/docs/controls/cupertinofilledbutton.md index dd359b4378..1efbe87b31 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinofilledbutton.md +++ b/sdk/python/packages/flet/docs/controls/cupertinofilledbutton.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_filled_button/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinolisttile.md b/sdk/python/packages/flet/docs/controls/cupertinolisttile.md index 05e50dae56..fadececbbc 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinolisttile.md +++ b/sdk/python/packages/flet/docs/controls/cupertinolisttile.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_list_tile/media ### Notched and non-notched list tiles ```python ---8<-- "{{ examples }}/notched.py" +--8<-- "{{ examples }}/notched/main.py" ``` {{ image(example_media + "/notched.png", alt="notched", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinonavigationbar.md b/sdk/python/packages/flet/docs/controls/cupertinonavigationbar.md index 50fc6eec46..329b531068 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinonavigationbar.md +++ b/sdk/python/packages/flet/docs/controls/cupertinonavigationbar.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/cupertino_navigation_bar/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} @@ -22,7 +22,7 @@ example_images: ../examples/controls/cupertino_navigation_bar/media ### Wired navigation bar ```python ---8<-- "{{ examples }}/wired.py" +--8<-- "{{ examples }}/wired/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinopicker.md b/sdk/python/packages/flet/docs/controls/cupertinopicker.md index 4a1fbcccb2..1961781aac 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinopicker.md +++ b/sdk/python/packages/flet/docs/controls/cupertinopicker.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/cupertino_picker/media ### Fruit selection ```python ---8<-- "{{ examples }}/fruit_selection.py" +--8<-- "{{ examples }}/fruit_selection/main.py" ``` {{ image(example_images + "/fruit_selection.gif", alt="fruit-selection", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinoradio.md b/sdk/python/packages/flet/docs/controls/cupertinoradio.md index 0ae14770b6..8990c90243 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinoradio.md +++ b/sdk/python/packages/flet/docs/controls/cupertinoradio.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_radio/media ### Cupertino, Material and Adaptive Radios ```python ---8<-- "{{ examples }}/cupertino_material_and_adaptive.py" +--8<-- "{{ examples }}/cupertino_material_and_adaptive/main.py" ``` {{ image(example_media + "/cupertino_material_and_adaptive.png", alt="cupertino-material-and-adaptive", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinosegmentedbutton.md b/sdk/python/packages/flet/docs/controls/cupertinosegmentedbutton.md index 04336b8bd0..a798cc5eb2 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinosegmentedbutton.md +++ b/sdk/python/packages/flet/docs/controls/cupertinosegmentedbutton.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_segmented_button/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", alt="basic", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/cupertino_segmented_button/media ### Adjusting segments padding ```python ---8<-- "{{ examples }}/segments_padding.py" +--8<-- "{{ examples }}/segments_padding/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinoslider.md b/sdk/python/packages/flet/docs/controls/cupertinoslider.md index b02d01f95e..629a15b05b 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinoslider.md +++ b/sdk/python/packages/flet/docs/controls/cupertinoslider.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_slider/media ### Handling events ```python ---8<-- "{{ examples }}/handling_events.py" +--8<-- "{{ examples }}/handling_events/main.py" ``` {{ image(example_media + "/handling_events.gif", alt="handling-events", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinoslidingsegmentedbutton.md b/sdk/python/packages/flet/docs/controls/cupertinoslidingsegmentedbutton.md index f704395654..3cf152b26e 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinoslidingsegmentedbutton.md +++ b/sdk/python/packages/flet/docs/controls/cupertinoslidingsegmentedbutton.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_sliding_segmented_button/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinoswitch.md b/sdk/python/packages/flet/docs/controls/cupertinoswitch.md index 166e83016d..ebe725d331 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinoswitch.md +++ b/sdk/python/packages/flet/docs/controls/cupertinoswitch.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_switch/media ### Cupertino, Material and Adaptive Switches ```python ---8<-- "{{ examples }}/cupertino_material_and_adaptive.py" +--8<-- "{{ examples }}/cupertino_material_and_adaptive/main.py" ``` {{ image(example_media + "/cupertino_material_and_adaptive.gif", alt="cupertino-material-and-adaptive", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinotextfield.md b/sdk/python/packages/flet/docs/controls/cupertinotextfield.md index 47519e59ac..d817d80e9e 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinotextfield.md +++ b/sdk/python/packages/flet/docs/controls/cupertinotextfield.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/cupertino_text_field/media ### Basic Example ```python ---8<-- "{{ examples }}/cupertino_material_and_adaptive.py" +--8<-- "{{ examples }}/cupertino_material_and_adaptive/main.py" ``` {{ image(example_media + "/cupertino_material_and_adaptive.png", alt="cupertino-material-and-adaptive", width="80%") }} @@ -22,13 +22,13 @@ example_media: ../examples/controls/cupertino_text_field/media ### Handling selection changes ```python ---8<-- "{{ examples }}/selection_change.py" +--8<-- "{{ examples }}/selection_change/main.py" ``` ### Background image ```python ---8<-- "{{ examples }}/background_image.py" +--8<-- "{{ examples }}/background_image/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/cupertinotimerpicker.md b/sdk/python/packages/flet/docs/controls/cupertinotimerpicker.md index bc470e8ea0..9466aaff22 100644 --- a/sdk/python/packages/flet/docs/controls/cupertinotimerpicker.md +++ b/sdk/python/packages/flet/docs/controls/cupertinotimerpicker.md @@ -14,7 +14,7 @@ example_images: ../test-images/examples/cupertino/golden/macos/cupertino_timer_p ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/datatable/index.md b/sdk/python/packages/flet/docs/controls/datatable/index.md index fdf613b583..bf6a50a2b1 100644 --- a/sdk/python/packages/flet/docs/controls/datatable/index.md +++ b/sdk/python/packages/flet/docs/controls/datatable/index.md @@ -15,7 +15,7 @@ example_images: ../../test-images/examples/material/golden/macos/datatable ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", width="80%") }} @@ -28,7 +28,7 @@ edge spacing of the first and last columns. Use [`column_spacing`][flet.DataTable.column_spacing] to control spacing between columns. ```python ---8<-- "{{ examples }}/spacing.py" +--8<-- "{{ examples }}/spacing/main.py" ``` ### Adaptive row heights @@ -38,7 +38,7 @@ Setting [`data_row_max_height`][flet.DataTable.data_row_max_height] to `float('i respective content, instead of all rows having the same height. ```python ---8<-- "{{ examples }}/adaptive_row_heights.py" +--8<-- "{{ examples }}/adaptive_row_heights/main.py" ``` ### Sortable columns and selectable rows @@ -47,7 +47,7 @@ This example demonstrates row selection (including select-all), sortable string and numeric columns, and stable selection across sorts and refreshes. ```python ---8<-- "{{ examples }}/sortable_and_selectable.py" +--8<-- "{{ examples }}/sortable_and_selectable/main.py" ``` {{ image(example_images + "/sortable_and_selectable.png", width="80%") }} @@ -56,7 +56,7 @@ sortable string and numeric columns, and stable selection across sorts and refre ### Handling events ```python ---8<-- "{{ examples }}/handling_events.py" +--8<-- "{{ examples }}/handling_events/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/datepicker.md b/sdk/python/packages/flet/docs/controls/datepicker.md index f07c9a9942..e88d92602d 100644 --- a/sdk/python/packages/flet/docs/controls/datepicker.md +++ b/sdk/python/packages/flet/docs/controls/datepicker.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/date_picker/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/daterangepicker.md b/sdk/python/packages/flet/docs/controls/daterangepicker.md index ee818c04d2..629da71088 100644 --- a/sdk/python/packages/flet/docs/controls/daterangepicker.md +++ b/sdk/python/packages/flet/docs/controls/daterangepicker.md @@ -13,7 +13,7 @@ example_images: ../test-images/controls/material/golden/macos/date_range_picker ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="60%") }} diff --git a/sdk/python/packages/flet/docs/controls/dismissible.md b/sdk/python/packages/flet/docs/controls/dismissible.md index 4fd4d88576..ac57631e88 100644 --- a/sdk/python/packages/flet/docs/controls/dismissible.md +++ b/sdk/python/packages/flet/docs/controls/dismissible.md @@ -13,7 +13,7 @@ example_images: ../examples/controls/dismissible/media ### Dismissible `ListTile`s ```python ---8<-- "{{ examples }}/dismissible_list_tiles.py" +--8<-- "{{ examples }}/dismissible_list_tiles/main.py" ``` {{ image(example_images + "/dismissible_list_tiles.gif", alt="dismissible-list-tiles", width="80%") }} @@ -42,7 +42,7 @@ On Flutter’s side, though, the already-dismissed `Dismissible` widget in the m Example: ```python ---8<-- "{{ examples }}/remove_on_dismiss_declarative.py" +--8<-- "{{ examples }}/remove_on_dismiss_declarative/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/divider.md b/sdk/python/packages/flet/docs/controls/divider.md index a14d51c6c1..9dc9387081 100644 --- a/sdk/python/packages/flet/docs/controls/divider.md +++ b/sdk/python/packages/flet/docs/controls/divider.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/divider/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/draggable.md b/sdk/python/packages/flet/docs/controls/draggable.md index 0b7f12f81f..d71049f1f8 100644 --- a/sdk/python/packages/flet/docs/controls/draggable.md +++ b/sdk/python/packages/flet/docs/controls/draggable.md @@ -15,13 +15,13 @@ example_images: ../examples/controls/drag_target_and_draggable/media #### Imperative ```python ---8<-- "{{ examples }}/drag_and_drop_containers.py" +--8<-- "{{ examples }}/drag_and_drop_containers/main.py" ``` #### Declarative ```python ---8<-- "{{ examples }}/drag_and_drop_containers_declarative.py" +--8<-- "{{ examples }}/drag_and_drop_containers_declarative/main.py" ``` {{ image(example_images + "/drag_and_drop_containers.gif", alt="drag-and-drop-containers", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/dropdown/index.md b/sdk/python/packages/flet/docs/controls/dropdown/index.md index e4f2ba9025..3d5e6904d7 100644 --- a/sdk/python/packages/flet/docs/controls/dropdown/index.md +++ b/sdk/python/packages/flet/docs/controls/dropdown/index.md @@ -16,7 +16,7 @@ example_media: ../../examples/controls/dropdown/media ### Color selection with filtering ```python ---8<-- "{{ examples }}/color_selection_with_filtering.py" +--8<-- "{{ examples }}/color_selection_with_filtering/main.py" ``` {{ image(example_media + "/color_selection_with_filtering.gif", alt="color-selection-with-filtering", width="80%") }} @@ -26,7 +26,7 @@ example_media: ../../examples/controls/dropdown/media ### Icon selection ```python ---8<-- "{{ examples }}/icon_selection.py" +--8<-- "{{ examples }}/icon_selection/main.py" ``` {{ image(example_media + "/icon_selection.png", alt="icon-selection", width="80%") }} @@ -35,7 +35,7 @@ example_media: ../../examples/controls/dropdown/media ### Styled dropdowns ```python ---8<-- "{{ examples }}/styled.py" +--8<-- "{{ examples }}/styled/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/dropdownm2.md b/sdk/python/packages/flet/docs/controls/dropdownm2.md index ce4315e4f0..b90b8c187e 100644 --- a/sdk/python/packages/flet/docs/controls/dropdownm2.md +++ b/sdk/python/packages/flet/docs/controls/dropdownm2.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/dropdown_m2/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", alt="basic", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/dropdown_m2/media ### Dropdown with label and hint ```python ---8<-- "{{ examples }}/label_and_hint.py" +--8<-- "{{ examples }}/label_and_hint/main.py" ``` {{ image(example_media + "/label_and_hint.gif", alt="label-and-hint", width="80%") }} @@ -32,7 +32,7 @@ example_media: ../examples/controls/dropdown_m2/media ### Handling events ```python ---8<-- "{{ examples }}/handling_events.py" +--8<-- "{{ examples }}/handling_events/main.py" ``` {{ image(example_media + "/handling_events.gif", alt="handling-events", width="80%") }} @@ -42,7 +42,7 @@ example_media: ../examples/controls/dropdown_m2/media ### Add and delete options ```python ---8<-- "{{ examples }}/add_and_delete_options.py" +--8<-- "{{ examples }}/add_and_delete_options/main.py" ``` {{ image(example_media + "/add_and_delete_options.gif", alt="add-and-delete-options", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/expansionpanellist.md b/sdk/python/packages/flet/docs/controls/expansionpanellist.md index 94ea8358c2..8f08d98d08 100644 --- a/sdk/python/packages/flet/docs/controls/expansionpanellist.md +++ b/sdk/python/packages/flet/docs/controls/expansionpanellist.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/expansion_panel_list/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/expansiontile.md b/sdk/python/packages/flet/docs/controls/expansiontile.md index 7c45eeb2b4..27ee54bc78 100644 --- a/sdk/python/packages/flet/docs/controls/expansiontile.md +++ b/sdk/python/packages/flet/docs/controls/expansiontile.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/expansion_tile ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", width="80%") }} @@ -21,25 +21,25 @@ example_images: ../test-images/examples/material/golden/macos/expansion_tile ## Programmatic expansion/collapse ```python ---8<-- "{{ examples }}/programmatic_expansion.py" +--8<-- "{{ examples }}/programmatic_expansion/main.py" ``` ## Custom animations ```python ---8<-- "{{ examples }}/custom_animations.py" +--8<-- "{{ examples }}/custom_animations/main.py" ``` ### Theme mode toggle ```python ---8<-- "{{ examples }}/theme_mode_toggle.py" +--8<-- "{{ examples }}/theme_mode_toggle/main.py" ``` ### Borders ```python ---8<-- "{{ examples }}/borders.py" +--8<-- "{{ examples }}/borders/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/filledbutton.md b/sdk/python/packages/flet/docs/controls/filledbutton.md index 493c66e55b..ea05dd45b9 100644 --- a/sdk/python/packages/flet/docs/controls/filledbutton.md +++ b/sdk/python/packages/flet/docs/controls/filledbutton.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/filled_button/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/filledtonalbutton.md b/sdk/python/packages/flet/docs/controls/filledtonalbutton.md index 8dd3a4f5bf..28f7652193 100644 --- a/sdk/python/packages/flet/docs/controls/filledtonalbutton.md +++ b/sdk/python/packages/flet/docs/controls/filledtonalbutton.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/filled_tonal_button/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/floatingactionbutton.md b/sdk/python/packages/flet/docs/controls/floatingactionbutton.md index bf4fbc319c..d67fb92335 100644 --- a/sdk/python/packages/flet/docs/controls/floatingactionbutton.md +++ b/sdk/python/packages/flet/docs/controls/floatingactionbutton.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/floating_action_button/media ### Handling clicks ```python ---8<-- "{{ examples }}/handling_clicks.py" +--8<-- "{{ examples }}/handling_clicks/main.py" ``` {{ image(example_media + "/handling_clicks.gif", alt="handling-clicks", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/gesturedetector.md b/sdk/python/packages/flet/docs/controls/gesturedetector.md index 6b8590aac9..ae8a65e132 100644 --- a/sdk/python/packages/flet/docs/controls/gesturedetector.md +++ b/sdk/python/packages/flet/docs/controls/gesturedetector.md @@ -15,7 +15,7 @@ example_images: ../examples/controls/gesture_detector/media ### Handling events ```python ---8<-- "{{ examples }}/handling_events.py" +--8<-- "{{ examples }}/handling_events/main.py" ``` ### Draggable containers @@ -26,7 +26,7 @@ The sample also shows that GestureDetector can have a child control (blue contai inside another control (yellow container) giving the same results. ```python ---8<-- "{{ examples }}/draggable_containers.py" +--8<-- "{{ examples }}/draggable_containers/main.py" ``` {{ image(example_images + "/draggable_containers.gif", alt="draggable-containers", width="80%") }} @@ -35,13 +35,13 @@ inside another control (yellow container) giving the same results. ### Window drag area ```python ---8<-- "{{ examples }}/window_drag_area.py" +--8<-- "{{ examples }}/window_drag_area/main.py" ``` ### Mouse Cursors ```python ---8<-- "{{ examples }}/mouse_cursors.py" +--8<-- "{{ examples }}/mouse_cursors/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/gridview.md b/sdk/python/packages/flet/docs/controls/gridview.md index c8d72b98f0..67435f3222 100644 --- a/sdk/python/packages/flet/docs/controls/gridview.md +++ b/sdk/python/packages/flet/docs/controls/gridview.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/grid_view/media ### Photo gallery ```python ---8<-- "{{ examples }}/photo_gallery.py" +--8<-- "{{ examples }}/photo_gallery/main.py" ``` {{ image(example_media + "/photo_gallery.png", alt="photo-gallery", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/hero.md b/sdk/python/packages/flet/docs/controls/hero.md index 150bdcc61b..3bab57fc44 100644 --- a/sdk/python/packages/flet/docs/controls/hero.md +++ b/sdk/python/packages/flet/docs/controls/hero.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/core/golden/macos/hero ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.gif", width="80%") }} @@ -19,7 +19,7 @@ example_images: ../test-images/examples/core/golden/macos/hero ### Gallery ```python ---8<-- "{{ examples }}/gallery.py" +--8<-- "{{ examples }}/gallery/main.py" ``` diff --git a/sdk/python/packages/flet/docs/controls/icon.md b/sdk/python/packages/flet/docs/controls/icon.md index c18f5d03a7..de568efd9d 100644 --- a/sdk/python/packages/flet/docs/controls/icon.md +++ b/sdk/python/packages/flet/docs/controls/icon.md @@ -17,7 +17,7 @@ visit our [icons browser](https://examples.flet.dev/icons_browser/) ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/iconbutton.md b/sdk/python/packages/flet/docs/controls/iconbutton.md index b3a61b2eab..485e4bc9ab 100644 --- a/sdk/python/packages/flet/docs/controls/iconbutton.md +++ b/sdk/python/packages/flet/docs/controls/iconbutton.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/icon_button/media ### Handling clicks ```python ---8<-- "{{ examples }}/handling_clicks.py" +--8<-- "{{ examples }}/handling_clicks/main.py" ``` {{ image(example_media + "/handling_clicks.gif", alt="handling-clicks", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/icon_button/media ### Selected icon ```python ---8<-- "{{ examples }}/selected_icon.py" +--8<-- "{{ examples }}/selected_icon/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/image.md b/sdk/python/packages/flet/docs/controls/image.md index d133e6937c..1d13190d6c 100644 --- a/sdk/python/packages/flet/docs/controls/image.md +++ b/sdk/python/packages/flet/docs/controls/image.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/image/media ### Image gallery ```python ---8<-- "{{ examples }}/gallery.py" +--8<-- "{{ examples }}/gallery/main.py" ``` {{ image(example_media + "/gallery.gif", width="80%") }} @@ -22,31 +22,31 @@ example_media: ../examples/controls/image/media ### Fade-in images with a placeholder ```python ---8<-- "{{ examples }}/fade_in.py" +--8<-- "{{ examples }}/fade_in/main.py" ``` ### Displaying images from base64 strings and byte data ```python ---8<-- "{{ examples }}/src_base64_and_bytes.py" +--8<-- "{{ examples }}/src_base64_and_bytes/main.py" ``` ### Displaying a static SVG image ```python ---8<-- "{{ examples }}/static_svg.py" +--8<-- "{{ examples }}/static_svg/main.py" ``` ### Displaying a dynamic SVG image ```python ---8<-- "{{ examples }}/dynamic_svg.py" +--8<-- "{{ examples }}/dynamic_svg/main.py" ``` ### Displaying a Lucide icon ```python ---8<-- "{{ examples }}/lucide_icons.py" +--8<-- "{{ examples }}/lucide_icons/main.py" ``` ### Gapless playback when changing image sources @@ -57,7 +57,7 @@ image loads. With [`gapless_playback`][flet.Image.gapless_playback] set to `Fals briefly be empty, causing a flicker/blink effect. ```python ---8<-- "{{ examples }}/gapless_playback.py" +--8<-- "{{ examples }}/gapless_playback/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/interactiveviewer.md b/sdk/python/packages/flet/docs/controls/interactiveviewer.md index 0f9fc41d1a..40218644a5 100644 --- a/sdk/python/packages/flet/docs/controls/interactiveviewer.md +++ b/sdk/python/packages/flet/docs/controls/interactiveviewer.md @@ -12,13 +12,13 @@ examples: ../../examples/controls/interactive_viewer ### Handling events ```python ---8<-- "{{ examples }}/handling_events.py" +--8<-- "{{ examples }}/handling_events/main.py" ``` ### Programmatic transformations ```python ---8<-- "{{ examples }}/transformations.py" +--8<-- "{{ examples }}/transformations/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/keyboardlistener.md b/sdk/python/packages/flet/docs/controls/keyboardlistener.md index a293db45df..f599ff3f25 100644 --- a/sdk/python/packages/flet/docs/controls/keyboardlistener.md +++ b/sdk/python/packages/flet/docs/controls/keyboardlistener.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/keyboard_listener ### Press any keys ```python ---8<-- "{{ examples }}/detect_keys.py" +--8<-- "{{ examples }}/detect_keys/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/layoutcontrol.md b/sdk/python/packages/flet/docs/controls/layoutcontrol.md index 5ac52aba8e..b63260cb8e 100644 --- a/sdk/python/packages/flet/docs/controls/layoutcontrol.md +++ b/sdk/python/packages/flet/docs/controls/layoutcontrol.md @@ -12,7 +12,7 @@ example_images_examples: ../test-images/examples/core/golden/macos/layout_contro ### Flip ```python ---8<-- "{{ examples }}/flip.py" +--8<-- "{{ examples }}/flip/main.py" ``` {{ image(example_images + "/flip.png", width="80%") }} @@ -20,7 +20,7 @@ example_images_examples: ../test-images/examples/core/golden/macos/layout_contro ### Rotate ```python ---8<-- "{{ examples }}/rotate.py" +--8<-- "{{ examples }}/rotate/main.py" ``` {{ image(example_images + "/rotate.png", width="80%") }} @@ -28,7 +28,7 @@ example_images_examples: ../test-images/examples/core/golden/macos/layout_contro ### RotatedBox ```python ---8<-- "../../examples/controls/rotated_box/basic.py" +--8<-- "../../examples/controls/rotated_box/basic/main.py" ``` {{ image("../test-images/controls/core/golden/macos/rotated_box/rotated_box.png", width="80%") }} @@ -36,7 +36,7 @@ example_images_examples: ../test-images/examples/core/golden/macos/layout_contro ### Scale ```python ---8<-- "{{ examples }}/scale.py" +--8<-- "{{ examples }}/scale/main.py" ``` {{ image(example_images + "/scale.png", width="80%") }} @@ -44,7 +44,7 @@ example_images_examples: ../test-images/examples/core/golden/macos/layout_contro ### Offset ```python ---8<-- "{{ examples }}/offset.py" +--8<-- "{{ examples }}/offset/main.py" ``` {{ image(example_images + "/offset.png", width="80%") }} @@ -52,7 +52,7 @@ example_images_examples: ../test-images/examples/core/golden/macos/layout_contro ### Matrix4 Transform ```python ---8<-- "{{ examples }}/matrix4_transform.py" +--8<-- "{{ examples }}/matrix4_transform/main.py" ``` {{ image(example_images_examples + "/matrix4_transform.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/listtile.md b/sdk/python/packages/flet/docs/controls/listtile.md index 0edd2263f3..c0b1619a60 100644 --- a/sdk/python/packages/flet/docs/controls/listtile.md +++ b/sdk/python/packages/flet/docs/controls/listtile.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/list_tile/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/listview.md b/sdk/python/packages/flet/docs/controls/listview.md index 4a7146e4e9..18c63e5b40 100644 --- a/sdk/python/packages/flet/docs/controls/listview.md +++ b/sdk/python/packages/flet/docs/controls/listview.md @@ -14,7 +14,7 @@ example_images: ../test-images/examples/core/golden/macos/list_view ### Auto-scrolling and dynamical items addition ```python ---8<-- "{{ examples }}/autoscroll_and_dynamic_items.py" +--8<-- "{{ examples }}/autoscroll_and_dynamic_items/main.py" ``` {{ image(example_media + "/autoscroll_and_dynamic_items.gif", alt="autoscroll-and-dynamic-items", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/markdown.md b/sdk/python/packages/flet/docs/controls/markdown.md index 0b335d5bcd..566e286841 100644 --- a/sdk/python/packages/flet/docs/controls/markdown.md +++ b/sdk/python/packages/flet/docs/controls/markdown.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/markdown/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", alt="basic", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/markdown/media ### Code syntax highlight ```python ---8<-- "{{ examples }}/code_syntax_highlight.py" +--8<-- "{{ examples }}/code_syntax_highlight/main.py" ``` {{ image(example_media + "/code_syntax_highlight.png", alt="code-syntax-highlight", width="80%") }} @@ -32,7 +32,7 @@ example_media: ../examples/controls/markdown/media ### Custom text theme ```python ---8<-- "{{ examples }}/custom_text_theme.py" +--8<-- "{{ examples }}/custom_text_theme/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/menubar.md b/sdk/python/packages/flet/docs/controls/menubar.md index 6349b91155..915b80c71c 100644 --- a/sdk/python/packages/flet/docs/controls/menubar.md +++ b/sdk/python/packages/flet/docs/controls/menubar.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/menu_bar ### `MenuBar` with Nested Submenus ```python ---8<-- "{{ examples }}/nested_submenus.py" +--8<-- "{{ examples }}/nested_submenus/main.py" ``` {{ image(example_images + "/nested_submenus.gif", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/menuitembutton.md b/sdk/python/packages/flet/docs/controls/menuitembutton.md index 02d8e61783..a035f76216 100644 --- a/sdk/python/packages/flet/docs/controls/menuitembutton.md +++ b/sdk/python/packages/flet/docs/controls/menuitembutton.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/menu_item_button ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.gif", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/navigationbar/index.md b/sdk/python/packages/flet/docs/controls/navigationbar/index.md index e1ee7317a4..38acbc7def 100644 --- a/sdk/python/packages/flet/docs/controls/navigationbar/index.md +++ b/sdk/python/packages/flet/docs/controls/navigationbar/index.md @@ -15,7 +15,7 @@ example_images: ../../test-images/examples/material/golden/macos/navigation_bar ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/navigationdrawer/index.md b/sdk/python/packages/flet/docs/controls/navigationdrawer/index.md index db2ce17308..2c890ccf5a 100644 --- a/sdk/python/packages/flet/docs/controls/navigationdrawer/index.md +++ b/sdk/python/packages/flet/docs/controls/navigationdrawer/index.md @@ -15,7 +15,7 @@ example_images: ../../test-images/examples/material/golden/macos/navigation_draw ### Start-aligned drawer ```python ---8<-- "{{ examples }}/position_start.py" +--8<-- "{{ examples }}/position_start/main.py" ``` {{ image(example_images + "/position_start.gif", alt="position-start", width="80%") }} @@ -24,7 +24,7 @@ example_images: ../../test-images/examples/material/golden/macos/navigation_draw ### End-aligned drawer ```python ---8<-- "{{ examples }}/position_end.py" +--8<-- "{{ examples }}/position_end/main.py" ``` {{ image(example_images + "/position_end.gif", alt="position-end", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/navigationrail/index.md b/sdk/python/packages/flet/docs/controls/navigationrail/index.md index 5b4a8859db..092854fe13 100644 --- a/sdk/python/packages/flet/docs/controls/navigationrail/index.md +++ b/sdk/python/packages/flet/docs/controls/navigationrail/index.md @@ -15,7 +15,7 @@ example_images: ../../test-images/examples/material/golden/macos/navigation_rail ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/outlinedbutton.md b/sdk/python/packages/flet/docs/controls/outlinedbutton.md index e8b2e2004b..0b09bc2207 100644 --- a/sdk/python/packages/flet/docs/controls/outlinedbutton.md +++ b/sdk/python/packages/flet/docs/controls/outlinedbutton.md @@ -14,7 +14,7 @@ example_images: ../test-images/examples/material/golden/macos/outlined_button ### Basic example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} @@ -23,7 +23,7 @@ example_images: ../test-images/examples/material/golden/macos/outlined_button ### Handling clicks ```python ---8<-- "{{ examples }}/handling_clicks.py" +--8<-- "{{ examples }}/handling_clicks/main.py" ``` {{ image(example_images + "/handling_clicks.gif", alt="handling-clicks", width="80%") }} @@ -32,7 +32,7 @@ example_images: ../test-images/examples/material/golden/macos/outlined_button ### Icons ```python ---8<-- "{{ examples }}/icons.py" +--8<-- "{{ examples }}/icons/main.py" ``` {{ image(example_images + "/icons.png", alt="icons", width="80%") }} @@ -41,7 +41,7 @@ example_images: ../test-images/examples/material/golden/macos/outlined_button ### Custom content ```python ---8<-- "{{ examples }}/custom_content.py" +--8<-- "{{ examples }}/custom_content/main.py" ``` {{ image(example_images + "/custom_content.png", alt="custom-content", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/pagelet.md b/sdk/python/packages/flet/docs/controls/pagelet.md index a824569939..2b34f9eb5a 100644 --- a/sdk/python/packages/flet/docs/controls/pagelet.md +++ b/sdk/python/packages/flet/docs/controls/pagelet.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/core/golden/macos/pagelet ### Basic example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/pageview.md b/sdk/python/packages/flet/docs/controls/pageview.md index 0586b20e22..b9e3cc677e 100644 --- a/sdk/python/packages/flet/docs/controls/pageview.md +++ b/sdk/python/packages/flet/docs/controls/pageview.md @@ -10,13 +10,13 @@ examples: ../../examples/controls/page_view ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` ### Programmatic Swipes ```python ---8<-- "{{ examples }}/programmatic_swipe.py" +--8<-- "{{ examples }}/programmatic_swipe/main.py" ``` diff --git a/sdk/python/packages/flet/docs/controls/placeholder.md b/sdk/python/packages/flet/docs/controls/placeholder.md index 123d737e39..afd784d7b1 100644 --- a/sdk/python/packages/flet/docs/controls/placeholder.md +++ b/sdk/python/packages/flet/docs/controls/placeholder.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/core/golden/macos/placeholder ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/popupmenubutton.md b/sdk/python/packages/flet/docs/controls/popupmenubutton.md index 46fa8bf38d..d1bbd2b74b 100644 --- a/sdk/python/packages/flet/docs/controls/popupmenubutton.md +++ b/sdk/python/packages/flet/docs/controls/popupmenubutton.md @@ -15,7 +15,7 @@ popup_menu_item_class_name: flet.PopupMenuItem ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/progressbar.md b/sdk/python/packages/flet/docs/controls/progressbar.md index f81b7e6c2f..3d4ab307f2 100644 --- a/sdk/python/packages/flet/docs/controls/progressbar.md +++ b/sdk/python/packages/flet/docs/controls/progressbar.md @@ -14,7 +14,7 @@ example_images: ../test-images/examples/material/golden/macos/progress_bar ### Determinate and indeterminate progress bars ```python ---8<-- "{{ examples }}/determinate_and_indeterminate.py" +--8<-- "{{ examples }}/determinate_and_indeterminate/main.py" ``` {{ image(example_media + "/determinate_and_indeterminate.gif", alt="determinate-and-indeterminate", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/progressring.md b/sdk/python/packages/flet/docs/controls/progressring.md index f07d8624e7..20ee62bd49 100644 --- a/sdk/python/packages/flet/docs/controls/progressring.md +++ b/sdk/python/packages/flet/docs/controls/progressring.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/progress_ring/media ### Determinate and indeterminate progress rings ```python ---8<-- "{{ examples }}/determinate_and_indeterminate.py" +--8<-- "{{ examples }}/determinate_and_indeterminate/main.py" ``` {{ image(example_media + "/determinate_and_indeterminate.gif", alt="determinate-and-indeterminate", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/progress_ring/media ### Gauge with progress ```python ---8<-- "{{ examples }}/gauge_with_progress.py" +--8<-- "{{ examples }}/gauge_with_progress/main.py" ``` {{ image(example_images + "/gauge_with_progress.png", alt="determinate-and-indeterminate", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/rangeslider.md b/sdk/python/packages/flet/docs/controls/rangeslider.md index 64f4115a4f..bc1510ff24 100644 --- a/sdk/python/packages/flet/docs/controls/rangeslider.md +++ b/sdk/python/packages/flet/docs/controls/rangeslider.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/range_slider ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.gif", alt="basic", width="80%") }} @@ -22,7 +22,7 @@ example_images: ../test-images/examples/material/golden/macos/range_slider ### RangeSlider with events ```python ---8<-- "{{ examples }}/handling_change_events.py" +--8<-- "{{ examples }}/handling_change_events/main.py" ``` {{ image(example_images + "/handling_events.gif", alt="handling_events", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/reorderabledraghandle.md b/sdk/python/packages/flet/docs/controls/reorderabledraghandle.md index 42b0530a3a..2a7e19c5dc 100644 --- a/sdk/python/packages/flet/docs/controls/reorderabledraghandle.md +++ b/sdk/python/packages/flet/docs/controls/reorderabledraghandle.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/reorderable_drag_handle ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/reorderablelistview.md b/sdk/python/packages/flet/docs/controls/reorderablelistview.md index ead463218d..640c1f73f1 100644 --- a/sdk/python/packages/flet/docs/controls/reorderablelistview.md +++ b/sdk/python/packages/flet/docs/controls/reorderablelistview.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/reorderable_list_v ### Horizontal and Vertical ```python ---8<-- "{{ examples }}/horizontal_and_vertical.py" +--8<-- "{{ examples }}/horizontal_and_vertical/main.py" ``` {{ image(example_images + "/horizontal_and_vertical.png", alt="horizontal-and-vertical", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/responsiverow.md b/sdk/python/packages/flet/docs/controls/responsiverow.md index d7234dd535..9ca9174893 100644 --- a/sdk/python/packages/flet/docs/controls/responsiverow.md +++ b/sdk/python/packages/flet/docs/controls/responsiverow.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/core/golden/macos/responsive_row ### ResponsiveRow ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.gif", width="80%") }} @@ -22,7 +22,7 @@ example_images: ../test-images/examples/core/golden/macos/responsive_row ### Custom breakpoints ```python ---8<-- "{{ examples }}/custom_breakpoint.py" +--8<-- "{{ examples }}/custom_breakpoint/main.py" ``` {{ image(example_images + "/custom_breakpoint.gif", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/rotatedbox.md b/sdk/python/packages/flet/docs/controls/rotatedbox.md index 0a5714bfea..902608fcb2 100644 --- a/sdk/python/packages/flet/docs/controls/rotatedbox.md +++ b/sdk/python/packages/flet/docs/controls/rotatedbox.md @@ -9,7 +9,7 @@ example_images: ../test-images/examples/core/golden/macos/rotated_box ## Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/rotated_box.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/row.md b/sdk/python/packages/flet/docs/controls/row.md index aa1f07143a..f5a12949de 100644 --- a/sdk/python/packages/flet/docs/controls/row.md +++ b/sdk/python/packages/flet/docs/controls/row.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/core/golden/macos/row ### Spacing children ```python ---8<-- "{{ examples }}/spacing.py" +--8<-- "{{ examples }}/spacing/main.py" ``` {{ image(example_images + "/row_spacing_adjustment.gif", alt="spacing", width="80%") }} @@ -22,7 +22,7 @@ example_images: ../test-images/examples/core/golden/macos/row ### Wrapping children ```python ---8<-- "{{ examples }}/wrap.py" +--8<-- "{{ examples }}/wrap/main.py" ``` {{ image(example_images + "/wrap_adjustment.gif", alt="wrap", width="80%") }} @@ -31,7 +31,7 @@ example_images: ../test-images/examples/core/golden/macos/row ### Setting horizontal alignment ```python ---8<-- "{{ examples }}/alignment.py" +--8<-- "{{ examples }}/alignment/main.py" ``` {{ image(example_images + "/alignment.png", alt="alignment", width="60%") }} @@ -40,7 +40,7 @@ example_images: ../test-images/examples/core/golden/macos/row ### Setting vertical alignment ```python ---8<-- "{{ examples }}/vertical_alignment.py" +--8<-- "{{ examples }}/vertical_alignment/main.py" ``` {{ image(example_images + "/vertical_alignment.png", alt="vertical-alignment", width="60%") }} diff --git a/sdk/python/packages/flet/docs/controls/safearea.md b/sdk/python/packages/flet/docs/controls/safearea.md index 3daff58e89..18e6e35366 100644 --- a/sdk/python/packages/flet/docs/controls/safearea.md +++ b/sdk/python/packages/flet/docs/controls/safearea.md @@ -12,7 +12,7 @@ examples: ../../examples/controls/safe_area ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/screenshot.md b/sdk/python/packages/flet/docs/controls/screenshot.md index 4873722e58..1240ac1fdc 100644 --- a/sdk/python/packages/flet/docs/controls/screenshot.md +++ b/sdk/python/packages/flet/docs/controls/screenshot.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/screenshot ### Taking control screenshot ```python ---8<-- "{{ examples }}/taking_screenshot.py" +--8<-- "{{ examples }}/taking_screenshot/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/searchbar.md b/sdk/python/packages/flet/docs/controls/searchbar.md index 8a8610a5ba..a15fea457d 100644 --- a/sdk/python/packages/flet/docs/controls/searchbar.md +++ b/sdk/python/packages/flet/docs/controls/searchbar.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/search_bar ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/segmentedbutton.md b/sdk/python/packages/flet/docs/controls/segmentedbutton.md index 30c59806ce..433af13184 100644 --- a/sdk/python/packages/flet/docs/controls/segmentedbutton.md +++ b/sdk/python/packages/flet/docs/controls/segmentedbutton.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/segmented_button ### Basic Example ```python ---8<-- "{{ examples }}/single_multiple_selection.py" +--8<-- "{{ examples }}/single_multiple_selection/main.py" ``` {{ image(example_images + "/single_multiple_selection.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/selectionarea.md b/sdk/python/packages/flet/docs/controls/selectionarea.md index f64bbf0a30..9df2070459 100644 --- a/sdk/python/packages/flet/docs/controls/selectionarea.md +++ b/sdk/python/packages/flet/docs/controls/selectionarea.md @@ -12,7 +12,7 @@ example_media: ../examples/controls/selection_area/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/semantics.md b/sdk/python/packages/flet/docs/controls/semantics.md index 15377b0943..a9027bcfcc 100644 --- a/sdk/python/packages/flet/docs/controls/semantics.md +++ b/sdk/python/packages/flet/docs/controls/semantics.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/semantics ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/shadermask.md b/sdk/python/packages/flet/docs/controls/shadermask.md index ee3c13d039..dc53614dd7 100644 --- a/sdk/python/packages/flet/docs/controls/shadermask.md +++ b/sdk/python/packages/flet/docs/controls/shadermask.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/core/golden/macos/shader_mask ### Pink glow around image edges ```python ---8<-- "{{ examples }}/pink_radial_glow.py" +--8<-- "{{ examples }}/pink_radial_glow/main.py" ``` {{ image(example_images + "/pink_radial_glow.png", alt="pink-radial-glow", width="80%") }} @@ -23,7 +23,7 @@ example_images: ../test-images/examples/core/golden/macos/shader_mask ### Fade out bottom edge of an image ```python ---8<-- "{{ examples }}/fade_out_image_bottom.py" +--8<-- "{{ examples }}/fade_out_image_bottom/main.py" ``` {{ image(example_images + "/fade_out_image_bottom.png", alt="fade-out-image-bottom", width="80%") }} @@ -32,7 +32,7 @@ example_images: ../test-images/examples/core/golden/macos/shader_mask ### Applying linear and radial gradients/shaders ```python ---8<-- "{{ examples }}/linear_and_radial_gradients.py" +--8<-- "{{ examples }}/linear_and_radial_gradients/main.py" ``` {{ image(example_images + "/linear_and_radial_gradients.png", alt="fade-out-image-bottom", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/shimmer.md b/sdk/python/packages/flet/docs/controls/shimmer.md index b828222001..0d8d5c0d78 100644 --- a/sdk/python/packages/flet/docs/controls/shimmer.md +++ b/sdk/python/packages/flet/docs/controls/shimmer.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/core/golden/macos/shimmer ### Basic ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/image_for_docs.gif", alt="custom-label", width="50%") }} @@ -19,7 +19,7 @@ example_images: ../test-images/examples/core/golden/macos/shimmer ### Skeleton list placeholders ```python ---8<-- "{{ examples }}/basic_placeholder.py" +--8<-- "{{ examples }}/basic_placeholder/main.py" ``` {{ image(example_images + "/basic_placeholder.png", alt="custom-label", width="50%") }} @@ -27,7 +27,7 @@ example_images: ../test-images/examples/core/golden/macos/shimmer ### Custom gradients and directions ```python ---8<-- "{{ examples }}/custom_gradient.py" +--8<-- "{{ examples }}/custom_gradient/main.py" ``` {{ image(example_images + "/custom_gradient.png", alt="custom-label", width="50%") }} diff --git a/sdk/python/packages/flet/docs/controls/slider.md b/sdk/python/packages/flet/docs/controls/slider.md index 2d58ac65b6..2a5ac46f02 100644 --- a/sdk/python/packages/flet/docs/controls/slider.md +++ b/sdk/python/packages/flet/docs/controls/slider.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/slider ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} @@ -22,7 +22,7 @@ example_images: ../test-images/examples/material/golden/macos/slider ### Setting a custom label ```python ---8<-- "{{ examples }}/custom_label.py" +--8<-- "{{ examples }}/custom_label/main.py" ``` {{ image(example_images + "/custom_label.png", alt="custom-label", width="80%") }} @@ -31,7 +31,7 @@ example_images: ../test-images/examples/material/golden/macos/slider ### Handling events ```python ---8<-- "{{ examples }}/handling_events.py" +--8<-- "{{ examples }}/handling_events/main.py" ``` {{ image(example_images + "/handling_events.png", alt="handling-events", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/snackbar.md b/sdk/python/packages/flet/docs/controls/snackbar.md index c1e0a87e55..50132a45e8 100644 --- a/sdk/python/packages/flet/docs/controls/snackbar.md +++ b/sdk/python/packages/flet/docs/controls/snackbar.md @@ -14,7 +14,7 @@ snack_bar_action_class_name: flet.SnackBarAction ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} @@ -23,7 +23,7 @@ snack_bar_action_class_name: flet.SnackBarAction ### Counter ```python ---8<-- "{{ examples }}/counter.py" +--8<-- "{{ examples }}/counter/main.py" ``` {{ image(example_images + "/snack_bar_flow.gif", alt="Snack bar with counter", caption="Snack bar with counter",width="50%") }} @@ -31,7 +31,7 @@ snack_bar_action_class_name: flet.SnackBarAction ### Action ```python ---8<-- "{{ examples }}/action.py" +--8<-- "{{ examples }}/action/main.py" ``` {{ image(example_images + "/action_simple.png", alt="Snack bar with a simple action", caption="Snack bar with a simple action", width="50%") }} diff --git a/sdk/python/packages/flet/docs/controls/stack.md b/sdk/python/packages/flet/docs/controls/stack.md index 493127ccc9..83fe819f67 100644 --- a/sdk/python/packages/flet/docs/controls/stack.md +++ b/sdk/python/packages/flet/docs/controls/stack.md @@ -15,7 +15,7 @@ example_images: ../test-images/examples/core/golden/macos/stack ### Avatar with online status ```python ---8<-- "{{ examples }}/online_avatar.py" +--8<-- "{{ examples }}/online_avatar/main.py" ``` {{ image(example_images + "/online_avatar.png", alt="online-avatar", width="80%") }} @@ -24,7 +24,7 @@ example_images: ../test-images/examples/core/golden/macos/stack ### Absolute positioning ```python ---8<-- "{{ examples }}/absolute_positioning.py" +--8<-- "{{ examples }}/absolute_positioning/main.py" ``` {{ image(example_images + "/absolute_positioning.png", alt="absolute-positioning", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/submenubutton.md b/sdk/python/packages/flet/docs/controls/submenubutton.md index 814cd17748..0004891914 100644 --- a/sdk/python/packages/flet/docs/controls/submenubutton.md +++ b/sdk/python/packages/flet/docs/controls/submenubutton.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/submenu_button ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/switch.md b/sdk/python/packages/flet/docs/controls/switch.md index 522ab1cda1..8687ced97b 100644 --- a/sdk/python/packages/flet/docs/controls/switch.md +++ b/sdk/python/packages/flet/docs/controls/switch.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/switch/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/switch/media ### Handling change events ```python ---8<-- "{{ examples }}/handling_events.py" +--8<-- "{{ examples }}/handling_events/main.py" ``` {{ image(example_media + "/handling_events.gif", alt="handling-events", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/tabs/index.md b/sdk/python/packages/flet/docs/controls/tabs/index.md index 001046e97d..9ea3c36697 100644 --- a/sdk/python/packages/flet/docs/controls/tabs/index.md +++ b/sdk/python/packages/flet/docs/controls/tabs/index.md @@ -15,7 +15,7 @@ example_images: ../../examples/controls/tabs/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.gif", width="80%") }} @@ -24,25 +24,25 @@ example_images: ../../examples/controls/tabs/media ### Nesting tabs ```python ---8<-- "{{ examples }}/nested.py" +--8<-- "{{ examples }}/nested/main.py" ``` ### Dynamic tab addition ```python ---8<-- "{{ examples }}/dynamic_tab_addition.py" +--8<-- "{{ examples }}/dynamic_tab_addition/main.py" ``` ### Custom indicator ```python ---8<-- "{{ examples }}/custom_indicator.py" +--8<-- "{{ examples }}/custom_indicator/main.py" ``` ### Programmatical Tab switch ```python ---8<-- "{{ examples }}/move_to.py" +--8<-- "{{ examples }}/move_to/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/text.md b/sdk/python/packages/flet/docs/controls/text.md index 91c20f9308..33ae5c73bd 100644 --- a/sdk/python/packages/flet/docs/controls/text.md +++ b/sdk/python/packages/flet/docs/controls/text.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/text/media ### Custom text styles ```python ---8<-- "{{ examples }}/custom_styles.py" +--8<-- "{{ examples }}/custom_styles/main.py" ``` {{ image(example_media + "/custom_styles.gif", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/text/media ### Pre-defined theme text styles ```python ---8<-- "{{ examples }}/text_theme_styles.py" +--8<-- "{{ examples }}/text_theme_styles/main.py" ``` {{ image(example_media + "/text_theme_styles.png", width="80%") }} @@ -32,7 +32,7 @@ example_media: ../examples/controls/text/media ### Font with variable weight ```python ---8<-- "{{ examples }}/variable_font_weight.py" +--8<-- "{{ examples }}/variable_font_weight/main.py" ``` {{ image(example_media + "/variable_font_weight.gif", width="80%") }} @@ -41,7 +41,7 @@ example_media: ../examples/controls/text/media ### Basic rich text example ```python ---8<-- "{{ examples }}/rich_text_basic.py" +--8<-- "{{ examples }}/rich_text_basic/main.py" ``` {{ image(example_media + "/rich_text_basic.png", width="80%") }} @@ -50,7 +50,7 @@ example_media: ../examples/controls/text/media ### Rich text with borders and stroke ```python ---8<-- "{{ examples }}/rich_text_border_stroke.py" +--8<-- "{{ examples }}/rich_text_border_stroke/main.py" ``` {{ image(example_media + "/rich_text_border_stroke.png", width="80%") }} @@ -59,7 +59,7 @@ example_media: ../examples/controls/text/media ### Rich text with gradient ```python ---8<-- "{{ examples }}/rich_text_gradient.py" +--8<-- "{{ examples }}/rich_text_gradient/main.py" ``` {{ image(example_media + "/rich_text_gradient.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/textbutton.md b/sdk/python/packages/flet/docs/controls/textbutton.md index d82d0ff6ac..7a23b848b3 100644 --- a/sdk/python/packages/flet/docs/controls/textbutton.md +++ b/sdk/python/packages/flet/docs/controls/textbutton.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/text_button ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} @@ -22,7 +22,7 @@ example_images: ../test-images/examples/material/golden/macos/text_button ### Icons ```python ---8<-- "{{ examples }}/icons.py" +--8<-- "{{ examples }}/icons/main.py" ``` {{ image(example_images + "/icons.png", alt="icons", width="80%") }} @@ -31,7 +31,7 @@ example_images: ../test-images/examples/material/golden/macos/text_button ### Handling clicks ```python ---8<-- "{{ examples }}/handling_clicks.py" +--8<-- "{{ examples }}/handling_clicks/main.py" ``` {{ image(example_images + "/handling_clicks.png", alt="handling-clicks", width="80%") }} @@ -40,7 +40,7 @@ example_images: ../test-images/examples/material/golden/macos/text_button ### Custom content ```python ---8<-- "{{ examples }}/custom_content.py" +--8<-- "{{ examples }}/custom_content/main.py" ``` {{ image(example_images + "/custom_content.png", alt="custom-content", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/textfield.md b/sdk/python/packages/flet/docs/controls/textfield.md index 20529fb418..ab264d0c5c 100644 --- a/sdk/python/packages/flet/docs/controls/textfield.md +++ b/sdk/python/packages/flet/docs/controls/textfield.md @@ -14,7 +14,7 @@ example_media: ../examples/controls/text_field/media ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_media + "/basic.gif", alt="basic", width="80%") }} @@ -23,7 +23,7 @@ example_media: ../examples/controls/text_field/media ### Handling change events ```python ---8<-- "{{ examples }}/handling_change_events.py" +--8<-- "{{ examples }}/handling_change_events/main.py" ``` {{ image(example_media + "/handling_change_events.gif", alt="handling-change-events", width="80%") }} @@ -31,13 +31,13 @@ example_media: ../examples/controls/text_field/media ### Handling selection changes ```python ---8<-- "{{ examples }}/selection_change.py" +--8<-- "{{ examples }}/selection_change/main.py" ``` ### Password with reveal button ```python ---8<-- "{{ examples }}/password.py" +--8<-- "{{ examples }}/password/main.py" ``` {{ image(example_media + "/password.gif", alt="password", width="80%") }} @@ -46,7 +46,7 @@ example_media: ../examples/controls/text_field/media ### Multiline fields ```python ---8<-- "{{ examples }}/multiline.py" +--8<-- "{{ examples }}/multiline/main.py" ``` {{ image(example_media + "/multiline.gif", alt="multiline", width="80%") }} @@ -55,7 +55,7 @@ example_media: ../examples/controls/text_field/media ### Underlined and borderless TextFields ```python ---8<-- "{{ examples }}/underlined_and_borderless.py" +--8<-- "{{ examples }}/underlined_and_borderless/main.py" ``` {{ image(example_media + "/underlined_and_borderless.gif", alt="underlined-and-borderless", width="80%") }} @@ -64,7 +64,7 @@ example_media: ../examples/controls/text_field/media ### Setting prefixes and suffixes ```python ---8<-- "{{ examples }}/prefix_and_suffix.py" +--8<-- "{{ examples }}/prefix_and_suffix/main.py" ``` {{ image(example_media + "/prefix_and_suffix.gif", alt="prefix-and-suffix", width="80%") }} @@ -73,13 +73,13 @@ example_media: ../examples/controls/text_field/media ### Styled TextField ```python ---8<-- "{{ examples }}/styled.py" +--8<-- "{{ examples }}/styled/main.py" ``` ### Custom label, hint, helper, and counter texts and styles ```python ---8<-- "{{ examples }}/label_hint_helper_counter.py" +--8<-- "{{ examples }}/label_hint_helper_counter/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/timepicker.md b/sdk/python/packages/flet/docs/controls/timepicker.md index e1b1c28887..a98b658294 100644 --- a/sdk/python/packages/flet/docs/controls/timepicker.md +++ b/sdk/python/packages/flet/docs/controls/timepicker.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/time_picker ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", width="80%") }} @@ -21,7 +21,7 @@ example_images: ../test-images/examples/material/golden/macos/time_picker ### Hour Formats ```python ---8<-- "{{ examples }}/hour_formats.py" +--8<-- "{{ examples }}/hour_formats/main.py" ``` {{ image(example_images + "/hour_formats.gif", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/transparentpointer.md b/sdk/python/packages/flet/docs/controls/transparentpointer.md index 373ead9eff..cdd30bbaf6 100644 --- a/sdk/python/packages/flet/docs/controls/transparentpointer.md +++ b/sdk/python/packages/flet/docs/controls/transparentpointer.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/transparent_pointer ## Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/controls/verticaldivider.md b/sdk/python/packages/flet/docs/controls/verticaldivider.md index fc5a776b7a..123d7c9fe3 100644 --- a/sdk/python/packages/flet/docs/controls/verticaldivider.md +++ b/sdk/python/packages/flet/docs/controls/verticaldivider.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/core/golden/macos/vertical_divider ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", alt="basic", width="80%") }} diff --git a/sdk/python/packages/flet/docs/controls/windowdragarea.md b/sdk/python/packages/flet/docs/controls/windowdragarea.md index 2ced1c493d..be36025467 100644 --- a/sdk/python/packages/flet/docs/controls/windowdragarea.md +++ b/sdk/python/packages/flet/docs/controls/windowdragarea.md @@ -11,7 +11,7 @@ example_images: ../test-images/examples/core/golden/macos/window_drag_area ### No frame window ```python ---8<-- "{{ examples }}/no_frame_window.py" +--8<-- "{{ examples }}/no_frame_window/main.py" ``` {{ image(example_images + "/no_frame_window.png", alt="no-frame-window", width="80%") }} diff --git a/sdk/python/packages/flet/docs/cookbook/accessibility.md b/sdk/python/packages/flet/docs/cookbook/accessibility.md index 3d0358b150..5eac6634f6 100644 --- a/sdk/python/packages/flet/docs/cookbook/accessibility.md +++ b/sdk/python/packages/flet/docs/cookbook/accessibility.md @@ -55,5 +55,5 @@ semantics debugger during app development: {{ image("../assets/cookbook/accessibility/debug-accessibility-toggle.gif", width="80%") }} ```python ---8<-- "../../examples/controls/page/semantics_debugger.py" +--8<-- "../../examples/controls/page/semantics_debugger/main.py" ``` diff --git a/sdk/python/packages/flet/docs/cookbook/animations.md b/sdk/python/packages/flet/docs/cookbook/animations.md index d0c646cfaf..a6d84bafdc 100644 --- a/sdk/python/packages/flet/docs/cookbook/animations.md +++ b/sdk/python/packages/flet/docs/cookbook/animations.md @@ -40,7 +40,7 @@ Setting control's `animate_opacity` to either `True`, number or an instance of ` enables implicit animation of [`Control.opacity`][flet.Control.opacity] property. ```python ---8<-- "../../examples/controls/layout_control/animate_opacity.py" +--8<-- "../../examples/controls/layout_control/animate_opacity/main.py" ``` {{ image("../examples/controls/layout_control/media/animate_opacity.gif", alt="animate-opacity", width="80%") }} @@ -52,7 +52,7 @@ Setting control's `animate_rotation` to either `True`, number or an instance of enables implicit animation of [`LayoutControl.rotate`][flet.LayoutControl.rotate] property. ```python ---8<-- "../../examples/controls/constrained-control/animate_rotation.py" +--8<-- "../../examples/controls/layout_control/animate_rotation/main.py" ``` {{ image("../examples/controls/layout_control/media/animate_rotation.gif", alt="animate-rotation", width="80%") }} @@ -64,7 +64,7 @@ Setting control's `animate_scale` to either `True`, number or an instance of `An enables implicit animation of [`LayoutControl.scale`][flet.LayoutControl.scale] property. ```python ---8<-- "../../examples/controls/layout_control/animate_scale.py" +--8<-- "../../examples/controls/layout_control/animate_scale/main.py" ``` {{ image("../examples/controls/layout_control/media/animate_scale.gif", alt="animate-scale", width="80%") }} @@ -82,7 +82,7 @@ a horizontal translation of one quarter the width of the control. Offset animation is used for various sliding effects: ```python ---8<-- "../../examples/controls/layout_control/animate_offset.py" +--8<-- "../../examples/controls/layout_control/animate_offset/main.py" ``` {{ image("../examples/controls/layout_control/media/animate_offset.gif", alt="animate-offset", width="80%") }} @@ -103,7 +103,7 @@ Note: - [`Page.overlay`][flet.Page.overlay] list ```python ---8<-- "../../examples/controls/layout_control/animate_position.py" +--8<-- "../../examples/controls/layout_control/animate_position/main.py" ``` {{ image("../examples/controls/layout_control/media/animate_position.gif", alt="animate-position", width="80%") }} diff --git a/sdk/python/packages/flet/docs/cookbook/authentication.md b/sdk/python/packages/flet/docs/cookbook/authentication.md index f4787ead11..f65253c14c 100644 --- a/sdk/python/packages/flet/docs/cookbook/authentication.md +++ b/sdk/python/packages/flet/docs/cookbook/authentication.md @@ -340,7 +340,7 @@ if ejt: page.login(provider, saved_token=jt) ``` -[See complete app example](https://github.com/flet-dev/flet/blob/main/sdk/python/examples/apps/authentication/github_oauth_with_listing_repos.py). +[See complete app example](https://github.com/flet-dev/flet/blob/main/sdk/python/examples/apps/authentication/github_repos_browser/main.py). ## Signing out @@ -354,7 +354,7 @@ async def logout_button_click(e): page.logout() ``` -[See complete app example](https://github.com/flet-dev/flet/blob/main/sdk/python/examples/apps/authentication/github_oauth_with_listing_repos.py). +[See complete app example](https://github.com/flet-dev/flet/blob/main/sdk/python/examples/apps/authentication/github_repos_browser/main.py). ## Customizing authorization flow diff --git a/sdk/python/packages/flet/docs/cookbook/expanding-controls.md b/sdk/python/packages/flet/docs/cookbook/expanding-controls.md index b23868ad10..29923d09f3 100644 --- a/sdk/python/packages/flet/docs/cookbook/expanding-controls.md +++ b/sdk/python/packages/flet/docs/cookbook/expanding-controls.md @@ -27,7 +27,7 @@ In this example, a [`TextField`][flet.TextField] stretches to fill all remaining while the [`Button`][flet.Button] stays sized to its content: ```python ---8<-- "{{ examples }}/expand_textfield_in_row.py" +--8<-- "{{ examples }}/expand_textfield_in_row/main.py" ``` {{ image(example_images + "/expand_textfield_in_row.png", alt="expand textfield in row", width="70%") }} @@ -37,7 +37,7 @@ while the [`Button`][flet.Button] stays sized to its content: In this example, we create a [`Row`][flet.Row] with three [`Container`][flet.Container]s, distributed like 20% / 60% / 20%: ```python ---8<-- "{{ examples }}/expand_row_proportional_1_3_1.py" +--8<-- "{{ examples }}/expand_row_proportional_1_3_1/main.py" ``` Here, the available space is split into 5 total parts (1+3+1). @@ -54,7 +54,7 @@ The layout uses a parent [`Container`][flet.Container] and a nested row, where b expanded equally, resulting in a 50/50 split. ```python ---8<-- "{{ examples }}/expand_row_equal_split.py" +--8<-- "{{ examples }}/expand_row_equal_split/main.py" ``` {{ image(example_images + "/expand_row_equal_split.png", alt="expand row equal split", width="70%") }} @@ -81,7 +81,7 @@ or any of their subclasses: [`Row`][flet.Row], [`Column`][flet.Column], [`View`] In this example, [`Container`][flet.Container]s being placed in [`Row`][flet.Row]s with `expand_loose = True`: ```python ---8<-- "{{ examples }}/expand_loose_chat_messages.py" +--8<-- "{{ examples }}/expand_loose_chat_messages/main.py" ``` {{ image(example_images + "/expand_loose_chat_messages.png", alt="expand loose chat messages", width="70%") }} diff --git a/sdk/python/packages/flet/docs/cookbook/keyboard-shortcuts.md b/sdk/python/packages/flet/docs/cookbook/keyboard-shortcuts.md index 2ea35413ee..dc9c635c20 100644 --- a/sdk/python/packages/flet/docs/cookbook/keyboard-shortcuts.md +++ b/sdk/python/packages/flet/docs/cookbook/keyboard-shortcuts.md @@ -37,5 +37,5 @@ ft.run(main) Below is a more advanced example: ```python ---8<-- "../../examples/controls/page/keyboard_events.py" +--8<-- "../../examples/controls/page/keyboard_events/main.py" ``` diff --git a/sdk/python/packages/flet/docs/cookbook/navigation-and-routing.md b/sdk/python/packages/flet/docs/cookbook/navigation-and-routing.md index 2d355933af..a2525c9c56 100644 --- a/sdk/python/packages/flet/docs/cookbook/navigation-and-routing.md +++ b/sdk/python/packages/flet/docs/cookbook/navigation-and-routing.md @@ -20,7 +20,7 @@ A reliable setup uses a single source of truth: derive [`page.views`][flet.Page. The default route is `/` when no route is provided. ```python ---8<-- "../../examples/apps/routing_navigation/initial_route.py" +--8<-- "../../examples/apps/routing_navigation/initial_route/main.py" ``` All routes should start with `/`, for example `/store`, `/products/42`, `/settings/mail`. @@ -32,7 +32,7 @@ Whenever route changes (URL edit, browser Back/Forward, or app navigation), Use this event as the place where you decide which views must exist for the current route. ```python ---8<-- "../../examples/apps/routing_navigation/route_change_event.py" +--8<-- "../../examples/apps/routing_navigation/route_change_event/main.py" ``` ## Building views from route @@ -52,7 +52,7 @@ The pattern below is the baseline for most apps: /// ```python ---8<-- "../../examples/apps/routing_navigation/building_views_on_route_change.py" +--8<-- "../../examples/apps/routing_navigation/building_views_on_route_change/main.py" ``` ## Programmatic navigation @@ -72,7 +72,7 @@ For flows requiring confirmation (for example, unsaved changes), disable automat and confirm manually with [`View.can_pop`][flet.View.can_pop] + [`View.on_confirm_pop`][flet.View.on_confirm_pop]. ```python ---8<-- "../../examples/apps/routing_navigation/pop_view_confirm.py" +--8<-- "../../examples/apps/routing_navigation/pop_view_confirm/main.py" ``` ## Navigation UI patterns @@ -81,7 +81,7 @@ Routing composes well with navigation controls such as drawer, rail, and tabs. This example shows route-driven drawer navigation with multiple top-level destinations: ```python ---8<-- "../../examples/apps/routing_navigation/drawer_navigation.py" +--8<-- "../../examples/apps/routing_navigation/drawer_navigation/main.py" ``` ## Route templates (parameterized routes) diff --git a/sdk/python/packages/flet/docs/datatable2/index.md b/sdk/python/packages/flet/docs/datatable2/index.md index 8957facc60..b427ed5a45 100644 --- a/sdk/python/packages/flet/docs/datatable2/index.md +++ b/sdk/python/packages/flet/docs/datatable2/index.md @@ -38,13 +38,13 @@ pip install flet-datatable2 # (1)! ### Example 1 ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ### Example 2 ```python ---8<-- "{{ examples }}/example_2.py" +--8<-- "{{ examples }}/example_2/main.py" ``` {{ image(example_media + "/example_2.gif", width="80%") }} diff --git a/sdk/python/packages/flet/docs/flashlight/index.md b/sdk/python/packages/flet/docs/flashlight/index.md index 2c388330cd..ba00892439 100644 --- a/sdk/python/packages/flet/docs/flashlight/index.md +++ b/sdk/python/packages/flet/docs/flashlight/index.md @@ -34,7 +34,7 @@ pip install flet-flashlight # (1)! ## Example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ## Description diff --git a/sdk/python/packages/flet/docs/geolocator/index.md b/sdk/python/packages/flet/docs/geolocator/index.md index 87f9832fa0..e57488a8ed 100644 --- a/sdk/python/packages/flet/docs/geolocator/index.md +++ b/sdk/python/packages/flet/docs/geolocator/index.md @@ -145,7 +145,7 @@ permissions = ["location"] ## Example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ## Description diff --git a/sdk/python/packages/flet/docs/lottie/index.md b/sdk/python/packages/flet/docs/lottie/index.md index d7a4b70fd5..6d696a86a5 100644 --- a/sdk/python/packages/flet/docs/lottie/index.md +++ b/sdk/python/packages/flet/docs/lottie/index.md @@ -36,7 +36,7 @@ pip install flet-lottie # (1)! ## Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ## Description diff --git a/sdk/python/packages/flet/docs/map/index.md b/sdk/python/packages/flet/docs/map/index.md index 399122754d..09ee58456e 100644 --- a/sdk/python/packages/flet/docs/map/index.md +++ b/sdk/python/packages/flet/docs/map/index.md @@ -43,31 +43,31 @@ More details [here](tilelayer.md). ### Basic ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` ### Camera Controls ```python ---8<-- "{{ examples }}/camera_controls.py" +--8<-- "{{ examples }}/camera_controls/main.py" ``` ### Idle Camera ```python ---8<-- "{{ examples }}/idle_camera.py" +--8<-- "{{ examples }}/idle_camera/main.py" ``` ### Interaction Flags ```python ---8<-- "{{ examples }}/interaction_flags.py" +--8<-- "{{ examples }}/interaction_flags/main.py" ``` ### Multiple Layers ```python ---8<-- "{{ examples }}/multi_layers.py" +--8<-- "{{ examples }}/multi_layers/main.py" ``` ## Reference diff --git a/sdk/python/packages/flet/docs/permission_handler/index.md b/sdk/python/packages/flet/docs/permission_handler/index.md index 105d0c9e69..7896720609 100644 --- a/sdk/python/packages/flet/docs/permission_handler/index.md +++ b/sdk/python/packages/flet/docs/permission_handler/index.md @@ -61,7 +61,7 @@ See: ## Example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ## Description diff --git a/sdk/python/packages/flet/docs/secure_storage/index.md b/sdk/python/packages/flet/docs/secure_storage/index.md index a220c5313e..decb228612 100644 --- a/sdk/python/packages/flet/docs/secure_storage/index.md +++ b/sdk/python/packages/flet/docs/secure_storage/index.md @@ -47,7 +47,7 @@ pip install flet-secure-storage # (1)! ## Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` ## Description diff --git a/sdk/python/packages/flet/docs/services/accelerometer.md b/sdk/python/packages/flet/docs/services/accelerometer.md index fc9c0e01a2..9a949f66f5 100644 --- a/sdk/python/packages/flet/docs/services/accelerometer.md +++ b/sdk/python/packages/flet/docs/services/accelerometer.md @@ -8,7 +8,7 @@ examples: ../../examples/services/accelerometer ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/barometer.md b/sdk/python/packages/flet/docs/services/barometer.md index dec579306d..ec8ff540c0 100644 --- a/sdk/python/packages/flet/docs/services/barometer.md +++ b/sdk/python/packages/flet/docs/services/barometer.md @@ -8,7 +8,7 @@ examples: ../../examples/services/barometer ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/battery.md b/sdk/python/packages/flet/docs/services/battery.md index 3384cfeb77..1b4f6bec37 100644 --- a/sdk/python/packages/flet/docs/services/battery.md +++ b/sdk/python/packages/flet/docs/services/battery.md @@ -8,7 +8,7 @@ examples: ../../examples/services/battery ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/browsercontextmenu.md b/sdk/python/packages/flet/docs/services/browsercontextmenu.md index a0d82b85b5..d57dc145ce 100644 --- a/sdk/python/packages/flet/docs/services/browsercontextmenu.md +++ b/sdk/python/packages/flet/docs/services/browsercontextmenu.md @@ -8,7 +8,7 @@ examples: ../../examples/services/browsercontextmenu ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/clipboard.md b/sdk/python/packages/flet/docs/services/clipboard.md index f3a60e7e04..82042d838b 100644 --- a/sdk/python/packages/flet/docs/services/clipboard.md +++ b/sdk/python/packages/flet/docs/services/clipboard.md @@ -10,19 +10,19 @@ examples: ../../examples/services/clipboard ### Text ```python ---8<-- "{{ examples }}/text.py" +--8<-- "{{ examples }}/text/main.py" ``` ### Images ```python ---8<-- "{{ examples }}/images.py" +--8<-- "{{ examples }}/images/main.py" ``` ### Files ```python ---8<-- "{{ examples }}/files.py" +--8<-- "{{ examples }}/files/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/connectivity.md b/sdk/python/packages/flet/docs/services/connectivity.md index 8de0f503ac..6ef4ddb49a 100644 --- a/sdk/python/packages/flet/docs/services/connectivity.md +++ b/sdk/python/packages/flet/docs/services/connectivity.md @@ -8,7 +8,7 @@ examples: ../../examples/services/connectivity ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/filepicker.md b/sdk/python/packages/flet/docs/services/filepicker.md index 501401d93f..1a72867874 100644 --- a/sdk/python/packages/flet/docs/services/filepicker.md +++ b/sdk/python/packages/flet/docs/services/filepicker.md @@ -90,7 +90,7 @@ ft.Image(src="/uploads/") ### Pick, save, and get directory paths ```python ---8<-- "{{ examples }}/pick_save_and_get_directory_path.py" +--8<-- "{{ examples }}/pick_save_and_get_directory_path/main.py" ``` {{ image(example_images + "/pick_save_and_get_directory_path.png", width="80%") }} @@ -102,7 +102,7 @@ The following example demonstrates multi-file [pick][flet.FilePicker.pick_files] and [upload][flet.FilePicker.upload] app. ```python ---8<-- "{{ examples }}/pick_and_upload.py" +--8<-- "{{ examples }}/pick_and_upload/main.py" ``` {{ image(example_images + "/pick_and_upload.png", width="80%") }} @@ -114,7 +114,7 @@ you need file contents directly, such as in web apps where [`FilePickerFile.path`][flet.FilePickerFile.path] is not available. ```python ---8<-- "{{ examples }}/pick_and_save_text_content.py" +--8<-- "{{ examples }}/pick_and_save_text_content/main.py" ``` diff --git a/sdk/python/packages/flet/docs/services/gyroscope.md b/sdk/python/packages/flet/docs/services/gyroscope.md index 3fe566142c..6500582a6b 100644 --- a/sdk/python/packages/flet/docs/services/gyroscope.md +++ b/sdk/python/packages/flet/docs/services/gyroscope.md @@ -8,7 +8,7 @@ examples: ../../examples/services/gyroscope ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/hapticfeedback.md b/sdk/python/packages/flet/docs/services/hapticfeedback.md index 2a43e26a0f..da034ad41c 100644 --- a/sdk/python/packages/flet/docs/services/hapticfeedback.md +++ b/sdk/python/packages/flet/docs/services/hapticfeedback.md @@ -10,7 +10,7 @@ examples: ../../examples/services/haptic_feedback ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/magnetometer.md b/sdk/python/packages/flet/docs/services/magnetometer.md index bb005ad90b..ddba403e53 100644 --- a/sdk/python/packages/flet/docs/services/magnetometer.md +++ b/sdk/python/packages/flet/docs/services/magnetometer.md @@ -8,7 +8,7 @@ examples: ../../examples/services/magnetometer ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/screenbrightness.md b/sdk/python/packages/flet/docs/services/screenbrightness.md index 236fb9b8ff..5abbf85b8b 100644 --- a/sdk/python/packages/flet/docs/services/screenbrightness.md +++ b/sdk/python/packages/flet/docs/services/screenbrightness.md @@ -8,7 +8,7 @@ examples: ../../examples/services/screen_brightness ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/semanticsservice.md b/sdk/python/packages/flet/docs/services/semanticsservice.md index 5eac64f5a5..0b3fd3a1d8 100644 --- a/sdk/python/packages/flet/docs/services/semanticsservice.md +++ b/sdk/python/packages/flet/docs/services/semanticsservice.md @@ -10,7 +10,7 @@ examples: ../../examples/services/semantics_service ### Retrieve accessibility features ```python ---8<-- "{{ examples }}/accessibility_features.py" +--8<-- "{{ examples }}/accessibility_features/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/shakedetector.md b/sdk/python/packages/flet/docs/services/shakedetector.md index 8a2759fe08..bedb4f009f 100644 --- a/sdk/python/packages/flet/docs/services/shakedetector.md +++ b/sdk/python/packages/flet/docs/services/shakedetector.md @@ -10,7 +10,7 @@ examples: ../../examples/services/shake_detector ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/share.md b/sdk/python/packages/flet/docs/services/share.md index 11f472ac3a..b0246baa49 100644 --- a/sdk/python/packages/flet/docs/services/share.md +++ b/sdk/python/packages/flet/docs/services/share.md @@ -8,7 +8,7 @@ examples: ../../examples/services/share ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/sharedpreferences.md b/sdk/python/packages/flet/docs/services/sharedpreferences.md index a3e4350229..727dd375d8 100644 --- a/sdk/python/packages/flet/docs/services/sharedpreferences.md +++ b/sdk/python/packages/flet/docs/services/sharedpreferences.md @@ -10,7 +10,7 @@ examples: ../../examples/services/sharedpreferences ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/storagepaths.md b/sdk/python/packages/flet/docs/services/storagepaths.md index 7166597d51..db0f6d8e41 100644 --- a/sdk/python/packages/flet/docs/services/storagepaths.md +++ b/sdk/python/packages/flet/docs/services/storagepaths.md @@ -10,7 +10,7 @@ examples: ../../examples/services/storagepaths ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/urllauncher.md b/sdk/python/packages/flet/docs/services/urllauncher.md index 691ce990c0..fea307f9e2 100644 --- a/sdk/python/packages/flet/docs/services/urllauncher.md +++ b/sdk/python/packages/flet/docs/services/urllauncher.md @@ -10,7 +10,7 @@ examples: ../../examples/services/urllauncher ### Basic Example ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/useraccelerometer.md b/sdk/python/packages/flet/docs/services/useraccelerometer.md index ab46a5cb3a..c81058a152 100644 --- a/sdk/python/packages/flet/docs/services/useraccelerometer.md +++ b/sdk/python/packages/flet/docs/services/useraccelerometer.md @@ -8,7 +8,7 @@ examples: ../../examples/services/user_accelerometer ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/services/wakelock.md b/sdk/python/packages/flet/docs/services/wakelock.md index a232b62c33..56e6e474ca 100644 --- a/sdk/python/packages/flet/docs/services/wakelock.md +++ b/sdk/python/packages/flet/docs/services/wakelock.md @@ -8,7 +8,7 @@ examples: ../../examples/services/wakelock ## Examples ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/tutorials/todo.md b/sdk/python/packages/flet/docs/tutorials/todo.md index 8b7b9b1815..7a588820ec 100644 --- a/sdk/python/packages/flet/docs/tutorials/todo.md +++ b/sdk/python/packages/flet/docs/tutorials/todo.md @@ -6,7 +6,7 @@ examples: ../../examples/tutorials/todo In this tutorial we will show you, step-by-step, how to create a To-Do app in Python using Flet framework and then publish it as a desktop, mobile or web app. The app is a single-file console program of just -[163 lines (formatted!) of Python code](https://github.com/flet-dev/flet/blob/main/sdk/python/examples/apps/todo/todo.py), +[163 lines (formatted!) of Python code](https://github.com/flet-dev/flet/blob/main/sdk/python/examples/apps/todo/basic/main.py), yet it is a multi-platform application with rich, responsive UI: {{ image("../examples/tutorials/todo/media/complete-demo-web.gif", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/alignment.md b/sdk/python/packages/flet/docs/types/alignment.md index d82381a830..2f23dc08d6 100644 --- a/sdk/python/packages/flet/docs/types/alignment.md +++ b/sdk/python/packages/flet/docs/types/alignment.md @@ -10,7 +10,7 @@ example_media: ../examples/controls/types/alignment/media ### Example 1 ```python ---8<-- "{{ examples }}/container.py" +--8<-- "{{ examples }}/container/main.py" ``` {{ image(example_media + "/container.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/animatedswitchertransition.md b/sdk/python/packages/flet/docs/types/animatedswitchertransition.md index 1be795d167..a7ec5b96c4 100644 --- a/sdk/python/packages/flet/docs/types/animatedswitchertransition.md +++ b/sdk/python/packages/flet/docs/types/animatedswitchertransition.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/animated_switcher_transition ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/animationcurve.md b/sdk/python/packages/flet/docs/types/animationcurve.md index 2013613b55..8ee3fe6763 100644 --- a/sdk/python/packages/flet/docs/types/animationcurve.md +++ b/sdk/python/packages/flet/docs/types/animationcurve.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/animation_curve ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/applifecyclestate.md b/sdk/python/packages/flet/docs/types/applifecyclestate.md index 4f7fabec72..b54a534f0e 100644 --- a/sdk/python/packages/flet/docs/types/applifecyclestate.md +++ b/sdk/python/packages/flet/docs/types/applifecyclestate.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/app_lifecycle_state ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/assertiveness.md b/sdk/python/packages/flet/docs/types/assertiveness.md index dfd5651bb2..a39f75fc8a 100644 --- a/sdk/python/packages/flet/docs/types/assertiveness.md +++ b/sdk/python/packages/flet/docs/types/assertiveness.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/assertiveness ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/axis.md b/sdk/python/packages/flet/docs/types/axis.md index b9a614f653..f3a670387a 100644 --- a/sdk/python/packages/flet/docs/types/axis.md +++ b/sdk/python/packages/flet/docs/types/axis.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/axis ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/badge.md b/sdk/python/packages/flet/docs/types/badge.md index e393ff734d..93bb96924d 100644 --- a/sdk/python/packages/flet/docs/types/badge.md +++ b/sdk/python/packages/flet/docs/types/badge.md @@ -13,7 +13,7 @@ example_images: ../test-images/examples/material/golden/macos/badge ### Badge decorating an icon on a NavigationBar ```python ---8<-- "{{ examples }}/basic.py" +--8<-- "{{ examples }}/basic/main.py" ``` {{ image(example_images + "/basic.png", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/blendmode.md b/sdk/python/packages/flet/docs/types/blendmode.md index 1236800fc5..47bda18f35 100644 --- a/sdk/python/packages/flet/docs/types/blendmode.md +++ b/sdk/python/packages/flet/docs/types/blendmode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/blend_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/blur.md b/sdk/python/packages/flet/docs/types/blur.md index 38c2321494..e94da2118d 100644 --- a/sdk/python/packages/flet/docs/types/blur.md +++ b/sdk/python/packages/flet/docs/types/blur.md @@ -5,7 +5,7 @@ ### Example 1 ```python ---8<-- "../../examples/controls/types/blur/container.py" +--8<-- "../../examples/controls/types/blur/container/main.py" ``` {{ image("../examples/controls/types/blur/media/container.gif", alt="container", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/blurstyle.md b/sdk/python/packages/flet/docs/types/blurstyle.md index daa337dc6c..90ebe5c32c 100644 --- a/sdk/python/packages/flet/docs/types/blurstyle.md +++ b/sdk/python/packages/flet/docs/types/blurstyle.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/blur_style ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/blurtilemode.md b/sdk/python/packages/flet/docs/types/blurtilemode.md index 509457fb85..96b7d9e9e9 100644 --- a/sdk/python/packages/flet/docs/types/blurtilemode.md +++ b/sdk/python/packages/flet/docs/types/blurtilemode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/blur_tile_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/border.md b/sdk/python/packages/flet/docs/types/border.md index 8bc6a847b3..a5b196f347 100644 --- a/sdk/python/packages/flet/docs/types/border.md +++ b/sdk/python/packages/flet/docs/types/border.md @@ -5,5 +5,5 @@ ### Example 1 ```python ---8<-- "../../examples/controls/types/border/container.py" +--8<-- "../../examples/controls/types/border/container/main.py" ``` diff --git a/sdk/python/packages/flet/docs/types/bordersidestrokealign.md b/sdk/python/packages/flet/docs/types/bordersidestrokealign.md index c50b49ac0b..dcbf39cb6f 100644 --- a/sdk/python/packages/flet/docs/types/bordersidestrokealign.md +++ b/sdk/python/packages/flet/docs/types/bordersidestrokealign.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/border_side_stroke_align ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/borderstyle.md b/sdk/python/packages/flet/docs/types/borderstyle.md index 246a764187..ebb02e4c4c 100644 --- a/sdk/python/packages/flet/docs/types/borderstyle.md +++ b/sdk/python/packages/flet/docs/types/borderstyle.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/border_style ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/boxfit.md b/sdk/python/packages/flet/docs/types/boxfit.md index 1677b6a5f1..c3e0f4dbee 100644 --- a/sdk/python/packages/flet/docs/types/boxfit.md +++ b/sdk/python/packages/flet/docs/types/boxfit.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/box_fit ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/boxshadow.md b/sdk/python/packages/flet/docs/types/boxshadow.md index 45d22447ba..588a778eb6 100644 --- a/sdk/python/packages/flet/docs/types/boxshadow.md +++ b/sdk/python/packages/flet/docs/types/boxshadow.md @@ -5,5 +5,5 @@ ### Example 1 ```python ---8<-- "../../examples/controls/types/box_shadow/container.py" +--8<-- "../../examples/controls/types/box_shadow/container/main.py" ``` diff --git a/sdk/python/packages/flet/docs/types/boxshape.md b/sdk/python/packages/flet/docs/types/boxshape.md index ad9c31da6b..d7e576d895 100644 --- a/sdk/python/packages/flet/docs/types/boxshape.md +++ b/sdk/python/packages/flet/docs/types/boxshape.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/box_shape ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/cardvariant.md b/sdk/python/packages/flet/docs/types/cardvariant.md index a81ecd0b56..7b6be41335 100644 --- a/sdk/python/packages/flet/docs/types/cardvariant.md +++ b/sdk/python/packages/flet/docs/types/cardvariant.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/card_variant ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/clipbehavior.md b/sdk/python/packages/flet/docs/types/clipbehavior.md index 4449b82a89..6d7d6a9f0e 100644 --- a/sdk/python/packages/flet/docs/types/clipbehavior.md +++ b/sdk/python/packages/flet/docs/types/clipbehavior.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/clip_behavior ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/contextmenutrigger.md b/sdk/python/packages/flet/docs/types/contextmenutrigger.md index d02b4a7fe5..371cedcc91 100644 --- a/sdk/python/packages/flet/docs/types/contextmenutrigger.md +++ b/sdk/python/packages/flet/docs/types/contextmenutrigger.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/context_menu_trigger ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/crossaxisalignment.md b/sdk/python/packages/flet/docs/types/crossaxisalignment.md index 6a823f189c..891b3566c2 100644 --- a/sdk/python/packages/flet/docs/types/crossaxisalignment.md +++ b/sdk/python/packages/flet/docs/types/crossaxisalignment.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/cross_axis_alignment ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/cupertinobuttonsize.md b/sdk/python/packages/flet/docs/types/cupertinobuttonsize.md index acb9798760..213d344aa6 100644 --- a/sdk/python/packages/flet/docs/types/cupertinobuttonsize.md +++ b/sdk/python/packages/flet/docs/types/cupertinobuttonsize.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/cupertino_button_size ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/cupertinodatepickerdateorder.md b/sdk/python/packages/flet/docs/types/cupertinodatepickerdateorder.md index 13af23f92b..e0c8a83155 100644 --- a/sdk/python/packages/flet/docs/types/cupertinodatepickerdateorder.md +++ b/sdk/python/packages/flet/docs/types/cupertinodatepickerdateorder.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/cupertino_date_picker_date_order ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/cupertinodatepickermode.md b/sdk/python/packages/flet/docs/types/cupertinodatepickermode.md index 522295bba7..27a24f61cd 100644 --- a/sdk/python/packages/flet/docs/types/cupertinodatepickermode.md +++ b/sdk/python/packages/flet/docs/types/cupertinodatepickermode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/cupertino_date_picker_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/cupertinotimerpickermode.md b/sdk/python/packages/flet/docs/types/cupertinotimerpickermode.md index 237661c671..c743b4ba91 100644 --- a/sdk/python/packages/flet/docs/types/cupertinotimerpickermode.md +++ b/sdk/python/packages/flet/docs/types/cupertinotimerpickermode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/cupertino_timer_picker_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/datepickerentrymode.md b/sdk/python/packages/flet/docs/types/datepickerentrymode.md index 196e9aa504..245c360314 100644 --- a/sdk/python/packages/flet/docs/types/datepickerentrymode.md +++ b/sdk/python/packages/flet/docs/types/datepickerentrymode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/date_picker_entry_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/datepickermode.md b/sdk/python/packages/flet/docs/types/datepickermode.md index fdbb8d1710..dbcb6b07e1 100644 --- a/sdk/python/packages/flet/docs/types/datepickermode.md +++ b/sdk/python/packages/flet/docs/types/datepickermode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/date_picker_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/dismissdirection.md b/sdk/python/packages/flet/docs/types/dismissdirection.md index a3e082b489..a928606dff 100644 --- a/sdk/python/packages/flet/docs/types/dismissdirection.md +++ b/sdk/python/packages/flet/docs/types/dismissdirection.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/dismiss_direction ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/filterquality.md b/sdk/python/packages/flet/docs/types/filterquality.md index 6a6795dde8..7686c380c2 100644 --- a/sdk/python/packages/flet/docs/types/filterquality.md +++ b/sdk/python/packages/flet/docs/types/filterquality.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/filter_quality ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/floatingactionbuttonlocation.md b/sdk/python/packages/flet/docs/types/floatingactionbuttonlocation.md index 71f82e30b2..fd258d0fea 100644 --- a/sdk/python/packages/flet/docs/types/floatingactionbuttonlocation.md +++ b/sdk/python/packages/flet/docs/types/floatingactionbuttonlocation.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/floating_action_button_location ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/fontweight.md b/sdk/python/packages/flet/docs/types/fontweight.md index b12002695c..24aa3ef6f8 100644 --- a/sdk/python/packages/flet/docs/types/fontweight.md +++ b/sdk/python/packages/flet/docs/types/fontweight.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/font_weight ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/gradient/index.md b/sdk/python/packages/flet/docs/types/gradient/index.md index f1c652e0f9..33a14ad41c 100644 --- a/sdk/python/packages/flet/docs/types/gradient/index.md +++ b/sdk/python/packages/flet/docs/types/gradient/index.md @@ -5,5 +5,5 @@ ### Containers with gradients ```python ---8<-- "../../examples/controls/types/gradient/container.py" +--8<-- "../../examples/controls/types/gradient/container/main.py" ``` diff --git a/sdk/python/packages/flet/docs/types/gradienttilemode.md b/sdk/python/packages/flet/docs/types/gradienttilemode.md index d2d2cd169f..473eb2a945 100644 --- a/sdk/python/packages/flet/docs/types/gradienttilemode.md +++ b/sdk/python/packages/flet/docs/types/gradienttilemode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/gradient_tile_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/imagerepeat.md b/sdk/python/packages/flet/docs/types/imagerepeat.md index ec56b99715..8ea9197213 100644 --- a/sdk/python/packages/flet/docs/types/imagerepeat.md +++ b/sdk/python/packages/flet/docs/types/imagerepeat.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/image_repeat ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/labelposition.md b/sdk/python/packages/flet/docs/types/labelposition.md index f11d4c90dc..570819cd38 100644 --- a/sdk/python/packages/flet/docs/types/labelposition.md +++ b/sdk/python/packages/flet/docs/types/labelposition.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/label_position ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/launchmode.md b/sdk/python/packages/flet/docs/types/launchmode.md index eae14c3c9d..d608d40dfa 100644 --- a/sdk/python/packages/flet/docs/types/launchmode.md +++ b/sdk/python/packages/flet/docs/types/launchmode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/launch_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/lineargradient.md b/sdk/python/packages/flet/docs/types/lineargradient.md index 5ac84b9b98..5887f9c23e 100644 --- a/sdk/python/packages/flet/docs/types/lineargradient.md +++ b/sdk/python/packages/flet/docs/types/lineargradient.md @@ -5,7 +5,7 @@ ### Container with linear gradient ```python ---8<-- "../../examples/controls/types/gradient/linear_gradient/container.py" +--8<-- "../../examples/controls/types/gradient/linear_gradient/container/main.py" ``` {{ image("../examples/controls/types/gradient/linear_gradient/media/container.png", alt="container", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/listtilestyle.md b/sdk/python/packages/flet/docs/types/listtilestyle.md index 0100694017..98731f89c7 100644 --- a/sdk/python/packages/flet/docs/types/listtilestyle.md +++ b/sdk/python/packages/flet/docs/types/listtilestyle.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/list_tile_style ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/listtiletitlealignment.md b/sdk/python/packages/flet/docs/types/listtiletitlealignment.md index 2b1c888e98..b596a3c297 100644 --- a/sdk/python/packages/flet/docs/types/listtiletitlealignment.md +++ b/sdk/python/packages/flet/docs/types/listtiletitlealignment.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/list_tile_title_alignment ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/mainaxisalignment.md b/sdk/python/packages/flet/docs/types/mainaxisalignment.md index 97c4a07042..662451729c 100644 --- a/sdk/python/packages/flet/docs/types/mainaxisalignment.md +++ b/sdk/python/packages/flet/docs/types/mainaxisalignment.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/main_axis_alignment ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/margin.md b/sdk/python/packages/flet/docs/types/margin.md index b86381f051..bcca7b5d85 100644 --- a/sdk/python/packages/flet/docs/types/margin.md +++ b/sdk/python/packages/flet/docs/types/margin.md @@ -5,5 +5,5 @@ ### Example 1 ```python ---8<-- "../../examples/controls/types/margin/container.py" +--8<-- "../../examples/controls/types/margin/container/main.py" ``` diff --git a/sdk/python/packages/flet/docs/types/mousecursor.md b/sdk/python/packages/flet/docs/types/mousecursor.md index 377a886920..9a43481805 100644 --- a/sdk/python/packages/flet/docs/types/mousecursor.md +++ b/sdk/python/packages/flet/docs/types/mousecursor.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/mouse_cursor ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/navigationbarlabelbehavior.md b/sdk/python/packages/flet/docs/types/navigationbarlabelbehavior.md index bcdccb4e2b..6b7503f839 100644 --- a/sdk/python/packages/flet/docs/types/navigationbarlabelbehavior.md +++ b/sdk/python/packages/flet/docs/types/navigationbarlabelbehavior.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/navigation_bar_label_behavior ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/navigationraillabeltype.md b/sdk/python/packages/flet/docs/types/navigationraillabeltype.md index 7e62d73ede..9667db4552 100644 --- a/sdk/python/packages/flet/docs/types/navigationraillabeltype.md +++ b/sdk/python/packages/flet/docs/types/navigationraillabeltype.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/navigation_rail_label_type ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/orientation.md b/sdk/python/packages/flet/docs/types/orientation.md index 44854abade..e3c2a8341e 100644 --- a/sdk/python/packages/flet/docs/types/orientation.md +++ b/sdk/python/packages/flet/docs/types/orientation.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/orientation ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/overlayvisibilitymode.md b/sdk/python/packages/flet/docs/types/overlayvisibilitymode.md index c462b4a08f..a3e3fcfc98 100644 --- a/sdk/python/packages/flet/docs/types/overlayvisibilitymode.md +++ b/sdk/python/packages/flet/docs/types/overlayvisibilitymode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/overlay_visibility_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/padding.md b/sdk/python/packages/flet/docs/types/padding.md index d7365d43dd..e6ec8c3a06 100644 --- a/sdk/python/packages/flet/docs/types/padding.md +++ b/sdk/python/packages/flet/docs/types/padding.md @@ -5,7 +5,7 @@ ### Example 1 ```python ---8<-- "../../examples/controls/types/padding/container.py" +--8<-- "../../examples/controls/types/padding/container/main.py" ``` {{ image("../examples/controls/types/padding/media/container.png", alt="container", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/paintgradient/index.md b/sdk/python/packages/flet/docs/types/paintgradient/index.md index c5a8337be7..4e96cc86c1 100644 --- a/sdk/python/packages/flet/docs/types/paintgradient/index.md +++ b/sdk/python/packages/flet/docs/types/paintgradient/index.md @@ -5,5 +5,5 @@ ### Canvas paint ```python ---8<-- "../../examples/controls/types/paint_gradient/canvas_paint.py" +--8<-- "../../examples/controls/types/paint_gradient/canvas_paint/main.py" ``` diff --git a/sdk/python/packages/flet/docs/types/paintingstyle.md b/sdk/python/packages/flet/docs/types/paintingstyle.md index 51645db9d1..0bd4290226 100644 --- a/sdk/python/packages/flet/docs/types/paintingstyle.md +++ b/sdk/python/packages/flet/docs/types/paintingstyle.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/painting_style ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/paintlineargradient.md b/sdk/python/packages/flet/docs/types/paintlineargradient.md index e46d3cf861..af04caa489 100644 --- a/sdk/python/packages/flet/docs/types/paintlineargradient.md +++ b/sdk/python/packages/flet/docs/types/paintlineargradient.md @@ -5,7 +5,7 @@ ### Canvas paint ```python ---8<-- "../../examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint.py" +--8<-- "../../examples/controls/types/paint_gradient/paint_linear_gradient/canvas_paint/main.py" ``` {{ image("../examples/controls/types/paint_gradient/paint_linear_gradient/media/canvas_paint.png", alt="canvas-paint", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/paintradialgradient.md b/sdk/python/packages/flet/docs/types/paintradialgradient.md index 7293d8fde5..ef08e96989 100644 --- a/sdk/python/packages/flet/docs/types/paintradialgradient.md +++ b/sdk/python/packages/flet/docs/types/paintradialgradient.md @@ -5,7 +5,7 @@ ### Canvas paint ```python ---8<-- "../../examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint.py" +--8<-- "../../examples/controls/types/paint_gradient/paint_radial_gradient/canvas_paint/main.py" ``` {{ image("../examples/controls/types/paint_gradient/paint_radial_gradient/media/canvas_paint.png", alt="canvas-paint", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/paintsweepgradient.md b/sdk/python/packages/flet/docs/types/paintsweepgradient.md index 21f2f1441c..1593e045fb 100644 --- a/sdk/python/packages/flet/docs/types/paintsweepgradient.md +++ b/sdk/python/packages/flet/docs/types/paintsweepgradient.md @@ -5,7 +5,7 @@ ### Canvas paint ```python ---8<-- "../../examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint.py" +--8<-- "../../examples/controls/types/paint_gradient/paint_sweep_gradient/canvas_paint/main.py" ``` {{ image("../examples/controls/types/paint_gradient/paint_sweep_gradient/media/canvas_paint.png", alt="canvas-paint", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/pointmode.md b/sdk/python/packages/flet/docs/types/pointmode.md index 821ac4e70f..8e2269171d 100644 --- a/sdk/python/packages/flet/docs/types/pointmode.md +++ b/sdk/python/packages/flet/docs/types/pointmode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/point_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/popupmenuposition.md b/sdk/python/packages/flet/docs/types/popupmenuposition.md index fee12117d9..0632a7d501 100644 --- a/sdk/python/packages/flet/docs/types/popupmenuposition.md +++ b/sdk/python/packages/flet/docs/types/popupmenuposition.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/popup_menu_position ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/radialgradient.md b/sdk/python/packages/flet/docs/types/radialgradient.md index 75004ee3b5..d5653a061c 100644 --- a/sdk/python/packages/flet/docs/types/radialgradient.md +++ b/sdk/python/packages/flet/docs/types/radialgradient.md @@ -5,7 +5,7 @@ ### Container with radial gradient ```python ---8<-- "../../examples/controls/types/gradient/radial_gradient/container.py" +--8<-- "../../examples/controls/types/gradient/radial_gradient/container/main.py" ``` {{ image("../examples/controls/types/gradient/radial_gradient/media/container.png", alt="container", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/scrollbar.md b/sdk/python/packages/flet/docs/types/scrollbar.md index 283521427a..8bf700e2da 100644 --- a/sdk/python/packages/flet/docs/types/scrollbar.md +++ b/sdk/python/packages/flet/docs/types/scrollbar.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/scroll_bar ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/docs/types/scrollbarorientation.md b/sdk/python/packages/flet/docs/types/scrollbarorientation.md index 13960d5fec..d240429b83 100644 --- a/sdk/python/packages/flet/docs/types/scrollbarorientation.md +++ b/sdk/python/packages/flet/docs/types/scrollbarorientation.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/scroll_bar_orientation ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/scrolldirection.md b/sdk/python/packages/flet/docs/types/scrolldirection.md index 0e93f34d48..56b5ce8886 100644 --- a/sdk/python/packages/flet/docs/types/scrolldirection.md +++ b/sdk/python/packages/flet/docs/types/scrolldirection.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/scroll_direction ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/scrollmode.md b/sdk/python/packages/flet/docs/types/scrollmode.md index 75c5518642..2cfec74409 100644 --- a/sdk/python/packages/flet/docs/types/scrollmode.md +++ b/sdk/python/packages/flet/docs/types/scrollmode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/scroll_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/scrolltype.md b/sdk/python/packages/flet/docs/types/scrolltype.md index 9366f9c186..71e9821983 100644 --- a/sdk/python/packages/flet/docs/types/scrolltype.md +++ b/sdk/python/packages/flet/docs/types/scrolltype.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/scroll_type ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/sliderinteraction.md b/sdk/python/packages/flet/docs/types/sliderinteraction.md index 0b5cb54fe8..6014fdc429 100644 --- a/sdk/python/packages/flet/docs/types/sliderinteraction.md +++ b/sdk/python/packages/flet/docs/types/sliderinteraction.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/slider_interaction ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/snackbarbehavior.md b/sdk/python/packages/flet/docs/types/snackbarbehavior.md index 1e5cdca2b8..f160cb3e04 100644 --- a/sdk/python/packages/flet/docs/types/snackbarbehavior.md +++ b/sdk/python/packages/flet/docs/types/snackbarbehavior.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/snack_bar_behavior ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/strokecap.md b/sdk/python/packages/flet/docs/types/strokecap.md index 15e3f96c61..4ee70c33f2 100644 --- a/sdk/python/packages/flet/docs/types/strokecap.md +++ b/sdk/python/packages/flet/docs/types/strokecap.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/stroke_cap ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/strokejoin.md b/sdk/python/packages/flet/docs/types/strokejoin.md index 569f03b83f..d9c137f541 100644 --- a/sdk/python/packages/flet/docs/types/strokejoin.md +++ b/sdk/python/packages/flet/docs/types/strokejoin.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/stroke_join ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/sweepgradient.md b/sdk/python/packages/flet/docs/types/sweepgradient.md index c2fc1e5b02..a6720843f4 100644 --- a/sdk/python/packages/flet/docs/types/sweepgradient.md +++ b/sdk/python/packages/flet/docs/types/sweepgradient.md @@ -5,7 +5,7 @@ ### Container with sweep gradient ```python ---8<-- "../../examples/controls/types/gradient/sweep_gradient/container.py" +--8<-- "../../examples/controls/types/gradient/sweep_gradient/container/main.py" ``` {{ image("../examples/controls/types/gradient/sweep_gradient/media/container.png", alt="container", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/tabalignment.md b/sdk/python/packages/flet/docs/types/tabalignment.md index 4a6bcff4ba..eb6ac7ba3f 100644 --- a/sdk/python/packages/flet/docs/types/tabalignment.md +++ b/sdk/python/packages/flet/docs/types/tabalignment.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/tab_alignment ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/tabbarindicatorsize.md b/sdk/python/packages/flet/docs/types/tabbarindicatorsize.md index d8c3e4e5b6..2635d70803 100644 --- a/sdk/python/packages/flet/docs/types/tabbarindicatorsize.md +++ b/sdk/python/packages/flet/docs/types/tabbarindicatorsize.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/tab_bar_indicator_size ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/tabindicatoranimation.md b/sdk/python/packages/flet/docs/types/tabindicatoranimation.md index 5883719fb3..d75718ad59 100644 --- a/sdk/python/packages/flet/docs/types/tabindicatoranimation.md +++ b/sdk/python/packages/flet/docs/types/tabindicatoranimation.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/tab_indicator_animation ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/textalign.md b/sdk/python/packages/flet/docs/types/textalign.md index 5d6f86a0a6..6e8479e2a7 100644 --- a/sdk/python/packages/flet/docs/types/textalign.md +++ b/sdk/python/packages/flet/docs/types/textalign.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/text_align ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/textcapitalization.md b/sdk/python/packages/flet/docs/types/textcapitalization.md index d33ef51986..3515cfa7a7 100644 --- a/sdk/python/packages/flet/docs/types/textcapitalization.md +++ b/sdk/python/packages/flet/docs/types/textcapitalization.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/text_capitalization ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/textdecorationstyle.md b/sdk/python/packages/flet/docs/types/textdecorationstyle.md index 93da4c20df..37699870ff 100644 --- a/sdk/python/packages/flet/docs/types/textdecorationstyle.md +++ b/sdk/python/packages/flet/docs/types/textdecorationstyle.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/text_decoration_style ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/textoverflow.md b/sdk/python/packages/flet/docs/types/textoverflow.md index 7109524f95..05bf614876 100644 --- a/sdk/python/packages/flet/docs/types/textoverflow.md +++ b/sdk/python/packages/flet/docs/types/textoverflow.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/text_overflow ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/thememode.md b/sdk/python/packages/flet/docs/types/thememode.md index 574938727c..c2d8506414 100644 --- a/sdk/python/packages/flet/docs/types/thememode.md +++ b/sdk/python/packages/flet/docs/types/thememode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/theme_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/tileaffinity.md b/sdk/python/packages/flet/docs/types/tileaffinity.md index e5327a850d..b93b40db9a 100644 --- a/sdk/python/packages/flet/docs/types/tileaffinity.md +++ b/sdk/python/packages/flet/docs/types/tileaffinity.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/tile_affinity ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/timepickerentrymode.md b/sdk/python/packages/flet/docs/types/timepickerentrymode.md index 3fe38ba71c..86a9aa576f 100644 --- a/sdk/python/packages/flet/docs/types/timepickerentrymode.md +++ b/sdk/python/packages/flet/docs/types/timepickerentrymode.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/time_picker_entry_mode ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/timepickerhourformat.md b/sdk/python/packages/flet/docs/types/timepickerhourformat.md index 3134538cdc..25caf9ec22 100644 --- a/sdk/python/packages/flet/docs/types/timepickerhourformat.md +++ b/sdk/python/packages/flet/docs/types/timepickerhourformat.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/time_picker_hour_format ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/tooltip.md b/sdk/python/packages/flet/docs/types/tooltip.md index 5600cf21ea..ec48e74af8 100644 --- a/sdk/python/packages/flet/docs/types/tooltip.md +++ b/sdk/python/packages/flet/docs/types/tooltip.md @@ -5,7 +5,7 @@ ### Tooltip with decoration ```python ---8<-- "../../examples/controls/types/tooltip/with_decoration.py" +--8<-- "../../examples/controls/types/tooltip/with_decoration/main.py" ``` {{ image("../examples/controls/types/tooltip/media/with_decoration.gif", alt="with-decoration", width="80%") }} diff --git a/sdk/python/packages/flet/docs/types/urltarget.md b/sdk/python/packages/flet/docs/types/urltarget.md index c23ebc7fb7..a24b1ffc78 100644 --- a/sdk/python/packages/flet/docs/types/urltarget.md +++ b/sdk/python/packages/flet/docs/types/urltarget.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/url_target ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/visualdensity.md b/sdk/python/packages/flet/docs/types/visualdensity.md index 86d5bdcc9f..4708f21821 100644 --- a/sdk/python/packages/flet/docs/types/visualdensity.md +++ b/sdk/python/packages/flet/docs/types/visualdensity.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/visual_density ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/types/windoweventtype.md b/sdk/python/packages/flet/docs/types/windoweventtype.md index 9224f0ff41..41d82741a6 100644 --- a/sdk/python/packages/flet/docs/types/windoweventtype.md +++ b/sdk/python/packages/flet/docs/types/windoweventtype.md @@ -10,7 +10,7 @@ examples: ../../examples/controls/types/window_event_type ### Showcase ```python ---8<-- "{{ examples }}/showcase.py" +--8<-- "{{ examples }}/showcase/main.py" ``` {{ class_members(class_name, separate_signature=False) }} diff --git a/sdk/python/packages/flet/docs/video/index.md b/sdk/python/packages/flet/docs/video/index.md index 2dd66544a5..4ad4932e19 100644 --- a/sdk/python/packages/flet/docs/video/index.md +++ b/sdk/python/packages/flet/docs/video/index.md @@ -93,7 +93,7 @@ sudo ln -s /usr/lib/x86_64-linux-gnu/libmpv.so /usr/lib/libmpv.so.1 ### Basic example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ## Description diff --git a/sdk/python/packages/flet/docs/webview/index.md b/sdk/python/packages/flet/docs/webview/index.md index 509d07eef3..7d14b3ff1a 100644 --- a/sdk/python/packages/flet/docs/webview/index.md +++ b/sdk/python/packages/flet/docs/webview/index.md @@ -37,7 +37,7 @@ pip install flet-webview # (1)! ## Example ```python ---8<-- "{{ examples }}/example_1.py" +--8<-- "{{ examples }}/example_1/main.py" ``` ## Troubleshooting diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/container/nested_themes_1.png b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/container/nested_themes_1.png similarity index 100% rename from sdk/python/packages/flet/integration_tests/examples/material/golden/macos/container/nested_themes_1.png rename to sdk/python/packages/flet/integration_tests/examples/core/golden/macos/container/nested_themes_1.png diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/container/nested_themes_2.png b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/container/nested_themes_2.png similarity index 100% rename from sdk/python/packages/flet/integration_tests/examples/material/golden/macos/container/nested_themes_2.png rename to sdk/python/packages/flet/integration_tests/examples/core/golden/macos/container/nested_themes_2.png diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/container/size_aware.png b/sdk/python/packages/flet/integration_tests/examples/core/golden/macos/container/size_aware.png similarity index 100% rename from sdk/python/packages/flet/integration_tests/examples/material/golden/macos/container/size_aware.png rename to sdk/python/packages/flet/integration_tests/examples/core/golden/macos/container/size_aware.png diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_container.py b/sdk/python/packages/flet/integration_tests/examples/core/test_container.py similarity index 89% rename from sdk/python/packages/flet/integration_tests/examples/material/test_container.py rename to sdk/python/packages/flet/integration_tests/examples/core/test_container.py index 0a03ddbba3..cb3b4fe70f 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_container.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_container.py @@ -1,11 +1,9 @@ import pytest +import examples.controls.container.nested_themes_1.main as nested_themes_1 +import examples.controls.container.nested_themes_2.main as nested_themes_2 +import examples.controls.container.size_aware.main as size_aware import flet.testing as ftt -from examples.controls.container import ( - nested_themes_1, - nested_themes_2, - size_aware, -) @pytest.mark.parametrize( diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_control.py b/sdk/python/packages/flet/integration_tests/examples/core/test_control.py index 17b51ddeb4..b306dd9ede 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_control.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_control.py @@ -1,17 +1,23 @@ import pytest import flet.testing as ftt -from examples.controls.control import ( - expand_loose_chat_messages, - expand_row_equal_split, - expand_row_proportional_1_3_1, - expand_textfield_in_row, +from examples.controls.control.expand_loose_chat_messages import ( + main as expand_loose_chat_messages_main, +) +from examples.controls.control.expand_row_equal_split import ( + main as expand_row_equal_split_main, +) +from examples.controls.control.expand_row_proportional_1_3_1 import ( + main as expand_row_proportional_1_3_1_main, +) +from examples.controls.control.expand_textfield_in_row import ( + main as expand_textfield_in_row_main, ) @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": expand_textfield_in_row.main}], + [{"flet_app_main": expand_textfield_in_row_main.main}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -24,7 +30,7 @@ async def test_expand_textfield_in_row(flet_app_function: ftt.FletTestApp): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": expand_row_proportional_1_3_1.main}], + [{"flet_app_main": expand_row_proportional_1_3_1_main.main}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -37,7 +43,7 @@ async def test_expand_row_proportional_1_3_1(flet_app_function: ftt.FletTestApp) @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": expand_row_equal_split.main}], + [{"flet_app_main": expand_row_equal_split_main.main}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -50,7 +56,7 @@ async def test_expand_row_equal_split(flet_app_function: ftt.FletTestApp): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": expand_loose_chat_messages.main}], + [{"flet_app_main": expand_loose_chat_messages_main.main}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_hero.py b/sdk/python/packages/flet/integration_tests/examples/core/test_hero.py index 09142d76d9..2ef30273b7 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_hero.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_hero.py @@ -2,7 +2,7 @@ import flet as ft import flet.testing as ftt -from examples.controls.hero import basic +from examples.controls.hero.basic import main as basic @pytest.mark.parametrize( diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_layout_control.py b/sdk/python/packages/flet/integration_tests/examples/core/test_layout_control.py index 8627431742..6cce09452e 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_layout_control.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_layout_control.py @@ -1,7 +1,8 @@ import pytest import flet.testing as ftt -from examples.controls.layout_control import flip, matrix4_transform +from examples.controls.layout_control.flip import main as flip +from examples.controls.layout_control.matrix4_transform import main as matrix4_transform @pytest.mark.parametrize( diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_pagelet.py b/sdk/python/packages/flet/integration_tests/examples/core/test_pagelet.py index 040c8b5f3c..bd8e3b5323 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_pagelet.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_pagelet.py @@ -2,7 +2,7 @@ import flet as ft import flet.testing as ftt -from examples.controls.pagelet import basic +from examples.controls.pagelet.basic.main import main as basic @pytest.mark.asyncio(loop_scope="function") @@ -27,7 +27,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": basic.main}], + [{"flet_app_main": basic}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_placeholder.py b/sdk/python/packages/flet/integration_tests/examples/core/test_placeholder.py index af2d1b5ee8..c8893949f7 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_placeholder.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_placeholder.py @@ -2,7 +2,7 @@ import flet as ft import flet.testing as ftt -from examples.controls.placeholder import basic +from examples.controls.placeholder.basic.main import main as basic @pytest.mark.asyncio(loop_scope="function") @@ -17,20 +17,28 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): ) +@pytest.mark.skip(reason="Will fix it later") @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": basic.main}], + [{"flet_app_main": basic}], indirect=True, ) +# @pytest.mark.asyncio(loop_scope="function") +# async def test_basic(flet_app_function: ftt.FletTestApp): +# flet_app_function.page.enable_screenshots = True +# flet_app_function.resize_page(200, 200) +# flet_app_function.page.update() +# await flet_app_function.tester.pump_and_settle() +# flet_app_function.assert_screenshot( +# "basic", +# await flet_app_function.page.take_screenshot( +# pixel_ratio=flet_app_function.screenshots_pixel_ratio +# ), +# ) +# ) @pytest.mark.asyncio(loop_scope="function") async def test_basic(flet_app_function: ftt.FletTestApp): - flet_app_function.page.enable_screenshots = True - flet_app_function.resize_page(200, 200) - flet_app_function.page.update() - await flet_app_function.tester.pump_and_settle() flet_app_function.assert_screenshot( "basic", - await flet_app_function.page.take_screenshot( - pixel_ratio=flet_app_function.screenshots_pixel_ratio - ), + await flet_app_function.take_page_controls_screenshot(), ) diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_responsive_row.py b/sdk/python/packages/flet/integration_tests/examples/core/test_responsive_row.py index 04aa4f0b32..22f295ac83 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_responsive_row.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_responsive_row.py @@ -4,7 +4,10 @@ import flet as ft import flet.testing as ftt -from examples.controls.responsive_row import basic, custom_breakpoint +from examples.controls.responsive_row.basic.main import main as basic +from examples.controls.responsive_row.custom_breakpoint.main import ( + main as custom_breakpoint, +) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_rotated_box.py b/sdk/python/packages/flet/integration_tests/examples/core/test_rotated_box.py index a7074cb8e7..1cf26aded4 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_rotated_box.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_rotated_box.py @@ -1,7 +1,7 @@ import pytest import flet.testing as ftt -from examples.controls.rotated_box import basic +from examples.controls.rotated_box.basic.main import main as basic @pytest.mark.parametrize( diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_row.py b/sdk/python/packages/flet/integration_tests/examples/core/test_row.py index d0631dc5ad..1055d707fb 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_row.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_row.py @@ -1,8 +1,11 @@ import pytest +import examples.controls.row.alignment.main as alignment +import examples.controls.row.spacing.main as spacing +import examples.controls.row.vertical_alignment.main as vertical_alignment +import examples.controls.row.wrap.main as wrap import flet as ft import flet.testing as ftt -from examples.controls.row import alignment, spacing, vertical_alignment, wrap @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_shader_mask.py b/sdk/python/packages/flet/integration_tests/examples/core/test_shader_mask.py index e8905a8b0b..761fc6aa74 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_shader_mask.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_shader_mask.py @@ -1,11 +1,11 @@ import pytest +import examples.controls.shader_mask.fade_out_image_bottom.main as fade_out_image_bottom +import examples.controls.shader_mask.pink_radial_glow.main as pink_radial_glow import flet as ft import flet.testing as ftt -from examples.controls.shader_mask import ( - fade_out_image_bottom, - linear_and_radial_gradients, - pink_radial_glow, +from examples.controls.shader_mask.linear_and_radial_gradients import ( + main as linear_gradients, ) @@ -59,7 +59,7 @@ async def test_pink_radial_glow(flet_app_function: ftt.FletTestApp): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": linear_and_radial_gradients.main}], + [{"flet_app_main": linear_gradients.main}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_shimmer.py b/sdk/python/packages/flet/integration_tests/examples/core/test_shimmer.py index 5130cd649a..97712a80de 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_shimmer.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_shimmer.py @@ -1,8 +1,9 @@ import pytest +import examples.controls.shimmer.basic_placeholder.main as basic_placeholder +import examples.controls.shimmer.custom_gradient.main as custom_gradient import flet as ft import flet.testing as ftt -from examples.controls.shimmer import basic_placeholder, custom_gradient @pytest.mark.skip(reason="The test is flaky on CI") diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_stack.py b/sdk/python/packages/flet/integration_tests/examples/core/test_stack.py index 84cfac06ae..54e929ec84 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_stack.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_stack.py @@ -1,11 +1,9 @@ import pytest +import examples.controls.stack.absolute_positioning.main as absolute_positioning +import examples.controls.stack.online_avatar.main as online_avatar import flet as ft import flet.testing as ftt -from examples.controls.stack import ( - absolute_positioning, - online_avatar, -) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_vertical_divider.py b/sdk/python/packages/flet/integration_tests/examples/core/test_vertical_divider.py index 86b2bf3005..3df5c67d47 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_vertical_divider.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_vertical_divider.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.vertical_divider.basic.main as basic import flet as ft import flet.testing as ftt -from examples.controls.vertical_divider import basic @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/core/test_window_drag_area.py b/sdk/python/packages/flet/integration_tests/examples/core/test_window_drag_area.py index e12f1ade5a..4195ce672f 100644 --- a/sdk/python/packages/flet/integration_tests/examples/core/test_window_drag_area.py +++ b/sdk/python/packages/flet/integration_tests/examples/core/test_window_drag_area.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.window_drag_area.no_frame_window.main as no_frame_window import flet as ft import flet.testing as ftt -from examples.controls.window_drag_area import no_frame_window @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/cupertino/test_cupertino_timer_picker.py b/sdk/python/packages/flet/integration_tests/examples/cupertino/test_cupertino_timer_picker.py index 65f170f7f0..3ff4f5c72f 100644 --- a/sdk/python/packages/flet/integration_tests/examples/cupertino/test_cupertino_timer_picker.py +++ b/sdk/python/packages/flet/integration_tests/examples/cupertino/test_cupertino_timer_picker.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.cupertino_timer_picker.basic.main as basic import flet as ft import flet.testing as ftt -from examples.controls.cupertino_timer_picker import basic @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/extensions/code_editor/test_code_editor.py b/sdk/python/packages/flet/integration_tests/examples/extensions/code_editor/test_code_editor.py index 10904d80b6..5f7068114a 100644 --- a/sdk/python/packages/flet/integration_tests/examples/extensions/code_editor/test_code_editor.py +++ b/sdk/python/packages/flet/integration_tests/examples/extensions/code_editor/test_code_editor.py @@ -1,7 +1,9 @@ import pytest +import examples.controls.code_editor.example_1.main as example_1 +import examples.controls.code_editor.example_2.main as example_2 +import examples.controls.code_editor.example_3.main as example_3 import flet.testing as ftt -from examples.controls.code_editor import example_1, example_2, example_3 @pytest.mark.parametrize( diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/alert_dialog/alert_dialog_flow.gif b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/alert_dialog/alert_dialog_flow.gif index 45692cbdff..8ae3224269 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/alert_dialog/alert_dialog_flow.gif and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/alert_dialog/alert_dialog_flow.gif differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic.gif b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic.gif index cb51a04769..ee176dbd46 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic.gif and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic.gif differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen.gif b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen.gif index 71f2d5846d..680d23ffd0 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen.gif and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen.gif differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_1.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_1.png index 1467fb94a6..791f772ff7 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_1.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_1.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_2.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_2.png index 592c628759..fa3b3b0df8 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_2.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_2.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_3.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_3.png index 1467fb94a6..791f772ff7 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_3.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_3.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_4.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_4.png index 46c5ca0be3..6a065ee4cb 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_4.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_4.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_5.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_5.png index 5c502af9c6..a698486568 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_5.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_5.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/expansion_tile/basic.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/expansion_tile/basic.png index e7d4ae17db..4a19f7a2fe 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/expansion_tile/basic.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/expansion_tile/basic.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks.gif b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks.gif index 36127a5cd6..1a8794494a 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks.gif and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks.gif differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks1.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks1.png index cc8dece8c7..2a15539714 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks1.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks1.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks2.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks2.png index c52021676a..9c87225b9f 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks2.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks2.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks3.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks3.png index 5585d2a34d..b4777602a5 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks3.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/outlined_button/handling_clicks3.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic.gif b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic.gif index 1e1a481757..8786476703 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic.gif and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic.gif differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic1.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic1.png index 20bdfd9be9..bab6e8165f 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic1.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic1.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic2.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic2.png index 755d805b87..c495e7e57d 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic2.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic2.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic3.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic3.png index e34a553bce..f4dc2d7e96 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic3.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/basic3.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events.gif b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events.gif index 0f4f516575..f796a1fa2f 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events.gif and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events.gif differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events1.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events1.png index a8ee8c021a..5b41c13662 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events1.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events1.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events2.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events2.png index b60e37f2d8..a7aeeb5887 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events2.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events2.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events3.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events3.png index 853e937fe6..52271a1a64 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events3.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/handling_events3.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/image_for_docs.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/image_for_docs.png index b83f588c2e..a0a2fcb0f7 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/image_for_docs.png and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/range_slider/image_for_docs.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/snack_bar/snack_bar_flow.gif b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/snack_bar/snack_bar_flow.gif index 474369f01d..5f0ad99cf6 100644 Binary files a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/snack_bar/snack_bar_flow.gif and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/snack_bar/snack_bar_flow.gif differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_alert_dialog.py b/sdk/python/packages/flet/integration_tests/examples/material/test_alert_dialog.py index 2d939c74b6..671af43c94 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_alert_dialog.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_alert_dialog.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.alert_dialog.modal_and_non_modal.main as modal_and_non_modal import flet as ft import flet.testing as ftt -from examples.controls.alert_dialog import modal_and_non_modal @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_badge.py b/sdk/python/packages/flet/integration_tests/examples/material/test_badge.py index 67a8ee52b7..76749ebfaa 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_badge.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_badge.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.badge.basic.main as basic import flet as ft import flet.testing as ftt -from examples.controls.badge import basic @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_app_bar.py b/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_app_bar.py index 32e55ea9ad..fff254d679 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_app_bar.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_app_bar.py @@ -1,8 +1,9 @@ import pytest +import examples.controls.bottom_app_bar.border_radius.main as border_radius +import examples.controls.bottom_app_bar.notched_fab.main as notched_fab import flet as ft import flet.testing as ftt -from examples.controls.bottom_app_bar import border_radius, notched_fab @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_sheet.py b/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_sheet.py index b2dd3fc901..b73be06496 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_sheet.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_sheet.py @@ -1,8 +1,9 @@ import pytest +import examples.controls.bottom_sheet.basic.main as basic +import examples.controls.bottom_sheet.fullscreen.main as fullscreen import flet as ft import flet.testing as ftt -from examples.controls.bottom_sheet import basic, fullscreen @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_button.py b/sdk/python/packages/flet/integration_tests/examples/material/test_button.py index 5f83cf38c6..9b467211cb 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_button.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_button.py @@ -1,16 +1,14 @@ import pytest +import examples.controls.button.animate_on_hover.main as animate_on_hover +import examples.controls.button.basic.main as basic +import examples.controls.button.button_shapes.main as button_shapes +import examples.controls.button.custom_content.main as custom_content +import examples.controls.button.handling_clicks.main as handling_clicks +import examples.controls.button.icons.main as icons +import examples.controls.button.styling.main as styling import flet as ft import flet.testing as ftt -from examples.controls.button import ( - animate_on_hover, - basic, - button_shapes, - custom_content, - handling_clicks, - icons, - styling, -) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_card.py b/sdk/python/packages/flet/integration_tests/examples/material/test_card.py index 908fef0629..7f5e64acf1 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_card.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_card.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.card.music_info.main as music_info import flet as ft import flet.testing as ftt -from examples.controls.card import music_info @pytest.mark.asyncio(loop_scope="function") @@ -34,7 +34,6 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.asyncio(loop_scope="function") async def test_music_info(flet_app_function: ftt.FletTestApp): flet_app_function.assert_screenshot( - test_music_info.__name__, + "music_info", await flet_app_function.take_page_controls_screenshot(), - similarity_threshold=98.4, ) diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_checkbox.py b/sdk/python/packages/flet/integration_tests/examples/material/test_checkbox.py index 627de3da6c..e0ccab0563 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_checkbox.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_checkbox.py @@ -1,8 +1,10 @@ import pytest +import examples.controls.checkbox.basic.main as basic +import examples.controls.checkbox.handling_events.main as handling_events +import examples.controls.checkbox.styled.main as styled import flet as ft import flet.testing as ftt -from examples.controls.checkbox import basic, handling_events, styled @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_context_menu.py b/sdk/python/packages/flet/integration_tests/examples/material/test_context_menu.py index 89f32ab44c..77be1e16b7 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_context_menu.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_context_menu.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.context_menu.programmatic_open.main as programmatic_open import flet as ft import flet.testing as ftt -from examples.controls.context_menu import programmatic_open @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_datatable.py b/sdk/python/packages/flet/integration_tests/examples/material/test_datatable.py index f3d650b31c..3f5e64eaa8 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_datatable.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_datatable.py @@ -2,7 +2,10 @@ import flet as ft import flet.testing as ftt -from examples.controls.data_table import basic, sortable_and_selectable +from examples.controls.data_table.basic import main as basic +from examples.controls.data_table.sortable_and_selectable import ( + main as sortable_and_selectable, +) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_expansion_tile.py b/sdk/python/packages/flet/integration_tests/examples/material/test_expansion_tile.py index 85dd1972fc..29c0200460 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_expansion_tile.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_expansion_tile.py @@ -2,7 +2,7 @@ import flet as ft import flet.testing as ftt -from examples.controls.expansion_tile import basic +from examples.controls.expansion_tile.basic import main as basic @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_menu_bar.py b/sdk/python/packages/flet/integration_tests/examples/material/test_menu_bar.py index 6238f8990c..1435f4fd96 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_menu_bar.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_menu_bar.py @@ -2,7 +2,7 @@ import flet as ft import flet.testing as ftt -from examples.controls.menu_bar import nested_submenus +from examples.controls.menu_bar.nested_submenus import main as nested_submenus @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_menu_item_button.py b/sdk/python/packages/flet/integration_tests/examples/material/test_menu_item_button.py index 1f38cb5bfb..c3cf8cd0c2 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_menu_item_button.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_menu_item_button.py @@ -2,7 +2,7 @@ import flet as ft import flet.testing as ftt -from examples.controls.menu_item_button import basic +from examples.controls.menu_item_button.basic import main as basic @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_bar.py b/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_bar.py index 663b74bbdf..941244a2dc 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_bar.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_bar.py @@ -2,7 +2,7 @@ import flet as ft import flet.testing as ftt -from examples.controls.navigation_bar import basic +from examples.controls.navigation_bar.basic.main import main as basic @pytest.mark.asyncio(loop_scope="function") @@ -32,7 +32,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": basic.main}], + [{"flet_app_main": basic}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_drawer.py b/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_drawer.py index 4490cf6add..3a53e85cc7 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_drawer.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_drawer.py @@ -2,7 +2,10 @@ import flet as ft import flet.testing as ftt -from examples.controls.navigation_drawer import position_end, position_start +from examples.controls.navigation_drawer.position_end.main import main as position_end +from examples.controls.navigation_drawer.position_start.main import ( + main as position_start, +) @pytest.mark.asyncio(loop_scope="function") @@ -38,7 +41,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": position_end.main}], + [{"flet_app_main": position_end}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -86,7 +89,7 @@ async def test_position_end(flet_app_function: ftt.FletTestApp): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": position_start.main}], + [{"flet_app_main": position_start}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_rail.py b/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_rail.py index ac6e6e685d..5cc871fb4a 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_rail.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_navigation_rail.py @@ -2,7 +2,7 @@ import flet as ft import flet.testing as ftt -from examples.controls.navigation_rail import basic +from examples.controls.navigation_rail.basic.main import main as basic @pytest.mark.asyncio(loop_scope="function") @@ -34,7 +34,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": basic.main}], + [{"flet_app_main": basic}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_outlined_button.py b/sdk/python/packages/flet/integration_tests/examples/material/test_outlined_button.py index aeea041f94..3784d2166f 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_outlined_button.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_outlined_button.py @@ -2,12 +2,12 @@ import flet as ft import flet.testing as ftt -from examples.controls.outlined_button import ( - basic, - custom_content, - handling_clicks, - icons, +from examples.controls.outlined_button.basic.main import main as basic +from examples.controls.outlined_button.custom_content.main import main as custom_content +from examples.controls.outlined_button.handling_clicks.main import ( + main as handling_clicks, ) +from examples.controls.outlined_button.icons.main import main as icons @pytest.mark.asyncio(loop_scope="function") @@ -21,7 +21,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": basic.main}], + [{"flet_app_main": basic}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -35,7 +35,7 @@ async def test_basic(flet_app_function: ftt.FletTestApp): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": custom_content.main}], + [{"flet_app_main": custom_content}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -49,7 +49,7 @@ async def test_custom_content(flet_app_function: ftt.FletTestApp): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": icons.main}], + [{"flet_app_main": icons}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -63,7 +63,7 @@ async def test_icons(flet_app_function: ftt.FletTestApp): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": handling_clicks.main}], + [{"flet_app_main": handling_clicks}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_popup_menu_button.py b/sdk/python/packages/flet/integration_tests/examples/material/test_popup_menu_button.py index 6579fb1028..c7867cfa31 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_popup_menu_button.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_popup_menu_button.py @@ -2,7 +2,7 @@ import flet as ft import flet.testing as ftt -from examples.controls.popup_menu_button import basic +from examples.controls.popup_menu_button.basic.main import main as basic @pytest.mark.asyncio(loop_scope="function") @@ -39,7 +39,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.skip(reason="Test runs asynchronously") @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": basic.main}], + [{"flet_app_main": basic}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_progress_bar.py b/sdk/python/packages/flet/integration_tests/examples/material/test_progress_bar.py index f02167ceab..0577a52421 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_progress_bar.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_progress_bar.py @@ -2,7 +2,9 @@ import flet as ft import flet.testing as ftt -from examples.controls.progress_ring import determinate_and_indeterminate +from examples.controls.progress_bar.determinate_and_indeterminate.main import ( + main as determinate_and_indeterminate, +) @pytest.mark.asyncio(loop_scope="function") @@ -17,7 +19,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.skip(reason="Test runs asynchronously") @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": determinate_and_indeterminate.main}], + [{"flet_app_main": determinate_and_indeterminate}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_progress_ring.py b/sdk/python/packages/flet/integration_tests/examples/material/test_progress_ring.py index 4a32cbd5dc..13f5de22fa 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_progress_ring.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_progress_ring.py @@ -2,9 +2,11 @@ import flet as ft import flet.testing as ftt -from examples.controls.progress_ring import ( - determinate_and_indeterminate, - gauge_with_progress, +from examples.controls.progress_ring.determinate_and_indeterminate.main import ( + main as determinate_and_indeterminate, +) +from examples.controls.progress_ring.gauge_with_progress.main import ( + main as gauge_with_progress, ) @@ -19,7 +21,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": gauge_with_progress.main}], + [{"flet_app_main": gauge_with_progress}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -34,7 +36,7 @@ async def test_gauge_with_progress(flet_app_function: ftt.FletTestApp): @pytest.mark.skip(reason="Test runs asynchronously") @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": determinate_and_indeterminate.main}], + [{"flet_app_main": determinate_and_indeterminate}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_radio.py b/sdk/python/packages/flet/integration_tests/examples/material/test_radio.py index 25c5c85b53..d17b6f9ccd 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_radio.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_radio.py @@ -2,7 +2,10 @@ import flet as ft import flet.testing as ftt -from examples.controls.radio import basic, handling_selection_changes +from examples.controls.radio.basic.main import main as basic +from examples.controls.radio.handling_selection_changes.main import ( + main as handling_selection_changes, +) @pytest.mark.asyncio(loop_scope="function") @@ -23,7 +26,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": basic.main}], + [{"flet_app_main": basic}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -45,7 +48,7 @@ async def test_basic(flet_app_function: ftt.FletTestApp): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": handling_selection_changes.main}], + [{"flet_app_main": handling_selection_changes}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_range_slider.py b/sdk/python/packages/flet/integration_tests/examples/material/test_range_slider.py index 6055def4db..f6f8fedbba 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_range_slider.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_range_slider.py @@ -2,7 +2,10 @@ import flet as ft import flet.testing as ftt -from examples.controls.range_slider import basic, handling_change_events +from examples.controls.range_slider.basic.main import main as basic +from examples.controls.range_slider.handling_change_events.main import ( + main as handling_change_events, +) @pytest.mark.asyncio(loop_scope="function") @@ -22,7 +25,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": basic.main}], + [{"flet_app_main": basic}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") @@ -63,7 +66,7 @@ async def test_basic(flet_app_function: ftt.FletTestApp): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": handling_change_events.main}], + [{"flet_app_main": handling_change_events}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_search_bar.py b/sdk/python/packages/flet/integration_tests/examples/material/test_search_bar.py index f877ec2be9..83a5950d5f 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_search_bar.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_search_bar.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.search_bar.basic.main as basic import flet as ft import flet.testing as ftt -from examples.controls.search_bar import basic @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_segmented_button.py b/sdk/python/packages/flet/integration_tests/examples/material/test_segmented_button.py index 032a8f57d0..0d347158b8 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_segmented_button.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_segmented_button.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.segmented_button.single_multiple_selection.main as sms import flet as ft import flet.testing as ftt -from examples.controls.segmented_button import single_multiple_selection @pytest.mark.asyncio(loop_scope="function") @@ -41,7 +41,7 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): @pytest.mark.parametrize( "flet_app_function", - [{"flet_app_main": single_multiple_selection.main}], + [{"flet_app_main": sms.main}], indirect=True, ) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_selection_area.py b/sdk/python/packages/flet/integration_tests/examples/material/test_selection_area.py index 2dff5e3773..356039f883 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_selection_area.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_selection_area.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.selection_area.basic.main as basic import flet as ft import flet.testing as ftt -from examples.controls.selection_area import basic @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_slider.py b/sdk/python/packages/flet/integration_tests/examples/material/test_slider.py index cc1643547d..3a590b68d0 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_slider.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_slider.py @@ -1,12 +1,10 @@ import pytest +import examples.controls.slider.basic.main as basic +import examples.controls.slider.custom_label.main as custom_label +import examples.controls.slider.handling_events.main as handling_events import flet as ft import flet.testing as ftt -from examples.controls.slider import ( - basic, - custom_label, - handling_events, -) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_snack_bar.py b/sdk/python/packages/flet/integration_tests/examples/material/test_snack_bar.py index bcc0805bdf..7fa88f8bcf 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_snack_bar.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_snack_bar.py @@ -1,8 +1,10 @@ import pytest +import examples.controls.snack_bar.action.main as action +import examples.controls.snack_bar.basic.main as basic +import examples.controls.snack_bar.counter.main as counter import flet as ft import flet.testing as ftt -from examples.controls.snack_bar import action, basic, counter @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_submenu_button.py b/sdk/python/packages/flet/integration_tests/examples/material/test_submenu_button.py index 28c221b966..34f29a3416 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_submenu_button.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_submenu_button.py @@ -1,8 +1,8 @@ import pytest +import examples.controls.submenu_button.basic.main as basic import flet as ft import flet.testing as ftt -from examples.controls.submenu_button import basic @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_switch.py b/sdk/python/packages/flet/integration_tests/examples/material/test_switch.py index 2e5fdc3478..f156ff478d 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_switch.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_switch.py @@ -1,10 +1,8 @@ import pytest +import examples.controls.switch.basic.main as basic import flet as ft import flet.testing as ftt -from examples.controls.switch import ( - basic, -) @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_text_button.py b/sdk/python/packages/flet/integration_tests/examples/material/test_text_button.py index b4afbff3d0..1910f31912 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_text_button.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_text_button.py @@ -1,8 +1,11 @@ import pytest +import examples.controls.text_button.basic.main as basic +import examples.controls.text_button.custom_content.main as custom_content +import examples.controls.text_button.handling_clicks.main as handling_clicks +import examples.controls.text_button.icons.main as icons import flet as ft import flet.testing as ftt -from examples.controls.text_button import basic, custom_content, handling_clicks, icons @pytest.mark.asyncio(loop_scope="function") diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_time_picker.py b/sdk/python/packages/flet/integration_tests/examples/material/test_time_picker.py index a548275958..71309ec364 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_time_picker.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_time_picker.py @@ -2,9 +2,10 @@ import pytest +import examples.controls.time_picker.basic.main as basic +import examples.controls.time_picker.hour_formats.main as hour_formats import flet as ft import flet.testing as ftt -from examples.controls.time_picker import basic, hour_formats # Note: CI macOS runner uses a 12-hour (AM / PM) time format by default. diff --git a/sdk/python/packages/flet/mkdocs.yml b/sdk/python/packages/flet/mkdocs.yml index 3d403c4f3b..0b5545119a 100644 --- a/sdk/python/packages/flet/mkdocs.yml +++ b/sdk/python/packages/flet/mkdocs.yml @@ -327,7 +327,9 @@ nav: - controls/cupertinoactionsheet/index.md - CupertinoActionSheetAction: controls/cupertinoactionsheetaction.md - CupertinoActivityIndicator: controls/cupertinoactivityindicator.md - - CupertinoAlertDialog: controls/cupertinoalertdialog.md + - CupertinoAlertDialog: + - controls/cupertinoalertdialog/index.md + - CupertinoDialogAction: controls/cupertinodialogaction.md - CupertinoAppBar: controls/cupertinoappbar.md - CupertinoBottomSheet: controls/cupertinobottomsheet.md - CupertinoButton: controls/cupertinobutton.md @@ -336,7 +338,6 @@ nav: - controls/cupertinocontextmenu/index.md - CupertinoContextMenuAction: controls/cupertinocontextmenuaction.md - CupertinoDatePicker: controls/cupertinodatepicker.md - - CupertinoDialogAction: controls/cupertinodialogaction.md - CupertinoFilledButton: controls/cupertinofilledbutton.md - CupertinoListTile: controls/cupertinolisttile.md - CupertinoNavigationBar: controls/cupertinonavigationbar.md