From 3479623e59b30e543c52a4852cb008e401b3831a Mon Sep 17 00:00:00 2001 From: Kot <86029286+SKbarbon@users.noreply.github.com> Date: Fri, 22 Sep 2023 22:58:26 +0300 Subject: [PATCH 1/5] Supporting WebView for mobile. --- 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/mobilewebviewer.dart | 96 ++++++++ package/pubspec.yaml | 3 + .../flet-core/src/flet_core/__init__.py | 1 + .../src/flet_core/mobilewebviewer.py | 231 ++++++++++++++++++ 9 files changed, 383 insertions(+), 7 deletions(-) create mode 100644 package/lib/src/controls/mobilewebviewer.dart create mode 100644 sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.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; + } + + // // WebView controller + 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)); + if (Platform.isIOS || Platform.isAndroid) { + return WebViewWidget( + controller: controller + ); + } 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 c8d1daa78..d6ce0beb9 100644 --- a/package/pubspec.yaml +++ b/package/pubspec.yaml @@ -42,6 +42,9 @@ dependencies: js: ^0.6.5 fl_chart: ^0.63.0 + webview_flutter: ^4.2.4 + webview_universal: ^0.0.5 + 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 5cd725aaa..409dde4a3 100644 --- a/sdk/python/packages/flet-core/src/flet_core/__init__.py +++ b/sdk/python/packages/flet-core/src/flet_core/__init__.py @@ -202,3 +202,4 @@ from flet_core.vertical_divider import VerticalDivider from flet_core.view import View from flet_core.window_drag_area import WindowDragArea +from flet_core.mobilewebviewer import MobileWebViewer \ No newline at end of file diff --git a/sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py b/sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py new file mode 100644 index 000000000..702badf50 --- /dev/null +++ b/sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py @@ -0,0 +1,231 @@ +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 + +TextOverflowString = Literal[None, "clip", "ellipsis", "fade", "visible"] + + +class TextOverflow(Enum): + NONE = None + CLIP = "clip" + ELLIPSIS = "ellipsis" + FADE = "fade" + VISIBLE = "visible" + + +TextThemeStyleString = Literal[ + "displayLarge", + "displayMedium", + "displaySmall", + "headlineLarge", + "headlineMedium", + "headlineSmall", + "titleLarge", + "titleMedium", + "titleSmall", + "labelLarge", + "labelMedium", + "labelSmall", + "bodyLarge", + "bodyMedium", + "bodySmall", +] + + +class TextThemeStyle(Enum): + DISPLAY_LARGE = "displayLarge" + DISPLAY_MEDIUM = "displayMedium" + DISPLAY_SMALL = "displaySmall" + HEADLINE_LARGE = "headlineLarge" + HEADLINE_MEDIUM = "headlineMedium" + HEADLINE_SMALL = "headlineSmall" + TITLE_LARGE = "titleLarge" + TITLE_MEDIUM = "titleMedium" + TITLE_SMALL = "titleSmall" + LABEL_LARGE = "labelLarge" + LABEL_MEDIUM = "labelMedium" + LABEL_SMALL = "labelSmall" + BODY_LARGE = "bodyLarge" + BODY_MEDIUM = "bodyMedium" + BODY_SMALL = "bodySmall" + + +class MobileWebViewer(ConstrainedControl): + """ + To be documented + """ + + 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 d9eec3bc5e68ede7c7368d318d33bbe381065237 Mon Sep 17 00:00:00 2001 From: Kot <86029286+SKbarbon@users.noreply.github.com> Date: Sat, 23 Sep 2023 14:44:42 +0300 Subject: [PATCH 2/5] update `MobileWebViewer` with document --- .../src/flet_core/mobilewebviewer.py | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py b/sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py index 702badf50..722639e2a 100644 --- a/sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py +++ b/sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py @@ -72,7 +72,50 @@ class TextThemeStyle(Enum): class MobileWebViewer(ConstrainedControl): """ - To be documented +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__( From 2566e0c03e782e390ecb193baefa9df7e8973e7d Mon Sep 17 00:00:00 2001 From: Kot <86029286+SKbarbon@users.noreply.github.com> Date: Tue, 26 Sep 2023 18:17:59 +0300 Subject: [PATCH 3/5] remove Unnecessary flutter dependency --- package/pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/package/pubspec.yaml b/package/pubspec.yaml index d6ce0beb9..33a73b4d7 100644 --- a/package/pubspec.yaml +++ b/package/pubspec.yaml @@ -43,7 +43,6 @@ dependencies: fl_chart: ^0.63.0 webview_flutter: ^4.2.4 - webview_universal: ^0.0.5 dev_dependencies: flutter_test: From 6436504acf584f41a81ab6c0f2dc54a78df22e84 Mon Sep 17 00:00:00 2001 From: Kot <86029286+SKbarbon@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:45:43 +0300 Subject: [PATCH 4/5] Update `MobileWebView` to become `WebView` As Discussed in the PR, we will make the WebView Universal instead of Separating for each platform. --- package/lib/src/controls/create_control.dart | 6 +++--- .../{mobilewebviewer.dart => webview.dart} | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) rename package/lib/src/controls/{mobilewebviewer.dart => webview.dart} (84%) diff --git a/package/lib/src/controls/create_control.dart b/package/lib/src/controls/create_control.dart index b9706ba58..cdca09a56 100644 --- a/package/lib/src/controls/create_control.dart +++ b/package/lib/src/controls/create_control.dart @@ -73,7 +73,7 @@ import 'tooltip.dart'; import 'transparent_pointer.dart'; import 'vertical_divider.dart'; import 'window_drag_area.dart'; -import 'mobilewebviewer.dart'; +import 'webview.dart'; Widget createControl(Control? parent, String id, bool parentDisabled, {Widget? nextChild}) { @@ -528,8 +528,8 @@ Widget createWidget(Key? key, ControlViewModel controlView, Control? parent, control: controlView.control, children: controlView.children, parentDisabled: parentDisabled); - case "mobilewebviewer": - return MobileWebViewer( + case "webview": + return WebView( key: key, parent: parent, control: controlView.control, diff --git a/package/lib/src/controls/mobilewebviewer.dart b/package/lib/src/controls/webview.dart similarity index 84% rename from package/lib/src/controls/mobilewebviewer.dart rename to package/lib/src/controls/webview.dart index b1faa4140..d6d840829 100644 --- a/package/lib/src/controls/mobilewebviewer.dart +++ b/package/lib/src/controls/webview.dart @@ -1,4 +1,5 @@ import 'dart:io' show Platform; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; @@ -13,13 +14,12 @@ import 'create_control.dart'; import 'package:webview_flutter/webview_flutter.dart'; - -class MobileWebViewer extends StatelessWidget { +class WebView extends StatelessWidget { final Control? parent; final Control control; final bool parentDisabled; - const MobileWebViewer( + const WebView( {Key? key, required this.parent, required this.control, @@ -48,8 +48,8 @@ class MobileWebViewer extends StatelessWidget { jsMode = JavaScriptMode.disabled; } - // // WebView controller - var controller = WebViewController() + if (Platform.isIOS || Platform.isAndroid){ + var controller = WebViewController() ..setJavaScriptMode(jsMode) ..setBackgroundColor(bgcolor) ..setNavigationDelegate( @@ -82,11 +82,16 @@ class MobileWebViewer extends StatelessWidget { ), ) ..loadRequest(Uri.parse(url)); - if (Platform.isIOS || Platform.isAndroid) { return WebViewWidget( controller: controller ); - } else { + }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."); } }); From 15f88c3732377ebe8542fbbf862ae972607ee074 Mon Sep 17 00:00:00 2001 From: Kot <86029286+SKbarbon@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:50:27 +0300 Subject: [PATCH 5/5] Update python SDK to support the updates about the `WebView` --- .../flet-core/src/flet_core/__init__.py | 2 +- .../{mobilewebviewer.py => webview.py} | 49 +------------------ 2 files changed, 2 insertions(+), 49 deletions(-) rename sdk/python/packages/flet-core/src/flet_core/{mobilewebviewer.py => webview.py} (84%) 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 409dde4a3..a7838f739 100644 --- a/sdk/python/packages/flet-core/src/flet_core/__init__.py +++ b/sdk/python/packages/flet-core/src/flet_core/__init__.py @@ -202,4 +202,4 @@ from flet_core.vertical_divider import VerticalDivider from flet_core.view import View from flet_core.window_drag_area import WindowDragArea -from flet_core.mobilewebviewer import MobileWebViewer \ No newline at end of file +from flet_core.webview import WebView \ No newline at end of file diff --git a/sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py b/sdk/python/packages/flet-core/src/flet_core/webview.py similarity index 84% rename from sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py rename to sdk/python/packages/flet-core/src/flet_core/webview.py index 722639e2a..1d3146850 100644 --- a/sdk/python/packages/flet-core/src/flet_core/mobilewebviewer.py +++ b/sdk/python/packages/flet-core/src/flet_core/webview.py @@ -22,55 +22,8 @@ except ImportError: from typing_extensions import Literal -TextOverflowString = Literal[None, "clip", "ellipsis", "fade", "visible"] - -class TextOverflow(Enum): - NONE = None - CLIP = "clip" - ELLIPSIS = "ellipsis" - FADE = "fade" - VISIBLE = "visible" - - -TextThemeStyleString = Literal[ - "displayLarge", - "displayMedium", - "displaySmall", - "headlineLarge", - "headlineMedium", - "headlineSmall", - "titleLarge", - "titleMedium", - "titleSmall", - "labelLarge", - "labelMedium", - "labelSmall", - "bodyLarge", - "bodyMedium", - "bodySmall", -] - - -class TextThemeStyle(Enum): - DISPLAY_LARGE = "displayLarge" - DISPLAY_MEDIUM = "displayMedium" - DISPLAY_SMALL = "displaySmall" - HEADLINE_LARGE = "headlineLarge" - HEADLINE_MEDIUM = "headlineMedium" - HEADLINE_SMALL = "headlineSmall" - TITLE_LARGE = "titleLarge" - TITLE_MEDIUM = "titleMedium" - TITLE_SMALL = "titleSmall" - LABEL_LARGE = "labelLarge" - LABEL_MEDIUM = "labelMedium" - LABEL_SMALL = "labelSmall" - BODY_LARGE = "bodyLarge" - BODY_MEDIUM = "bodyMedium" - BODY_SMALL = "bodySmall" - - -class MobileWebViewer(ConstrainedControl): +class WebView(ConstrainedControl): """ Easily load webpages while allowing user interaction.