// You have generated a new plugin project without // specifying the `--platforms` flag. A plugin project supports no platforms is generated. // To add platforms, run `flutter create -t plugin --platforms .` under the same // directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. import 'dart:async'; import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:path/path.dart' as p; import 'src/create_configuration.dart'; import 'src/message_channel.dart'; import 'src/webview.dart'; import 'src/webview_impl.dart'; export 'src/create_configuration.dart'; export 'src/title_bar.dart'; export 'src/webview.dart'; final List _webviews = []; class WebviewWindow { static const MethodChannel _channel = MethodChannel('webview_window'); static const _otherIsolateMessageHandler = ClientMessageChannel(); static bool _inited = false; static void _init() { if (_inited) { return; } _inited = true; _channel.setMethodCallHandler((call) async { try { return await _handleMethodCall(call); } catch (e, s) { debugPrint("method: ${call.method} args: ${call.arguments}"); debugPrint('handleMethodCall error: $e $s'); } }); _otherIsolateMessageHandler.setMessageHandler((call) async { try { return await _handleOtherIsolateMethodCall(call); } catch (e, s) { debugPrint('_handleOtherIsolateMethodCall error: $e $s'); } }); } /// Check if WebView runtime is available on the current devices. static Future isWebviewAvailable() async { if (Platform.isWindows) { final ret = await _channel.invokeMethod('isWebviewAvailable'); return ret == true; } return true; } static Future create({ CreateConfiguration? configuration, }) async { configuration ??= CreateConfiguration.platform(); _init(); final viewId = await _channel.invokeMethod( "create", configuration.toMap(), ) as int; final webview = WebviewImpl(viewId, _channel); _webviews.add(webview); return webview; } static Future _handleOtherIsolateMethodCall(MethodCall call) async { final webViewId = call.arguments['webViewId'] as int; final webView = _webviews .cast() .firstWhere((w) => w?.viewId == webViewId, orElse: () => null); if (webView == null) { return; } switch (call.method) { case 'onBackPressed': await webView.back(); break; case 'onForwardPressed': await webView.forward(); break; case 'onRefreshPressed': await webView.reload(); break; case 'onStopPressed': await webView.stop(); break; case 'onClosePressed': webView.close(); break; } } static Future _handleMethodCall(MethodCall call) async { final args = call.arguments as Map; final viewId = args['id'] as int; final webview = _webviews .cast() .firstWhere((e) => e?.viewId == viewId, orElse: () => null); assert(webview != null); if (webview == null) { return; } switch (call.method) { case "onWindowClose": _webviews.remove(webview); webview.onClosed(); break; case "onJavaScriptMessage": webview.onJavaScriptMessage(args['name'], args['body']); break; case "runJavaScriptTextInputPanelWithPrompt": return webview.onRunJavaScriptTextInputPanelWithPrompt( args['prompt'], args['defaultText'], ); case "onHistoryChanged": webview.onHistoryChanged(args['canGoBack'], args['canGoForward']); await _otherIsolateMessageHandler.invokeMethod('onHistoryChanged', { 'webViewId': viewId, 'canGoBack': args['canGoBack'] as bool, 'canGoForward': args['canGoForward'] as bool, }); break; case "onNavigationStarted": webview.onNavigationStarted(); await _otherIsolateMessageHandler.invokeMethod('onNavigationStarted', { 'webViewId': viewId, }); break; case "onUrlRequested": final url = args['url'] as String; webview.notifyUrlChanged(url); await _otherIsolateMessageHandler.invokeMethod('onUrlRequested', { 'webViewId': viewId, 'url': url, }); break; case "onWebMessageReceived": final message = args['message'] as String; webview.notifyWebMessageReceived(message); await _otherIsolateMessageHandler.invokeMethod('onWebMessageReceived', { 'webViewId': viewId, 'message': message, }); break; case "onNavigationCompleted": webview.onNavigationCompleted(); await _otherIsolateMessageHandler .invokeMethod('onNavigationCompleted', { 'webViewId': viewId, }); break; default: return; } } /// Clear all cookies and storage. static Future clearAll({ String userDataFolderWindows = 'webview_window_WebView2', }) async { await _channel.invokeMethod('clearAll'); // FIXME(boyan01) Move the logic to windows platform if WebView2 provider a way to clean caches. // https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/user-data-folder#create-user-data-folders if (Platform.isWindows) { final Directory webview2Dir; if (p.isAbsolute(userDataFolderWindows)) { webview2Dir = Directory(userDataFolderWindows); } else { webview2Dir = Directory(p.join( p.dirname(Platform.resolvedExecutable), userDataFolderWindows)); } if (await (webview2Dir.exists())) { for (var i = 0; i <= 4; i++) { try { await webview2Dir.delete(recursive: true); break; } catch (e) { debugPrint("delete cache failed. retring.... $e"); } // wait to ensure all web window has been closed and file handle has been release. await Future.delayed(const Duration(seconds: 1)); } } } } }