add webview for linux

This commit is contained in:
kodjomoustapha 2024-11-13 10:53:36 +01:00
parent a38bfc8b83
commit 32afc3c71e
10 changed files with 265 additions and 238 deletions

View file

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:bot_toast/bot_toast.dart';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -30,6 +31,11 @@ late Isar isar;
WebViewEnvironment? webViewEnvironment;
void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isLinux) {
if (runWebViewTitleBarWidget(args)) {
return;
}
}
MediaKit.ensureInitialized();
await RustLib.init();
if (!(Platform.isAndroid || Platform.isIOS)) {

View file

@ -48,8 +48,6 @@ import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import 'package:share_plus/share_plus.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../utils/constant.dart';
class MangaDetailView extends ConsumerStatefulWidget {
@ -1559,22 +1557,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
'sourceId': source.id.toString(),
'title': manga.name!
};
if (Platform.isLinux) {
final urll = Uri.parse(url);
if (!await launchUrl(
urll,
mode: LaunchMode.inAppBrowserView,
)) {
if (!await launchUrl(
urll,
mode: LaunchMode.externalApplication,
)) {
throw 'Could not launch $url';
}
}
} else {
context.push("/mangawebview", extra: data);
}
context.push("/mangawebview", extra: data);
},
child: Column(
children: [

View file

@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@ -25,7 +24,6 @@ import 'package:mangayomi/modules/manga/home/widget/mangas_card_selector.dart';
import 'package:mangayomi/modules/widgets/gridview_widget.dart';
import 'package:mangayomi/modules/widgets/manga_image_card_widget.dart';
import 'package:mangayomi/utils/global_style.dart';
import 'package:url_launcher/url_launcher.dart';
class MangaHomeScreen extends ConsumerStatefulWidget {
final Source source;
@ -267,22 +265,7 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
'sourceId': source.id.toString(),
'title': ''
};
if (Platform.isLinux) {
final url = Uri.parse(baseUrl);
if (!await launchUrl(
url,
mode: LaunchMode.inAppBrowserView,
)) {
if (!await launchUrl(
url,
mode: LaunchMode.externalApplication,
)) {
throw 'Could not launch $url';
}
}
} else {
context.push("/mangawebview", extra: data);
}
context.push("/mangawebview", extra: data);
} else {
final res =
await context.push('/extension_detail', extra: source);
@ -623,22 +606,7 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
"hasCloudFlare":
source.hasCloudflare ?? false
};
if (Platform.isLinux) {
final url = Uri.parse(baseUrl);
if (!await launchUrl(
url,
mode: LaunchMode.inAppBrowserView,
)) {
if (!await launchUrl(
url,
mode: LaunchMode.externalApplication,
)) {
throw 'Could not launch $url';
}
}
} else {
context.push("/mangawebview", extra: data);
}
context.push("/mangawebview", extra: data);
},
icon: Icon(
Icons.public,

View file

@ -41,7 +41,6 @@ import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:window_manager/window_manager.dart';
typedef DoubleClickAnimationListener = void Function();
@ -1344,22 +1343,7 @@ class _MangaChapterPageGalleryState
'sourceId': source.id.toString(),
'title': chapter.name!
};
if (Platform.isLinux) {
final urll = Uri.parse(url);
if (!await launchUrl(
urll,
mode: LaunchMode.inAppBrowserView,
)) {
if (!await launchUrl(
urll,
mode: LaunchMode.externalApplication,
)) {
throw 'Could not launch $url';
}
}
} else {
context.push("/mangawebview", extra: data);
}
context.push("/mangawebview", extra: data);
},
icon: const Icon(Icons.public)),
],

View file

