Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Windows platform support using webview_windows package

### Changed

- Updated platform support documentation in README

## [0.2.0] - 2025-06-26

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This package supports the following platforms:

| Platform | Supported |
|----------|:---------:|
| Windows | |
| Windows | |
| macOS | ✅ |
| Linux | ❌ |
| iOS | ✅ |
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ authors = [{ name = "Flet contributors", email = "hello@flet.dev" }]
license = "Apache-2.0"
requires-python = ">=3.10"
dependencies = [
"flet >=0.70.0.dev0",
"flet",
]

[project.urls]
Expand Down
2 changes: 1 addition & 1 deletion src/flutter/flet_webview/lib/src/webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class WebViewControl extends StatelessWidget {
} else if (isMobilePlatform() || isMacOSDesktop()) {
view = WebviewMobileAndMac(control: control);
} else if (isWindowsDesktop() || isLinuxDesktop()) {
view = const WebviewDesktop();
view = WebviewDesktop(control: control);
}

return ConstrainedControl(control: control, child: view);
Expand Down
191 changes: 188 additions & 3 deletions src/flutter/flet_webview/lib/src/webview_windows_and_linux.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,196 @@
import 'dart:io' show Platform;
import 'package:flet/flet.dart';
import 'package:flet_webview/src/utils/webview.dart';
import 'package:flutter/material.dart';
import 'package:webview_windows/webview_windows.dart' as windows_webview;

class WebviewDesktop extends StatelessWidget {
const WebviewDesktop({Key? key}) : super(key: key);
class WebviewDesktop extends StatefulWidget {
final Control control;

const WebviewDesktop({Key? key, required this.control}) : super(key: key);

@override
State<WebviewDesktop> createState() => _WebviewDesktopState();
}

class _WebviewDesktopState extends State<WebviewDesktop> {
final windows_webview.WebviewController _controller =
windows_webview.WebviewController();
bool _isInitialized = false;
String? _errorMessage;
bool _canGoBack = false;
bool _canGoForward = false;

@override
void initState() {
super.initState();
widget.control.addInvokeMethodListener(_invokeMethod);

// Only initialize on Windows
if (Platform.isWindows) {
_initializeWebView();
} else {
// On Linux, just mark as initialized to show the error message
setState(() {
_isInitialized = true;
});
}
}

Future<void> _initializeWebView() async {
try {
await _controller.initialize();

// Set up event listeners
_controller.url.listen((url) {
widget.control.triggerEvent("url_change", url);
});

_controller.loadingState.listen((state) {
if (state == windows_webview.LoadingState.loading) {
widget.control.triggerEvent(
"page_started", _controller.url.value);
} else if (state == windows_webview.LoadingState.navigationCompleted) {
widget.control.triggerEvent(
"page_ended", _controller.url.value);
}
});

_controller.historyChanged.listen((history) {
setState(() {
_canGoBack = history.canGoBack;
_canGoForward = history.canGoForward;
});
});

// Load initial URL
final url = widget.control.getString("url", "https://flet.dev")!;
await _controller.loadUrl(url);

// Set background color if specified
var bgcolor = widget.control.getColor("bgcolor", context);
if (bgcolor != null) {
await _controller.setBackgroundColor(bgcolor);
}

setState(() {
_isInitialized = true;
});
} catch (e) {
setState(() {
_errorMessage = "Failed to initialize WebView: $e";
});
}
}

Future<dynamic> _invokeMethod(String name, dynamic args) async {
debugPrint("WebView.$name($args)");

if (!_isInitialized) {
debugPrint("WebView not initialized, ignoring method call: $name");
return null;
}

switch (name) {
case "reload":
await _controller.reload();
break;
case "can_go_back":
return _canGoBack.toString();
case "can_go_forward":
return _canGoForward.toString();
case "go_back":
if (_canGoBack) {
await _controller.goBack();
}
break;
case "go_forward":
if (_canGoForward) {
await _controller.goForward();
}
break;
case "enable_zoom":
debugPrint("enable_zoom not explicitly supported on Windows WebView (use setZoomFactor)");
break;
case "disable_zoom":
debugPrint("disable_zoom not explicitly supported on Windows WebView (use setZoomFactor)");
break;
case "clear_cache":
await _controller.clearCache();
break;
case "clear_cookies":
await _controller.clearCookies();
break;
case "clear_local_storage":
debugPrint("clear_local_storage not supported on Windows WebView");
break;
case "get_current_url":
return _controller.url.value;
case "get_title":
return _controller.title.value;
case "get_user_agent":
debugPrint("get_user_agent not supported on Windows WebView (can only set)");
return null;
case "load_file":
debugPrint("load_file not supported on Windows WebView");
break;
case "load_html":
await _controller.loadStringContent(args["value"]);
break;
case "load_request":
var url = args["url"];
if (url != null) {
await _controller.loadUrl(url);
}
break;
case "run_javascript":
var javascript = args["value"];
if (javascript != null) {
await _controller.executeScript(javascript);
}
break;
case "scroll_to":
debugPrint("scroll_to not supported on Windows WebView");
break;
case "scroll_by":
debugPrint("scroll_by not supported on Windows WebView");
break;
case "set_javascript_mode":
// webview_windows always has JavaScript enabled
debugPrint("JavaScript mode is always enabled on Windows");
break;
default:
debugPrint("Unknown WebView method: $name");
}
}

@override
void dispose() {
debugPrint("WebViewControl dispose: ${widget.control.id}");
widget.control.removeInvokeMethodListener(_invokeMethod);
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return const ErrorControl("Webview is not yet supported on this Platform.");
if (_errorMessage != null) {
return ErrorControl(_errorMessage!);
}

if (!_isInitialized) {
return const Center(child: CircularProgressIndicator());
}

if (Platform.isWindows) {
return windows_webview.Webview(_controller);
} else {
return const ErrorControl(
"WebView is not yet supported on Linux. "
"Linux support requires additional system dependencies (webkit2gtk-4.1) "
"and there are no stable embeddable WebView packages available for Flutter on Linux yet. "
"For more information, see: https://github.com/flet-dev/flet-webview/issues/17"
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import 'package:flet/flet.dart';
import 'package:flutter/material.dart';

class WebviewDesktop extends StatefulWidget {
const WebviewDesktop({Key? key}) : super(key: key);
final Control control;

const WebviewDesktop({Key? key, required this.control}) : super(key: key);

@override
State<WebviewDesktop> createState() => _WebviewDesktopState();
Expand Down
1 change: 1 addition & 0 deletions src/flutter/flet_webview/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies:
sdk: flutter
webview_flutter_web: ^0.2.3+4
webview_flutter_platform_interface: ^2.13.0
webview_windows: ^0.4.0

# flet: 0.70.0
flet:
Expand Down