Merge branch 'main' into fix

This commit is contained in:
guozhigq
2024-10-18 00:18:50 +08:00
26 changed files with 912 additions and 848 deletions

View File

@ -47,13 +47,14 @@
<activity <activity
android:name="com.guozhigq.pilipala.MainActivity" android:name="com.guozhigq.pilipala.MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTask"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:supportsPictureInPicture="true" android:supportsPictureInPicture="true"
android:resizeableActivity="true" android:resizeableActivity="true"
android:autoVerify="true"
> >
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
@ -63,10 +64,21 @@
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"
/> />
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<!-- Deep Link -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="pili"/>
<data android:scheme="pilipala"/>
</intent-filter>
<!-- App Link -->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEARCH" /> <action android:name="android.intent.action.SEARCH" />

View File

@ -1,4 +1,6 @@
PODS: PODS:
- app_links (0.0.2):
- Flutter
- appscheme (1.0.4): - appscheme (1.0.4):
- Flutter - Flutter
- audio_service (0.0.1): - audio_service (0.0.1):
@ -66,6 +68,7 @@ PODS:
- Flutter - Flutter
DEPENDENCIES: DEPENDENCIES:
- app_links (from `.symlinks/plugins/app_links/ios`)
- appscheme (from `.symlinks/plugins/appscheme/ios`) - appscheme (from `.symlinks/plugins/appscheme/ios`)
- audio_service (from `.symlinks/plugins/audio_service/ios`) - audio_service (from `.symlinks/plugins/audio_service/ios`)
- audio_session (from `.symlinks/plugins/audio_session/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`)
@ -102,6 +105,8 @@ SPEC REPOS:
- Toast - Toast
EXTERNAL SOURCES: EXTERNAL SOURCES:
app_links:
:path: ".symlinks/plugins/app_links/ios"
appscheme: appscheme:
:path: ".symlinks/plugins/appscheme/ios" :path: ".symlinks/plugins/appscheme/ios"
audio_service: audio_service:
@ -160,6 +165,7 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios" :path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8 appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8
audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_service: f509d65da41b9521a61f1c404dd58651f265a567
audio_session: 4f3e461722055d21515cf3261b64c973c062f345 audio_session: 4f3e461722055d21515cf3261b64c973c062f345

View File

@ -65,44 +65,29 @@
<array> <array>
<dict> <dict>
<key>CFBundleURLName</key> <key>CFBundleURLName</key>
<string></string> <string>bilibili</string>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>http</string> <string>http</string>
<string>https</string> <string>https</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>m.bilibili.com</string>
<string>bilibili.com</string>
<string>www.bilibili.com</string>
<string>bangumi.bilibili.com</string>
<string>bilibili.cn</string>
<string>www.bilibili.cn</string>
<string>bangumi.bilibili.cn</string>
<string>bilibili.tv</string>
<string>www.bilibili.tv</string>
<string>bangumi.bilibili.tv</string>
<string>miniapp.bilibili.com</string>
<string>live.bilibili.com</string>
</array>
</dict>
</array>
</dict>
<!-- 当其他应用程序或系统通过 bilibili -->
<dict>
<key>CFBundleURLName</key>
<string>bilibili</string>
<key>CFBundleURLSchemes</key>
<array>
<string>bilibili</string> <string>bilibili</string>
<string>m.bilibili.com</string>
<string>bilibili.com</string>
<string>www.bilibili.com</string>
<string>bangumi.bilibili.com</string>
<string>bilibili.cn</string>
<string>www.bilibili.cn</string>
<string>bangumi.bilibili.cn</string>
<string>bilibili.tv</string>
<string>www.bilibili.tv</string>
<string>bangumi.bilibili.tv</string>
<string>miniapp.bilibili.com</string>
<string>live.bilibili.com</string>
<string>pili</string>
<string>pilipala</string>
</array> </array>
<key>FlutterDeepLinkingEnabled</key>
<false/>
</dict> </dict>
</array> </array>
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>

View File

