From 6a06bfc5f1cf8866c98c0197319c7f840a137da9 Mon Sep 17 00:00:00 2001 From: Kot <86029286+SKbarbon@users.noreply.github.com> Date: Wed, 8 Nov 2023 21:09:38 +0300 Subject: [PATCH 1/3] Supporting `WebView` control (#1865) * Supporting WebView for mobile. * update `MobileWebViewer` with document * remove Unnecessary flutter dependency * Update `MobileWebView` to become `WebView` As Discussed in the PR, we will make the WebView Universal instead of Separating for each platform. * Update python SDK to support the updates about the `WebView` --------- Co-authored-by: Feodor Fitsner --- client/ios/Podfile.lock | 16 +- client/ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- client/pubspec.lock | 32 +++ package/lib/src/controls/create_control.dart | 7 + package/lib/src/controls/webview.dart | 101 ++++++++ package/pubspec.yaml | 2 + .../flet-core/src/flet_core/__init__.py | 1 + .../flet-core/src/flet_core/webview.py | 227 ++++++++++++++++++ 9 files changed, 383 insertions(+), 7 deletions(-) create mode 100644 package/lib/src/controls/webview.dart create mode 100644 sdk/python/packages/flet-core/src/flet_core/webview.py diff --git a/client/ios/Podfile.lock b/client/ios/Podfile.lock index 5b653814a..26fa685f9 100644 --- a/client/ios/Podfile.lock +++ b/client/ios/Podfile.lock @@ -52,6 +52,8 @@ PODS: - SwiftyGif (5.4.3) - url_launcher_ios (0.0.1): - Flutter + - webview_flutter_wkwebview (0.0.1): + - Flutter DEPENDENCIES: - audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`) @@ -60,8 +62,9 @@ DEPENDENCIES: - integration_test (from `.symlinks/plugins/integration_test/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - sensors_plus (from `.symlinks/plugins/sensors_plus/ios`) - - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) SPEC REPOS: trunk: @@ -84,9 +87,11 @@ EXTERNAL SOURCES: sensors_plus: :path: ".symlinks/plugins/sensors_plus/ios" shared_preferences_foundation: - :path: ".symlinks/plugins/shared_preferences_foundation/ios" + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" + webview_flutter_wkwebview: + :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 @@ -95,12 +100,13 @@ SPEC CHECKSUMS: file_picker: ce3938a0df3cc1ef404671531facef740d03f920 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 integration_test: 13825b8a9334a850581300559b8839134b124670 - path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866 sensors_plus: 5717760720f7e6acd96fdbd75b7428f5ad755ec2 - shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 - url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2 + url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 + webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 diff --git a/client/ios/Runner.xcodeproj/project.pbxproj b/client/ios/Runner.xcodeproj/project.pbxproj index 08c0c1096..ce36cef07 100644 --- a/client/ios/Runner.xcodeproj/project.pbxproj +++ b/client/ios/Runner.xcodeproj/project.pbxproj @@ -155,7 +155,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/client/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/client/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a33..a6b826db2 100644 --- a/client/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/client/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ ( + distinct: true, + converter: (store) => ControlTreeViewModel.fromStore(store, control), + builder: (context, viewModel) { + debugPrint("MobileWebViewer build: ${control.id}"); + + String url = control.attrString("url") ?? "https://flet.dev/"; + bool javascriptEnabled = control.attrBool("javascriptEnabled") ?? false; + var bgcolor = HexColor.fromString(Theme.of(context), + control.attrString("bgcolor") ?? "white")!; + String preventLink = control.attrString("preventLink") ?? "none"; + + + var jsMode = JavaScriptMode.unrestricted; + if (javascriptEnabled){ + jsMode = JavaScriptMode.unrestricted; + }else{ + jsMode = JavaScriptMode.disabled; + } + + if (Platform.isIOS || Platform.isAndroid){ + var controller = WebViewController() + ..setJavaScriptMode(jsMode) + ..setBackgroundColor(bgcolor) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int progress) {}, + onPageStarted: (String url) { + FletAppServices.of(context).server.sendPageEvent( + eventTarget: control.id, + eventName: "page_started", + eventData: url); + }, + onPageFinished: (String url) { + FletAppServices.of(context).server.sendPageEvent( + eventTarget: control.id, + eventName: "page_ended", + eventData: url); + }, + onWebResourceError: (WebResourceError error) { + FletAppServices.of(context).server.sendPageEvent( + eventTarget: control.id, + eventName: "web_resource_error", + eventData: error.toString()); + }, + onNavigationRequest: (NavigationRequest request) { + if (request.url.startsWith(preventLink)) { + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + ), + ) + ..loadRequest(Uri.parse(url)); + return WebViewWidget( + controller: controller + ); + }else if (Platform.isMacOS){ + return const Text("'MobileWbBrowser' is not supported on macOS yet."); + }else if (Platform.isWindows){ + return const Text("'MobileWbBrowser' is not supported on windows yey."); + }else if (Platform.isLinux){ + return const Text("'MobileWbBrowser' is not supported on this platform."); + }else{ + return const Text("'MobileWbBrowser' is not supported on this platform."); + } + }); + + return constrainedControl(context, result, parent, control); + } +} diff --git a/package/pubspec.yaml b/package/pubspec.yaml index b904eecc6..72045eca3 100644 --- a/package/pubspec.yaml +++ b/package/pubspec.yaml @@ -42,6 +42,8 @@ dependencies: js: ^0.6.5 fl_chart: ^0.64.0 + webview_flutter: ^4.2.4 + dev_dependencies: flutter_test: sdk: flutter diff --git a/sdk/python/packages/flet-core/src/flet_core/__init__.py b/sdk/python/packages/flet-core/src/flet_core/__init__.py index 1fd981e8d..288871bd9 100644 --- a/sdk/python/packages/flet-core/src/flet_core/__init__.py +++ b/sdk/python/packages/flet-core/src/flet_core/__init__.py @@ -204,5 +204,6 @@ from flet_core.vertical_divider import VerticalDivider from flet_core.view import View from flet_core.window_drag_area import WindowDragArea +from flet_core.webview import WebView from flet_core.range_slider import RangeSlider from flet_core.badge import Badge diff --git a/sdk/python/packages/flet-core/src/flet_core/webview.py b/sdk/python/packages/flet-core/src/flet_core/webview.py new file mode 100644 index 000000000..1d3146850 --- /dev/null +++ b/sdk/python/packages/flet-core/src/flet_core/webview.py @@ -0,0 +1,227 @@ +from enum import Enum +from typing import Any, List, Optional, Union + +from flet_core.constrained_control import ConstrainedControl +from flet_core.control import OptionalNumber +from flet_core.ref import Ref +from flet_core.text_span import TextSpan +from flet_core.types import ( + AnimationValue, + FontWeight, + FontWeightString, + OffsetValue, + ResponsiveNumber, + RotateValue, + ScaleValue, + TextAlign, + TextAlignString, +) + +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal + + +class WebView(ConstrainedControl): + """ +Easily load webpages while allowing user interaction. + +The `MobileWebViewer` control is designed exclusively for iOS and Android platforms. To use a webview on desktop or in a browser, consider utilizing the `DesktopWebViewer` control. + +## Examples +A simple webview implementation using this class could be like: +```python +import flet, time + +def main (page:flet.Page): + wv = flet.MobileWebViewer("https://flet.dev", width=400, height=650) + page.add(wv) + +flet.app(target=main, view=flet.AppView.WEB_BROWSER, port=8550) +``` + + +## Properties +### `url` +Start the webview by loading the `url` value. + +### `width` & `height` +The width and height of the webview. + +### `javascript_enabled` +Enable or disable the javascript execution of the page. Note that disabling the javascript execution of the page may result unexpected webpage behaviour. + +### `prevent_link` +Specify a link to prevent it from downloading. + +### `bgcolor` +Set the background color of the webview. + +## Events +### `on_page_started` +Fires soon as the first loading process of the webpage is started. + +### `on_page_ended` +Fires when all the webpage loading processes are ended. + +### `on_web_resource_error` +Fires when there is error with loading a webpage resource. + +View docs in github: [MobileWebViewer in github](https://flet.dev/docs/controls/mobilewebviewer) + """ + + def __init__( + self, + # + # mobilewebviewer-specific + # + url: str, + width: OptionalNumber, + height: OptionalNumber, + javascript_enabled: bool = True, + prevent_link: str = "none", + bgcolor: Optional[str] = None, + + #--THE REST-- + ref: Optional[Ref] = None, + key: Optional[str] = None, + left: OptionalNumber = None, + top: OptionalNumber = None, + right: OptionalNumber = None, + bottom: OptionalNumber = None, + expand: Union[None, bool, int] = None, + col: Optional[ResponsiveNumber] = None, + opacity: OptionalNumber = None, + rotate: RotateValue = None, + scale: ScaleValue = None, + offset: OffsetValue = None, + aspect_ratio: OptionalNumber = None, + animate_opacity: AnimationValue = None, + animate_size: AnimationValue = None, + animate_position: AnimationValue = None, + animate_rotation: AnimationValue = None, + animate_scale: AnimationValue = None, + animate_offset: AnimationValue = None, + on_animation_end=None, + tooltip: Optional[str] = None, + visible: Optional[bool] = None, + disabled: Optional[bool] = None, + data: Any = None, + semantics_label: Optional[str] = None, + ): + ConstrainedControl.__init__( + self, + ref=ref, + key=key, + width=width, + height=height, + left=left, + top=top, + right=right, + bottom=bottom, + expand=expand, + col=col, + opacity=opacity, + rotate=rotate, + scale=scale, + offset=offset, + aspect_ratio=aspect_ratio, + animate_opacity=animate_opacity, + animate_size=animate_size, + animate_position=animate_position, + animate_rotation=animate_rotation, + animate_scale=animate_scale, + animate_offset=animate_offset, + on_animation_end=on_animation_end, + tooltip=tooltip, + visible=visible, + disabled=disabled, + data=data, + ) + + self.url : str = url + self.javascript_enabled: bool = javascript_enabled + self.prevent_link: str = prevent_link + self.bgcolor = bgcolor + + # events + self.on_page_started = None + self.on_page_ended = None + self.on_web_resource_error = None + + def _get_control_name(self): + return "mobilewebviewer" + + def _get_children(self): + children = [] + print("Childrins are gettn") + return children + + # bgcolor + @property + def bgcolor(self): + return self._get_attr("bgcolor") + + @bgcolor.setter + def bgcolor(self, value): + self._set_attr("bgcolor", value) + + # url + @property + def url (self): + return self._get_attr("url") + + @url.setter + def url (self, value): + self._set_attr("url", value) + + + # javascript_enabled + @property + def javascript_enabled(self): + return self._get_attr("javascriptEnabled") + + @javascript_enabled.setter + def javascript_enabled(self, value): + self._set_attr("javascriptEnabled", value) + + + # prevent_link + @property + def prevent_link (self): + return self._get_attr("prevent_link") + + @prevent_link.setter + def prevent_link(self, value): + self._set_attr("prevent_link", value) + + + ## EVENTS + # on_page_started + @property + def on_page_started(self): + return self._get_event_handler("page_started") + + @on_page_started.setter + def on_page_started(self, handler): + self._add_event_handler("page_started", handler) + + + # on_page_ended + @property + def on_page_ended(self): + return self._get_event_handler("page_ended") + + @on_page_ended.setter + def on_page_ended(self, handler): + self._add_event_handler("page_ended", handler) + + # on_web_resource_error + @property + def on_web_resource_error(self): + return self._get_event_handler("web_resource_error") + + @on_web_resource_error.setter + def on_web_resource_error(self, handler): + self._add_event_handler("web_resource_error", handler) \ No newline at end of file From d9e0be90d12d8dee3d99a0fb6e16d75ea9183f20 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 8 Nov 2023 12:00:34 -0800 Subject: [PATCH 2/3] Added `--host` option to `flet run` command. --- .../packages/flet-runtime/src/flet_runtime/app.py | 14 +++++++++++--- .../packages/flet/src/flet/cli/commands/run.py | 12 ++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/sdk/python/packages/flet-runtime/src/flet_runtime/app.py b/sdk/python/packages/flet-runtime/src/flet_runtime/app.py index 75fa04825..0f2af50b9 100644 --- a/sdk/python/packages/flet-runtime/src/flet_runtime/app.py +++ b/sdk/python/packages/flet-runtime/src/flet_runtime/app.py @@ -318,6 +318,10 @@ def __connect_internal_sync( if env_port is not None and env_port: port = int(env_port) + env_host = os.getenv("FLET_SERVER_IP") + if env_host is not None and env_host: + host = env_host + uds_path = os.getenv("FLET_SERVER_UDS_PATH") env_assets_dir = os.getenv("FLET_ASSETS_PATH") @@ -406,6 +410,10 @@ async def __connect_internal_async( if env_port is not None and env_port: port = int(env_port) + env_host = os.getenv("FLET_SERVER_IP") + if env_host is not None and env_host: + host = env_host + uds_path = os.getenv("FLET_SERVER_UDS_PATH") env_assets_dir = os.getenv("FLET_ASSETS_PATH") @@ -485,7 +493,7 @@ def __start_flet_server( use_color_emoji, route_url_strategy, ): - server_ip = host if host not in [None, "", "*"] else "127.0.0.1" + server_ip = "127.0.0.1" if host in [None, "", "*"] else host if port == 0: port = get_free_tcp_port() @@ -517,9 +525,9 @@ def __start_flet_server( logger.info(f"Upload path configured: {upload_dir}") fletd_env["FLET_UPLOAD_ROOT_DIR"] = upload_dir - if host not in [None, "", "*"]: + if host not in [None, ""]: logger.info(f"Host binding configured: {host}") - fletd_env["FLET_SERVER_IP"] = host + fletd_env["FLET_SERVER_IP"] = host if host != "*" else "" if host != "127.0.0.1": fletd_env["FLET_ALLOW_REMOTE_HOST_CLIENTS"] = "true" diff --git a/sdk/python/packages/flet/src/flet/cli/commands/run.py b/sdk/python/packages/flet/src/flet/cli/commands/run.py index 11f478e6c..8cf5051ec 100644 --- a/sdk/python/packages/flet/src/flet/cli/commands/run.py +++ b/sdk/python/packages/flet/src/flet/cli/commands/run.py @@ -46,6 +46,13 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: default=None, help="custom TCP port to run Flet app on", ) + parser.add_argument( + "--host", + dest="host", + type=str, + default=None, + help='host to run Flet web app on. Use "*" to listen on all IPs.', + ) parser.add_argument( "--name", dest="app_name", @@ -158,6 +165,7 @@ def handle(self, options: argparse.Namespace) -> None: + [options.script if options.module else script_path], None if options.directory or options.recursive else script_path, port, + options.host, options.app_name, uds_path, options.web, @@ -189,6 +197,7 @@ def __init__( args, script_path, port, + host, page_name, uds_path, web, @@ -201,6 +210,7 @@ def __init__( self.args = args self.script_path = script_path self.port = port + self.host = host self.page_name = page_name self.uds_path = uds_path self.web = web @@ -228,6 +238,8 @@ def start_process(self): p_env["FLET_PAGE_NAME"] = "/".join(Path(self.script_path).parts[-2:]) if self.port is not None: p_env["FLET_SERVER_PORT"] = str(self.port) + if self.host is not None: + p_env["FLET_SERVER_IP"] = str(self.host) if self.page_name: p_env["FLET_PAGE_NAME"] = self.page_name if self.uds_path is not None: From e891874321f53af9dc60e713f2467b811eb9e6fe Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 8 Nov 2023 12:01:12 -0800 Subject: [PATCH 3/3] WebView control --- client/android/build.gradle | 2 +- package/lib/src/controls/create_control.dart | 12 +- package/lib/src/controls/webview.dart | 118 +++++++-------- .../flet-core/src/flet_core/webview.py | 140 +++++++++--------- 4 files changed, 135 insertions(+), 137 deletions(-) diff --git a/client/android/build.gradle b/client/android/build.gradle index 3cdaac958..ea855b667 100644 --- a/client/android/build.gradle +++ b/client/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/package/lib/src/controls/create_control.dart b/package/lib/src/controls/create_control.dart index 89b680950..f8a52bcf5 100644 --- a/package/lib/src/controls/create_control.dart +++ b/package/lib/src/controls/create_control.dart @@ -76,8 +76,8 @@ import 'textfield.dart'; import 'tooltip.dart'; import 'transparent_pointer.dart'; import 'vertical_divider.dart'; -import 'window_drag_area.dart'; import 'webview.dart'; +import 'window_drag_area.dart'; Widget createControl(Control? parent, String id, bool parentDisabled, {Widget? nextChild}) { @@ -572,11 +572,11 @@ Widget createWidget(Key? key, ControlViewModel controlView, Control? parent, children: controlView.children, parentDisabled: parentDisabled); case "webview": - return WebView( - key: key, - parent: parent, - control: controlView.control, - parentDisabled: parentDisabled); + return WebViewControl( + key: key, + parent: parent, + control: controlView.control, + parentDisabled: parentDisabled); default: throw Exception("Unknown control type: ${controlView.control.type}"); } diff --git a/package/lib/src/controls/webview.dart b/package/lib/src/controls/webview.dart index d6d840829..302da3f39 100644 --- a/package/lib/src/controls/webview.dart +++ b/package/lib/src/controls/webview.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; +import 'package:webview_flutter/webview_flutter.dart'; import '../flet_app_services.dart'; import '../models/app_state.dart'; @@ -10,16 +11,14 @@ import '../models/control.dart'; import '../models/control_tree_view_model.dart'; import '../utils/colors.dart'; import 'create_control.dart'; +import 'error.dart'; - -import 'package:webview_flutter/webview_flutter.dart'; - -class WebView extends StatelessWidget { +class WebViewControl extends StatelessWidget { final Control? parent; final Control control; final bool parentDisabled; - const WebView( + const WebViewControl( {Key? key, required this.parent, required this.control, @@ -32,67 +31,62 @@ class WebView extends StatelessWidget { distinct: true, converter: (store) => ControlTreeViewModel.fromStore(store, control), builder: (context, viewModel) { - debugPrint("MobileWebViewer build: ${control.id}"); + debugPrint("WebViewControl build: ${control.id}"); - String url = control.attrString("url") ?? "https://flet.dev/"; - bool javascriptEnabled = control.attrBool("javascriptEnabled") ?? false; - var bgcolor = HexColor.fromString(Theme.of(context), - control.attrString("bgcolor") ?? "white")!; - String preventLink = control.attrString("preventLink") ?? "none"; - - - var jsMode = JavaScriptMode.unrestricted; - if (javascriptEnabled){ - jsMode = JavaScriptMode.unrestricted; - }else{ - jsMode = JavaScriptMode.disabled; + String url = control.attrString("url", "")!; + if (url == "") { + return const ErrorControl("WebView.url cannot be empty."); } - if (Platform.isIOS || Platform.isAndroid){ + bool javascriptEnabled = + control.attrBool("javascriptEnabled", false)!; + var bgcolor = HexColor.fromString( + Theme.of(context), control.attrString("bgcolor", "")!); + String preventLink = control.attrString("preventLink", "")!; + + if (Platform.isIOS || Platform.isAndroid) { var controller = WebViewController() - ..setJavaScriptMode(jsMode) - ..setBackgroundColor(bgcolor) - ..setNavigationDelegate( - NavigationDelegate( - onProgress: (int progress) {}, - onPageStarted: (String url) { - FletAppServices.of(context).server.sendPageEvent( - eventTarget: control.id, - eventName: "page_started", - eventData: url); - }, - onPageFinished: (String url) { - FletAppServices.of(context).server.sendPageEvent( - eventTarget: control.id, - eventName: "page_ended", - eventData: url); - }, - onWebResourceError: (WebResourceError error) { - FletAppServices.of(context).server.sendPageEvent( - eventTarget: control.id, - eventName: "web_resource_error", - eventData: error.toString()); - }, - onNavigationRequest: (NavigationRequest request) { - if (request.url.startsWith(preventLink)) { - return NavigationDecision.prevent; - } - return NavigationDecision.navigate; - }, - ), - ) - ..loadRequest(Uri.parse(url)); - return WebViewWidget( - controller: controller - ); - }else if (Platform.isMacOS){ - return const Text("'MobileWbBrowser' is not supported on macOS yet."); - }else if (Platform.isWindows){ - return const Text("'MobileWbBrowser' is not supported on windows yey."); - }else if (Platform.isLinux){ - return const Text("'MobileWbBrowser' is not supported on this platform."); - }else{ - return const Text("'MobileWbBrowser' is not supported on this platform."); + ..setJavaScriptMode(javascriptEnabled + ? JavaScriptMode.unrestricted + : JavaScriptMode.disabled) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int progress) {}, + onPageStarted: (String url) { + FletAppServices.of(context).server.sendPageEvent( + eventTarget: control.id, + eventName: "page_started", + eventData: url); + }, + onPageFinished: (String url) { + FletAppServices.of(context).server.sendPageEvent( + eventTarget: control.id, + eventName: "page_ended", + eventData: url); + }, + onWebResourceError: (WebResourceError error) { + FletAppServices.of(context).server.sendPageEvent( + eventTarget: control.id, + eventName: "web_resource_error", + eventData: error.toString()); + }, + onNavigationRequest: (NavigationRequest request) { + if (preventLink != "" && + request.url.startsWith(preventLink)) { + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + ), + ); + if (bgcolor != null) { + controller.setBackgroundColor(bgcolor); + } + controller.loadRequest(Uri.parse(url)); + return WebViewWidget(controller: controller); + } else { + return const ErrorControl( + "WebView control is not supported on this platform yet."); } }); diff --git a/sdk/python/packages/flet-core/src/flet_core/webview.py b/sdk/python/packages/flet-core/src/flet_core/webview.py index 1d3146850..ab58222d1 100644 --- a/sdk/python/packages/flet-core/src/flet_core/webview.py +++ b/sdk/python/packages/flet-core/src/flet_core/webview.py @@ -25,67 +25,71 @@ class WebView(ConstrainedControl): """ -Easily load webpages while allowing user interaction. + Easily load webpages while allowing user interaction. -The `MobileWebViewer` control is designed exclusively for iOS and Android platforms. To use a webview on desktop or in a browser, consider utilizing the `DesktopWebViewer` control. + The `WebView` control is designed exclusively for iOS and Android platforms. -## Examples -A simple webview implementation using this class could be like: -```python -import flet, time + ## Examples + A simple webview implementation using this class could be like: -def main (page:flet.Page): - wv = flet.MobileWebViewer("https://flet.dev", width=400, height=650) - page.add(wv) + ```python + import flet -flet.app(target=main, view=flet.AppView.WEB_BROWSER, port=8550) -``` + def main(page: flet.Page): + wv = flet.WebView( + "https://flet.dev", + expand=True, + on_page_started=lambda _: print("Page started"), + on_page_ended=lambda _: print("Page ended"), + on_web_resource_error=lambda e: print("Page error:", e.data), + ) + page.add(wv) + + flet.app(main) + ``` + + ## Properties + + ### `url` + + Start the webview by loading the `url` value. + + ### `javascript_enabled` + + Enable or disable the javascript execution of the page. Note that disabling the javascript execution of the page may result unexpected webpage behaviour. + + ### `prevent_link` + Specify a link to prevent it from downloading. -## Properties -### `url` -Start the webview by loading the `url` value. + ### `bgcolor` -### `width` & `height` -The width and height of the webview. + Set the background color of the webview. -### `javascript_enabled` -Enable or disable the javascript execution of the page. Note that disabling the javascript execution of the page may result unexpected webpage behaviour. + ## Events -### `prevent_link` -Specify a link to prevent it from downloading. + ### `on_page_started` -### `bgcolor` -Set the background color of the webview. + Fires soon as the first loading process of the webpage is started. -## Events -### `on_page_started` -Fires soon as the first loading process of the webpage is started. + ### `on_page_ended` -### `on_page_ended` -Fires when all the webpage loading processes are ended. + Fires when all the webpage loading processes are ended. -### `on_web_resource_error` -Fires when there is error with loading a webpage resource. + ### `on_web_resource_error` -View docs in github: [MobileWebViewer in github](https://flet.dev/docs/controls/mobilewebviewer) + Fires when there is error with loading a webpage resource. + + View docs: [WebView](https://flet.dev/docs/controls/webview) """ def __init__( self, - # - # mobilewebviewer-specific - # url: str, - width: OptionalNumber, - height: OptionalNumber, - javascript_enabled: bool = True, - prevent_link: str = "none", - bgcolor: Optional[str] = None, - - #--THE REST-- ref: Optional[Ref] = None, key: Optional[str] = None, + width: OptionalNumber = None, + height: OptionalNumber = None, left: OptionalNumber = None, top: OptionalNumber = None, right: OptionalNumber = None, @@ -108,7 +112,15 @@ def __init__( visible: Optional[bool] = None, disabled: Optional[bool] = None, data: Any = None, - semantics_label: Optional[str] = None, + # + # webview-specific + # + javascript_enabled: bool = True, + prevent_link: str = "none", + bgcolor: Optional[str] = None, + on_page_started=None, + on_page_ended=None, + on_web_resource_error=None, ): ConstrainedControl.__init__( self, @@ -140,23 +152,18 @@ def __init__( data=data, ) - self.url : str = url - self.javascript_enabled: bool = javascript_enabled - self.prevent_link: str = prevent_link + self.url = url + self.javascript_enabled = javascript_enabled + self.prevent_link = prevent_link self.bgcolor = bgcolor # events - self.on_page_started = None - self.on_page_ended = None - self.on_web_resource_error = None + self.on_page_started = on_page_started + self.on_page_ended = on_page_ended + self.on_web_resource_error = on_web_resource_error def _get_control_name(self): - return "mobilewebviewer" - - def _get_children(self): - children = [] - print("Childrins are gettn") - return children + return "webview" # bgcolor @property @@ -169,59 +176,56 @@ def bgcolor(self, value): # url @property - def url (self): + def url(self): return self._get_attr("url") - + @url.setter - def url (self, value): + def url(self, value): self._set_attr("url", value) - # javascript_enabled @property def javascript_enabled(self): return self._get_attr("javascriptEnabled") - + @javascript_enabled.setter def javascript_enabled(self, value): self._set_attr("javascriptEnabled", value) - # prevent_link @property - def prevent_link (self): + def prevent_link(self): return self._get_attr("prevent_link") - + @prevent_link.setter def prevent_link(self, value): self._set_attr("prevent_link", value) - ## EVENTS + # on_page_started @property def on_page_started(self): return self._get_event_handler("page_started") - + @on_page_started.setter def on_page_started(self, handler): self._add_event_handler("page_started", handler) - # on_page_ended @property def on_page_ended(self): return self._get_event_handler("page_ended") - + @on_page_ended.setter def on_page_ended(self, handler): self._add_event_handler("page_ended", handler) - + # on_web_resource_error @property def on_web_resource_error(self): return self._get_event_handler("web_resource_error") - + @on_web_resource_error.setter def on_web_resource_error(self, handler): - self._add_event_handler("web_resource_error", handler) \ No newline at end of file + self._add_event_handler("web_resource_error", handler)