初始化提交
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import 'package:get/get.dart';
|
||||
import '../controllers/kr_webview_controller.dart';
|
||||
|
||||
class KRWebViewBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<KRWebViewController>(
|
||||
() => KRWebViewController(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:kaer_with_panels/app/services/api_service/api.dart';
|
||||
import 'package:kaer_with_panels/app/services/api_service/kr_web_api.dart';
|
||||
import 'package:kaer_with_panels/app/localization/app_translations.dart';
|
||||
|
||||
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
|
||||
|
||||
/// WebView 控制器
|
||||
/// 用于管理 WebView 的状态和行为
|
||||
class KRWebViewController extends GetxController {
|
||||
// 页面加载状态
|
||||
final RxBool kr_isLoading = true.obs;
|
||||
|
||||
// 页面标题
|
||||
final RxString kr_title = ''.obs;
|
||||
|
||||
// WebView 控制器
|
||||
late final WebViewController kr_webViewController;
|
||||
|
||||
// 默认URL
|
||||
static const String kr_defaultUrl = '';
|
||||
|
||||
final String kr_url = Get.arguments['url'] as String;
|
||||
|
||||
// Web API 实例
|
||||
final KRWebApi _kr_webApi = KRWebApi();
|
||||
|
||||
// 内容类型
|
||||
final RxBool kr_isHtml = false.obs;
|
||||
final RxBool kr_isMarkdown = false.obs;
|
||||
final RxString kr_content = ''.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// 根据 URL 类型决定初始化方式
|
||||
if (kr_url.contains(Api.kr_getSiteTos) || kr_url.contains(Api.kr_getSitePrivacy)) {
|
||||
// 用户协议和隐私政策页面,直接获取文本内容
|
||||
if (kr_url.contains(Api.kr_getSiteTos)) {
|
||||
kr_title.value = AppTranslations.kr_login.termsOfService;
|
||||
} else {
|
||||
kr_title.value = AppTranslations.kr_login.privacyPolicy;
|
||||
}
|
||||
kr_getWebText();
|
||||
} else {
|
||||
// 其他页面,初始化 WebView
|
||||
kr_initWebView();
|
||||
}
|
||||
}
|
||||
|
||||
/// 初始化 WebView
|
||||
void kr_initWebView() {
|
||||
kr_webViewController = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onNavigationRequest: (NavigationRequest request) async {
|
||||
// 只在移动平台处理支付应用跳转
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||
KRLogUtil.kr_i('处理支付链接: ${request.url}', tag: 'WebViewController');
|
||||
// 处理支付链接
|
||||
if (await kr_handleUrlLaunch(request.url)) {
|
||||
return NavigationDecision.prevent;
|
||||
}
|
||||
}
|
||||
return NavigationDecision.navigate;
|
||||
},
|
||||
onPageStarted: kr_handlePageStarted,
|
||||
onPageFinished: kr_handlePageFinished,
|
||||
),
|
||||
);
|
||||
|
||||
// 检查是否是用户协议或隐私政策
|
||||
if (kr_url.contains(Api.kr_getSiteTos) || kr_url.contains(Api.kr_getSitePrivacy)) {
|
||||
kr_getWebText();
|
||||
} else {
|
||||
kr_webViewController.loadRequest(Uri.parse(kr_url));
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取网页文本内容并加载到 WebView
|
||||
Future<void> kr_getWebText() async {
|
||||
try {
|
||||
final response = await _kr_webApi.kr_getWebText(kr_url);
|
||||
response.fold(
|
||||
(error) async {
|
||||
KRLogUtil.kr_e('获取网页内容失败: $error', tag: 'WebViewController');
|
||||
// 如果获取失败,直接设置错误内容
|
||||
kr_content.value = 'Failed to load, please try again later';
|
||||
kr_isLoading.value = false;
|
||||
},
|
||||
(content) async {
|
||||
KRLogUtil.kr_i('获取到内容: $content', tag: 'WebViewController');
|
||||
// 判断内容类型,优先判断 Markdown
|
||||
kr_isMarkdown.value = content.contains('**') ||
|
||||
content.contains('*') ||
|
||||
content.contains('#') ||
|
||||
content.contains('- ') ||
|
||||
content.contains('[');
|
||||
kr_isHtml.value = !kr_isMarkdown.value && content.contains('<') && content.contains('>');
|
||||
|
||||
KRLogUtil.kr_i('内容类型 - Markdown: ${kr_isMarkdown.value}, HTML: ${kr_isHtml.value}', tag: 'WebViewController');
|
||||
|
||||
if (kr_isMarkdown.value) {
|
||||
// 如果是 Markdown 内容,直接使用
|
||||
kr_content.value = content;
|
||||
} else if (kr_isHtml.value) {
|
||||
// 如果是 HTML 内容,直接使用
|
||||
kr_content.value = content;
|
||||
} else {
|
||||
// 如果是普通文本,直接使用
|
||||
kr_content.value = content;
|
||||
}
|
||||
|
||||
kr_isLoading.value = false;
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('获取网页内容出错: $e', tag: 'WebViewController');
|
||||
kr_content.value = 'Loading error, please try again later';
|
||||
kr_isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理页面开始加载事件
|
||||
void kr_handlePageStarted(String url) {
|
||||
kr_isLoading.value = true;
|
||||
}
|
||||
|
||||
/// 处理页面加载完成事件
|
||||
void kr_handlePageFinished(String url) async {
|
||||
kr_isLoading.value = false;
|
||||
await kr_updateTitle();
|
||||
}
|
||||
|
||||
/// 更新页面标题
|
||||
Future<void> kr_updateTitle() async {
|
||||
final String? kr_pageTitle = await kr_webViewController.getTitle();
|
||||
kr_title.value = kr_pageTitle ?? '';
|
||||
}
|
||||
|
||||
/// 重新加载页面
|
||||
Future<void> kr_reloadPage() async {
|
||||
await kr_webViewController.reload();
|
||||
}
|
||||
|
||||
/// 加载新的URL
|
||||
Future<void> kr_loadUrl(String url) async {
|
||||
await kr_webViewController.loadRequest(Uri.parse(url));
|
||||
}
|
||||
|
||||
/// 处理URL启动
|
||||
Future<bool> kr_handleUrlLaunch(String url) async {
|
||||
try {
|
||||
KRLogUtil.kr_i('正在处理URL跳转: $url', tag: 'WebViewController');
|
||||
final uri = Uri.parse(url);
|
||||
// 处理支付应用和外部链接
|
||||
if (uri.scheme == 'alipays' ||
|
||||
uri.scheme == 'alipay' ||
|
||||
uri.scheme == 'weixin' ||
|
||||
uri.scheme == 'wx') {
|
||||
KRLogUtil.kr_i('检测到支付应用scheme: ${uri.scheme}', tag: 'WebViewController');
|
||||
// 尝试打开支付应用
|
||||
if (await canLaunchUrl(uri)) {
|
||||
return await launchUrl(
|
||||
uri,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
// 如果支付应用无法打开,尝试使用外部浏览器打开
|
||||
final httpUri = Uri.parse('https://${uri.host}${uri.path}?${uri.query}');
|
||||
KRLogUtil.kr_i('尝试使用浏览器打开: $httpUri', tag: 'WebViewController');
|
||||
if (await canLaunchUrl(httpUri)) {
|
||||
return await launchUrl(
|
||||
httpUri,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
KRLogUtil.kr_e('无法启动URL: $url', tag: 'WebViewController');
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('URL跳转错误: $e', tag: 'WebViewController');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import '../../../widgets/kr_app_text_style.dart';
|
||||
import '../controllers/kr_webview_controller.dart';
|
||||
import '../../../services/api_service/api.dart';
|
||||
import '../../../utils/kr_log_util.dart';
|
||||
|
||||
/// WebView 页面组件
|
||||
class KRWebView extends GetView<KRWebViewController> {
|
||||
const KRWebView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_back_ios,
|
||||
size: 20.sp,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
controller.kr_title.value,
|
||||
style: KrAppTextStyle(
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
body: _buildBody(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建主体内容
|
||||
Widget _buildBody() {
|
||||
return Stack(
|
||||
children: [
|
||||
_buildContent(),
|
||||
_buildLoadingIndicator(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建内容组件
|
||||
Widget _buildContent() {
|
||||
if (controller.kr_url.contains(Api.kr_getSiteTos) ||
|
||||
controller.kr_url.contains(Api.kr_getSitePrivacy)) {
|
||||
return _buildProtocolContent();
|
||||
} else {
|
||||
return _buildWebView();
|
||||
}
|
||||
}
|
||||
|
||||
/// 构建协议内容
|
||||
Widget _buildProtocolContent() {
|
||||
return Obx(() {
|
||||
if (controller.kr_isHtml.value) {
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Html(
|
||||
data: controller.kr_content.value,
|
||||
style: {
|
||||
'body': Style(
|
||||
margin: Margins.all(0),
|
||||
padding: HtmlPaddings.all(0),
|
||||
fontSize: FontSize(14.sp),
|
||||
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
lineHeight: LineHeight(1.4),
|
||||
),
|
||||
'p': Style(
|
||||
margin: Margins.only(bottom: 8.h),
|
||||
),
|
||||
'b': Style(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
'i': Style(
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
'a': Style(
|
||||
color: Colors.blue,
|
||||
textDecoration: TextDecoration.underline,
|
||||
),
|
||||
},
|
||||
shrinkWrap: true,
|
||||
),
|
||||
);
|
||||
} else if (controller.kr_isMarkdown.value) {
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: MarkdownBody(
|
||||
data: controller.kr_content.value,
|
||||
styleSheet: MarkdownStyleSheet(
|
||||
p: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
),
|
||||
strong: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
),
|
||||
em: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontStyle: FontStyle.italic,
|
||||
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
),
|
||||
a: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.blue,
|
||||
decoration: TextDecoration.underline,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Text(
|
||||
controller.kr_content.value,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 构建 WebView 组件
|
||||
Widget _buildWebView() {
|
||||
return WebViewWidget(
|
||||
controller: controller.kr_webViewController,
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建加载指示器
|
||||
Widget _buildLoadingIndicator() {
|
||||
return Obx(
|
||||
() => controller.kr_isLoading.value
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 显示错误提示
|
||||
void _showErrorSnackbar(String title, String message) {
|
||||
Get.snackbar(
|
||||
title,
|
||||
message,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user