@ -217,12 +217,13 @@ class Request {
/* /*
* get请求 * get请求
*/ */
get(url, {data, options, cancelToken, extra}) async { get(url, {data, Options? options, cancelToken, extra}) async {
Response response; Response response;
final Options options = Options(); options ??= Options(); // 如果 options 为 null则初始化一个新的 Options 对象
ResponseType resType = ResponseType.json; ResponseType resType = ResponseType.json;
if (extra != null) { if (extra != null) {
resType = extra!['resType'] ?? ResponseType.json; resType = extra['resType'] ?? ResponseType.json;
if (extra['ua'] != null) { if (extra['ua'] != null) {
options.headers = {'user-agent': headerUa(type: extra['ua'])}; options.headers = {'user-agent': headerUa(type: extra['ua'])};
} }
@ -238,14 +239,11 @@ class Request {
); );
return response; return response;
} on DioException catch (e) { } on DioException catch (e) {
Response errResponse = Response( return Response(
data: { data: {'message': await ApiInterceptor.dioError(e)},
'message': await ApiInterceptor.dioError(e)
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
statusCode: 200, statusCode: 200,
requestOptions: RequestOptions(), requestOptions: RequestOptions(),
); );
return errResponse;
} }
} }

View File

@ -4,6 +4,7 @@ import 'package:hive/hive.dart';
import 'package:html/parser.dart'; import 'package:html/parser.dart';
import 'package:pilipala/models/member/article.dart'; import 'package:pilipala/models/member/article.dart';
import 'package:pilipala/models/member/like.dart'; import 'package:pilipala/models/member/like.dart';
import 'package:pilipala/utils/global_data_cache.dart';
import '../common/constants.dart'; import '../common/constants.dart';
import '../models/dynamics/result.dart'; import '../models/dynamics/result.dart';
import '../models/follow/result.dart'; import '../models/follow/result.dart';
@ -19,14 +20,20 @@ import 'index.dart';
class MemberHttp { class MemberHttp {
static Future memberInfo({ static Future memberInfo({
int? mid, required int mid,
String token = '', String token = '',
}) async { }) async {
String? wWebid;
if ((await getWWebid(mid: mid))['status']) {
wWebid = GlobalDataCache().wWebid;
}
Map params = await WbiSign().makSign({ Map params = await WbiSign().makSign({
'mid': mid, 'mid': mid,
'token': token, 'token': token,
'platform': 'web', 'platform': 'web',
'web_location': 1550101, 'web_location': 1550101,
...wWebid != null ? {'w_webid': wWebid} : {},
}); });
var res = await Request().get( var res = await Request().get(
Api.memberInfo, Api.memberInfo,
@ -566,6 +573,10 @@ class MemberHttp {
} }
static Future getWWebid({required int mid}) async { static Future getWWebid({required int mid}) async {
String? wWebid = GlobalDataCache().wWebid;
if (wWebid != null) {
return {'status': true, 'data': wWebid};
}
var res = await Request().get('https://space.bilibili.com/$mid/article'); var res = await Request().get('https://space.bilibili.com/$mid/article');
String? headContent = parse(res.data).head?.outerHtml; String? headContent = parse(res.data).head?.outerHtml;
final regex = RegExp( final regex = RegExp(
@ -576,6 +587,7 @@ class MemberHttp {
final content = match.group(1); final content = match.group(1);
String decodedString = Uri.decodeComponent(content!); String decodedString = Uri.decodeComponent(content!);
Map<String, dynamic> map = jsonDecode(decodedString); Map<String, dynamic> map = jsonDecode(decodedString);
GlobalDataCache().wWebid = map['access_id'];
return {'status': true, 'data': map['access_id']}; return {'status': true, 'data': map['access_id']};
} else { } else {
return {'status': false, 'data': '请检查登录状态'}; return {'status': false, 'data': '请检查登录状态'};
@ -588,25 +600,20 @@ class MemberHttp {
static Future getMemberArticle({ static Future getMemberArticle({
required int mid, required int mid,
required int pn, required int pn,
required String wWebid,
String? offset, String? offset,
}) async { }) async {
String? wWebid;
if ((await getWWebid(mid: mid))['status']) {
wWebid = GlobalDataCache().wWebid;
}
Map params = await WbiSign().makSign({ Map params = await WbiSign().makSign({
'host_mid': mid, 'host_mid': mid,
'page': pn, 'page': pn,
'offset': offset, 'offset': offset,
'web_location': 333.999, 'web_location': 333.999,
'w_webid': wWebid, ...wWebid != null ? {'w_webid': wWebid} : {},
});
var res = await Request().get(Api.opusList, data: {
'host_mid': mid,
'page': pn,
'offset': offset,
'web_location': 333.999,
'w_webid': wWebid,
'w_rid': params['w_rid'],
'wts': params['wts'],
}); });
var res = await Request().get(Api.opusList, data: params);
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return { return {
'status': true, 'status': true,

View File

@ -4,9 +4,12 @@ enum FullScreenGestureMode {
/// 从下滑到上 /// 从下滑到上
fromBottomtoTop, fromBottomtoTop,
/// 关闭手势
none,
} }
extension FullScreenGestureModeExtension on FullScreenGestureMode { extension FullScreenGestureModeExtension on FullScreenGestureMode {
String get values => ['fromToptoBottom', 'fromBottomtoTop'][index]; String get values => ['fromToptoBottom', 'fromBottomtoTop', 'none'][index];
String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index]; String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏', '关闭手势'][index];
} }

View File

@ -295,7 +295,7 @@ class AboutController extends GetxController {
displayTime: const Duration(milliseconds: 500), displayTime: const Duration(milliseconds: 500),
).then( ).then(
(value) => launchUrl( (value) => launchUrl(
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'),
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
), ),
); );
@ -349,7 +349,7 @@ class AboutController extends GetxController {
// 官网 // 官网
webSiteUrl() { webSiteUrl() {
launchUrl( launchUrl(
Uri.parse('https://pilipalanet.mysxl.cn'), Uri.parse('https://pilipala.life'),
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
); );
} }

View File

@ -49,6 +49,8 @@ class MemberController extends GetxController {
if (res['status']) { if (res['status']) {
memberInfo.value = res['data']; memberInfo.value = res['data'];
face.value = res['data'].face; face.value = res['data'].face;
} else {
SmartDialog.showToast('用户信息请求异常:${res['msg']}');
} }
return res; return res;
} }
@ -78,42 +80,10 @@ class MemberController extends GetxController {
return; return;
} }
if (attribute.value == 128) { if (attribute.value == 128) {
blockUser(); modifyRelation('block');
return; } else {
modifyRelation('follow');
} }
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: Text(
'点错了',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
await VideoHttp.relationMod(
mid: mid,
act: memberInfo.value.isFollowed! ? 2 : 1,
reSrc: 11,
);
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
relationSearch();
SmartDialog.dismiss();
memberInfo.update((val) {});
},
child: const Text('确认'),
)
],
);
},
);
} }
// 关系查询 // 关系查询
@ -123,24 +93,15 @@ class MemberController extends GetxController {
var res = await UserHttp.hasFollow(mid); var res = await UserHttp.hasFollow(mid);
if (res['status']) { if (res['status']) {
attribute.value = res['data']['attribute']; attribute.value = res['data']['attribute'];
switch (attribute.value) { final Map<int, String> attributeTextMap = {
case 1: 1: '悄悄关注',
attributeText.value = '悄悄关注'; 2: '关注',
break; 6: '已互关',
case 2: 128: '已拉黑',
attributeText.value = '已关注'; };
break; attributeText.value = attributeTextMap[attribute.value] ?? '关注';
case 6:
attributeText.value = '已互关';
break;
case 128:
attributeText.value = '已拉黑';
break;
default:
attributeText.value = '关注';
}
if (res['data']['special'] == 1) { if (res['data']['special'] == 1) {
attributeText.value += 'SP'; attributeText.value = '特别关注';
} }
} }
} }
@ -151,16 +112,37 @@ class MemberController extends GetxController {
SmartDialog.showToast('账号未登录'); SmartDialog.showToast('账号未登录');
return; return;
} }
SmartDialog.show( modifyRelation('block');
useSystem: true, }
animationType: SmartAnimationType.centerFade_otherSlide,
// 合并关注/取关和拉黑逻辑
Future modifyRelation(String actionType) async {
if (userInfo == null) {
SmartDialog.showToast('账号未登录');
return;
}
String contentText;
int act;
if (actionType == 'follow') {
contentText = memberInfo.value.isFollowed! ? '确定取消关注UP主?' : '确定关注UP主?';
act = memberInfo.value.isFollowed! ? 2 : 1;
} else if (actionType == 'block') {
contentText = attribute.value != 128 ? '确定拉黑UP主?' : '确定从黑名单移除UP主';
act = attribute.value != 128 ? 5 : 6;
} else {
return;
}
showDialog(
context: Get.context!,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: const Text('提示'), title: const Text('提示'),
content: Text(attribute.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'), content: Text(contentText),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => SmartDialog.dismiss(), onPressed: () => Navigator.of(context).pop(),
child: Text( child: Text(
'点错了', '点错了',
style: TextStyle(color: Theme.of(context).colorScheme.outline), style: TextStyle(color: Theme.of(context).colorScheme.outline),
@ -170,19 +152,26 @@ class MemberController extends GetxController {
onPressed: () async { onPressed: () async {
var res = await VideoHttp.relationMod( var res = await VideoHttp.relationMod(
mid: mid, mid: mid,
act: attribute.value != 128 ? 5 : 6, act: act,
reSrc: 11, reSrc: 11,
); );
SmartDialog.dismiss(); SmartDialog.dismiss();
if (res['status']) { if (res['status']) {
attribute.value = attribute.value != 128 ? 128 : 0; if (actionType == 'follow') {
attributeText.value = attribute.value == 128 ? '已拉黑' : '关注'; memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
memberInfo.value.isFollowed = false; } else if (actionType == 'block') {
attribute.value = attribute.value != 128 ? 128 : 0;
attributeText.value = attribute.value == 128 ? '已拉黑' : '关注';
memberInfo.value.isFollowed = false;
}
relationSearch(); relationSearch();
if (context.mounted) {
Navigator.of(context).pop();
}
memberInfo.update((val) {}); memberInfo.update((val) {});
} }
}, },
child: const Text(''), child: const Text(''),
) )
], ],
); );
@ -228,17 +217,14 @@ class MemberController extends GetxController {
// 跳转查看动态 // 跳转查看动态
void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid'); void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid');
// 跳转查看投稿 // 跳转查看投稿
void pushArchivesPage() => Get.toNamed('/memberArchive?mid=$mid'); void pushArchivesPage() => Get.toNamed('/memberArchive?mid=$mid');
// 跳转查看专栏
void pushSeasonsPage() {}
// 跳转查看最近投币 // 跳转查看最近投币
void pushRecentCoinsPage() async { void pushRecentCoinsPage() async {
if (recentCoinsList.isNotEmpty) {} if (recentCoinsList.isNotEmpty) {}
} }
// 跳转查看收藏夹
void pushfavPage() => Get.toNamed('/fav?mid=$mid'); void pushfavPage() => Get.toNamed('/fav?mid=$mid');
// 跳转图文专栏 // 跳转图文专栏
void pushArticlePage() => Get.toNamed('/memberArticle?mid=$mid'); void pushArticlePage() => Get.toNamed('/memberArticle?mid=$mid');

View File

@ -1,13 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/member/info.dart';
import 'package:pilipala/pages/member/index.dart'; import 'package:pilipala/pages/member/index.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import 'widgets/commen_widget.dart';
import 'widgets/conis.dart'; import 'widgets/conis.dart';
import 'widgets/like.dart'; import 'widgets/like.dart';
import 'widgets/profile.dart'; import 'widgets/profile.dart';
@ -65,259 +65,233 @@ class _MemberPageState extends State<MemberPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
primary: true, appBar: AppBar(
body: Column( title: StreamBuilder(
children: [ stream: appbarStream.stream.distinct(),
AppBar( initialData: false,
title: StreamBuilder( builder: (BuildContext context, AsyncSnapshot snapshot) {
stream: appbarStream.stream.distinct(), return AnimatedOpacity(
initialData: false, opacity: snapshot.data ? 1 : 0,
builder: (BuildContext context, AsyncSnapshot snapshot) { curve: Curves.easeOut,
return AnimatedOpacity( duration: const Duration(milliseconds: 500),
opacity: snapshot.data ? 1 : 0, child: Row(
curve: Curves.easeOut, children: [
duration: const Duration(milliseconds: 500), Obx(
child: Row( () => NetworkImgLayer(
children: [ width: 35,
Row( height: 35,
children: [ type: 'avatar',
Obx( src: _memberController.face.value,
() => NetworkImgLayer( ),
width: 35,
height: 35,
type: 'avatar',
src: _memberController.face.value,
),
),
const SizedBox(width: 10),
Obx(
() => Text(
_memberController.memberInfo.value.name ?? '',
style: TextStyle(
color:
Theme.of(context).colorScheme.onSurface,
fontSize: 14),
),
),
],
)
],
), ),
); const SizedBox(width: 10),
}, Obx(
), () => Text(
actions: [ _memberController.memberInfo.value.name ?? '',
IconButton( style: TextStyle(
onPressed: () => Get.toNamed( color: Theme.of(context).colorScheme.onSurface,
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'), fontSize: 14),
icon: const Icon(Icons.search_outlined),
),
PopupMenuButton(
icon: const Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
if (_memberController.ownerMid != _memberController.mid) ...[
PopupMenuItem(
onTap: () => _memberController.blockUser(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.block, size: 19),
const SizedBox(width: 10),
Text(_memberController.attribute.value != 128
? '加入黑名单'
: '移除黑名单'),
],
),
)
],
PopupMenuItem(
onTap: () => _memberController.shareUser(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.share_outlined, size: 19),
const SizedBox(width: 10),
Text(_memberController.ownerMid != _memberController.mid
? '分享UP主'
: '分享我的主页'),
],
), ),
), ),
], ],
), ),
const SizedBox(width: 4), );
], },
),
actions: [
IconButton(
onPressed: () => Get.toNamed(
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'),
icon: const Icon(Icons.search_outlined),
), ),
Expanded( PopupMenuButton(
child: SingleChildScrollView( icon: const Icon(Icons.more_vert),
controller: _extendNestCtr, itemBuilder: (BuildContext context) => <PopupMenuEntry>[
child: Padding( if (_memberController.ownerMid != _memberController.mid) ...[
padding: EdgeInsets.only( PopupMenuItem(
bottom: MediaQuery.of(context).padding.bottom + 20, onTap: () => _memberController.blockUser(),
), child: Row(
child: Column( mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.block, size: 19),
const SizedBox(width: 10),
Text(_memberController.attribute.value != 128
? '加入黑名单'
: '移除黑名单'),
],
),
)
],
PopupMenuItem(
onTap: () => _memberController.shareUser(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [ children: [
profileWidget(), const Icon(Icons.share_outlined, size: 19),
const SizedBox(width: 10),
/// 动态链接 Text(_memberController.ownerMid != _memberController.mid
Obx( ? '分享UP主'
() => ListTile( : '分享我的主页'),
onTap: _memberController.pushDynamicsPage,
title: Text(
'${_memberController.isOwner.value ? '' : 'Ta'}的动态'),
trailing:
const Icon(Icons.arrow_forward_outlined, size: 19),
),
),
/// 视频
Obx(
() => ListTile(
onTap: _memberController.pushArchivesPage,
title: Text(
'${_memberController.isOwner.value ? '' : 'Ta'}的投稿'),
trailing:
const Icon(Icons.arrow_forward_outlined, size: 19),
),
),
/// 他的收藏夹
Obx(
() => ListTile(
onTap: _memberController.pushfavPage,
title: Text(
'${_memberController.isOwner.value ? '' : 'Ta'}的收藏'),
trailing:
const Icon(Icons.arrow_forward_outlined, size: 19),
),
),
/// 专栏
Obx(
() => ListTile(
onTap: _memberController.pushArticlePage,
title: Text(
'${_memberController.isOwner.value ? '' : 'Ta'}的专栏'),
trailing:
const Icon(Icons.arrow_forward_outlined, size: 19),
),
),
/// 合集
Obx(
() => ListTile(
title: Text(
'${_memberController.isOwner.value ? '' : 'Ta'}的合集')),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
context: context,
child: FutureBuilder(
future: _memberSeasonsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.data == null) {
return const SizedBox();
}
if (snapshot.data['status']) {
Map data = snapshot.data as Map;
if (data['data'].seasonsList.isEmpty) {
return commenWidget('用户没有设置合集');
} else {
return MemberSeasonsPanel(data: data['data']);
}
} else {
// 请求错误
return const SizedBox();
}
} else {
return const SizedBox();
}
},
),
),
/// 追番
/// 最近投币
Obx(
() => _memberController.recentCoinsList.isNotEmpty
? const ListTile(title: Text('最近投币的视频'))
: const SizedBox(),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
context: context,
child: Padding(
padding: const EdgeInsets.only(
left: StyleString.safeSpace,
right: StyleString.safeSpace,
),
child: FutureBuilder(
future: _memberCoinsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.data == null) {
return const SizedBox();
}
if (snapshot.data['status']) {
Map data = snapshot.data as Map;
return MemberCoinsPanel(data: data['data']);
} else {
// 请求错误
return const SizedBox();
}
} else {
return const SizedBox();
}
},
),
),
),
/// 最近点赞
Obx(
() => _memberController.recentLikeList.isNotEmpty
? const ListTile(title: Text('最近点赞的视频'))
: const SizedBox(),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
context: context,
child: Padding(
padding: const EdgeInsets.only(
left: StyleString.safeSpace,
right: StyleString.safeSpace,
),
child: FutureBuilder(
future: _memberLikeFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.data == null) {
return const SizedBox();
}
if (snapshot.data['status']) {
Map data = snapshot.data as Map;
return MemberLikePanel(data: data['data']);
} else {
// 请求错误
return const SizedBox();
}
} else {
return const SizedBox();
}
},
),
),
),
], ],
), ),
), ),
],
),
const SizedBox(width: 4),
],
),
primary: true,
body: ListView(
controller: _extendNestCtr,
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 20,
),
children: [
profileWidget(),
/// 动态链接
Obx(
() => ListTile(
onTap: _memberController.pushDynamicsPage,
title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的动态'),
trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
),
),
/// 视频
Obx(
() => ListTile(
onTap: _memberController.pushArchivesPage,
title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的投稿'),
trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
),
),
/// 他的收藏夹
Obx(
() => ListTile(
onTap: _memberController.pushfavPage,
title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的收藏'),
trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
),
),
/// 专栏
Obx(
() => ListTile(
onTap: _memberController.pushArticlePage,
title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的专栏'),
trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
),
),
/// 合集
Obx(
() => ListTile(
title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的合集'),
),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
context: context,
child: FutureBuilder(
future: _memberSeasonsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return const SizedBox();
}
if (snapshot.data['status']) {
Map data = snapshot.data as Map;
if (data['data'].seasonsList.isEmpty) {
return const CommenWidget(msg: '用户没有设置合集');
} else {
return MemberSeasonsPanel(data: data['data']);
}
} else {
// 请求错误
return const SizedBox();
}
} else {
return const SizedBox();
}
},
),
),
/// 追番
/// 最近投币
Obx(
() => _memberController.recentCoinsList.isNotEmpty
? const ListTile(title: Text('最近投币的视频'))
: const SizedBox(),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
context: context,
child: Padding(
padding: const EdgeInsets.only(
left: StyleString.safeSpace,
right: StyleString.safeSpace,
),
child: FutureBuilder(
future: _memberCoinsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return const SizedBox();
}
if (snapshot.data['status']) {
Map data = snapshot.data as Map;
return MemberCoinsPanel(data: data['data']);
} else {
// 请求错误
return const SizedBox();
}
} else {
return const SizedBox();
}
},
),
),
),
/// 最近点赞
Obx(
() => _memberController.recentLikeList.isNotEmpty
? const ListTile(title: Text('最近点赞的视频'))
: const SizedBox(),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
context: context,
child: Padding(
padding: const EdgeInsets.only(
left: StyleString.safeSpace,
right: StyleString.safeSpace,
),
child: FutureBuilder(
future: _memberLikeFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return const SizedBox();
}
if (snapshot.data['status']) {
Map data = snapshot.data as Map;
return MemberLikePanel(data: data['data']);
} else {
// 请求错误
return const SizedBox();
}
} else {
return const SizedBox();
}
},
),
), ),
), ),
], ],
@ -334,115 +308,90 @@ class _MemberPageState extends State<MemberPage>
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
Map? data = snapshot.data; Map? data = snapshot.data;
if (data != null && data['status']) { if (data != null && data['status']) {
Rx<MemberInfoModel> memberInfo = _memberController.memberInfo;
return Obx( return Obx(
() => Stack( () => Column(
alignment: AlignmentDirectional.center, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Column( ProfilePanel(ctr: _memberController),
crossAxisAlignment: CrossAxisAlignment.start, const SizedBox(height: 20),
Row(
children: [ children: [
ProfilePanel(ctr: _memberController), Flexible(
const SizedBox(height: 20), child: Text(
Row( memberInfo.value.name!,
children: [ maxLines: 1,
Flexible( overflow: TextOverflow.ellipsis,
child: Text( style: Theme.of(context)
_memberController.memberInfo.value.name!, .textTheme
maxLines: 1, .titleMedium!
overflow: TextOverflow.ellipsis, .copyWith(
style: Theme.of(context) fontWeight: FontWeight.bold,
.textTheme color: memberInfo.value.vip!.nicknameColor !=
.titleMedium! null
.copyWith( ? Color(_memberController
fontWeight: FontWeight.bold, .memberInfo.value.vip!.nicknameColor!)
color: _memberController.memberInfo.value : null),
.vip!.nicknameColor != )),
null const SizedBox(width: 2),
? Color(_memberController.memberInfo if (memberInfo.value.sex == '')
.value.vip!.nicknameColor!) const Icon(
: null), FontAwesomeIcons.venus,
)), size: 14,
const SizedBox(width: 2), color: Colors.pink,
if (_memberController.memberInfo.value.sex == '') ),
const Icon( if (memberInfo.value.sex == '')
FontAwesomeIcons.venus, const Icon(
size: 14, FontAwesomeIcons.mars,
color: Colors.pink, size: 14,
), color: Colors.blue,
if (_memberController.memberInfo.value.sex == '') ),
const Icon( const SizedBox(width: 4),
FontAwesomeIcons.mars, Image.asset(
size: 14, 'assets/images/lv/lv${memberInfo.value.level}.png',
color: Colors.blue, height: 11,
),
const SizedBox(width: 4),
Image.asset(
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png',
height: 11,
),
const SizedBox(width: 6),
if (_memberController
.memberInfo.value.vip!.status ==
1 &&
_memberController.memberInfo.value.vip!
.label!['img_label_uri_hans'] !=
'') ...[
Image.network(
_memberController.memberInfo.value.vip!
.label!['img_label_uri_hans'],
height: 20,
),
] else if (_memberController
.memberInfo.value.vip!.status ==
1 &&
_memberController.memberInfo.value.vip!
.label!['img_label_uri_hans_static'] !=
'') ...[
Image.network(
_memberController.memberInfo.value.vip!
.label!['img_label_uri_hans_static'],
height: 20,
),
]
],
), ),
if (_memberController const SizedBox(width: 6),
.memberInfo.value.official!['title'] != if (memberInfo.value.vip!.status == 1 &&
'') ...[ memberInfo
const SizedBox(height: 6), .value.vip!.label!['img_label_uri_hans'] !=
Text.rich( '') ...[
maxLines: 2, Image.network(
TextSpan( memberInfo.value.vip!.label!['img_label_uri_hans'],
text: _memberController height: 20,
.memberInfo.value.official!['role'] ==
1
? '个人认证:'
: '企业认证:',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
children: [
TextSpan(
text: _memberController
.memberInfo.value.official!['title'],
),
],
),
softWrap: true,
), ),
], ] else if (memberInfo.value.vip!.status == 1 &&
const SizedBox(height: 6), memberInfo.value.vip!
if (_memberController.memberInfo.value.sign != '') .label!['img_label_uri_hans_static'] !=
SelectableText( '') ...[
_memberController.memberInfo.value.sign!, Image.network(
memberInfo
.value.vip!.label!['img_label_uri_hans_static'],
height: 20,
), ),
]
], ],
), ),
if (memberInfo.value.official!['title'] != '') ...[
const SizedBox(height: 6),
Text(
memberInfo.value.official!['role'] == 1
? '个人认证:${memberInfo.value.official!['title']}'
: '企业认证:${memberInfo.value.official!['title']}',
maxLines: 2,
softWrap: true,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
],
const SizedBox(height: 6),
SelectableText(memberInfo.value.sign ?? ''),
], ],
), ),
); );
} else { } else {
return const SizedBox(); return ProfilePanel(ctr: _memberController, loadingStatus: true);
} }
} else { } else {
// 骨架屏 // 骨架屏
@ -452,22 +401,4 @@ class _MemberPageState extends State<MemberPage>
), ),
); );
} }
Widget commenWidget(msg) {
return Padding(
padding: const EdgeInsets.only(
top: 20,
bottom: 30,
),
child: Center(
child: Text(
msg,
style: Theme.of(context)
.textTheme
.labelMedium!
.copyWith(color: Theme.of(context).colorScheme.outline),
),
),
);
}
} }

