From fce96d497689b18959995baddcbe1a45eccbfcf0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 20 Feb 2024 23:54:45 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E8=AF=9D?= =?UTF-8?q?=E9=A2=98=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/reply/content.dart | 3 +++ .../detail/reply/widgets/reply_item.dart | 20 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/models/video/reply/content.dart b/lib/models/video/reply/content.dart index 9180ec97..d62a4bca 100644 --- a/lib/models/video/reply/content.dart +++ b/lib/models/video/reply/content.dart @@ -9,6 +9,7 @@ class ReplyContent { this.vote, this.richText, this.isText, + this.topicsMeta, }); String? message; @@ -20,6 +21,7 @@ class ReplyContent { Map? vote; Map? richText; bool? isText; + Map? topicsMeta; ReplyContent.fromJson(Map json) { message = json['message'] @@ -39,6 +41,7 @@ class ReplyContent { richText = json['rich_text'] ?? {}; // 不包含@ 笔记 图片的时候,文字可折叠 isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty; + topicsMeta = json['topics_meta'] ?? {}; } } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 7991a366..d0b2b0e5 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -582,6 +582,7 @@ InlineSpan buildContent( // 构建正则表达式 final List specialTokens = [ ...content.emote.keys, + ...content.topicsMeta?.keys?.map((e) => '#$e#') ?? [], ...content.atNameToMid.keys.map((e) => '@$e'), ...content.jumpUrl.keys.map((e) => e.replaceAll('?', '\\?').replaceAll('+', '\\+').replaceAll('*', '\\*')), @@ -664,7 +665,7 @@ InlineSpan buildContent( ), ); } else { - // print("matchStr=$matchStr"); + print("matchStr=$matchStr"); String appUrlSchema = ''; final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, defaultValue: false) as bool; @@ -727,6 +728,23 @@ InlineSpan buildContent( ); // 只显示一次 matchedStrs.add(matchStr); + } else if (content + .topicsMeta[matchStr.substring(1, matchStr.length - 1)] != + null) { + spanChilds.add( + TextSpan( + text: matchStr, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + final String topic = + matchStr.substring(1, matchStr.length - 1); + Get.toNamed('/searchResult', parameters: {'keyword': topic}); + }, + ), + ); } else { addPlainTextSpan(matchStr); } From a0f92df5b558cfb002c9c8142282bd4eae2835d6 Mon Sep 17 00:00:00 2001 From: My-Responsitories <107370289+My-Responsitories@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:16:38 +0800 Subject: [PATCH 2/5] fix dynamic risk challenge --- lib/http/api.dart | 6 ++++++ lib/http/init.dart | 42 ++++++++++++++++++++++++++++++++++++------ lib/http/member.dart | 8 +++++++- lib/utils/utils.dart | 15 ++++++++++++++- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 4b6bbeb8..2e758439 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -477,4 +477,10 @@ class Api { /// 获取未读动态数 static const getUnreadDynamic = '/x/web-interface/dynamic/entrance'; + + /// 用户动态主页 + static const dynamicSpmPrefix = 'https://space.bilibili.com/1/dynamic'; + + /// 激活buvid3 + static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi'; } diff --git a/lib/http/init.dart b/lib/http/init.dart index dcc9f402..a0b36369 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -1,7 +1,9 @@ // ignore_for_file: avoid_print import 'dart:async'; +import 'dart:convert'; import 'dart:developer'; import 'dart:io'; +import 'dart:math' show Random; import 'package:cookie_jar/cookie_jar.dart'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; @@ -11,6 +13,7 @@ import 'package:hive/hive.dart'; import 'package:pilipala/utils/id_utils.dart'; import '../utils/storage.dart'; import '../utils/utils.dart'; +import 'api.dart'; import 'constants.dart'; import 'interceptor.dart'; @@ -24,6 +27,7 @@ class Request { late bool enableSystemProxy; late String systemProxyHost; late String systemProxyPort; + static final RegExp spmPrefixExp = RegExp(r''); /// 设置cookie static setCookie() async { @@ -51,13 +55,12 @@ class Request { } setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null); - if (cookie.isEmpty) { - try { - await Request().get(HttpString.baseUrl); - } catch (e) { - log("setCookie, ${e.toString()}"); - } + try { + await buvidActivate(); + } catch (e) { + log("setCookie, ${e.toString()}"); } + final String cookieString = cookie .map((Cookie cookie) => '${cookie.name}=${cookie.value}') .join('; '); @@ -87,6 +90,33 @@ class Request { dio.options.headers['referer'] = 'https://www.bilibili.com/'; } + static Future buvidActivate() async { + var html = await Request().get(Api.dynamicSpmPrefix); + String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!; + Random rand = Random(); + String rand_png_end = base64.encode( + List.generate(32, (_) => rand.nextInt(256)) + + List.filled(4, 0) + + [73, 69, 78, 68] + + List.generate(4, (_) => rand.nextInt(256)) + ); + + String jsonData = json.encode({ + '3064': 1, + '39c8': '${spmPrefix}.fp.risk', + '3c43': { + 'adca': 'Linux', + 'bfe9': rand_png_end.substring(rand_png_end.length - 50), + }, + }); + + await Request().post( + Api.activateBuvidApi, + data: {'payload': jsonData}, + options: Options(contentType: 'application/json') + ); + } + /* * config it and create */ diff --git a/lib/http/member.dart b/lib/http/member.dart index 82d2992b..1af0f9a4 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -79,6 +79,8 @@ class MemberHttp { String order = 'pubdate', bool orderAvoided = true, }) async { + String dmImgStr = Utils.base64EncodeRandomString(16, 64); + String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); Map params = await WbiSign().makSign({ 'mid': mid, 'ps': ps, @@ -88,7 +90,11 @@ class MemberHttp { 'order': order, 'platform': 'web', 'web_location': 1550101, - 'order_avoided': orderAvoided + 'order_avoided': orderAvoided, + 'dm_img_list': '[]', + 'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2), + 'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2), + 'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}', }); var res = await Request().get( Api.memberArchive, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index d68ac51b..09d8c97f 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -16,6 +16,8 @@ import '../http/index.dart'; import '../models/github/latest.dart'; class Utils { + static final Random random = Random(); + static Future getCookiePath() async { final Directory tempDir = await getApplicationSupportDirectory(); final String tempPath = "${tempDir.path}/.plpl/"; @@ -180,7 +182,7 @@ class Utils { } static String makeHeroTag(v) { - return v.toString() + Random().nextInt(9999).toString(); + return v.toString() + random.nextInt(9999).toString(); } static int duration(String duration) { @@ -340,4 +342,15 @@ class Utils { return md5String; } + + static List generateRandomBytes(int minLength, int maxLength) { + return List.generate( + random.nextInt(maxLength-minLength+1), (_) => random.nextInt(0x60) + 0x20 + ); + } + + static String base64EncodeRandomString(int minLength, int maxLength) { + List randomBytes = generateRandomBytes(minLength, maxLength); + return base64.encode(randomBytes); + } } From 35dc94014c8346fd28a8c03bd0a97bbdd58b35cc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 23 Feb 2024 00:30:26 +0800 Subject: [PATCH 3/5] =?UTF-8?q?mod:=20=E7=9B=B4=E6=92=ADmcdn=E9=93=BE?= =?UTF-8?q?=E6=8E=A5=E6=9B=BF=E6=8D=A2=20issues=20#568?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/controller.dart | 13 ++++++++++--- lib/utils/video_utils.dart | 13 +++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index ee7b8214..2bb1cd0a 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -4,6 +4,8 @@ import 'package:pilipala/http/live.dart'; import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import '../../models/live/room_info_h5.dart'; +import '../../utils/storage.dart'; +import '../../utils/video_utils.dart'; class LiveRoomController extends GetxController { String cover = ''; @@ -16,6 +18,7 @@ class LiveRoomController extends GetxController { PlPlayerController plPlayerController = PlPlayerController.getInstance(videoType: 'live'); Rx roomInfoH5 = RoomInfoH5Model().obs; + late bool enableCDN; @override void onInit() { @@ -31,6 +34,8 @@ class LiveRoomController extends GetxController { cover = liveItem.cover; } } + // CDN优化 + enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); } playerInit(source) async { @@ -57,9 +62,11 @@ class LiveRoomController extends GetxController { List codec = res['data'].playurlInfo.playurl.stream.first.format.first.codec; CodecItem item = codec.first; - String videoUrl = (item.urlInfo?.first.host)! + - item.baseUrl! + - item.urlInfo!.first.extra!; + String videoUrl = enableCDN + ? VideoUtils.getCdnUrl(item) + : (item.urlInfo?.first.host)! + + item.baseUrl! + + item.urlInfo!.first.extra!; await playerInit(videoUrl); return res; } diff --git a/lib/utils/video_utils.dart b/lib/utils/video_utils.dart index 88faba3c..a4bdd027 100644 --- a/lib/utils/video_utils.dart +++ b/lib/utils/video_utils.dart @@ -1,5 +1,7 @@ import 'package:pilipala/models/video/play/url.dart'; +import '../models/live/room_info.dart'; + class VideoUtils { static String getCdnUrl(dynamic item) { var backupUrl = ""; @@ -12,13 +14,20 @@ class VideoUtils { } else if (item is AudioItem) { backupUrl = item.backupUrl ?? ""; videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); + } else if (item is CodecItem) { + backupUrl = (item.urlInfo?.first.host)! + + item.baseUrl! + + item.urlInfo!.first.extra!; + videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); } else { return ""; } /// issues #70 - if (videoUrl.contains(".mcdn.bilivideo") || - videoUrl.contains("/upgcxcode/")) { + if (videoUrl.contains(".mcdn.bilivideo")) { + videoUrl = + 'https://proxy-tf-all-ws.bilivideo.com/?url=${Uri.encodeComponent(videoUrl)}'; + } else if (videoUrl.contains("/upgcxcode/")) { //CDN列表 var cdnList = { 'ali': 'upos-sz-mirrorali.bilivideo.com', From 02cc1646359ad032c0088874c7779009aa41613d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 23 Feb 2024 22:44:10 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=E9=A6=96=E9=A1=B5tabbar=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E8=AE=BE=E7=BD=AE=20issues=20#564?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/controller.dart | 3 + lib/pages/home/view.dart | 88 ++++++++++++++++++++-------- lib/pages/setting/style_setting.dart | 6 ++ lib/utils/storage.dart | 3 +- 4 files changed, 75 insertions(+), 25 deletions(-) diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index 685ff5c3..9f6f8ac5 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -26,6 +26,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { late List defaultTabs; late List tabbarSort; RxString defaultSearch = ''.obs; + late bool enableGradientBg; @override void onInit() { @@ -40,6 +41,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) { searchDefault(); } + enableGradientBg = + setting.get(SettingBoxKey.enableGradientBg, defaultValue: true); } void onRefresh() { diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index ce7b46c6..91d0ea8c 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -48,38 +48,48 @@ class _HomePageState extends State super.build(context); Brightness currentBrightness = MediaQuery.of(context).platformBrightness; // 设置状态栏图标的亮度 - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - statusBarIconBrightness: currentBrightness == Brightness.light - ? Brightness.dark - : Brightness.light, - )); + if (_homeController.enableGradientBg) { + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + statusBarIconBrightness: currentBrightness == Brightness.light + ? Brightness.dark + : Brightness.light, + )); + } return Scaffold( extendBody: true, extendBodyBehindAppBar: true, body: Stack( children: [ // gradient background - Align( - alignment: Alignment.topLeft, - child: Opacity( - opacity: 0.6, - child: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Theme.of(context).colorScheme.primary.withOpacity(0.9), - Theme.of(context).colorScheme.primary.withOpacity(0.5), - Theme.of(context).colorScheme.surface - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - stops: const [0, 0.0034, 0.34]), + if (_homeController.enableGradientBg) ...[ + Align( + alignment: Alignment.topLeft, + child: Opacity( + opacity: 0.6, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Theme.of(context) + .colorScheme + .primary + .withOpacity(0.9), + Theme.of(context) + .colorScheme + .primary + .withOpacity(0.5), + Theme.of(context).colorScheme.surface + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + stops: const [0, 0.0034, 0.34]), + ), ), ), ), - ), + ], Column( children: [ CustomAppBar( @@ -90,7 +100,37 @@ class _HomePageState extends State callback: showUserBottomSheet, ), if (_homeController.tabs.length > 1) ...[ - const CustomTabs(), + if (_homeController.enableGradientBg) ...[ + const CustomTabs(), + ] else ...[ + const SizedBox(height: 4), + SizedBox( + width: double.infinity, + height: 42, + child: Align( + alignment: Alignment.center, + child: TabBar( + controller: _homeController.tabController, + tabs: [ + for (var i in _homeController.tabs) + Tab(text: i['label']) + ], + isScrollable: true, + dividerColor: Colors.transparent, + enableFeedback: true, + splashBorderRadius: BorderRadius.circular(10), + tabAlignment: TabAlignment.center, + onTap: (value) { + feedBack(); + if (_homeController.initialIndex.value == value) { + _homeController.tabsCtrList[value]().animateToTop(); + } + _homeController.initialIndex.value = value; + }, + ), + ), + ), + ], ] else ...[ const SizedBox(height: 6), ], diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index fb3c780a..c9bffa69 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -102,6 +102,12 @@ class _StyleSettingState extends State { defaultVal: true, needReboot: true, ), + const SetSwitchItem( + title: '首页底栏背景渐变', + setKey: SettingBoxKey.enableGradientBg, + defaultVal: true, + needReboot: true, + ), ListTile( onTap: () async { int? result = await showDialog( diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 41ec0ce8..16cef463 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -135,7 +135,8 @@ class SettingBoxKey { hideSearchBar = 'hideSearchBar', // 收起顶栏 hideTabBar = 'hideTabBar', // 收起底栏 tabbarSort = 'tabbarSort', // 首页tabbar - dynamicBadgeMode = 'dynamicBadgeMode'; + dynamicBadgeMode = 'dynamicBadgeMode', + enableGradientBg = 'enableGradientBg'; } class LocalCacheKey { From b1a05c5c27bf30c2a1b502ee28dcd4ad0063cf8c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Feb 2024 01:38:27 +0800 Subject: [PATCH 5/5] =?UTF-8?q?mod:=20=E4=BF=AE=E6=94=B9=E5=85=B3=E4=BA=8E?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/about/index.dart | 145 +++++++++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 39 deletions(-) diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 0f159ce7..41ee1516 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -53,29 +53,54 @@ class _AboutPageState extends State { style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 6), - Text( - '使用Flutter开发的哔哩哔哩第三方客户端', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - const SizedBox(height: 20), Obx( - () => ListTile( - title: const Text('当前版本'), - trailing: Text(_aboutController.currentVersion.value, - style: subTitleStyle), - ), - ), - Obx( - () => ListTile( - onTap: () => _aboutController.onUpdate(), - title: const Text('最新版本'), - trailing: Text( - _aboutController.isLoading.value - ? '正在获取' - : _aboutController.isUpdate.value - ? '有新版本 ❤️${_aboutController.remoteVersion.value}' - : '当前已是最新版', - style: subTitleStyle, + () => Badge( + isLabelVisible: _aboutController.isLoading.value + ? false + : _aboutController.isUpdate.value, + label: const Text('New'), + child: Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 30), + child: FilledButton.tonal( + onPressed: () { + showModalBottomSheet( + context: context, + builder: (context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + onTap: () => _aboutController.githubRelease(), + title: const Text('Github下载'), + ), + ListTile( + onTap: () => _aboutController.panDownload(), + title: const Text('网盘下载'), + ), + ListTile( + onTap: () => _aboutController.webSiteUrl(), + title: const Text('官网下载'), + ), + ListTile( + onTap: () => _aboutController.qimiao(), + title: const Text('奇妙应用'), + ), + SizedBox( + height: + MediaQuery.of(context).padding.bottom + + 20) + ], + ); + }, + ); + }, + child: Text( + 'V${_aboutController.currentVersion.value}', + style: subTitleStyle.copyWith( + color: Theme.of(context).primaryColor, + ), + ), + ), ), ), ), @@ -87,14 +112,9 @@ class _AboutPageState extends State { // size: 16, // ), // ), - Divider( - thickness: 1, - height: 30, - color: Theme.of(context).colorScheme.outlineVariant, - ), ListTile( onTap: () => _aboutController.githubUrl(), - title: const Text('Github'), + title: const Text('开源地址'), trailing: Text( 'github.com/guozhigq/pilipala', style: subTitleStyle, @@ -129,19 +149,43 @@ class _AboutPageState extends State { ), ), ListTile( - onTap: () => _aboutController.qqChanel(), - title: const Text('QQ群'), + onTap: () { + showModalBottomSheet( + context: context, + builder: (context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + onTap: () => _aboutController.qqChanel(), + title: const Text('QQ群'), + trailing: Text( + '616150809', + style: subTitleStyle, + ), + ), + ListTile( + onTap: () => _aboutController.tgChanel(), + title: const Text('TG频道'), + trailing: Text( + 'https://t.me/+lm_oOVmF0RJiODk1', + style: subTitleStyle, + ), + ), + SizedBox( + height: MediaQuery.of(context).padding.bottom + 20) + ], + ); + }, + ); + }, + title: const Text('交流社区'), trailing: Icon( Icons.arrow_forward_ios, size: 16, color: outline, ), ), - ListTile( - onTap: () => _aboutController.tgChanel(), - title: const Text('TG频道'), - trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), - ), ListTile( onTap: () => _aboutController.aPay(), title: const Text('赞助'), @@ -163,6 +207,7 @@ class _AboutPageState extends State { subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle), trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), ), + SizedBox(height: MediaQuery.of(context).padding.bottom + 20) ], ), ), @@ -230,11 +275,26 @@ class AboutController extends GetxController { ); } + githubRelease() { + launchUrl( + Uri.parse('https://github.com/guozhigq/pilipala/release'), + mode: LaunchMode.externalApplication, + ); + } + // 从网盘下载 panDownload() { - launchUrl( - Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), - mode: LaunchMode.externalApplication, + Clipboard.setData( + const ClipboardData(text: 'pili'), + ); + SmartDialog.showToast( + '已复制提取码:pili', + displayTime: const Duration(milliseconds: 500), + ).then( + (value) => launchUrl( + Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), + mode: LaunchMode.externalApplication, + ), ); } @@ -250,7 +310,7 @@ class AboutController extends GetxController { // qq频道 qqChanel() { Clipboard.setData( - const ClipboardData(text: '489981949'), + const ClipboardData(text: '616150809'), ); SmartDialog.showToast('已复制QQ群号'); } @@ -291,6 +351,13 @@ class AboutController extends GetxController { ); } + qimiao() { + launchUrl( + Uri.parse('https://www.magicalapk.com/home'), + mode: LaunchMode.externalApplication, + ); + } + // 日志 logs() { Get.toNamed('/logs');