@ -1,3 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -20,6 +23,41 @@ class MangaWebView extends ConsumerStatefulWidget {
class _MangaWebViewState extends ConsumerState<MangaWebView> {
double _progress = 0;
bool isNotDesktop = false;
@override
void initState() {
if (Platform.isLinux) {
_runWebViewDesktop();
} else {
setState(() {
isNotDesktop = true;
});
}
super.initState();
}
Webview? _desktopWebview;
_runWebViewDesktop() async {
_desktopWebview = await WebviewWindow.create();
final timer = Timer.periodic(const Duration(seconds: 1), (timer) async {
try {
final cookieList = await _desktopWebview!.getAllCookies();
final ua =
await _desktopWebview!.evaluateJavaScript("navigator.userAgent") ??
"";
final cookie = cookieList.map((e) => "${e.name}=${e.value}").join(";");
await MClient.setCookie(_url, ua, null, cookie: cookie);
} catch (_) {}
});
_desktopWebview!
..setBrightness(Brightness.dark)
..launch(widget.url)
..onClose.whenComplete(() {
timer.cancel();
Navigator.pop(context);
});
}
InAppWebViewController? _webViewController;
late String _url = widget.url;
@ -29,170 +67,191 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
@override
Widget build(BuildContext context) {
final l10n = l10nLocalizations(context);
return Material(
child: SafeArea(
child: WillPopScope(
onWillPop: () async {
final canGoback = await _webViewController?.canGoBack();
if (canGoback ?? false) {
_webViewController?.goBack();
} else if (context.mounted) {
context.pop();
}
return false;
},
child: Column(
children: [
SizedBox(
height: AppBar().preferredSize.height,
child: Row(
return !isNotDesktop
? Scaffold(
appBar: AppBar(
title: Text(
_title,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold),
),
leading: IconButton(
onPressed: () {
_desktopWebview!.close();
Navigator.pop(context);
},
icon: const Icon(Icons.close)),
),
)
: Material(
child: SafeArea(
child: WillPopScope(
onWillPop: () async {
final canGoback = await _webViewController?.canGoBack();
if (canGoback ?? false) {
_webViewController?.goBack();
} else if (context.mounted) {
context.pop();
}
return false;
},
child: Column(
children: [
Expanded(
child: ListTile(
dense: true,
subtitle: Text(
_url,
style: const TextStyle(
fontSize: 10, overflow: TextOverflow.ellipsis),
),
title: Text(
_title,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold),
),
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(Icons.close)),
SizedBox(
height: AppBar().preferredSize.height,
child: Row(
children: [
Expanded(
child: ListTile(
dense: true,
subtitle: Text(
_url,
style: const TextStyle(
fontSize: 10,
overflow: TextOverflow.ellipsis),
),
title: Text(
_title,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold),
),
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(Icons.close)),
),
),
IconButton(
icon: Icon(Icons.arrow_back,
color: _canGoback ? null : Colors.grey),
onPressed: _canGoback
? () {
_webViewController?.goBack();
}
: null,
),
IconButton(
icon: Icon(Icons.arrow_forward,
color: _canGoForward ? null : Colors.grey),
onPressed: _canGoForward
? () {
_webViewController?.goForward();
}
: null,
),
PopupMenuButton(
popUpAnimationStyle: popupAnimationStyle,
itemBuilder: (context) {
return [
PopupMenuItem<int>(
value: 0, child: Text(l10n!.refresh)),
PopupMenuItem<int>(
value: 1, child: Text(l10n.share)),
PopupMenuItem<int>(
value: 2,
child: Text(l10n.open_in_browser)),
PopupMenuItem<int>(
value: 3, child: Text(l10n.clear_cookie)),
];
},
onSelected: (value) async {
if (value == 0) {
_webViewController?.reload();
} else if (value == 1) {
Share.share(_url);
} else if (value == 2) {
await InAppBrowser.openWithSystemBrowser(
url: WebUri(_url));
} else if (value == 3) {
CookieManager.instance().deleteAllCookies();
MClient.deleteAllCookies(_url);
}
}),
],
),
),
IconButton(
icon: Icon(Icons.arrow_back,
color: _canGoback ? null : Colors.grey),
onPressed: _canGoback
? () {
_webViewController?.goBack();
}
: null,
),
IconButton(
icon: Icon(Icons.arrow_forward,
color: _canGoForward ? null : Colors.grey),
onPressed: _canGoForward
? () {
_webViewController?.goForward();
}
: null,
),
PopupMenuButton(
popUpAnimationStyle: popupAnimationStyle,
itemBuilder: (context) {
return [
PopupMenuItem<int>(
value: 0, child: Text(l10n!.refresh)),
PopupMenuItem<int>(
value: 1, child: Text(l10n.share)),
PopupMenuItem<int>(
value: 2, child: Text(l10n.open_in_browser)),
PopupMenuItem<int>(
value: 3, child: Text(l10n.clear_cookie)),
];
_progress < 1.0
? LinearProgressIndicator(value: _progress)
: Container(),
Expanded(
child: InAppWebView(
webViewEnvironment: webViewEnvironment,
onWebViewCreated: (controller) async {
_webViewController = controller;
},
onSelected: (value) async {
if (value == 0) {
_webViewController?.reload();
} else if (value == 1) {
Share.share(_url);
} else if (value == 2) {
await InAppBrowser.openWithSystemBrowser(
url: WebUri(_url));
} else if (value == 3) {
CookieManager.instance().deleteAllCookies();
MClient.deleteAllCookies(_url);
onLoadStart: (controller, url) async {
setState(() {
_url = url.toString();
});
},
shouldOverrideUrlLoading:
(controller, navigationAction) async {
var uri = navigationAction.request.url!;
if (![
"http",
"https",
"file",
"chrome",
"data",
"javascript",
"about"
].contains(uri.scheme)) {
if (await canLaunchUrl(uri)) {
// Launch the App
await launchUrl(
uri,
);
// and cancel the request
return NavigationActionPolicy.CANCEL;
}
}
}),
return NavigationActionPolicy.ALLOW;
},
onLoadStop: (controller, url) async {
if (mounted) {
setState(() {
_url = url.toString();
});
}
},
onProgressChanged: (controller, progress) async {
if (mounted) {
setState(() {
_progress = progress / 100;
});
}
},
onUpdateVisitedHistory:
(controller, url, isReload) async {
final ua = await controller.evaluateJavascript(
source: "navigator.userAgent") ??
"";
await MClient.setCookie(
url.toString(), ua, controller);
final canGoback = await controller.canGoBack();
final canGoForward = await controller.canGoForward();
final title = await controller.getTitle();
if (mounted) {
setState(() {
_url = url.toString();
_title = title!;
_canGoback = canGoback;
_canGoForward = canGoForward;
});
}
},
initialUrlRequest: URLRequest(url: WebUri(widget.url)),
),
),
],
),
),
_progress < 1.0
? LinearProgressIndicator(value: _progress)
: Container(),
Expanded(
child: InAppWebView(
webViewEnvironment: webViewEnvironment,
onWebViewCreated: (controller) async {
_webViewController = controller;
},
onLoadStart: (controller, url) async {
setState(() {
_url = url.toString();
});
},
shouldOverrideUrlLoading:
(controller, navigationAction) async {
var uri = navigationAction.request.url!;
if (![
"http",
"https",
"file",
"chrome",
"data",
"javascript",
"about"
].contains(uri.scheme)) {
if (await canLaunchUrl(uri)) {
// Launch the App
await launchUrl(
uri,
);
// and cancel the request
return NavigationActionPolicy.CANCEL;
}
}
return NavigationActionPolicy.ALLOW;
},
onLoadStop: (controller, url) async {
if (mounted) {
setState(() {
_url = url.toString();
});
}
},
onProgressChanged: (controller, progress) async {
if (mounted) {
setState(() {
_progress = progress / 100;
});
}
},
onUpdateVisitedHistory: (controller, url, isReload) async {
final ua = await controller.evaluateJavascript(
source: "navigator.userAgent") ??
"";
await MClient.setCookie(url.toString(), ua, controller);
final canGoback = await controller.canGoBack();
final canGoForward = await controller.canGoForward();
final title = await controller.getTitle();
if (mounted) {
setState(() {
_url = url.toString();
_title = title!;
_canGoback = canGoback;
_canGoForward = canGoForward;
});
}
},
initialUrlRequest: URLRequest(url: WebUri(widget.url)),
),
),
],
),
),
),
);
),
);
}
}

View file

@ -67,16 +67,24 @@ class MClient {
}
static Future<void> setCookie(String url, String ua,
flutter_inappwebview.InAppWebViewController webViewController,
flutter_inappwebview.InAppWebViewController? webViewController,
{String? cookie}) async {
List<String> cookies = [];
cookies = (await flutter_inappwebview.CookieManager.instance(
webViewEnvironment: webViewEnvironment)
.getCookies(
url: flutter_inappwebview.WebUri(url),
webViewController: webViewController))
.map((e) => "${e.name}=${e.value}")
.toList();
if (Platform.isLinux) {
cookies = cookie
?.split(RegExp('(?<=)(,)(?=[^;]+?=)'))
.where((cookie) => cookie.isNotEmpty)
.toList() ??
[];
} else {
cookies = (await flutter_inappwebview.CookieManager.instance(
webViewEnvironment: webViewEnvironment)
.getCookies(
url: flutter_inappwebview.WebUri(url),
webViewController: webViewController))
.map((e) => "${e.name}=${e.value}")
.toList();
}
if (cookies.isNotEmpty) {
final host = Uri.parse(url).host;
final newCookie = cookies.join("; ");

View file

@ -6,6 +6,7 @@
#include "generated_plugin_registrant.h"
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <flutter_qjs/flutter_qjs_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
@ -16,6 +17,9 @@
#include <window_to_front/window_to_front_plugin.h>
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);

View file

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window
flutter_qjs
isar_flutter_libs
media_kit_libs_linux

View file

@ -318,6 +318,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.10"
desktop_webview_window:
dependency: "direct main"
description:
path: "packages/desktop_webview_window"
ref: main
resolved-ref: "2aa8d449881974182d033df9635cf7c198d2553a"
url: "https://github.com/kodjodevf/desktop_webview_window.git"
source: git
version: "0.2.4"
directed_graph:
dependency: transitive
description:
@ -671,10 +680,10 @@ packages:
dependency: "direct main"
description:
name: go_router
sha256: ce89c5a993ca5eea74535f798478502c30a625ecb10a1de4d7fef5cd1bcac2a4
sha256: "8ae664a70174163b9f65ea68dd8673e29db8f9095de7b5cd00e167c621f4fef5"
url: "https://pub.dev"
source: hosted
version: "14.4.1"
version: "14.6.0"
google_fonts:
dependency: "direct main"
description:
@ -1614,10 +1623,10 @@ packages:
dependency: transitive
description:
name: url_launcher_linux
sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
version: "3.2.1"
url_launcher_macos:
dependency: transitive
description:

View file

@ -68,6 +68,11 @@ dependencies:
pseudom: ^1.0.1
path: ^1.9.0
freezed_annotation: ^2.0.0
desktop_webview_window:
git:
url: https://github.com/kodjodevf/desktop_webview_window.git
path: packages/desktop_webview_window
ref: main
dependency_overrides:
http: ^1.2.1