{"id":32633032,"url":"https://github.com/brunobrown/flet-health","last_synced_at":"2025-10-30T23:49:18.913Z","repository":{"id":293910092,"uuid":"967876363","full_name":"brunobrown/flet-health","owner":"brunobrown","description":null,"archived":false,"fork":false,"pushed_at":"2025-05-22T09:07:23.000Z","size":153,"stargazers_count":10,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-15T10:00:14.280Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brunobrown.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-17T06:23:49.000Z","updated_at":"2025-07-19T13:01:19.000Z","dependencies_parsed_at":"2025-05-17T22:29:31.917Z","dependency_job_id":null,"html_url":"https://github.com/brunobrown/flet-health","commit_stats":null,"previous_names":["brunobrown/flet-health"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/brunobrown/flet-health","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-health","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-health/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-health/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-health/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brunobrown","download_url":"https://codeload.github.com/brunobrown/flet-health/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-health/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281902524,"owners_count":26581164,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-30T02:00:06.501Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-30T23:49:17.498Z","updated_at":"2025-10-30T23:49:18.902Z","avatar_url":"https://github.com/brunobrown.png","language":"Python","funding_links":["https://www.buymeacoffee.com/brunobrown"],"categories":["Extensions"],"sub_categories":["Community Extensions"],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://github.com/user-attachments/assets/e82a9555-8b02-47f0-8f0b-499423383c1f\" width=\"50%\" alt=\"Flet Health\"\u003e\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003e Flet Health \u003c/h1\u003e\n\n## 📖 Overview\n\n`flet-health` is an extension of the Flutter `health` package for Python/Flet. It allows integration with health data on both **Google Health Connect (Android)** and **Apple HealthKit (iOS)**.\n\n\u003e ⚠️ **Note**:\n\u003e \n\u003e This package is a Python/Flet wrapper for the Flutter [`health`](https://pub.dev/packages/health) plugin.  \n\u003e All credits for the original plugin go to its maintainers.  \n\u003e This wrapper was created to allow Python developers to access the same Health Connect/Apple HealthKit features using Flet.\n\n---\n\n## ✨ Features\n\n* handling permissions to access health data using the `has_permissions`, `request_authorization`, `revoke_permissions` methods.\n* reading health data using the `get_health_data_from_types` method.\n* writing health data using the `write_health_data` method.\n* writing workouts using the `write_workout` method.\n* writing meals on iOS (Apple Health) \u0026 Android using the `write_meal` method.\n* writing audiograms on iOS using the `write_audiogram` method.\n* writing blood pressure data using the `write_blood_pressure` method.\n* accessing total step counts using the `get_total_steps_in_interval` method.\n* cleaning up duplicate data points via the `remove_duplicates` method.\n* removing data of a given type in a selected period of time using the `delete` method.\n* removing data by UUID using the `delete_by_uuid` method.\n\n\u003e ⚠ Note that for Android, the target phone needs to have the [`Health Connect`](https://play.google.com/store/apps/details?id=com.google.android.apps.healthdata\u0026hl=en) app installed.\n\n---\n\n## ☕ Buy me a coffee\n\nIf you liked this project, please consider supporting its development with a donation. Your contribution will help me maintain and improve it.\n\n\u003ca href=\"https://www.buymeacoffee.com/brunobrown\"\u003e\n\u003cimg src=\"https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-1.svg\" width=\"200\" alt=\"Buy Me a Coffee\"\u003e\n\u003c/a\u003e\n\n---\n\n## 📦 Installation\n\nInstall using your package manager of choice:\n\n**Pip**\n\n```bash\npip install flet-health\n```\n\n**Poetry**\n\n```bash\npoetry add flet-health\n```\n\n**UV**\n\n```bash\nuv add flet-health\n```\n\n---\n\n## ⚙️ Configuration\n\n### 1. Obtain the `flet-build-template` to customize the native files. In the root of your project, execute:\n\n```bash\ngit clone https://github.com/flet-dev/flet-build-template.git\ncd flet-build-template\ngit checkout 0.25.2 # or another version according to the flet version used in your project\n```\n\n---\n\n### 2. iOS (Apple Health)\n\nEdit `flet-build-template/{{cookiecutter.out_dir}}/ios/Runner/Info.plist`.\nAdd the following two entries to the ```Info.plist``` file:\n\n```xml\n\u003ckey\u003eNSHealthShareUsageDescription\u003c/key\u003e\n\u003cstring\u003eWe will sync your data with the Apple Health app to give you better insights\u003c/string\u003e\n\u003ckey\u003eNSHealthUpdateUsageDescription\u003c/key\u003e\n\u003cstring\u003eWe will sync your data with the Apple Health app to give you better insights\u003c/string\u003e\n```\n\n**Optional:**\n\nThen, open your Flutter project in Xcode by right clicking on the \"ios\" folder and selecting \"Open in Xcode\". \nNext, enable \"HealthKit\" by adding a capability inside the \"Signing \u0026 Capabilities\" tab of the Runner target's settings.\n\nBefore:\n![Info](https://github.com/user-attachments/assets/5256fc14-c36d-4d54-bf6c-fcd74f9e11e0)\n\nAfter:\n![Info](https://github.com/user-attachments/assets/cc392117-70bd-4934-9bad-f8038dfe493a)\n\n---\n\n### 3. Android (Health Connect)\n\nEdit the **AndroidManifest** file at `flet-build-template/{{cookiecutter.out_dir}}/android/app/src/main/AndroidManifest.xml`.\nHealth Connect requires the following lines in the `AndroidManifest.xml`.\n\nInclude:\n\n```xml\n    \u003c!-- Permission handling for Android 13- --\u003e\n    \u003cintent-filter\u003e\n        \u003caction android:name=\"androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE\"/\u003e\n    \u003c/intent-filter\u003e\n\n    \u003c!-- Permission handling for Android 14+ --\u003e\n    \u003cintent-filter\u003e\n        \u003caction android:name=\"android.intent.action.VIEW_PERMISSION_USAGE\"/\u003e\n        \u003ccategory android:name=\"android.intent.category.HEALTH_PERMISSIONS\"/\u003e\n    \u003c/intent-filter\u003e\n\n    \u003c!-- Check whether Health Connect is installed or not --\u003e\n    \u003cqueries\u003e\n        \u003cpackage android:name=\"com.google.android.apps.healthdata\"/\u003e\n        \u003c!-- Intention to show Permissions screen for Health Connect API --\u003e\n        \u003cintent\u003e\n            \u003caction android:name=\"androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE\"/\u003e\n        \u003c/intent\u003e\n    \u003c/queries\u003e\n```\n\nBefore:\n![AndroidManifest](https://github.com/user-attachments/assets/7828bb10-280b-4378-bdec-da0d9966dfb5)\n\nAfter:\n![AndroidManifest](https://github.com/user-attachments/assets/34ee1e73-ba29-4e90-95b3-f0690d753189)\n\n---\n\nModify the **MainActivity**  file at `{{cookiecutter.out_dir}}/android/app/src/main/kotlin/{{ cookiecutter.kotlin_dir }}/MainActivity.kt`.\nIn the `MainActivity.kt` file, update the MainActivity class to extend from FlutterFragmentActivity instead of the default FlutterActivity.\n\nChange:\n\n```kotlin\nimport io.flutter.embedding.android.FlutterFragmentActivity\n\nclass MainActivity: FlutterFragmentActivity()\n```\n\nBefore:\n![MainActivity](https://github.com/user-attachments/assets/18f5b375-2541-49ab-b6bd-1f325a1c758f)\n\n\nAfter:\n![MainActivity](https://github.com/user-attachments/assets/2d8edb85-d359-4669-800d-9c9d0ca78693)\n\n\n---\n\n### 4. Add the desired permissions and reference the `flet-build-template` directory in `pyproject.toml`\n\nFor each type of data you want to access, READ and WRITE permissions need to be added to the AndroidManifest.xml file \nvia the `[tool.flet.android.permission]` section in the `pyproject.toml` file.\nThe list of [permissions](https://developer.android.com/health-and-fitness/guides/health-connect/plan/data-types#permissions) \ncan be found here on the [data types](https://developer.android.com/health-and-fitness/guides/health-connect/plan/data-types) page.\n\n\u003e See the [official Flet documentation](https://flet.dev/blog/pyproject-toml-support-for-flet-build-command/#android-settings) \n\u003e for more information on how to configure the `pyproject.toml` file with flet build commands.\n\nExample:\n\n```toml\n[project]\nname = \"flet-health-example\"\nversion = \"0.1.0\"\ndescription = \"flet-health-example\"\nreadme = \"README.md\"\nrequires-python = \"\u003e=3.9\"\nauthors = [\n    { name = \"developer\", email = \"you@example.com\" }\n]\n\ndependencies = [\n    \"flet\u003e=0.25.2\",\n    \"flet-health\u003e=0.1.0\",\n    \"flet-permission-handler\u003e=0.1.0\",\n]\n\n[tool.uv]\ndev-dependencies = [\n    \"flet[all]\u003e=0.25.2\",\n]\n\n[tool.flet.android.permission] # --android-permissions\n\"android.permission.health.READ_STEPS\" = true\n\"android.permission.health.WRITE_STEPS\" = true\n\"android.permission.health.READ_HEART_RATE\" = true\n\"android.permission.health.WRITE_HEART_RATE\" = true\n\"android.permission.health.READ_HEIGHT\" = true\n\"android.permission.health.WRITE_HEIGHT\" = true\n\"android.permission.health.READ_WEIGHT\" = true\n\"android.permission.health.WRITE_WEIGHT\" = true\n\"android.permission.health.READ_ACTIVE_CALORIES_BURNED\" = true\n\"android.permission.health.WRITE_ACTIVE_CALORIES_BURNED\" = true\n\"android.permission.health.READ_DISTANCE\" = true\n\"android.permission.health.WRITE_DISTANCE\" = true\n\"android.permission.health.READ_BLOOD_GLUCOSE\" = true\n\"android.permission.health.WRITE_BLOOD_GLUCOSE\" = true\n\"android.permission.health.READ_OXYGEN_SATURATION\" = true\n\"android.permission.health.WRITE_OXYGEN_SATURATION\" = true\n\"android.permission.health.READ_BODY_TEMPERATURE\" = true\n\"android.permission.health.WRITE_BODY_TEMPERATURE\" = true\n\"android.permission.health.READ_BLOOD_PRESSURE\" = true\n\"android.permission.health.WRITE_BLOOD_PRESSURE\" = true\n\"android.permission.health.READ_SLEEP\" = true\n\"android.permission.health.WRITE_SLEEP\" = true\n\"android.permission.health.READ_EXERCISE\" = true\n\"android.permission.health.WRITE_EXERCISE\" = true\n\"android.permission.health.READ_NUTRITION\" = true\n\"android.permission.health.WRITE_NUTRITION\" = true\n\"android.permission.health.READ_HYDRATION\" = true\n\"android.permission.health.WRITE_HYDRATION\" = true\n\"android.permission.health.READ_MENSTRUATION\" = true\n\"android.permission.health.WRITE_MENSTRUATION\" = true\n\"android.permission.health.READ_HEART_RATE_VARIABILITY\" = true\n\"android.permission.health.WRITE_HEART_RATE_VARIABILITY\" = true\n\"android.permission.health.READ_LEAN_BODY_MASS\" = true\n\"android.permission.health.WRITE_LEAN_BODY_MASS\" = true\n\"android.permission.health.READ_TOTAL_CALORIES_BURNED\" = true\n\"android.permission.health.WRITE_TOTAL_CALORIES_BURNED\" = true\n\n# Additional permissions if required\n\"android.permission.BODY_SENSORS\" = true\n\"android.permission.ACCESS_BACKGROUND_LOCATION\" = true\n\"android.permission.ACCESS_COARSE_LOCATION\" = true\n\"android.permission.ACCESS_FINE_LOCATION\" = true\n\"android.permission.ACTIVITY_RECOGNITION\" = true\n\n\n# Reference the `flet-build-template` directory.\n[tool.flet.template]\n#path = \"gh:some-github/repo\" # --template\n#ref = \"\" # --template-ref\ndir = \"/absolute/path/to/yourProject/flet-build-template\" # --template-dir\n```\n\n---\n\nBy default, Health Connect restricts read data to 30 days from when permission has been granted.\nYou can check and request access to historical data using the `is_health_data_history_authorized` and `request_health_data_history_authorization` methods, respectively.\nThe above methods require the following permission to be declared:\n\n```toml\n[tool.flet.android.permission] # --android-permissions\n\"android.permission.health.READ_HEALTH_DATA_HISTORY\" = true\n```\n\n---\n\nAccess to fitness data (e.g. Steps) requires permission to access the “Activity Recognition” API.\nTo configure this, add the following line to your `AndroidManifest.xml` file using the `[tool.flet.android.permission]` section:\n\n```toml\n[tool.flet.android.permission] # --android-permissions\n\"android.permission.ACTIVITY_RECOGNITION\" = true\n```\n\nAdditionally, for workouts, if the distance of a workout is requested then the location permissions below are needed.\n\n```toml\n[tool.flet.android.permission] # --android-permissions\n\"android.permission.ACCESS_COARSE_LOCATION\" = true\n\"android.permission.ACCESS_FINE_LOCATION\" = true\n```\n\nBecause this is labeled as a `dangerous` protection level, the permission system will not grant it automatically and it \nrequires the user's action. You can prompt the user for it using the [flet-pemission-handler](https://flet.dev/docs/controls/permissionhandler) plugin. Follow the plugin setup \ninstructions.\n\nInstall using your package manager of choice:\n\n**Pip**\n\n```bash\npip install flet-permission-handler\n```\n\n**Poetry**\n\n```bash\npoetry add flet-permission-handler\n```\n\n**UV**\n\n```bash\nuv add flet-permission-handler\n```\n\n---\n\nExample:\n\n```python\nimport os\nimport re\nimport logging\nimport datetime\nimport contextlib\nimport flet as ft\nimport flet_health as fh\nimport flet_permission_handler as fph\nfrom enum import Enum\n\n\nlog_file_path = os.getenv(\"FLET_APP_CONSOLE\") or \"debug.log\"\nlogging.basicConfig(\n    level=logging.INFO,\n    format=\"[{asctime}] [{levelname}] [{filename}:{funcName}:{lineno}] - {message}\",\n    style=\"{\",\n    handlers=[\n        logging.FileHandler(log_file_path, mode=\"a\", encoding=\"utf-8\")\n    ]\n)\nlogger = logging.getLogger(__name__)\n\nhealth = fh.Health()\nph = fph.PermissionHandler()\n\n\ndef parse_log_block(lines: list[str]) -\u003e list[dict]:\n    logs = []\n    buffer = []\n\n    for line in lines:\n        if re.match(r\"\\[\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d+] \\[.*?]\", line):\n            if buffer:\n                logs.append(\"\".join(buffer))\n                buffer.clear()\n        buffer.append(line)\n    if buffer:\n        logs.append(\"\".join(buffer))\n\n    parsed_logs = []\n    for entry in logs:\n        match = re.search(r\"\\[(.*?)] \\[(.*?)] \\[(.*?):(.*?):(\\d+)] - (.+)\", entry, re.DOTALL)\n        if match:\n            timestamp, level, file, func, line_no, message = match.groups()\n            parsed_logs.append({\n                \"timestamp\": timestamp,\n                \"level\": level,\n                \"file\": file,\n                \"func\": func,\n                \"line\": line_no,\n                \"message\": message.strip()\n            })\n    return parsed_logs\n\n\ndef color_for_level(level: str):\n    return {\n        \"INFO\": ft.Colors.BLUE_100,\n        \"WARNING\": ft.Colors.AMBER_100,\n        \"ERROR\": ft.Colors.RED_100,\n        \"DEBUG\": ft.Colors.GREY_100\n    }.get(level, ft.Colors.GREY_200)\n\n\ndef show_console_log(e: ft.ControlEvent) -\u003e None:\n    if not log_file_path or not os.path.exists(log_file_path):\n        open_snack_bar(e.page, \"No logs found\")\n        return\n\n    with open(log_file_path, \"r\", encoding=\"utf-8\") as f:\n        lines = f.readlines()\n\n    parsed_logs = parse_log_block(lines)\n\n    log_controls = []\n    for log in parsed_logs:\n        # Formatar data\n        formatted_timestamp = log[\"timestamp\"]\n        with contextlib.suppress(Exception):\n            dt_obj = datetime.datetime.strptime(log[\"timestamp\"], \"%Y-%m-%d %H:%M:%S,%f\")\n            formatted_timestamp = dt_obj.strftime(\"%d/%m/%Y | %H:%M:%S\")\n\n        log_controls.append(\n            ft.Container(\n                bgcolor=color_for_level(log[\"level\"]),\n                border_radius=10,\n                padding=10,\n                margin=5,\n                content=ft.Column(\n                    horizontal_alignment=ft.CrossAxisAlignment.START,\n                    controls=[\n                        ft.Text(f\"📄 {log['file']} | line: {log['line']} | func: {log['func']}\",\n                                weight=ft.FontWeight.BOLD, size=10, selectable=True),\n                        ft.Text(f\"⏱️ {formatted_timestamp}\", size=10, italic=True, selectable=True),\n                        ft.Text(f\"📌 messgage:\", size=10, italic=True),\n                        ft.Text(\n                            f\"{log['message']}\",\n                            size=10,\n                            selectable=True,\n                            max_lines=15 if len(log['message']) \u003e 100 else None,\n                            overflow=ft.TextOverflow.VISIBLE\n                        ),\n                    ]\n                )\n            )\n        )\n\n    dialog = ft.AlertDialog(\n        title=ft.Text(\"Debug Logs\"),\n        content=ft.Container(\n            height=500,\n            width=380,\n            content=ft.ListView(controls=log_controls, auto_scroll=True)\n        ),\n        scrollable=True\n    )\n\n    e.page.open(dialog)\n    e.page.debug_icon.clear_notify(e.page)\n\n\nclass SnackBarType(Enum):\n    INFO = \"info\"\n    SUCCESS = \"success\"\n    WARNING = \"warning\"\n    ERROR = \"error\"\n    NONE = \"none\"\n\n\ndef open_snack_bar(page: ft.Page, msg, snack_type: SnackBarType = SnackBarType.NONE, duration=3000):\n    \"\"\"Opens a SnackBar with a message and type-based styling.\"\"\"\n\n    snackbar = getattr(page, 'snackbar')\n\n    match snack_type.value:\n        case 'info':\n            bgcolor = ft.Colors.BLUE_100\n            text_colors = ft.Colors.BLUE_900\n        case 'success':\n            bgcolor = ft.Colors.GREEN_ACCENT_100\n            text_colors = ft.Colors.GREEN_900\n        case 'warning':\n            bgcolor = ft.Colors.AMBER_100\n            text_colors = ft.Colors.AMBER_900\n        case 'error':\n            bgcolor = ft.Colors.RED_ACCENT_100\n            text_colors = ft.Colors.RED_900\n        case 'none':\n            bgcolor = ft.Colors.BLACK87\n            text_colors = ft.Colors.WHITE\n        case _:\n            bgcolor = ft.Colors.BLACK87\n            text_colors = ft.Colors.WHITE\n\n    # Atualiza o conteúdo e estilo\n    snackbar.content = ft.Text(msg, color=text_colors)\n    snackbar.bgcolor = bgcolor\n    snackbar.duration = duration\n\n    # Exibe o snackbar\n    page.open(snackbar)\n\n\nclass DebugNotificationIcon(ft.Stack):\n    def __init__(self, page, func):\n        super().__init__()\n        self.page = page\n        self.func = func\n        self.number_errors = 0\n        self.debug_button = ft.IconButton(\n            icon=ft.Icons.BUG_REPORT,\n            icon_color=ft.Colors.WHITE,\n            tooltip=\"Show logs\",\n            padding=ft.padding.only(right=20),\n            on_click=self.func,\n        )\n\n        self.text_notifi = ft.Text(\n            color=ft.Colors.WHITE,\n            size=10,\n            weight=ft.FontWeight.BOLD,\n            text_align=ft.TextAlign.CENTER,\n        )\n\n        self.container = ft.Container(\n            margin=ft.margin.only(top=20),\n            bgcolor=ft.Colors.RED,\n            border_radius=10,\n            width=20,\n            height=20,\n            alignment=ft.alignment.center,\n            offset=ft.Offset(0.5, -0.5),\n            # on_click=self.func,\n        )\n\n    def add_notify_error(self, page: ft.Page):\n        self.number_errors += 1\n        self.text_notifi.value = (\n            str(self.number_errors) if self.number_errors \u003c 11 else '. . .'\n        )\n        self.container.content = self.text_notifi\n        self.container.bgcolor = ft.Colors.RED\n        self.controls.append(self.container)\n        page.update()\n\n    def clear_notify(self, page: ft.Page):\n        self.number_errors = 0\n        self.container.content = None\n        self.container.bgcolor = None\n        page.update()\n\n    def build(self):\n        self.controls = [self.debug_button]\n        if self.number_errors \u003e 0:\n            self.controls.append(self.container)\n\n\nasync def add_data(e):\n    end_time = datetime.datetime.now()\n    start_time = end_time - datetime.timedelta(hours=1)\n\n    success = True\n\n    def combine(success, result):\n        return success and result\n\n    # Basic data examples\n    result = await health.write_health_data_async(\n        value=1.925,\n        types=fh.HealthDataTypeAndroid.HEIGHT,\n        start_time=start_time,\n        end_time=end_time,\n        recording_method=fh.RecordingMethod.MANUAL\n    )\n    success = combine(success, result)\n\n    result = await health.write_health_data_async(\n        value=90,\n        types=fh.HealthDataTypeAndroid.WEIGHT,\n        start_time=start_time,\n        end_time=end_time,\n        recording_method=fh.RecordingMethod.MANUAL\n    )\n    success = combine(success, result)\n\n    result = await health.write_health_data_async(\n        value=90,\n        types=fh.HealthDataTypeAndroid.HEART_RATE,\n        start_time=start_time,\n        end_time=end_time,\n        recording_method=fh.RecordingMethod.MANUAL\n    )\n    success = combine(success, result)\n\n    result = await health.write_health_data_async(\n        value=90,\n        types=fh.HealthDataTypeAndroid.STEPS,\n        start_time=start_time,\n        end_time=end_time,\n        recording_method=fh.RecordingMethod.MANUAL\n    )\n    success = combine(success, result)\n\n    result = await health.write_health_data_async(\n        value=200,\n        types=fh.HealthDataTypeAndroid.ACTIVE_ENERGY_BURNED,\n        start_time=start_time,\n        end_time=end_time\n    )\n    success = combine(success, result)\n\n    result = await health.write_health_data_async(\n        value=70,\n        types=fh.HealthDataTypeAndroid.HEART_RATE,\n        start_time=start_time,\n        end_time=end_time\n    )\n    success = combine(success, result)\n\n    if e.page.platform == ft.PagePlatform.IOS:  # iOS\n        result = await health.write_health_data_async(\n            value=30,\n            types=fh.HealthDataTypeIOS.HEART_RATE_VARIABILITY_SDNN,\n            start_time=start_time,\n            end_time=end_time\n        )\n        success = combine(success, result)\n    else:\n        result = await health.write_health_data_async(\n            value=30,\n            types=fh.HealthDataTypeAndroid.HEART_RATE_VARIABILITY_RMSSD,\n            start_time=start_time,\n            end_time=end_time\n        )\n        success = combine(success, result)\n\n    result = await health.write_health_data_async(\n        value=37,\n        types=fh.HealthDataTypeAndroid.BODY_TEMPERATURE,\n        start_time=start_time,\n        end_time=end_time\n    )\n    success = combine(success, result)\n\n    result = await health.write_health_data_async(\n        value=105,\n        types=fh.HealthDataTypeAndroid.BLOOD_GLUCOSE,\n        start_time=start_time,\n        end_time=end_time\n    )\n    success = combine(success, result)\n\n    result = await health.write_health_data_async(\n        value=1.8,\n        types=fh.HealthDataTypeAndroid.WATER,\n        start_time=start_time,\n        end_time=end_time\n    )\n    success = combine(success, result)\n\n    # Types of sleep\n    for sleep_type in [\n        fh.HealthDataTypeAndroid.SLEEP_REM,\n        fh.HealthDataTypeAndroid.SLEEP_ASLEEP,\n        fh.HealthDataTypeAndroid.SLEEP_AWAKE,\n        fh.HealthDataTypeAndroid.SLEEP_DEEP\n    ]:\n        result = await health.write_health_data_async(\n            value=0.0,\n            types=sleep_type,\n            start_time=start_time,\n            end_time=end_time\n        )\n\n        success = combine(success, result)\n\n    result = await health.write_health_data_async(\n        value=22,\n        types=fh.HealthDataTypeAndroid.LEAN_BODY_MASS,\n        start_time=start_time,\n        end_time=end_time\n    )\n    success = combine(success, result)\n\n    # Specialized\n    result = await health.write_blood_oxygen_async(\n        saturation=98,\n        start_time=start_time,\n        end_time=end_time\n    )\n    success = combine(success, result)\n\n    result = await health.write_workout_data_async(\n        activity_type=fh.HealthWorkoutActivityType.RUNNING,\n        title=\"RUNNING - Workout Test\",\n        start_time=end_time - datetime.timedelta(minutes=30),\n        end_time=end_time,\n        total_distance=500,\n        total_energy_burned=100,\n        recording_method=fh.RecordingMethod.ACTIVE\n    )\n    success = combine(success, result)\n\n    result = await health.write_blood_pressure_async(\n        systolic=90,\n        diastolic=80,\n        start_time=start_time\n    )\n    success = combine(success, result)\n\n    result = await health.write_meal_async(\n        meal_type=fh.MealType.SNACK,\n        start_time=start_time,\n        end_time=end_time,\n        calories_consumed=1000,\n        carbohydrates=50,\n        protein=25,\n        fat_total=50,\n        name=\"Banana\",\n        caffeine=0.002,\n        vitamin_a=0.001,\n        vitamin_c=0.002,\n        vitamin_d=0.003,\n        vitamin_e=0.004,\n        vitamin_k=0.005,\n        b1_thiamin=0.006,\n        b2_riboflavin=0.007,\n        b3_niacin=0.008,\n        b5_pantothenic_acid=0.009,\n        b6_pyridoxine=0.010,\n        b7_biotin=0.011,\n        b9_folate=0.012,\n        b12_cobalamin=0.013,\n        calcium=0.015,\n        copper=0.016,\n        iodine=0.017,\n        iron=0.018,\n        magnesium=0.019,\n        manganese=0.020,\n        phosphorus=0.021,\n        potassium=0.022,\n        selenium=0.023,\n        sodium=0.024,\n        zinc=0.025,\n        water=0.026,\n        molybdenum=0.027,\n        chloride=0.028,\n        chromium=0.029,\n        cholesterol=0.030,\n        fiber=0.031,\n        fat_monounsaturated=0.032,\n        fat_polyunsaturated=0.033,\n        fat_unsaturated=0.065,\n        fat_trans_monoenoic=0.65,\n        fat_saturated=0.066,\n        sugar=0.067,\n        recording_method=fh.RecordingMethod.MANUAL\n    )\n    success = combine(success, result)\n\n    result = await health.write_menstruation_flow_async(\n        flow=fh.MenstrualFlow.MEDIUM,\n        is_start_of_cycle=True,\n        start_time=start_time,\n        end_time=end_time\n    )\n    success = combine(success, result)\n\n    # iOS 16+ only\n    if e.page.platform == ft.PagePlatform.IOS:\n        result = await health.write_health_data_async(\n            value=22,\n            types=fh.HealthDataTypeIOS.WATER_TEMPERATURE,\n            start_time=start_time,\n            end_time=end_time,\n            recording_method=fh.RecordingMethod.MANUAL\n        )\n        success = combine(success, result)\n\n        result = await health.write_health_data_async(\n            value=55,\n            types=fh.HealthDataTypeIOS.UNDERWATER_DEPTH,\n            start_time=start_time,\n            end_time=end_time,\n            recording_method=fh.RecordingMethod.MANUAL\n        )\n        success = combine(success, result)\n\n    return success\n\n\ndef main(page: ft.Page):\n    page.title = \"Flet Health\"\n    page.locale_configuration = ft.LocaleConfiguration(\n        supported_locales=[ft.Locale(\"pt\", \"BR\")],\n        current_locale=ft.Locale(\"pt\", \"BR\"),\n    )\n    debug_icon = DebugNotificationIcon(page, show_console_log)\n    setattr(page, \"debug_icon\", debug_icon)\n    snackbar = ft.SnackBar(\n        content=ft.Text(\"\"),\n        bgcolor=ft.Colors.BLUE_100,\n        duration=3000,\n    )\n    setattr(page, \"snackbar\", snackbar)\n\n    page.overlay.extend([health, ph, snackbar])\n    page.update()\n    text = ft.Text()\n\n    async def request_permission(e):\n        try:\n            activity_result = await ph.request_permission_async(\"activity_recognition\")\n            logger.info(f'request activity: {activity_result}')\n            if activity_result == fph.PermissionStatus.GRANTED:\n                open_snack_bar(page, 'Activity recognition permission granted', SnackBarType.SUCCESS)\n            else:\n                open_snack_bar(page, 'Activity recognition permission denied', SnackBarType.ERROR)\n\n            location_result = await ph.request_permission_async(fph.PermissionType.LOCATION)\n            logger.info(f'request location: {location_result}')\n            if location_result == fph.PermissionStatus.GRANTED:\n                open_snack_bar(page, 'Location permission granted', SnackBarType.SUCCESS)\n            else:\n                open_snack_bar(page, 'Location permission denied', SnackBarType.ERROR)\n\n            sensors_result = await ph.request_permission_async(fph.PermissionType.SENSORS)\n            logger.info(f'request sensors: {sensors_result}')\n            if sensors_result == fph.PermissionStatus.GRANTED:\n                open_snack_bar(page, 'Sensors permission granted', SnackBarType.SUCCESS)\n            else:\n                open_snack_bar(page, 'Sensors permission denied', SnackBarType.ERROR)\n\n        except Exception as error:\n            print(error)\n            logger.error(f\"Error request_permission\", exc_info=True)\n            debug_icon.add_notify_error(page)\n            open_snack_bar(page, f\"Error request_permission\", SnackBarType.ERROR)\n\n        page.update()\n\n    async def request_health_permission(e):\n        android_types = [\n            fh.HealthDataTypeAndroid.STEPS,\n            fh.HealthDataTypeAndroid.WATER,\n            fh.HealthDataTypeAndroid.WEIGHT,\n            fh.HealthDataTypeAndroid.HEIGHT,\n            fh.HealthDataTypeAndroid.WORKOUT,\n            fh.HealthDataTypeAndroid.NUTRITION,\n            fh.HealthDataTypeAndroid.HEART_RATE,\n            fh.HealthDataTypeAndroid.BLOOD_OXYGEN,\n            fh.HealthDataTypeAndroid.SLEEP_SESSION,\n            fh.HealthDataTypeAndroid.BLOOD_GLUCOSE,\n            fh.HealthDataTypeAndroid.DISTANCE_DELTA,\n            fh.HealthDataTypeAndroid.LEAN_BODY_MASS,\n            fh.HealthDataTypeAndroid.BODY_TEMPERATURE,\n            fh.HealthDataTypeAndroid.MENSTRUATION_FLOW,\n            fh.HealthDataTypeAndroid.ACTIVE_ENERGY_BURNED,\n            fh.HealthDataTypeAndroid.TOTAL_CALORIES_BURNED,\n            fh.HealthDataTypeAndroid.BLOOD_PRESSURE_SYSTOLIC,\n            fh.HealthDataTypeAndroid.BLOOD_PRESSURE_DIASTOLIC,\n            fh.HealthDataTypeAndroid.HEART_RATE_VARIABILITY_RMSSD,\n        ]\n\n        ios_types = [\n            fh.HealthDataTypeIOS.EXERCISE_TIME,\n            fh.HealthDataTypeIOS.ELECTROCARDIOGRAM,\n            fh.HealthDataTypeIOS.WALKING_HEART_RATE,\n            fh.HealthDataTypeIOS.LOW_HEART_RATE_EVENT,\n            fh.HealthDataTypeIOS.HIGH_HEART_RATE_EVENT,\n            fh.HealthDataTypeIOS.IRREGULAR_HEART_RATE_EVENT,\n        ]\n\n        types = android_types\n        if e.page.platform == ft.PagePlatform.IOS:\n            types = types + ios_types\n\n        data_access = [fh.DataAccess.READ_WRITE for _ in types]\n\n        try:\n            has_permissions = await health.request_authorization_async(\n                types=types,\n                data_access=data_access\n            )\n\n            if has_permissions:\n                open_snack_bar(e.page, \"Permissions granted!\", SnackBarType.SUCCESS)\n            else:\n                logger.info(\"Permissions denied.\")\n                open_snack_bar(e.page, \"Permissions denied.\", SnackBarType.ERROR)\n                e.page.debug_icon.add_notify_error(e.page)\n\n        except Exception as error:\n            print(error)\n            logger.error(f\"Error checking health permissions\", exc_info=True)\n            open_snack_bar(e.page, f\"Error checking health permissions\", SnackBarType.ERROR)\n            e.page.debug_icon.add_notify_error(e.page)\n\n        page.update()\n\n    async def insert_example_data(e):\n        try:\n            result = await add_data(e)\n\n            if result:\n                text.value = \"All data entered successfully.\"\n                open_snack_bar(page, \"All data entered successfully.\", SnackBarType.SUCCESS)\n            else:\n                text.value = \"Failed to enter the data.\"\n                open_snack_bar(page, \"Failed to enter the data.\", SnackBarType.ERROR)\n\n        except Exception as error:\n            print(error)\n            logger.error(f\"Error insert_example_data\", exc_info=True)\n            debug_icon.add_notify_error(page)\n            open_snack_bar(page, f\"Error insert_example_data\", SnackBarType.ERROR)\n\n        page.update()\n\n    def handle_error(e):\n        logger.error(f\"handle_error: {e.data}\")\n        debug_icon.add_notify_error(page)\n\n    health.on_error = handle_error\n    ph.on_error = handle_error\n\n    page.appbar = ft.AppBar(\n        bgcolor=ft.Colors.BLUE,\n        center_title=True,\n        title=ft.Text(\"Flet Health Test\", color=ft.Colors.WHITE),\n        actions=[debug_icon],\n    )\n\n    page.add(\n        ft.SafeArea(\n            minimum_padding=20,\n            content=ft.Row(\n                wrap=True,\n                controls=[\n                    ft.Column(\n                        horizontal_alignment=ft.CrossAxisAlignment.CENTER,\n                        controls=[\n                            ft.ElevatedButton(\n                                width=250,\n                                text=\"Request permissions\",\n                                on_click=request_permission,\n                            ),\n                            ft.ElevatedButton(\n                                width=250,\n                                text=\"Request Health permissions\",\n                                on_click=request_health_permission,\n                            ),\n                            ft.ElevatedButton(\n                                width=250,\n                                text=\"Add data\",\n                                color=ft.Colors.WHITE,\n                                on_click=insert_example_data,\n                                bgcolor=ft.Colors.BLUE,\n                            ),\n                            ft.Container(\n                                alignment=ft.alignment.center,\n                                margin=ft.margin.only(top=20),\n                                content=text\n                            ),\n                        ]\n                    )\n                ]\n            )\n        )\n    )\n\n\nif __name__ == \"__main__\":\n    ft.app(target=main)\n\n```\n\n---\n\n## 🌐 Community\n\n#### Join the community to contribute or get help:\n\n1. [Discord](https://discord.gg/5xJ7qvWM)\n2. [Report an issue](https://github.com/brunobrown/flet-health/issues)\n\n## ⭐ Support\n\nIf you like this project, please give it a [GitHub star](https://github.com/brunobrown/flet-health) and stay tuned for future updates!\n\n## 🤝🏽 Contributing\n\nContributions and feedback are welcome!\n\n#### 🔧 How to contribute:\n\n1. **Fork the repository.**\n2. **Create a feature branch.**\n3. **Submit a pull request with a detailed explanation of your changes.**\n\n#### 💬 How to give feedback:\n\n\u003e We value your opinion! Feel free to share suggestions, ideas, or constructive criticism to help improve the project.\n\n1. **Open an issue.**\n2. **Describe the problem or suggestion clearly**\n3. **Optionally propose a solution**\n\n#### 🕓 Waiting for help?\n\nBe patient — or reach out to the community on [Discord](https://discord.gg/5xJ7qvWM) for quicker responses.\n\n---\n\n## 🚀 Try flet-health today and integrate health data into your applications! 💪🏽📊\n\n\u003cimg src=\"https://github.com/user-attachments/assets/431aa05f-5fbc-4daa-9689-b9723583e25a\" width=\"500\"\u003e\n\n[Commit your work to the LORD, and your plans will succeed. Proverbs 16: 3](https://www.bible.com/bible/116/PRO.16.NLT)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunobrown%2Fflet-health","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrunobrown%2Fflet-health","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunobrown%2Fflet-health/lists"}