flet-dev / flet

Flet enables developers to easily build realtime web, mobile and desktop apps in Python. No frontend experience required.
https://flet.dev
Apache License 2.0
9.43k stars 360 forks source link

Geolocator #3179

Open lekshmanmj opened 2 weeks ago

lekshmanmj commented 2 weeks ago

PR on GPS Location Feature @FeodorFitsner @ndonkoHenri

As Stream function provided in Geolocator dart package just stops streaming when app is minimized, Initially I planned to implement location streaming using Background Service... But while trying that in a clean/pure flutter project itself I got stuck with some Kotlin version related error(which I couldn't solve). Therefore I finally decided to just have a normal function to get location in dart side and decided to call it explicitly in a loop in flet app using run_task/run_thread method to implement stream(to leave it to user's/developer's side) as implemented in the code.

playground/main.py

import flet as ft
import asyncio

def main(page: ft.Page):
    page.scroll = ft.ScrollMode.ADAPTIVE
    page.add(ft.SafeArea(ft.Text("Geolocator test")))
    gl = ft.Geolocator()
    page.overlay.append(gl)
    page.update()
    position = gl.get_location()
    page.add(
        ft.SafeArea(
            ft.Text(
                "latitude: {}, longitude: {}".format(
                    position.latitude, position.longitude
                )
            )
        )
    )
    page.add(ft.SafeArea(ft.Text("Geolocation Stream using Thread/AsyncTask")))

    async def loop():
        while True:
            position = await gl.get_location_async()
            page.add(
                ft.SafeArea(
                    ft.Text(
                        "latitude: {}, longitude: {}".format(
                            position.latitude, position.longitude
                        )
                    )
                )
            )
            await asyncio.sleep(1)

    page.run_task(loop)
    page.update()

ft.app(main)
FeodorFitsner commented 2 weeks ago

Thank you! Useful control - great PR! 👍

I'm looking at the example for Geolocator widget: https://pub.dev/packages/geolocator#example

I think we need to implement methods to check/request permissions:

We obviously can't rely on an assumption the location service is allowed/enabled on user's device, right? What do you think?

lekshmanmj commented 2 weeks ago

Thank you! Useful control - great PR! 👍

I'm looking at the example for Geolocator widget: https://pub.dev/packages/geolocator#example

I think we need to implement methods to check/request permissions:

  • isLocationServiceEnabled()
  • checkPermission()
  • requestPermission()
  • LocationPermission enum.

We obviously can't rely on an assumption the location service is allowed/enabled on user's device, right? What do you think?

Permission.location.request().isGranted This line does the job.. way 1 - if Permission is not given : It prompts the user for both permission and to enable location service --> waits infinitely to get users response --> return true/false way 2- If Permission is already granted: It just directly return true/false. I have witnessed the above scenario while test in clean flutter project in my android device.

In dart side waiting event(to get response from permission and service prompt) happens for infinite amount of time, but in python side invoke_method was waiting only for 5 seconds. Within this time limit as we can't expect the end user to respond to permission and service prompt, ...have increased the waiting time passed to invoke_method from geolocator to 25 seconds.

I've ignorned package(geolocator) provided permission related function in current implementation, as I have better understanding about common PermissionHandler dart package.

Planned to add check_permission and request_permission functions to common flet_permission_handler feature(...will be submitting a PR on it shortly).

ndonkoHenri commented 1 week ago

I've ignorned package(geolocator) provided permission related function in current implementation, as I have better understanding about common PermissionHandler dart package.

Seems like the permissions_handler package is limited to mobile only? 😯

In any case, I suggest we go for independence: expose all the possible methods provided by the geolocator package as they might be optimised for its specific use-case. By the way, seems like geolocator does not depend on permissions_handler.

permission_handler package you implemented, will of course still be useful - perhaps not that much for the location-related checks, but for all others. (thanks for working on it!)

lekshmanmj commented 1 week ago

I've ignorned package(geolocator) provided permission related function in current implementation, as I have better understanding about common PermissionHandler dart package.

Seems like the permissions_handler package is limited to mobile only? 😯

In any case, I suggest we go for independence: expose all the possible methods provided by the geolocator package as they might be optimised for its specific use-case. By the way, seems like geolocator does not depend on permissions_handler.

permission_handler package you implemented, will of course still be useful - perhaps not that much for the location-related checks, but for all others. (thanks for working on it!)

image

yes... will expose all other functions of that package. and please let me know what other functions do you expect to be present in flet implementation.

Geolocator is directly dependent on permission_handler.dart rather than on permission_handler which is recently implemented in flet.

permission_handler is intended to be used in rare scenarios only... like if user wish to check/get multiple permission on opening app.

ndonkoHenri commented 1 week ago

So, I went on and added/updated some more stuffs such as permissions, utils and the exposed methods as suggested by Feodor. I think your PR is now ready for it's v1. Additionally, if you have desire and time, you can pick up from here and expose more stuffs... (no problem when you can't - I could do that some other day). Awesome work on this PR! 👏🏾

