use linux_webview_window to support linux geetest (#1889)

* use webview_all to support linux geetest

* use desktop_webview_window to support linux geetest

* remove previous change in my_application.cc

* update

---------

Co-authored-by: dom <githubaccount56556@proton.me>
This commit is contained in:
Origuchi
2026-04-16 18:05:25 +08:00
committed by GitHub
parent e778f2b463
commit 2232bc009d
7 changed files with 163 additions and 25 deletions

View File

@@ -313,9 +313,6 @@ class LoginPageController extends GetxController
}
if (data['status'] == 2) {
SmartDialog.showToast(data['message']);
if (Platform.isLinux) {
return;
}
// return;
//{"code":0,"message":"0","ttl":1,"data":{"status":2,"message":"本次登录环境存在风险, 需使用手机号进行验证或绑定","url":"https://passport.bilibili.com/h5-app/passport/risk/verify?tmp_token=9e785433940891dfa78f033fb7928181&request_id=e5a6d6480df04097870be56c6e60f7ef&source=risk","token_info":null,"cookie_info":null,"sso":null,"is_new":false,"is_tourist":false}}
String url = data['url']!;
@@ -520,7 +517,7 @@ class LoginPageController extends GetxController
case 0:
// login success
break;
case -105 when (!Platform.isLinux):
case -105:
String captureUrl = res['data']['url'];
Uri captureUri = Uri.parse(captureUrl);
captchaData.token = captureUri.queryParameters['recaptcha_token']!;

View File

@@ -1,24 +1,44 @@
import 'dart:convert';
import 'dart:convert' show jsonDecode, jsonEncode;
import 'dart:io' show Platform, Directory, File;
import 'package:PiliPlus/http/browser_ua.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/main.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:get/get.dart';
class GeetestWebviewDialog extends StatelessWidget {
class GeetestWebviewDialog extends StatefulWidget {
const GeetestWebviewDialog(this.gt, this.challenge, {super.key});
final String gt;
final String challenge;
@override
State<GeetestWebviewDialog> createState() => _GeetestWebviewDialogState();
}
class _GeetestWebviewDialogState extends State<GeetestWebviewDialog> {
static const _geetestJsUri =
'https://static.geetest.com/static/js/fullpage.0.0.0.js';
late final Future<LoadingState<String>> _future;
Webview? _linuxWebview;
late bool _linuxWebviewLoading = true;
@override
void initState() {
super.initState();
_future = _getConfig(widget.gt, widget.challenge);
if (Platform.isLinux) {
_initLinuxWebview();
}
}
static Future<LoadingState<String>> _getConfig(
String gt,
String challenge,
@@ -62,9 +82,126 @@ class GeetestWebviewDialog extends StatelessWidget {
return Error(res.data['message']);
}
Future<void> _initLinuxWebview() async {
final config = await _future;
if (!mounted) {
return;
}
if (config is Error) {
config.toast();
Get.back();
return;
}
final response = (config as Success<String>).response;
_linuxWebview = await WebviewWindow.create(
configuration: const CreateConfiguration(
windowWidth: 300,
windowHeight: 400,
title: "验证码",
),
);
if (!mounted) {
_closeLinuxWebview();
return;
}
_linuxWebview!.addOnWebMessageReceivedCallback((msg) {
final msgStr = msg.toString();
if (msgStr.startsWith("success:")) {
final dataStr = msgStr.substring("success:".length);
try {
final data = jsonDecode(dataStr);
Get.back(result: data);
} catch (e) {
debugPrint('geetest decode error: $e');
}
} else if (msgStr.startsWith("error:")) {
debugPrint('geetest error: $msgStr');
}
});
_linuxWebview!.onClose.whenComplete(() {
if (mounted) {
Get.back();
}
});
final html =
'''
<!DOCTYPE html><html><head></head><body>
<script src="$_geetestJsUri"></script>
<script>
function R(n,o){
window.webkit.messageHandlers.msgToNative.postMessage(n + ':' + JSON.stringify(o));
}
let t=Geetest($response).onSuccess(()=>R("success",t.getValidate())).onError((o)=>R("error",o));
t.onReady(()=>t.verify());
</script>
</body></html>
''';
final tempDir = Directory.systemTemp;
final file = File(
'${tempDir.path}/geetest_${DateTime.now().millisecondsSinceEpoch}.html',
);
await file.writeAsString(html);
if (!mounted) {
_closeLinuxWebview();
return;
}
_linuxWebview!.launch('file://${file.path}');
if (mounted) {
setState(() {
_linuxWebviewLoading = false;
});
}
}
void _closeLinuxWebview() {
_linuxWebview?.close();
_linuxWebview = null;
}
@override
void dispose() {
_closeLinuxWebview();
super.dispose();
}
@override
Widget build(BuildContext context) {
final future = _getConfig(gt, challenge);
if (Platform.isLinux) {
return AlertDialog(
title: const Text('验证码'),
content: SizedBox(
width: 300,
height: 400,
child: Center(
child: _linuxWebviewLoading
? const CircularProgressIndicator()
: const Text('请在弹出的新窗口中完成验证'),
),
),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(color: ColorScheme.of(context).outline),
),
),
],
);
}
return AlertDialog(
title: const Text('验证码'),
content: SizedBox(
@@ -108,7 +245,8 @@ class GeetestWebviewDialog extends StatelessWidget {
);
},
onLoadStop: (ctr, _) async {
final config = await future;
final config = await _future;
if (!mounted) return;
if (config case Success(:final response)) {
ctr.evaluateJavascript(
source:

View File

@@ -1,4 +1,3 @@
import 'dart:io';
import 'dart:ui';
import 'package:PiliPlus/common/constants.dart';
@@ -380,7 +379,6 @@ class _LoginPageState extends State<LoginPage> {
Builder(
builder: (context) {
return PopupMenuButton(
enabled: !Platform.isLinux,
padding: EdgeInsets.zero,
tooltip:
'选择国际冠码,'
@@ -428,7 +426,6 @@ class _LoginPageState extends State<LoginPage> {
const SizedBox(width: 6),
Expanded(
child: TextField(
enabled: !Platform.isLinux,
controller: _loginPageCtr.telTextController,
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
@@ -460,7 +457,6 @@ class _LoginPageState extends State<LoginPage> {
children: [
Expanded(
child: TextField(
enabled: !Platform.isLinux,
controller: _loginPageCtr.smsCodeTextController,
decoration: const InputDecoration(
prefixIcon: Icon(Icons.sms_outlined),
@@ -475,11 +471,9 @@ class _LoginPageState extends State<LoginPage> {
),
Obx(
() => TextButton.icon(
onPressed: !Platform.isLinux
? _loginPageCtr.smsSendCooldown > 0
onPressed: _loginPageCtr.smsSendCooldown > 0
? null
: _loginPageCtr.sendSmsCode
: null,
: _loginPageCtr.sendSmsCode,
icon: const Icon(Icons.send),
label: Text(
_loginPageCtr.smsSendCooldown > 0
@@ -494,7 +488,7 @@ class _LoginPageState extends State<LoginPage> {
),
const SizedBox(height: 20),
OutlinedButton.icon(
onPressed: !Platform.isLinux ? _loginPageCtr.loginBySmsCode : null,
onPressed: _loginPageCtr.loginBySmsCode,
icon: const Icon(Icons.login),
label: const Text('登录'),
),

View File

@@ -493,10 +493,6 @@ abstract final class RequestUtils {
String vVoucher,
ValueChanged<String> onSuccess,
) async {
if (Platform.isLinux) {
return;
}
final res = await ValidateHttp.gaiaVgateRegister(vVoucher);
if (!res.isSuccess) {
res.toast();

View File

@@ -46,8 +46,8 @@ static void my_application_activate(GApplication *application) {
// if future cases occur).
const gboolean use_header_bar = [window]() -> gboolean {
if (g_file_test(
g_build_filename(g_get_user_data_dir(), "com.example.piliplus", "use_ssd", NULL),
if (g_file_test(g_build_filename(g_get_user_data_dir(),
"com.example.piliplus", "use_ssd", NULL),
G_FILE_TEST_EXISTS))
return FALSE;

View File

@@ -387,6 +387,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.12"
desktop_webview_window:
dependency: "direct main"
description:
path: "."
ref: main
resolved-ref: b429f8ba6b8a99cd0f7eb5fe8d1621f325635c3d
url: "https://github.com/Predidit/linux_webview_window"
source: git
version: "0.2.4"
device_info_plus:
dependency: "direct main"
description:

View File

@@ -79,6 +79,10 @@ dependencies:
# 浏览器
# webview_flutter: ^4.10.0
flutter_inappwebview: ^6.1.5
desktop_webview_window:
git:
url: https://github.com/Predidit/linux_webview_window
ref: main
# 解决sliver滑动不同步
# extended_nested_scroll_view: ^6.2.1
extended_nested_scroll_view: