From d01d0b5ddb71aa35739f265115c9517de08b546d Mon Sep 17 00:00:00 2001 From: nyne Date: Wed, 16 Oct 2024 10:55:57 +0800 Subject: [PATCH] cloudflare verification --- lib/components/components.dart | 1 + lib/components/loading.dart | 15 +- lib/foundation/js_engine.dart | 4 +- lib/main.dart | 6 +- lib/network/cloudflare.dart | 177 +++++++++++ lib/pages/settings/settings_page.dart | 14 - lib/pages/webview.dart | 282 ++++++++++++++++++ linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 4 + pubspec.lock | 101 ++++++- pubspec.yaml | 9 +- .../flutter/generated_plugin_registrant.cc | 6 + windows/flutter/generated_plugins.cmake | 2 + 14 files changed, 591 insertions(+), 35 deletions(-) create mode 100644 lib/network/cloudflare.dart create mode 100644 lib/pages/webview.dart diff --git a/lib/components/components.dart b/lib/components/components.dart index f0596c1..aec2573 100644 --- a/lib/components/components.dart +++ b/lib/components/components.dart @@ -22,6 +22,7 @@ import 'package:venera/foundation/image_provider/cached_image.dart'; import 'package:venera/foundation/local.dart'; import 'package:venera/foundation/res.dart'; import 'package:venera/foundation/state_controller.dart'; +import 'package:venera/network/cloudflare.dart'; import 'package:venera/pages/comic_page.dart'; import 'package:venera/pages/favorites/favorites_page.dart'; import 'package:venera/utils/ext.dart'; diff --git a/lib/components/loading.dart b/lib/components/loading.dart index 3d6c13c..6bbd367 100644 --- a/lib/components/loading.dart +++ b/lib/components/loading.dart @@ -16,6 +16,7 @@ class NetworkError extends StatelessWidget { @override Widget build(BuildContext context) { + var cfe = CloudflareException.fromString(message); Widget body = Center( child: Column( mainAxisSize: MainAxisSize.min, @@ -41,7 +42,7 @@ class NetworkError extends StatelessWidget { height: 8, ), Text( - message, + cfe == null ? message : "Cloudflare verification required".tl, textAlign: TextAlign.center, maxLines: 3, ), @@ -50,7 +51,17 @@ class NetworkError extends StatelessWidget { height: 12, ), if (retry != null) - FilledButton(onPressed: retry, child: Text('重试'.tl)) + if (cfe != null) + FilledButton( + onPressed: () => passCloudflare( + CloudflareException.fromString(message)!, retry!), + child: Text('Verify'.tl), + ) + else + FilledButton( + onPressed: retry, + child: Text('Retry'.tl), + ), ], ), ); diff --git a/lib/foundation/js_engine.dart b/lib/foundation/js_engine.dart index 26fea3d..3192d0a 100644 --- a/lib/foundation/js_engine.dart +++ b/lib/foundation/js_engine.dart @@ -21,6 +21,7 @@ import 'package:pointycastle/block/modes/ecb.dart'; import 'package:pointycastle/block/modes/ofb.dart'; import 'package:uuid/uuid.dart'; import 'package:venera/network/app_dio.dart'; +import 'package:venera/network/cloudflare.dart'; import 'package:venera/network/cookie_jar.dart'; import 'comic_source/comic_source.dart'; @@ -67,8 +68,7 @@ class JsEngine with _JSEngineApi{ responseType: ResponseType.plain, validateStatus: (status) => true)); _cookieJar ??= SingleInstanceCookieJar.instance!; _dio!.interceptors.add(CookieManagerSql(_cookieJar!)); - // TODO: Cloudflare Interceptor - // _dio!.interceptors.add(CloudflareInterceptor()); + _dio!.interceptors.add(CloudflareInterceptor()); _closed = false; _engine = FlutterQjs(); _engine!.dispatch(); diff --git a/lib/main.dart b/lib/main.dart index b74278b..4619dc6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:desktop_webview_window/desktop_webview_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -11,7 +12,10 @@ import 'foundation/app.dart'; import 'foundation/appdata.dart'; import 'init.dart'; -void main() { +void main(List args) { + if (runWebViewTitleBarWidget(args)) { + return; + } runZonedGuarded(() async { WidgetsFlutterBinding.ensureInitialized(); await init(); diff --git a/lib/network/cloudflare.dart b/lib/network/cloudflare.dart new file mode 100644 index 0000000..e69f0b7 --- /dev/null +++ b/lib/network/cloudflare.dart @@ -0,0 +1,177 @@ +import 'dart:io' as io; + +import 'package:dio/dio.dart'; +import 'package:venera/foundation/app.dart'; +import 'package:venera/foundation/appdata.dart'; +import 'package:venera/foundation/consts.dart'; +import 'package:venera/pages/webview.dart'; + +import 'cookie_jar.dart'; + +class CloudflareException implements DioException { + final String url; + + const CloudflareException(this.url); + + @override + String toString() { + return "CloudflareException: $url"; + } + + static CloudflareException? fromString(String message) { + var match = RegExp(r"CloudflareException: (.+)").firstMatch(message); + if (match == null) return null; + return CloudflareException(match.group(1)!); + } + + @override + DioException copyWith( + {RequestOptions? requestOptions, + Response? response, + DioExceptionType? type, + Object? error, + StackTrace? stackTrace, + String? message}) { + return this; + } + + @override + Object? get error => this; + + @override + String? get message => toString(); + + @override + RequestOptions get requestOptions => RequestOptions(); + + @override + Response? get response => null; + + @override + StackTrace get stackTrace => StackTrace.empty; + + @override + DioExceptionType get type => DioExceptionType.badResponse; +} + +class CloudflareInterceptor extends Interceptor { + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + if(options.headers['cookie'].toString().contains('cf_clearance')) { + options.headers['user-agent'] = appdata.implicitData['ua'] ?? webUA; + } + handler.next(options); + } + + @override + void onError(DioException err, ErrorInterceptorHandler handler) async { + if (err.response?.statusCode == 403) { + handler.next(_check(err.response!) ?? err); + } else { + handler.next(err); + } + } + + @override + void onResponse(Response response, ResponseInterceptorHandler handler) { + if (response.statusCode == 403) { + var err = _check(response); + if (err != null) { + handler.reject(err); + return; + } + } + handler.next(response); + } + + CloudflareException? _check(Response response) { + if (response.headers['cf-mitigated']?.firstOrNull == "challenge") { + return CloudflareException(response.requestOptions.uri.toString()); + } + return null; + } +} + +void passCloudflare(CloudflareException e, void Function() onFinished) async { + var url = e.url; + var uri = Uri.parse(url); + + void saveCookies(Map cookies) { + var domain = uri.host; + var splits = domain.split('.'); + if (splits.length > 1) { + domain = ".${splits[splits.length - 2]}.${splits[splits.length - 1]}"; + } + SingleInstanceCookieJar.instance!.saveFromResponse( + uri, + List.generate(cookies.length, (index) { + var cookie = io.Cookie( + cookies.keys.elementAt(index), cookies.values.elementAt(index)); + cookie.domain = domain; + return cookie; + }), + ); + } + + if (App.isDesktop && (await DesktopWebview.isAvailable())) { + var webview = DesktopWebview( + initialUrl: url, + onTitleChange: (title, controller) async { + var res = await controller.evaluateJavascript( + "document.head.innerHTML.includes('#challenge-success-text')"); + if (res == 'false') { + var ua = controller.userAgent; + if (ua != null) { + appdata.implicitData['ua'] = ua; + appdata.writeImplicitData(); + } + var cookiesMap = await controller.getCookies(url); + if(cookiesMap['cf_clearance'] == null) { + return; + } + saveCookies(cookiesMap); + controller.close(); + onFinished(); + } + }, + ); + webview.open(); + } else if (App.isMobile) { + await App.rootContext.to( + () => AppWebview( + initialUrl: url, + singlePage: true, + onTitleChange: (title, controller) async { + var res = await controller.platform.evaluateJavascript( + source: + "document.head.innerHTML.includes('#challenge-success-text')"); + if (res == false) { + var ua = await controller.getUA(); + if (ua != null) { + appdata.implicitData['ua'] = ua; + appdata.writeImplicitData(); + } + var cookiesMap = await controller.getCookies(url) ?? {}; + if(cookiesMap['cf_clearance'] == null) { + return; + } + saveCookies(cookiesMap); + App.rootPop(); + } + }, + onStarted: (controller) async { + var ua = await controller.getUA(); + if (ua != null) { + appdata.implicitData['ua'] = ua; + appdata.writeImplicitData(); + } + var cookiesMap = await controller.getCookies(url) ?? {}; + saveCookies(cookiesMap); + }, + ), + ); + onFinished(); + } else { + App.rootContext.showMessage(message: "Unsupported device"); + } +} diff --git a/lib/pages/settings/settings_page.dart b/lib/pages/settings/settings_page.dart index cf74168..64f3c7c 100644 --- a/lib/pages/settings/settings_page.dart +++ b/lib/pages/settings/settings_page.dart @@ -326,9 +326,6 @@ class _SettingsPageState extends State implements PopEntry { @override ValueListenable get canPopNotifier => canPop; - /* - flutter >=3.24.0 api - @override void onPopInvokedWithResult(bool didPop, result) { if (currentPage != -1) { @@ -346,15 +343,4 @@ class _SettingsPageState extends State implements PopEntry { }); } } - */ - - // flutter <3.24.0 api - @override - PopInvokedCallback? get onPopInvoked => (bool didPop) { - if (currentPage != -1) { - setState(() { - currentPage = -1; - }); - } - }; } diff --git a/lib/pages/webview.dart b/lib/pages/webview.dart new file mode 100644 index 0000000..644440d --- /dev/null +++ b/lib/pages/webview.dart @@ -0,0 +1,282 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:desktop_webview_window/desktop_webview_window.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:url_launcher/url_launcher_string.dart'; +import 'package:venera/components/components.dart'; +import 'package:venera/foundation/app.dart'; +import 'package:venera/network/app_dio.dart'; +import 'package:venera/utils/ext.dart'; +import 'package:venera/utils/translations.dart'; + +export 'package:flutter_inappwebview/flutter_inappwebview.dart' show WebUri, URLRequest; + +extension WebviewExtension on InAppWebViewController{ + Future?> getCookies(String url) async{ + if(url.contains("https://")){ + url.replaceAll("https://", ""); + } + if(url[url.length-1] == '/'){ + url = url.substring(0, url.length-1); + } + CookieManager cookieManager = CookieManager.instance(); + final cookies = await cookieManager.getCookies(url: WebUri(url)); + Map res = {}; + for(var cookie in cookies){ + res[cookie.name] = cookie.value; + } + return res; + } + + Future getUA() async{ + var res = await evaluateJavascript(source: "navigator.userAgent"); + if(res is String){ + if(res[0] == "'" || res[0] == "\"") { + res = res.substring(1, res.length-1); + } + } + return res is String ? res : null; + } +} + +class AppWebview extends StatefulWidget { + const AppWebview({required this.initialUrl, this.onTitleChange, + this.onNavigation, this.singlePage = false, this.onStarted, super.key}); + + final String initialUrl; + + final void Function(String title, InAppWebViewController controller)? onTitleChange; + + final bool Function(String url)? onNavigation; + + final void Function(InAppWebViewController controller)? onStarted; + + final bool singlePage; + + @override + State createState() => _AppWebviewState(); +} + +class _AppWebviewState extends State { + InAppWebViewController? controller; + + String title = "Webview"; + + double _progress = 0; + + @override + Widget build(BuildContext context) { + final actions = [ + Tooltip( + message: "More", + child: IconButton( + icon: const Icon(Icons.more_horiz), + onPressed: (){ + showMenu(context: context, position: RelativeRect.fromLTRB( + MediaQuery.of(context).size.width, + 0, + MediaQuery.of(context).size.width, + 0 + ), items: [ + PopupMenuItem( + child: Text("Open in browser".tl), + onTap: () async => launchUrlString((await controller?.getUrl())!.path), + ), + PopupMenuItem( + child: Text("Copy link".tl), + onTap: () async => Clipboard.setData(ClipboardData(text: (await controller?.getUrl())!.path)), + ), + PopupMenuItem( + child: Text("Reload".tl), + onTap: () => controller?.reload(), + ), + ]); + }, + ), + ) + ]; + + Widget body = InAppWebView( + initialUrlRequest: URLRequest(url: WebUri(widget.initialUrl)), + onTitleChanged: (c, t){ + if(mounted){ + setState(() { + title = t ?? "Webview"; + }); + } + widget.onTitleChange?.call(title, controller!); + }, + shouldOverrideUrlLoading: (c, r) async { + var res = widget.onNavigation?.call(r.request.url?.toString() ?? "") ?? false; + if(res) { + return NavigationActionPolicy.CANCEL; + } else { + return NavigationActionPolicy.ALLOW; + } + }, + onWebViewCreated: (c){ + controller = c; + widget.onStarted?.call(c); + }, + onProgressChanged: (c, p){ + if(mounted){ + setState(() { + _progress = p / 100; + }); + } + }, + ); + + body = Stack( + children: [ + Positioned.fill(child: body), + if(_progress < 1.0) + const Positioned.fill(child: Center( + child: CircularProgressIndicator())) + ], + ); + + return Scaffold( + appBar: Appbar( + title: Text(title, maxLines: 1, overflow: TextOverflow.ellipsis,), + actions: actions, + ), + body: body + ); + } +} + +class DesktopWebview { + static Future isAvailable() => WebviewWindow.isWebviewAvailable(); + + final String initialUrl; + + final void Function(String title, DesktopWebview controller)? onTitleChange; + + final void Function(String url, DesktopWebview webview)? onNavigation; + + final void Function(DesktopWebview controller)? onStarted; + + final void Function()? onClose; + + DesktopWebview({ + required this.initialUrl, + this.onTitleChange, + this.onNavigation, + this.onStarted, + this.onClose + }); + + Webview? _webview; + + String? _ua; + + String? title; + + void onMessage(String message) { + var json = jsonDecode(message); + if(json is Map){ + if(json["id"] == "document_created"){ + title = json["data"]["title"]; + _ua = json["data"]["ua"]; + onTitleChange?.call(title!, this); + } + } + } + + String? get userAgent => _ua; + + Timer? timer; + + void _runTimer() { + timer ??= Timer.periodic(const Duration(seconds: 2), (t) async { + const js = ''' + function collect() { + if(document.readyState === 'loading') { + return ''; + } + let data = { + id: "document_created", + data: { + title: document.title, + url: location.href, + ua: navigator.userAgent + } + }; + return data; + } + collect(); + '''; + if(_webview != null) { + onMessage(await evaluateJavascript(js) ?? ''); + } + }); + } + + void open() async { + _webview = await WebviewWindow.create(configuration: CreateConfiguration( + useWindowPositionAndSize: true, + userDataFolderWindows: "${App.dataPath}\\webview", + title: "webview", + proxy: AppDio.proxy, + )); + _webview!.addOnWebMessageReceivedCallback(onMessage); + _webview!.setOnNavigation((s) => onNavigation?.call(s, this)); + _webview!.launch(initialUrl, triggerOnUrlRequestEvent: false); + _runTimer(); + _webview!.onClose.then((value) { + _webview = null; + timer?.cancel(); + timer = null; + onClose?.call(); + }); + Future.delayed(const Duration(milliseconds: 200), () { + onStarted?.call(this); + }); + } + + Future evaluateJavascript(String source) { + return _webview!.evaluateJavaScript(source); + } + + Future> getCookies(String url) async{ + var allCookies = await _webview!.getAllCookies(); + var res = {}; + for(var c in allCookies) { + if(_cookieMatch(url, c.domain)){ + res[_removeCode0(c.name)] = _removeCode0(c.value); + } + } + return res; + } + + String _removeCode0(String s) { + var codeUints = List.from(s.codeUnits); + codeUints.removeWhere((e) => e == 0); + return String.fromCharCodes(codeUints); + } + + bool _cookieMatch(String url, String domain) { + domain = _removeCode0(domain); + var host = Uri.parse(url).host; + var acceptedHost = _getAcceptedDomains(host); + return acceptedHost.contains(domain.removeAllBlank); + } + + List _getAcceptedDomains(String host) { + var acceptedDomains = [host]; + var hostParts = host.split("."); + for (var i = 0; i < hostParts.length - 1; i++) { + acceptedDomains.add(".${hostParts.sublist(i).join(".")}"); + } + return acceptedDomains; + } + + void close() { + _webview?.close(); + _webview = null; + } +} \ No newline at end of file diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 71de411..8bc6f9c 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -13,6 +14,9 @@ #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin"); + desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar); g_autoptr(FlPluginRegistrar) flutter_qjs_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterQjsPlugin"); flutter_qjs_plugin_register_with_registrar(flutter_qjs_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 0dfc8a9..1ddbe25 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + desktop_webview_window flutter_qjs screen_retriever sqlite3_flutter_libs diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e95ebaa..3e07b38 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import desktop_webview_window +import flutter_inappwebview_macos import path_provider_foundation import screen_retriever import share_plus @@ -13,6 +15,8 @@ import url_launcher_macos import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin")) + InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) diff --git a/pubspec.lock b/pubspec.lock index b8aeed0..e2171d3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -73,6 +73,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + desktop_webview_window: + dependency: "direct main" + description: + path: "packages/desktop_webview_window" + ref: HEAD + resolved-ref: b8f7e94c576acf4ca3dce5b9f8fb8076e5eaca5e + url: "https://github.com/wgh136/flutter_desktop_webview" + source: git + version: "0.2.4" dio: dependency: "direct main" description: @@ -134,6 +143,70 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_inappwebview: + dependency: "direct main" + description: + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "5f80fd30e208ddded7dbbcd0d569e7995f9f63d45ea3f548d8dd4c0b473fb4c8" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_windows: + dependency: transitive + description: + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" flutter_lints: dependency: "direct dev" description: @@ -218,18 +291,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -258,18 +331,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: "direct main" description: @@ -473,10 +546,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -569,10 +642,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" web: dependency: transitive description: @@ -614,5 +687,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.4 <4.0.0" - flutter: ">=3.22.3" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.3" diff --git a/pubspec.yaml b/pubspec.yaml index 2e88fe5..8c905f8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,8 +5,8 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=3.4.4 <4.0.0' - flutter: 3.22.3 + sdk: '>=3.5.0 <4.0.0' + flutter: 3.24.3 dependencies: flutter: @@ -43,6 +43,11 @@ dependencies: flutter_reorderable_grid_view: 5.0.1 yaml: any uuid: ^4.5.1 + desktop_webview_window: + git: + url: https://github.com/wgh136/flutter_desktop_webview + path: packages/desktop_webview_window + flutter_inappwebview: ^6.1.5 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 3175947..fe07201 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,8 @@ #include "generated_plugin_registrant.h" +#include +#include #include #include #include @@ -14,6 +16,10 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { + DesktopWebviewWindowPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin")); + FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); FlutterQjsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterQjsPlugin")); ScreenRetrieverPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index f8a619c..b64cb9b 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + desktop_webview_window + flutter_inappwebview_windows flutter_qjs screen_retriever share_plus