View File

@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
class CommenWidget extends StatelessWidget {
final String msg;
const CommenWidget({required this.msg, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0),
child: Center(
child: Text(
msg,
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.labelMedium!
.copyWith(color: Theme.of(context).colorScheme.outline),
),
),
);
}
}

View File

@ -18,253 +18,245 @@ class ProfilePanel extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
MemberInfoModel memberInfo = ctr.memberInfo.value; MemberInfoModel memberInfo = ctr.memberInfo.value;
return Builder( final int? mid = memberInfo.mid;
builder: ((context) { final String? name = memberInfo.name;
return Padding(
padding: Map<String, dynamic> buildStatItem({
EdgeInsets.only(top: MediaQuery.of(context).padding.top - 20), required String label,
child: Row( required String value,
children: [ required VoidCallback onTap,
Hero( }) {
tag: ctr.heroTag!, return {
child: Stack( 'label': label,
children: [ 'value': value,
NetworkImgLayer( 'fn': onTap,
width: 90, };
height: 90, }
type: 'avatar',
src: !loadingStatus ? memberInfo.face : ctr.face.value, final List<Map<String, dynamic>> statList = [
), buildStatItem(
if (!loadingStatus && label: '关注',
memberInfo.liveRoom != null && value: !loadingStatus ? "${ctr.userStat!['following']}" : '-',
memberInfo.liveRoom!.liveStatus == 1) onTap: () {
Positioned( Get.toNamed('/follow?mid=$mid&name=$name');
bottom: 0, },
left: 14, ),
child: GestureDetector( buildStatItem(
onTap: () { label: '粉丝',
LiveItemModel liveItem = LiveItemModel.fromJson({ value: !loadingStatus
'title': memberInfo.liveRoom!.title, ? ctr.userStat!['follower'] != null
'uname': memberInfo.name, ? Utils.numFormat(ctr.userStat!['follower'])
'face': memberInfo.face, : '-'
'roomid': memberInfo.liveRoom!.roomId, : '-',
'watched_show': memberInfo.liveRoom!.watchedShow, onTap: () {
}); Get.toNamed('/fan?mid=$mid&name=$name');
Get.toNamed( },
'/liveRoom?roomid=${memberInfo.liveRoom!.roomId}', ),
arguments: {'liveItem': liveItem}, buildStatItem(
); label: '获赞',
}, value: !loadingStatus
child: Container( ? ctr.userStat!['likes'] != null
padding: const EdgeInsets.fromLTRB(6, 2, 6, 2), ? Utils.numFormat(ctr.userStat!['likes'])
decoration: BoxDecoration( : '-'
color: Theme.of(context).colorScheme.primary, : '-',
borderRadius: onTap: () {},
const BorderRadius.all(Radius.circular(10)), ),
), ];
child: Row(children: [
Image.asset( return Padding(
'assets/images/live.gif', padding: const EdgeInsets.only(top: 30, left: 4),
height: 10, child: Row(
), children: [
Text( Hero(
' 直播中', tag: ctr.heroTag!,
style: TextStyle( child: Stack(
color: Colors.white, children: [
fontSize: Theme.of(context) NetworkImgLayer(
.textTheme width: 90,
.labelSmall! height: 90,
.fontSize), type: 'avatar',
) src: !loadingStatus ? memberInfo.face : ctr.face.value,
]),
),
),
)
],
), ),
), if (!loadingStatus &&
const SizedBox(width: 12), memberInfo.liveRoom != null &&
Expanded( memberInfo.liveRoom!.liveStatus == 1)
child: Column( Positioned(
mainAxisSize: MainAxisSize.min, bottom: 0,
children: [ left: 14,
Padding( child: GestureDetector(
padding: onTap: () {
const EdgeInsets.only(top: 10, left: 10, right: 10), LiveItemModel liveItem = LiveItemModel(
child: Row( title: memberInfo.liveRoom!.title,
mainAxisSize: MainAxisSize.max, uname: memberInfo.name,
mainAxisAlignment: MainAxisAlignment.spaceAround, face: memberInfo.face,
children: [ roomId: memberInfo.liveRoom!.roomId,
InkWell( watchedShow: memberInfo.liveRoom!.watchedShow,
onTap: () { );
Get.toNamed( Get.toNamed(
'/follow?mid=${memberInfo.mid}&name=${memberInfo.name}'); '/liveRoom?roomid=${memberInfo.liveRoom!.roomId}',
}, arguments: {'liveItem': liveItem},
child: Column( );
children: [ },
Text( child: Container(
!loadingStatus padding: const EdgeInsets.fromLTRB(6, 2, 6, 2),
? ctr.userStat!['following'].toString() decoration: BoxDecoration(
: '-', color: Theme.of(context).colorScheme.primary,
style: const TextStyle( borderRadius:
fontWeight: FontWeight.bold), const BorderRadius.all(Radius.circular(10)),
), ),
Text( child: Row(children: [
'关注', Image.asset(
style: TextStyle( 'assets/images/live.gif',
fontSize: Theme.of(context) height: 10,
.textTheme
.labelMedium!
.fontSize),
)
],
),
), ),
InkWell( Text(
onTap: () { ' 直播中',
Get.toNamed( style: TextStyle(
'/fan?mid=${memberInfo.mid}&name=${memberInfo.name}'); color: Colors.white,
}, fontSize: Theme.of(context)
child: Column( .textTheme
children: [ .labelSmall!
Text( .fontSize),
!loadingStatus )
? ctr.userStat!['follower'] != null ]),
? Utils.numFormat(
ctr.userStat!['follower'],
)
: '-'
: '-',
style: const TextStyle(
fontWeight: FontWeight.bold)),
Text(
'粉丝',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
),
Column(
children: [
Text(
!loadingStatus
? ctr.userStat!['likes'] != null
? Utils.numFormat(
ctr.userStat!['likes'],
)
: '-'
: '-',
style: const TextStyle(
fontWeight: FontWeight.bold)),
Text(
'获赞',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
],
), ),
), ),
const SizedBox(height: 10), )
if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1) ...[ ],
Row( ),
children: [
Obx(
() => Expanded(
child: TextButton(
onPressed: () => loadingStatus
? null
: ctr.actionRelationMod(),
style: TextButton.styleFrom(
foregroundColor: ctr.attribute.value == -1
? Colors.transparent
: ctr.attribute.value != 0
? Theme.of(context)
.colorScheme
.outline
: Theme.of(context)
.colorScheme
.onPrimary,
backgroundColor: ctr.attribute.value != 0
? Theme.of(context)
.colorScheme
.onInverseSurface
: Theme.of(context)
.colorScheme
.primary, // 设置按钮背景色
),
child: Obx(() => Text(ctr.attributeText.value)),
),
),
),
const SizedBox(width: 8),
Expanded(
child: TextButton(
onPressed: () {
Get.toNamed(
'/whisperDetail',
parameters: {
'name': memberInfo.name!,
'face': memberInfo.face!,
'mid': memberInfo.mid.toString(),
'heroTag': ctr.heroTag!,
},
);
},
style: TextButton.styleFrom(
backgroundColor: Theme.of(context)
.colorScheme
.onInverseSurface,
),
child: const Text('发消息'),
),
)
],
)
],
if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1) ...[
TextButton(
onPressed: () {
SmartDialog.showToast('功能开发中 💪');
},
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 80, right: 80),
foregroundColor:
Theme.of(context).colorScheme.onPrimary,
backgroundColor:
Theme.of(context).colorScheme.primary,
),
child: const Text('编辑资料'),
)
],
if (ctr.ownerMid == -1) ...[
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 80, right: 80),
foregroundColor:
Theme.of(context).colorScheme.outline,
backgroundColor:
Theme.of(context).colorScheme.onInverseSurface,
),
child: const Text('未登录'),
)
]
],
),
),
],
), ),
); const SizedBox(width: 12),
}), Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: statList.map((item) {
return buildStatColumn(
context,
item['label'],
item['value'],
item['fn'],
);
}).toList(),
),
),
const SizedBox(height: 16),
if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1)
buildActionButtons(context, ctr, memberInfo),
if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1)
buildEditProfileButton(context),
if (ctr.ownerMid == -1) buildNotLoggedInButton(context),
],
),
),
],
),
);
}
Widget buildStatColumn(
BuildContext context,
String label,
String value,
VoidCallback? onTap,
) {
return InkWell(
onTap: onTap,
child: Column(
children: [
Text(
value,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
Text(
label,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
),
),
],
),
);
}
Widget buildActionButtons(
BuildContext context,
dynamic ctr,
MemberInfoModel memberInfo,
) {
ColorScheme colorScheme = Theme.of(context).colorScheme;
return Row(
children: [
const SizedBox(width: 20),
Obx(
() => Expanded(
child: TextButton(
onPressed: () => loadingStatus ? null : ctr.actionRelationMod(),
style: TextButton.styleFrom(
foregroundColor: ctr.attribute.value == -1
? Colors.transparent
: ctr.attribute.value != 0
? colorScheme.outline
: colorScheme.onPrimary,
backgroundColor: ctr.attribute.value != 0
? colorScheme.onInverseSurface
: colorScheme.primary,
),
child: Obx(() => Text(ctr.attributeText.value)),
),
),
),
const SizedBox(width: 8),
Expanded(
child: TextButton(
onPressed: () {
Get.toNamed(
'/whisperDetail',
parameters: {
'name': memberInfo.name!,
'face': memberInfo.face!,
'mid': memberInfo.mid.toString(),
'heroTag': ctr.heroTag!,
},
);
},
style: TextButton.styleFrom(
backgroundColor: colorScheme.onInverseSurface,
),
child: const Text('发消息'),
),
),
],
);
}
Widget buildEditProfileButton(BuildContext context) {
return TextButton(
onPressed: () {
SmartDialog.showToast('功能开发中 💪');
},
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 80),
foregroundColor: Theme.of(context).colorScheme.onPrimary,
backgroundColor: Theme.of(context).colorScheme.primary,
),
child: const Text('编辑资料'),
);
}
Widget buildNotLoggedInButton(BuildContext context) {
return TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 80),
foregroundColor: Theme.of(context).colorScheme.outline,
backgroundColor: Theme.of(context).colorScheme.onInverseSurface,
),
child: const Text('未登录'),
); );
} }
} }

View File

@ -10,7 +10,6 @@ class MemberArticleController extends GetxController {
int pn = 1; int pn = 1;
String? offset; String? offset;
bool hasMore = true; bool hasMore = true;
String? wWebid;
RxBool isLoading = false.obs; RxBool isLoading = false.obs;
RxList<MemberArticleItemModel> articleList = <MemberArticleItemModel>[].obs; RxList<MemberArticleItemModel> articleList = <MemberArticleItemModel>[].obs;
@ -20,25 +19,11 @@ class MemberArticleController extends GetxController {
mid = int.parse(Get.parameters['mid']!); mid = int.parse(Get.parameters['mid']!);
} }
// 获取wWebid
Future getWWebid() async {
var res = await MemberHttp.getWWebid(mid: mid);
if (res['status']) {
wWebid = res['data'];
} else {
wWebid = '-1';
SmartDialog.showToast(res['msg']);
}
}
Future getMemberArticle(type) async { Future getMemberArticle(type) async {
if (isLoading.value) { if (isLoading.value) {
return; return;
} }
isLoading.value = true; isLoading.value = true;
if (wWebid == null) {
await getWWebid();
}
if (type == 'init') { if (type == 'init') {
pn = 1; pn = 1;
articleList.clear(); articleList.clear();
@ -47,7 +32,6 @@ class MemberArticleController extends GetxController {
mid: mid, mid: mid,
pn: pn, pn: pn,
offset: offset, offset: offset,
wWebid: wWebid!,
); );
if (res['status']) { if (res['status']) {
offset = res['data'].offset; offset = res['data'].offset;

View File

@ -125,21 +125,29 @@ class LikeItem extends StatelessWidget {
Color outline = Theme.of(context).colorScheme.outline; Color outline = Theme.of(context).colorScheme.outline;
final nickNameList = item.users!.map((e) => e.nickname).take(2).toList(); final nickNameList = item.users!.map((e) => e.nickname).take(2).toList();
int usersLen = item.users!.length > 3 ? 3 : item.users!.length; int usersLen = item.users!.length > 3 ? 3 : item.users!.length;
final String bvid = item.item!.uri!.split('/').last; final Uri uri = Uri.parse(item.item!.uri!);
final String path = uri.path;
final String bvid = path.split('/').last;
/// bilibili://
final Uri nativeUri = Uri.parse(item.item!.nativeUri!);
final Map<String, String> queryParameters = nativeUri.queryParameters;
final String type = item.item!.type!;
// cid
final String? argCid = queryParameters['cid'];
// 页码 // 页码
final String page = final String? page = queryParameters['page'];
item.item!.nativeUri!.split('page=').last.split('&').first;
// 根评论id // 根评论id
final String commentRootId = final String? commentRootId = queryParameters['comment_root_id'];
item.item!.nativeUri!.split('comment_root_id=').last.split('&').first;
// 二级评论id // 二级评论id
final String commentSecondaryId = final String? commentSecondaryId = queryParameters['comment_secondary_id'];
item.item!.nativeUri!.split('comment_secondary_id=').last;
return InkWell( return InkWell(
onTap: () async { onTap: () async {
try { try {
final int cid = await SearchHttp.ab2c(bvid: bvid); final int cid = argCid != null
? int.parse(argCid)
: await SearchHttp.ab2c(bvid: bvid);
final String heroTag = Utils.makeHeroTag(bvid); final String heroTag = Utils.makeHeroTag(bvid);
Get.toNamed<dynamic>( Get.toNamed<dynamic>(
'/video?bvid=$bvid&cid=$cid', '/video?bvid=$bvid&cid=$cid',
@ -148,8 +156,8 @@ class LikeItem extends StatelessWidget {
'heroTag': heroTag, 'heroTag': heroTag,
}, },
); );
} catch (_) { } catch (e) {
SmartDialog.showToast('视频可能失效了'); SmartDialog.showToast('视频可能失效了$e');
} }
}, },
child: Stack( child: Stack(
@ -222,7 +230,7 @@ class LikeItem extends StatelessWidget {
), ),
), ),
const SizedBox(width: 25), const SizedBox(width: 25),
if (item.item!.type! == 'reply') if (type == 'reply' || type == 'danmu')
Container( Container(
width: 60, width: 60,
height: 60, height: 60,
@ -234,7 +242,7 @@ class LikeItem extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
if (item.item!.type! == 'video') if (type == 'video')
NetworkImgLayer( NetworkImgLayer(
width: 60, width: 60,
height: 60, height: 60,

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/global_data_cache.dart';
@ -22,7 +23,7 @@ class _PlayGesturePageState extends State<PlayGesturePage> {
void initState() { void initState() {
super.initState(); super.initState();
fullScreenGestureMode = setting.get(SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode = setting.get(SettingBoxKey.fullScreenGestureMode,
defaultValue: FullScreenGestureMode.values.last.index); defaultValue: FullScreenGestureMode.fromBottomtoTop.index);
} }
@override @override
@ -71,6 +72,7 @@ class _PlayGesturePageState extends State<PlayGesturePage> {
GlobalDataCache().fullScreenGestureMode.index; GlobalDataCache().fullScreenGestureMode.index;
setting.put( setting.put(
SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode); SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode);
SmartDialog.showToast('设置成功');
setState(() {}); setState(() {});
} }
}, },

View File

@ -730,7 +730,7 @@ InlineSpan buildContent(
source: '', source: '',
dataString: matchStr, dataString: matchStr,
); );
PiliSchame.fullPathPush(scheme); PiliSchame.httpsScheme(scheme);
} }
} else { } else {
if (appUrlSchema.startsWith('bilibili://search')) { if (appUrlSchema.startsWith('bilibili://search')) {

View File

@ -204,6 +204,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
widget.controller.brightness.value = value; widget.controller.brightness.value = value;
} }
bool isUsingFullScreenGestures(double tapPosition, double sectionWidth) {
return fullScreenGestureMode != FullScreenGestureMode.none &&
tapPosition < sectionWidth * 2;
}
@override @override
void dispose() { void dispose() {
animationController.dispose(); animationController.dispose();
@ -638,7 +643,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
onVerticalDragUpdate: (DragUpdateDetails details) async { onVerticalDragUpdate: (DragUpdateDetails details) async {
final double totalWidth = MediaQuery.sizeOf(context).width; final double totalWidth = MediaQuery.sizeOf(context).width;
final double tapPosition = details.localPosition.dx; final double tapPosition = details.localPosition.dx;
final double sectionWidth = totalWidth / 3; final double sectionWidth =
fullScreenGestureMode == FullScreenGestureMode.none
? totalWidth / 2
: totalWidth / 3;
final double delta = details.delta.dy; final double delta = details.delta.dy;
/// 锁定时禁用 /// 锁定时禁用
@ -660,12 +668,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
_brightnessValue.value - delta / level; _brightnessValue.value - delta / level;
final double result = brightness.clamp(0.0, 1.0); final double result = brightness.clamp(0.0, 1.0);
setBrightness(result); setBrightness(result);
} else if (tapPosition < sectionWidth * 2) { } else if (isUsingFullScreenGestures(tapPosition, sectionWidth)) {
// 全屏 // 全屏
final double dy = details.delta.dy; final double dy = details.delta.dy;
const double threshold = 7.0; // 滑动阈值 const double threshold = 7.0; // 滑动阈值
final bool flag = final bool flag = fullScreenGestureMode !=
fullScreenGestureMode != FullScreenGestureMode.values.last; FullScreenGestureMode.fromBottomtoTop;
if (dy > _distance.value && if (dy > _distance.value &&
dy > threshold && dy > threshold &&
!_.controlsLock.value) { !_.controlsLock.value) {

View File

@ -1,3 +1,4 @@
import 'package:app_links/app_links.dart';
import 'package:appscheme/appscheme.dart'; import 'package:appscheme/appscheme.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -10,22 +11,29 @@ import 'url_utils.dart';
import 'utils.dart'; import 'utils.dart';
class PiliSchame { class PiliSchame {
static late AppLinks appLinks;
static AppScheme appScheme = AppSchemeImpl.getInstance()!; static AppScheme appScheme = AppSchemeImpl.getInstance()!;
static Future<void> init() async { static Future<void> init() async {
/// appLinks = AppLinks();
final SchemeEntity? value = await appScheme.getInitScheme(); appLinks.uriLinkStream.listen((Uri uri) {
if (value != null) { final String scheme = uri.scheme;
_routePush(value); if (RegExp(r'^pili', caseSensitive: false).hasMatch(scheme)) {
} piliScheme(uri);
}
});
appScheme.getInitScheme().then((SchemeEntity? value) {
if (value != null) {
_routePush(value);
}
});
/// 完整链接进入 b23.无效
appScheme.getLatestScheme().then((SchemeEntity? value) { appScheme.getLatestScheme().then((SchemeEntity? value) {
if (value != null) { if (value != null) {
_routePush(value); _routePush(value);
} }
}); });
/// 注册从外部打开的Scheme监听信息 #
appScheme.registerSchemeListener().listen((SchemeEntity? event) { appScheme.registerSchemeListener().listen((SchemeEntity? event) {
if (event != null) { if (event != null) {
_routePush(event); _routePush(event);
@ -36,88 +44,11 @@ class PiliSchame {
/// 路由跳转 /// 路由跳转
static void _routePush(value) async { static void _routePush(value) async {
final String scheme = value.scheme; final String scheme = value.scheme;
final String host = value.host;
final String path = value.path;
if (scheme == 'bilibili') { if (scheme == 'bilibili') {
switch (host) { biliScheme(value);
case 'root':
Navigator.popUntil(
Get.context!, (Route<dynamic> route) => route.isFirst);
break;
case 'space':
final String mid = path.split('/').last;
Get.toNamed<dynamic>(
'/member?mid=$mid',
arguments: <String, dynamic>{'face': null},
);
break;
case 'video':
String pathQuery = path.split('/').last;
final numericRegex = RegExp(r'^[0-9]+$');
if (numericRegex.hasMatch(pathQuery)) {
pathQuery = 'AV$pathQuery';
}
Map map = IdUtils.matchAvorBv(input: pathQuery);
if (map.containsKey('AV')) {
_videoPush(map['AV'], null);
} else if (map.containsKey('BV')) {
_videoPush(null, map['BV']);
} else {
SmartDialog.showToast('投稿匹配失败');
}
break;
case 'live':
final String roomId = path.split('/').last;
Get.toNamed<dynamic>(
'/liveRoom?roomid=$roomId',
arguments: <String, String?>{'liveItem': null, 'heroTag': roomId},
);
break;
case 'bangumi':
if (path.startsWith('/season')) {
final String seasonId = path.split('/').last;
RoutePush.bangumiPush(int.parse(seasonId), null);
}
break;
case 'opus':
if (path.startsWith('/detail')) {
var opusId = path.split('/').last;
Get.toNamed('/opus', parameters: {
'title': '',
'id': opusId,
'articleType': 'opus',
});
}
break;
case 'search':
Get.toNamed('/searchResult', parameters: {'keyword': ''});
break;
case 'article':
final String id = path.split('/').last.split('?').first;
Get.toNamed(
'/read',
parameters: {
'title': 'cv$id',
'id': id,
'dynamicType': 'read',
},
);
break;
case 'pgc':
if (path.contains('ep')) {
final String lastPathSegment = path.split('/').last;
RoutePush.bangumiPush(
null, int.parse(lastPathSegment.split('?').first));
}
break;
default:
SmartDialog.showToast('未匹配地址,请联系开发者');
Clipboard.setData(ClipboardData(text: value.toJson().toString()));
break;
}
} }
if (scheme == 'https') { if (scheme == 'https') {
fullPathPush(value); httpsScheme(value);
} }
} }
@ -148,7 +79,7 @@ class PiliSchame {
} }
} }
static Future<void> fullPathPush(SchemeEntity value) async { static Future<void> httpsScheme(SchemeEntity value) async {
// https://m.bilibili.com/bangumi/play/ss39708 // https://m.bilibili.com/bangumi/play/ss39708
// https | m.bilibili.com | /bangumi/play/ss39708 // https | m.bilibili.com | /bangumi/play/ss39708
// final String scheme = value.scheme!; // final String scheme = value.scheme!;
@ -281,6 +212,140 @@ class PiliSchame {
} }
} }
static Future<void> biliScheme(SchemeEntity value) async {
final String host = value.host!;
final String path = value.path!;
switch (host) {
case 'root':
Navigator.popUntil(
Get.context!, (Route<dynamic> route) => route.isFirst);
break;
case 'space':
final String mid = path.split('/').last;
Get.toNamed<dynamic>(
'/member?mid=$mid',
arguments: <String, dynamic>{'face': null},
);
break;
case 'video':
String pathQuery = path.split('/').last;
final numericRegex = RegExp(r'^[0-9]+$');
if (numericRegex.hasMatch(pathQuery)) {
pathQuery = 'AV$pathQuery';
}
Map map = IdUtils.matchAvorBv(input: pathQuery);
if (map.containsKey('AV')) {
_videoPush(map['AV'], null);
} else if (map.containsKey('BV')) {
_videoPush(null, map['BV']);
} else {
SmartDialog.showToast('投稿匹配失败');
}
break;
case 'live':
final String roomId = path.split('/').last;
Get.toNamed<dynamic>(
'/liveRoom?roomid=$roomId',
arguments: <String, String?>{'liveItem': null, 'heroTag': roomId},
);
break;
case 'bangumi':
if (path.startsWith('/season')) {
final String seasonId = path.split('/').last;
RoutePush.bangumiPush(int.parse(seasonId), null);
}
break;
case 'opus':
if (path.startsWith('/detail')) {
var opusId = path.split('/').last;
Get.toNamed('/opus', parameters: {
'title': '',
'id': opusId,
'articleType': 'opus',
});
}
break;
case 'search':
Get.toNamed('/searchResult', parameters: {'keyword': ''});
break;
case 'article':
final String id = path.split('/').last.split('?').first;
Get.toNamed(
'/read',
parameters: {
'title': 'cv$id',
'id': id,
'dynamicType': 'read',
},
);
break;
case 'pgc':
if (path.contains('ep')) {
final String lastPathSegment = path.split('/').last;
RoutePush.bangumiPush(
null, int.parse(lastPathSegment.split('?').first));
}
break;
default:
SmartDialog.showToast('未匹配地址,请联系开发者');
Clipboard.setData(ClipboardData(text: value.toJson().toString()));
break;
}
}
static Future<void> piliScheme(Uri value) async {
final String host = value.host;
final String path = value.path;
final String arg = path.split('/').last;
switch (host) {
case 'home':
case 'root':
Get.toNamed('/');
break;
case 'member':
if (arg != '') {
final int? mid = int.tryParse(arg);
if (mid == null) {
SmartDialog.showToast('用户id有误');
return;
}
Get.toNamed<dynamic>(
'/member?mid=$mid',
arguments: <String, dynamic>{'face': null},
);
} else {
Get.toNamed('/mine');
}
break;
case 'search':
if (arg != '') {
final String encodedArg = Uri.decodeComponent(arg);
Get.toNamed('/searchResult', parameters: {'keyword': encodedArg});
} else {
Get.toNamed('/search');
}
break;
case 'setting':
Get.toNamed('/setting');
break;
case 'fav':
Get.toNamed('/fav');
break;
case 'history':
Get.toNamed('/history');
break;
case 'later':
Get.toNamed('/later');
break;
case 'msg':
Get.toNamed('/whisper');
break;
default:
Get.toNamed('/');
break;
}
}
static void _handleEpisodePath(String lastPathSegment, String redirectUrl) { static void _handleEpisodePath(String lastPathSegment, String redirectUrl) {
final String seasonId = _extractIdFromPath(lastPathSegment); final String seasonId = _extractIdFromPath(lastPathSegment);
RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first); RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first);

View File

@ -15,6 +15,7 @@ class GlobalDataCache {
late FullScreenGestureMode fullScreenGestureMode; late FullScreenGestureMode fullScreenGestureMode;
late bool enablePlayerControlAnimation; late bool enablePlayerControlAnimation;
late List<String> actionTypeSort; late List<String> actionTypeSort;
String? wWebid;
/// 播放器相关 /// 播放器相关
// 弹幕开关 // 弹幕开关
@ -59,7 +60,7 @@ class GlobalDataCache {
defaultValue: 10); // 设置全局变量 defaultValue: 10); // 设置全局变量
fullScreenGestureMode = FullScreenGestureMode.values[setting.get( fullScreenGestureMode = FullScreenGestureMode.values[setting.get(
SettingBoxKey.fullScreenGestureMode, SettingBoxKey.fullScreenGestureMode,
defaultValue: FullScreenGestureMode.values.last.index) as int]; defaultValue: FullScreenGestureMode.fromBottomtoTop.index)];
enablePlayerControlAnimation = setting enablePlayerControlAnimation = setting
.get(SettingBoxKey.enablePlayerControlAnimation, defaultValue: true); .get(SettingBoxKey.enablePlayerControlAnimation, defaultValue: true);
actionTypeSort = await setting.get(SettingBoxKey.actionTypeSort, actionTypeSort = await setting.get(SettingBoxKey.actionTypeSort,

View File

@ -306,7 +306,7 @@ class Utils {
onPressed: () async { onPressed: () async {
await SmartDialog.dismiss(); await SmartDialog.dismiss();
launchUrl( launchUrl(
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'),
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
); );
}, },

View File

@ -8,6 +8,7 @@
#include <dynamic_color/dynamic_color_plugin.h> #include <dynamic_color/dynamic_color_plugin.h>
#include <flutter_volume_controller/flutter_volume_controller_plugin.h> #include <flutter_volume_controller/flutter_volume_controller_plugin.h>
#include <gtk/gtk_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h> #include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h> #include <media_kit_video/media_kit_video_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
@ -19,6 +20,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar = g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin");
flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar); flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar);
g_autoptr(FlPluginRegistrar) gtk_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
gtk_plugin_register_with_registrar(gtk_registrar);
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);

View File

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color dynamic_color
flutter_volume_controller flutter_volume_controller
gtk
media_kit_libs_linux media_kit_libs_linux
media_kit_video media_kit_video
url_launcher_linux url_launcher_linux

View File

@ -5,6 +5,7 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import app_links
import audio_service import audio_service
import audio_session import audio_session
import connectivity_plus import connectivity_plus
@ -22,6 +23,7 @@ import url_launcher_macos
import wakelock_plus import wakelock_plus
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))

View File

@ -17,6 +17,38 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.2.0" version: "6.2.0"
app_links:
dependency: "direct main"
description:
name: app_links
sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.3.2"
app_links_linux:
dependency: transitive
description:
name: app_links_linux
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.3"
app_links_platform_interface:
dependency: transitive
description:
name: app_links_platform_interface
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
app_links_web:
dependency: transitive
description:
name: app_links_web
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.4"
appscheme: appscheme:
dependency: "direct main" dependency: "direct main"
description: description:
@ -686,6 +718,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.1.0" version: "0.1.0"
gtk:
dependency: transitive
description:
name: gtk
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
hive: hive:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -112,6 +112,7 @@ dependencies:
flutter_displaymode: ^0.6.0 flutter_displaymode: ^0.6.0
# scheme跳转 # scheme跳转
appscheme: ^1.0.8 appscheme: ^1.0.8
app_links: ^6.3.2
# 弹幕 # 弹幕
ns_danmaku: ns_danmaku:
git: git:

View File

@ -6,6 +6,7 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <app_links/app_links_plugin_c_api.h>
#include <connectivity_plus/connectivity_plus_windows_plugin.h> #include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <dynamic_color/dynamic_color_plugin_c_api.h> #include <dynamic_color/dynamic_color_plugin_c_api.h>
#include <flutter_volume_controller/flutter_volume_controller_plugin_c_api.h> #include <flutter_volume_controller/flutter_volume_controller_plugin_c_api.h>
@ -17,6 +18,8 @@
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
AppLinksPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
ConnectivityPlusWindowsPluginRegisterWithRegistrar( ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
DynamicColorPluginCApiRegisterWithRegistrar( DynamicColorPluginCApiRegisterWithRegistrar(

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
app_links
connectivity_plus connectivity_plus
dynamic_color dynamic_color
flutter_volume_controller flutter_volume_controller