I tested simultaneously on 4 platforms and it worked: android, mac, web and ios - only windows is left out now.

XX

Updated Test Code

import flet as ft

async def main(page: ft.Page):
    page.window_always_on_top = True
    page.on_error = lambda e: print(f"Page Error: {e.data}")
    page.scroll = ft.ScrollMode.ADAPTIVE
    page.appbar = ft.AppBar(title=ft.Text("Geolocator Tests"))
    gl = ft.Geolocator()
    page.overlay.append(gl)

    settings_dlg = lambda handler: ft.AlertDialog(
        adaptive=True,
        title=ft.Text("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(
                text="OK",
                on_click=handler,
            ),
        ],
        actions_alignment=ft.MainAxisAlignment.CENTER,
    )

    def handle_permission_request(e):
        page.add(ft.Text(f"request_permission: {gl.request_permission()}"))

    def handle_has_permission(e):
        page.add(ft.Text(f"has_permission: {gl.has_permission()}"))

    def handle_get_current_position(e):
        p = gl.get_current_position()
        page.add(ft.Text(f"get_current_position: ({p.latitude}, {p.longitude})"))

    def handle_get_last_known_position(e):
        p = gl.get_last_known_position()
        page.add(ft.Text(f"get_last_known_position: ({p.latitude}, {p.longitude})"))

    def handle_location_service_enabled(e):
        page.add(
            ft.Text(f"is_location_service_enabled: {gl.is_location_service_enabled()}")
        )

    def handle_open_location_settings(e):
        page.close_dialog()
        page.add(ft.Text(f"open_location_settings: {gl.open_location_settings()}"))

    def handle_open_app_settings(e):
        page.close_dialog()
        page.add(ft.Text(f"open_app_settings: {gl.open_app_settings()}"))

    page.add(
        ft.Row(
            [
                ft.OutlinedButton(
                    "request_permission",
                    on_click=handle_permission_request,
                ),
                ft.OutlinedButton(
                    "has_permission",
                    on_click=handle_has_permission,
                ),
                ft.OutlinedButton(
                    "get_current_position",
                    on_click=handle_get_current_position,
                ),
                ft.OutlinedButton(
                    "get_last_known_position",
                    visible=False if page.web else True,
                    on_click=handle_get_last_known_position,
                ),
                ft.OutlinedButton(
                    "is_location_service_enabled",
                    on_click=handle_location_service_enabled,
                ),
                ft.OutlinedButton(
                    "open_location_settings",
                    visible=False if page.web else True,
                    on_click=lambda e: page.show_dialog(
                        settings_dlg(handle_open_location_settings)
                    ),
                ),
                ft.OutlinedButton(
                    "open_app_settings",
                    visible=False if page.web else True,
                    on_click=lambda e: page.show_dialog(
                        settings_dlg(handle_open_app_settings)
                    ),
                ),
            ],
            wrap=True,
        )
    )

ft.app(main)